org.mozilla.classfile.ClassFileWriter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rhino Show documentation
Show all versions of rhino Show documentation
The Rhino JavaScript Engine for Java
The newest version!
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.classfile;
import org.mozilla.javascript.ObjToIntMap;
import org.mozilla.javascript.ObjArray;
import org.mozilla.javascript.UintMap;
import java.io.*;
import java.util.Arrays;
/**
* ClassFileWriter
*
* A ClassFileWriter is used to write a Java class file. Methods are
* provided to create fields and methods, and within methods to write
* Java bytecodes.
*
* @author Roger Lawrence
*/
public class ClassFileWriter {
/**
* Thrown for cases where the error in generating the class file is
* due to a program size constraints rather than a likely bug in the
* compiler.
*/
public static class ClassFileFormatException extends RuntimeException {
private static final long serialVersionUID = 1263998431033790599L;
ClassFileFormatException(String message) {
super(message);
}
}
/**
* Construct a ClassFileWriter for a class.
*
* @param className the name of the class to write, including
* full package qualification.
* @param superClassName the name of the superclass of the class
* to write, including full package qualification.
* @param sourceFileName the name of the source file to use for
* producing debug information, or null if debug information
* is not desired
*/
public ClassFileWriter(String className, String superClassName,
String sourceFileName)
{
generatedClassName = className;
itsConstantPool = new ConstantPool(this);
itsThisClassIndex = itsConstantPool.addClass(className);
itsSuperClassIndex = itsConstantPool.addClass(superClassName);
if (sourceFileName != null)
itsSourceFileNameIndex = itsConstantPool.addUtf8(sourceFileName);
// All "new" implementations are supposed to output ACC_SUPER as a
// class flag. This is specified in the first JVM spec, so it should
// be old enough that it's okay to always set it.
itsFlags = ACC_PUBLIC | ACC_SUPER;
}
public final String getClassName()
{
return generatedClassName;
}
/**
* Add an interface implemented by this class.
*
* This method may be called multiple times for classes that
* implement multiple interfaces.
*
* @param interfaceName a name of an interface implemented
* by the class being written, including full package
* qualification.
*/
public void addInterface(String interfaceName) {
short interfaceIndex = itsConstantPool.addClass(interfaceName);
itsInterfaces.add(Short.valueOf(interfaceIndex));
}
public static final short
ACC_PUBLIC = 0x0001,
ACC_PRIVATE = 0x0002,
ACC_PROTECTED = 0x0004,
ACC_STATIC = 0x0008,
ACC_FINAL = 0x0010,
ACC_SUPER = 0x0020,
ACC_SYNCHRONIZED = 0x0020,
ACC_VOLATILE = 0x0040,
ACC_TRANSIENT = 0x0080,
ACC_NATIVE = 0x0100,
ACC_ABSTRACT = 0x0400;
/**
* Set the class's flags.
*
* Flags must be a set of the following flags, bitwise or'd
* together:
* ACC_PUBLIC
* ACC_PRIVATE
* ACC_PROTECTED
* ACC_FINAL
* ACC_ABSTRACT
* TODO: check that this is the appropriate set
* @param flags the set of class flags to set
*/
public void setFlags(short flags) {
itsFlags = flags;
}
static String getSlashedForm(String name)
{
return name.replace('.', '/');
}
/**
* Convert Java class name in dot notation into
* "Lname-with-dots-replaced-by-slashes;" form suitable for use as
* JVM type signatures.
*/
public static String classNameToSignature(String name)
{
int nameLength = name.length();
int colonPos = 1 + nameLength;
char[] buf = new char[colonPos + 1];
buf[0] = 'L';
buf[colonPos] = ';';
name.getChars(0, nameLength, buf, 1);
for (int i = 1; i != colonPos; ++i) {
if (buf[i] == '.') {
buf[i] = '/';
}
}
return new String(buf, 0, colonPos + 1);
}
/**
* Add a field to the class.
*
* @param fieldName the name of the field
* @param type the type of the field using ...
* @param flags the attributes of the field, such as ACC_PUBLIC, etc.
* bitwise or'd together
*/
public void addField(String fieldName, String type, short flags) {
short fieldNameIndex = itsConstantPool.addUtf8(fieldName);
short typeIndex = itsConstantPool.addUtf8(type);
itsFields.add(new ClassFileField(fieldNameIndex, typeIndex, flags));
}
/**
* Add a field to the class.
*
* @param fieldName the name of the field
* @param type the type of the field using ...
* @param flags the attributes of the field, such as ACC_PUBLIC, etc.
* bitwise or'd together
* @param value an initial integral value
*/
public void addField(String fieldName, String type, short flags,
int value)
{
short fieldNameIndex = itsConstantPool.addUtf8(fieldName);
short typeIndex = itsConstantPool.addUtf8(type);
ClassFileField field = new ClassFileField(fieldNameIndex, typeIndex,
flags);
field.setAttributes(itsConstantPool.addUtf8("ConstantValue"),
(short)0,
(short)0,
itsConstantPool.addConstant(value));
itsFields.add(field);
}
/**
* Add a field to the class.
*
* @param fieldName the name of the field
* @param type the type of the field using ...
* @param flags the attributes of the field, such as ACC_PUBLIC, etc.
* bitwise or'd together
* @param value an initial long value
*/
public void addField(String fieldName, String type, short flags,
long value)
{
short fieldNameIndex = itsConstantPool.addUtf8(fieldName);
short typeIndex = itsConstantPool.addUtf8(type);
ClassFileField field = new ClassFileField(fieldNameIndex, typeIndex,
flags);
field.setAttributes(itsConstantPool.addUtf8("ConstantValue"),
(short)0,
(short)2,
itsConstantPool.addConstant(value));
itsFields.add(field);
}
/**
* Add a field to the class.
*
* @param fieldName the name of the field
* @param type the type of the field using ...
* @param flags the attributes of the field, such as ACC_PUBLIC, etc.
* bitwise or'd together
* @param value an initial double value
*/
public void addField(String fieldName, String type, short flags,
double value)
{
short fieldNameIndex = itsConstantPool.addUtf8(fieldName);
short typeIndex = itsConstantPool.addUtf8(type);
ClassFileField field = new ClassFileField(fieldNameIndex, typeIndex,
flags);
field.setAttributes(itsConstantPool.addUtf8("ConstantValue"),
(short)0,
(short)2,
itsConstantPool.addConstant(value));
itsFields.add(field);
}
/**
* Add Information about java variable to use when generating the local
* variable table.
*
* @param name variable name.
* @param type variable type as bytecode descriptor string.
* @param startPC the starting bytecode PC where this variable is live,
* or -1 if it does not have a Java register.
* @param register the Java register number of variable
* or -1 if it does not have a Java register.
*/
public void addVariableDescriptor(String name, String type, int startPC, int register)
{
int nameIndex = itsConstantPool.addUtf8(name);
int descriptorIndex = itsConstantPool.addUtf8(type);
int [] chunk = { nameIndex, descriptorIndex, startPC, register };
if (itsVarDescriptors == null) {
itsVarDescriptors = new ObjArray();
}
itsVarDescriptors.add(chunk);
}
/**
* Add a method and begin adding code.
*
* This method must be called before other methods for adding code,
* exception tables, etc. can be invoked.
*
* @param methodName the name of the method
* @param type a string representing the type
* @param flags the attributes of the field, such as ACC_PUBLIC, etc.
* bitwise or'd together
*/
public void startMethod(String methodName, String type, short flags) {
short methodNameIndex = itsConstantPool.addUtf8(methodName);
short typeIndex = itsConstantPool.addUtf8(type);
itsCurrentMethod = new ClassFileMethod(methodName, methodNameIndex,
type, typeIndex, flags);
itsJumpFroms = new UintMap();
itsMethods.add(itsCurrentMethod);
addSuperBlockStart(0);
}
/**
* Complete generation of the method.
*
* After this method is called, no more code can be added to the
* method begun with startMethod
.
*
* @param maxLocals the maximum number of local variable slots
* (a.k.a. Java registers) used by the method
*/
public void stopMethod(short maxLocals) {
if (itsCurrentMethod == null)
throw new IllegalStateException("No method to stop");
fixLabelGotos();
itsMaxLocals = maxLocals;
StackMapTable stackMap = null;
if (GenerateStackMap) {
finalizeSuperBlockStarts();
stackMap = new StackMapTable();
stackMap.generate();
}
int lineNumberTableLength = 0;
if (itsLineNumberTable != null) {
// 6 bytes for the attribute header
// 2 bytes for the line number count
// 4 bytes for each entry
lineNumberTableLength = 6 + 2 + (itsLineNumberTableTop * 4);
}
int variableTableLength = 0;
if (itsVarDescriptors != null) {
// 6 bytes for the attribute header
// 2 bytes for the variable count
// 10 bytes for each entry
variableTableLength = 6 + 2 + (itsVarDescriptors.size() * 10);
}
int stackMapTableLength = 0;
if (stackMap != null) {
int stackMapWriteSize = stackMap.computeWriteSize();
if (stackMapWriteSize > 0) {
stackMapTableLength = 6 + stackMapWriteSize;
}
}
int attrLength = 2 + // attribute_name_index
4 + // attribute_length
2 + // max_stack
2 + // max_locals
4 + // code_length
itsCodeBufferTop +
2 + // exception_table_length
(itsExceptionTableTop * 8) +
2 + // attributes_count
lineNumberTableLength +
variableTableLength +
stackMapTableLength;
if (attrLength > 65536) {
// See http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html,
// section 4.10, "The amount of code per non-native, non-abstract
// method is limited to 65536 bytes...
throw new ClassFileFormatException(
"generated bytecode for method exceeds 64K limit.");
}
byte[] codeAttribute = new byte[attrLength];
int index = 0;
int codeAttrIndex = itsConstantPool.addUtf8("Code");
index = putInt16(codeAttrIndex, codeAttribute, index);
attrLength -= 6; // discount the attribute header
index = putInt32(attrLength, codeAttribute, index);
index = putInt16(itsMaxStack, codeAttribute, index);
index = putInt16(itsMaxLocals, codeAttribute, index);
index = putInt32(itsCodeBufferTop, codeAttribute, index);
System.arraycopy(itsCodeBuffer, 0, codeAttribute, index,
itsCodeBufferTop);
index += itsCodeBufferTop;
if (itsExceptionTableTop > 0) {
index = putInt16(itsExceptionTableTop, codeAttribute, index);
for (int i = 0; i < itsExceptionTableTop; i++) {
ExceptionTableEntry ete = itsExceptionTable[i];
short startPC = (short)getLabelPC(ete.itsStartLabel);
short endPC = (short)getLabelPC(ete.itsEndLabel);
short handlerPC = (short)getLabelPC(ete.itsHandlerLabel);
short catchType = ete.itsCatchType;
if (startPC == -1)
throw new IllegalStateException("start label not defined");
if (endPC == -1)
throw new IllegalStateException("end label not defined");
if (handlerPC == -1)
throw new IllegalStateException(
"handler label not defined");
index = putInt16(startPC, codeAttribute, index);
index = putInt16(endPC, codeAttribute, index);
index = putInt16(handlerPC, codeAttribute, index);
index = putInt16(catchType, codeAttribute, index);
}
}
else {
// write 0 as exception table length
index = putInt16(0, codeAttribute, index);
}
int attributeCount = 0;
if (itsLineNumberTable != null)
attributeCount++;
if (itsVarDescriptors != null)
attributeCount++;
if (stackMapTableLength > 0) {
attributeCount++;
}
index = putInt16(attributeCount, codeAttribute, index);
if (itsLineNumberTable != null) {
int lineNumberTableAttrIndex
= itsConstantPool.addUtf8("LineNumberTable");
index = putInt16(lineNumberTableAttrIndex, codeAttribute, index);
int tableAttrLength = 2 + (itsLineNumberTableTop * 4);
index = putInt32(tableAttrLength, codeAttribute, index);
index = putInt16(itsLineNumberTableTop, codeAttribute, index);
for (int i = 0; i < itsLineNumberTableTop; i++) {
index = putInt32(itsLineNumberTable[i], codeAttribute, index);
}
}
if (itsVarDescriptors != null) {
int variableTableAttrIndex
= itsConstantPool.addUtf8("LocalVariableTable");
index = putInt16(variableTableAttrIndex, codeAttribute, index);
int varCount = itsVarDescriptors.size();
int tableAttrLength = 2 + (varCount * 10);
index = putInt32(tableAttrLength, codeAttribute, index);
index = putInt16(varCount, codeAttribute, index);
for (int i = 0; i < varCount; i++) {
int[] chunk = (int[])itsVarDescriptors.get(i);
int nameIndex = chunk[0];
int descriptorIndex = chunk[1];
int startPC = chunk[2];
int register = chunk[3];
int length = itsCodeBufferTop - startPC;
index = putInt16(startPC, codeAttribute, index);
index = putInt16(length, codeAttribute, index);
index = putInt16(nameIndex, codeAttribute, index);
index = putInt16(descriptorIndex, codeAttribute, index);
index = putInt16(register, codeAttribute, index);
}
}
if (stackMapTableLength > 0) {
int stackMapTableAttrIndex =
itsConstantPool.addUtf8("StackMapTable");
int start = index;
index = putInt16(stackMapTableAttrIndex, codeAttribute, index);
index = stackMap.write(codeAttribute, index);
}
itsCurrentMethod.setCodeAttribute(codeAttribute);
itsExceptionTable = null;
itsExceptionTableTop = 0;
itsLineNumberTableTop = 0;
itsCodeBufferTop = 0;
itsCurrentMethod = null;
itsMaxStack = 0;
itsStackTop = 0;
itsLabelTableTop = 0;
itsFixupTableTop = 0;
itsVarDescriptors = null;
itsSuperBlockStarts = null;
itsSuperBlockStartsTop = 0;
itsJumpFroms = null;
}
/**
* Add the single-byte opcode to the current method.
*
* @param theOpCode the opcode of the bytecode
*/
public void add(int theOpCode) {
if (opcodeCount(theOpCode) != 0)
throw new IllegalArgumentException("Unexpected operands");
int newStack = itsStackTop + stackChange(theOpCode);
if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
if (DEBUGCODE)
System.out.println("Add " + bytecodeStr(theOpCode));
addToCodeBuffer(theOpCode);
itsStackTop = (short)newStack;
if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
if (DEBUGSTACK) {
System.out.println("After "+bytecodeStr(theOpCode)
+" stack = "+itsStackTop);
}
if (theOpCode == ByteCode.ATHROW) {
addSuperBlockStart(itsCodeBufferTop);
}
}
/**
* Add a single-operand opcode to the current method.
*
* @param theOpCode the opcode of the bytecode
* @param theOperand the operand of the bytecode
*/
public void add(int theOpCode, int theOperand) {
if (DEBUGCODE) {
System.out.println("Add "+bytecodeStr(theOpCode)
+", "+Integer.toHexString(theOperand));
}
int newStack = itsStackTop + stackChange(theOpCode);
if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
switch (theOpCode) {
case ByteCode.GOTO :
// This is necessary because dead code is seemingly being
// generated and Sun's verifier is expecting type state to be
// placed even at dead blocks of code.
addSuperBlockStart(itsCodeBufferTop + 3);
// fallthru...
case ByteCode.IFEQ :
case ByteCode.IFNE :
case ByteCode.IFLT :
case ByteCode.IFGE :
case ByteCode.IFGT :
case ByteCode.IFLE :
case ByteCode.IF_ICMPEQ :
case ByteCode.IF_ICMPNE :
case ByteCode.IF_ICMPLT :
case ByteCode.IF_ICMPGE :
case ByteCode.IF_ICMPGT :
case ByteCode.IF_ICMPLE :
case ByteCode.IF_ACMPEQ :
case ByteCode.IF_ACMPNE :
case ByteCode.JSR :
case ByteCode.IFNULL :
case ByteCode.IFNONNULL : {
if ((theOperand & 0x80000000) != 0x80000000) {
if ((theOperand < 0) || (theOperand > 65535))
throw new IllegalArgumentException(
"Bad label for branch");
}
int branchPC = itsCodeBufferTop;
addToCodeBuffer(theOpCode);
if ((theOperand & 0x80000000) != 0x80000000) {
// hard displacement
addToCodeInt16(theOperand);
int target = theOperand + branchPC;
addSuperBlockStart(target);
itsJumpFroms.put(target, branchPC);
}
else { // a label
int targetPC = getLabelPC(theOperand);
if (DEBUGLABELS) {
int theLabel = theOperand & 0x7FFFFFFF;
System.out.println("Fixing branch to " +
theLabel + " at " + targetPC +
" from " + branchPC);
}
if (targetPC != -1) {
int offset = targetPC - branchPC;
addToCodeInt16(offset);
addSuperBlockStart(targetPC);
itsJumpFroms.put(targetPC, branchPC);
}
else {
addLabelFixup(theOperand, branchPC + 1);
addToCodeInt16(0);
}
}
}
break;
case ByteCode.BIPUSH :
if ((byte)theOperand != theOperand)
throw new IllegalArgumentException("out of range byte");
addToCodeBuffer(theOpCode);
addToCodeBuffer((byte)theOperand);
break;
case ByteCode.SIPUSH :
if ((short)theOperand != theOperand)
throw new IllegalArgumentException("out of range short");
addToCodeBuffer(theOpCode);
addToCodeInt16(theOperand);
break;
case ByteCode.NEWARRAY :
if (!(0 <= theOperand && theOperand < 256))
throw new IllegalArgumentException("out of range index");
addToCodeBuffer(theOpCode);
addToCodeBuffer(theOperand);
break;
case ByteCode.GETFIELD :
case ByteCode.PUTFIELD :
if (!(0 <= theOperand && theOperand < 65536))
throw new IllegalArgumentException("out of range field");
addToCodeBuffer(theOpCode);
addToCodeInt16(theOperand);
break;
case ByteCode.LDC :
case ByteCode.LDC_W :
case ByteCode.LDC2_W :
if (!(0 <= theOperand && theOperand < 65536))
throw new IllegalArgumentException("out of range index");
if (theOperand >= 256
|| theOpCode == ByteCode.LDC_W
|| theOpCode == ByteCode.LDC2_W)
{
if (theOpCode == ByteCode.LDC) {
addToCodeBuffer(ByteCode.LDC_W);
} else {
addToCodeBuffer(theOpCode);
}
addToCodeInt16(theOperand);
} else {
addToCodeBuffer(theOpCode);
addToCodeBuffer(theOperand);
}
break;
case ByteCode.RET :
case ByteCode.ILOAD :
case ByteCode.LLOAD :
case ByteCode.FLOAD :
case ByteCode.DLOAD :
case ByteCode.ALOAD :
case ByteCode.ISTORE :
case ByteCode.LSTORE :
case ByteCode.FSTORE :
case ByteCode.DSTORE :
case ByteCode.ASTORE :
if (!(0 <= theOperand && theOperand < 65536))
throw new ClassFileFormatException("out of range variable");
if (theOperand >= 256) {
addToCodeBuffer(ByteCode.WIDE);
addToCodeBuffer(theOpCode);
addToCodeInt16(theOperand);
}
else {
addToCodeBuffer(theOpCode);
addToCodeBuffer(theOperand);
}
break;
default :
throw new IllegalArgumentException(
"Unexpected opcode for 1 operand");
}
itsStackTop = (short)newStack;
if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
if (DEBUGSTACK) {
System.out.println("After "+bytecodeStr(theOpCode)
+" stack = "+itsStackTop);
}
}
/**
* Generate the load constant bytecode for the given integer.
*
* @param k the constant
*/
public void addLoadConstant(int k) {
switch (k) {
case 0: add(ByteCode.ICONST_0); break;
case 1: add(ByteCode.ICONST_1); break;
case 2: add(ByteCode.ICONST_2); break;
case 3: add(ByteCode.ICONST_3); break;
case 4: add(ByteCode.ICONST_4); break;
case 5: add(ByteCode.ICONST_5); break;
default:
add(ByteCode.LDC, itsConstantPool.addConstant(k));
break;
}
}
/**
* Generate the load constant bytecode for the given long.
*
* @param k the constant
*/
public void addLoadConstant(long k) {
add(ByteCode.LDC2_W, itsConstantPool.addConstant(k));
}
/**
* Generate the load constant bytecode for the given float.
*
* @param k the constant
*/
public void addLoadConstant(float k) {
add(ByteCode.LDC, itsConstantPool.addConstant(k));
}
/**
* Generate the load constant bytecode for the given double.
*
* @param k the constant
*/
public void addLoadConstant(double k) {
add(ByteCode.LDC2_W, itsConstantPool.addConstant(k));
}
/**
* Generate the load constant bytecode for the given string.
*
* @param k the constant
*/
public void addLoadConstant(String k) {
add(ByteCode.LDC, itsConstantPool.addConstant(k));
}
/**
* Add the given two-operand bytecode to the current method.
*
* @param theOpCode the opcode of the bytecode
* @param theOperand1 the first operand of the bytecode
* @param theOperand2 the second operand of the bytecode
*/
public void add(int theOpCode, int theOperand1, int theOperand2) {
if (DEBUGCODE) {
System.out.println("Add "+bytecodeStr(theOpCode)
+", "+Integer.toHexString(theOperand1)
+", "+Integer.toHexString(theOperand2));
}
int newStack = itsStackTop + stackChange(theOpCode);
if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
if (theOpCode == ByteCode.IINC) {
if (!(0 <= theOperand1 && theOperand1 < 65536))
throw new ClassFileFormatException("out of range variable");
if (!(0 <= theOperand2 && theOperand2 < 65536))
throw new ClassFileFormatException("out of range increment");
if (theOperand1 > 255 || theOperand2 < -128 || theOperand2 > 127) {
addToCodeBuffer(ByteCode.WIDE);
addToCodeBuffer(ByteCode.IINC);
addToCodeInt16(theOperand1);
addToCodeInt16(theOperand2);
}
else {
addToCodeBuffer(ByteCode.IINC);
addToCodeBuffer(theOperand1);
addToCodeBuffer(theOperand2);
}
}
else if (theOpCode == ByteCode.MULTIANEWARRAY) {
if (!(0 <= theOperand1 && theOperand1 < 65536))
throw new IllegalArgumentException("out of range index");
if (!(0 <= theOperand2 && theOperand2 < 256))
throw new IllegalArgumentException("out of range dimensions");
addToCodeBuffer(ByteCode.MULTIANEWARRAY);
addToCodeInt16(theOperand1);
addToCodeBuffer(theOperand2);
}
else {
throw new IllegalArgumentException(
"Unexpected opcode for 2 operands");
}
itsStackTop = (short)newStack;
if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
if (DEBUGSTACK) {
System.out.println("After "+bytecodeStr(theOpCode)
+" stack = "+itsStackTop);
}
}
public void add(int theOpCode, String className) {
if (DEBUGCODE) {
System.out.println("Add "+bytecodeStr(theOpCode)
+", "+className);
}
int newStack = itsStackTop + stackChange(theOpCode);
if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
switch (theOpCode) {
case ByteCode.NEW :
case ByteCode.ANEWARRAY :
case ByteCode.CHECKCAST :
case ByteCode.INSTANCEOF : {
short classIndex = itsConstantPool.addClass(className);
addToCodeBuffer(theOpCode);
addToCodeInt16(classIndex);
}
break;
default :
throw new IllegalArgumentException(
"bad opcode for class reference");
}
itsStackTop = (short)newStack;
if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
if (DEBUGSTACK) {
System.out.println("After "+bytecodeStr(theOpCode)
+" stack = "+itsStackTop);
}
}
public void add(int theOpCode, String className, String fieldName,
String fieldType)
{
if (DEBUGCODE) {
System.out.println("Add "+bytecodeStr(theOpCode)
+", "+className+", "+fieldName+", "+fieldType);
}
int newStack = itsStackTop + stackChange(theOpCode);
char fieldTypeChar = fieldType.charAt(0);
int fieldSize = (fieldTypeChar == 'J' || fieldTypeChar == 'D')
? 2 : 1;
switch (theOpCode) {
case ByteCode.GETFIELD :
case ByteCode.GETSTATIC :
newStack += fieldSize;
break;
case ByteCode.PUTSTATIC :
case ByteCode.PUTFIELD :
newStack -= fieldSize;
break;
default :
throw new IllegalArgumentException(
"bad opcode for field reference");
}
if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
short fieldRefIndex = itsConstantPool.addFieldRef(className,
fieldName, fieldType);
addToCodeBuffer(theOpCode);
addToCodeInt16(fieldRefIndex);
itsStackTop = (short)newStack;
if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
if (DEBUGSTACK) {
System.out.println("After "+bytecodeStr(theOpCode)
+" stack = "+itsStackTop);
}
}
public void addInvoke(int theOpCode, String className, String methodName,
String methodType)
{
if (DEBUGCODE) {
System.out.println("Add "+bytecodeStr(theOpCode)
+", "+className+", "+methodName+", "
+methodType);
}
int parameterInfo = sizeOfParameters(methodType);
int parameterCount = parameterInfo >>> 16;
int stackDiff = (short)parameterInfo;
int newStack = itsStackTop + stackDiff;
newStack += stackChange(theOpCode); // adjusts for 'this'
if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
switch (theOpCode) {
case ByteCode.INVOKEVIRTUAL :
case ByteCode.INVOKESPECIAL :
case ByteCode.INVOKESTATIC :
case ByteCode.INVOKEINTERFACE : {
addToCodeBuffer(theOpCode);
if (theOpCode == ByteCode.INVOKEINTERFACE) {
short ifMethodRefIndex
= itsConstantPool.addInterfaceMethodRef(
className, methodName,
methodType);
addToCodeInt16(ifMethodRefIndex);
addToCodeBuffer(parameterCount + 1);
addToCodeBuffer(0);
}
else {
short methodRefIndex = itsConstantPool.addMethodRef(
className, methodName,
methodType);
addToCodeInt16(methodRefIndex);
}
}
break;
default :
throw new IllegalArgumentException(
"bad opcode for method reference");
}
itsStackTop = (short)newStack;
if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
if (DEBUGSTACK) {
System.out.println("After "+bytecodeStr(theOpCode)
+" stack = "+itsStackTop);
}
}
/**
* Generate code to load the given integer on stack.
*
* @param k the constant
*/
public void addPush(int k)
{
if ((byte)k == k) {
if (k == -1) {
add(ByteCode.ICONST_M1);
} else if (0 <= k && k <= 5) {
add((byte)(ByteCode.ICONST_0 + k));
} else {
add(ByteCode.BIPUSH, (byte)k);
}
} else if ((short)k == k) {
add(ByteCode.SIPUSH, (short)k);
} else {
addLoadConstant(k);
}
}
public void addPush(boolean k)
{
add(k ? ByteCode.ICONST_1 : ByteCode.ICONST_0);
}
/**
* Generate code to load the given long on stack.
*
* @param k the constant
*/
public void addPush(long k)
{
int ik = (int)k;
if (ik == k) {
addPush(ik);
add(ByteCode.I2L);
} else {
addLoadConstant(k);
}
}
/**
* Generate code to load the given double on stack.
*
* @param k the constant
*/
public void addPush(double k)
{
if (k == 0.0) {
// zero
add(ByteCode.DCONST_0);
if (1.0 / k < 0) {
// Negative zero
add(ByteCode.DNEG);
}
} else if (k == 1.0 || k == -1.0) {
add(ByteCode.DCONST_1);
if (k < 0) {
add(ByteCode.DNEG);
}
} else {
addLoadConstant(k);
}
}
/**
* Generate the code to leave on stack the given string even if the
* string encoding exeeds the class file limit for single string constant
*
* @param k the constant
*/
public void addPush(String k) {
int length = k.length();
int limit = itsConstantPool.getUtfEncodingLimit(k, 0, length);
if (limit == length) {
addLoadConstant(k);
return;
}
// Split string into picies fitting the UTF limit and generate code for
// StringBuilder sb = new StringBuilder(length);
// sb.append(loadConstant(piece_1));
// ...
// sb.append(loadConstant(piece_N));
// sb.toString();
final String SB = "java/lang/StringBuilder";
add(ByteCode.NEW, SB);
add(ByteCode.DUP);
addPush(length);
addInvoke(ByteCode.INVOKESPECIAL, SB, "", "(I)V");
int cursor = 0;
for (;;) {
add(ByteCode.DUP);
String s = k.substring(cursor, limit);
addLoadConstant(s);
addInvoke(ByteCode.INVOKEVIRTUAL, SB, "append",
"(Ljava/lang/String;)Ljava/lang/StringBuilder;");
add(ByteCode.POP);
if (limit == length) {
break;
}
cursor = limit;
limit = itsConstantPool.getUtfEncodingLimit(k, limit, length);
}
addInvoke(ByteCode.INVOKEVIRTUAL, SB, "toString",
"()Ljava/lang/String;");
}
/**
* Check if k fits limit on string constant size imposed by class file
* format.
*
* @param k the string constant
*/
public boolean isUnderStringSizeLimit(String k)
{
return itsConstantPool.isUnderUtfEncodingLimit(k);
}
/**
* Store integer from stack top into the given local.
*
* @param local number of local register
*/
public void addIStore(int local)
{
xop(ByteCode.ISTORE_0, ByteCode.ISTORE, local);
}
/**
* Store long from stack top into the given local.
*
* @param local number of local register
*/
public void addLStore(int local)
{
xop(ByteCode.LSTORE_0, ByteCode.LSTORE, local);
}
/**
* Store float from stack top into the given local.
*
* @param local number of local register
*/
public void addFStore(int local)
{
xop(ByteCode.FSTORE_0, ByteCode.FSTORE, local);
}
/**
* Store double from stack top into the given local.
*
* @param local number of local register
*/
public void addDStore(int local)
{
xop(ByteCode.DSTORE_0, ByteCode.DSTORE, local);
}
/**
* Store object from stack top into the given local.
*
* @param local number of local register
*/
public void addAStore(int local)
{
xop(ByteCode.ASTORE_0, ByteCode.ASTORE, local);
}
/**
* Load integer from the given local into stack.
*
* @param local number of local register
*/
public void addILoad(int local)
{
xop(ByteCode.ILOAD_0, ByteCode.ILOAD, local);
}
/**
* Load long from the given local into stack.
*
* @param local number of local register
*/
public void addLLoad(int local)
{
xop(ByteCode.LLOAD_0, ByteCode.LLOAD, local);
}
/**
* Load float from the given local into stack.
*
* @param local number of local register
*/
public void addFLoad(int local)
{
xop(ByteCode.FLOAD_0, ByteCode.FLOAD, local);
}
/**
* Load double from the given local into stack.
*
* @param local number of local register
*/
public void addDLoad(int local)
{
xop(ByteCode.DLOAD_0, ByteCode.DLOAD, local);
}
/**
* Load object from the given local into stack.
*
* @param local number of local register
*/
public void addALoad(int local)
{
xop(ByteCode.ALOAD_0, ByteCode.ALOAD, local);
}
/**
* Load "this" into stack.
*/
public void addLoadThis()
{
add(ByteCode.ALOAD_0);
}
private void xop(int shortOp, int op, int local)
{
switch (local) {
case 0:
add(shortOp);
break;
case 1:
add(shortOp + 1);
break;
case 2:
add(shortOp + 2);
break;
case 3:
add(shortOp + 3);
break;
default:
add(op, local);
}
}
public int addTableSwitch(int low, int high)
{
if (DEBUGCODE) {
System.out.println("Add "+bytecodeStr(ByteCode.TABLESWITCH)
+" "+low+" "+high);
}
if (low > high)
throw new ClassFileFormatException("Bad bounds: "+low+' '+ high);
int newStack = itsStackTop + stackChange(ByteCode.TABLESWITCH);
if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
int entryCount = high - low + 1;
int padSize = 3 & ~itsCodeBufferTop; // == 3 - itsCodeBufferTop % 4
int N = addReservedCodeSpace(1 + padSize + 4 * (1 + 2 + entryCount));
int switchStart = N;
itsCodeBuffer[N++] = (byte)ByteCode.TABLESWITCH;
while (padSize != 0) {
itsCodeBuffer[N++] = 0;
--padSize;
}
N += 4; // skip default offset
N = putInt32(low, itsCodeBuffer, N);
putInt32(high, itsCodeBuffer, N);
itsStackTop = (short)newStack;
if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
if (DEBUGSTACK) {
System.out.println("After "+bytecodeStr(ByteCode.TABLESWITCH)
+" stack = "+itsStackTop);
}
return switchStart;
}
public final void markTableSwitchDefault(int switchStart)
{
addSuperBlockStart(itsCodeBufferTop);
itsJumpFroms.put(itsCodeBufferTop, switchStart);
setTableSwitchJump(switchStart, -1, itsCodeBufferTop);
}
public final void markTableSwitchCase(int switchStart, int caseIndex)
{
addSuperBlockStart(itsCodeBufferTop);
itsJumpFroms.put(itsCodeBufferTop, switchStart);
setTableSwitchJump(switchStart, caseIndex, itsCodeBufferTop);
}
public final void markTableSwitchCase(int switchStart, int caseIndex,
int stackTop)
{
if (!(0 <= stackTop && stackTop <= itsMaxStack))
throw new IllegalArgumentException("Bad stack index: "+stackTop);
itsStackTop = (short)stackTop;
addSuperBlockStart(itsCodeBufferTop);
itsJumpFroms.put(itsCodeBufferTop, switchStart);
setTableSwitchJump(switchStart, caseIndex, itsCodeBufferTop);
}
/**
* Set a jump case for a tableswitch instruction. The jump target should
* be marked as a super block start for stack map generation.
*/
public void setTableSwitchJump(int switchStart, int caseIndex,
int jumpTarget)
{
if (!(0 <= jumpTarget && jumpTarget <= itsCodeBufferTop))
throw new IllegalArgumentException("Bad jump target: "+jumpTarget);
if (!(caseIndex >= -1))
throw new IllegalArgumentException("Bad case index: "+caseIndex);
int padSize = 3 & ~switchStart; // == 3 - switchStart % 4
int caseOffset;
if (caseIndex < 0) {
// default label
caseOffset = switchStart + 1 + padSize;
} else {
caseOffset = switchStart + 1 + padSize + 4 * (3 + caseIndex);
}
if (!(0 <= switchStart
&& switchStart <= itsCodeBufferTop - 4 * 4 - padSize - 1))
{
throw new IllegalArgumentException(
switchStart+" is outside a possible range of tableswitch"
+" in already generated code");
}
if ((0xFF & itsCodeBuffer[switchStart]) != ByteCode.TABLESWITCH) {
throw new IllegalArgumentException(
switchStart+" is not offset of tableswitch statement");
}
if (!(0 <= caseOffset && caseOffset + 4 <= itsCodeBufferTop)) {
// caseIndex >= -1 does not guarantee that caseOffset >= 0 due
// to a possible overflow.
throw new ClassFileFormatException(
"Too big case index: "+caseIndex);
}
// ALERT: perhaps check against case bounds?
putInt32(jumpTarget - switchStart, itsCodeBuffer, caseOffset);
}
public int acquireLabel()
{
int top = itsLabelTableTop;
if (itsLabelTable == null || top == itsLabelTable.length) {
if (itsLabelTable == null) {
itsLabelTable = new int[MIN_LABEL_TABLE_SIZE];
}else {
int[] tmp = new int[itsLabelTable.length * 2];
System.arraycopy(itsLabelTable, 0, tmp, 0, top);
itsLabelTable = tmp;
}
}
itsLabelTableTop = top + 1;
itsLabelTable[top] = -1;
return top | 0x80000000;
}
public void markLabel(int label)
{
if (!(label < 0))
throw new IllegalArgumentException("Bad label, no biscuit");
label &= 0x7FFFFFFF;
if (label > itsLabelTableTop)
throw new IllegalArgumentException("Bad label");
if (itsLabelTable[label] != -1) {
throw new IllegalStateException("Can only mark label once");
}
itsLabelTable[label] = itsCodeBufferTop;
}
public void markLabel(int label, short stackTop)
{
markLabel(label);
itsStackTop = stackTop;
}
public void markHandler(int theLabel) {
itsStackTop = 1;
markLabel(theLabel);
}
public int getLabelPC(int label)
{
if (!(label < 0))
throw new IllegalArgumentException("Bad label, no biscuit");
label &= 0x7FFFFFFF;
if (!(label < itsLabelTableTop))
throw new IllegalArgumentException("Bad label");
return itsLabelTable[label];
}
private void addLabelFixup(int label, int fixupSite)
{
if (!(label < 0))
throw new IllegalArgumentException("Bad label, no biscuit");
label &= 0x7FFFFFFF;
if (!(label < itsLabelTableTop))
throw new IllegalArgumentException("Bad label");
int top = itsFixupTableTop;
if (itsFixupTable == null || top == itsFixupTable.length) {
if (itsFixupTable == null) {
itsFixupTable = new long[MIN_FIXUP_TABLE_SIZE];
}else {
long[] tmp = new long[itsFixupTable.length * 2];
System.arraycopy(itsFixupTable, 0, tmp, 0, top);
itsFixupTable = tmp;
}
}
itsFixupTableTop = top + 1;
itsFixupTable[top] = ((long)label << 32) | fixupSite;
}
private void fixLabelGotos()
{
byte[] codeBuffer = itsCodeBuffer;
for (int i = 0; i < itsFixupTableTop; i++) {
long fixup = itsFixupTable[i];
int label = (int)(fixup >> 32);
int fixupSite = (int)fixup;
int pc = itsLabelTable[label];
if (pc == -1) {
// Unlocated label
throw new RuntimeException();
}
// -1 to get delta from instruction start
addSuperBlockStart(pc);
itsJumpFroms.put(pc, fixupSite - 1);
int offset = pc - (fixupSite - 1);
if ((short)offset != offset) {
throw new ClassFileFormatException
("Program too complex: too big jump offset");
}
codeBuffer[fixupSite] = (byte)(offset >> 8);
codeBuffer[fixupSite + 1] = (byte)offset;
}
itsFixupTableTop = 0;
}
/**
* Get the current offset into the code of the current method.
*
* @return an integer representing the offset
*/
public int getCurrentCodeOffset() {
return itsCodeBufferTop;
}
public short getStackTop() {
return itsStackTop;
}
public void setStackTop(short n) {
itsStackTop = n;
}
public void adjustStackTop(int delta) {
int newStack = itsStackTop + delta;
if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
itsStackTop = (short)newStack;
if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
if (DEBUGSTACK) {
System.out.println("After "+"adjustStackTop("+delta+")"
+" stack = "+itsStackTop);
}
}
private void addToCodeBuffer(int b)
{
int N = addReservedCodeSpace(1);
itsCodeBuffer[N] = (byte)b;
}
private void addToCodeInt16(int value)
{
int N = addReservedCodeSpace(2);
putInt16(value, itsCodeBuffer, N);
}
private int addReservedCodeSpace(int size)
{
if (itsCurrentMethod == null)
throw new IllegalArgumentException("No method to add to");
int oldTop = itsCodeBufferTop;
int newTop = oldTop + size;
if (newTop > itsCodeBuffer.length) {
int newSize = itsCodeBuffer.length * 2;
if (newTop > newSize) { newSize = newTop; }
byte[] tmp = new byte[newSize];
System.arraycopy(itsCodeBuffer, 0, tmp, 0, oldTop);
itsCodeBuffer = tmp;
}
itsCodeBufferTop = newTop;
return oldTop;
}
public void addExceptionHandler(int startLabel, int endLabel,
int handlerLabel, String catchClassName)
{
if ((startLabel & 0x80000000) != 0x80000000)
throw new IllegalArgumentException("Bad startLabel");
if ((endLabel & 0x80000000) != 0x80000000)
throw new IllegalArgumentException("Bad endLabel");
if ((handlerLabel & 0x80000000) != 0x80000000)
throw new IllegalArgumentException("Bad handlerLabel");
/*
* If catchClassName is null, use 0 for the catch_type_index; which
* means catch everything. (Even when the verifier has let you throw
* something other than a Throwable.)
*/
short catch_type_index = (catchClassName == null)
? 0
: itsConstantPool.addClass(catchClassName);
ExceptionTableEntry newEntry = new ExceptionTableEntry(
startLabel,
endLabel,
handlerLabel,
catch_type_index);
int N = itsExceptionTableTop;
if (N == 0) {
itsExceptionTable = new ExceptionTableEntry[ExceptionTableSize];
} else if (N == itsExceptionTable.length) {
ExceptionTableEntry[] tmp = new ExceptionTableEntry[N * 2];
System.arraycopy(itsExceptionTable, 0, tmp, 0, N);
itsExceptionTable = tmp;
}
itsExceptionTable[N] = newEntry;
itsExceptionTableTop = N + 1;
}
public void addLineNumberEntry(short lineNumber) {
if (itsCurrentMethod == null)
throw new IllegalArgumentException("No method to stop");
int N = itsLineNumberTableTop;
if (N == 0) {
itsLineNumberTable = new int[LineNumberTableSize];
} else if (N == itsLineNumberTable.length) {
int[] tmp = new int[N * 2];
System.arraycopy(itsLineNumberTable, 0, tmp, 0, N);
itsLineNumberTable = tmp;
}
itsLineNumberTable[N] = (itsCodeBufferTop << 16) + lineNumber;
itsLineNumberTableTop = N + 1;
}
/**
* A stack map table is a code attribute introduced in Java 6 that
* gives type information at key points in the method body (namely, at
* the beginning of each super block after the first). Each frame of a
* stack map table contains the state of local variable and operand stack
* for a given super block.
*/
final class StackMapTable {
StackMapTable() {
superBlocks = null;
locals = stack = null;
workList = null;
rawStackMap = null;
localsTop = 0;
stackTop = 0;
workListTop = 0;
rawStackMapTop = 0;
wide = false;
}
void generate() {
superBlocks = new SuperBlock[itsSuperBlockStartsTop];
int[] initialLocals = createInitialLocals();
for (int i = 0; i < itsSuperBlockStartsTop; i++) {
int start = itsSuperBlockStarts[i];
int end;
if (i == itsSuperBlockStartsTop - 1) {
end = itsCodeBufferTop;
} else {
end = itsSuperBlockStarts[i + 1];
}
superBlocks[i] = new SuperBlock(i, start, end, initialLocals);
}
if (DEBUGSTACKMAP) {
System.out.println("super blocks: ");
for (int i = 0;
i < superBlocks.length && superBlocks[i] != null; i++) {
System.out.println("sb " + i + ": [" +
superBlocks[i].getStart() + ", " +
superBlocks[i].getEnd() + ")");
}
}
superBlockDeps = getSuperBlockDependencies();
verify();
if (DEBUGSTACKMAP) {
System.out.println("type information:");
for (int i = 0; i < superBlocks.length; i++) {
SuperBlock sb = superBlocks[i];
System.out.println("sb " + i + ":");
TypeInfo.print(sb.getLocals(), sb.getStack(),
itsConstantPool);
}
}
}
private SuperBlock getSuperBlockFromOffset(int offset) {
for (int i = 0; i < superBlocks.length; i++) {
SuperBlock sb = superBlocks[i];
if (sb == null) {
break;
} else if (offset >= sb.getStart() && offset < sb.getEnd()) {
return sb;
}
}
throw new IllegalArgumentException("bad offset: " + offset);
}
/**
* Determine whether or not an opcode is an actual end to a super
* block. This includes any returns or unconditional jumps.
*/
private boolean isSuperBlockEnd(int opcode) {
switch (opcode) {
case ByteCode.ARETURN:
case ByteCode.FRETURN:
case ByteCode.IRETURN:
case ByteCode.LRETURN:
case ByteCode.RETURN:
case ByteCode.ATHROW:
case ByteCode.GOTO:
case ByteCode.GOTO_W:
case ByteCode.TABLESWITCH:
case ByteCode.LOOKUPSWITCH:
return true;
default:
return false;
}
}
/**
* Calculate partial dependencies for super blocks.
*
* This is used as a workaround for dead code that is generated. Only
* one dependency per super block is given.
*/
private SuperBlock[] getSuperBlockDependencies() {
SuperBlock[] deps = new SuperBlock[superBlocks.length];
for (int i = 0; i < itsExceptionTableTop; i++) {
ExceptionTableEntry ete = itsExceptionTable[i];
short startPC = (short) getLabelPC(ete.itsStartLabel);
short handlerPC = (short) getLabelPC(ete.itsHandlerLabel);
SuperBlock handlerSB = getSuperBlockFromOffset(handlerPC);
SuperBlock dep = getSuperBlockFromOffset(startPC);
deps[handlerSB.getIndex()] = dep;
}
int[] targetPCs = itsJumpFroms.getKeys();
for (int i = 0; i < targetPCs.length; i++) {
int targetPC = targetPCs[i];
int branchPC = itsJumpFroms.getInt(targetPC, -1);
SuperBlock branchSB = getSuperBlockFromOffset(branchPC);
SuperBlock targetSB = getSuperBlockFromOffset(targetPC);
deps[targetSB.getIndex()] = branchSB;
}
return deps;
}
/**
* Get the target super block of a branch instruction.
*
* @param bci the index of the branch instruction in the code buffer
*/
private SuperBlock getBranchTarget(int bci) {
int target;
if ((itsCodeBuffer[bci] & 0xFF) == ByteCode.GOTO_W) {
target = bci + getOperand(bci + 1, 4);
} else {
target = bci + (short) getOperand(bci + 1, 2);
}
return getSuperBlockFromOffset(target);
}
/**
* Determine whether or not an opcode is a conditional or unconditional
* jump.
*/
private boolean isBranch(int opcode) {
switch (opcode) {
case ByteCode.GOTO:
case ByteCode.GOTO_W:
case ByteCode.IFEQ:
case ByteCode.IFGE:
case ByteCode.IFGT:
case ByteCode.IFLE:
case ByteCode.IFLT:
case ByteCode.IFNE:
case ByteCode.IFNONNULL:
case ByteCode.IFNULL:
case ByteCode.IF_ACMPEQ:
case ByteCode.IF_ACMPNE:
case ByteCode.IF_ICMPEQ:
case ByteCode.IF_ICMPGE:
case ByteCode.IF_ICMPGT:
case ByteCode.IF_ICMPLE:
case ByteCode.IF_ICMPLT:
case ByteCode.IF_ICMPNE:
return true;
default:
return false;
}
}
private int getOperand(int offset) {
return getOperand(offset, 1);
}
/**
* Extract a logical operand from the byte code.
*
* This is used, for example, to get branch offsets.
*/
private int getOperand(int start, int size) {
int result = 0;
if (size > 4) {
throw new IllegalArgumentException("bad operand size");
}
for (int i = 0; i < size; i++) {
result = (result << 8) | (itsCodeBuffer[start + i] & 0xFF);
}
return result;
}
/**
* Calculate initial local variable and op stack types for each super
* block in the method.
*/
private void verify() {
int[] initialLocals = createInitialLocals();
superBlocks[0].merge(initialLocals, initialLocals.length,
new int[0], 0, itsConstantPool);
// Start from the top of the method and queue up block dependencies
// as they come along.
workList = new SuperBlock[] { superBlocks[0] };
workListTop = 1;
executeWorkList();
// Replace dead code with no-ops.
for (int i = 0; i < superBlocks.length; i++) {
SuperBlock sb = superBlocks[i];
if (!sb.isInitialized()) {
killSuperBlock(sb);
}
}
executeWorkList();
}
/**
* Replace the contents of a super block with no-ops.
*
* The above description is not strictly true; the last instruction is
* an athrow instruction. This technique is borrowed from ASM's
* developer guide: http://asm.ow2.org/doc/developer-guide.html#deadcode
*
* The proposed algorithm fills a block with nop, ending it with an
* athrow. The stack map generated would be empty locals with an
* exception on the stack. In theory, it shouldn't matter what the
* locals are, as long as the stack has an exception for the athrow bit.
* However, it turns out that if the code being modified falls into an
* exception handler, it causes problems. Therefore, if it does, then
* we steal the locals from the exception block.
*
* If the block itself is an exception handler, we remove it from the
* exception table to simplify block dependencies.
*/
private void killSuperBlock(SuperBlock sb) {
int[] locals = new int[0];
int[] stack = new int[] { TypeInfo.OBJECT("java/lang/Throwable",
itsConstantPool) };
// If the super block is handled by any exception handler, use its
// locals as the killed block's locals. Ignore uninitialized
// handlers, because they will also be killed and removed from the
// exception table.
for (int i = 0; i < itsExceptionTableTop; i++) {
ExceptionTableEntry ete = itsExceptionTable[i];
int eteStart = getLabelPC(ete.itsStartLabel);
int eteEnd = getLabelPC(ete.itsEndLabel);
int handlerPC = getLabelPC(ete.itsHandlerLabel);
SuperBlock handlerSB = getSuperBlockFromOffset(handlerPC);
if ((sb.getStart() > eteStart && sb.getStart() < eteEnd) ||
(eteStart > sb.getStart() && eteStart < sb.getEnd()) &&
handlerSB.isInitialized()) {
locals = handlerSB.getLocals();
break;
}
}
// Remove any exception table entry whose handler is the killed
// block. This removes block dependencies to make stack maps for
// dead blocks easier to create.
for (int i = 0; i < itsExceptionTableTop; i++) {
ExceptionTableEntry ete = itsExceptionTable[i];
int eteStart = getLabelPC(ete.itsStartLabel);
if (eteStart == sb.getStart()) {
for (int j = i + 1; j < itsExceptionTableTop; j++) {
itsExceptionTable[j - 1] = itsExceptionTable[j];
}
itsExceptionTableTop--;
i--;
}
}
sb.merge(locals, locals.length, stack, stack.length,
itsConstantPool);
int end = sb.getEnd() - 1;
itsCodeBuffer[end] = (byte) ByteCode.ATHROW;
for (int bci = sb.getStart(); bci < end; bci++) {
itsCodeBuffer[bci] = (byte) ByteCode.NOP;
}
}
private void executeWorkList() {
while (workListTop > 0) {
SuperBlock work = workList[--workListTop];
work.setInQueue(false);
locals = work.getLocals();
stack = work.getStack();
localsTop = locals.length;
stackTop = stack.length;
executeBlock(work);
}
}
/**
* Simulate the local variable and op stack for a super block.
*/
private void executeBlock(SuperBlock work) {
int bc = 0;
int next = 0;
if (DEBUGSTACKMAP) {
System.out.println("working on sb " + work.getIndex());
System.out.println("initial type state:");
TypeInfo.print(locals, localsTop, stack, stackTop,
itsConstantPool);
}
for (int bci = work.getStart(); bci < work.getEnd(); bci += next) {
bc = itsCodeBuffer[bci] & 0xFF;
next = execute(bci);
// If we have a branch to some super block, we need to merge
// the current state of the local table and op stack with what's
// currently stored as the initial state of the super block. If
// something actually changed, we need to add it to the work
// list.
if (isBranch(bc)) {
SuperBlock targetSB = getBranchTarget(bci);
if (DEBUGSTACKMAP) {
System.out.println("sb " + work.getIndex() +
" points to sb " +
targetSB.getIndex() +
" (offset " + bci + " -> " +
targetSB.getStart() + ")");
System.out.println("type state at " + bci + ":");
TypeInfo.print(locals, localsTop, stack, stackTop,
itsConstantPool);
}
flowInto(targetSB);
if (DEBUGSTACKMAP) {
System.out.println("type state of " + targetSB +
" after merge:");
TypeInfo.print(targetSB.getLocals(),
targetSB.getStack(), itsConstantPool);
}
} else if (bc == ByteCode.TABLESWITCH) {
int switchStart = bci + 1 + (3 & ~bci); // 3 - bci % 4
int defaultOffset = getOperand(switchStart, 4);
SuperBlock targetSB =
getSuperBlockFromOffset(bci + defaultOffset);
if (DEBUGSTACK) {
System.out.println("merging sb " + work.getIndex() +
" with sb " + targetSB.getIndex());
}
flowInto(targetSB);
int low = getOperand(switchStart + 4, 4);
int high = getOperand(switchStart + 8, 4);
int numCases = high - low + 1;
int caseBase = switchStart + 12;
for (int i = 0; i < numCases; i++) {
int label = bci + getOperand(caseBase + 4 * i, 4);
targetSB = getSuperBlockFromOffset(label);
if (DEBUGSTACKMAP) {
System.out.println("merging sb " +
work.getIndex() + " with sb " +
targetSB.getIndex());
}
flowInto(targetSB);
}
}
for (int i = 0; i < itsExceptionTableTop; i++) {
ExceptionTableEntry ete = itsExceptionTable[i];
short startPC = (short) getLabelPC(ete.itsStartLabel);
short endPC = (short) getLabelPC(ete.itsEndLabel);
if (bci < startPC || bci >= endPC) {
continue;
}
short handlerPC =
(short) getLabelPC(ete.itsHandlerLabel);
SuperBlock sb = getSuperBlockFromOffset(handlerPC);
int exceptionType;
if (ete.itsCatchType == 0) {
exceptionType = TypeInfo.OBJECT(
itsConstantPool.addClass("java/lang/Throwable"));
} else {
exceptionType = TypeInfo.OBJECT(ete.itsCatchType);
}
sb.merge(locals, localsTop, new int[] { exceptionType }, 1,
itsConstantPool);
addToWorkList(sb);
}
}
if (DEBUGSTACKMAP) {
System.out.println("end of sb " + work.getIndex() + ":");
TypeInfo.print(locals, localsTop, stack, stackTop,
itsConstantPool);
}
// Check the last instruction to see if it is a true end of a
// super block (ie., if the instruction is a return). If it
// isn't, we need to continue processing the next chunk.
if (!isSuperBlockEnd(bc)) {
int nextIndex = work.getIndex() + 1;
if (nextIndex < superBlocks.length) {
if (DEBUGSTACKMAP) {
System.out.println("continuing from sb " +
work.getIndex() + " into sb " +
nextIndex);
}
flowInto(superBlocks[nextIndex]);
}
}
}
/**
* Perform a merge of type state and add the super block to the work
* list if the merge changed anything.
*/
private void flowInto(SuperBlock sb) {
if (sb.merge(locals, localsTop, stack, stackTop, itsConstantPool)) {
addToWorkList(sb);
}
}
private void addToWorkList(SuperBlock sb) {
if (!sb.isInQueue()) {
sb.setInQueue(true);
sb.setInitialized(true);
if (workListTop == workList.length) {
SuperBlock[] tmp = new SuperBlock[workListTop * 2];
System.arraycopy(workList, 0, tmp, 0, workListTop);
workList = tmp;
}
workList[workListTop++] = sb;
}
}
/**
* Execute a single byte code instruction.
*
* @param bci the index of the byte code instruction to execute
* @return the length of the byte code instruction
*/
private int execute(int bci) {
int bc = itsCodeBuffer[bci] & 0xFF;
int type, type2, index;
int length = 0;
long lType, lType2;
String className;
switch (bc) {
case ByteCode.NOP:
case ByteCode.IINC:
case ByteCode.GOTO:
case ByteCode.GOTO_W:
// No change
break;
case ByteCode.CHECKCAST:
pop();
push(TypeInfo.OBJECT(getOperand(bci + 1, 2)));
break;
case ByteCode.IASTORE: // pop; pop; pop
case ByteCode.LASTORE:
case ByteCode.FASTORE:
case ByteCode.DASTORE:
case ByteCode.AASTORE:
case ByteCode.BASTORE:
case ByteCode.CASTORE:
case ByteCode.SASTORE:
pop();
case ByteCode.PUTFIELD: // pop; pop
case ByteCode.IF_ICMPEQ:
case ByteCode.IF_ICMPNE:
case ByteCode.IF_ICMPLT:
case ByteCode.IF_ICMPGE:
case ByteCode.IF_ICMPGT:
case ByteCode.IF_ICMPLE:
case ByteCode.IF_ACMPEQ:
case ByteCode.IF_ACMPNE:
pop();
case ByteCode.IFEQ: // pop
case ByteCode.IFNE:
case ByteCode.IFLT:
case ByteCode.IFGE:
case ByteCode.IFGT:
case ByteCode.IFLE:
case ByteCode.IFNULL:
case ByteCode.IFNONNULL:
case ByteCode.POP:
case ByteCode.MONITORENTER:
case ByteCode.MONITOREXIT:
case ByteCode.PUTSTATIC:
pop();
break;
case ByteCode.POP2:
pop2();
break;
case ByteCode.ACONST_NULL:
push(TypeInfo.NULL);
break;
case ByteCode.IALOAD: // pop; pop; push(INTEGER)
case ByteCode.BALOAD:
case ByteCode.CALOAD:
case ByteCode.SALOAD:
case ByteCode.IADD:
case ByteCode.ISUB:
case ByteCode.IMUL:
case ByteCode.IDIV:
case ByteCode.IREM:
case ByteCode.ISHL:
case ByteCode.ISHR:
case ByteCode.IUSHR:
case ByteCode.IAND:
case ByteCode.IOR:
case ByteCode.IXOR:
case ByteCode.LCMP:
case ByteCode.FCMPL:
case ByteCode.FCMPG:
case ByteCode.DCMPL:
case ByteCode.DCMPG:
pop();
case ByteCode.INEG: // pop; push(INTEGER)
case ByteCode.L2I:
case ByteCode.F2I:
case ByteCode.D2I:
case ByteCode.I2B:
case ByteCode.I2C:
case ByteCode.I2S:
case ByteCode.ARRAYLENGTH:
case ByteCode.INSTANCEOF:
pop();
case ByteCode.ICONST_M1: // push(INTEGER)
case ByteCode.ICONST_0:
case ByteCode.ICONST_1:
case ByteCode.ICONST_2:
case ByteCode.ICONST_3:
case ByteCode.ICONST_4:
case ByteCode.ICONST_5:
case ByteCode.ILOAD:
case ByteCode.ILOAD_0:
case ByteCode.ILOAD_1:
case ByteCode.ILOAD_2:
case ByteCode.ILOAD_3:
case ByteCode.BIPUSH:
case ByteCode.SIPUSH:
push(TypeInfo.INTEGER);
break;
case ByteCode.LALOAD: // pop; pop; push(LONG)
case ByteCode.LADD:
case ByteCode.LSUB:
case ByteCode.LMUL:
case ByteCode.LDIV:
case ByteCode.LREM:
case ByteCode.LSHL:
case ByteCode.LSHR:
case ByteCode.LUSHR:
case ByteCode.LAND:
case ByteCode.LOR:
case ByteCode.LXOR:
pop();
case ByteCode.LNEG: // pop; push(LONG)
case ByteCode.I2L:
case ByteCode.F2L:
case ByteCode.D2L:
pop();
case ByteCode.LCONST_0: // push(LONG)
case ByteCode.LCONST_1:
case ByteCode.LLOAD:
case ByteCode.LLOAD_0:
case ByteCode.LLOAD_1:
case ByteCode.LLOAD_2:
case ByteCode.LLOAD_3:
push(TypeInfo.LONG);
break;
case ByteCode.FALOAD: // pop; pop; push(FLOAT)
case ByteCode.FADD:
case ByteCode.FSUB:
case ByteCode.FMUL:
case ByteCode.FDIV:
case ByteCode.FREM:
pop();
case ByteCode.FNEG: // pop; push(FLOAT)
case ByteCode.I2F:
case ByteCode.L2F:
case ByteCode.D2F:
pop();
case ByteCode.FCONST_0: // push(FLOAT)
case ByteCode.FCONST_1:
case ByteCode.FCONST_2:
case ByteCode.FLOAD:
case ByteCode.FLOAD_0:
case ByteCode.FLOAD_1:
case ByteCode.FLOAD_2:
case ByteCode.FLOAD_3:
push(TypeInfo.FLOAT);
break;
case ByteCode.DALOAD: // pop; pop; push(DOUBLE)
case ByteCode.DADD:
case ByteCode.DSUB:
case ByteCode.DMUL:
case ByteCode.DDIV:
case ByteCode.DREM:
pop();
case ByteCode.DNEG: // pop; push(DOUBLE)
case ByteCode.I2D:
case ByteCode.L2D:
case ByteCode.F2D:
pop();
case ByteCode.DCONST_0: // push(DOUBLE)
case ByteCode.DCONST_1:
case ByteCode.DLOAD:
case ByteCode.DLOAD_0:
case ByteCode.DLOAD_1:
case ByteCode.DLOAD_2:
case ByteCode.DLOAD_3:
push(TypeInfo.DOUBLE);
break;
case ByteCode.ISTORE:
executeStore(getOperand(bci + 1, wide ? 2 : 1), TypeInfo.INTEGER);
break;
case ByteCode.ISTORE_0:
case ByteCode.ISTORE_1:
case ByteCode.ISTORE_2:
case ByteCode.ISTORE_3:
executeStore(bc - ByteCode.ISTORE_0, TypeInfo.INTEGER);
break;
case ByteCode.LSTORE:
executeStore(getOperand(bci + 1, wide ? 2 : 1), TypeInfo.LONG);
break;
case ByteCode.LSTORE_0:
case ByteCode.LSTORE_1:
case ByteCode.LSTORE_2:
case ByteCode.LSTORE_3:
executeStore(bc - ByteCode.LSTORE_0, TypeInfo.LONG);
break;
case ByteCode.FSTORE:
executeStore(getOperand(bci + 1, wide ? 2 : 1), TypeInfo.FLOAT);
break;
case ByteCode.FSTORE_0:
case ByteCode.FSTORE_1:
case ByteCode.FSTORE_2:
case ByteCode.FSTORE_3:
executeStore(bc - ByteCode.FSTORE_0, TypeInfo.FLOAT);
break;
case ByteCode.DSTORE:
executeStore(getOperand(bci + 1, wide ? 2 : 1), TypeInfo.DOUBLE);
break;
case ByteCode.DSTORE_0:
case ByteCode.DSTORE_1:
case ByteCode.DSTORE_2:
case ByteCode.DSTORE_3:
executeStore(bc - ByteCode.DSTORE_0, TypeInfo.DOUBLE);
break;
case ByteCode.ALOAD:
executeALoad(getOperand(bci + 1, wide ? 2 : 1));
break;
case ByteCode.ALOAD_0:
case ByteCode.ALOAD_1:
case ByteCode.ALOAD_2:
case ByteCode.ALOAD_3:
executeALoad(bc - ByteCode.ALOAD_0);
break;
case ByteCode.ASTORE:
executeAStore(getOperand(bci + 1, wide ? 2 : 1));
break;
case ByteCode.ASTORE_0:
case ByteCode.ASTORE_1:
case ByteCode.ASTORE_2:
case ByteCode.ASTORE_3:
executeAStore(bc - ByteCode.ASTORE_0);
break;
case ByteCode.IRETURN:
case ByteCode.LRETURN:
case ByteCode.FRETURN:
case ByteCode.DRETURN:
case ByteCode.ARETURN:
case ByteCode.RETURN:
clearStack();
break;
case ByteCode.ATHROW:
type = pop();
clearStack();
push(type);
break;
case ByteCode.SWAP:
type = pop();
type2 = pop();
push(type);
push(type2);
break;
case ByteCode.LDC:
case ByteCode.LDC_W:
case ByteCode.LDC2_W:
if (bc == ByteCode.LDC) {
index = getOperand(bci + 1);
} else {
index = getOperand(bci + 1, 2);
}
byte constType = itsConstantPool.getConstantType(index);
switch (constType) {
case ConstantPool.CONSTANT_Double:
push(TypeInfo.DOUBLE);
break;
case ConstantPool.CONSTANT_Float:
push(TypeInfo.FLOAT);
break;
case ConstantPool.CONSTANT_Long:
push(TypeInfo.LONG);
break;
case ConstantPool.CONSTANT_Integer:
push(TypeInfo.INTEGER);
break;
case ConstantPool.CONSTANT_String:
push(TypeInfo.OBJECT("java/lang/String",
itsConstantPool));
break;
default:
throw new IllegalArgumentException(
"bad const type " + constType);
}
break;
case ByteCode.NEW:
push(TypeInfo.UNINITIALIZED_VARIABLE(bci));
break;
case ByteCode.NEWARRAY:
pop();
char componentType =
arrayTypeToName(itsCodeBuffer[bci + 1]);
index = itsConstantPool.addClass("[" + componentType);
push(TypeInfo.OBJECT((short) index));
break;
case ByteCode.ANEWARRAY:
index = getOperand(bci + 1, 2);
className = (String) itsConstantPool.getConstantData(index);
pop();
push(TypeInfo.OBJECT("[L" + className + ';',
itsConstantPool));
break;
case ByteCode.INVOKEVIRTUAL:
case ByteCode.INVOKESPECIAL:
case ByteCode.INVOKESTATIC:
case ByteCode.INVOKEINTERFACE:
index = getOperand(bci + 1, 2);
FieldOrMethodRef m = (FieldOrMethodRef)
itsConstantPool.getConstantData(index);
String methodType = m.getType();
String methodName = m.getName();
int parameterCount = sizeOfParameters(methodType) >>> 16;
for (int i = 0; i < parameterCount; i++) {
pop();
}
if (bc != ByteCode.INVOKESTATIC) {
int instType = pop();
int tag = TypeInfo.getTag(instType);
if (tag == TypeInfo.UNINITIALIZED_VARIABLE(0) ||
tag == TypeInfo.UNINITIALIZED_THIS) {
if ("".equals(methodName)) {
int newType =
TypeInfo.OBJECT(itsThisClassIndex);
initializeTypeInfo(instType, newType);
} else {
throw new IllegalStateException("bad instance");
}
}
}
int rParen = methodType.indexOf(')');
String returnType = methodType.substring(rParen + 1);
returnType = descriptorToInternalName(returnType);
if (!returnType.equals("V")) {
push(TypeInfo.fromType(returnType, itsConstantPool));
}
break;
case ByteCode.GETFIELD:
pop();
case ByteCode.GETSTATIC:
index = getOperand(bci + 1, 2);
FieldOrMethodRef f = (FieldOrMethodRef)
itsConstantPool.getConstantData(index);
String fieldType = descriptorToInternalName(f.getType());
push(TypeInfo.fromType(fieldType, itsConstantPool));
break;
case ByteCode.DUP:
type = pop();
push(type);
push(type);
break;
case ByteCode.DUP_X1:
type = pop();
type2 = pop();
push(type);
push(type2);
push(type);
break;
case ByteCode.DUP_X2:
type = pop();
lType = pop2();
push(type);
push2(lType);
push(type);
break;
case ByteCode.DUP2:
lType = pop2();
push2(lType);
push2(lType);
break;
case ByteCode.DUP2_X1:
lType = pop2();
type = pop();
push2(lType);
push(type);
push2(lType);
break;
case ByteCode.DUP2_X2:
lType = pop2();
lType2 = pop2();
push2(lType);
push2(lType2);
push2(lType);
break;
case ByteCode.TABLESWITCH:
int switchStart = bci + 1 + (3 & ~bci);
int low = getOperand(switchStart + 4, 4);
int high = getOperand(switchStart + 8, 4);
length = 4 * (high - low + 4) + switchStart - bci;
pop();
break;
case ByteCode.AALOAD:
pop();
int typeIndex = pop() >>> 8;
className =
(String) itsConstantPool.getConstantData(typeIndex);
String arrayType = className;
if (arrayType.charAt(0) != '[') {
throw new IllegalStateException("bad array type");
}
String elementDesc = arrayType.substring(1);
String elementType = descriptorToInternalName(elementDesc);
typeIndex = itsConstantPool.addClass(elementType);
push(TypeInfo.OBJECT(typeIndex));
break;
case ByteCode.WIDE:
// Alters behaviour of next instruction
wide = true;
break;
case ByteCode.MULTIANEWARRAY:
case ByteCode.LOOKUPSWITCH:
// Currently not used in any part of Rhino, so ignore it
case ByteCode.JSR: // TODO: JSR is deprecated
case ByteCode.RET:
case ByteCode.JSR_W:
default:
throw new IllegalArgumentException("bad opcode: " + bc);
}
if (length == 0) {
length = opcodeLength(bc, wide);
}
if (wide && bc != ByteCode.WIDE) {
wide = false;
}
return length;
}
private void executeALoad(int localIndex) {
int type = getLocal(localIndex);
int tag = TypeInfo.getTag(type);
if (tag == TypeInfo.OBJECT_TAG ||
tag == TypeInfo.UNINITIALIZED_THIS ||
tag == TypeInfo.UNINITIALIZED_VAR_TAG ||
tag == TypeInfo.NULL) {
push(type);
} else {
throw new IllegalStateException("bad local variable type: " +
type + " at index: " +
localIndex);
}
}
private void executeAStore(int localIndex) {
setLocal(localIndex, pop());
}
private void executeStore(int localIndex, int typeInfo) {
pop();
setLocal(localIndex, typeInfo);
}
/**
* Change an UNINITIALIZED_OBJECT or UNINITIALIZED_THIS to the proper
* type of the object. This occurs when the proper constructor is
* invoked.
*/
private void initializeTypeInfo(int prevType, int newType) {
initializeTypeInfo(prevType, newType, locals, localsTop);
initializeTypeInfo(prevType, newType, stack, stackTop);
}
private void initializeTypeInfo(int prevType, int newType, int[] data,
int dataTop) {
for (int i = 0; i < dataTop; i++) {
if (data[i] == prevType) {
data[i] = newType;
}
}
}
private int getLocal(int localIndex) {
if (localIndex < localsTop) {
return locals[localIndex];
} else {
return TypeInfo.TOP;
}
}
private void setLocal(int localIndex, int typeInfo) {
if (localIndex >= localsTop) {
int[] tmp = new int[localIndex + 1];
System.arraycopy(locals, 0, tmp, 0, localsTop);
locals = tmp;
localsTop = localIndex + 1;
}
locals[localIndex] = typeInfo;
}
private void push(int typeInfo) {
if (stackTop == stack.length) {
int[] tmp = new int[Math.max(stackTop * 2, 4)];
System.arraycopy(stack, 0, tmp, 0, stackTop);
stack = tmp;
}
stack[stackTop++] = typeInfo;
}
private int pop() {
return stack[--stackTop];
}
/**
* Push two words onto the op stack.
*
* This is only meant to be used as a complement to pop2(), and both
* methods are helpers for the more complex DUP operations.
*/
private void push2(long typeInfo) {
push((int) (typeInfo & 0xFFFFFF));
typeInfo >>>= 32;
if (typeInfo != 0) {
push((int) (typeInfo & 0xFFFFFF));
}
}
/**
* Pop two words from the op stack.
*
* If the top of the stack is a DOUBLE or LONG, then the bottom 32 bits
* reflects the appropriate type and the top 32 bits are 0. Otherwise,
* the top 32 bits are the first word on the stack and the lower 32
* bits are the second word on the stack.
*/
private long pop2() {
long type = pop();
if (TypeInfo.isTwoWords((int) type)) {
return type;
} else {
return type << 32 | (pop() & 0xFFFFFF);
}
}
private void clearStack() {
stackTop = 0;
}
/**
* Compute the output size of the stack map table.
*
* Because this would share much in common with actual writing of the
* stack map table, we instead just write the stack map table to a
* buffer and return the size from it. The buffer is later used in
* the actual writing of bytecode.
*/
int computeWriteSize() {
// Allocate a buffer that can handle the worst case size of the
// stack map to prevent lots of reallocations.
int writeSize = getWorstCaseWriteSize();
rawStackMap = new byte[writeSize];
computeRawStackMap();
return rawStackMapTop + 2;
}
int write(byte[] data, int offset) {
offset = putInt32(rawStackMapTop + 2, data, offset);
offset = putInt16(superBlocks.length - 1, data, offset);
System.arraycopy(rawStackMap, 0, data, offset, rawStackMapTop);
return offset + rawStackMapTop;
}
/**
* Compute a space-optimal stack map table.
*/
private void computeRawStackMap() {
SuperBlock prev = superBlocks[0];
int[] prevLocals = prev.getTrimmedLocals();
int prevOffset = -1;
for (int i = 1; i < superBlocks.length; i++) {
SuperBlock current = superBlocks[i];
int[] currentLocals = current.getTrimmedLocals();
int[] currentStack = current.getStack();
int offsetDelta = current.getStart() - prevOffset - 1;
if (currentStack.length == 0) {
int last = prevLocals.length > currentLocals.length ?
currentLocals.length : prevLocals.length;
int delta = Math.abs(prevLocals.length -
currentLocals.length);
int j;
// Compare locals until one is different or the end of a
// local variable array is reached
for (j = 0; j < last; j++) {
if (prevLocals[j] != currentLocals[j]) {
break;
}
}
if (j == currentLocals.length && delta == 0) {
// All of the compared locals are equal and the local
// arrays are of equal size
writeSameFrame(currentLocals, offsetDelta);
} else if (j == currentLocals.length && delta <= 3) {
// All of the compared locals are equal and the current
// frame has less locals than the previous frame
writeChopFrame(delta, offsetDelta);
} else if (j == prevLocals.length && delta <= 3) {
// All of the compared locals are equal and the current
// frame has more locals than the previous frame
writeAppendFrame(currentLocals, delta, offsetDelta);
} else {
// Not all locals were compared were equal, so a full
// frame is necessary
writeFullFrame(currentLocals, currentStack,
offsetDelta);
}
} else if (currentStack.length == 1) {
if (Arrays.equals(prevLocals, currentLocals)) {
writeSameLocalsOneStackItemFrame(currentLocals,
currentStack,
offsetDelta);
} else {
// Output a full frame, since no other frame types have
// one operand stack item.
writeFullFrame(currentLocals, currentStack,
offsetDelta);
}
} else {
// Any stack map frame that has more than one operand stack
// item has to be a full frame. All other frame types have
// at most one item on the stack.
writeFullFrame(currentLocals, currentStack, offsetDelta);
}
prev = current;
prevLocals = currentLocals;
prevOffset = current.getStart();
}
}
/**
* Get the worst case write size of the stack map table.
*
* This computes how much full frames would take, if each full frame
* contained the maximum number of locals and stack operands, and each
* verification type was 3 bytes.
*/
private int getWorstCaseWriteSize() {
return (superBlocks.length - 1) * (7 + itsMaxLocals * 3 +
itsMaxStack * 3);
}
private void writeSameFrame(int[] locals, int offsetDelta) {
if (offsetDelta <= 63) {
// Output a same_frame frame. Despite the name,
// the operand stack may differ, but the current
// operand stack must be empty.
rawStackMap[rawStackMapTop++] = (byte) offsetDelta;
} else {
// Output a same_frame_extended frame. Similar to
// the above, except with a larger offset delta.
rawStackMap[rawStackMapTop++] = (byte) 251;
rawStackMapTop = putInt16(offsetDelta, rawStackMap,
rawStackMapTop);
}
}
private void writeSameLocalsOneStackItemFrame(int[] locals,
int[] stack,
int offsetDelta) {
if (offsetDelta <= 63) {
// Output a same_locals_1_stack_item frame. Similar
// to same_frame, only with one item on the operand
// stack instead of zero.
rawStackMap[rawStackMapTop++] = (byte) (64 + offsetDelta);
} else {
// Output a same_locals_1_stack_item_extended frame.
// Similar to same_frame_extended, only with one
// item on the operand stack instead of zero.
rawStackMap[rawStackMapTop++] = (byte) 247;
rawStackMapTop = putInt16(offsetDelta, rawStackMap,
rawStackMapTop);
}
writeType(stack[0]);
}
private void writeFullFrame(int[] locals, int[] stack,
int offsetDelta) {
rawStackMap[rawStackMapTop++] = (byte) 255;
rawStackMapTop = putInt16(offsetDelta, rawStackMap, rawStackMapTop);
rawStackMapTop = putInt16(locals.length, rawStackMap,
rawStackMapTop);
rawStackMapTop = writeTypes(locals);
rawStackMapTop = putInt16(stack.length, rawStackMap,
rawStackMapTop);
rawStackMapTop = writeTypes(stack);
}
private void writeAppendFrame(int[] locals, int localsDelta,
int offsetDelta) {
int start = locals.length - localsDelta;
rawStackMap[rawStackMapTop++] = (byte) (251 + localsDelta);
rawStackMapTop = putInt16(offsetDelta, rawStackMap, rawStackMapTop);
rawStackMapTop = writeTypes(locals, start);
}
private void writeChopFrame(int localsDelta, int offsetDelta) {
rawStackMap[rawStackMapTop++] = (byte) (251 - localsDelta);
rawStackMapTop = putInt16(offsetDelta, rawStackMap, rawStackMapTop);
}
private int writeTypes(int[] types) {
return writeTypes(types, 0);
}
private int writeTypes(int[] types, int start) {
int startOffset = rawStackMapTop;
for (int i = start; i < types.length; i++) {
rawStackMapTop = writeType(types[i]);
}
return rawStackMapTop;
}
private int writeType(int type) {
int tag = type & 0xFF;
rawStackMap[rawStackMapTop++] = (byte) tag;
if (tag == TypeInfo.OBJECT_TAG ||
tag == TypeInfo.UNINITIALIZED_VAR_TAG) {
rawStackMapTop = putInt16(type >>> 8, rawStackMap,
rawStackMapTop);
}
return rawStackMapTop;
}
// Intermediate operand stack and local variable state. During
// execution of a block, these are initialized to copies of the initial
// block type state and are modified by the actual stack/local
// emulation.
private int[] locals;
private int localsTop;
private int[] stack;
private int stackTop;
private SuperBlock[] workList;
private int workListTop;
private SuperBlock[] superBlocks;
private SuperBlock[] superBlockDeps;
private byte[] rawStackMap;
private int rawStackMapTop;
private boolean wide;
static final boolean DEBUGSTACKMAP = false;
}
/**
* Convert a newarray operand into an internal type.
*/
private static char arrayTypeToName(int type) {
switch (type) {
case ByteCode.T_BOOLEAN:
return 'Z';
case ByteCode.T_CHAR:
return 'C';
case ByteCode.T_FLOAT:
return 'F';
case ByteCode.T_DOUBLE:
return 'D';
case ByteCode.T_BYTE:
return 'B';
case ByteCode.T_SHORT:
return 'S';
case ByteCode.T_INT:
return 'I';
case ByteCode.T_LONG:
return 'J';
default:
throw new IllegalArgumentException("bad operand");
}
}
/**
* Convert a class descriptor into an internal name.
*
* For example, descriptor Ljava/lang/Object; becomes java/lang/Object.
*/
private static String classDescriptorToInternalName(String descriptor) {
return descriptor.substring(1, descriptor.length() - 1);
}
/**
* Convert a non-method type descriptor into an internal type.
*
* @param descriptor the simple type descriptor to convert
*/
private static String descriptorToInternalName(String descriptor) {
switch (descriptor.charAt(0)) {
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'Z':
case 'V':
case '[':
return descriptor;
case 'L':
return classDescriptorToInternalName(descriptor);
default:
throw new IllegalArgumentException("bad descriptor:" +
descriptor);
}
}
/**
* Compute the initial local variable array for the current method.
*
* Creates an array of the size of the method's max locals, regardless of
* the number of parameters in the method.
*/
private int[] createInitialLocals() {
int[] initialLocals = new int[itsMaxLocals];
int localsTop = 0;
// Instance methods require the first local variable in the array
// to be "this". However, if the method being created is a
// constructor, aka the method is , then the type of "this"
// should be StackMapTable.UNINITIALIZED_THIS
if ((itsCurrentMethod.getFlags() & ACC_STATIC) == 0) {
if ("".equals(itsCurrentMethod.getName())) {
initialLocals[localsTop++] = TypeInfo.UNINITIALIZED_THIS;
} else {
initialLocals[localsTop++] = TypeInfo.OBJECT(itsThisClassIndex);
}
}
// No error checking should be necessary, sizeOfParameters does this
String type = itsCurrentMethod.getType();
int lParenIndex = type.indexOf('(');
int rParenIndex = type.indexOf(')');
if (lParenIndex != 0 || rParenIndex < 0) {
throw new IllegalArgumentException("bad method type");
}
int start = lParenIndex + 1;
StringBuilder paramType = new StringBuilder();
while (start < rParenIndex) {
switch (type.charAt(start)) {
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'Z':
paramType.append(type.charAt(start));
++start;
break;
case 'L':
int end = type.indexOf(';', start) + 1;
String name = type.substring(start, end);
paramType.append(name);
start = end;
break;
case '[':
paramType.append('[');
++start;
continue;
}
String internalType =
descriptorToInternalName(paramType.toString());
int typeInfo = TypeInfo.fromType(internalType, itsConstantPool);
initialLocals[localsTop++] = typeInfo;
if (TypeInfo.isTwoWords(typeInfo)) {
localsTop++;
}
paramType.setLength(0);
}
return initialLocals;
}
/**
* Write the class file to the OutputStream.
*
* @param oStream the stream to write to
* @throws IOException if writing to the stream produces an exception
*/
public void write(OutputStream oStream)
throws IOException
{
byte[] array = toByteArray();
oStream.write(array);
}
private int getWriteSize()
{
int size = 0;
if (itsSourceFileNameIndex != 0) {
itsConstantPool.addUtf8("SourceFile");
}
size += 8; //writeLong(FileHeaderConstant);
size += itsConstantPool.getWriteSize();
size += 2; //writeShort(itsFlags);
size += 2; //writeShort(itsThisClassIndex);
size += 2; //writeShort(itsSuperClassIndex);
size += 2; //writeShort(itsInterfaces.size());
size += 2 * itsInterfaces.size();
size += 2; //writeShort(itsFields.size());
for (int i = 0; i < itsFields.size(); i++) {
size += ((ClassFileField)(itsFields.get(i))).getWriteSize();
}
size += 2; //writeShort(itsMethods.size());
for (int i = 0; i < itsMethods.size(); i++) {
size += ((ClassFileMethod)(itsMethods.get(i))).getWriteSize();
}
if (itsSourceFileNameIndex != 0) {
size += 2; //writeShort(1); attributes count
size += 2; //writeShort(sourceFileAttributeNameIndex);
size += 4; //writeInt(2);
size += 2; //writeShort(itsSourceFileNameIndex);
}else {
size += 2; //out.writeShort(0); no attributes
}
return size;
}
/**
* Get the class file as array of bytesto the OutputStream.
*/
public byte[] toByteArray()
{
int dataSize = getWriteSize();
byte[] data = new byte[dataSize];
int offset = 0;
short sourceFileAttributeNameIndex = 0;
if (itsSourceFileNameIndex != 0) {
sourceFileAttributeNameIndex = itsConstantPool.addUtf8(
"SourceFile");
}
offset = putInt32(FileHeaderConstant, data, offset);
offset = putInt16(MinorVersion, data, offset);
offset = putInt16(MajorVersion, data, offset);
offset = itsConstantPool.write(data, offset);
offset = putInt16(itsFlags, data, offset);
offset = putInt16(itsThisClassIndex, data, offset);
offset = putInt16(itsSuperClassIndex, data, offset);
offset = putInt16(itsInterfaces.size(), data, offset);
for (int i = 0; i < itsInterfaces.size(); i++) {
int interfaceIndex = ((Short)(itsInterfaces.get(i))).shortValue();
offset = putInt16(interfaceIndex, data, offset);
}
offset = putInt16(itsFields.size(), data, offset);
for (int i = 0; i < itsFields.size(); i++) {
ClassFileField field = (ClassFileField)itsFields.get(i);
offset = field.write(data, offset);
}
offset = putInt16(itsMethods.size(), data, offset);
for (int i = 0; i < itsMethods.size(); i++) {
ClassFileMethod method = (ClassFileMethod)itsMethods.get(i);
offset = method.write(data, offset);
}
if (itsSourceFileNameIndex != 0) {
offset = putInt16(1, data, offset); // attributes count
offset = putInt16(sourceFileAttributeNameIndex, data, offset);
offset = putInt32(2, data, offset);
offset = putInt16(itsSourceFileNameIndex, data, offset);
} else {
offset = putInt16(0, data, offset); // no attributes
}
if (offset != dataSize) {
// Check getWriteSize is consistent with write!
throw new RuntimeException();
}
return data;
}
static int putInt64(long value, byte[] array, int offset)
{
offset = putInt32((int)(value >>> 32), array, offset);
return putInt32((int)value, array, offset);
}
private static void badStack(int value)
{
String s;
if (value < 0) { s = "Stack underflow: "+value; }
else { s = "Too big stack: "+value; }
throw new IllegalStateException(s);
}
/*
Really weird. Returns an int with # parameters in hi 16 bits, and
stack difference removal of parameters from stack and pushing the
result (it does not take into account removal of this in case of
non-static methods).
If Java really supported references we wouldn't have to be this
perverted.
*/
private static int sizeOfParameters(String pString)
{
int length = pString.length();
int rightParenthesis = pString.lastIndexOf(')');
if (3 <= length /* minimal signature takes at least 3 chars: ()V */
&& pString.charAt(0) == '('
&& 1 <= rightParenthesis && rightParenthesis + 1 < length)
{
boolean ok = true;
int index = 1;
int stackDiff = 0;
int count = 0;
stringLoop:
while (index != rightParenthesis) {
switch (pString.charAt(index)) {
default:
ok = false;
break stringLoop;
case 'J' :
case 'D' :
--stackDiff;
// fall thru
case 'B' :
case 'S' :
case 'C' :
case 'I' :
case 'Z' :
case 'F' :
--stackDiff;
++count;
++index;
continue;
case '[' :
++index;
int c = pString.charAt(index);
while (c == '[') {
++index;
c = pString.charAt(index);
}
switch (c) {
default:
ok = false;
break stringLoop;
case 'J' :
case 'D' :
case 'B' :
case 'S' :
case 'C' :
case 'I' :
case 'Z' :
case 'F' :
--stackDiff;
++count;
++index;
continue;
case 'L':
// fall thru
}
// fall thru
case 'L' : {
--stackDiff;
++count;
++index;
int semicolon = pString.indexOf(';', index);
if (!(index + 1 <= semicolon
&& semicolon < rightParenthesis))
{
ok = false;
break stringLoop;
}
index = semicolon + 1;
continue;
}
}
}
if (ok) {
switch (pString.charAt(rightParenthesis + 1)) {
default:
ok = false;
break;
case 'J' :
case 'D' :
++stackDiff;
// fall thru
case 'B' :
case 'S' :
case 'C' :
case 'I' :
case 'Z' :
case 'F' :
case 'L' :
case '[' :
++stackDiff;
// fall thru
case 'V' :
break;
}
if (ok) {
return ((count << 16) | (0xFFFF & stackDiff));
}
}
}
throw new IllegalArgumentException(
"Bad parameter signature: "+pString);
}
static int putInt16(int value, byte[] array, int offset)
{
array[offset + 0] = (byte)(value >>> 8);
array[offset + 1] = (byte)value;
return offset + 2;
}
static int putInt32(int value, byte[] array, int offset)
{
array[offset + 0] = (byte)(value >>> 24);
array[offset + 1] = (byte)(value >>> 16);
array[offset + 2] = (byte)(value >>> 8);
array[offset + 3] = (byte)value;
return offset + 4;
}
/**
* Size of a bytecode instruction, counting the opcode and its operands.
*
* This is different from opcodeCount, since opcodeCount counts logical
* operands.
*/
static int opcodeLength(int opcode, boolean wide) {
switch (opcode) {
case ByteCode.AALOAD:
case ByteCode.AASTORE:
case ByteCode.ACONST_NULL:
case ByteCode.ALOAD_0:
case ByteCode.ALOAD_1:
case ByteCode.ALOAD_2:
case ByteCode.ALOAD_3:
case ByteCode.ARETURN:
case ByteCode.ARRAYLENGTH:
case ByteCode.ASTORE_0:
case ByteCode.ASTORE_1:
case ByteCode.ASTORE_2:
case ByteCode.ASTORE_3:
case ByteCode.ATHROW:
case ByteCode.BALOAD:
case ByteCode.BASTORE:
case ByteCode.BREAKPOINT:
case ByteCode.CALOAD:
case ByteCode.CASTORE:
case ByteCode.D2F:
case ByteCode.D2I:
case ByteCode.D2L:
case ByteCode.DADD:
case ByteCode.DALOAD:
case ByteCode.DASTORE:
case ByteCode.DCMPG:
case ByteCode.DCMPL:
case ByteCode.DCONST_0:
case ByteCode.DCONST_1:
case ByteCode.DDIV:
case ByteCode.DLOAD_0:
case ByteCode.DLOAD_1:
case ByteCode.DLOAD_2:
case ByteCode.DLOAD_3:
case ByteCode.DMUL:
case ByteCode.DNEG:
case ByteCode.DREM:
case ByteCode.DRETURN:
case ByteCode.DSTORE_0:
case ByteCode.DSTORE_1:
case ByteCode.DSTORE_2:
case ByteCode.DSTORE_3:
case ByteCode.DSUB:
case ByteCode.DUP:
case ByteCode.DUP2:
case ByteCode.DUP2_X1:
case ByteCode.DUP2_X2:
case ByteCode.DUP_X1:
case ByteCode.DUP_X2:
case ByteCode.F2D:
case ByteCode.F2I:
case ByteCode.F2L:
case ByteCode.FADD:
case ByteCode.FALOAD:
case ByteCode.FASTORE:
case ByteCode.FCMPG:
case ByteCode.FCMPL:
case ByteCode.FCONST_0:
case ByteCode.FCONST_1:
case ByteCode.FCONST_2:
case ByteCode.FDIV:
case ByteCode.FLOAD_0:
case ByteCode.FLOAD_1:
case ByteCode.FLOAD_2:
case ByteCode.FLOAD_3:
case ByteCode.FMUL:
case ByteCode.FNEG:
case ByteCode.FREM:
case ByteCode.FRETURN:
case ByteCode.FSTORE_0:
case ByteCode.FSTORE_1:
case ByteCode.FSTORE_2:
case ByteCode.FSTORE_3:
case ByteCode.FSUB:
case ByteCode.I2B:
case ByteCode.I2C:
case ByteCode.I2D:
case ByteCode.I2F:
case ByteCode.I2L:
case ByteCode.I2S:
case ByteCode.IADD:
case ByteCode.IALOAD:
case ByteCode.IAND:
case ByteCode.IASTORE:
case ByteCode.ICONST_0:
case ByteCode.ICONST_1:
case ByteCode.ICONST_2:
case ByteCode.ICONST_3:
case ByteCode.ICONST_4:
case ByteCode.ICONST_5:
case ByteCode.ICONST_M1:
case ByteCode.IDIV:
case ByteCode.ILOAD_0:
case ByteCode.ILOAD_1:
case ByteCode.ILOAD_2:
case ByteCode.ILOAD_3:
case ByteCode.IMPDEP1:
case ByteCode.IMPDEP2:
case ByteCode.IMUL:
case ByteCode.INEG:
case ByteCode.IOR:
case ByteCode.IREM:
case ByteCode.IRETURN:
case ByteCode.ISHL:
case ByteCode.ISHR:
case ByteCode.ISTORE_0:
case ByteCode.ISTORE_1:
case ByteCode.ISTORE_2:
case ByteCode.ISTORE_3:
case ByteCode.ISUB:
case ByteCode.IUSHR:
case ByteCode.IXOR:
case ByteCode.L2D:
case ByteCode.L2F:
case ByteCode.L2I:
case ByteCode.LADD:
case ByteCode.LALOAD:
case ByteCode.LAND:
case ByteCode.LASTORE:
case ByteCode.LCMP:
case ByteCode.LCONST_0:
case ByteCode.LCONST_1:
case ByteCode.LDIV:
case ByteCode.LLOAD_0:
case ByteCode.LLOAD_1:
case ByteCode.LLOAD_2:
case ByteCode.LLOAD_3:
case ByteCode.LMUL:
case ByteCode.LNEG:
case ByteCode.LOR:
case ByteCode.LREM:
case ByteCode.LRETURN:
case ByteCode.LSHL:
case ByteCode.LSHR:
case ByteCode.LSTORE_0:
case ByteCode.LSTORE_1:
case ByteCode.LSTORE_2:
case ByteCode.LSTORE_3:
case ByteCode.LSUB:
case ByteCode.LUSHR:
case ByteCode.LXOR:
case ByteCode.MONITORENTER:
case ByteCode.MONITOREXIT:
case ByteCode.NOP:
case ByteCode.POP:
case ByteCode.POP2:
case ByteCode.RETURN:
case ByteCode.SALOAD:
case ByteCode.SASTORE:
case ByteCode.SWAP:
case ByteCode.WIDE:
return 1;
case ByteCode.BIPUSH:
case ByteCode.LDC:
case ByteCode.NEWARRAY:
return 2;
case ByteCode.ALOAD:
case ByteCode.ASTORE:
case ByteCode.DLOAD:
case ByteCode.DSTORE:
case ByteCode.FLOAD:
case ByteCode.FSTORE:
case ByteCode.ILOAD:
case ByteCode.ISTORE:
case ByteCode.LLOAD:
case ByteCode.LSTORE:
case ByteCode.RET:
return wide ? 3 : 2;
case ByteCode.ANEWARRAY:
case ByteCode.CHECKCAST:
case ByteCode.GETFIELD:
case ByteCode.GETSTATIC:
case ByteCode.GOTO:
case ByteCode.IFEQ:
case ByteCode.IFGE:
case ByteCode.IFGT:
case ByteCode.IFLE:
case ByteCode.IFLT:
case ByteCode.IFNE:
case ByteCode.IFNONNULL:
case ByteCode.IFNULL:
case ByteCode.IF_ACMPEQ:
case ByteCode.IF_ACMPNE:
case ByteCode.IF_ICMPEQ:
case ByteCode.IF_ICMPGE:
case ByteCode.IF_ICMPGT:
case ByteCode.IF_ICMPLE:
case ByteCode.IF_ICMPLT:
case ByteCode.IF_ICMPNE:
case ByteCode.INSTANCEOF:
case ByteCode.INVOKESPECIAL:
case ByteCode.INVOKESTATIC:
case ByteCode.INVOKEVIRTUAL:
case ByteCode.JSR:
case ByteCode.LDC_W:
case ByteCode.LDC2_W:
case ByteCode.NEW:
case ByteCode.PUTFIELD:
case ByteCode.PUTSTATIC:
case ByteCode.SIPUSH:
return 3;
case ByteCode.IINC:
return wide ? 5 : 3;
case ByteCode.MULTIANEWARRAY:
return 4;
case ByteCode.GOTO_W:
case ByteCode.INVOKEINTERFACE:
case ByteCode.JSR_W:
return 5;
/*
case ByteCode.LOOKUPSWITCH:
case ByteCode.TABLESWITCH:
return -1;
*/
}
throw new IllegalArgumentException("Bad opcode: " + opcode);
}
/**
* Number of operands accompanying the opcode.
*/
static int opcodeCount(int opcode)
{
switch (opcode) {
case ByteCode.AALOAD:
case ByteCode.AASTORE:
case ByteCode.ACONST_NULL:
case ByteCode.ALOAD_0:
case ByteCode.ALOAD_1:
case ByteCode.ALOAD_2:
case ByteCode.ALOAD_3:
case ByteCode.ARETURN:
case ByteCode.ARRAYLENGTH:
case ByteCode.ASTORE_0:
case ByteCode.ASTORE_1:
case ByteCode.ASTORE_2:
case ByteCode.ASTORE_3:
case ByteCode.ATHROW:
case ByteCode.BALOAD:
case ByteCode.BASTORE:
case ByteCode.BREAKPOINT:
case ByteCode.CALOAD:
case ByteCode.CASTORE:
case ByteCode.D2F:
case ByteCode.D2I:
case ByteCode.D2L:
case ByteCode.DADD:
case ByteCode.DALOAD:
case ByteCode.DASTORE:
case ByteCode.DCMPG:
case ByteCode.DCMPL:
case ByteCode.DCONST_0:
case ByteCode.DCONST_1:
case ByteCode.DDIV:
case ByteCode.DLOAD_0:
case ByteCode.DLOAD_1:
case ByteCode.DLOAD_2:
case ByteCode.DLOAD_3:
case ByteCode.DMUL:
case ByteCode.DNEG:
case ByteCode.DREM:
case ByteCode.DRETURN:
case ByteCode.DSTORE_0:
case ByteCode.DSTORE_1:
case ByteCode.DSTORE_2:
case ByteCode.DSTORE_3:
case ByteCode.DSUB:
case ByteCode.DUP:
case ByteCode.DUP2:
case ByteCode.DUP2_X1:
case ByteCode.DUP2_X2:
case ByteCode.DUP_X1:
case ByteCode.DUP_X2:
case ByteCode.F2D:
case ByteCode.F2I:
case ByteCode.F2L:
case ByteCode.FADD:
case ByteCode.FALOAD:
case ByteCode.FASTORE:
case ByteCode.FCMPG:
case ByteCode.FCMPL:
case ByteCode.FCONST_0:
case ByteCode.FCONST_1:
case ByteCode.FCONST_2:
case ByteCode.FDIV:
case ByteCode.FLOAD_0:
case ByteCode.FLOAD_1:
case ByteCode.FLOAD_2:
case ByteCode.FLOAD_3:
case ByteCode.FMUL:
case ByteCode.FNEG:
case ByteCode.FREM:
case ByteCode.FRETURN:
case ByteCode.FSTORE_0:
case ByteCode.FSTORE_1:
case ByteCode.FSTORE_2:
case ByteCode.FSTORE_3:
case ByteCode.FSUB:
case ByteCode.I2B:
case ByteCode.I2C:
case ByteCode.I2D:
case ByteCode.I2F:
case ByteCode.I2L:
case ByteCode.I2S:
case ByteCode.IADD:
case ByteCode.IALOAD:
case ByteCode.IAND:
case ByteCode.IASTORE:
case ByteCode.ICONST_0:
case ByteCode.ICONST_1:
case ByteCode.ICONST_2:
case ByteCode.ICONST_3:
case ByteCode.ICONST_4:
case ByteCode.ICONST_5:
case ByteCode.ICONST_M1:
case ByteCode.IDIV:
case ByteCode.ILOAD_0:
case ByteCode.ILOAD_1:
case ByteCode.ILOAD_2:
case ByteCode.ILOAD_3:
case ByteCode.IMPDEP1:
case ByteCode.IMPDEP2:
case ByteCode.IMUL:
case ByteCode.INEG:
case ByteCode.IOR:
case ByteCode.IREM:
case ByteCode.IRETURN:
case ByteCode.ISHL:
case ByteCode.ISHR:
case ByteCode.ISTORE_0:
case ByteCode.ISTORE_1:
case ByteCode.ISTORE_2:
case ByteCode.ISTORE_3:
case ByteCode.ISUB:
case ByteCode.IUSHR:
case ByteCode.IXOR:
case ByteCode.L2D:
case ByteCode.L2F:
case ByteCode.L2I:
case ByteCode.LADD:
case ByteCode.LALOAD:
case ByteCode.LAND:
case ByteCode.LASTORE:
case ByteCode.LCMP:
case ByteCode.LCONST_0:
case ByteCode.LCONST_1:
case ByteCode.LDIV:
case ByteCode.LLOAD_0:
case ByteCode.LLOAD_1:
case ByteCode.LLOAD_2:
case ByteCode.LLOAD_3:
case ByteCode.LMUL:
case ByteCode.LNEG:
case ByteCode.LOR:
case ByteCode.LREM:
case ByteCode.LRETURN:
case ByteCode.LSHL:
case ByteCode.LSHR:
case ByteCode.LSTORE_0:
case ByteCode.LSTORE_1:
case ByteCode.LSTORE_2:
case ByteCode.LSTORE_3:
case ByteCode.LSUB:
case ByteCode.LUSHR:
case ByteCode.LXOR:
case ByteCode.MONITORENTER:
case ByteCode.MONITOREXIT:
case ByteCode.NOP:
case ByteCode.POP:
case ByteCode.POP2:
case ByteCode.RETURN:
case ByteCode.SALOAD:
case ByteCode.SASTORE:
case ByteCode.SWAP:
case ByteCode.WIDE:
return 0;
case ByteCode.ALOAD:
case ByteCode.ANEWARRAY:
case ByteCode.ASTORE:
case ByteCode.BIPUSH:
case ByteCode.CHECKCAST:
case ByteCode.DLOAD:
case ByteCode.DSTORE:
case ByteCode.FLOAD:
case ByteCode.FSTORE:
case ByteCode.GETFIELD:
case ByteCode.GETSTATIC:
case ByteCode.GOTO:
case ByteCode.GOTO_W:
case ByteCode.IFEQ:
case ByteCode.IFGE:
case ByteCode.IFGT:
case ByteCode.IFLE:
case ByteCode.IFLT:
case ByteCode.IFNE:
case ByteCode.IFNONNULL:
case ByteCode.IFNULL:
case ByteCode.IF_ACMPEQ:
case ByteCode.IF_ACMPNE:
case ByteCode.IF_ICMPEQ:
case ByteCode.IF_ICMPGE:
case ByteCode.IF_ICMPGT:
case ByteCode.IF_ICMPLE:
case ByteCode.IF_ICMPLT:
case ByteCode.IF_ICMPNE:
case ByteCode.ILOAD:
case ByteCode.INSTANCEOF:
case ByteCode.INVOKEINTERFACE:
case ByteCode.INVOKESPECIAL:
case ByteCode.INVOKESTATIC:
case ByteCode.INVOKEVIRTUAL:
case ByteCode.ISTORE:
case ByteCode.JSR:
case ByteCode.JSR_W:
case ByteCode.LDC:
case ByteCode.LDC2_W:
case ByteCode.LDC_W:
case ByteCode.LLOAD:
case ByteCode.LSTORE:
case ByteCode.NEW:
case ByteCode.NEWARRAY:
case ByteCode.PUTFIELD:
case ByteCode.PUTSTATIC:
case ByteCode.RET:
case ByteCode.SIPUSH:
return 1;
case ByteCode.IINC:
case ByteCode.MULTIANEWARRAY:
return 2;
case ByteCode.LOOKUPSWITCH:
case ByteCode.TABLESWITCH:
return -1;
}
throw new IllegalArgumentException("Bad opcode: "+opcode);
}
/**
* The effect on the operand stack of a given opcode.
*/
static int stackChange(int opcode)
{
// For INVOKE... accounts only for popping this (unless static),
// ignoring parameters and return type
switch (opcode) {
case ByteCode.DASTORE:
case ByteCode.LASTORE:
return -4;
case ByteCode.AASTORE:
case ByteCode.BASTORE:
case ByteCode.CASTORE:
case ByteCode.DCMPG:
case ByteCode.DCMPL:
case ByteCode.FASTORE:
case ByteCode.IASTORE:
case ByteCode.LCMP:
case ByteCode.SASTORE:
return -3;
case ByteCode.DADD:
case ByteCode.DDIV:
case ByteCode.DMUL:
case ByteCode.DREM:
case ByteCode.DRETURN:
case ByteCode.DSTORE:
case ByteCode.DSTORE_0:
case ByteCode.DSTORE_1:
case ByteCode.DSTORE_2:
case ByteCode.DSTORE_3:
case ByteCode.DSUB:
case ByteCode.IF_ACMPEQ:
case ByteCode.IF_ACMPNE:
case ByteCode.IF_ICMPEQ:
case ByteCode.IF_ICMPGE:
case ByteCode.IF_ICMPGT:
case ByteCode.IF_ICMPLE:
case ByteCode.IF_ICMPLT:
case ByteCode.IF_ICMPNE:
case ByteCode.LADD:
case ByteCode.LAND:
case ByteCode.LDIV:
case ByteCode.LMUL:
case ByteCode.LOR:
case ByteCode.LREM:
case ByteCode.LRETURN:
case ByteCode.LSTORE:
case ByteCode.LSTORE_0:
case ByteCode.LSTORE_1:
case ByteCode.LSTORE_2:
case ByteCode.LSTORE_3:
case ByteCode.LSUB:
case ByteCode.LXOR:
case ByteCode.POP2:
return -2;
case ByteCode.AALOAD:
case ByteCode.ARETURN:
case ByteCode.ASTORE:
case ByteCode.ASTORE_0:
case ByteCode.ASTORE_1:
case ByteCode.ASTORE_2:
case ByteCode.ASTORE_3:
case ByteCode.ATHROW:
case ByteCode.BALOAD:
case ByteCode.CALOAD:
case ByteCode.D2F:
case ByteCode.D2I:
case ByteCode.FADD:
case ByteCode.FALOAD:
case ByteCode.FCMPG:
case ByteCode.FCMPL:
case ByteCode.FDIV:
case ByteCode.FMUL:
case ByteCode.FREM:
case ByteCode.FRETURN:
case ByteCode.FSTORE:
case ByteCode.FSTORE_0:
case ByteCode.FSTORE_1:
case ByteCode.FSTORE_2:
case ByteCode.FSTORE_3:
case ByteCode.FSUB:
case ByteCode.GETFIELD:
case ByteCode.IADD:
case ByteCode.IALOAD:
case ByteCode.IAND:
case ByteCode.IDIV:
case ByteCode.IFEQ:
case ByteCode.IFGE:
case ByteCode.IFGT:
case ByteCode.IFLE:
case ByteCode.IFLT:
case ByteCode.IFNE:
case ByteCode.IFNONNULL:
case ByteCode.IFNULL:
case ByteCode.IMUL:
case ByteCode.INVOKEINTERFACE: //
case ByteCode.INVOKESPECIAL: // but needs to account for
case ByteCode.INVOKEVIRTUAL: // pops 'this' (unless static)
case ByteCode.IOR:
case ByteCode.IREM:
case ByteCode.IRETURN:
case ByteCode.ISHL:
case ByteCode.ISHR:
case ByteCode.ISTORE:
case ByteCode.ISTORE_0:
case ByteCode.ISTORE_1:
case ByteCode.ISTORE_2:
case ByteCode.ISTORE_3:
case ByteCode.ISUB:
case ByteCode.IUSHR:
case ByteCode.IXOR:
case ByteCode.L2F:
case ByteCode.L2I:
case ByteCode.LOOKUPSWITCH:
case ByteCode.LSHL:
case ByteCode.LSHR:
case ByteCode.LUSHR:
case ByteCode.MONITORENTER:
case ByteCode.MONITOREXIT:
case ByteCode.POP:
case ByteCode.PUTFIELD:
case ByteCode.SALOAD:
case ByteCode.TABLESWITCH:
return -1;
case ByteCode.ANEWARRAY:
case ByteCode.ARRAYLENGTH:
case ByteCode.BREAKPOINT:
case ByteCode.CHECKCAST:
case ByteCode.D2L:
case ByteCode.DALOAD:
case ByteCode.DNEG:
case ByteCode.F2I:
case ByteCode.FNEG:
case ByteCode.GETSTATIC:
case ByteCode.GOTO:
case ByteCode.GOTO_W:
case ByteCode.I2B:
case ByteCode.I2C:
case ByteCode.I2F:
case ByteCode.I2S:
case ByteCode.IINC:
case ByteCode.IMPDEP1:
case ByteCode.IMPDEP2:
case ByteCode.INEG:
case ByteCode.INSTANCEOF:
case ByteCode.INVOKESTATIC:
case ByteCode.L2D:
case ByteCode.LALOAD:
case ByteCode.LNEG:
case ByteCode.NEWARRAY:
case ByteCode.NOP:
case ByteCode.PUTSTATIC:
case ByteCode.RET:
case ByteCode.RETURN:
case ByteCode.SWAP:
case ByteCode.WIDE:
return 0;
case ByteCode.ACONST_NULL:
case ByteCode.ALOAD:
case ByteCode.ALOAD_0:
case ByteCode.ALOAD_1:
case ByteCode.ALOAD_2:
case ByteCode.ALOAD_3:
case ByteCode.BIPUSH:
case ByteCode.DUP:
case ByteCode.DUP_X1:
case ByteCode.DUP_X2:
case ByteCode.F2D:
case ByteCode.F2L:
case ByteCode.FCONST_0:
case ByteCode.FCONST_1:
case ByteCode.FCONST_2:
case ByteCode.FLOAD:
case ByteCode.FLOAD_0:
case ByteCode.FLOAD_1:
case ByteCode.FLOAD_2:
case ByteCode.FLOAD_3:
case ByteCode.I2D:
case ByteCode.I2L:
case ByteCode.ICONST_0:
case ByteCode.ICONST_1:
case ByteCode.ICONST_2:
case ByteCode.ICONST_3:
case ByteCode.ICONST_4:
case ByteCode.ICONST_5:
case ByteCode.ICONST_M1:
case ByteCode.ILOAD:
case ByteCode.ILOAD_0:
case ByteCode.ILOAD_1:
case ByteCode.ILOAD_2:
case ByteCode.ILOAD_3:
case ByteCode.JSR:
case ByteCode.JSR_W:
case ByteCode.LDC:
case ByteCode.LDC_W:
case ByteCode.MULTIANEWARRAY:
case ByteCode.NEW:
case ByteCode.SIPUSH:
return 1;
case ByteCode.DCONST_0:
case ByteCode.DCONST_1:
case ByteCode.DLOAD:
case ByteCode.DLOAD_0:
case ByteCode.DLOAD_1:
case ByteCode.DLOAD_2:
case ByteCode.DLOAD_3:
case ByteCode.DUP2:
case ByteCode.DUP2_X1:
case ByteCode.DUP2_X2:
case ByteCode.LCONST_0:
case ByteCode.LCONST_1:
case ByteCode.LDC2_W:
case ByteCode.LLOAD:
case ByteCode.LLOAD_0:
case ByteCode.LLOAD_1:
case ByteCode.LLOAD_2:
case ByteCode.LLOAD_3:
return 2;
}
throw new IllegalArgumentException("Bad opcode: "+opcode);
}
/*
* Number of bytes of operands generated after the opcode.
* Not in use currently.
*/
/*
int extra(int opcode)
{
switch (opcode) {
case ByteCode.AALOAD:
case ByteCode.AASTORE:
case ByteCode.ACONST_NULL:
case ByteCode.ALOAD_0:
case ByteCode.ALOAD_1:
case ByteCode.ALOAD_2:
case ByteCode.ALOAD_3:
case ByteCode.ARETURN:
case ByteCode.ARRAYLENGTH:
case ByteCode.ASTORE_0:
case ByteCode.ASTORE_1:
case ByteCode.ASTORE_2:
case ByteCode.ASTORE_3:
case ByteCode.ATHROW:
case ByteCode.BALOAD:
case ByteCode.BASTORE:
case ByteCode.BREAKPOINT:
case ByteCode.CALOAD:
case ByteCode.CASTORE:
case ByteCode.D2F:
case ByteCode.D2I:
case ByteCode.D2L:
case ByteCode.DADD:
case ByteCode.DALOAD:
case ByteCode.DASTORE:
case ByteCode.DCMPG:
case ByteCode.DCMPL:
case ByteCode.DCONST_0:
case ByteCode.DCONST_1:
case ByteCode.DDIV:
case ByteCode.DLOAD_0:
case ByteCode.DLOAD_1:
case ByteCode.DLOAD_2:
case ByteCode.DLOAD_3:
case ByteCode.DMUL:
case ByteCode.DNEG:
case ByteCode.DREM:
case ByteCode.DRETURN:
case ByteCode.DSTORE_0:
case ByteCode.DSTORE_1:
case ByteCode.DSTORE_2:
case ByteCode.DSTORE_3:
case ByteCode.DSUB:
case ByteCode.DUP2:
case ByteCode.DUP2_X1:
case ByteCode.DUP2_X2:
case ByteCode.DUP:
case ByteCode.DUP_X1:
case ByteCode.DUP_X2:
case ByteCode.F2D:
case ByteCode.F2I:
case ByteCode.F2L:
case ByteCode.FADD:
case ByteCode.FALOAD:
case ByteCode.FASTORE:
case ByteCode.FCMPG:
case ByteCode.FCMPL:
case ByteCode.FCONST_0:
case ByteCode.FCONST_1:
case ByteCode.FCONST_2:
case ByteCode.FDIV:
case ByteCode.FLOAD_0:
case ByteCode.FLOAD_1:
case ByteCode.FLOAD_2:
case ByteCode.FLOAD_3:
case ByteCode.FMUL:
case ByteCode.FNEG:
case ByteCode.FREM:
case ByteCode.FRETURN:
case ByteCode.FSTORE_0:
case ByteCode.FSTORE_1:
case ByteCode.FSTORE_2:
case ByteCode.FSTORE_3:
case ByteCode.FSUB:
case ByteCode.I2B:
case ByteCode.I2C:
case ByteCode.I2D:
case ByteCode.I2F:
case ByteCode.I2L:
case ByteCode.I2S:
case ByteCode.IADD:
case ByteCode.IALOAD:
case ByteCode.IAND:
case ByteCode.IASTORE:
case ByteCode.ICONST_0:
case ByteCode.ICONST_1:
case ByteCode.ICONST_2:
case ByteCode.ICONST_3:
case ByteCode.ICONST_4:
case ByteCode.ICONST_5:
case ByteCode.ICONST_M1:
case ByteCode.IDIV:
case ByteCode.ILOAD_0:
case ByteCode.ILOAD_1:
case ByteCode.ILOAD_2:
case ByteCode.ILOAD_3:
case ByteCode.IMPDEP1:
case ByteCode.IMPDEP2:
case ByteCode.IMUL:
case ByteCode.INEG:
case ByteCode.IOR:
case ByteCode.IREM:
case ByteCode.IRETURN:
case ByteCode.ISHL:
case ByteCode.ISHR:
case ByteCode.ISTORE_0:
case ByteCode.ISTORE_1:
case ByteCode.ISTORE_2:
case ByteCode.ISTORE_3:
case ByteCode.ISUB:
case ByteCode.IUSHR:
case ByteCode.IXOR:
case ByteCode.L2D:
case ByteCode.L2F:
case ByteCode.L2I:
case ByteCode.LADD:
case ByteCode.LALOAD:
case ByteCode.LAND:
case ByteCode.LASTORE:
case ByteCode.LCMP:
case ByteCode.LCONST_0:
case ByteCode.LCONST_1:
case ByteCode.LDIV:
case ByteCode.LLOAD_0:
case ByteCode.LLOAD_1:
case ByteCode.LLOAD_2:
case ByteCode.LLOAD_3:
case ByteCode.LMUL:
case ByteCode.LNEG:
case ByteCode.LOR:
case ByteCode.LREM:
case ByteCode.LRETURN:
case ByteCode.LSHL:
case ByteCode.LSHR:
case ByteCode.LSTORE_0:
case ByteCode.LSTORE_1:
case ByteCode.LSTORE_2:
case ByteCode.LSTORE_3:
case ByteCode.LSUB:
case ByteCode.LUSHR:
case ByteCode.LXOR:
case ByteCode.MONITORENTER:
case ByteCode.MONITOREXIT:
case ByteCode.NOP:
case ByteCode.POP2:
case ByteCode.POP:
case ByteCode.RETURN:
case ByteCode.SALOAD:
case ByteCode.SASTORE:
case ByteCode.SWAP:
case ByteCode.WIDE:
return 0;
case ByteCode.ALOAD:
case ByteCode.ASTORE:
case ByteCode.BIPUSH:
case ByteCode.DLOAD:
case ByteCode.DSTORE:
case ByteCode.FLOAD:
case ByteCode.FSTORE:
case ByteCode.ILOAD:
case ByteCode.ISTORE:
case ByteCode.LDC:
case ByteCode.LLOAD:
case ByteCode.LSTORE:
case ByteCode.NEWARRAY:
case ByteCode.RET:
return 1;
case ByteCode.ANEWARRAY:
case ByteCode.CHECKCAST:
case ByteCode.GETFIELD:
case ByteCode.GETSTATIC:
case ByteCode.GOTO:
case ByteCode.IFEQ:
case ByteCode.IFGE:
case ByteCode.IFGT:
case ByteCode.IFLE:
case ByteCode.IFLT:
case ByteCode.IFNE:
case ByteCode.IFNONNULL:
case ByteCode.IFNULL:
case ByteCode.IF_ACMPEQ:
case ByteCode.IF_ACMPNE:
case ByteCode.IF_ICMPEQ:
case ByteCode.IF_ICMPGE:
case ByteCode.IF_ICMPGT:
case ByteCode.IF_ICMPLE:
case ByteCode.IF_ICMPLT:
case ByteCode.IF_ICMPNE:
case ByteCode.IINC:
case ByteCode.INSTANCEOF:
case ByteCode.INVOKEINTERFACE:
case ByteCode.INVOKESPECIAL:
case ByteCode.INVOKESTATIC:
case ByteCode.INVOKEVIRTUAL:
case ByteCode.JSR:
case ByteCode.LDC2_W:
case ByteCode.LDC_W:
case ByteCode.NEW:
case ByteCode.PUTFIELD:
case ByteCode.PUTSTATIC:
case ByteCode.SIPUSH:
return 2;
case ByteCode.MULTIANEWARRAY:
return 3;
case ByteCode.GOTO_W:
case ByteCode.JSR_W:
return 4;
case ByteCode.LOOKUPSWITCH: // depends on alignment
case ByteCode.TABLESWITCH: // depends on alignment
return -1;
}
throw new IllegalArgumentException("Bad opcode: "+opcode);
}
*/
private static String bytecodeStr(int code)
{
if (DEBUGSTACK || DEBUGCODE) {
switch (code) {
case ByteCode.NOP: return "nop";
case ByteCode.ACONST_NULL: return "aconst_null";
case ByteCode.ICONST_M1: return "iconst_m1";
case ByteCode.ICONST_0: return "iconst_0";
case ByteCode.ICONST_1: return "iconst_1";
case ByteCode.ICONST_2: return "iconst_2";
case ByteCode.ICONST_3: return "iconst_3";
case ByteCode.ICONST_4: return "iconst_4";
case ByteCode.ICONST_5: return "iconst_5";
case ByteCode.LCONST_0: return "lconst_0";
case ByteCode.LCONST_1: return "lconst_1";
case ByteCode.FCONST_0: return "fconst_0";
case ByteCode.FCONST_1: return "fconst_1";
case ByteCode.FCONST_2: return "fconst_2";
case ByteCode.DCONST_0: return "dconst_0";
case ByteCode.DCONST_1: return "dconst_1";
case ByteCode.BIPUSH: return "bipush";
case ByteCode.SIPUSH: return "sipush";
case ByteCode.LDC: return "ldc";
case ByteCode.LDC_W: return "ldc_w";
case ByteCode.LDC2_W: return "ldc2_w";
case ByteCode.ILOAD: return "iload";
case ByteCode.LLOAD: return "lload";
case ByteCode.FLOAD: return "fload";
case ByteCode.DLOAD: return "dload";
case ByteCode.ALOAD: return "aload";
case ByteCode.ILOAD_0: return "iload_0";
case ByteCode.ILOAD_1: return "iload_1";
case ByteCode.ILOAD_2: return "iload_2";
case ByteCode.ILOAD_3: return "iload_3";
case ByteCode.LLOAD_0: return "lload_0";
case ByteCode.LLOAD_1: return "lload_1";
case ByteCode.LLOAD_2: return "lload_2";
case ByteCode.LLOAD_3: return "lload_3";
case ByteCode.FLOAD_0: return "fload_0";
case ByteCode.FLOAD_1: return "fload_1";
case ByteCode.FLOAD_2: return "fload_2";
case ByteCode.FLOAD_3: return "fload_3";
case ByteCode.DLOAD_0: return "dload_0";
case ByteCode.DLOAD_1: return "dload_1";
case ByteCode.DLOAD_2: return "dload_2";
case ByteCode.DLOAD_3: return "dload_3";
case ByteCode.ALOAD_0: return "aload_0";
case ByteCode.ALOAD_1: return "aload_1";
case ByteCode.ALOAD_2: return "aload_2";
case ByteCode.ALOAD_3: return "aload_3";
case ByteCode.IALOAD: return "iaload";
case ByteCode.LALOAD: return "laload";
case ByteCode.FALOAD: return "faload";
case ByteCode.DALOAD: return "daload";
case ByteCode.AALOAD: return "aaload";
case ByteCode.BALOAD: return "baload";
case ByteCode.CALOAD: return "caload";
case ByteCode.SALOAD: return "saload";
case ByteCode.ISTORE: return "istore";
case ByteCode.LSTORE: return "lstore";
case ByteCode.FSTORE: return "fstore";
case ByteCode.DSTORE: return "dstore";
case ByteCode.ASTORE: return "astore";
case ByteCode.ISTORE_0: return "istore_0";
case ByteCode.ISTORE_1: return "istore_1";
case ByteCode.ISTORE_2: return "istore_2";
case ByteCode.ISTORE_3: return "istore_3";
case ByteCode.LSTORE_0: return "lstore_0";
case ByteCode.LSTORE_1: return "lstore_1";
case ByteCode.LSTORE_2: return "lstore_2";
case ByteCode.LSTORE_3: return "lstore_3";
case ByteCode.FSTORE_0: return "fstore_0";
case ByteCode.FSTORE_1: return "fstore_1";
case ByteCode.FSTORE_2: return "fstore_2";
case ByteCode.FSTORE_3: return "fstore_3";
case ByteCode.DSTORE_0: return "dstore_0";
case ByteCode.DSTORE_1: return "dstore_1";
case ByteCode.DSTORE_2: return "dstore_2";
case ByteCode.DSTORE_3: return "dstore_3";
case ByteCode.ASTORE_0: return "astore_0";
case ByteCode.ASTORE_1: return "astore_1";
case ByteCode.ASTORE_2: return "astore_2";
case ByteCode.ASTORE_3: return "astore_3";
case ByteCode.IASTORE: return "iastore";
case ByteCode.LASTORE: return "lastore";
case ByteCode.FASTORE: return "fastore";
case ByteCode.DASTORE: return "dastore";
case ByteCode.AASTORE: return "aastore";
case ByteCode.BASTORE: return "bastore";
case ByteCode.CASTORE: return "castore";
case ByteCode.SASTORE: return "sastore";
case ByteCode.POP: return "pop";
case ByteCode.POP2: return "pop2";
case ByteCode.DUP: return "dup";
case ByteCode.DUP_X1: return "dup_x1";
case ByteCode.DUP_X2: return "dup_x2";
case ByteCode.DUP2: return "dup2";
case ByteCode.DUP2_X1: return "dup2_x1";
case ByteCode.DUP2_X2: return "dup2_x2";
case ByteCode.SWAP: return "swap";
case ByteCode.IADD: return "iadd";
case ByteCode.LADD: return "ladd";
case ByteCode.FADD: return "fadd";
case ByteCode.DADD: return "dadd";
case ByteCode.ISUB: return "isub";
case ByteCode.LSUB: return "lsub";
case ByteCode.FSUB: return "fsub";
case ByteCode.DSUB: return "dsub";
case ByteCode.IMUL: return "imul";
case ByteCode.LMUL: return "lmul";
case ByteCode.FMUL: return "fmul";
case ByteCode.DMUL: return "dmul";
case ByteCode.IDIV: return "idiv";
case ByteCode.LDIV: return "ldiv";
case ByteCode.FDIV: return "fdiv";
case ByteCode.DDIV: return "ddiv";
case ByteCode.IREM: return "irem";
case ByteCode.LREM: return "lrem";
case ByteCode.FREM: return "frem";
case ByteCode.DREM: return "drem";
case ByteCode.INEG: return "ineg";
case ByteCode.LNEG: return "lneg";
case ByteCode.FNEG: return "fneg";
case ByteCode.DNEG: return "dneg";
case ByteCode.ISHL: return "ishl";
case ByteCode.LSHL: return "lshl";
case ByteCode.ISHR: return "ishr";
case ByteCode.LSHR: return "lshr";
case ByteCode.IUSHR: return "iushr";
case ByteCode.LUSHR: return "lushr";
case ByteCode.IAND: return "iand";
case ByteCode.LAND: return "land";
case ByteCode.IOR: return "ior";
case ByteCode.LOR: return "lor";
case ByteCode.IXOR: return "ixor";
case ByteCode.LXOR: return "lxor";
case ByteCode.IINC: return "iinc";
case ByteCode.I2L: return "i2l";
case ByteCode.I2F: return "i2f";
case ByteCode.I2D: return "i2d";
case ByteCode.L2I: return "l2i";
case ByteCode.L2F: return "l2f";
case ByteCode.L2D: return "l2d";
case ByteCode.F2I: return "f2i";
case ByteCode.F2L: return "f2l";
case ByteCode.F2D: return "f2d";
case ByteCode.D2I: return "d2i";
case ByteCode.D2L: return "d2l";
case ByteCode.D2F: return "d2f";
case ByteCode.I2B: return "i2b";
case ByteCode.I2C: return "i2c";
case ByteCode.I2S: return "i2s";
case ByteCode.LCMP: return "lcmp";
case ByteCode.FCMPL: return "fcmpl";
case ByteCode.FCMPG: return "fcmpg";
case ByteCode.DCMPL: return "dcmpl";
case ByteCode.DCMPG: return "dcmpg";
case ByteCode.IFEQ: return "ifeq";
case ByteCode.IFNE: return "ifne";
case ByteCode.IFLT: return "iflt";
case ByteCode.IFGE: return "ifge";
case ByteCode.IFGT: return "ifgt";
case ByteCode.IFLE: return "ifle";
case ByteCode.IF_ICMPEQ: return "if_icmpeq";
case ByteCode.IF_ICMPNE: return "if_icmpne";
case ByteCode.IF_ICMPLT: return "if_icmplt";
case ByteCode.IF_ICMPGE: return "if_icmpge";
case ByteCode.IF_ICMPGT: return "if_icmpgt";
case ByteCode.IF_ICMPLE: return "if_icmple";
case ByteCode.IF_ACMPEQ: return "if_acmpeq";
case ByteCode.IF_ACMPNE: return "if_acmpne";
case ByteCode.GOTO: return "goto";
case ByteCode.JSR: return "jsr";
case ByteCode.RET: return "ret";
case ByteCode.TABLESWITCH: return "tableswitch";
case ByteCode.LOOKUPSWITCH: return "lookupswitch";
case ByteCode.IRETURN: return "ireturn";
case ByteCode.LRETURN: return "lreturn";
case ByteCode.FRETURN: return "freturn";
case ByteCode.DRETURN: return "dreturn";
case ByteCode.ARETURN: return "areturn";
case ByteCode.RETURN: return "return";
case ByteCode.GETSTATIC: return "getstatic";
case ByteCode.PUTSTATIC: return "putstatic";
case ByteCode.GETFIELD: return "getfield";
case ByteCode.PUTFIELD: return "putfield";
case ByteCode.INVOKEVIRTUAL: return "invokevirtual";
case ByteCode.INVOKESPECIAL: return "invokespecial";
case ByteCode.INVOKESTATIC: return "invokestatic";
case ByteCode.INVOKEINTERFACE: return "invokeinterface";
case ByteCode.NEW: return "new";
case ByteCode.NEWARRAY: return "newarray";
case ByteCode.ANEWARRAY: return "anewarray";
case ByteCode.ARRAYLENGTH: return "arraylength";
case ByteCode.ATHROW: return "athrow";
case ByteCode.CHECKCAST: return "checkcast";
case ByteCode.INSTANCEOF: return "instanceof";
case ByteCode.MONITORENTER: return "monitorenter";
case ByteCode.MONITOREXIT: return "monitorexit";
case ByteCode.WIDE: return "wide";
case ByteCode.MULTIANEWARRAY: return "multianewarray";
case ByteCode.IFNULL: return "ifnull";
case ByteCode.IFNONNULL: return "ifnonnull";
case ByteCode.GOTO_W: return "goto_w";
case ByteCode.JSR_W: return "jsr_w";
case ByteCode.BREAKPOINT: return "breakpoint";
case ByteCode.IMPDEP1: return "impdep1";
case ByteCode.IMPDEP2: return "impdep2";
}
}
return "";
}
final char[] getCharBuffer(int minimalSize)
{
if (minimalSize > tmpCharBuffer.length) {
int newSize = tmpCharBuffer.length * 2;
if (minimalSize > newSize) { newSize = minimalSize; }
tmpCharBuffer = new char[newSize];
}
return tmpCharBuffer;
}
/**
* Add a pc as the start of super block.
*
* A pc is the beginning of a super block if:
* - pc == 0
* - it is the target of a branch instruction
* - it is the beginning of an exception handler
* - it is directly after an unconditional jump
*/
private void addSuperBlockStart(int pc) {
if (GenerateStackMap) {
if (itsSuperBlockStarts == null) {
itsSuperBlockStarts = new int[SuperBlockStartsSize];
} else if (itsSuperBlockStarts.length == itsSuperBlockStartsTop) {
int[] tmp = new int[itsSuperBlockStartsTop * 2];
System.arraycopy(itsSuperBlockStarts, 0, tmp, 0,
itsSuperBlockStartsTop);
itsSuperBlockStarts = tmp;
}
itsSuperBlockStarts[itsSuperBlockStartsTop++] = pc;
}
}
/**
* Sort the list of recorded super block starts and remove duplicates.
*
* Also adds exception handling blocks as block starts, since there is no
* explicit control flow to these. Used for stack map table generation.
*/
private void finalizeSuperBlockStarts() {
if (GenerateStackMap) {
for (int i = 0; i < itsExceptionTableTop; i++) {
ExceptionTableEntry ete = itsExceptionTable[i];
short handlerPC = (short) getLabelPC(ete.itsHandlerLabel);
addSuperBlockStart(handlerPC);
}
Arrays.sort(itsSuperBlockStarts, 0, itsSuperBlockStartsTop);
int prev = itsSuperBlockStarts[0];
int copyTo = 1;
for (int i = 1; i < itsSuperBlockStartsTop; i++) {
int curr = itsSuperBlockStarts[i];
if (prev != curr) {
if (copyTo != i) {
itsSuperBlockStarts[copyTo] = curr;
}
copyTo++;
prev = curr;
}
}
itsSuperBlockStartsTop = copyTo;
if (itsSuperBlockStarts[copyTo - 1] == itsCodeBufferTop) {
itsSuperBlockStartsTop--;
}
}
}
private int[] itsSuperBlockStarts = null;
private int itsSuperBlockStartsTop = 0;
private static final int SuperBlockStartsSize = 4;
// Used to find blocks of code with no dependencies (aka dead code).
// Necessary for generating type information for dead code, which is
// expected by the Sun verifier. It is only necessary to store a single
// jump source to determine if a block is reachable or not.
private UintMap itsJumpFroms = null;
private static final int LineNumberTableSize = 16;
private static final int ExceptionTableSize = 4;
private static final int MajorVersion;
private static final int MinorVersion;
private static final boolean GenerateStackMap;
static {
// Figure out which classfile version should be generated. This assumes
// that the runtime used to compile the JavaScript files is the same as
// the one used to run them. This is important because there are cases
// when bytecode is generated at runtime, where it is not easy to pass
// along what version is necessary. Instead, we grab the version numbers
// from the bytecode of this class and use that.
//
// Based on the version numbers we scrape, we can also determine what
// bytecode features we need. For example, Java 6 bytecode (classfile
// version 50) should have stack maps generated.
InputStream is = null;
int major = 48, minor = 0;
try {
is = ClassFileWriter.class.getResourceAsStream("ClassFileWriter.class");
if (is == null) {
is = ClassLoader.getSystemResourceAsStream(
"org/mozilla/classfile/ClassFileWriter.class");
}
byte[] header = new byte[8];
// read loop is required since JDK7 will only provide 2 bytes
// on the first read() - see bug #630111
int read = 0;
while (read < 8) {
int c = is.read(header, read, 8 - read);
if (c < 0) throw new IOException();
read += c;
}
minor = (header[4] << 8) | (header[5] & 0xff);
major = (header[6] << 8) | (header[7] & 0xff);
} catch (Exception e) {
// Unable to get class file, use default bytecode version
} finally {
MinorVersion = minor;
MajorVersion = major;
GenerateStackMap = major >= 50;
if (is != null) {
try {
is.close();
} catch (IOException e) {
}
}
}
}
private final static int FileHeaderConstant = 0xCAFEBABE;
// Set DEBUG flags to true to get better checking and progress info.
private static final boolean DEBUGSTACK = false;
private static final boolean DEBUGLABELS = false;
private static final boolean DEBUGCODE = false;
private String generatedClassName;
private ExceptionTableEntry itsExceptionTable[];
private int itsExceptionTableTop;
private int itsLineNumberTable[]; // pack start_pc & line_number together
private int itsLineNumberTableTop;
private byte[] itsCodeBuffer = new byte[256];
private int itsCodeBufferTop;
private ConstantPool itsConstantPool;
private ClassFileMethod itsCurrentMethod;
private short itsStackTop;
private short itsMaxStack;
private short itsMaxLocals;
private ObjArray itsMethods = new ObjArray();
private ObjArray itsFields = new ObjArray();
private ObjArray itsInterfaces = new ObjArray();
private short itsFlags;
private short itsThisClassIndex;
private short itsSuperClassIndex;
private short itsSourceFileNameIndex;
private static final int MIN_LABEL_TABLE_SIZE = 32;
private int[] itsLabelTable;
private int itsLabelTableTop;
// itsFixupTable[i] = (label_index << 32) | fixup_site
private static final int MIN_FIXUP_TABLE_SIZE = 40;
private long[] itsFixupTable;
private int itsFixupTableTop;
private ObjArray itsVarDescriptors;
private char[] tmpCharBuffer = new char[64];
}
final class ExceptionTableEntry
{
ExceptionTableEntry(int startLabel, int endLabel,
int handlerLabel, short catchType)
{
itsStartLabel = startLabel;
itsEndLabel = endLabel;
itsHandlerLabel = handlerLabel;
itsCatchType = catchType;
}
int itsStartLabel;
int itsEndLabel;
int itsHandlerLabel;
short itsCatchType;
}
final class ClassFileField
{
ClassFileField(short nameIndex, short typeIndex, short flags)
{
itsNameIndex = nameIndex;
itsTypeIndex = typeIndex;
itsFlags = flags;
itsHasAttributes = false;
}
void setAttributes(short attr1, short attr2, short attr3, int index)
{
itsHasAttributes = true;
itsAttr1 = attr1;
itsAttr2 = attr2;
itsAttr3 = attr3;
itsIndex = index;
}
int write(byte[] data, int offset)
{
offset = ClassFileWriter.putInt16(itsFlags, data, offset);
offset = ClassFileWriter.putInt16(itsNameIndex, data, offset);
offset = ClassFileWriter.putInt16(itsTypeIndex, data, offset);
if (!itsHasAttributes) {
// write 0 attributes
offset = ClassFileWriter.putInt16(0, data, offset);
} else {
offset = ClassFileWriter.putInt16(1, data, offset);
offset = ClassFileWriter.putInt16(itsAttr1, data, offset);
offset = ClassFileWriter.putInt16(itsAttr2, data, offset);
offset = ClassFileWriter.putInt16(itsAttr3, data, offset);
offset = ClassFileWriter.putInt16(itsIndex, data, offset);
}
return offset;
}
int getWriteSize()
{
int size = 2 * 3;
if (!itsHasAttributes) {
size += 2;
} else {
size += 2 + 2 * 4;
}
return size;
}
private short itsNameIndex;
private short itsTypeIndex;
private short itsFlags;
private boolean itsHasAttributes;
private short itsAttr1, itsAttr2, itsAttr3;
private int itsIndex;
}
final class ClassFileMethod
{
ClassFileMethod(String name, short nameIndex, String type, short typeIndex,
short flags)
{
itsName = name;
itsNameIndex = nameIndex;
itsType = type;
itsTypeIndex = typeIndex;
itsFlags = flags;
}
void setCodeAttribute(byte codeAttribute[])
{
itsCodeAttribute = codeAttribute;
}
int write(byte[] data, int offset)
{
offset = ClassFileWriter.putInt16(itsFlags, data, offset);
offset = ClassFileWriter.putInt16(itsNameIndex, data, offset);
offset = ClassFileWriter.putInt16(itsTypeIndex, data, offset);
// Code attribute only
offset = ClassFileWriter.putInt16(1, data, offset);
System.arraycopy(itsCodeAttribute, 0, data, offset,
itsCodeAttribute.length);
offset += itsCodeAttribute.length;
return offset;
}
int getWriteSize()
{
return 2 * 4 + itsCodeAttribute.length;
}
String getName()
{
return itsName;
}
String getType()
{
return itsType;
}
short getFlags()
{
return itsFlags;
}
private String itsName;
private String itsType;
private short itsNameIndex;
private short itsTypeIndex;
private short itsFlags;
private byte[] itsCodeAttribute;
}
final class ConstantPool
{
ConstantPool(ClassFileWriter cfw)
{
this.cfw = cfw;
itsTopIndex = 1; // the zero'th entry is reserved
itsPool = new byte[ConstantPoolSize];
itsTop = 0;
}
private static final int ConstantPoolSize = 256;
static final byte
CONSTANT_Class = 7,
CONSTANT_Fieldref = 9,
CONSTANT_Methodref = 10,
CONSTANT_InterfaceMethodref = 11,
CONSTANT_String = 8,
CONSTANT_Integer = 3,
CONSTANT_Float = 4,
CONSTANT_Long = 5,
CONSTANT_Double = 6,
CONSTANT_NameAndType = 12,
CONSTANT_Utf8 = 1;
int write(byte[] data, int offset)
{
offset = ClassFileWriter.putInt16((short)itsTopIndex, data, offset);
System.arraycopy(itsPool, 0, data, offset, itsTop);
offset += itsTop;
return offset;
}
int getWriteSize()
{
return 2 + itsTop;
}
int addConstant(int k)
{
ensure(5);
itsPool[itsTop++] = CONSTANT_Integer;
itsTop = ClassFileWriter.putInt32(k, itsPool, itsTop);
itsPoolTypes.put(itsTopIndex, CONSTANT_Integer);
return (short)(itsTopIndex++);
}
int addConstant(long k)
{
ensure(9);
itsPool[itsTop++] = CONSTANT_Long;
itsTop = ClassFileWriter.putInt64(k, itsPool, itsTop);
int index = itsTopIndex;
itsTopIndex += 2;
itsPoolTypes.put(index, CONSTANT_Long);
return index;
}
int addConstant(float k)
{
ensure(5);
itsPool[itsTop++] = CONSTANT_Float;
int bits = Float.floatToIntBits(k);
itsTop = ClassFileWriter.putInt32(bits, itsPool, itsTop);
itsPoolTypes.put(itsTopIndex, CONSTANT_Float);
return itsTopIndex++;
}
int addConstant(double k)
{
ensure(9);
itsPool[itsTop++] = CONSTANT_Double;
long bits = Double.doubleToLongBits(k);
itsTop = ClassFileWriter.putInt64(bits, itsPool, itsTop);
int index = itsTopIndex;
itsTopIndex += 2;
itsPoolTypes.put(index, CONSTANT_Double);
return index;
}
int addConstant(String k)
{
int utf8Index = 0xFFFF & addUtf8(k);
int theIndex = itsStringConstHash.getInt(utf8Index, -1);
if (theIndex == -1) {
theIndex = itsTopIndex++;
ensure(3);
itsPool[itsTop++] = CONSTANT_String;
itsTop = ClassFileWriter.putInt16(utf8Index, itsPool, itsTop);
itsStringConstHash.put(utf8Index, theIndex);
}
itsPoolTypes.put(theIndex, CONSTANT_String);
return theIndex;
}
boolean isUnderUtfEncodingLimit(String s)
{
int strLen = s.length();
if (strLen * 3 <= MAX_UTF_ENCODING_SIZE) {
return true;
} else if (strLen > MAX_UTF_ENCODING_SIZE) {
return false;
}
return strLen == getUtfEncodingLimit(s, 0, strLen);
}
/**
* Get maximum i such that start <= i <= end and
* s.substring(start, i) fits JVM UTF string encoding limit.
*/
int getUtfEncodingLimit(String s, int start, int end)
{
if ((end - start) * 3 <= MAX_UTF_ENCODING_SIZE) {
return end;
}
int limit = MAX_UTF_ENCODING_SIZE;
for (int i = start; i != end; i++) {
int c = s.charAt(i);
if (0 != c && c <= 0x7F) {
--limit;
} else if (c < 0x7FF) {
limit -= 2;
} else {
limit -= 3;
}
if (limit < 0) {
return i;
}
}
return end;
}
short addUtf8(String k)
{
int theIndex = itsUtf8Hash.get(k, -1);
if (theIndex == -1) {
int strLen = k.length();
boolean tooBigString;
if (strLen > MAX_UTF_ENCODING_SIZE) {
tooBigString = true;
} else {
tooBigString = false;
// Ask for worst case scenario buffer when each char takes 3
// bytes
ensure(1 + 2 + strLen * 3);
int top = itsTop;
itsPool[top++] = CONSTANT_Utf8;
top += 2; // skip length
char[] chars = cfw.getCharBuffer(strLen);
k.getChars(0, strLen, chars, 0);
for (int i = 0; i != strLen; i++) {
int c = chars[i];
if (c != 0 && c <= 0x7F) {
itsPool[top++] = (byte)c;
} else if (c > 0x7FF) {
itsPool[top++] = (byte)(0xE0 | (c >> 12));
itsPool[top++] = (byte)(0x80 | ((c >> 6) & 0x3F));
itsPool[top++] = (byte)(0x80 | (c & 0x3F));
} else {
itsPool[top++] = (byte)(0xC0 | (c >> 6));
itsPool[top++] = (byte)(0x80 | (c & 0x3F));
}
}
int utfLen = top - (itsTop + 1 + 2);
if (utfLen > MAX_UTF_ENCODING_SIZE) {
tooBigString = true;
} else {
// Write back length
itsPool[itsTop + 1] = (byte)(utfLen >>> 8);
itsPool[itsTop + 2] = (byte)utfLen;
itsTop = top;
theIndex = itsTopIndex++;
itsUtf8Hash.put(k, theIndex);
}
}
if (tooBigString) {
throw new IllegalArgumentException("Too big string");
}
}
setConstantData(theIndex, k);
itsPoolTypes.put(theIndex, CONSTANT_Utf8);
return (short)theIndex;
}
private short addNameAndType(String name, String type)
{
short nameIndex = addUtf8(name);
short typeIndex = addUtf8(type);
ensure(5);
itsPool[itsTop++] = CONSTANT_NameAndType;
itsTop = ClassFileWriter.putInt16(nameIndex, itsPool, itsTop);
itsTop = ClassFileWriter.putInt16(typeIndex, itsPool, itsTop);
itsPoolTypes.put(itsTopIndex, CONSTANT_NameAndType);
return (short)(itsTopIndex++);
}
short addClass(String className)
{
int theIndex = itsClassHash.get(className, -1);
if (theIndex == -1) {
String slashed = className;
if (className.indexOf('.') > 0) {
slashed = ClassFileWriter.getSlashedForm(className);
theIndex = itsClassHash.get(slashed, -1);
if (theIndex != -1) {
itsClassHash.put(className, theIndex);
}
}
if (theIndex == -1) {
int utf8Index = addUtf8(slashed);
ensure(3);
itsPool[itsTop++] = CONSTANT_Class;
itsTop = ClassFileWriter.putInt16(utf8Index, itsPool, itsTop);
theIndex = itsTopIndex++;
itsClassHash.put(slashed, theIndex);
if (className != slashed) {
itsClassHash.put(className, theIndex);
}
}
}
setConstantData(theIndex, className);
itsPoolTypes.put(theIndex, CONSTANT_Class);
return (short)theIndex;
}
short addFieldRef(String className, String fieldName, String fieldType)
{
FieldOrMethodRef ref = new FieldOrMethodRef(className, fieldName,
fieldType);
int theIndex = itsFieldRefHash.get(ref, -1);
if (theIndex == -1) {
short ntIndex = addNameAndType(fieldName, fieldType);
short classIndex = addClass(className);
ensure(5);
itsPool[itsTop++] = CONSTANT_Fieldref;
itsTop = ClassFileWriter.putInt16(classIndex, itsPool, itsTop);
itsTop = ClassFileWriter.putInt16(ntIndex, itsPool, itsTop);
theIndex = itsTopIndex++;
itsFieldRefHash.put(ref, theIndex);
}
setConstantData(theIndex, ref);
itsPoolTypes.put(theIndex, CONSTANT_Fieldref);
return (short)theIndex;
}
short addMethodRef(String className, String methodName,
String methodType)
{
FieldOrMethodRef ref = new FieldOrMethodRef(className, methodName,
methodType);
int theIndex = itsMethodRefHash.get(ref, -1);
if (theIndex == -1) {
short ntIndex = addNameAndType(methodName, methodType);
short classIndex = addClass(className);
ensure(5);
itsPool[itsTop++] = CONSTANT_Methodref;
itsTop = ClassFileWriter.putInt16(classIndex, itsPool, itsTop);
itsTop = ClassFileWriter.putInt16(ntIndex, itsPool, itsTop);
theIndex = itsTopIndex++;
itsMethodRefHash.put(ref, theIndex);
}
setConstantData(theIndex, ref);
itsPoolTypes.put(theIndex, CONSTANT_Methodref);
return (short)theIndex;
}
short addInterfaceMethodRef(String className,
String methodName, String methodType)
{
short ntIndex = addNameAndType(methodName, methodType);
short classIndex = addClass(className);
ensure(5);
itsPool[itsTop++] = CONSTANT_InterfaceMethodref;
itsTop = ClassFileWriter.putInt16(classIndex, itsPool, itsTop);
itsTop = ClassFileWriter.putInt16(ntIndex, itsPool, itsTop);
FieldOrMethodRef r = new FieldOrMethodRef(className, methodName,
methodType);
setConstantData(itsTopIndex, r);
itsPoolTypes.put(itsTopIndex, CONSTANT_InterfaceMethodref);
return (short)(itsTopIndex++);
}
Object getConstantData(int index)
{
return itsConstantData.getObject(index);
}
void setConstantData(int index, Object data)
{
itsConstantData.put(index, data);
}
byte getConstantType(int index)
{
return (byte) itsPoolTypes.getInt(index, 0);
}
void ensure(int howMuch)
{
if (itsTop + howMuch > itsPool.length) {
int newCapacity = itsPool.length * 2;
if (itsTop + howMuch > newCapacity) {
newCapacity = itsTop + howMuch;
}
byte[] tmp = new byte[newCapacity];
System.arraycopy(itsPool, 0, tmp, 0, itsTop);
itsPool = tmp;
}
}
private ClassFileWriter cfw;
private static final int MAX_UTF_ENCODING_SIZE = 65535;
private UintMap itsStringConstHash = new UintMap();
private ObjToIntMap itsUtf8Hash = new ObjToIntMap();
private ObjToIntMap itsFieldRefHash = new ObjToIntMap();
private ObjToIntMap itsMethodRefHash = new ObjToIntMap();
private ObjToIntMap itsClassHash = new ObjToIntMap();
private int itsTop;
private int itsTopIndex;
private UintMap itsConstantData = new UintMap();
private UintMap itsPoolTypes = new UintMap();
private byte itsPool[];
}
final class FieldOrMethodRef
{
FieldOrMethodRef(String className, String name, String type)
{
this.className = className;
this.name = name;
this.type = type;
}
public String getClassName()
{
return className;
}
public String getName()
{
return name;
}
public String getType()
{
return type;
}
@Override
public boolean equals(Object obj)
{
if (!(obj instanceof FieldOrMethodRef)) { return false; }
FieldOrMethodRef x = (FieldOrMethodRef)obj;
return className.equals(x.className)
&& name.equals(x.name)
&& type.equals(x.type);
}
@Override
public int hashCode()
{
if (hashCode == -1) {
int h1 = className.hashCode();
int h2 = name.hashCode();
int h3 = type.hashCode();
hashCode = h1 ^ h2 ^ h3;
}
return hashCode;
}
private String className;
private String name;
private String type;
private int hashCode = -1;
}
/**
* A super block is defined as a contiguous chunk of code with a single entry
* point and multiple exit points (therefore ending in an unconditional jump
* or the end of the method). This is used to emulate OpenJDK's compiler, which
* outputs stack map frames at the start of every super block except the method
* start.
*/
final class SuperBlock {
SuperBlock(int index, int start, int end, int[] initialLocals) {
this.index = index;
this.start = start;
this.end = end;
locals = new int[initialLocals.length];
System.arraycopy(initialLocals, 0, locals, 0, initialLocals.length);
stack = new int[0];
isInitialized = false;
isInQueue = false;
}
int getIndex() {
return index;
}
int[] getLocals() {
int[] copy = new int[locals.length];
System.arraycopy(locals, 0, copy, 0, locals.length);
return copy;
}
/**
* Get a copy of the super block's locals without any trailing TOP types.
*
* This is useful for actual writing stack maps; during the computation of
* stack map types, all local arrays have the same size; the max locals for
* the method. In addition, DOUBLE and LONG types have trailing TOP types
* because they occupy two words. For writing purposes, these are not
* useful.
*/
int[] getTrimmedLocals() {
int last = locals.length - 1;
// Exclude all of the trailing TOPs not bound to a DOUBLE/LONG
while (last >= 0 && locals[last] == TypeInfo.TOP &&
!TypeInfo.isTwoWords(locals[last - 1])) {
last--;
}
last++;
// Exclude trailing TOPs following a DOUBLE/LONG
int size = last;
for (int i = 0; i < last; i++) {
if (TypeInfo.isTwoWords(locals[i])) {
size--;
}
}
int[] copy = new int[size];
for (int i = 0, j = 0; i < size; i++, j++) {
copy[i] = locals[j];
if (TypeInfo.isTwoWords(locals[j])) {
j++;
}
}
return copy;
}
int[] getStack() {
int[] copy = new int[stack.length];
System.arraycopy(stack, 0, copy, 0, stack.length);
return copy;
}
boolean merge(int[] locals, int localsTop, int[] stack, int stackTop,
ConstantPool pool) {
if (!isInitialized) {
System.arraycopy(locals, 0, this.locals, 0, localsTop);
this.stack = new int[stackTop];
System.arraycopy(stack, 0, this.stack, 0, stackTop);
isInitialized = true;
return true;
} else if (this.locals.length == localsTop &&
this.stack.length == stackTop) {
boolean localsChanged = mergeState(this.locals, locals, localsTop,
pool);
boolean stackChanged = mergeState(this.stack, stack, stackTop,
pool);
return localsChanged || stackChanged;
} else {
if (ClassFileWriter.StackMapTable.DEBUGSTACKMAP) {
System.out.println("bad merge");
System.out.println("current type state:");
TypeInfo.print(this.locals, this.stack, pool);
System.out.println("incoming type state:");
TypeInfo.print(locals, localsTop, stack, stackTop, pool);
}
throw new IllegalArgumentException("bad merge attempt");
}
}
/**
* Merge an operand stack or local variable array with incoming state.
*
* They are treated the same way; by this point, it should already be
* ensured that the array sizes are the same, which is the only additional
* constraint that is imposed on merging operand stacks (the local variable
* array is always the same size).
*/
private boolean mergeState(int[] current, int[] incoming, int size,
ConstantPool pool) {
boolean changed = false;
for (int i = 0; i < size; i++) {
int currentType = current[i];
current[i] = TypeInfo.merge(current[i], incoming[i], pool);
if (currentType != current[i]) {
changed = true;
}
}
return changed;
}
int getStart() {
return start;
}
int getEnd() {
return end;
}
@Override
public String toString() {
return "sb " + index;
}
boolean isInitialized() {
return isInitialized;
}
void setInitialized(boolean b) {
isInitialized = b;
}
boolean isInQueue() {
return isInQueue;
}
void setInQueue(boolean b) {
isInQueue = b;
}
private int index;
private int start;
private int end;
private int[] locals;
private int[] stack;
private boolean isInitialized;
private boolean isInQueue;
}
/**
* Helper class for internal representations of type information. In most
* cases, type information can be represented by a constant, but in some
* cases, a payload is included. Despite the payload coming after the type
* tag in the output, we store it in bits 8-23 for uniformity; the tag is
* always in bits 0-7.
*/
final class TypeInfo {
private TypeInfo() { }
static final int TOP = 0;
static final int INTEGER = 1;
static final int FLOAT = 2;
static final int DOUBLE = 3;
static final int LONG = 4;
static final int NULL = 5;
static final int UNINITIALIZED_THIS = 6;
static final int OBJECT_TAG = 7;
static final int UNINITIALIZED_VAR_TAG = 8;
static final int OBJECT(int constantPoolIndex) {
return ((constantPoolIndex & 0xFFFF) << 8) | OBJECT_TAG;
}
static final int OBJECT(String type, ConstantPool pool) {
return OBJECT(pool.addClass(type));
}
static final int UNINITIALIZED_VARIABLE(int bytecodeOffset) {
return ((bytecodeOffset & 0xFFFF) << 8) | UNINITIALIZED_VAR_TAG;
}
static final int getTag(int typeInfo) {
return typeInfo & 0xFF;
}
static final int getPayload(int typeInfo) {
return typeInfo >>> 8;
}
/**
* Treat the result of getPayload as a constant pool index and fetch the
* corresponding String mapped to it.
*
* Only works on OBJECT types.
*/
static final String getPayloadAsType(int typeInfo, ConstantPool pool) {
if (getTag(typeInfo) == OBJECT_TAG) {
return (String) pool.getConstantData(getPayload(typeInfo));
}
throw new IllegalArgumentException("expecting object type");
}
/**
* Create type information from an internal type.
*/
static final int fromType(String type, ConstantPool pool) {
if (type.length() == 1) {
switch (type.charAt(0)) {
case 'B': // sbyte
case 'C': // unicode char
case 'S': // short
case 'Z': // boolean
case 'I': // all of the above are verified as integers
return INTEGER;
case 'D':
return DOUBLE;
case 'F':
return FLOAT;
case 'J':
return LONG;
default:
throw new IllegalArgumentException("bad type");
}
}
return TypeInfo.OBJECT(type, pool);
}
static boolean isTwoWords(int type) {
return type == DOUBLE || type == LONG;
}
/**
* Merge two verification types.
*
* In most cases, the verification types must be the same. For example,
* INTEGER and DOUBLE cannot be merged and an exception will be thrown.
* The basic rules are:
*
* - If the types are equal, simply return one.
* - If either type is TOP, return TOP.
* - If either type is NULL, return the other type.
* - If both types are objects, find the lowest common ancestor in the
* class hierarchy.
*
* This method uses reflection to traverse the class hierarchy. Therefore,
* it is assumed that the current class being generated is never the target
* of a full object-object merge, which would need to load the current
* class reflectively.
*/
static int merge(int current, int incoming, ConstantPool pool) {
int currentTag = getTag(current);
int incomingTag = getTag(incoming);
boolean currentIsObject = currentTag == TypeInfo.OBJECT_TAG;
boolean incomingIsObject = incomingTag == TypeInfo.OBJECT_TAG;
if (current == incoming || (currentIsObject && incoming == NULL)) {
return current;
} else if (currentTag == TypeInfo.TOP ||
incomingTag == TypeInfo.TOP) {
return TypeInfo.TOP;
} else if (current == NULL && incomingIsObject) {
return incoming;
} else if (currentIsObject && incomingIsObject) {
String currentName = getPayloadAsType(current, pool);
String incomingName = getPayloadAsType(incoming, pool);
// The class file always has the class and super names in the same
// spot. The constant order is: class_data, class_name, super_data,
// super_name.
String currentlyGeneratedName = (String) pool.getConstantData(2);
String currentlyGeneratedSuperName =
(String) pool.getConstantData(4);
// If any of the merged types are the class that's currently being
// generated, automatically start at the super class instead. At
// this point, we already know the classes are different, so we
// don't need to handle that case.
if (currentName.equals(currentlyGeneratedName)) {
currentName = currentlyGeneratedSuperName;
}
if (incomingName.equals(currentlyGeneratedName)) {
incomingName = currentlyGeneratedSuperName;
}
Class> currentClass = getClassFromInternalName(currentName);
Class> incomingClass = getClassFromInternalName(incomingName);
if (currentClass.isAssignableFrom(incomingClass)) {
return current;
} else if (incomingClass.isAssignableFrom(currentClass)) {
return incoming;
} else if (incomingClass.isInterface() ||
currentClass.isInterface()) {
// For verification purposes, Sun specifies that interfaces are
// subtypes of Object. Therefore, we know that the merge result
// involving interfaces where one is not assignable to the
// other results in Object.
return OBJECT("java/lang/Object", pool);
} else {
Class> commonClass = incomingClass.getSuperclass();
while (commonClass != null) {
if (commonClass.isAssignableFrom(currentClass)) {
String name = commonClass.getName();
name = ClassFileWriter.getSlashedForm(name);
return OBJECT(name, pool);
}
commonClass = commonClass.getSuperclass();
}
}
}
throw new IllegalArgumentException("bad merge attempt between " +
toString(current, pool) + " and " +
toString(incoming, pool));
}
static String toString(int type, ConstantPool pool) {
int tag = getTag(type);
switch (tag) {
case TypeInfo.TOP:
return "top";
case TypeInfo.INTEGER:
return "int";
case TypeInfo.FLOAT:
return "float";
case TypeInfo.DOUBLE:
return "double";
case TypeInfo.LONG:
return "long";
case TypeInfo.NULL:
return "null";
case TypeInfo.UNINITIALIZED_THIS:
return "uninitialized_this";
default:
if (tag == TypeInfo.OBJECT_TAG) {
return getPayloadAsType(type, pool);
} else if (tag == TypeInfo.UNINITIALIZED_VAR_TAG) {
return "uninitialized";
} else {
throw new IllegalArgumentException("bad type");
}
}
}
/**
* Take an internal name and return a java.lang.Class instance that
* represents it.
*
* For example, given "java/lang/Object", returns the equivalent of
* Class.forName("java.lang.Object"), but also handles exceptions.
*/
static Class getClassFromInternalName(String internalName) {
try {
return Class.forName(internalName.replace('/', '.'));
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
static String toString(int[] types, ConstantPool pool) {
return toString(types, types.length, pool);
}
static String toString(int[] types, int typesTop, ConstantPool pool) {
StringBuilder sb = new StringBuilder();
sb.append("[");
for (int i = 0; i < typesTop; i++) {
if (i > 0) {
sb.append(", ");
}
sb.append(toString(types[i], pool));
}
sb.append("]");
return sb.toString();
}
static void print(int[] locals, int[] stack, ConstantPool pool) {
print(locals, locals.length, stack, stack.length, pool);
}
static void print(int[] locals, int localsTop, int[] stack, int stackTop,
ConstantPool pool) {
System.out.print("locals: ");
System.out.println(toString(locals, localsTop, pool));
System.out.print("stack: ");
System.out.println(toString(stack, stackTop, pool));
System.out.println();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy