/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
* the terms of the GNU Lesser General Public License Version 2.1 or later,
* or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*/
package javassist.bytecode;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Code_attribute
.
*
*
To browse the code
field of
* a Code_attribute
structure,
* use CodeIterator
.
*
* @see CodeIterator
* @see #iterator()
*/
public class CodeAttribute extends AttributeInfo implements Opcode {
/**
* The name of this attribute "Code"
.
*/
public static final String tag = "Code";
// code[] is stored in AttributeInfo.info.
private int maxStack;
private int maxLocals;
private ExceptionTable exceptions;
private ListCode_attribute
.
*
* @param cp constant pool table
* @param stack max_stack
* @param locals max_locals
* @param code code[]
* @param etable exception_table[]
*/
public CodeAttribute(ConstPool cp, int stack, int locals, byte[] code,
ExceptionTable etable)
{
super(cp, tag);
maxStack = stack;
maxLocals = locals;
info = code;
exceptions = etable;
attributes = new ArrayListCode_attribute
.
* Specified class names are replaced during the copy.
*
* @param cp constant pool table.
* @param src source Code attribute.
* @param classnames pairs of replaced and substituted
* class names.
*/
private CodeAttribute(ConstPool cp, CodeAttribute src, MapMap
object.
*
* @param newCp the constant pool table used by the new copy.
* @param classnames pairs of replaced and substituted
* class names.
* @exception RuntimeCopyException if a BadBytecode
* exception is thrown, it is
* converted into
* RuntimeCopyException
.
*
* @return CodeAttribute
object.
*/
@Override
public AttributeInfo copy(ConstPool newCp, Mapcopy()
* in CodeAttribute
.
*/
public static class RuntimeCopyException extends RuntimeException {
/** default serialVersionUID */
private static final long serialVersionUID = 1L;
/**
* Constructs an exception.
*/
public RuntimeCopyException(String s) {
super(s);
}
}
/**
* Returns the length of this attribute_info
* structure.
* The returned value is attribute_length + 6
.
*/
@Override
public int length() {
return 18 + info.length + exceptions.size() * 8
+ AttributeInfo.getLength(attributes);
}
@Override
void write(DataOutputStream out) throws IOException {
out.writeShort(name); // attribute_name_index
out.writeInt(length() - 6); // attribute_length
out.writeShort(maxStack); // max_stack
out.writeShort(maxLocals); // max_locals
out.writeInt(info.length); // code_length
out.write(info); // code
exceptions.write(out);
out.writeShort(attributes.size()); // attributes_count
AttributeInfo.writeAll(attributes, out); // attributes
}
/**
* This method is not available.
*
* @throws java.lang.UnsupportedOperationException always thrown.
*/
@Override
public byte[] get() {
throw new UnsupportedOperationException("CodeAttribute.get()");
}
/**
* This method is not available.
*
* @throws java.lang.UnsupportedOperationException always thrown.
*/
@Override
public void set(byte[] newinfo) {
throw new UnsupportedOperationException("CodeAttribute.set()");
}
@Override
void renameClass(String oldname, String newname) {
AttributeInfo.renameClass(attributes, oldname, newname);
}
@Override
void renameClass(Mapmax_stack
.
*/
public int getMaxStack() {
return maxStack;
}
/**
* Sets max_stack
.
*/
public void setMaxStack(int value) {
maxStack = value;
}
/**
* Computes the maximum stack size and sets max_stack
* to the computed size.
*
* @throws BadBytecode if this method fails in computing.
* @return the newly computed value of max_stack
*/
public int computeMaxStack() throws BadBytecode {
maxStack = new CodeAnalyzer(this).computeMaxStack();
return maxStack;
}
/**
* Returns max_locals
.
*/
public int getMaxLocals() {
return maxLocals;
}
/**
* Sets max_locals
.
*/
public void setMaxLocals(int value) {
maxLocals = value;
}
/**
* Returns code_length
.
*/
public int getCodeLength() {
return info.length;
}
/**
* Returns code[]
.
*/
public byte[] getCode() {
return info;
}
/**
* Sets code[]
.
*/
void setCode(byte[] newinfo) { super.set(newinfo); }
/**
* Makes a new iterator for reading this code attribute.
*/
public CodeIterator iterator() {
return new CodeIterator(this);
}
/**
* Returns exception_table[]
.
*/
public ExceptionTable getExceptionTable() { return exceptions; }
/**
* Returns attributes[]
.
* It returns a list of AttributeInfo
.
* A new element can be added to the returned list
* and an existing element can be removed from the list.
*
* @see AttributeInfo
*/
public ListAttributeInfo
object or null.
*/
public AttributeInfo getAttribute(String name) {
return AttributeInfo.lookup(attributes, name);
}
/**
* Adds a stack map table. If another copy of stack map table
* is already contained, the old one is removed.
*
* @param smt the stack map table added to this code attribute.
* If it is null, a new stack map is not added.
* Only the old stack map is removed.
*/
public void setAttribute(StackMapTable smt) {
AttributeInfo.remove(attributes, StackMapTable.tag);
if (smt != null)
attributes.add(smt);
}
/**
* Adds a stack map table for J2ME (CLDC). If another copy of stack map table
* is already contained, the old one is removed.
*
* @param sm the stack map table added to this code attribute.
* If it is null, a new stack map is not added.
* Only the old stack map is removed.
* @since 3.12
*/
public void setAttribute(StackMap sm) {
AttributeInfo.remove(attributes, StackMap.tag);
if (sm != null)
attributes.add(sm);
}
/**
* Copies code.
*/
private byte[] copyCode(ConstPool destCp, MapLocalVariableAttribute
,
* LocalVariableTypeAttribute
,
* StackMapTable
, or StackMap
.
* These attributes must be explicitly updated.
*
* @param where the index of the new parameter.
* @param size the type size of the new parameter (1 or 2).
*
* @see LocalVariableAttribute#shiftIndex(int, int)
* @see LocalVariableTypeAttribute#shiftIndex(int, int)
* @see StackMapTable#insertLocal(int, int, int)
* @see StackMap#insertLocal(int, int, int)
*/
public void insertLocalVar(int where, int size) throws BadBytecode {
CodeIterator ci = iterator();
while (ci.hasNext())
shiftIndex(ci, where, size);
setMaxLocals(getMaxLocals() + size);
}
/**
* @param lessThan If the index of the local variable is
* less than this value, it does not change.
* Otherwise, the index is increased.
* @param delta the indexes of the local variables are
* increased by this value.
*/
private static void shiftIndex(CodeIterator ci, int lessThan, int delta) throws BadBytecode {
int index = ci.next();
int opcode = ci.byteAt(index);
if (opcode < ILOAD)
return;
else if (opcode < IASTORE) {
if (opcode < ILOAD_0) {
// iload, lload, fload, dload, aload
shiftIndex8(ci, index, opcode, lessThan, delta);
}
else if (opcode < IALOAD) {
// iload_0, ..., aload_3
shiftIndex0(ci, index, opcode, lessThan, delta, ILOAD_0, ILOAD);
}
else if (opcode < ISTORE)
return;
else if (opcode < ISTORE_0) {
// istore, lstore, ...
shiftIndex8(ci, index, opcode, lessThan, delta);
}
else {
// istore_0, ..., astore_3
shiftIndex0(ci, index, opcode, lessThan, delta, ISTORE_0, ISTORE);
}
}
else if (opcode == IINC) {
int var = ci.byteAt(index + 1);
if (var < lessThan)
return;
var += delta;
if (var < 0x100)
ci.writeByte(var, index + 1);
else {
int plus = (byte)ci.byteAt(index + 2);
int pos = ci.insertExGap(3);
ci.writeByte(WIDE, pos - 3);
ci.writeByte(IINC, pos - 2);
ci.write16bit(var, pos - 1);
ci.write16bit(plus, pos + 1);
}
}
else if (opcode == RET)
shiftIndex8(ci, index, opcode, lessThan, delta);
else if (opcode == WIDE) {
int var = ci.u16bitAt(index + 2);
if (var < lessThan)
return;
var += delta;
ci.write16bit(var, index + 2);
}
}
private static void shiftIndex8(CodeIterator ci, int index, int opcode,
int lessThan, int delta)
throws BadBytecode
{
int var = ci.byteAt(index + 1);
if (var < lessThan)
return;
var += delta;
if (var < 0x100)
ci.writeByte(var, index + 1);
else {
int pos = ci.insertExGap(2);
ci.writeByte(WIDE, pos - 2);
ci.writeByte(opcode, pos - 1);
ci.write16bit(var, pos);
}
}
private static void shiftIndex0(CodeIterator ci, int index, int opcode,
int lessThan, int delta,
int opcode_i_0, int opcode_i)
throws BadBytecode
{
int var = (opcode - opcode_i_0) % 4;
if (var < lessThan)
return;
var += delta;
if (var < 4)
ci.writeByte(opcode + delta, index);
else {
opcode = (opcode - opcode_i_0) / 4 + opcode_i;
if (var < 0x100) {
int pos = ci.insertExGap(1);
ci.writeByte(opcode, pos - 1);
ci.writeByte(var, pos);
}
else {
int pos = ci.insertExGap(3);
ci.writeByte(WIDE, pos - 1);
ci.writeByte(opcode, pos);
ci.write16bit(var, pos + 1);
}
}
}
}