All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mozilla.classfile.ClassFileWriter Maven / Gradle / Ivy

Go to download

Rhino JavaScript runtime jar, excludes tools & JSR-223 Script Engine wrapper.

There is a newer version: 1.7.15
Show newest version
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.classfile;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import org.mozilla.javascript.Kit;
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() + ")"); } } 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) { int startIdx = Arrays.binarySearch(itsSuperBlockStarts, 0, itsSuperBlockStartsTop, offset); if (startIdx < 0) { // if offset was not found, insertion point is returned (See // Arrays.binarySearch) // we convert it back to the matching superblock. startIdx = -startIdx - 2; } if (startIdx < itsSuperBlockStartsTop) { SuperBlock sb = superBlocks[startIdx]; // check, if it is really the matching one if (offset < sb.getStart() || offset >= sb.getEnd()) Kit.codeBug(); 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; } } /** * 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. int sbStart = sb.getStart(); for (int i = 0; i < itsExceptionTableTop; i++) { ExceptionTableEntry ete = itsExceptionTable[i]; int eteStart = getLabelPC(ete.itsStartLabel); // this is "hot" code and it has been optimized so that // there are not too many function calls if (sbStart > eteStart && sbStart < getLabelPC(ete.itsEndLabel)) { int handlerPC = getLabelPC(ete.itsHandlerLabel); SuperBlock handlerSB = getSuperBlockFromOffset(handlerPC); locals = handlerSB.getLocals(); if (handlerSB.isInitialized()) { break; } else { continue; } } if (eteStart > sbStart && eteStart < sb.getEnd()) { int handlerPC = getLabelPC(ete.itsHandlerLabel); SuperBlock handlerSB = getSuperBlockFromOffset(handlerPC); if (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() || getLabelPC(ete.itsHandlerLabel) == 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); } int etStart = 0; int etEnd = itsExceptionTableTop; if (itsExceptionTableTop > 1) { // determine the relevant search range in the exception table. // this will reduce the search time if we have many exception // blocks. There may be false positives in the range, but in // most cases, this code does a good job, which leads in to // fewer checks in the double-for-loop. etStart = Integer.MAX_VALUE; etEnd = 0; for (int i = 0; i < itsExceptionTableTop; i++) { ExceptionTableEntry ete = itsExceptionTable[i]; // we have found an entry, that overlaps with our work block if (work.getEnd() >= getLabelPC(ete.itsStartLabel) && work.getStart() < getLabelPC(ete.itsEndLabel)) { etStart = Math.min(etStart, i); etEnd = Math.max(etEnd, i + 1); } } if (DEBUGSTACK) { if (etStart == 0 && etEnd == itsExceptionTableTop) { System.out.println( "lookup size " + itsExceptionTableTop + ": could not be reduced"); } else if (etStart < 0) { System.out.println( "lookup size " + itsExceptionTableTop + ": reduced completely"); } else { System.out.println( "lookup size " + itsExceptionTableTop + ": reduced to " + (etEnd - etStart)); } } } 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 = etStart; i < etEnd; 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; if (tag == TypeInfo.UNINITIALIZED_VARIABLE(0)) { newType = TypeInfo.OBJECT(m.getClassName(), itsConstantPool); } else { 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); writeTypes(locals); rawStackMapTop = putInt16(stack.length, rawStackMap, 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 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 static final 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