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
Rhino is an open-source implementation of JavaScript written entirely in Java. It is typically
embedded into Java applications to provide scripting to end users.
/* -*- 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 java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import org.mozilla.javascript.ObjArray;
import org.mozilla.javascript.UintMap;
/**
* 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];
int startPC = getLabelPC(ete.itsStartLabel);
int endPC = getLabelPC(ete.itsEndLabel);
int handlerPC = 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");
// no need to cast to short here, the putInt16 uses only
// the short part of the int
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");
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);
// fall through...
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 ClassFileFormatException("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 (theOperand < 0 || 65536 <= theOperand)
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 (theOperand1 < 0 || 65536 <= theOperand1)
throw new ClassFileFormatException("out of range variable");
if (theOperand2 < 0 || 65536 <= theOperand2)
throw new ClassFileFormatException("out of range increment");
if (theOperand1 > 255 || 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);
}
}
public void addInvokeDynamic(String methodName, String methodType,
MHandle bsm, Object... bsmArgs) {
if (DEBUGCODE) {
System.out.println("Add invokedynamic, " + methodName + ", " + methodType);
}
// JDK 1.7 major class file version is required for invokedynamic
if (MajorVersion < 51) {
throw new RuntimeException(
"Please build and run with JDK 1.7 for invokedynamic support");
}
int parameterInfo = sizeOfParameters(methodType);
// int parameterCount = parameterInfo >>> 16;
int stackDiff = (short) parameterInfo;
int newStack = itsStackTop + stackDiff;
if (newStack < 0 || Short.MAX_VALUE < newStack)
badStack(newStack);
BootstrapEntry bsmEntry = new BootstrapEntry(bsm, bsmArgs);
if (itsBootstrapMethods == null) {
itsBootstrapMethods = new ObjArray();
}
int bootstrapIndex = itsBootstrapMethods.indexOf(bsmEntry);
if (bootstrapIndex == -1) {
bootstrapIndex = itsBootstrapMethods.size();
itsBootstrapMethods.add(bsmEntry);
itsBootstrapMethodsLength += bsmEntry.code.length;
}
short invokedynamicIndex = itsConstantPool.addInvokeDynamic(
methodName, methodType, bootstrapIndex);
addToCodeBuffer(ByteCode.INVOKEDYNAMIC);
addToCodeInt16(invokedynamicIndex);
addToCodeInt16(0);
itsStackTop = (short) newStack;
if (newStack > itsMaxStack)
itsMaxStack = (short) newStack;
if (DEBUGSTACK) {
System.out.println("After invokedynamic 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 (jumpTarget < 0 || itsCodeBufferTop < jumpTarget)
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 (switchStart < 0 || itsCodeBufferTop - 4 * 4 - padSize - 1 < switchStart) {
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 (caseOffset < 0 || itsCodeBufferTop < caseOffset + 4) {
// 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("unlocated label");
}
// -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];
int startPC = getLabelPC(ete.itsStartLabel);
int handlerPC = 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];
int startPC = getLabelPC(ete.itsStartLabel);
int endPC = getLabelPC(ete.itsEndLabel);
if (bci < startPC || bci >= endPC) {
continue;
}
int handlerPC = 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();
// fall through
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();
// fall through
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();
// fall through
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();
// fall through
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();
// fall through
case ByteCode.LNEG: // pop; push(LONG)
case ByteCode.I2L:
case ByteCode.F2L:
case ByteCode.D2L:
pop();
// fall through
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();
// fall through
case ByteCode.FNEG: // pop; push(FLOAT)
case ByteCode.I2F:
case ByteCode.L2F:
case ByteCode.D2F:
pop();
// fall through
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();
// fall through
case ByteCode.DNEG: // pop; push(DOUBLE)
case ByteCode.I2D:
case ByteCode.L2D:
case ByteCode.F2D:
pop();
// fall through
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.INVOKEDYNAMIC:
index = getOperand(bci + 1, 2);
methodType = (String) itsConstantPool.getConstantData(index);
parameterCount = sizeOfParameters(methodType) >>> 16;
for (int i = 0; i < parameterCount; i++) {
pop();
}
rParen = methodType.indexOf(')');
returnType = methodType.substring(rParen + 1);
returnType = descriptorToInternalName(returnType);
if (!returnType.equals("V")) {
push(TypeInfo.fromType(returnType, itsConstantPool));
}
break;
case ByteCode.GETFIELD:
pop();
// fall through
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];
}
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;
}
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(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(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 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[] 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) {
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();
}
size += 2; //writeShort(1); attributes count, could be zero
if (itsSourceFileNameIndex != 0) {
size += 2; //writeShort(sourceFileAttributeNameIndex);
size += 4; //writeInt(2);
size += 2; //writeShort(itsSourceFileNameIndex);
}
if (itsBootstrapMethods != null) {
size += 2; //writeShort(bootstrapMethodsAttrNameIndex);
size += 4; //writeInt(itsBootstrapMethodsLength);
size += 2; //writeShort(bootstrapMethods.size());
size += itsBootstrapMethodsLength;
}
return size;
}
/**
* Get the class file as array of bytesto the OutputStream.
*/
public byte[] toByteArray() {
short bootstrapMethodsAttrNameIndex = 0;
int attributeCount = 0;
short sourceFileAttributeNameIndex = 0;
if (itsBootstrapMethods != null) {
++attributeCount;
bootstrapMethodsAttrNameIndex = itsConstantPool.addUtf8("BootstrapMethods");
}
if (itsSourceFileNameIndex != 0) {
++attributeCount;
sourceFileAttributeNameIndex = itsConstantPool.addUtf8(
"SourceFile");
}
// Don't calculate the data size until we know how many bootstrap
// methods there will be.
int offset = 0;
int dataSize = getWriteSize();
byte[] data = new byte[dataSize];
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);
}
offset = putInt16(attributeCount, data, offset); // attributes count
if (itsBootstrapMethods != null) {
offset = putInt16(bootstrapMethodsAttrNameIndex, data, offset);
offset = putInt32(itsBootstrapMethodsLength + 2, data, offset);
offset = putInt16(itsBootstrapMethods.size(), data, offset);
for (int i = 0; i < itsBootstrapMethods.size(); i++) {
BootstrapEntry entry = (BootstrapEntry) itsBootstrapMethods.get(i);
System.arraycopy(entry.code, 0, data, offset, entry.code.length);
offset += entry.code.length;
}
}
if (itsSourceFileNameIndex != 0) {
offset = putInt16(sourceFileAttributeNameIndex, data, offset);
offset = putInt32(2, data, offset);
offset = putInt16(itsSourceFileNameIndex, data, offset);
}
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 through
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 through
}
// fall through
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 through
case 'B':
case 'S':
case 'C':
case 'I':
case 'Z':
case 'F':
case 'L':
case '[':
++stackDiff;
// fall through
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.
*/
private 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.INVOKEDYNAMIC:
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.
*/
private 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.
*/
private 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.INVOKEDYNAMIC:
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);
}
*/
@SuppressWarnings("unused")
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.INVOKEDYNAMIC:
return "invokedynamic";
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];
int handlerPC = 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) {
}
}
}
}
final class BootstrapEntry {
final byte[] code;
BootstrapEntry(ClassFileWriter.MHandle bsm, Object... bsmArgs) {
int length = 2 + 2 + bsmArgs.length * 2;
code = new byte[length];
putInt16(itsConstantPool.addMethodHandle(bsm), code, 0);
putInt16(bsmArgs.length, code, 2);
for (int i = 0; i < bsmArgs.length; i++) {
putInt16(itsConstantPool.addConstant(bsmArgs[i]), code, 4 + i * 2);
}
}
@Override
public boolean equals(Object obj) {
return obj instanceof BootstrapEntry
&& Arrays.equals(code, ((BootstrapEntry) obj).code);
}
@Override
public int hashCode() {
return ~Arrays.hashCode(code);
}
}
public static final class MHandle {
final byte tag;
final String owner;
final String name;
final String desc;
public MHandle(byte tag, String owner, String name, String desc) {
this.tag = tag;
this.owner = owner;
this.name = name;
this.desc = desc;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof MHandle)) {
return false;
}
MHandle mh = (MHandle) obj;
return tag == mh.tag && owner.equals(mh.owner)
&& name.equals(mh.name) && desc.equals(mh.desc);
}
@Override
public int hashCode() {
return tag + owner.hashCode() * name.hashCode() * desc.hashCode();
}
@Override
public String toString() {
return owner + '.' + name + desc + " (" + tag + ')';
}
}
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 ObjArray itsBootstrapMethods;
private int itsBootstrapMethodsLength = 0;
private char[] tmpCharBuffer = new char[64];
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy