javassist.bytecode.CodeAttribute Maven / Gradle / Ivy
/*
* 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 List attributes;
/**
* Constructs a Code_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 ArrayList();
}
/**
* Constructs a copy of Code_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, Map classnames)
throws BadBytecode
{
super(cp, tag);
maxStack = src.getMaxStack();
maxLocals = src.getMaxLocals();
exceptions = src.getExceptionTable().copy(cp, classnames);
attributes = new ArrayList();
List src_attr = src.getAttributes();
int num = src_attr.size();
for (int i = 0; i < num; ++i) {
AttributeInfo ai = src_attr.get(i);
attributes.add(ai.copy(cp, classnames));
}
info = src.copyCode(cp, classnames, exceptions, this);
}
CodeAttribute(ConstPool cp, int name_id, DataInputStream in)
throws IOException
{
super(cp, name_id, (byte[])null);
@SuppressWarnings("unused")
int attr_len = in.readInt();
maxStack = in.readUnsignedShort();
maxLocals = in.readUnsignedShort();
int code_len = in.readInt();
info = new byte[code_len];
in.readFully(info);
exceptions = new ExceptionTable(cp, in);
attributes = new ArrayList();
int num = in.readUnsignedShort();
for (int i = 0; i < num; ++i)
attributes.add(AttributeInfo.read(cp, in));
}
/**
* Makes a copy. Class names are replaced according to the
* given Map
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, Map classnames)
throws RuntimeCopyException
{
try {
return new CodeAttribute(newCp, this, classnames);
}
catch (BadBytecode e) {
throw new RuntimeCopyException("bad bytecode. fatal?");
}
}
/**
* An exception that may be thrown by copy()
* 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(Map classnames) {
AttributeInfo.renameClass(attributes, classnames);
}
@Override
void getRefClasses(Map classnames) {
AttributeInfo.getRefClasses(attributes, classnames);
}
/**
* Returns the name of the class declaring the method including
* this code attribute.
*/
public String getDeclaringClass() {
ConstPool cp = getConstPool();
return cp.getClassName();
}
/**
* Returns max_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 List getAttributes() { return attributes; }
/**
* Returns the attribute with the specified name.
* If it is not found, this method returns null.
*
* @param name attribute name
* @return an AttributeInfo
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, Map classnames,
ExceptionTable etable, CodeAttribute destCa)
throws BadBytecode
{
int len = getCodeLength();
byte[] newCode = new byte[len];
destCa.info = newCode;
LdcEntry ldc = copyCode(this.info, 0, len, this.getConstPool(),
newCode, destCp, classnames);
return LdcEntry.doit(newCode, ldc, etable, destCa);
}
private static LdcEntry copyCode(byte[] code, int beginPos, int endPos,
ConstPool srcCp, byte[] newcode,
ConstPool destCp, Map classnameMap)
throws BadBytecode
{
int i2, index;
LdcEntry ldcEntry = null;
for (int i = beginPos; i < endPos; i = i2) {
i2 = CodeIterator.nextOpcode(code, i);
byte c = code[i];
newcode[i] = c;
switch (c & 0xff) {
case LDC_W :
case LDC2_W :
case GETSTATIC :
case PUTSTATIC :
case GETFIELD :
case PUTFIELD :
case INVOKEVIRTUAL :
case INVOKESPECIAL :
case INVOKESTATIC :
case NEW :
case ANEWARRAY :
case CHECKCAST :
case INSTANCEOF :
copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp,
classnameMap);
break;
case LDC :
index = code[i + 1] & 0xff;
index = srcCp.copy(index, destCp, classnameMap);
if (index < 0x100)
newcode[i + 1] = (byte)index;
else {
newcode[i] = NOP;
newcode[i + 1] = NOP;
LdcEntry ldc = new LdcEntry();
ldc.where = i;
ldc.index = index;
ldc.next = ldcEntry;
ldcEntry = ldc;
}
break;
case INVOKEINTERFACE :
copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp,
classnameMap);
newcode[i + 3] = code[i + 3];
newcode[i + 4] = code[i + 4];
break;
case INVOKEDYNAMIC :
copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp,
classnameMap);
newcode[i + 3] = 0;
newcode[i + 4] = 0;
break;
case MULTIANEWARRAY :
copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp,
classnameMap);
newcode[i + 3] = code[i + 3];
break;
default :
while (++i < i2)
newcode[i] = code[i];
break;
}
}
return ldcEntry;
}
private static void copyConstPoolInfo(int i, byte[] code, ConstPool srcCp,
byte[] newcode, ConstPool destCp,
Map classnameMap) {
int index = ((code[i] & 0xff) << 8) | (code[i + 1] & 0xff);
index = srcCp.copy(index, destCp, classnameMap);
newcode[i] = (byte)(index >> 8);
newcode[i + 1] = (byte)index;
}
static class LdcEntry {
LdcEntry next;
int where;
int index;
static byte[] doit(byte[] code, LdcEntry ldc, ExceptionTable etable,
CodeAttribute ca)
throws BadBytecode
{
if (ldc != null)
code = CodeIterator.changeLdcToLdcW(code, etable, ca, ldc);
/* The original code was the following:
while (ldc != null) {
int where = ldc.where;
code = CodeIterator.insertGapCore0(code, where, 1, false, etable, ca);
code[where] = (byte)Opcode.LDC_W;
ByteArray.write16bit(ldc.index, code, where + 1);
ldc = ldc.next;
}
But this code does not support a large method > 32KB.
*/
return code;
}
}
/**
* Changes the index numbers of the local variables
* to append a new parameter.
* This method does not update LocalVariableAttribute
,
* 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);
}
}
}
}