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];
-}
diff --git a/src/main/java/org/mozilla/classfile/ConstantEntry.java b/src/main/java/org/mozilla/classfile/ConstantEntry.java
deleted file mode 100644
index 44bf9945..00000000
--- a/src/main/java/org/mozilla/classfile/ConstantEntry.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/* -*- 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;
-
-final class ConstantEntry {
- private int type;
- private int intval;
- private long longval;
- private String str1;
- private String str2;
- private int hashcode;
-
- ConstantEntry(int type, int intval, String str1, String str2) {
- this.type = type;
- this.intval = intval;
- this.str1 = str1;
- this.str2 = str2;
- hashcode = type ^ intval + str1.hashCode() * str2.hashCode();
- }
-
- @Override
- public int hashCode() {
- return hashcode;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof ConstantEntry)) {
- return false;
- }
- ConstantEntry entry = (ConstantEntry) obj;
- if (type != entry.type) {
- return false;
- }
- switch (type) {
- case ConstantPool.CONSTANT_Integer:
- case ConstantPool.CONSTANT_Float:
- return intval == entry.intval;
- case ConstantPool.CONSTANT_Long:
- case ConstantPool.CONSTANT_Double:
- return longval == entry.longval;
- case ConstantPool.CONSTANT_NameAndType:
- return str1.equals(entry.str1) && str2.equals(entry.str2);
- case ConstantPool.CONSTANT_InvokeDynamic:
- return intval == entry.intval
- && str1.equals(entry.str1)
- && str2.equals(entry.str2);
- default:
- throw new RuntimeException("unsupported constant type");
-
- }
- }
-}
diff --git a/src/main/java/org/mozilla/classfile/ConstantPool.java b/src/main/java/org/mozilla/classfile/ConstantPool.java
deleted file mode 100644
index 46f31850..00000000
--- a/src/main/java/org/mozilla/classfile/ConstantPool.java
+++ /dev/null
@@ -1,418 +0,0 @@
-/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.classfile;
-
-import org.mozilla.javascript.ObjToIntMap;
-import org.mozilla.javascript.UintMap;
-
-final class ConstantPool
-{
- ConstantPool(ClassFileWriter cfw)
- {
- this.cfw = cfw;
- itsTopIndex = 1; // the zero'th entry is reserved
- itsPool = new byte[ConstantPoolSize];
- itsTop = 0;
- }
-
- private static final int ConstantPoolSize = 256;
- static final byte
- CONSTANT_Class = 7,
- CONSTANT_Fieldref = 9,
- CONSTANT_Methodref = 10,
- CONSTANT_InterfaceMethodref = 11,
- CONSTANT_String = 8,
- CONSTANT_Integer = 3,
- CONSTANT_Float = 4,
- CONSTANT_Long = 5,
- CONSTANT_Double = 6,
- CONSTANT_NameAndType = 12,
- CONSTANT_Utf8 = 1,
- CONSTANT_MethodType = 16,
- CONSTANT_MethodHandle = 15,
- CONSTANT_InvokeDynamic = 18;
-
- int write(byte[] data, int offset)
- {
- offset = ClassFileWriter.putInt16((short)itsTopIndex, data, offset);
- System.arraycopy(itsPool, 0, data, offset, itsTop);
- offset += itsTop;
- return offset;
- }
-
- int getWriteSize()
- {
- return 2 + itsTop;
- }
-
- int addConstant(int k)
- {
- ensure(5);
- itsPool[itsTop++] = CONSTANT_Integer;
- itsTop = ClassFileWriter.putInt32(k, itsPool, itsTop);
- itsPoolTypes.put(itsTopIndex, CONSTANT_Integer);
- return (short)(itsTopIndex++);
- }
-
- int addConstant(long k)
- {
- ensure(9);
- itsPool[itsTop++] = CONSTANT_Long;
- itsTop = ClassFileWriter.putInt64(k, itsPool, itsTop);
- int index = itsTopIndex;
- itsTopIndex += 2;
- itsPoolTypes.put(index, CONSTANT_Long);
- return index;
- }
-
- int addConstant(float k)
- {
- ensure(5);
- itsPool[itsTop++] = CONSTANT_Float;
- int bits = Float.floatToIntBits(k);
- itsTop = ClassFileWriter.putInt32(bits, itsPool, itsTop);
- itsPoolTypes.put(itsTopIndex, CONSTANT_Float);
- return itsTopIndex++;
- }
-
- int addConstant(double k)
- {
- ensure(9);
- itsPool[itsTop++] = CONSTANT_Double;
- long bits = Double.doubleToLongBits(k);
- itsTop = ClassFileWriter.putInt64(bits, itsPool, itsTop);
- int index = itsTopIndex;
- itsTopIndex += 2;
- itsPoolTypes.put(index, CONSTANT_Double);
- return index;
- }
-
- int addConstant(String k)
- {
- int utf8Index = 0xFFFF & addUtf8(k);
- int theIndex = itsStringConstHash.getInt(utf8Index, -1);
- if (theIndex == -1) {
- theIndex = itsTopIndex++;
- ensure(3);
- itsPool[itsTop++] = CONSTANT_String;
- itsTop = ClassFileWriter.putInt16(utf8Index, itsPool, itsTop);
- itsStringConstHash.put(utf8Index, theIndex);
- }
- itsPoolTypes.put(theIndex, CONSTANT_String);
- return theIndex;
- }
-
- int addConstant(Object value) {
- if (value instanceof Integer || value instanceof Byte
- || value instanceof Short) {
- return addConstant(((Number) value).intValue());
- } else if (value instanceof Character) {
- return addConstant(((Character) value).charValue());
- } else if (value instanceof Boolean) {
- return addConstant(((Boolean) value).booleanValue() ? 1 : 0);
- } else if (value instanceof Float) {
- return addConstant(((Float) value).floatValue());
- } else if (value instanceof Long) {
- return addConstant(((Long) value).longValue());
- } else if (value instanceof Double) {
- return addConstant(((Double) value).doubleValue());
- } else if (value instanceof String) {
- return addConstant((String) value);
- //} else if (value instanceof ClassFileWriter.MethodType) {
- // return addMethodType((ClassFileWriter.MethodType) value);
- } else if (value instanceof ClassFileWriter.MHandle) {
- return addMethodHandle((ClassFileWriter.MHandle) value);
- } else {
- throw new IllegalArgumentException("value " + value);
- }
- }
-
-
- boolean isUnderUtfEncodingLimit(String s)
- {
- int strLen = s.length();
- if (strLen * 3 <= MAX_UTF_ENCODING_SIZE) {
- return true;
- } else if (strLen > MAX_UTF_ENCODING_SIZE) {
- return false;
- }
- return strLen == getUtfEncodingLimit(s, 0, strLen);
- }
-
- /**
- * Get maximum i such that start <= i <= end and
- * s.substring(start, i) fits JVM UTF string encoding limit.
- */
- int getUtfEncodingLimit(String s, int start, int end)
- {
- if ((end - start) * 3 <= MAX_UTF_ENCODING_SIZE) {
- return end;
- }
- int limit = MAX_UTF_ENCODING_SIZE;
- for (int i = start; i != end; i++) {
- int c = s.charAt(i);
- if (0 != c && c <= 0x7F) {
- --limit;
- } else if (c < 0x7FF) {
- limit -= 2;
- } else {
- limit -= 3;
- }
- if (limit < 0) {
- return i;
- }
- }
- return end;
- }
-
- short addUtf8(String k)
- {
- int theIndex = itsUtf8Hash.get(k, -1);
- if (theIndex == -1) {
- int strLen = k.length();
- boolean tooBigString;
- if (strLen > MAX_UTF_ENCODING_SIZE) {
- tooBigString = true;
- } else {
- tooBigString = false;
- // Ask for worst case scenario buffer when each char takes 3
- // bytes
- ensure(1 + 2 + strLen * 3);
- int top = itsTop;
-
- itsPool[top++] = CONSTANT_Utf8;
- top += 2; // skip length
-
- char[] chars = cfw.getCharBuffer(strLen);
- k.getChars(0, strLen, chars, 0);
-
- for (int i = 0; i != strLen; i++) {
- int c = chars[i];
- if (c != 0 && c <= 0x7F) {
- itsPool[top++] = (byte)c;
- } else if (c > 0x7FF) {
- itsPool[top++] = (byte)(0xE0 | (c >> 12));
- itsPool[top++] = (byte)(0x80 | ((c >> 6) & 0x3F));
- itsPool[top++] = (byte)(0x80 | (c & 0x3F));
- } else {
- itsPool[top++] = (byte)(0xC0 | (c >> 6));
- itsPool[top++] = (byte)(0x80 | (c & 0x3F));
- }
- }
-
- int utfLen = top - (itsTop + 1 + 2);
- if (utfLen > MAX_UTF_ENCODING_SIZE) {
- tooBigString = true;
- } else {
- // Write back length
- itsPool[itsTop + 1] = (byte)(utfLen >>> 8);
- itsPool[itsTop + 2] = (byte)utfLen;
-
- itsTop = top;
- theIndex = itsTopIndex++;
- itsUtf8Hash.put(k, theIndex);
- }
- }
- if (tooBigString) {
- throw new IllegalArgumentException("Too big string");
- }
- }
- setConstantData(theIndex, k);
- itsPoolTypes.put(theIndex, CONSTANT_Utf8);
- return (short)theIndex;
- }
-
- private short addNameAndType(String name, String type)
- {
- short nameIndex = addUtf8(name);
- short typeIndex = addUtf8(type);
- ensure(5);
- itsPool[itsTop++] = CONSTANT_NameAndType;
- itsTop = ClassFileWriter.putInt16(nameIndex, itsPool, itsTop);
- itsTop = ClassFileWriter.putInt16(typeIndex, itsPool, itsTop);
- itsPoolTypes.put(itsTopIndex, CONSTANT_NameAndType);
- return (short)(itsTopIndex++);
- }
-
- short addClass(String className)
- {
- int theIndex = itsClassHash.get(className, -1);
- if (theIndex == -1) {
- String slashed = className;
- if (className.indexOf('.') > 0) {
- slashed = ClassFileWriter.getSlashedForm(className);
- theIndex = itsClassHash.get(slashed, -1);
- if (theIndex != -1) {
- itsClassHash.put(className, theIndex);
- }
- }
- if (theIndex == -1) {
- int utf8Index = addUtf8(slashed);
- ensure(3);
- itsPool[itsTop++] = CONSTANT_Class;
- itsTop = ClassFileWriter.putInt16(utf8Index, itsPool, itsTop);
- theIndex = itsTopIndex++;
- itsClassHash.put(slashed, theIndex);
- if (!className.equals(slashed)) {
- itsClassHash.put(className, theIndex);
- }
- }
- }
- setConstantData(theIndex, className);
- itsPoolTypes.put(theIndex, CONSTANT_Class);
- return (short)theIndex;
- }
-
- short addFieldRef(String className, String fieldName, String fieldType)
- {
- FieldOrMethodRef ref = new FieldOrMethodRef(className, fieldName,
- fieldType);
-
- int theIndex = itsFieldRefHash.get(ref, -1);
- if (theIndex == -1) {
- short ntIndex = addNameAndType(fieldName, fieldType);
- short classIndex = addClass(className);
- ensure(5);
- itsPool[itsTop++] = CONSTANT_Fieldref;
- itsTop = ClassFileWriter.putInt16(classIndex, itsPool, itsTop);
- itsTop = ClassFileWriter.putInt16(ntIndex, itsPool, itsTop);
- theIndex = itsTopIndex++;
- itsFieldRefHash.put(ref, theIndex);
- }
- setConstantData(theIndex, ref);
- itsPoolTypes.put(theIndex, CONSTANT_Fieldref);
- return (short)theIndex;
- }
-
- short addMethodRef(String className, String methodName,
- String methodType)
- {
- FieldOrMethodRef ref = new FieldOrMethodRef(className, methodName,
- methodType);
-
- int theIndex = itsMethodRefHash.get(ref, -1);
- if (theIndex == -1) {
- short ntIndex = addNameAndType(methodName, methodType);
- short classIndex = addClass(className);
- ensure(5);
- itsPool[itsTop++] = CONSTANT_Methodref;
- itsTop = ClassFileWriter.putInt16(classIndex, itsPool, itsTop);
- itsTop = ClassFileWriter.putInt16(ntIndex, itsPool, itsTop);
- theIndex = itsTopIndex++;
- itsMethodRefHash.put(ref, theIndex);
- }
- setConstantData(theIndex, ref);
- itsPoolTypes.put(theIndex, CONSTANT_Methodref);
- return (short)theIndex;
- }
-
- short addInterfaceMethodRef(String className,
- String methodName, String methodType)
- {
- short ntIndex = addNameAndType(methodName, methodType);
- short classIndex = addClass(className);
- ensure(5);
- itsPool[itsTop++] = CONSTANT_InterfaceMethodref;
- itsTop = ClassFileWriter.putInt16(classIndex, itsPool, itsTop);
- itsTop = ClassFileWriter.putInt16(ntIndex, itsPool, itsTop);
- FieldOrMethodRef r = new FieldOrMethodRef(className, methodName,
- methodType);
- setConstantData(itsTopIndex, r);
- itsPoolTypes.put(itsTopIndex, CONSTANT_InterfaceMethodref);
- return (short)(itsTopIndex++);
- }
-
- short addInvokeDynamic(String methodName, String methodType, int bootstrapIndex)
- {
- ConstantEntry entry = new ConstantEntry(CONSTANT_InvokeDynamic,
- bootstrapIndex, methodName, methodType);
- int theIndex = itsConstantHash.get(entry, -1);
-
- if (theIndex == -1) {
- short nameTypeIndex = addNameAndType(methodName, methodType);
- ensure(5);
- itsPool[itsTop++] = CONSTANT_InvokeDynamic;
- itsTop = ClassFileWriter.putInt16(bootstrapIndex, itsPool, itsTop);
- itsTop = ClassFileWriter.putInt16(nameTypeIndex, itsPool, itsTop);
- theIndex = itsTopIndex++;
- itsConstantHash.put(entry, theIndex);
- setConstantData(theIndex, methodType);
- itsPoolTypes.put(theIndex, CONSTANT_InvokeDynamic);
- }
- return (short)(theIndex);
- }
-
- short addMethodHandle(ClassFileWriter.MHandle mh)
- {
- int theIndex = itsConstantHash.get(mh, -1);
-
- if (theIndex == -1) {
- short ref;
- if (mh.tag <= ByteCode.MH_PUTSTATIC) {
- ref = addFieldRef(mh.owner, mh.name, mh.desc);
- } else if (mh.tag == ByteCode.MH_INVOKEINTERFACE) {
- ref = addInterfaceMethodRef(mh.owner, mh.name, mh.desc);
- } else {
- ref = addMethodRef(mh.owner, mh.name, mh.desc);
- }
-
- ensure(4);
- itsPool[itsTop++] = CONSTANT_MethodHandle;
- itsPool[itsTop++] = mh.tag;
- itsTop = ClassFileWriter.putInt16(ref, itsPool, itsTop);
- theIndex = itsTopIndex++;
- itsConstantHash.put(mh, theIndex);
- itsPoolTypes.put(theIndex, CONSTANT_MethodHandle);
- }
- return (short)(theIndex);
- }
-
- Object getConstantData(int index)
- {
- return itsConstantData.getObject(index);
- }
-
- void setConstantData(int index, Object data)
- {
- itsConstantData.put(index, data);
- }
-
- byte getConstantType(int index)
- {
- return (byte) itsPoolTypes.getInt(index, 0);
- }
-
- private void ensure(int howMuch)
- {
- if (itsTop + howMuch > itsPool.length) {
- int newCapacity = itsPool.length * 2;
- if (itsTop + howMuch > newCapacity) {
- newCapacity = itsTop + howMuch;
- }
- byte[] tmp = new byte[newCapacity];
- System.arraycopy(itsPool, 0, tmp, 0, itsTop);
- itsPool = tmp;
- }
- }
-
- private ClassFileWriter cfw;
-
- private static final int MAX_UTF_ENCODING_SIZE = 65535;
-
- private UintMap itsStringConstHash = new UintMap();
- private ObjToIntMap itsUtf8Hash = new ObjToIntMap();
- private ObjToIntMap itsFieldRefHash = new ObjToIntMap();
- private ObjToIntMap itsMethodRefHash = new ObjToIntMap();
- private ObjToIntMap itsClassHash = new ObjToIntMap();
- private ObjToIntMap itsConstantHash = new ObjToIntMap();
-
- private int itsTop;
- private int itsTopIndex;
- private UintMap itsConstantData = new UintMap();
- private UintMap itsPoolTypes = new UintMap();
- private byte itsPool[];
-}
diff --git a/src/main/java/org/mozilla/classfile/ExceptionTableEntry.java b/src/main/java/org/mozilla/classfile/ExceptionTableEntry.java
deleted file mode 100644
index cfc26bc1..00000000
--- a/src/main/java/org/mozilla/classfile/ExceptionTableEntry.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/* -*- 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;
-
-final class ExceptionTableEntry
-{
- ExceptionTableEntry(int startLabel, int endLabel,
- int handlerLabel, short catchType)
- {
- itsStartLabel = startLabel;
- itsEndLabel = endLabel;
- itsHandlerLabel = handlerLabel;
- itsCatchType = catchType;
- }
-
- int itsStartLabel;
- int itsEndLabel;
- int itsHandlerLabel;
- short itsCatchType;
-}
diff --git a/src/main/java/org/mozilla/classfile/FieldOrMethodRef.java b/src/main/java/org/mozilla/classfile/FieldOrMethodRef.java
deleted file mode 100644
index b109fc97..00000000
--- a/src/main/java/org/mozilla/classfile/FieldOrMethodRef.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/* -*- 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;
-
-final class FieldOrMethodRef
-{
- FieldOrMethodRef(String className, String name, String type)
- {
- this.className = className;
- this.name = name;
- this.type = type;
- }
-
- public String getClassName()
- {
- return className;
- }
-
- public String getName()
- {
- return name;
- }
-
- public String getType()
- {
- return type;
- }
-
- @Override
- public boolean equals(Object obj)
- {
- if (!(obj instanceof FieldOrMethodRef)) { return false; }
- FieldOrMethodRef x = (FieldOrMethodRef)obj;
- return className.equals(x.className)
- && name.equals(x.name)
- && type.equals(x.type);
- }
-
- @Override
- public int hashCode()
- {
- if (hashCode == -1) {
- int h1 = className.hashCode();
- int h2 = name.hashCode();
- int h3 = type.hashCode();
- hashCode = h1 ^ h2 ^ h3;
- }
- return hashCode;
- }
-
- private String className;
- private String name;
- private String type;
- private int hashCode = -1;
-}
diff --git a/src/main/java/org/mozilla/classfile/SuperBlock.java b/src/main/java/org/mozilla/classfile/SuperBlock.java
deleted file mode 100644
index b55bb722..00000000
--- a/src/main/java/org/mozilla/classfile/SuperBlock.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/* -*- 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;
-
-/**
- * A super block is defined as a contiguous chunk of code with a single entry
- * point and multiple exit points (therefore ending in an unconditional jump
- * or the end of the method). This is used to emulate OpenJDK's compiler, which
- * outputs stack map frames at the start of every super block except the method
- * start.
- */
-final class SuperBlock {
- SuperBlock(int index, int start, int end, int[] initialLocals) {
- this.index = index;
- this.start = start;
- this.end = end;
- locals = new int[initialLocals.length];
- System.arraycopy(initialLocals, 0, locals, 0, initialLocals.length);
- stack = new int[0];
- isInitialized = false;
- isInQueue = false;
- }
-
- int getIndex() {
- return index;
- }
-
- int[] getLocals() {
- int[] copy = new int[locals.length];
- System.arraycopy(locals, 0, copy, 0, locals.length);
- return copy;
- }
-
- /**
- * Get a copy of the super block's locals without any trailing TOP types.
- *
- * This is useful for actual writing stack maps; during the computation of
- * stack map types, all local arrays have the same size; the max locals for
- * the method. In addition, DOUBLE and LONG types have trailing TOP types
- * because they occupy two words. For writing purposes, these are not
- * useful.
- */
- int[] getTrimmedLocals() {
- int last = locals.length - 1;
- // Exclude all of the trailing TOPs not bound to a DOUBLE/LONG
- while (last >= 0 && locals[last] == TypeInfo.TOP &&
- !TypeInfo.isTwoWords(locals[last - 1])) {
- last--;
- }
- last++;
- // Exclude trailing TOPs following a DOUBLE/LONG
- int size = last;
- for (int i = 0; i < last; i++) {
- if (TypeInfo.isTwoWords(locals[i])) {
- size--;
- }
- }
- int[] copy = new int[size];
- for (int i = 0, j = 0; i < size; i++, j++) {
- copy[i] = locals[j];
- if (TypeInfo.isTwoWords(locals[j])) {
- j++;
- }
- }
- return copy;
- }
-
- int[] getStack() {
- int[] copy = new int[stack.length];
- System.arraycopy(stack, 0, copy, 0, stack.length);
- return copy;
- }
-
- boolean merge(int[] locals, int localsTop, int[] stack, int stackTop,
- ConstantPool pool) {
- if (!isInitialized) {
- System.arraycopy(locals, 0, this.locals, 0, localsTop);
- this.stack = new int[stackTop];
- System.arraycopy(stack, 0, this.stack, 0, stackTop);
- isInitialized = true;
- return true;
- } else if (this.locals.length == localsTop &&
- this.stack.length == stackTop) {
- boolean localsChanged = mergeState(this.locals, locals, localsTop,
- pool);
- boolean stackChanged = mergeState(this.stack, stack, stackTop,
- pool);
- return localsChanged || stackChanged;
- } else {
- if (ClassFileWriter.StackMapTable.DEBUGSTACKMAP) {
- System.out.println("bad merge");
- System.out.println("current type state:");
- TypeInfo.print(this.locals, this.stack, pool);
- System.out.println("incoming type state:");
- TypeInfo.print(locals, localsTop, stack, stackTop, pool);
- }
- throw new IllegalArgumentException("bad merge attempt");
- }
- }
-
- /**
- * Merge an operand stack or local variable array with incoming state.
- *
- * They are treated the same way; by this point, it should already be
- * ensured that the array sizes are the same, which is the only additional
- * constraint that is imposed on merging operand stacks (the local variable
- * array is always the same size).
- */
- private static boolean mergeState(int[] current, int[] incoming, int size,
- ConstantPool pool) {
- boolean changed = false;
- for (int i = 0; i < size; i++) {
- int currentType = current[i];
-
- current[i] = TypeInfo.merge(current[i], incoming[i], pool);
- if (currentType != current[i]) {
- changed = true;
- }
- }
- return changed;
- }
-
- int getStart() {
- return start;
- }
-
- int getEnd() {
- return end;
- }
-
- @Override
- public String toString() {
- return "sb " + index;
- }
-
- boolean isInitialized() {
- return isInitialized;
- }
-
- void setInitialized(boolean b) {
- isInitialized = b;
- }
-
- boolean isInQueue() {
- return isInQueue;
- }
-
- void setInQueue(boolean b) {
- isInQueue = b;
- }
-
- private int index;
- private int start;
- private int end;
- private int[] locals;
- private int[] stack;
- private boolean isInitialized;
- private boolean isInQueue;
-}
diff --git a/src/main/java/org/mozilla/classfile/TypeInfo.java b/src/main/java/org/mozilla/classfile/TypeInfo.java
deleted file mode 100644
index 75805807..00000000
--- a/src/main/java/org/mozilla/classfile/TypeInfo.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/* -*- 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;
-
-/**
- * Helper class for internal representations of type information. In most cases, type information
- * can be represented by a constant, but in some cases, a payload is included. Despite the payload
- * coming after the type tag in the output, we store it in bits 8-23 for uniformity; the tag is
- * always in bits 0-7.
- */
-final class TypeInfo {
- private TypeInfo() {}
-
- static final int TOP = 0;
- static final int INTEGER = 1;
- static final int FLOAT = 2;
- static final int DOUBLE = 3;
- static final int LONG = 4;
- static final int NULL = 5;
- static final int UNINITIALIZED_THIS = 6;
- static final int OBJECT_TAG = 7;
- static final int UNINITIALIZED_VAR_TAG = 8;
-
- static final int OBJECT(int constantPoolIndex) {
- return ((constantPoolIndex & 0xFFFF) << 8) | OBJECT_TAG;
- }
-
- static final int OBJECT(String type, ConstantPool pool) {
- return OBJECT(pool.addClass(type));
- }
-
- static final int UNINITIALIZED_VARIABLE(int bytecodeOffset) {
- return ((bytecodeOffset & 0xFFFF) << 8) | UNINITIALIZED_VAR_TAG;
- }
-
- static final int getTag(int typeInfo) {
- return typeInfo & 0xFF;
- }
-
- static final int getPayload(int typeInfo) {
- return typeInfo >>> 8;
- }
-
- /**
- * Treat the result of getPayload as a constant pool index and fetch the corresponding String
- * mapped to it.
- *
- *
Only works on OBJECT types.
- */
- static final String getPayloadAsType(int typeInfo, ConstantPool pool) {
- if (getTag(typeInfo) == OBJECT_TAG) {
- return (String) pool.getConstantData(getPayload(typeInfo));
- }
- throw new IllegalArgumentException("expecting object type");
- }
-
- /** Create type information from an internal type. */
- static final int fromType(String type, ConstantPool pool) {
- if (type.length() == 1) {
- switch (type.charAt(0)) {
- case 'B': // sbyte
- case 'C': // unicode char
- case 'S': // short
- case 'Z': // boolean
- case 'I': // all of the above are verified as integers
- return INTEGER;
- case 'D':
- return DOUBLE;
- case 'F':
- return FLOAT;
- case 'J':
- return LONG;
- default:
- throw new IllegalArgumentException("bad type");
- }
- }
- return TypeInfo.OBJECT(type, pool);
- }
-
- static boolean isTwoWords(int type) {
- return type == DOUBLE || type == LONG;
- }
-
- /**
- * Merge two verification types.
- *
- *
In most cases, the verification types must be the same. For example, INTEGER and DOUBLE
- * cannot be merged and an exception will be thrown. The basic rules are:
- *
- *
- If the types are equal, simply return one. - If either type is TOP, return TOP. - If
- * either type is NULL, return the other type. - If both types are objects, find the lowest
- * common ancestor in the class hierarchy.
- *
- *
This method uses reflection to traverse the class hierarchy. Therefore, it is assumed that
- * the current class being generated is never the target of a full object-object merge, which
- * would need to load the current class reflectively.
- */
- static int merge(int current, int incoming, ConstantPool pool) {
- if (current == incoming) {
- return current;
- }
- int currentTag = getTag(current);
- int incomingTag = getTag(incoming);
- boolean currentIsObject = currentTag == TypeInfo.OBJECT_TAG;
- boolean incomingIsObject = incomingTag == TypeInfo.OBJECT_TAG;
-
- if (currentIsObject && incoming == NULL) {
- return current;
- } else if (currentTag == TypeInfo.TOP || incomingTag == TypeInfo.TOP) {
- return TypeInfo.TOP;
- } else if (current == NULL && incomingIsObject) {
- return incoming;
- } else if (currentIsObject && incomingIsObject) {
- String currentName = getPayloadAsType(current, pool);
- String incomingName = getPayloadAsType(incoming, pool);
- // The class file always has the class and super names in the same
- // spot. The constant order is: class_data, class_name, super_data,
- // super_name.
- String currentlyGeneratedName = (String) pool.getConstantData(2);
- String currentlyGeneratedSuperName = (String) pool.getConstantData(4);
-
- // If any of the merged types are the class that's currently being
- // generated, automatically start at the super class instead. At
- // this point, we already know the classes are different, so we
- // don't need to handle that case.
- if (currentName.equals(currentlyGeneratedName)) {
- currentName = currentlyGeneratedSuperName;
- }
- if (incomingName.equals(currentlyGeneratedName)) {
- incomingName = currentlyGeneratedSuperName;
- }
-
- Class> currentClass = getClassFromInternalName(currentName);
- Class> incomingClass = getClassFromInternalName(incomingName);
-
- if (currentClass.isAssignableFrom(incomingClass)) {
- return current;
- } else if (incomingClass.isAssignableFrom(currentClass)) {
- return incoming;
- } else if (incomingClass.isInterface() || currentClass.isInterface()) {
- // For verification purposes, Sun specifies that interfaces are
- // subtypes of Object. Therefore, we know that the merge result
- // involving interfaces where one is not assignable to the
- // other results in Object.
- return OBJECT("java/lang/Object", pool);
- } else {
- Class> commonClass = incomingClass.getSuperclass();
- while (commonClass != null) {
- if (commonClass.isAssignableFrom(currentClass)) {
- String name = commonClass.getName();
- name = ClassFileWriter.getSlashedForm(name);
- return OBJECT(name, pool);
- }
- commonClass = commonClass.getSuperclass();
- }
- }
- }
- throw new IllegalArgumentException(
- "bad merge attempt between "
- + toString(current, pool)
- + " and "
- + toString(incoming, pool));
- }
-
- static String toString(int type, ConstantPool pool) {
- int tag = getTag(type);
- switch (tag) {
- case TypeInfo.TOP:
- return "top";
- case TypeInfo.INTEGER:
- return "int";
- case TypeInfo.FLOAT:
- return "float";
- case TypeInfo.DOUBLE:
- return "double";
- case TypeInfo.LONG:
- return "long";
- case TypeInfo.NULL:
- return "null";
- case TypeInfo.UNINITIALIZED_THIS:
- return "uninitialized_this";
- default:
- if (tag == TypeInfo.OBJECT_TAG) {
- return getPayloadAsType(type, pool);
- } else if (tag == TypeInfo.UNINITIALIZED_VAR_TAG) {
- return "uninitialized";
- } else {
- throw new IllegalArgumentException("bad type");
- }
- }
- }
-
- /**
- * Take an internal name and return a java.lang.Class instance that represents it.
- *
- *
For example, given "java/lang/Object", returns the equivalent of
- * Class.forName("java.lang.Object"), but also handles exceptions.
- */
- private static Class> getClassFromInternalName(String internalName) {
- try {
- return Class.forName(internalName.replace('/', '.'));
- } catch (ClassNotFoundException e) {
- throw new RuntimeException(e);
- }
- }
-
- private static String toString(int[] types, int typesTop, ConstantPool pool) {
- StringBuilder sb = new StringBuilder();
- sb.append("[");
- for (int i = 0; i < typesTop; i++) {
- if (i > 0) {
- sb.append(", ");
- }
- sb.append(toString(types[i], pool));
- }
- sb.append("]");
- return sb.toString();
- }
-
- static void print(int[] locals, int[] stack, ConstantPool pool) {
- print(locals, locals.length, stack, stack.length, pool);
- }
-
- static void print(int[] locals, int localsTop, int[] stack, int stackTop, ConstantPool pool) {
- System.out.print("locals: ");
- System.out.println(toString(locals, localsTop, pool));
- System.out.print("stack: ");
- System.out.println(toString(stack, stackTop, pool));
- System.out.println();
- }
-}
diff --git a/src/main/java/org/mozilla/javascript/AbstractEcmaObjectOperations.java b/src/main/java/org/mozilla/javascript/AbstractEcmaObjectOperations.java
deleted file mode 100644
index 40913ea2..00000000
--- a/src/main/java/org/mozilla/javascript/AbstractEcmaObjectOperations.java
+++ /dev/null
@@ -1,172 +0,0 @@
-package org.mozilla.javascript;
-
-/**
- * Abstract Object Operations as defined by EcmaScript
- *
- * @see Abstract
- * Operations - Operations on Objects
- *
Notes
- *
- *
all methods are to deviate from the method signature defined in the EcmaScript
- * specification, by taking an additional 1st parameter of type Context: (downstream)
- * methods may need the Context object to read flags and we want to avoid having to look
- * up the current context (for performance reasons)
- *
all methods that implement an Abstract Operation as defined by EcmaScript are to be
- * package-scopes methods, to prevent them from being used directly by 3rd party code,
- * which would hamper evolving them over time to adept to newer EcmaScript specifications
- *
a link to the method specification of the specific (EcmaScript) version implemented
- * will be put in the JavaDoc of each method that implements an Abstract Operations
- *
- */
-class AbstractEcmaObjectOperations {
- enum INTEGRITY_LEVEL {
- FROZEN,
- SEALED
- }
-
- /**
- * Implementation of Abstract Object operation testIntegrityLevel as defined by EcmaScript
- *
- * @param cx
- * @param o
- * @param level
- * @return boolean
- * @see TestIntegrityLevel
- */
- static boolean testIntegrityLevel(Context cx, Object o, INTEGRITY_LEVEL level) {
- ScriptableObject obj = ScriptableObject.ensureScriptableObject(o);
-
- if (obj.isExtensible()) return false;
-
- for (Object name : obj.getIds(true, true)) {
- ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, name);
- if (Boolean.TRUE.equals(desc.get("configurable"))) return false;
-
- if (level == INTEGRITY_LEVEL.FROZEN
- && desc.isDataDescriptor(desc)
- && Boolean.TRUE.equals(desc.get("writable"))) return false;
- }
-
- return true;
- }
-
- /**
- * Implementation of Abstract Object operation setIntegrityLevel as defined by EcmaScript
- *
- * @param cx
- * @param o
- * @param level
- * @return boolean
- * @see SetIntegrityLevel
- */
- static boolean setIntegrityLevel(Context cx, Object o, INTEGRITY_LEVEL level) {
- /*
- 1. Assert: Type(O) is Object.
- 2. Assert: level is either sealed or frozen.
- 3. Let status be ? O.[[PreventExtensions]]().
- 4. If status is false, return false.
- 5. Let keys be ? O.[[OwnPropertyKeys]]().
- 6. If level is sealed, then
- a. For each element k of keys, do
- i. Perform ? DefinePropertyOrThrow(O, k, PropertyDescriptor { [[Configurable]]: false }).
- 7. Else,
- a. Assert: level is frozen.
- b. For each element k of keys, do
- i. Let currentDesc be ? O.[[GetOwnProperty]](k).
- ii. If currentDesc is not undefined, then
- 1. If IsAccessorDescriptor(currentDesc) is true, then
- a. Let desc be the PropertyDescriptor { [[Configurable]]: false }.
- 2. Else,
- a. Let desc be the PropertyDescriptor { [[Configurable]]: false, [[Writable]]: false }.
- 3. Perform ? DefinePropertyOrThrow(O, k, desc).
- 8. Return true.
-
- NOTES
- - While steps 6.a.i and 7.b.ii.3 call for the Abstract DefinePropertyOrThrow operation,
- the conditions under which a throw would occur aren't applicable when freezing or sealing an object,
- see https://262.ecma-international.org/11.0/#sec-validateandapplypropertydescriptor:
- 1. n/a
- 2. current cannot be undefined, because the logic operates only on existing properties
- 3. n/a
- 4. this code doesn't ever set configurable to true or modifies the enumerable property
- 5. n/a
- 6. as current and desc start out the same and the writable property is set only after checking if isDataDescriptor == true, this condition cannot occur
- 7. both conditions under which false would be returned cannot occur here
- 8. both conditions under which false would be returned cannot occur here
- */
- ScriptableObject obj = ScriptableObject.ensureScriptableObject(o);
-
- // TODO check .preventExtensions() return value once implemented and act accordingly to spec
- obj.preventExtensions();
-
- for (Object key : obj.getIds(true, true)) {
- ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, key);
-
- if (level == INTEGRITY_LEVEL.SEALED) {
- if (Boolean.TRUE.equals(desc.get("configurable"))) {
- desc.put("configurable", desc, false);
-
- obj.defineOwnProperty(cx, key, desc, false);
- }
- } else {
- if (obj.isDataDescriptor(desc) && Boolean.TRUE.equals(desc.get("writable"))) {
- desc.put("writable", desc, false);
- }
- if (Boolean.TRUE.equals(desc.get("configurable"))) {
- desc.put("configurable", desc, false);
- }
- obj.defineOwnProperty(cx, key, desc, false);
- }
- }
-
- return true;
- }
-
- /**
- * Implement the ECMAScript abstract operation "SpeciesConstructor" defined in section 7.2.33 of
- * ECMA262.
- *
- * @param cx context
- * @param s the object for which we will find the "species constructor" as per the spec
- * @param defaultConstructor as per the spec, the value that will be returned if there is no
- * constructor on "s" or if the "species" symbol is not set.
- * @see
- */
- public static Constructable speciesConstructor(
- Context cx, Scriptable s, Constructable defaultConstructor) {
- /*
- The abstract operation SpeciesConstructor takes arguments O (an Object) and
- defaultConstructor (a constructor). It is used to retrieve the constructor that should
- be used to create new objects that are derived from O. defaultConstructor is the
- constructor to use if a constructor @@species property cannot be found starting from O.
- It performs the following steps when called:
-
- 1. Assert: Type(O) is Object.
- 2. Let C be ? Get(O, "constructor").
- 3. If C is undefined, return defaultConstructor.
- 4. If Type(C) is not Object, throw a TypeError exception.
- 5. Let S be ? Get(C, @@species).
- 6. If S is either undefined or null, return defaultConstructor.
- 7. If IsConstructor(S) is true, return S.
- 8. Throw a TypeError exception.
- */
- Object constructor = ScriptableObject.getProperty(s, "constructor");
- if (constructor == Scriptable.NOT_FOUND || Undefined.isUndefined(constructor)) {
- return defaultConstructor;
- }
- if (!ScriptRuntime.isObject(constructor)) {
- throw ScriptRuntime.typeErrorById(
- "msg.arg.not.object", ScriptRuntime.typeof(constructor));
- }
- Object species = ScriptableObject.getProperty((Scriptable) constructor, SymbolKey.SPECIES);
- if (species == Scriptable.NOT_FOUND || species == null || Undefined.isUndefined(species)) {
- return defaultConstructor;
- }
- if (!(species instanceof Constructable)) {
- throw ScriptRuntime.typeErrorById("msg.not.ctor", ScriptRuntime.typeof(species));
- }
- return (Constructable) species;
- }
-}
diff --git a/src/main/java/org/mozilla/javascript/AccessorSlot.java b/src/main/java/org/mozilla/javascript/AccessorSlot.java
deleted file mode 100644
index adf1d58b..00000000
--- a/src/main/java/org/mozilla/javascript/AccessorSlot.java
+++ /dev/null
@@ -1,206 +0,0 @@
-package org.mozilla.javascript;
-
-/**
- * This is a specialization of Slot to store various types of values that are retrieved dynamically
- * using Java and JavaScript functions. Unlike LambdaSlot, the fact that these values are accessed
- * and mutated by functions is visible via the slot's property descriptor.
- */
-public class AccessorSlot extends Slot {
- private static final long serialVersionUID = 1677840254177335827L;
-
- AccessorSlot(Slot oldSlot) {
- super(oldSlot);
- }
-
- // The Getter and Setter may each be of a different type (JavaScript function, Java
- // function, or neither). So, use an abstraction to distinguish them.
- transient Getter getter;
- transient Setter setter;
-
- @Override
- boolean isValueSlot() {
- return false;
- }
-
- @Override
- boolean isSetterSlot() {
- return true;
- }
-
- @Override
- ScriptableObject getPropertyDescriptor(Context cx, Scriptable scope) {
- // It sounds logical that this would be the same as the logic for a normal Slot,
- // but the spec is super pedantic about things like the order of properties here,
- // so we need special support here.
- ScriptableObject desc = (ScriptableObject) cx.newObject(scope);
- desc.setCommonDescriptorProperties(getAttributes(), getter == null && setter == null);
- String fName = name == null ? "f" : name.toString();
- if (getter != null) {
- Function f = getter.asGetterFunction(fName, scope);
- desc.defineProperty("get", f == null ? Undefined.instance : f, ScriptableObject.EMPTY);
- }
- if (setter != null) {
- Function f = setter.asSetterFunction(fName, scope);
- desc.defineProperty("set", f == null ? Undefined.instance : f, ScriptableObject.EMPTY);
- }
- return desc;
- }
-
- @Override
- public boolean setValue(Object value, Scriptable owner, Scriptable start) {
- if (setter == null) {
- if (getter != null) {
- throwNoSetterException(start, value);
- return true;
- }
- } else {
- return setter.setValue(value, owner, start);
- }
- return super.setValue(value, owner, start);
- }
-
- @Override
- public Object getValue(Scriptable start) {
- if (getter != null) {
- return getter.getValue(start);
- }
- return super.getValue(start);
- }
-
- @Override
- Function getSetterFunction(String name, Scriptable scope) {
- if (setter == null) {
- return null;
- }
- return setter.asSetterFunction(name, scope);
- }
-
- @Override
- Function getGetterFunction(String name, Scriptable scope) {
- if (getter == null) {
- return null;
- }
- return getter.asGetterFunction(name, scope);
- }
-
- // # INNER CORE MODIFIED PART
- public interface Getter {
- // # END
- Object getValue(Scriptable start);
-
- Function asGetterFunction(final String name, final Scriptable scope);
- }
-
- /** This is a Getter that delegates to a Java function via a MemberBox. */
- static final class MemberBoxGetter implements Getter {
- final MemberBox member;
-
- MemberBoxGetter(MemberBox member) {
- this.member = member;
- }
-
- @Override
- public Object getValue(Scriptable start) {
- if (member.delegateTo == null) {
- return member.invoke(start, ScriptRuntime.emptyArgs);
- }
- return member.invoke(member.delegateTo, new Object[] {start});
- }
-
- @Override
- public Function asGetterFunction(String name, Scriptable scope) {
- return member.asGetterFunction(name, scope);
- }
- }
-
- /** This is a getter that delegates to a JavaScript function. */
- static final class FunctionGetter implements Getter {
- // The value of the function might actually be Undefined, so we need an Object here.
- final Object target;
-
- FunctionGetter(Object target) {
- this.target = target;
- }
-
- @Override
- public Object getValue(Scriptable start) {
- if (target instanceof Function) {
- Function t = (Function) target;
- Context cx = Context.getContext();
- return t.call(cx, t.getParentScope(), start, ScriptRuntime.emptyArgs);
- }
- return Undefined.instance;
- }
-
- @Override
- public Function asGetterFunction(String name, Scriptable scope) {
- return target instanceof Function ? (Function) target : null;
- }
- }
-
- // # INNER CORE MODIFIED PART
- public interface Setter {
- // # END
- boolean setValue(Object value, Scriptable owner, Scriptable start);
-
- Function asSetterFunction(final String name, final Scriptable scope);
- }
-
- /** Invoke the setter on this slot via reflection using MemberBox. */
- static final class MemberBoxSetter implements Setter {
- final MemberBox member;
-
- MemberBoxSetter(MemberBox member) {
- this.member = member;
- }
-
- @Override
- public boolean setValue(Object value, Scriptable owner, Scriptable start) {
- Context cx = Context.getContext();
- Class>[] pTypes = member.argTypes;
- // XXX: cache tag since it is already calculated in
- // defineProperty ?
- Class> valueType = pTypes[pTypes.length - 1];
- int tag = FunctionObject.getTypeTag(valueType);
- Object actualArg = FunctionObject.convertArg(cx, start, value, tag);
-
- if (member.delegateTo == null) {
- member.invoke(start, new Object[] {actualArg});
- } else {
- member.invoke(member.delegateTo, new Object[] {start, actualArg});
- }
- return true;
- }
-
- @Override
- public Function asSetterFunction(String name, Scriptable scope) {
- return member.asSetterFunction(name, scope);
- }
- }
-
- /**
- * Invoke the setter as a JavaScript function, taking care that it might actually be Undefined.
- */
- static final class FunctionSetter implements Setter {
- final Object target;
-
- FunctionSetter(Object target) {
- this.target = target;
- }
-
- @Override
- public boolean setValue(Object value, Scriptable owner, Scriptable start) {
- if (target instanceof Function) {
- Function t = (Function) target;
- Context cx = Context.getContext();
- t.call(cx, t.getParentScope(), start, new Object[] {value});
- }
- return true;
- }
-
- @Override
- public Function asSetterFunction(String name, Scriptable scope) {
- return target instanceof Function ? (Function) target : null;
- }
- }
-}
diff --git a/src/main/java/org/mozilla/javascript/Arguments.java b/src/main/java/org/mozilla/javascript/Arguments.java
deleted file mode 100644
index a8bc9250..00000000
--- a/src/main/java/org/mozilla/javascript/Arguments.java
+++ /dev/null
@@ -1,454 +0,0 @@
-/* -*- 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.javascript;
-
-import org.mozilla.javascript.NativeArrayIterator.ARRAY_ITERATOR_TYPE;
-
-/**
- * This class implements the "arguments" object.
- *
- *
See ECMA 10.1.8
- *
- * @see org.mozilla.javascript.NativeCall
- * @author Norris Boyd
- */
-final class Arguments extends IdScriptableObject {
- private static final long serialVersionUID = 4275508002492040609L;
-
- private static final String FTAG = "Arguments";
-
- public Arguments(NativeCall activation) {
- this.activation = activation;
-
- Scriptable parent = activation.getParentScope();
- setParentScope(parent);
- setPrototype(ScriptableObject.getObjectPrototype(parent));
-
- args = activation.originalArgs;
- lengthObj = Integer.valueOf(args.length);
-
- NativeFunction f = activation.function;
- calleeObj = f;
-
- int version = f.getLanguageVersion();
- if (version <= Context.VERSION_1_3 && version != Context.VERSION_DEFAULT) {
- callerObj = null;
- } else {
- callerObj = NOT_FOUND;
- }
-
- defineProperty(SymbolKey.ITERATOR, iteratorMethod, ScriptableObject.DONTENUM);
- }
-
- @Override
- public String getClassName() {
- return FTAG;
- }
-
- private Object arg(int index) {
- if (index < 0 || args.length <= index) return NOT_FOUND;
- return args[index];
- }
-
- // the following helper methods assume that 0 < index < args.length
-
- private void putIntoActivation(int index, Object value) {
- String argName = activation.function.getParamOrVarName(index);
- activation.put(argName, activation, value);
- }
-
- private Object getFromActivation(int index) {
- String argName = activation.function.getParamOrVarName(index);
- return activation.get(argName, activation);
- }
-
- private void replaceArg(int index, Object value) {
- if (sharedWithActivation(index)) {
- putIntoActivation(index, value);
- }
- synchronized (this) {
- if (args == activation.originalArgs) {
- args = args.clone();
- }
- args[index] = value;
- }
- }
-
- private void removeArg(int index) {
- synchronized (this) {
- if (args[index] != NOT_FOUND) {
- if (args == activation.originalArgs) {
- args = args.clone();
- }
- args[index] = NOT_FOUND;
- }
- }
- }
-
- // end helpers
-
- @Override
- public boolean has(int index, Scriptable start) {
- if (arg(index) != NOT_FOUND) {
- return true;
- }
- return super.has(index, start);
- }
-
- @Override
- public Object get(int index, Scriptable start) {
- final Object value = arg(index);
- if (value == NOT_FOUND) {
- return super.get(index, start);
- }
- if (sharedWithActivation(index)) {
- return getFromActivation(index);
- }
- return value;
- }
-
- private boolean sharedWithActivation(int index) {
- Context cx = Context.getContext();
- if (cx.isStrictMode()) {
- return false;
- }
- NativeFunction f = activation.function;
- int definedCount = f.getParamCount();
- if (index < definedCount) {
- // Check if argument is not hidden by later argument with the same
- // name as hidden arguments are not shared with activation
- if (index < definedCount - 1) {
- String argName = f.getParamOrVarName(index);
- for (int i = index + 1; i < definedCount; i++) {
- if (argName.equals(f.getParamOrVarName(i))) {
- return false;
- }
- }
- }
- return true;
- }
- return false;
- }
-
- @Override
- public void put(int index, Scriptable start, Object value) {
- if (arg(index) == NOT_FOUND) {
- super.put(index, start, value);
- } else {
- replaceArg(index, value);
- }
- }
-
- @Override
- public void put(String name, Scriptable start, Object value) {
- super.put(name, start, value);
- }
-
- @Override
- public void delete(int index) {
- if (0 <= index && index < args.length) {
- removeArg(index);
- }
- super.delete(index);
- }
-
- private static final int Id_callee = 1,
- Id_length = 2,
- Id_caller = 3,
- MAX_INSTANCE_ID = Id_caller;
-
- @Override
- protected int getMaxInstanceId() {
- return MAX_INSTANCE_ID;
- }
-
- @Override
- protected int findInstanceIdInfo(String s) {
- int id;
- switch (s) {
- case "callee":
- id = Id_callee;
- break;
- case "length":
- id = Id_length;
- break;
- case "caller":
- id = Id_caller;
- break;
- default:
- id = 0;
- break;
- }
- Context cx = Context.getContext();
- if (cx.isStrictMode()) {
- if (id == Id_callee || id == Id_caller) {
- return super.findInstanceIdInfo(s);
- }
- }
-
- if (id == 0) return super.findInstanceIdInfo(s);
-
- int attr;
- switch (id) {
- case Id_callee:
- attr = calleeAttr;
- break;
- case Id_caller:
- attr = callerAttr;
- break;
- case Id_length:
- attr = lengthAttr;
- break;
- default:
- throw new IllegalStateException();
- }
- return instanceIdInfo(attr, id);
- }
-
- @Override
- protected String getInstanceIdName(int id) {
- switch (id) {
- case Id_callee:
- return "callee";
- case Id_length:
- return "length";
- case Id_caller:
- return "caller";
- }
- return null;
- }
-
- @Override
- protected Object getInstanceIdValue(int id) {
- switch (id) {
- case Id_callee:
- return calleeObj;
- case Id_length:
- return lengthObj;
- case Id_caller:
- {
- Object value = callerObj;
- if (value == UniqueTag.NULL_VALUE) {
- value = null;
- } else if (value == null) {
- NativeCall caller = activation.parentActivationCall;
- if (caller != null) {
- value = caller.get("arguments", caller);
- }
- }
- return value;
- }
- }
- return super.getInstanceIdValue(id);
- }
-
- @Override
- protected void setInstanceIdValue(int id, Object value) {
- switch (id) {
- case Id_callee:
- calleeObj = value;
- return;
- case Id_length:
- lengthObj = value;
- return;
- case Id_caller:
- callerObj = (value != null) ? value : UniqueTag.NULL_VALUE;
- return;
- }
- super.setInstanceIdValue(id, value);
- }
-
- @Override
- protected void setInstanceIdAttributes(int id, int attr) {
- switch (id) {
- case Id_callee:
- calleeAttr = attr;
- return;
- case Id_length:
- lengthAttr = attr;
- return;
- case Id_caller:
- callerAttr = attr;
- return;
- }
- super.setInstanceIdAttributes(id, attr);
- }
-
- @Override
- Object[] getIds(boolean getNonEnumerable, boolean getSymbols) {
- Object[] ids = super.getIds(getNonEnumerable, getSymbols);
- if (args.length != 0) {
- boolean[] present = new boolean[args.length];
- int extraCount = args.length;
- for (int i = 0; i != ids.length; ++i) {
- Object id = ids[i];
- if (id instanceof Integer) {
- int index = ((Integer) id).intValue();
- if (0 <= index && index < args.length) {
- if (!present[index]) {
- present[index] = true;
- extraCount--;
- }
- }
- }
- }
- if (!getNonEnumerable) { // avoid adding args which were redefined to non-enumerable
- for (int i = 0; i < present.length; i++) {
- if (!present[i] && super.has(i, this)) {
- present[i] = true;
- extraCount--;
- }
- }
- }
- if (extraCount != 0) {
- Object[] tmp = new Object[extraCount + ids.length];
- System.arraycopy(ids, 0, tmp, extraCount, ids.length);
- ids = tmp;
- int offset = 0;
- for (int i = 0; i != args.length; ++i) {
- if (!present[i]) {
- ids[offset] = Integer.valueOf(i);
- ++offset;
- }
- }
- if (offset != extraCount) Kit.codeBug();
- }
- }
- return ids;
- }
-
- @Override
- protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) {
- if (ScriptRuntime.isSymbol(id) || id instanceof Scriptable) {
- return super.getOwnPropertyDescriptor(cx, id);
- }
-
- double d = ScriptRuntime.toNumber(id);
- int index = (int) d;
- if (d != index) {
- return super.getOwnPropertyDescriptor(cx, id);
- }
- Object value = arg(index);
- if (value == NOT_FOUND) {
- return super.getOwnPropertyDescriptor(cx, id);
- }
- if (sharedWithActivation(index)) {
- value = getFromActivation(index);
- }
- if (super.has(index, this)) { // the descriptor has been redefined
- ScriptableObject desc = super.getOwnPropertyDescriptor(cx, id);
- desc.put("value", desc, value);
- return desc;
- }
- Scriptable scope = getParentScope();
- if (scope == null) scope = this;
- return buildDataDescriptor(scope, value, EMPTY);
- }
-
- @Override
- protected void defineOwnProperty(
- Context cx, Object id, ScriptableObject desc, boolean checkValid) {
- super.defineOwnProperty(cx, id, desc, checkValid);
- if (ScriptRuntime.isSymbol(id)) {
- return;
- }
-
- double d = ScriptRuntime.toNumber(id);
- int index = (int) d;
- if (d != index) return;
-
- Object value = arg(index);
- if (value == NOT_FOUND) return;
-
- if (isAccessorDescriptor(desc)) {
- removeArg(index);
- return;
- }
-
- Object newValue = getProperty(desc, "value");
- if (newValue == NOT_FOUND) return;
-
- replaceArg(index, newValue);
-
- if (isFalse(getProperty(desc, "writable"))) {
- removeArg(index);
- }
- }
-
- // ECMAScript2015
- // 9.4.4.6 CreateUnmappedArgumentsObject(argumentsList)
- // 8. Perform DefinePropertyOrThrow(obj, "caller", PropertyDescriptor {[[Get]]:
- // %ThrowTypeError%,
- // [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, [[Configurable]]: false}).
- // 9. Perform DefinePropertyOrThrow(obj, "callee", PropertyDescriptor {[[Get]]:
- // %ThrowTypeError%,
- // [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, [[Configurable]]: false}).
- void defineAttributesForStrictMode() {
- Context cx = Context.getContext();
- if (!cx.isStrictMode()) {
- return;
- }
- setGetterOrSetter("caller", 0, new ThrowTypeError("caller"), true);
- setGetterOrSetter("caller", 0, new ThrowTypeError("caller"), false);
- setGetterOrSetter("callee", 0, new ThrowTypeError("callee"), true);
- setGetterOrSetter("callee", 0, new ThrowTypeError("callee"), false);
- setAttributes("caller", DONTENUM | PERMANENT);
- setAttributes("callee", DONTENUM | PERMANENT);
- callerObj = null;
- calleeObj = null;
- }
-
- private static BaseFunction iteratorMethod =
- new BaseFunction() {
- private static final long serialVersionUID = 4239122318596177391L;
-
- @Override
- public Object call(
- Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
- // TODO : call %ArrayProto_values%
- // 9.4.4.6 CreateUnmappedArgumentsObject(argumentsList)
- // 1. Perform DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor
- // {[[Value]]:%ArrayProto_values%,
- // [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}).
- return new NativeArrayIterator(scope, thisObj, ARRAY_ITERATOR_TYPE.VALUES);
- }
- };
-
- private static class ThrowTypeError extends BaseFunction {
- private static final long serialVersionUID = -744615873947395749L;
- private String propertyName;
-
- ThrowTypeError(String propertyName) {
- this.propertyName = propertyName;
- }
-
- @Override
- public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
- throw ScriptRuntime.typeErrorById("msg.arguments.not.access.strict", propertyName);
- }
- }
-
- // Fields to hold caller, callee and length properties,
- // where NOT_FOUND value tags deleted properties.
- // In addition if callerObj == NULL_VALUE, it tags null for scripts, as
- // initial callerObj == null means access to caller arguments available
- // only in JS <= 1.3 scripts
- private Object callerObj;
- private Object calleeObj;
- private Object lengthObj;
-
- private int callerAttr = DONTENUM;
- private int calleeAttr = DONTENUM;
- private int lengthAttr = DONTENUM;
-
- private NativeCall activation;
-
- // Initially args holds activation.getOriginalArgs(), but any modification
- // of its elements triggers creation of a copy. If its element holds NOT_FOUND,
- // it indicates deleted index, in which case super class is queried.
- private Object[] args;
-}
diff --git a/src/main/java/org/mozilla/javascript/ArrowFunction.java b/src/main/java/org/mozilla/javascript/ArrowFunction.java
deleted file mode 100644
index 0f64f804..00000000
--- a/src/main/java/org/mozilla/javascript/ArrowFunction.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/* -*- 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.javascript;
-
-/**
- * The class for Arrow Function Definitions
- * EcmaScript 6 Rev 14, March 8, 2013 Draft spec , 13.2
- */
-public class ArrowFunction extends BaseFunction {
-
- private static final long serialVersionUID = -7377989503697220633L;
-
- private final Callable targetFunction;
- private final Scriptable boundThis;
-
- public ArrowFunction(Context cx, Scriptable scope, Callable targetFunction, Scriptable boundThis)
- {
- this.targetFunction = targetFunction;
- this.boundThis = boundThis;
-
- ScriptRuntime.setFunctionProtoAndParent(this, scope);
-
- Function thrower = ScriptRuntime.typeErrorThrower(cx);
- NativeObject throwing = new NativeObject();
- throwing.put("get", throwing, thrower);
- throwing.put("set", throwing, thrower);
- throwing.put("enumerable", throwing, Boolean.FALSE);
- throwing.put("configurable", throwing, Boolean.FALSE);
- throwing.preventExtensions();
-
- this.defineOwnProperty(cx, "caller", throwing, false);
- this.defineOwnProperty(cx, "arguments", throwing, false);
- }
-
- @Override
- public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args)
- {
- Scriptable callThis = boundThis != null ? boundThis : ScriptRuntime.getTopCallScope(cx);
- return targetFunction.call(cx, scope, callThis, args);
- }
-
- @Override
- public Scriptable construct(Context cx, Scriptable scope, Object[] args) {
- throw ScriptRuntime.typeErrorById("msg.not.ctor", decompile(0, 0));
- }
-
- @Override
- public boolean hasInstance(Scriptable instance) {
- if (targetFunction instanceof Function) {
- return ((Function) targetFunction).hasInstance(instance);
- }
- throw ScriptRuntime.typeErrorById("msg.not.ctor");
- }
-
- @Override
- public int getLength() {
- if (targetFunction instanceof BaseFunction) {
- return ((BaseFunction) targetFunction).getLength();
- }
- return 0;
- }
-
- @Override
- public int getArity() {
- return getLength();
- }
-
- @Override
- String decompile(int indent, int flags)
- {
- if (targetFunction instanceof BaseFunction) {
- return ((BaseFunction)targetFunction).decompile(indent, flags);
- }
- return super.decompile(indent, flags);
- }
-
- static boolean equalObjectGraphs(ArrowFunction f1, ArrowFunction f2, EqualObjectGraphs eq) {
- return eq.equalGraphs(f1.boundThis, f2.boundThis) && eq.equalGraphs(f1.targetFunction, f2.targetFunction);
- }
-}
diff --git a/src/main/java/org/mozilla/javascript/BaseFunction.java b/src/main/java/org/mozilla/javascript/BaseFunction.java
deleted file mode 100644
index 4cda3ac8..00000000
--- a/src/main/java/org/mozilla/javascript/BaseFunction.java
+++ /dev/null
@@ -1,651 +0,0 @@
-/* -*- 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.javascript;
-
-/**
- * The base class for Function objects. That is one of two purposes. It is also the prototype for
- * every "function" defined except those that are used as GeneratorFunctions via the ES6 "function
- * *" syntax.
- *
- *
See ECMA 15.3.
- *
- * @author Norris Boyd
- */
-public class BaseFunction extends IdScriptableObject implements Function {
- private static final long serialVersionUID = 5311394446546053859L;
-
- private static final Object FUNCTION_TAG = "Function";
- private static final String FUNCTION_CLASS = "Function";
- static final String GENERATOR_FUNCTION_CLASS = "__GeneratorFunction";
-
- static void init(Scriptable scope, boolean sealed) {
- BaseFunction obj = new BaseFunction();
- // Function.prototype attributes: see ECMA 15.3.3.1
- obj.prototypePropertyAttributes = DONTENUM | READONLY | PERMANENT;
- obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
- }
-
- static Object initAsGeneratorFunction(Scriptable scope, boolean sealed) {
- BaseFunction obj = new BaseFunction(true);
- // Function.prototype attributes: see ECMA 15.3.3.1
- obj.prototypePropertyAttributes = READONLY | PERMANENT;
- obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
- // The "GeneratorFunction" name actually never appears in the global scope.
- // Return it here so it can be cached as a "builtin"
- return ScriptableObject.getProperty(scope, GENERATOR_FUNCTION_CLASS);
- }
-
- public BaseFunction() {}
-
- public BaseFunction(boolean isGenerator) {
- this.isGeneratorFunction = isGenerator;
- }
-
- public BaseFunction(Scriptable scope, Scriptable prototype) {
- super(scope, prototype);
- }
-
- @Override
- public String getClassName() {
- return isGeneratorFunction() ? GENERATOR_FUNCTION_CLASS : FUNCTION_CLASS;
- }
-
- // Generated code will override this
- protected boolean isGeneratorFunction() {
- return isGeneratorFunction;
- }
-
- /**
- * Gets the value returned by calling the typeof operator on this object.
- *
- * @see org.mozilla.javascript.ScriptableObject#getTypeOf()
- * @return "function" or "undefined" if {@link #avoidObjectDetection()} returns true
- *
- */
- @Override
- public String getTypeOf() {
- return avoidObjectDetection() ? "undefined" : "function";
- }
-
- /**
- * Implements the instanceof operator for JavaScript Function objects.
- *
- *
- * foo = new Foo();
- * foo instanceof Foo; // true
- *
- *
- * @param instance The value that appeared on the LHS of the instanceof operator
- * @return true if the "prototype" property of "this" appears in value's prototype chain
- */
- @Override
- public boolean hasInstance(Scriptable instance) {
- Object protoProp = ScriptableObject.getProperty(this, "prototype");
- if (protoProp instanceof Scriptable) {
- return ScriptRuntime.jsDelegatesTo(instance, (Scriptable) protoProp);
- }
- throw ScriptRuntime.typeErrorById("msg.instanceof.bad.prototype", getFunctionName());
- }
-
- private static final int Id_length = 1,
- Id_arity = 2,
- Id_name = 3,
- Id_prototype = 4,
- Id_arguments = 5,
- MAX_INSTANCE_ID = 5;
-
- @Override
- protected int getMaxInstanceId() {
- return MAX_INSTANCE_ID;
- }
-
- @Override
- protected int findInstanceIdInfo(String s) {
- switch (s) {
- case "length":
- if (lengthPropertyAttributes >= 0) {
- return instanceIdInfo(lengthPropertyAttributes, Id_length);
- }
- break;
- case "arity":
- if (arityPropertyAttributes >= 0) {
- return instanceIdInfo(arityPropertyAttributes, Id_arity);
- }
- break;
- case "name":
- if (namePropertyAttributes >= 0) {
- return instanceIdInfo(namePropertyAttributes, Id_name);
- }
- break;
- case "prototype":
- if (hasPrototypeProperty()) {
- return instanceIdInfo(prototypePropertyAttributes, Id_prototype);
- }
- break;
- case "arguments":
- return instanceIdInfo(argumentsAttributes, Id_arguments);
- default:
- break;
- }
-
- return super.findInstanceIdInfo(s);
- }
-
- @Override
- protected String getInstanceIdName(int id) {
- switch (id) {
- case Id_length:
- return "length";
- case Id_arity:
- return "arity";
- case Id_name:
- return "name";
- case Id_prototype:
- return "prototype";
- case Id_arguments:
- return "arguments";
- }
- return super.getInstanceIdName(id);
- }
-
- @Override
- protected Object getInstanceIdValue(int id) {
- switch (id) {
- case Id_length:
- return lengthPropertyAttributes >= 0 ? getLength() : NOT_FOUND;
- case Id_arity:
- return arityPropertyAttributes >= 0 ? getArity() : NOT_FOUND;
- case Id_name:
- return namePropertyAttributes >= 0 ? getFunctionName() : NOT_FOUND;
- case Id_prototype:
- return getPrototypeProperty();
- case Id_arguments:
- return getArguments();
- }
- return super.getInstanceIdValue(id);
- }
-
- @Override
- protected void setInstanceIdValue(int id, Object value) {
- switch (id) {
- case Id_prototype:
- if ((prototypePropertyAttributes & READONLY) == 0) {
- prototypeProperty = (value != null) ? value : UniqueTag.NULL_VALUE;
- }
- return;
- case Id_arguments:
- if (value == NOT_FOUND) {
- // This should not be called since "arguments" is PERMANENT
- Kit.codeBug();
- }
- if (defaultHas("arguments")) {
- defaultPut("arguments", value);
- } else if ((argumentsAttributes & READONLY) == 0) {
- argumentsObj = value;
- }
- return;
- case Id_name:
- if (value == NOT_FOUND) {
- namePropertyAttributes = -1;
- }
- return;
- case Id_arity:
- if (value == NOT_FOUND) {
- arityPropertyAttributes = -1;
- }
- return;
- case Id_length:
- if (value == NOT_FOUND) {
- lengthPropertyAttributes = -1;
- }
- return;
- }
- super.setInstanceIdValue(id, value);
- }
-
- @Override
- protected void setInstanceIdAttributes(int id, int attr) {
- switch (id) {
- case Id_prototype:
- prototypePropertyAttributes = attr;
- return;
- case Id_arguments:
- argumentsAttributes = attr;
- return;
- case Id_arity:
- arityPropertyAttributes = attr;
- return;
- case Id_name:
- namePropertyAttributes = attr;
- return;
- case Id_length:
- lengthPropertyAttributes = attr;
- return;
- }
- super.setInstanceIdAttributes(id, attr);
- }
-
- @Override
- protected void fillConstructorProperties(IdFunctionObject ctor) {
- // Fix up bootstrapping problem: getPrototype of the IdFunctionObject
- // can not return Function.prototype because Function object is not
- // yet defined.
- ctor.setPrototype(this);
- super.fillConstructorProperties(ctor);
- }
-
- @Override
- protected void initPrototypeId(int id) {
- String s;
- int arity;
- switch (id) {
- case Id_constructor:
- arity = 1;
- s = "constructor";
- break;
- case Id_toString:
- arity = 0;
- s = "toString";
- break;
- case Id_toSource:
- arity = 1;
- s = "toSource";
- break;
- case Id_apply:
- arity = 2;
- s = "apply";
- break;
- case Id_call:
- arity = 1;
- s = "call";
- break;
- case Id_bind:
- arity = 1;
- s = "bind";
- break;
- default:
- throw new IllegalArgumentException(String.valueOf(id));
- }
- initPrototypeMethod(FUNCTION_TAG, id, s, arity);
- }
-
- static boolean isApply(IdFunctionObject f) {
- return f.hasTag(FUNCTION_TAG) && f.methodId() == Id_apply;
- }
-
- static boolean isApplyOrCall(IdFunctionObject f) {
- if (f.hasTag(FUNCTION_TAG)) {
- switch (f.methodId()) {
- case Id_apply:
- case Id_call:
- return true;
- }
- }
- return false;
- }
-
- @Override
- public Object execIdCall(
- IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
- if (!f.hasTag(FUNCTION_TAG)) {
- return super.execIdCall(f, cx, scope, thisObj, args);
- }
- int id = f.methodId();
- switch (id) {
- case Id_constructor:
- return jsConstructor(cx, scope, args);
-
- case Id_toString:
- {
- BaseFunction realf = realFunction(thisObj, f);
- int indent = ScriptRuntime.toInt32(args, 0);
- return realf.decompile(indent, 0);
- }
-
- case Id_toSource:
- {
- BaseFunction realf = realFunction(thisObj, f);
- int indent = 0;
- int flags = Decompiler.TO_SOURCE_FLAG;
- if (args.length != 0) {
- indent = ScriptRuntime.toInt32(args[0]);
- if (indent >= 0) {
- flags = 0;
- } else {
- indent = 0;
- }
- }
- return realf.decompile(indent, flags);
- }
-
- case Id_apply:
- case Id_call:
- return ScriptRuntime.applyOrCall(id == Id_apply, cx, scope, thisObj, args);
-
- case Id_bind:
- if (!(thisObj instanceof Callable)) {
- throw ScriptRuntime.notFunctionError(thisObj);
- }
- Callable targetFunction = (Callable) thisObj;
- int argc = args.length;
- final Scriptable boundThis;
- final Object[] boundArgs;
- if (argc > 0) {
- boundThis = ScriptRuntime.toObjectOrNull(cx, args[0], scope);
- boundArgs = new Object[argc - 1];
- System.arraycopy(args, 1, boundArgs, 0, argc - 1);
- } else {
- boundThis = null;
- boundArgs = ScriptRuntime.emptyArgs;
- }
- return new BoundFunction(cx, scope, targetFunction, boundThis, boundArgs);
- }
- throw new IllegalArgumentException(String.valueOf(id));
- }
-
- private static BaseFunction realFunction(Scriptable thisObj, IdFunctionObject f) {
- if (thisObj == null) {
- throw ScriptRuntime.notFunctionError(null);
- }
- Object x = thisObj.getDefaultValue(ScriptRuntime.FunctionClass);
- if (x instanceof Delegator) {
- x = ((Delegator) x).getDelegee();
- }
- return ensureType(x, BaseFunction.class, f);
- }
-
- /** Make value as DontEnum, DontDelete, ReadOnly prototype property of this Function object */
- public void setImmunePrototypeProperty(Object value) {
- if ((prototypePropertyAttributes & READONLY) != 0) {
- throw new IllegalStateException();
- }
- prototypeProperty = (value != null) ? value : UniqueTag.NULL_VALUE;
- prototypePropertyAttributes = DONTENUM | PERMANENT | READONLY;
- }
-
- protected Scriptable getClassPrototype() {
- Object protoVal = getPrototypeProperty();
- if (protoVal instanceof Scriptable) {
- return (Scriptable) protoVal;
- }
- return ScriptableObject.getObjectPrototype(this);
- }
-
- /** Should be overridden. */
- @Override
- public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
- return Undefined.instance;
- }
-
- @Override
- public Scriptable construct(Context cx, Scriptable scope, Object[] args) {
- Scriptable result = createObject(cx, scope);
- if (result != null) {
- Object val = call(cx, scope, result, args);
- if (val instanceof Scriptable) {
- result = (Scriptable) val;
- }
- } else {
- Object val = call(cx, scope, null, args);
- if (!(val instanceof Scriptable)) {
- // It is program error not to return Scriptable from
- // the call method if createObject returns null.
- throw new IllegalStateException(
- "Bad implementation of call as constructor, name="
- + getFunctionName()
- + " in "
- + getClass().getName());
- }
- result = (Scriptable) val;
- if (result.getPrototype() == null) {
- Scriptable proto = getClassPrototype();
- if (result != proto) {
- result.setPrototype(proto);
- }
- }
- if (result.getParentScope() == null) {
- Scriptable parent = getParentScope();
- if (result != parent) {
- result.setParentScope(parent);
- }
- }
- }
- return result;
- }
-
- /**
- * Creates new script object. The default implementation of {@link #construct} uses the method
- * to to get the value for thisObj argument when invoking {@link #call}. The methos
- * is allowed to return null to indicate that {@link #call} will create a new
- * object itself. In this case {@link #construct} will set scope and prototype on the result
- * {@link #call} unless they are already set.
- */
- public Scriptable createObject(Context cx, Scriptable scope) {
- Scriptable newInstance = new NativeObject();
- newInstance.setPrototype(getClassPrototype());
- newInstance.setParentScope(getParentScope());
- return newInstance;
- }
-
- /**
- * Decompile the source information associated with this js function/script back into a string.
- *
- * @param indent How much to indent the decompiled result.
- * @param flags Flags specifying format of decompilation output.
- */
- String decompile(int indent, int flags) {
- StringBuilder sb = new StringBuilder();
- boolean justbody = (0 != (flags & Decompiler.ONLY_BODY_FLAG));
- if (!justbody) {
- sb.append("function ");
- sb.append(getFunctionName());
- sb.append("() {\n\t");
- }
- sb.append("[native code, arity=");
- sb.append(getArity());
- sb.append("]\n");
- if (!justbody) {
- sb.append("}\n");
- }
- return sb.toString();
- }
-
- public int getArity() {
- return 0;
- }
-
- public int getLength() {
- return 0;
- }
-
- public String getFunctionName() {
- return "";
- }
-
- /**
- * Sets the attributes of the "name", "length", and "arity" properties, which differ for many
- * native objects.
- */
- public void setStandardPropertyAttributes(int attributes) {
- namePropertyAttributes = attributes;
- lengthPropertyAttributes = attributes;
- arityPropertyAttributes = attributes;
- }
-
- public void setPrototypePropertyAttributes(int attributes) {
- prototypePropertyAttributes = attributes;
- }
-
- protected boolean hasPrototypeProperty() {
- return prototypeProperty != null || this instanceof NativeFunction;
- }
-
- protected Object getPrototypeProperty() {
- Object result = prototypeProperty;
- if (result == null) {
- // only create default prototype on native JavaScript functions,
- // not on built-in functions, java methods, host objects etc.
- if (this instanceof NativeFunction) {
- result = setupDefaultPrototype();
- } else {
- result = Undefined.instance;
- }
- } else if (result == UniqueTag.NULL_VALUE) {
- result = null;
- }
- return result;
- }
-
- protected void setPrototypeProperty(Object prototype) {
- this.prototypeProperty = prototype;
- }
-
- protected synchronized Object setupDefaultPrototype() {
- if (prototypeProperty != null) {
- return prototypeProperty;
- }
- NativeObject obj = new NativeObject();
- obj.defineProperty("constructor", this, DONTENUM);
- // put the prototype property into the object now, then in the
- // wacky case of a user defining a function Object(), we don't
- // get an infinite loop trying to find the prototype.
- prototypeProperty = obj;
- Scriptable proto = getObjectPrototype(this);
- if (proto != obj) {
- // not the one we just made, it must remain grounded
- obj.setPrototype(proto);
- }
- return obj;
- }
-
- private Object getArguments() {
- // .arguments is deprecated, so we use a slow
- // way of getting it that doesn't add to the invocation cost.
- // TODO: add warning, error based on version
- Object value = defaultHas("arguments") ? defaultGet("arguments") : argumentsObj;
- if (value != NOT_FOUND) {
- // Should after changing .arguments its
- // activation still be available during Function call?
- // This code assumes it should not:
- // defaultGet("arguments") != NOT_FOUND
- // means assigned arguments
- return value;
- }
- Context cx = Context.getContext();
- NativeCall activation = ScriptRuntime.findFunctionActivation(cx, this);
- return (activation == null) ? null : activation.get("arguments", activation);
- }
-
- private Object jsConstructor(Context cx, Scriptable scope, Object[] args) {
- int arglen = args.length;
- StringBuilder sourceBuf = new StringBuilder();
-
- sourceBuf.append("function ");
- if (isGeneratorFunction()) {
- sourceBuf.append("* ");
- }
- /* version != 1.2 Function constructor behavior -
- * print 'anonymous' as the function name if the
- * version (under which the function was compiled) is
- * less than 1.2... or if it's greater than 1.2, because
- * we need to be closer to ECMA.
- */
- if (cx.getLanguageVersion() != Context.VERSION_1_2) {
- sourceBuf.append("anonymous");
- }
- sourceBuf.append('(');
-
- // Append arguments as coma separated strings
- for (int i = 0; i < arglen - 1; i++) {
- if (i > 0) {
- sourceBuf.append(',');
- }
- sourceBuf.append(ScriptRuntime.toString(args[i]));
- }
- sourceBuf.append(") {");
- if (arglen != 0) {
- // append function body
- String funBody = ScriptRuntime.toString(args[arglen - 1]);
- sourceBuf.append(funBody);
- }
- sourceBuf.append("\n}");
- String source = sourceBuf.toString();
-
- int[] linep = new int[1];
- String filename = Context.getSourcePositionFromStack(linep);
- if (filename == null) {
- filename = "";
- linep[0] = 1;
- }
-
- String sourceURI = ScriptRuntime.makeUrlForGeneratedScript(false, filename, linep[0]);
-
- Scriptable global = ScriptableObject.getTopLevelScope(scope);
-
- ErrorReporter reporter;
- reporter = DefaultErrorReporter.forEval(cx.getErrorReporter());
-
- Evaluator evaluator = Context.createInterpreter();
- if (evaluator == null) {
- throw new JavaScriptException("Interpreter not present", filename, linep[0]);
- }
-
- // Compile with explicit interpreter instance to force interpreter
- // mode.
- return cx.compileFunction(global, source, evaluator, reporter, sourceURI, 1, null);
- }
-
- @Override
- protected int findPrototypeId(String s) {
- int id;
- switch (s) {
- case "constructor":
- id = Id_constructor;
- break;
- case "toString":
- id = Id_toString;
- break;
- case "toSource":
- id = Id_toSource;
- break;
- case "apply":
- id = Id_apply;
- break;
- case "call":
- id = Id_call;
- break;
- case "bind":
- id = Id_bind;
- break;
- default:
- id = 0;
- break;
- }
- return id;
- }
-
- private static final int Id_constructor = 1,
- Id_toString = 2,
- Id_toSource = 3,
- Id_apply = 4,
- Id_call = 5,
- Id_bind = 6,
- MAX_PROTOTYPE_ID = Id_bind;
-
- private Object prototypeProperty;
- private Object argumentsObj = NOT_FOUND;
- private boolean isGeneratorFunction = false;
-
- // For function object instances, attributes are
- // {configurable:false, enumerable:false};
- // see ECMA 15.3.5.2
- private int prototypePropertyAttributes = PERMANENT | DONTENUM;
- private int argumentsAttributes = PERMANENT | DONTENUM;
- private int arityPropertyAttributes = PERMANENT | READONLY | DONTENUM;
- private int namePropertyAttributes = PERMANENT | READONLY | DONTENUM;
- private int lengthPropertyAttributes = PERMANENT | READONLY | DONTENUM;
-}
diff --git a/src/main/java/org/mozilla/javascript/BoundFunction.java b/src/main/java/org/mozilla/javascript/BoundFunction.java
deleted file mode 100644
index 344ea4e3..00000000
--- a/src/main/java/org/mozilla/javascript/BoundFunction.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/* -*- 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.javascript;
-
-/**
- * The class for results of the Function.bind operation
- * EcmaScript 5 spec, 15.3.4.5
- * @author Raphael Speyer
- */
-public class BoundFunction extends BaseFunction {
-
- private static final long serialVersionUID = 2118137342826470729L;
-
- private final Callable targetFunction;
- private final Scriptable boundThis;
- private final Object[] boundArgs;
- private final int length;
-
- public BoundFunction(Context cx, Scriptable scope, Callable targetFunction, Scriptable boundThis,
- Object[] boundArgs)
- {
- this.targetFunction = targetFunction;
- this.boundThis = boundThis;
- this.boundArgs = boundArgs;
- if (targetFunction instanceof BaseFunction) {
- length = Math.max(0, ((BaseFunction) targetFunction).getLength() - boundArgs.length);
- } else {
- length = 0;
- }
-
- ScriptRuntime.setFunctionProtoAndParent(this, scope);
-
- Function thrower = ScriptRuntime.typeErrorThrower(cx);
- NativeObject throwing = new NativeObject();
- throwing.put("get", throwing, thrower);
- throwing.put("set", throwing, thrower);
- throwing.put("enumerable", throwing, Boolean.FALSE);
- throwing.put("configurable", throwing, Boolean.FALSE);
- throwing.preventExtensions();
-
- this.defineOwnProperty(cx, "caller", throwing, false);
- this.defineOwnProperty(cx, "arguments", throwing, false);
- }
-
- @Override
- public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] extraArgs)
- {
- Scriptable callThis = boundThis != null ? boundThis : ScriptRuntime.getTopCallScope(cx);
- return targetFunction.call(cx, scope, callThis, concat(boundArgs, extraArgs));
- }
-
- @Override
- public Scriptable construct(Context cx, Scriptable scope, Object[] extraArgs) {
- if (targetFunction instanceof Function) {
- return ((Function) targetFunction).construct(cx, scope, concat(boundArgs, extraArgs));
- }
- throw ScriptRuntime.typeErrorById("msg.not.ctor");
- }
-
- @Override
- public boolean hasInstance(Scriptable instance) {
- if (targetFunction instanceof Function) {
- return ((Function) targetFunction).hasInstance(instance);
- }
- throw ScriptRuntime.typeErrorById("msg.not.ctor");
- }
-
- @Override
- public int getLength() {
- return length;
- }
-
- private static Object[] concat(Object[] first, Object[] second) {
- Object[] args = new Object[first.length + second.length];
- System.arraycopy(first, 0, args, 0, first.length);
- System.arraycopy(second, 0, args, first.length, second.length);
- return args;
- }
-
- static boolean equalObjectGraphs(BoundFunction f1, BoundFunction f2, EqualObjectGraphs eq) {
- return eq.equalGraphs(f1.boundThis, f2.boundThis) && eq.equalGraphs(f1.targetFunction, f2.targetFunction) && eq.equalGraphs(f1.boundArgs, f2.boundArgs);
- }
-}
diff --git a/src/main/java/org/mozilla/javascript/Callable.java b/src/main/java/org/mozilla/javascript/Callable.java
deleted file mode 100644
index edbd6620..00000000
--- a/src/main/java/org/mozilla/javascript/Callable.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/* -*- 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.javascript;
-
-/**
- * Generic notion of callable object that can execute some script-related code
- * upon request with specified values for script scope and this objects.
- */
-public interface Callable
-{
- /**
- * Perform the call.
- *
- * @param cx the current Context for this thread
- * @param scope the scope to use to resolve properties.
- * @param thisObj the JavaScript this object
- * @param args the array of arguments
- * @return the result of the call
- */
- public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args);
-}
-
diff --git a/src/main/java/org/mozilla/javascript/ClassCache.java b/src/main/java/org/mozilla/javascript/ClassCache.java
deleted file mode 100644
index 19672cde..00000000
--- a/src/main/java/org/mozilla/javascript/ClassCache.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/* -*- 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.javascript;
-
-import java.io.Serializable;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Cache of generated classes and data structures to access Java runtime from JavaScript.
- *
- * @author Igor Bukanov
- * @since Rhino 1.5 Release 5
- */
-public class ClassCache implements Serializable {
-
- private static final long serialVersionUID = -8866246036237312215L;
- private static final Object AKEY = "ClassCache";
- private volatile boolean cachingIsEnabled = true;
- private transient Map classTable;
- private transient Map> classAdapterCache;
- private transient Map, Object> interfaceAdapterCache;
- private int generatedClassSerial;
- private Scriptable associatedScope;
-
- /**
- * CacheKey is a combination of class and securityContext. This is required when classes are
- * loaded from different security contexts
- */
- static class CacheKey {
- final Class> cls;
- final Object sec;
- /** Constructor. */
- public CacheKey(Class> cls, Object securityContext) {
- this.cls = cls;
- this.sec = securityContext;
- }
-
- @Override
- public int hashCode() {
- int result = cls.hashCode();
- if (sec != null) {
- result = sec.hashCode() * 31;
- }
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- return (obj instanceof CacheKey)
- && Objects.equals(this.cls, ((CacheKey) obj).cls)
- && Objects.equals(this.sec, ((CacheKey) obj).sec);
- }
- }
-
- /**
- * Search for ClassCache object in the given scope. The method first calls {@link
- * ScriptableObject#getTopLevelScope(Scriptable scope)} to get the top most scope and then tries
- * to locate associated ClassCache object in the prototype chain of the top scope.
- *
- * @param scope scope to search for ClassCache object.
- * @return previously associated ClassCache object or a new instance of ClassCache if no
- * ClassCache object was found.
- * @see #associate(ScriptableObject topScope)
- */
- public static ClassCache get(Scriptable scope) {
- ClassCache cache = (ClassCache) ScriptableObject.getTopScopeValue(scope, AKEY);
- if (cache == null) {
- throw new RuntimeException("Can't find top level scope for " + "ClassCache.get");
- }
- return cache;
- }
-
- /**
- * Associate ClassCache object with the given top-level scope. The ClassCache object can only be
- * associated with the given scope once.
- *
- * @param topScope scope to associate this ClassCache object with.
- * @return true if no previous ClassCache objects were embedded into the scope and this
- * ClassCache were successfully associated or false otherwise.
- * @see #get(Scriptable scope)
- */
- public boolean associate(ScriptableObject topScope) {
- if (topScope.getParentScope() != null) {
- // Can only associate cache with top level scope
- throw new IllegalArgumentException();
- }
- if (this == topScope.associateValue(AKEY, this)) {
- associatedScope = topScope;
- return true;
- }
- return false;
- }
-
- /** Empty caches of generated Java classes and Java reflection information. */
- public synchronized void clearCaches() {
- classTable = null;
- classAdapterCache = null;
- interfaceAdapterCache = null;
- }
-
- /** Check if generated Java classes and Java reflection information is cached. */
- public final boolean isCachingEnabled() {
- return cachingIsEnabled;
- }
-
- /**
- * Set whether to cache some values.
- *
- *
By default, the engine will cache the results of Class.getMethods() and
- * similar calls. This can speed execution dramatically, but increases the memory footprint.
- * Also, with caching enabled, references may be held to objects past the lifetime of any real
- * usage.
- *
- *
If caching is enabled and this method is called with a false argument, the
- * caches will be emptied.
- *
- *
Caching is enabled by default.
- *
- * @param enabled if true, caching is enabled
- * @see #clearCaches()
- */
- public synchronized void setCachingEnabled(boolean enabled) {
- if (enabled == cachingIsEnabled) return;
- if (!enabled) clearCaches();
- cachingIsEnabled = enabled;
- }
-
- /** @return a map from classes to associated JavaMembers objects */
- Map getClassCacheMap() {
- if (classTable == null) {
- // Use 1 as concurrency level here and for other concurrent hash maps
- // as we don't expect high levels of sustained concurrent writes.
- classTable = new ConcurrentHashMap(16, 0.75f, 1);
- }
- return classTable;
- }
-
- Map> getInterfaceAdapterCacheMap() {
- if (classAdapterCache == null) {
- classAdapterCache =
- new ConcurrentHashMap>(16, 0.75f, 1);
- }
- return classAdapterCache;
- }
-
- /**
- * @deprecated The method always returns false.
- * @see #setInvokerOptimizationEnabled(boolean enabled)
- */
- @Deprecated
- public boolean isInvokerOptimizationEnabled() {
- return false;
- }
-
- /**
- * @deprecated The method does nothing. Invoker optimization is no longer used by Rhino. On
- * modern JDK like 1.4 or 1.5 the disadvantages of the optimization like increased memory
- * usage or longer initialization time overweight small speed increase that can be gained
- * using generated proxy class to replace reflection.
- */
- @Deprecated
- public synchronized void setInvokerOptimizationEnabled(boolean enabled) {}
-
- /**
- * Internal engine method to return serial number for generated classes to ensure name
- * uniqueness.
- */
- public final synchronized int newClassSerialNumber() {
- return ++generatedClassSerial;
- }
-
- Object getInterfaceAdapter(Class> cl) {
- return interfaceAdapterCache == null ? null : interfaceAdapterCache.get(cl);
- }
-
- synchronized void cacheInterfaceAdapter(Class> cl, Object iadapter) {
- if (cachingIsEnabled) {
- if (interfaceAdapterCache == null) {
- interfaceAdapterCache = new ConcurrentHashMap, Object>(16, 0.75f, 1);
- }
- interfaceAdapterCache.put(cl, iadapter);
- }
- }
-
- Scriptable getAssociatedScope() {
- return associatedScope;
- }
-}
diff --git a/src/main/java/org/mozilla/javascript/ClassShutter.java b/src/main/java/org/mozilla/javascript/ClassShutter.java
deleted file mode 100644
index f425e08e..00000000
--- a/src/main/java/org/mozilla/javascript/ClassShutter.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/* -*- 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/. */
-
-// API class
-
-package org.mozilla.javascript;
-
-/**
-Embeddings that wish to filter Java classes that are visible to scripts
-through the LiveConnect, should implement this interface.
-
-@see Context#setClassShutter(ClassShutter)
-@since 1.5 Release 4
-@author Norris Boyd
-*/
-
- public interface ClassShutter {
-
- /**
- * Return true iff the Java class with the given name should be exposed
- * to scripts.
- *
- * An embedding may filter which Java classes are exposed through
- * LiveConnect to JavaScript scripts.
- *
- * Due to the fact that there is no package reflection in Java,
- * this method will also be called with package names. There
- * is no way for Rhino to tell if "Packages.a.b" is a package name
- * or a class that doesn't exist. What Rhino does is attempt
- * to load each segment of "Packages.a.b.c": It first attempts to
- * load class "a", then attempts to load class "a.b", then
- * finally attempts to load class "a.b.c". On a Rhino installation
- * without any ClassShutter set, and without any of the
- * above classes, the expression "Packages.a.b.c" will result in
- * a [JavaPackage a.b.c] and not an error.
- *
- * With ClassShutter supplied, Rhino will first call
- * visibleToScripts before attempting to look up the class name. If
- * visibleToScripts returns false, the class name lookup is not
- * performed and subsequent Rhino execution assumes the class is
- * not present. So for "java.lang.System.out.println" the lookup
- * of "java.lang.System" is skipped and thus Rhino assumes that
- * "java.lang.System" doesn't exist. So then for "java.lang.System.out",
- * Rhino attempts to load the class "java.lang.System.out" because
- * it assumes that "java.lang.System" is a package name.
- *
- * @param fullClassName the full name of the class (including the package
- * name, with '.' as a delimiter). For example the
- * standard string class is "java.lang.String"
- * @return whether or not to reveal this class to scripts
- */
- public boolean visibleToScripts(String fullClassName);
-}
diff --git a/src/main/java/org/mozilla/javascript/CodeGenerator.java b/src/main/java/org/mozilla/javascript/CodeGenerator.java
deleted file mode 100644
index 4049a630..00000000
--- a/src/main/java/org/mozilla/javascript/CodeGenerator.java
+++ /dev/null
@@ -1,1555 +0,0 @@
-/* -*- 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.javascript;
-
-import java.math.BigInteger;
-import java.util.List;
-import org.mozilla.javascript.ast.AstNode;
-import org.mozilla.javascript.ast.AstRoot;
-import org.mozilla.javascript.ast.Block;
-import org.mozilla.javascript.ast.FunctionNode;
-import org.mozilla.javascript.ast.Jump;
-import org.mozilla.javascript.ast.Scope;
-import org.mozilla.javascript.ast.ScriptNode;
-import org.mozilla.javascript.ast.TemplateCharacters;
-import org.mozilla.javascript.ast.VariableInitializer;
-
-/** Generates bytecode for the Interpreter. */
-class CodeGenerator extends Icode {
-
- private static final int MIN_LABEL_TABLE_SIZE = 32;
- private static final int MIN_FIXUP_TABLE_SIZE = 40;
-
- private CompilerEnvirons compilerEnv;
-
- private boolean itsInFunctionFlag;
- private boolean itsInTryFlag;
-
- private InterpreterData itsData;
-
- private ScriptNode scriptOrFn;
- private int iCodeTop;
- private int stackDepth;
- private int lineNumber;
- private int doubleTableTop;
-
- private ObjToIntMap strings = new ObjToIntMap(20);
- private ObjToIntMap bigInts = new ObjToIntMap(20);
- private int localTop;
- private int[] labelTable;
- private int labelTableTop;
-
- // fixupTable[i] = (label_index << 32) | fixup_site
- private long[] fixupTable;
- private int fixupTableTop;
- private ObjArray literalIds = new ObjArray();
-
- private int exceptionTableTop;
-
- // ECF_ or Expression Context Flags constants: for now only TAIL
- private static final int ECF_TAIL = 1 << 0;
-
- public InterpreterData compile(
- CompilerEnvirons compilerEnv,
- ScriptNode tree,
- String encodedSource,
- boolean returnFunction) {
- this.compilerEnv = compilerEnv;
-
- if (Token.printTrees) {
- System.out.println("before transform:");
- System.out.println(tree.toStringTree(tree));
- }
-
- new NodeTransformer().transform(tree, compilerEnv);
-
- if (Token.printTrees) {
- System.out.println("after transform:");
- System.out.println(tree.toStringTree(tree));
- }
-
- if (returnFunction) {
- scriptOrFn = tree.getFunctionNode(0);
- } else {
- scriptOrFn = tree;
- }
-
- itsData =
- new InterpreterData(
- compilerEnv.getLanguageVersion(),
- scriptOrFn.getSourceName(),
- encodedSource,
- scriptOrFn.isInStrictMode());
- itsData.topLevel = true;
-
- if (returnFunction) {
- generateFunctionICode();
- } else {
- generateICodeFromTree(scriptOrFn);
- }
- return itsData;
- }
-
- private void generateFunctionICode() {
- itsInFunctionFlag = true;
-
- FunctionNode theFunction = (FunctionNode) scriptOrFn;
-
- itsData.itsFunctionType = theFunction.getFunctionType();
- itsData.itsNeedsActivation = theFunction.requiresActivation();
- if (theFunction.getFunctionName() != null) {
- itsData.itsName = theFunction.getName();
- }
- if (theFunction.isGenerator()) {
- addIcode(Icode_GENERATOR);
- addUint16(theFunction.getBaseLineno() & 0xFFFF);
- }
- if (theFunction.isInStrictMode()) {
- itsData.isStrict = true;
- }
- if (theFunction.isES6Generator()) {
- itsData.isES6Generator = true;
- }
-
- itsData.declaredAsVar = (theFunction.getParent() instanceof VariableInitializer);
-
- generateICodeFromTree(theFunction.getLastChild());
- }
-
- private void generateICodeFromTree(Node tree) {
- generateNestedFunctions();
-
- generateRegExpLiterals();
-
- generateTemplateLiterals();
-
- visitStatement(tree, 0);
- fixLabelGotos();
- // add RETURN_RESULT only to scripts as function always ends with RETURN
- if (itsData.itsFunctionType == 0) {
- addToken(Token.RETURN_RESULT);
- }
-
- if (itsData.itsICode.length != iCodeTop) {
- // Make itsData.itsICode length exactly iCodeTop to save memory
- // and catch bugs with jumps beyond icode as early as possible
- byte[] tmp = new byte[iCodeTop];
- System.arraycopy(itsData.itsICode, 0, tmp, 0, iCodeTop);
- itsData.itsICode = tmp;
- }
- if (strings.size() == 0) {
- itsData.itsStringTable = null;
- } else {
- itsData.itsStringTable = new String[strings.size()];
- ObjToIntMap.Iterator iter = strings.newIterator();
- for (iter.start(); !iter.done(); iter.next()) {
- String str = (String) iter.getKey();
- int index = iter.getValue();
- if (itsData.itsStringTable[index] != null) Kit.codeBug();
- itsData.itsStringTable[index] = str;
- }
- }
- if (doubleTableTop == 0) {
- itsData.itsDoubleTable = null;
- } else if (itsData.itsDoubleTable.length != doubleTableTop) {
- double[] tmp = new double[doubleTableTop];
- System.arraycopy(itsData.itsDoubleTable, 0, tmp, 0, doubleTableTop);
- itsData.itsDoubleTable = tmp;
- }
- if (bigInts.size() == 0) {
- itsData.itsBigIntTable = null;
- } else {
- itsData.itsBigIntTable = new BigInteger[bigInts.size()];
- ObjToIntMap.Iterator iter = bigInts.newIterator();
- for (iter.start(); !iter.done(); iter.next()) {
- BigInteger bigInt = (BigInteger) iter.getKey();
- int index = iter.getValue();
- if (itsData.itsBigIntTable[index] != null) Kit.codeBug();
- itsData.itsBigIntTable[index] = bigInt;
- }
- }
- if (exceptionTableTop != 0 && itsData.itsExceptionTable.length != exceptionTableTop) {
- int[] tmp = new int[exceptionTableTop];
- System.arraycopy(itsData.itsExceptionTable, 0, tmp, 0, exceptionTableTop);
- itsData.itsExceptionTable = tmp;
- }
-
- itsData.itsMaxVars = scriptOrFn.getParamAndVarCount();
- // itsMaxFrameArray: interpret method needs this amount for its
- // stack and sDbl arrays
- itsData.itsMaxFrameArray = itsData.itsMaxVars + itsData.itsMaxLocals + itsData.itsMaxStack;
-
- itsData.argNames = scriptOrFn.getParamAndVarNames();
- itsData.argIsConst = scriptOrFn.getParamAndVarConst();
- itsData.argCount = scriptOrFn.getParamCount();
-
- itsData.encodedSourceStart = scriptOrFn.getEncodedSourceStart();
- itsData.encodedSourceEnd = scriptOrFn.getEncodedSourceEnd();
-
- if (literalIds.size() != 0) {
- itsData.literalIds = literalIds.toArray();
- }
-
- if (Token.printICode) Interpreter.dumpICode(itsData);
- }
-
- private void generateNestedFunctions() {
- int functionCount = scriptOrFn.getFunctionCount();
- if (functionCount == 0) return;
-
- InterpreterData[] array = new InterpreterData[functionCount];
- for (int i = 0; i != functionCount; i++) {
- FunctionNode fn = scriptOrFn.getFunctionNode(i);
- CodeGenerator gen = new CodeGenerator();
- gen.compilerEnv = compilerEnv;
- gen.scriptOrFn = fn;
- gen.itsData = new InterpreterData(itsData);
- gen.generateFunctionICode();
- array[i] = gen.itsData;
-
- final AstNode fnParent = fn.getParent();
- if (!(fnParent instanceof AstRoot
- || fnParent instanceof Scope
- || fnParent instanceof Block)) {
- gen.itsData.declaredAsFunctionExpression = true;
- }
- }
- itsData.itsNestedFunctions = array;
- }
-
- private void generateRegExpLiterals() {
- int N = scriptOrFn.getRegexpCount();
- if (N == 0) return;
-
- Context cx = Context.getContext();
- RegExpProxy rep = ScriptRuntime.checkRegExpProxy(cx);
- Object[] array = new Object[N];
- for (int i = 0; i != N; i++) {
- String string = scriptOrFn.getRegexpString(i);
- String flags = scriptOrFn.getRegexpFlags(i);
- array[i] = rep.compileRegExp(cx, string, flags);
- }
- itsData.itsRegExpLiterals = array;
- }
-
- private void generateTemplateLiterals() {
- int N = scriptOrFn.getTemplateLiteralCount();
- if (N == 0) return;
-
- Object[] array = new Object[N];
- for (int i = 0; i != N; i++) {
- List strings = scriptOrFn.getTemplateLiteralStrings(i);
- int j = 0;
- String[] values = new String[strings.size() * 2];
- for (TemplateCharacters s : strings) {
- values[j++] = s.getValue();
- values[j++] = s.getRawValue();
- }
- array[i] = values;
- }
- itsData.itsTemplateLiterals = array;
- }
-
- private void updateLineNumber(Node node) {
- int lineno = node.getLineno();
- if (lineno != lineNumber && lineno >= 0) {
- if (itsData.firstLinePC < 0) {
- itsData.firstLinePC = lineno;
- }
- lineNumber = lineno;
- addIcode(Icode_LINE);
- addUint16(lineno & 0xFFFF);
- }
- }
-
- private static RuntimeException badTree(Node node) {
- throw new RuntimeException(node.toString());
- }
-
- private void visitStatement(Node node, int initialStackDepth) {
- int type = node.getType();
- Node child = node.getFirstChild();
- switch (type) {
- case Token.FUNCTION:
- {
- int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
- int fnType = scriptOrFn.getFunctionNode(fnIndex).getFunctionType();
- // Only function expressions or function expression
- // statements need closure code creating new function
- // object on stack as function statements are initialized
- // at script/function start.
- // In addition, function expressions can not be present here
- // at statement level, they must only be present as expressions.
- if (fnType == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) {
- addIndexOp(Icode_CLOSURE_STMT, fnIndex);
- } else {
- if (fnType != FunctionNode.FUNCTION_STATEMENT) {
- throw Kit.codeBug();
- }
- }
- // For function statements or function expression statements
- // in scripts, we need to ensure that the result of the script
- // is the function if it is the last statement in the script.
- // For example, eval("function () {}") should return a
- // function, not undefined.
- if (!itsInFunctionFlag) {
- addIndexOp(Icode_CLOSURE_EXPR, fnIndex);
- stackChange(1);
- addIcode(Icode_POP_RESULT);
- stackChange(-1);
- }
- }
- break;
-
- case Token.LABEL:
- case Token.LOOP:
- case Token.BLOCK:
- case Token.EMPTY:
- case Token.WITH:
- updateLineNumber(node);
- // fall through
- case Token.SCRIPT:
- while (child != null) {
- visitStatement(child, initialStackDepth);
- child = child.getNext();
- }
- break;
-
- case Token.ENTERWITH:
- visitExpression(child, 0);
- addToken(Token.ENTERWITH);
- stackChange(-1);
- break;
-
- case Token.LEAVEWITH:
- addToken(Token.LEAVEWITH);
- break;
-
- case Token.LOCAL_BLOCK:
- {
- int local = allocLocal();
- node.putIntProp(Node.LOCAL_PROP, local);
- updateLineNumber(node);
- while (child != null) {
- visitStatement(child, initialStackDepth);
- child = child.getNext();
- }
- addIndexOp(Icode_LOCAL_CLEAR, local);
- releaseLocal(local);
- }
- break;
-
- case Token.DEBUGGER:
- addIcode(Icode_DEBUGGER);
- break;
-
- case Token.SWITCH:
- updateLineNumber(node);
- // See comments in IRFactory.createSwitch() for description
- // of SWITCH node
- {
- visitExpression(child, 0);
- for (Jump caseNode = (Jump) child.getNext();
- caseNode != null;
- caseNode = (Jump) caseNode.getNext()) {
- if (caseNode.getType() != Token.CASE) throw badTree(caseNode);
- Node test = caseNode.getFirstChild();
- addIcode(Icode_DUP);
- stackChange(1);
- visitExpression(test, 0);
- addToken(Token.SHEQ);
- stackChange(-1);
- // If true, Icode_IFEQ_POP will jump and remove case
- // value from stack
- addGoto(caseNode.target, Icode_IFEQ_POP);
- stackChange(-1);
- }
- addIcode(Icode_POP);
- stackChange(-1);
- }
- break;
-
- case Token.TARGET:
- markTargetLabel(node);
- break;
-
- case Token.IFEQ:
- case Token.IFNE:
- {
- Node target = ((Jump) node).target;
- visitExpression(child, 0);
- addGoto(target, type);
- stackChange(-1);
- }
- break;
-
- case Token.GOTO:
- {
- Node target = ((Jump) node).target;
- addGoto(target, type);
- }
- break;
-
- case Token.JSR:
- {
- Node target = ((Jump) node).target;
- addGoto(target, Icode_GOSUB);
- }
- break;
-
- case Token.FINALLY:
- {
- // Account for incomming GOTOSUB address
- stackChange(1);
- int finallyRegister = getLocalBlockRef(node);
- addIndexOp(Icode_STARTSUB, finallyRegister);
- stackChange(-1);
- while (child != null) {
- visitStatement(child, initialStackDepth);
- child = child.getNext();
- }
- addIndexOp(Icode_RETSUB, finallyRegister);
- }
- break;
-
- case Token.EXPR_VOID:
- case Token.EXPR_RESULT:
- updateLineNumber(node);
- visitExpression(child, 0);
- addIcode((type == Token.EXPR_VOID) ? Icode_POP : Icode_POP_RESULT);
- stackChange(-1);
- break;
-
- case Token.TRY:
- {
- Jump tryNode = (Jump) node;
- int exceptionObjectLocal = getLocalBlockRef(tryNode);
- int scopeLocal = allocLocal();
-
- addIndexOp(Icode_SCOPE_SAVE, scopeLocal);
-
- int tryStart = iCodeTop;
- boolean savedFlag = itsInTryFlag;
- itsInTryFlag = true;
- while (child != null) {
- visitStatement(child, initialStackDepth);
- child = child.getNext();
- }
- itsInTryFlag = savedFlag;
-
- Node catchTarget = tryNode.target;
- if (catchTarget != null) {
- int catchStartPC = labelTable[getTargetLabel(catchTarget)];
- addExceptionHandler(
- tryStart,
- catchStartPC,
- catchStartPC,
- false,
- exceptionObjectLocal,
- scopeLocal);
- }
- Node finallyTarget = tryNode.getFinally();
- if (finallyTarget != null) {
- int finallyStartPC = labelTable[getTargetLabel(finallyTarget)];
- addExceptionHandler(
- tryStart,
- finallyStartPC,
- finallyStartPC,
- true,
- exceptionObjectLocal,
- scopeLocal);
- }
-
- addIndexOp(Icode_LOCAL_CLEAR, scopeLocal);
- releaseLocal(scopeLocal);
- }
- break;
-
- case Token.CATCH_SCOPE:
- {
- int localIndex = getLocalBlockRef(node);
- int scopeIndex = node.getExistingIntProp(Node.CATCH_SCOPE_PROP);
- String name = child.getString();
- child = child.getNext();
- visitExpression(child, 0); // load expression object
- addStringPrefix(name);
- addIndexPrefix(localIndex);
- addToken(Token.CATCH_SCOPE);
- addUint8(scopeIndex != 0 ? 1 : 0);
- stackChange(-1);
- }
- break;
-
- case Token.THROW:
- updateLineNumber(node);
- visitExpression(child, 0);
- addToken(Token.THROW);
- addUint16(lineNumber & 0xFFFF);
- stackChange(-1);
- break;
-
- case Token.RETHROW:
- updateLineNumber(node);
- addIndexOp(Token.RETHROW, getLocalBlockRef(node));
- break;
-
- case Token.RETURN:
- updateLineNumber(node);
- if (node.getIntProp(Node.GENERATOR_END_PROP, 0) != 0) {
- if ((child == null)
- || (compilerEnv.getLanguageVersion() < Context.VERSION_ES6)) {
- // End generator function with no result, or old language version
- // in which generators never return a result.
- addIcode(Icode_GENERATOR_END);
- addUint16(lineNumber & 0xFFFF);
- } else {
- visitExpression(child, ECF_TAIL);
- addIcode(Icode_GENERATOR_RETURN);
- addUint16(lineNumber & 0xFFFF);
- stackChange(-1);
- }
-
- } else {
- if (child == null) {
- addIcode(Icode_RETUNDEF);
- } else {
- visitExpression(child, ECF_TAIL);
- addToken(Token.RETURN);
- stackChange(-1);
- }
- }
- break;
-
- case Token.RETURN_RESULT:
- updateLineNumber(node);
- addToken(Token.RETURN_RESULT);
- break;
-
- case Token.ENUM_INIT_KEYS:
- case Token.ENUM_INIT_VALUES:
- case Token.ENUM_INIT_ARRAY:
- case Token.ENUM_INIT_VALUES_IN_ORDER:
- visitExpression(child, 0);
- addIndexOp(type, getLocalBlockRef(node));
- stackChange(-1);
- break;
-
- case Icode_GENERATOR:
- break;
-
- default:
- throw badTree(node);
- }
-
- if (stackDepth != initialStackDepth) {
- throw Kit.codeBug();
- }
- }
-
- private void visitExpression(Node node, int contextFlags) {
- int type = node.getType();
- Node child = node.getFirstChild();
- int savedStackDepth = stackDepth;
- switch (type) {
- case Token.FUNCTION:
- {
- int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
- FunctionNode fn = scriptOrFn.getFunctionNode(fnIndex);
- // See comments in visitStatement for Token.FUNCTION case
- if (fn.getFunctionType() != FunctionNode.FUNCTION_EXPRESSION
- && fn.getFunctionType() != FunctionNode.ARROW_FUNCTION) {
- throw Kit.codeBug();
- }
- addIndexOp(Icode_CLOSURE_EXPR, fnIndex);
- stackChange(1);
- }
- break;
-
- case Token.LOCAL_LOAD:
- {
- int localIndex = getLocalBlockRef(node);
- addIndexOp(Token.LOCAL_LOAD, localIndex);
- stackChange(1);
- }
- break;
-
- case Token.COMMA:
- {
- Node lastChild = node.getLastChild();
- while (child != lastChild) {
- visitExpression(child, 0);
- addIcode(Icode_POP);
- stackChange(-1);
- child = child.getNext();
- }
- // Preserve tail context flag if any
- visitExpression(child, contextFlags & ECF_TAIL);
- }
- break;
-
- case Token.USE_STACK:
- // Indicates that stack was modified externally,
- // like placed catch object
- stackChange(1);
- break;
-
- case Token.REF_CALL:
- case Token.CALL:
- case Token.NEW:
- {
- if (type == Token.NEW) {
- visitExpression(child, 0);
- } else {
- generateCallFunAndThis(child);
- }
- int argCount = 0;
- while ((child = child.getNext()) != null) {
- visitExpression(child, 0);
- ++argCount;
- }
- int callType = node.getIntProp(Node.SPECIALCALL_PROP, Node.NON_SPECIALCALL);
- if (type != Token.REF_CALL && callType != Node.NON_SPECIALCALL) {
- // embed line number and source filename
- addIndexOp(Icode_CALLSPECIAL, argCount);
- addUint8(callType);
- addUint8(type == Token.NEW ? 1 : 0);
- addUint16(lineNumber & 0xFFFF);
- } else {
- // Only use the tail call optimization if we're not in a try
- // or we're not generating debug info (since the
- // optimization will confuse the debugger)
- if (type == Token.CALL
- && (contextFlags & ECF_TAIL) != 0
- && !compilerEnv.isGenerateDebugInfo()
- && !itsInTryFlag) {
- type = Icode_TAIL_CALL;
- }
- addIndexOp(type, argCount);
- }
- // adjust stack
- if (type == Token.NEW) {
- // new: f, args -> result
- stackChange(-argCount);
- } else {
- // call: f, thisObj, args -> result
- // ref_call: f, thisObj, args -> ref
- stackChange(-1 - argCount);
- }
- if (argCount > itsData.itsMaxCalleeArgs) {
- itsData.itsMaxCalleeArgs = argCount;
- }
- }
- break;
-
- case Token.AND:
- case Token.OR:
- {
- visitExpression(child, 0);
- addIcode(Icode_DUP);
- stackChange(1);
- int afterSecondJumpStart = iCodeTop;
- int jump = (type == Token.AND) ? Token.IFNE : Token.IFEQ;
- addGotoOp(jump);
- stackChange(-1);
- addIcode(Icode_POP);
- stackChange(-1);
- child = child.getNext();
- // Preserve tail context flag if any
- visitExpression(child, contextFlags & ECF_TAIL);
- resolveForwardGoto(afterSecondJumpStart);
- }
- break;
-
- case Token.HOOK:
- {
- Node ifThen = child.getNext();
- Node ifElse = ifThen.getNext();
- visitExpression(child, 0);
- int elseJumpStart = iCodeTop;
- addGotoOp(Token.IFNE);
- stackChange(-1);
- // Preserve tail context flag if any
- visitExpression(ifThen, contextFlags & ECF_TAIL);
- int afterElseJumpStart = iCodeTop;
- addGotoOp(Token.GOTO);
- resolveForwardGoto(elseJumpStart);
- stackDepth = savedStackDepth;
- // Preserve tail context flag if any
- visitExpression(ifElse, contextFlags & ECF_TAIL);
- resolveForwardGoto(afterElseJumpStart);
- }
- break;
-
- case Token.GETPROP:
- case Token.GETPROPNOWARN:
- visitExpression(child, 0);
- child = child.getNext();
- addStringOp(type, child.getString());
- break;
-
- case Token.DELPROP:
- boolean isName = child.getType() == Token.BINDNAME;
- visitExpression(child, 0);
- child = child.getNext();
- visitExpression(child, 0);
- if (isName) {
- // special handling for delete name
- addIcode(Icode_DELNAME);
- } else {
- addToken(Token.DELPROP);
- }
- stackChange(-1);
- break;
-
- case Token.GETELEM:
- case Token.BITAND:
- case Token.BITOR:
- case Token.BITXOR:
- case Token.LSH:
- case Token.RSH:
- case Token.URSH:
- case Token.ADD:
- case Token.SUB:
- case Token.MOD:
- case Token.DIV:
- case Token.MUL:
- case Token.EXP:
- case Token.EQ:
- case Token.NE:
- case Token.SHEQ:
- case Token.SHNE:
- case Token.IN:
- case Token.INSTANCEOF:
- case Token.LE:
- case Token.LT:
- case Token.GE:
- case Token.GT:
- visitExpression(child, 0);
- child = child.getNext();
- visitExpression(child, 0);
- addToken(type);
- stackChange(-1);
- break;
-
- case Token.POS:
- case Token.NEG:
- case Token.NOT:
- case Token.BITNOT:
- case Token.TYPEOF:
- case Token.VOID:
- visitExpression(child, 0);
- if (type == Token.VOID) {
- addIcode(Icode_POP);
- addIcode(Icode_UNDEF);
- } else {
- addToken(type);
- }
- break;
-
- case Token.GET_REF:
- case Token.DEL_REF:
- visitExpression(child, 0);
- addToken(type);
- break;
-
- case Token.SETPROP:
- case Token.SETPROP_OP:
- {
- visitExpression(child, 0);
- child = child.getNext();
- String property = child.getString();
- child = child.getNext();
- if (type == Token.SETPROP_OP) {
- addIcode(Icode_DUP);
- stackChange(1);
- addStringOp(Token.GETPROP, property);
- // Compensate for the following USE_STACK
- stackChange(-1);
- }
- visitExpression(child, 0);
- addStringOp(Token.SETPROP, property);
- stackChange(-1);
- }
- break;
-
- case Token.SETELEM:
- case Token.SETELEM_OP:
- visitExpression(child, 0);
- child = child.getNext();
- visitExpression(child, 0);
- child = child.getNext();
- if (type == Token.SETELEM_OP) {
- addIcode(Icode_DUP2);
- stackChange(2);
- addToken(Token.GETELEM);
- stackChange(-1);
- // Compensate for the following USE_STACK
- stackChange(-1);
- }
- visitExpression(child, 0);
- addToken(Token.SETELEM);
- stackChange(-2);
- break;
-
- case Token.SET_REF:
- case Token.SET_REF_OP:
- visitExpression(child, 0);
- child = child.getNext();
- if (type == Token.SET_REF_OP) {
- addIcode(Icode_DUP);
- stackChange(1);
- addToken(Token.GET_REF);
- // Compensate for the following USE_STACK
- stackChange(-1);
- }
- visitExpression(child, 0);
- addToken(Token.SET_REF);
- stackChange(-1);
- break;
-
- case Token.STRICT_SETNAME:
- case Token.SETNAME:
- {
- String name = child.getString();
- visitExpression(child, 0);
- child = child.getNext();
- visitExpression(child, 0);
- addStringOp(type, name);
- stackChange(-1);
- }
- break;
-
- case Token.SETCONST:
- {
- String name = child.getString();
- visitExpression(child, 0);
- child = child.getNext();
- visitExpression(child, 0);
- addStringOp(Icode_SETCONST, name);
- stackChange(-1);
- }
- break;
-
- case Token.TYPEOFNAME:
- {
- int index = -1;
- // use typeofname if an activation frame exists
- // since the vars all exist there instead of in jregs
- if (itsInFunctionFlag && !itsData.itsNeedsActivation)
- index = scriptOrFn.getIndexForNameNode(node);
- if (index == -1) {
- addStringOp(Icode_TYPEOFNAME, node.getString());
- stackChange(1);
- } else {
- addVarOp(Token.GETVAR, index);
- stackChange(1);
- addToken(Token.TYPEOF);
- }
- }
- break;
-
- case Token.BINDNAME:
- case Token.NAME:
- case Token.STRING:
- addStringOp(type, node.getString());
- stackChange(1);
- break;
-
- case Token.INC:
- case Token.DEC:
- visitIncDec(node, child);
- break;
-
- case Token.NUMBER:
- {
- double num = node.getDouble();
- int inum = (int) num;
- if (inum == num) {
- if (inum == 0) {
- addIcode(Icode_ZERO);
- // Check for negative zero
- if (1.0 / num < 0.0) {
- addToken(Token.NEG);
- }
- } else if (inum == 1) {
- addIcode(Icode_ONE);
- } else if ((short) inum == inum) {
- addIcode(Icode_SHORTNUMBER);
- // write short as uin16 bit pattern
- addUint16(inum & 0xFFFF);
- } else {
- addIcode(Icode_INTNUMBER);
- addInt(inum);
- }
- } else {
- int index = getDoubleIndex(num);
- addIndexOp(Token.NUMBER, index);
- }
- stackChange(1);
- }
- break;
-
- case Token.GETVAR:
- {
- if (itsData.itsNeedsActivation) Kit.codeBug();
- int index = scriptOrFn.getIndexForNameNode(node);
- addVarOp(Token.GETVAR, index);
- stackChange(1);
- }
- break;
-
- case Token.SETVAR:
- {
- if (itsData.itsNeedsActivation) Kit.codeBug();
- int index = scriptOrFn.getIndexForNameNode(child);
- child = child.getNext();
- visitExpression(child, 0);
- addVarOp(Token.SETVAR, index);
- }
- break;
-
- case Token.SETCONSTVAR:
- {
- if (itsData.itsNeedsActivation) Kit.codeBug();
- int index = scriptOrFn.getIndexForNameNode(child);
- child = child.getNext();
- visitExpression(child, 0);
- addVarOp(Token.SETCONSTVAR, index);
- }
- break;
-
- case Token.NULL:
- case Token.THIS:
- case Token.THISFN:
- case Token.FALSE:
- case Token.TRUE:
- addToken(type);
- stackChange(1);
- break;
-
- case Token.ENUM_NEXT:
- case Token.ENUM_ID:
- addIndexOp(type, getLocalBlockRef(node));
- stackChange(1);
- break;
-
- case Token.BIGINT:
- addBigInt(node.getBigInt());
- stackChange(1);
- break;
-
- case Token.REGEXP:
- {
- int index = node.getExistingIntProp(Node.REGEXP_PROP);
- addIndexOp(Token.REGEXP, index);
- stackChange(1);
- }
- break;
-
- case Token.ARRAYLIT:
- case Token.OBJECTLIT:
- visitLiteral(node, child);
- break;
-
- case Token.ARRAYCOMP:
- visitArrayComprehension(node, child, child.getNext());
- break;
-
- case Token.REF_SPECIAL:
- visitExpression(child, 0);
- addStringOp(type, (String) node.getProp(Node.NAME_PROP));
- break;
-
- case Token.REF_MEMBER:
- case Token.REF_NS_MEMBER:
- case Token.REF_NAME:
- case Token.REF_NS_NAME:
- {
- int memberTypeFlags = node.getIntProp(Node.MEMBER_TYPE_PROP, 0);
- // generate possible target, possible namespace and member
- int childCount = 0;
- do {
- visitExpression(child, 0);
- ++childCount;
- child = child.getNext();
- } while (child != null);
- addIndexOp(type, memberTypeFlags);
- stackChange(1 - childCount);
- }
- break;
-
- case Token.DOTQUERY:
- {
- int queryPC;
- updateLineNumber(node);
- visitExpression(child, 0);
- addIcode(Icode_ENTERDQ);
- stackChange(-1);
- queryPC = iCodeTop;
- visitExpression(child.getNext(), 0);
- addBackwardGoto(Icode_LEAVEDQ, queryPC);
- }
- break;
-
- case Token.DEFAULTNAMESPACE:
- case Token.ESCXMLATTR:
- case Token.ESCXMLTEXT:
- visitExpression(child, 0);
- addToken(type);
- break;
-
- case Token.YIELD:
- case Token.YIELD_STAR:
- if (child != null) {
- visitExpression(child, 0);
- } else {
- addIcode(Icode_UNDEF);
- stackChange(1);
- }
- if (type == Token.YIELD) {
- addToken(Token.YIELD);
- } else {
- addIcode(Icode_YIELD_STAR);
- }
- addUint16(node.getLineno() & 0xFFFF);
- break;
-
- case Token.WITHEXPR:
- {
- Node enterWith = node.getFirstChild();
- Node with = enterWith.getNext();
- visitExpression(enterWith.getFirstChild(), 0);
- addToken(Token.ENTERWITH);
- stackChange(-1);
- visitExpression(with.getFirstChild(), 0);
- addToken(Token.LEAVEWITH);
- break;
- }
-
- case Token.TEMPLATE_LITERAL:
- visitTemplateLiteral(node);
- break;
-
- default:
- throw badTree(node);
- }
- if (savedStackDepth + 1 != stackDepth) {
- Kit.codeBug();
- }
- }
-
- private void generateCallFunAndThis(Node left) {
- // Generate code to place on stack function and thisObj
- int type = left.getType();
- switch (type) {
- case Token.NAME:
- {
- String name = left.getString();
- // stack: ... -> ... function thisObj
- addStringOp(Icode_NAME_AND_THIS, name);
- stackChange(2);
- break;
- }
- case Token.GETPROP:
- case Token.GETELEM:
- {
- Node target = left.getFirstChild();
- visitExpression(target, 0);
- Node id = target.getNext();
- if (type == Token.GETPROP) {
- String property = id.getString();
- // stack: ... target -> ... function thisObj
- addStringOp(Icode_PROP_AND_THIS, property);
- stackChange(1);
- } else {
- visitExpression(id, 0);
- // stack: ... target id -> ... function thisObj
- addIcode(Icode_ELEM_AND_THIS);
- }
- break;
- }
- default:
- // Including Token.GETVAR
- visitExpression(left, 0);
- // stack: ... value -> ... function thisObj
- addIcode(Icode_VALUE_AND_THIS);
- stackChange(1);
- break;
- }
- }
-
- private void visitIncDec(Node node, Node child) {
- int incrDecrMask = node.getExistingIntProp(Node.INCRDECR_PROP);
- int childType = child.getType();
- switch (childType) {
- case Token.GETVAR:
- {
- if (itsData.itsNeedsActivation) Kit.codeBug();
- int i = scriptOrFn.getIndexForNameNode(child);
- addVarOp(Icode_VAR_INC_DEC, i);
- addUint8(incrDecrMask);
- stackChange(1);
- break;
- }
- case Token.NAME:
- {
- String name = child.getString();
- addStringOp(Icode_NAME_INC_DEC, name);
- addUint8(incrDecrMask);
- stackChange(1);
- break;
- }
- case Token.GETPROP:
- {
- Node object = child.getFirstChild();
- visitExpression(object, 0);
- String property = object.getNext().getString();
- addStringOp(Icode_PROP_INC_DEC, property);
- addUint8(incrDecrMask);
- break;
- }
- case Token.GETELEM:
- {
- Node object = child.getFirstChild();
- visitExpression(object, 0);
- Node index = object.getNext();
- visitExpression(index, 0);
- addIcode(Icode_ELEM_INC_DEC);
- addUint8(incrDecrMask);
- stackChange(-1);
- break;
- }
- case Token.GET_REF:
- {
- Node ref = child.getFirstChild();
- visitExpression(ref, 0);
- addIcode(Icode_REF_INC_DEC);
- addUint8(incrDecrMask);
- break;
- }
- default:
- {
- throw badTree(node);
- }
- }
- }
-
- private void visitLiteral(Node node, Node child) {
- int type = node.getType();
- int count;
- Object[] propertyIds = null;
- if (type == Token.ARRAYLIT) {
- count = 0;
- for (Node n = child; n != null; n = n.getNext()) {
- ++count;
- }
- } else if (type == Token.OBJECTLIT) {
- propertyIds = (Object[]) node.getProp(Node.OBJECT_IDS_PROP);
- count = propertyIds == null ? 0 : propertyIds.length;
- } else {
- throw badTree(node);
- }
- addIndexOp(Icode_LITERAL_NEW, count);
- stackChange(2);
- while (child != null) {
- int childType = child.getType();
- if (childType == Token.GET) {
- visitExpression(child.getFirstChild(), 0);
- addIcode(Icode_LITERAL_GETTER);
- } else if (childType == Token.SET) {
- visitExpression(child.getFirstChild(), 0);
- addIcode(Icode_LITERAL_SETTER);
- } else if (childType == Token.METHOD) {
- visitExpression(child.getFirstChild(), 0);
- addIcode(Icode_LITERAL_SET);
- } else {
- visitExpression(child, 0);
- addIcode(Icode_LITERAL_SET);
- }
- stackChange(-1);
- child = child.getNext();
- }
- if (type == Token.ARRAYLIT) {
- int[] skipIndexes = (int[]) node.getProp(Node.SKIP_INDEXES_PROP);
- if (skipIndexes == null) {
- addToken(Token.ARRAYLIT);
- } else {
- int index = literalIds.size();
- literalIds.add(skipIndexes);
- addIndexOp(Icode_SPARE_ARRAYLIT, index);
- }
- } else {
- int index = literalIds.size();
- literalIds.add(propertyIds);
- addIndexOp(Token.OBJECTLIT, index);
- }
- stackChange(-1);
- }
-
- private void visitTemplateLiteral(Node node) {
- int index = node.getExistingIntProp(Node.TEMPLATE_LITERAL_PROP);
- addIndexOp(Icode_TEMPLATE_LITERAL_CALLSITE, index);
- stackChange(1);
- }
-
- private void visitArrayComprehension(Node node, Node initStmt, Node expr) {
- // A bit of a hack: array comprehensions are implemented using
- // statement nodes for the iteration, yet they appear in an
- // expression context. So we pass the current stack depth to
- // visitStatement so it can check that the depth is not altered
- // by statements.
- visitStatement(initStmt, stackDepth);
- visitExpression(expr, 0);
- }
-
- private static int getLocalBlockRef(Node node) {
- Node localBlock = (Node) node.getProp(Node.LOCAL_BLOCK_PROP);
- return localBlock.getExistingIntProp(Node.LOCAL_PROP);
- }
-
- private int getTargetLabel(Node target) {
- int label = target.labelId();
- if (label != -1) {
- return label;
- }
- label = labelTableTop;
- if (labelTable == null || label == labelTable.length) {
- if (labelTable == null) {
- labelTable = new int[MIN_LABEL_TABLE_SIZE];
- } else {
- int[] tmp = new int[labelTable.length * 2];
- System.arraycopy(labelTable, 0, tmp, 0, label);
- labelTable = tmp;
- }
- }
- labelTableTop = label + 1;
- labelTable[label] = -1;
-
- target.labelId(label);
- return label;
- }
-
- private void markTargetLabel(Node target) {
- int label = getTargetLabel(target);
- if (labelTable[label] != -1) {
- // Can mark label only once
- Kit.codeBug();
- }
- labelTable[label] = iCodeTop;
- }
-
- private void addGoto(Node target, int gotoOp) {
- int label = getTargetLabel(target);
- if (!(label < labelTableTop)) Kit.codeBug();
- int targetPC = labelTable[label];
-
- if (targetPC != -1) {
- addBackwardGoto(gotoOp, targetPC);
- } else {
- int gotoPC = iCodeTop;
- addGotoOp(gotoOp);
- int top = fixupTableTop;
- if (fixupTable == null || top == fixupTable.length) {
- if (fixupTable == null) {
- fixupTable = new long[MIN_FIXUP_TABLE_SIZE];
- } else {
- long[] tmp = new long[fixupTable.length * 2];
- System.arraycopy(fixupTable, 0, tmp, 0, top);
- fixupTable = tmp;
- }
- }
- fixupTableTop = top + 1;
- fixupTable[top] = ((long) label << 32) | gotoPC;
- }
- }
-
- private void fixLabelGotos() {
- for (int i = 0; i < fixupTableTop; i++) {
- long fixup = fixupTable[i];
- int label = (int) (fixup >> 32);
- int jumpSource = (int) fixup;
- int pc = labelTable[label];
- if (pc == -1) {
- // Unlocated label
- throw Kit.codeBug();
- }
- resolveGoto(jumpSource, pc);
- }
- fixupTableTop = 0;
- }
-
- private void addBackwardGoto(int gotoOp, int jumpPC) {
- int fromPC = iCodeTop;
- // Ensure that this is a jump backward
- if (fromPC <= jumpPC) throw Kit.codeBug();
- addGotoOp(gotoOp);
- resolveGoto(fromPC, jumpPC);
- }
-
- private void resolveForwardGoto(int fromPC) {
- // Ensure that forward jump skips at least self bytecode
- if (iCodeTop < fromPC + 3) throw Kit.codeBug();
- resolveGoto(fromPC, iCodeTop);
- }
-
- private void resolveGoto(int fromPC, int jumpPC) {
- int offset = jumpPC - fromPC;
- // Ensure that jumps do not overlap
- if (0 <= offset && offset <= 2) throw Kit.codeBug();
- int offsetSite = fromPC + 1;
- if (offset != (short) offset) {
- if (itsData.longJumps == null) {
- itsData.longJumps = new UintMap();
- }
- itsData.longJumps.put(offsetSite, jumpPC);
- offset = 0;
- }
- byte[] array = itsData.itsICode;
- array[offsetSite] = (byte) (offset >> 8);
- array[offsetSite + 1] = (byte) offset;
- }
-
- private void addToken(int token) {
- if (!Icode.validTokenCode(token)) throw Kit.codeBug();
- addUint8(token);
- }
-
- private void addIcode(int icode) {
- if (!Icode.validIcode(icode)) throw Kit.codeBug();
- // Write negative icode as uint8 bits
- addUint8(icode & 0xFF);
- }
-
- private void addUint8(int value) {
- if ((value & ~0xFF) != 0) throw Kit.codeBug();
- byte[] array = itsData.itsICode;
- int top = iCodeTop;
- if (top == array.length) {
- array = increaseICodeCapacity(1);
- }
- array[top] = (byte) value;
- iCodeTop = top + 1;
- }
-
- private void addUint16(int value) {
- if ((value & ~0xFFFF) != 0) throw Kit.codeBug();
- byte[] array = itsData.itsICode;
- int top = iCodeTop;
- if (top + 2 > array.length) {
- array = increaseICodeCapacity(2);
- }
- array[top] = (byte) (value >>> 8);
- array[top + 1] = (byte) value;
- iCodeTop = top + 2;
- }
-
- private void addInt(int i) {
- byte[] array = itsData.itsICode;
- int top = iCodeTop;
- if (top + 4 > array.length) {
- array = increaseICodeCapacity(4);
- }
- array[top] = (byte) (i >>> 24);
- array[top + 1] = (byte) (i >>> 16);
- array[top + 2] = (byte) (i >>> 8);
- array[top + 3] = (byte) i;
- iCodeTop = top + 4;
- }
-
- private int getDoubleIndex(double num) {
- int index = doubleTableTop;
- if (index == 0) {
- itsData.itsDoubleTable = new double[64];
- } else if (itsData.itsDoubleTable.length == index) {
- double[] na = new double[index * 2];
- System.arraycopy(itsData.itsDoubleTable, 0, na, 0, index);
- itsData.itsDoubleTable = na;
- }
- itsData.itsDoubleTable[index] = num;
- doubleTableTop = index + 1;
- return index;
- }
-
- private void addGotoOp(int gotoOp) {
- byte[] array = itsData.itsICode;
- int top = iCodeTop;
- if (top + 3 > array.length) {
- array = increaseICodeCapacity(3);
- }
- array[top] = (byte) gotoOp;
- // Offset would written later
- iCodeTop = top + 1 + 2;
- }
-
- private void addVarOp(int op, int varIndex) {
- switch (op) {
- case Token.SETCONSTVAR:
- if (varIndex < 128) {
- addIcode(Icode_SETCONSTVAR1);
- addUint8(varIndex);
- return;
- }
- addIndexOp(Icode_SETCONSTVAR, varIndex);
- return;
- case Token.GETVAR:
- case Token.SETVAR:
- if (varIndex < 128) {
- addIcode(op == Token.GETVAR ? Icode_GETVAR1 : Icode_SETVAR1);
- addUint8(varIndex);
- return;
- }
- // fallthrough
- case Icode_VAR_INC_DEC:
- addIndexOp(op, varIndex);
- return;
- }
- throw Kit.codeBug();
- }
-
- private void addStringOp(int op, String str) {
- addStringPrefix(str);
- if (Icode.validIcode(op)) {
- addIcode(op);
- } else {
- addToken(op);
- }
- }
-
- private void addIndexOp(int op, int index) {
- addIndexPrefix(index);
- if (Icode.validIcode(op)) {
- addIcode(op);
- } else {
- addToken(op);
- }
- }
-
- private void addStringPrefix(String str) {
- int index = strings.get(str, -1);
- if (index == -1) {
- index = strings.size();
- strings.put(str, index);
- }
- if (index < 4) {
- addIcode(Icode_REG_STR_C0 - index);
- } else if (index <= 0xFF) {
- addIcode(Icode_REG_STR1);
- addUint8(index);
- } else if (index <= 0xFFFF) {
- addIcode(Icode_REG_STR2);
- addUint16(index);
- } else {
- addIcode(Icode_REG_STR4);
- addInt(index);
- }
- }
-
- private void addBigInt(BigInteger n) {
- int index = bigInts.get(n, -1);
- if (index == -1) {
- index = bigInts.size();
- bigInts.put(n, index);
- }
- if (index < 4) {
- addIcode(Icode_REG_BIGINT_C0 - index);
- } else if (index <= 0xFF) {
- addIcode(Icode_REG_BIGINT1);
- addUint8(index);
- } else if (index <= 0xFFFF) {
- addIcode(Icode_REG_BIGINT2);
- addUint16(index);
- } else {
- addIcode(Icode_REG_BIGINT4);
- addInt(index);
- }
- addToken(Token.BIGINT);
- }
-
- private void addIndexPrefix(int index) {
- if (index < 0) Kit.codeBug();
- if (index < 6) {
- addIcode(Icode_REG_IND_C0 - index);
- } else if (index <= 0xFF) {
- addIcode(Icode_REG_IND1);
- addUint8(index);
- } else if (index <= 0xFFFF) {
- addIcode(Icode_REG_IND2);
- addUint16(index);
- } else {
- addIcode(Icode_REG_IND4);
- addInt(index);
- }
- }
-
- private void addExceptionHandler(
- int icodeStart,
- int icodeEnd,
- int handlerStart,
- boolean isFinally,
- int exceptionObjectLocal,
- int scopeLocal) {
- int top = exceptionTableTop;
- int[] table = itsData.itsExceptionTable;
- if (table == null) {
- if (top != 0) Kit.codeBug();
- table = new int[Interpreter.EXCEPTION_SLOT_SIZE * 2];
- itsData.itsExceptionTable = table;
- } else if (table.length == top) {
- table = new int[table.length * 2];
- System.arraycopy(itsData.itsExceptionTable, 0, table, 0, top);
- itsData.itsExceptionTable = table;
- }
- table[top + Interpreter.EXCEPTION_TRY_START_SLOT] = icodeStart;
- table[top + Interpreter.EXCEPTION_TRY_END_SLOT] = icodeEnd;
- table[top + Interpreter.EXCEPTION_HANDLER_SLOT] = handlerStart;
- table[top + Interpreter.EXCEPTION_TYPE_SLOT] = isFinally ? 1 : 0;
- table[top + Interpreter.EXCEPTION_LOCAL_SLOT] = exceptionObjectLocal;
- table[top + Interpreter.EXCEPTION_SCOPE_SLOT] = scopeLocal;
-
- exceptionTableTop = top + Interpreter.EXCEPTION_SLOT_SIZE;
- }
-
- private byte[] increaseICodeCapacity(int extraSize) {
- int capacity = itsData.itsICode.length;
- int top = iCodeTop;
- if (top + extraSize <= capacity) throw Kit.codeBug();
- capacity *= 2;
- if (top + extraSize > capacity) {
- capacity = top + extraSize;
- }
- byte[] array = new byte[capacity];
- System.arraycopy(itsData.itsICode, 0, array, 0, top);
- itsData.itsICode = array;
- return array;
- }
-
- private void stackChange(int change) {
- if (change <= 0) {
- stackDepth += change;
- } else {
- int newDepth = stackDepth + change;
- if (newDepth > itsData.itsMaxStack) {
- itsData.itsMaxStack = newDepth;
- }
- stackDepth = newDepth;
- }
- }
-
- private int allocLocal() {
- int localSlot = localTop;
- ++localTop;
- if (localTop > itsData.itsMaxLocals) {
- itsData.itsMaxLocals = localTop;
- }
- return localSlot;
- }
-
- private void releaseLocal(int localSlot) {
- --localTop;
- if (localSlot != localTop) Kit.codeBug();
- }
-}
diff --git a/src/main/java/org/mozilla/javascript/CompilerEnvirons.java b/src/main/java/org/mozilla/javascript/CompilerEnvirons.java
deleted file mode 100644
index 24c50edf..00000000
--- a/src/main/java/org/mozilla/javascript/CompilerEnvirons.java
+++ /dev/null
@@ -1,295 +0,0 @@
-/* -*- 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.javascript;
-
-import java.util.Set;
-
-import org.mozilla.javascript.ast.ErrorCollector;
-
-public class CompilerEnvirons
-{
- public CompilerEnvirons()
- {
- errorReporter = DefaultErrorReporter.instance;
- languageVersion = Context.VERSION_DEFAULT;
- generateDebugInfo = true;
- reservedKeywordAsIdentifier = true;
- allowMemberExprAsFunctionName = false;
- xmlAvailable = true;
- optimizationLevel = 0;
- generatingSource = true;
- strictMode = false;
- warningAsError = false;
- generateObserverCount = false;
- allowSharpComments = false;
- }
-
- public void initFromContext(Context cx)
- {
- setErrorReporter(cx.getErrorReporter());
- languageVersion = cx.getLanguageVersion();
- generateDebugInfo = (!cx.isGeneratingDebugChanged()
- || cx.isGeneratingDebug());
- reservedKeywordAsIdentifier
- = cx.hasFeature(Context.FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER);
- allowMemberExprAsFunctionName
- = cx.hasFeature(Context.FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME);
- strictMode
- = cx.hasFeature(Context.FEATURE_STRICT_MODE);
- warningAsError = cx.hasFeature(Context.FEATURE_WARNING_AS_ERROR);
- xmlAvailable
- = cx.hasFeature(Context.FEATURE_E4X);
-
- optimizationLevel = cx.getOptimizationLevel();
-
- generatingSource = cx.isGeneratingSource();
- activationNames = cx.activationNames;
-
- // Observer code generation in compiled code :
- generateObserverCount = cx.generateObserverCount;
- }
-
- public final ErrorReporter getErrorReporter()
- {
- return errorReporter;
- }
-
- public void setErrorReporter(ErrorReporter errorReporter)
- {
- if (errorReporter == null) throw new IllegalArgumentException();
- this.errorReporter = errorReporter;
- }
-
- public final int getLanguageVersion()
- {
- return languageVersion;
- }
-
- public void setLanguageVersion(int languageVersion)
- {
- Context.checkLanguageVersion(languageVersion);
- this.languageVersion = languageVersion;
- }
-
- public final boolean isGenerateDebugInfo()
- {
- return generateDebugInfo;
- }
-
- public void setGenerateDebugInfo(boolean flag)
- {
- this.generateDebugInfo = flag;
- }
-
- public final boolean isReservedKeywordAsIdentifier()
- {
- return reservedKeywordAsIdentifier;
- }
-
- public void setReservedKeywordAsIdentifier(boolean flag)
- {
- reservedKeywordAsIdentifier = flag;
- }
-
- /**
- * Extension to ECMA: if 'function <name>' is not followed
- * by '(', assume <name> starts a {@code memberExpr}
- */
- public final boolean isAllowMemberExprAsFunctionName()
- {
- return allowMemberExprAsFunctionName;
- }
-
- public void setAllowMemberExprAsFunctionName(boolean flag)
- {
- allowMemberExprAsFunctionName = flag;
- }
-
- public final boolean isXmlAvailable()
- {
- return xmlAvailable;
- }
-
- public void setXmlAvailable(boolean flag)
- {
- xmlAvailable = flag;
- }
-
- public final int getOptimizationLevel()
- {
- return optimizationLevel;
- }
-
- public void setOptimizationLevel(int level)
- {
- Context.checkOptimizationLevel(level);
- this.optimizationLevel = level;
- }
-
- public final boolean isGeneratingSource()
- {
- return generatingSource;
- }
-
- public boolean getWarnTrailingComma() {
- return warnTrailingComma;
- }
-
- public void setWarnTrailingComma(boolean warn) {
- warnTrailingComma = warn;
- }
-
- public final boolean isStrictMode()
- {
- return strictMode;
- }
-
- public void setStrictMode(boolean strict)
- {
- strictMode = strict;
- }
-
- public final boolean reportWarningAsError()
- {
- return warningAsError;
- }
-
- /**
- * Specify whether or not source information should be generated.
- *
- * Without source information, evaluating the "toString" method
- * on JavaScript functions produces only "[native code]" for
- * the body of the function.
- * Note that code generated without source is not fully ECMA
- * conformant.
- */
- public void setGeneratingSource(boolean generatingSource)
- {
- this.generatingSource = generatingSource;
- }
-
- /**
- * @return true iff code will be generated with callbacks to enable
- * instruction thresholds
- */
- public boolean isGenerateObserverCount() {
- return generateObserverCount;
- }
-
- /**
- * Turn on or off generation of code with callbacks to
- * track the count of executed instructions.
- * Currently only affects JVM byte code generation: this slows down the
- * generated code, but code generated without the callbacks will not
- * be counted toward instruction thresholds. Rhino's interpretive
- * mode does instruction counting without inserting callbacks, so
- * there is no requirement to compile code differently.
- * @param generateObserverCount if true, generated code will contain
- * calls to accumulate an estimate of the instructions executed.
- */
- public void setGenerateObserverCount(boolean generateObserverCount) {
- this.generateObserverCount = generateObserverCount;
- }
-
- public boolean isRecordingComments() {
- return recordingComments;
- }
-
- public void setRecordingComments(boolean record) {
- recordingComments = record;
- }
-
- public boolean isRecordingLocalJsDocComments() {
- return recordingLocalJsDocComments;
- }
-
- public void setRecordingLocalJsDocComments(boolean record) {
- recordingLocalJsDocComments = record;
- }
-
- /**
- * Turn on or off full error recovery. In this mode, parse errors do not
- * throw an exception, and the parser attempts to build a full syntax tree
- * from the input. Useful for IDEs and other frontends.
- */
- public void setRecoverFromErrors(boolean recover) {
- recoverFromErrors = recover;
- }
-
- public boolean recoverFromErrors() {
- return recoverFromErrors;
- }
-
- /**
- * Puts the parser in "IDE" mode. This enables some slightly more expensive
- * computations, such as figuring out helpful error bounds.
- */
- public void setIdeMode(boolean ide) {
- ideMode = ide;
- }
-
- public boolean isIdeMode() {
- return ideMode;
- }
-
- public Set getActivationNames() {
- return activationNames;
- }
-
- public void setActivationNames(Set activationNames) {
- this.activationNames = activationNames;
- }
-
- /**
- * Mozilla sources use the C preprocessor.
- */
- public void setAllowSharpComments(boolean allow) {
- allowSharpComments = allow;
- }
-
- public boolean getAllowSharpComments() {
- return allowSharpComments;
- }
-
- /**
- * Returns a {@code CompilerEnvirons} suitable for using Rhino
- * in an IDE environment. Most features are enabled by default.
- * The {@link ErrorReporter} is set to an {@link ErrorCollector}.
- */
- public static CompilerEnvirons ideEnvirons() {
- CompilerEnvirons env = new CompilerEnvirons();
- env.setRecoverFromErrors(true);
- env.setRecordingComments(true);
- env.setStrictMode(true);
- env.setWarnTrailingComma(true);
- env.setLanguageVersion(Context.VERSION_1_7);
- env.setReservedKeywordAsIdentifier(true);
- env.setIdeMode(true);
- env.setErrorReporter(new ErrorCollector());
- return env;
- }
-
- private ErrorReporter errorReporter;
-
- private int languageVersion;
- private boolean generateDebugInfo;
- private boolean reservedKeywordAsIdentifier;
- private boolean allowMemberExprAsFunctionName;
- private boolean xmlAvailable;
- private int optimizationLevel;
- private boolean generatingSource;
- private boolean strictMode;
- private boolean warningAsError;
- private boolean generateObserverCount;
- private boolean recordingComments;
- private boolean recordingLocalJsDocComments;
- private boolean recoverFromErrors;
- private boolean warnTrailingComma;
- private boolean ideMode;
- private boolean allowSharpComments;
- Set activationNames;
-}
diff --git a/src/main/java/org/mozilla/javascript/ConsString.java b/src/main/java/org/mozilla/javascript/ConsString.java
deleted file mode 100644
index 47d541e1..00000000
--- a/src/main/java/org/mozilla/javascript/ConsString.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/* -*- 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.javascript;
-
-import java.io.Serializable;
-import java.util.ArrayDeque;
-
-/**
- *
This class represents a string composed of two components, each of which
- * may be a java.lang.String or another ConsString.
- *
- *
This string representation is optimized for concatenation using the "+"
- * operator. Instead of immediately copying both components to a new character
- * array, ConsString keeps references to the original components and only
- * converts them to a String if either toString() is called or a certain depth
- * level is reached.
- *
- *
Note that instances of this class are only immutable if both parts are
- * immutable, i.e. either Strings or ConsStrings that are ultimately composed
- * of Strings.
- *
- *
Both the name and the concept are borrowed from V8.
- */
-public class ConsString implements CharSequence, Serializable {
-
- private static final long serialVersionUID = -8432806714471372570L;
-
- private CharSequence left, right;
- private final int length;
- private boolean isFlat;
-
- public ConsString(CharSequence str1, CharSequence str2) {
- left = str1;
- right = str2;
- length = left.length() + right.length();
- isFlat = false;
- }
-
- // Replace with string representation when serializing
- private Object writeReplace() {
- return this.toString();
- }
-
- @Override
- public String toString() {
- return isFlat ? (String)left : flatten();
- }
-
- private synchronized String flatten() {
- if (!isFlat) {
- final char[] chars = new char[length];
- int charPos = length;
-
- ArrayDeque stack = new ArrayDeque();
- stack.addFirst(left);
-
- CharSequence next = right;
- do {
- if (next instanceof ConsString) {
- ConsString casted = (ConsString) next;
- if (casted.isFlat) {
- next = casted.left;
- } else {
- stack.addFirst(casted.left);
- next = casted.right;
- continue;
- }
- }
-
- final String str = (String) next;
- charPos -= str.length();
- str.getChars(0, str.length(), chars, charPos);
- next = stack.isEmpty() ? null : stack.removeFirst();
- } while (next != null);
-
- left = new String(chars);
- right = "";
- isFlat = true;
- }
- return (String)left;
- }
-
- @Override
- public int length() {
- return length;
- }
-
- @Override
- public char charAt(int index) {
- String str = isFlat ? (String)left : flatten();
- return str.charAt(index);
- }
-
- @Override
- public CharSequence subSequence(int start, int end) {
- String str = isFlat ? (String)left : flatten();
- return str.substring(start, end);
- }
-}
diff --git a/src/main/java/org/mozilla/javascript/ConstProperties.java b/src/main/java/org/mozilla/javascript/ConstProperties.java
deleted file mode 100644
index cef2159b..00000000
--- a/src/main/java/org/mozilla/javascript/ConstProperties.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/* -*- 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/. */
-
-// API class
-
-package org.mozilla.javascript;
-
-public interface ConstProperties {
- /**
- * Sets a named const property in this object.
- *
- * The property is specified by a string name
- * as defined for Scriptable.get.
- *
- * The possible values that may be passed in are as defined for
- * Scriptable.get. A class that implements this method may choose
- * to ignore calls to set certain properties, in which case those
- * properties are effectively read-only.
- * For properties defined in a prototype chain,
- * use putProperty in ScriptableObject.
- * Note that if a property a is defined in the prototype p
- * of an object o, then evaluating o.a = 23 will cause
- * set to be called on the prototype p with
- * o as the start parameter.
- * To preserve JavaScript semantics, it is the Scriptable
- * object's responsibility to modify o.
- * This design allows properties to be defined in prototypes and implemented
- * in terms of getters and setters of Java values without consuming slots
- * in each instance.
- *
- * The values that may be set are limited to the following:
- *
- *
java.lang.Boolean objects
- *
java.lang.String objects
- *
java.lang.Number objects
- *
org.mozilla.javascript.Scriptable objects
- *
null
- *
The value returned by Context.getUndefinedValue()
- *
- * Arbitrary Java objects may be wrapped in a Scriptable by first calling
- * Context.toObject. This allows the property of a JavaScript
- * object to contain an arbitrary Java object as a value.
- * Note that has will be called by the runtime first before
- * set is called to determine in which object the
- * property is defined.
- * Note that this method is not expected to traverse the prototype chain,
- * which is different from the ECMA [[Put]] operation.
- * @param name the name of the property
- * @param start the object whose property is being set
- * @param value value to set the property to
- * @see org.mozilla.javascript.Scriptable#has(String, Scriptable)
- * @see org.mozilla.javascript.Scriptable#get(String, Scriptable)
- * @see org.mozilla.javascript.ScriptableObject#putProperty(Scriptable, String, Object)
- * @see org.mozilla.javascript.Context#toObject(Object, Scriptable)
- */
- public void putConst(String name, Scriptable start, Object value);
-
- /**
- * Reserves a definition spot for a const. This will set up a definition
- * of the const property, but set its value to undefined. The semantics of
- * the start parameter is the same as for putConst.
- * @param name The name of the property.
- * @param start The object whose property is being reserved.
- */
- public void defineConst(String name, Scriptable start);
-
- /**
- * Returns true if the named property is defined as a const on this object.
- * @param name
- * @return true if the named property is defined as a const, false
- * otherwise.
- */
- public boolean isConst(String name);
-}
diff --git a/src/main/java/org/mozilla/javascript/Constructable.java b/src/main/java/org/mozilla/javascript/Constructable.java
deleted file mode 100644
index 9f3d53bc..00000000
--- a/src/main/java/org/mozilla/javascript/Constructable.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.mozilla.javascript;
-
-/** An interface that can be used to implement a constructor function as a lambda. */
-public interface Constructable {
-
- /**
- * Call the function as a constructor.
- *
- *
This method is invoked by the runtime in order to satisfy a use of the JavaScript
- * new operator. This method is expected to create a new object and return it.
- *
- * @param cx the current Context for this thread
- * @param scope an enclosing scope of the caller except when the function is called from a
- * closure.
- * @param args the array of arguments
- * @return the allocated object
- */
- Scriptable construct(Context cx, Scriptable scope, Object[] args);
-}
diff --git a/src/main/java/org/mozilla/javascript/Context.java b/src/main/java/org/mozilla/javascript/Context.java
deleted file mode 100644
index a317611f..00000000
--- a/src/main/java/org/mozilla/javascript/Context.java
+++ /dev/null
@@ -1,2688 +0,0 @@
-/* -*- 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/. */
-
-// API class
-
-package org.mozilla.javascript;
-
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.Reader;
-import java.io.StringWriter;
-import java.io.Writer;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayDeque;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.UnaryOperator;
-import org.mozilla.classfile.ClassFileWriter.ClassFileFormatException;
-import org.mozilla.javascript.ast.AstRoot;
-import org.mozilla.javascript.ast.ScriptNode;
-import org.mozilla.javascript.debug.DebuggableScript;
-import org.mozilla.javascript.debug.Debugger;
-import org.mozilla.javascript.xml.XMLLib;
-import com.reider745.hooks.ZoteClassLoaderPool;
-
-/**
- * This class represents the runtime context of an executing script.
- *
- *
Before executing a script, an instance of Context must be created and associated with the
- * thread that will be executing the script. The Context will be used to store information about the
- * executing of the script such as the call stack. Contexts are associated with the current thread
- * using the {@link #call(ContextAction)} or {@link #enter()} methods.
- *
- *
Different forms of script execution are supported. Scripts may be evaluated from the source
- * directly, or first compiled and then later executed. Interactive execution is also supported.
- *
- *
Some aspects of script execution, such as type conversions and object creation, may be
- * accessed directly through methods of Context.
- *
- * @see Scriptable
- * @author Norris Boyd
- * @author Brendan Eich
- */
-public class Context implements Closeable {
- /**
- * Language versions.
- *
- *
All integral values are reserved for future version numbers.
- */
-
- /**
- * The unknown version.
- *
- *
Be aware, this version will not support many of the newer language features and will not
- * change in the future.
- *
- *
Please use one of the other constants like VERSION_ES6 to get support for recent language
- * features.
- */
- public static final int VERSION_UNKNOWN = -1;
-
- /** The default version. */
- public static final int VERSION_DEFAULT = 0;
-
- /** JavaScript 1.0 */
- public static final int VERSION_1_0 = 100;
-
- /** JavaScript 1.1 */
- public static final int VERSION_1_1 = 110;
-
- /** JavaScript 1.2 */
- public static final int VERSION_1_2 = 120;
-
- /** JavaScript 1.3 */
- public static final int VERSION_1_3 = 130;
-
- /** JavaScript 1.4 */
- public static final int VERSION_1_4 = 140;
-
- /** JavaScript 1.5 */
- public static final int VERSION_1_5 = 150;
-
- /** JavaScript 1.6 */
- public static final int VERSION_1_6 = 160;
-
- /** JavaScript 1.7 */
- public static final int VERSION_1_7 = 170;
-
- /** JavaScript 1.8 */
- public static final int VERSION_1_8 = 180;
-
- /** ECMAScript 6. */
- public static final int VERSION_ES6 = 200;
-
- /**
- * Controls behaviour of Date.prototype.getYear(). If
- * hasFeature(FEATURE_NON_ECMA_GET_YEAR) returns true, Date.prototype.getYear subtructs
- * 1900 only if 1900 <= date < 2000. The default behavior of {@link #hasFeature(int)} is
- * always to subtruct 1900 as rquired by ECMAScript B.2.4.
- */
- public static final int FEATURE_NON_ECMA_GET_YEAR = 1;
-
- /**
- * Control if member expression as function name extension is available. If
- * hasFeature(FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME) returns true, allow
- * function memberExpression(args) { body } to be syntax sugar for
- * memberExpression = function(args) { body }, when memberExpression is not a simple
- * identifier. See ECMAScript-262, section 11.2 for definition of memberExpression. By default
- * {@link #hasFeature(int)} returns false.
- */
- public static final int FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME = 2;
-
- /**
- * Control if reserved keywords are treated as identifiers. If
- * hasFeature(RESERVED_KEYWORD_AS_IDENTIFIER) returns true, treat future reserved keyword
- * (see Ecma-262, section 7.5.3) as ordinary identifiers but warn about this usage.
- *
- *
By default {@link #hasFeature(int)} returns false.
- */
- public static final int FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER = 3;
-
- /**
- * Control if toString() should returns the same result as toSource()
- * when applied to objects and arrays. If hasFeature(FEATURE_TO_STRING_AS_SOURCE)
- * returns true, calling toString() on JS objects gives the same result as calling
- * toSource(). That is it returns JS source with code to create an object with all
- * enumeratable fields of the original object instead of printing [object result of
- * {@link Scriptable#getClassName()}].
- *
- *
By default {@link #hasFeature(int)} returns true only if the current JS version is set to
- * {@link #VERSION_1_2}.
- */
- public static final int FEATURE_TO_STRING_AS_SOURCE = 4;
-
- /**
- * Control if properties __proto__ and __parent__ are treated
- * specially. If hasFeature(FEATURE_PARENT_PROTO_PROPERTIES) returns true, treat
- * __parent__ and __proto__ as special properties.
- *
- *
The properties allow to query and set scope and prototype chains for the objects. The
- * special meaning of the properties is available only when they are used as the right hand side
- * of the dot operator. For example, while x.__proto__ = y changes the prototype
- * chain of the object x to point to y, x["__proto__"] = y
- * simply assigns a new value to the property __proto__ in x
- * even when the feature is on.
- *
- *
By default {@link #hasFeature(int)} returns true.
- */
- public static final int FEATURE_PARENT_PROTO_PROPERTIES = 5;
-
- /** @deprecated In previous releases, this name was given to FEATURE_PARENT_PROTO_PROPERTIES. */
- @Deprecated public static final int FEATURE_PARENT_PROTO_PROPRTIES = 5;
-
- /**
- * Control if support for E4X(ECMAScript for XML) extension is available. If
- * hasFeature(FEATURE_E4X) returns true, the XML syntax is available.
- *
- *
By default {@link #hasFeature(int)} returns true if the current JS version is set to
- * {@link #VERSION_DEFAULT} or is at least {@link #VERSION_1_6}.
- *
- * @since 1.6 Release 1
- */
- public static final int FEATURE_E4X = 6;
-
- /**
- * Control if dynamic scope should be used for name access. If hasFeature(FEATURE_DYNAMIC_SCOPE)
- * returns true, then the name lookup during name resolution will use the top scope of the
- * script or function which is at the top of JS execution stack instead of the top scope of the
- * script or function from the current stack frame if the top scope of the top stack frame
- * contains the top scope of the current stack frame on its prototype chain.
- *
- *
This is useful to define shared scope containing functions that can be called from scripts
- * and functions using private scopes.
- *
- *
By default {@link #hasFeature(int)} returns false.
- *
- * @since 1.6 Release 1
- */
- public static final int FEATURE_DYNAMIC_SCOPE = 7;
-
- /**
- * Control if strict variable mode is enabled. When the feature is on Rhino reports runtime
- * errors if assignment to a global variable that does not exist is executed. When the feature
- * is off such assignments create a new variable in the global scope as required by ECMA 262.
- *
- *
By default {@link #hasFeature(int)} returns false.
- *
- * @since 1.6 Release 1
- */
- public static final int FEATURE_STRICT_VARS = 8;
-
- /**
- * Control if strict eval mode is enabled. When the feature is on Rhino reports runtime errors
- * if non-string argument is passed to the eval function. When the feature is off eval simply
- * return non-string argument as is without performing any evaluation as required by ECMA 262.
- *
- *
By default {@link #hasFeature(int)} returns false.
- *
- * @since 1.6 Release 1
- */
- public static final int FEATURE_STRICT_EVAL = 9;
-
- /**
- * When the feature is on Rhino will add a "fileName" and "lineNumber" properties to Error
- * objects automatically. When the feature is off, you have to explicitly pass them as the
- * second and third argument to the Error constructor. Note that neither behavior is fully ECMA
- * 262 compliant (as 262 doesn't specify a three-arg constructor), but keeping the feature off
- * results in Error objects that don't have additional non-ECMA properties when constructed
- * using the ECMA-defined single-arg constructor and is thus desirable if a stricter ECMA
- * compliance is desired, specifically adherence to the point 15.11.5. of the standard.
- *
- *
By default {@link #hasFeature(int)} returns false.
- *
- * @since 1.6 Release 6
- */
- public static final int FEATURE_LOCATION_INFORMATION_IN_ERROR = 10;
-
- /**
- * Controls whether JS 1.5 'strict mode' is enabled. When the feature is on, Rhino reports more
- * than a dozen different warnings. When the feature is off, these warnings are not generated.
- * FEATURE_STRICT_MODE implies FEATURE_STRICT_VARS and FEATURE_STRICT_EVAL.
- *
- *
By default {@link #hasFeature(int)} returns false.
- *
- * @since 1.6 Release 6
- */
- public static final int FEATURE_STRICT_MODE = 11;
-
- /**
- * Controls whether a warning should be treated as an error.
- *
- * @since 1.6 Release 6
- */
- public static final int FEATURE_WARNING_AS_ERROR = 12;
-
- /**
- * Enables enhanced access to Java. Specifically, controls whether private and protected members
- * can be accessed, and whether scripts can catch all Java exceptions.
- *
- *
Note that this feature should only be enabled for trusted scripts.
- *
- *
By default {@link #hasFeature(int)} returns false.
- *
- * @since 1.7 Release 1
- */
- public static final int FEATURE_ENHANCED_JAVA_ACCESS = 13;
-
- /**
- * Enables access to JavaScript features from ECMAscript 6 that are present in JavaScript
- * engines that do not yet support version 6, such as V8. This includes support for typed
- * arrays. Default is true.
- *
- * @since 1.7 Release 3
- */
- public static final int FEATURE_V8_EXTENSIONS = 14;
-
- /**
- * Defines how an undefined "this" parameter is handled in certain calls. Previously Rhino would
- * convert an undefined "this" to null, whereas recent specs call for it to be treated
- * differently. Default is to be set if language version <= 1.7.
- *
- * @since 1.7.7
- */
- public static final int FEATURE_OLD_UNDEF_NULL_THIS = 15;
-
- /**
- * If set, then the order of property key enumeration will be first numeric keys in numeric
- * order, followed by string keys in order of creation, and finally Symbol keys, as specified in
- * ES6. Default is true for language version >= "ES6" and false otherwise.
- *
- * @since 1.7.7.1
- */
- public static final int FEATURE_ENUMERATE_IDS_FIRST = 16;
-
- /**
- * If set, then all objects will have a thread-safe property map. (Note that this doesn't make
- * everything else that they do thread-safe -- that depends on the specific implementation. If
- * not set, users should not share Rhino objects between threads, unless the "sync" function is
- * used to wrap them with an explicit synchronizer. The default is false, which means that by
- * default, individual objects are not thread-safe.
- *
- * @since 1.7.8
- */
- public static final int FEATURE_THREAD_SAFE_OBJECTS = 17;
-
- /**
- * If set, then all integer numbers will be returned without decimal place. For instance assume
- * there is a function like this: function foo() {return 5;} 5 will be returned if
- * feature is set, 5.0 otherwise.
- */
- public static final int FEATURE_INTEGER_WITHOUT_DECIMAL_PLACE = 18;
-
- /**
- * TypedArray buffer uses little/big endian depending on the platform. The default is big endian
- * for Rhino.
- *
- * @since 1.7 Release 11
- */
- public static final int FEATURE_LITTLE_ENDIAN = 19;
-
- /**
- * Configure the XMLProcessor to parse XML with security features or not. Security features
- * include not fetching remote entity references and disabling XIncludes
- *
- * @since 1.7 Release 12
- */
- public static final int FEATURE_ENABLE_XML_SECURE_PARSING = 20;
-
- /**
- * Configure whether the entries in a Java Map can be accessed by properties.
- *
- *
Not enabled:
- *
- *
var map = new java.util.HashMap(); map.put('foo', 1); map.foo; // undefined
- *
- *
Enabled:
- *
- *
var map = new java.util.HashMap(); map.put('foo', 1); map.foo; // 1
- *
- *
WARNING: This feature is similar to the one in Nashorn, but incomplete.
- *
- *
2. The distinction between numeric keys and string keys is ambiguous.
- *
- *
map.put('1', 123); map['1']; // Not found. This means `map[1]`.
- *
- * @since 1.7 Release 14
- */
- public static final int FEATURE_ENABLE_JAVA_MAP_ACCESS = 21;
-
- public static final String languageVersionProperty = "language version";
- public static final String errorReporterProperty = "error reporter";
-
- /** Convenient value to use as zero-length array of objects. */
- public static final Object[] emptyArgs = ScriptRuntime.emptyArgs;
-
- /**
- * Creates a new Context. The context will be associated with the {@link
- * ContextFactory#getGlobal() global context factory}.
- *
- *
Note that the Context must be associated with a thread before it can be used to execute a
- * script.
- *
- * @deprecated this constructor is deprecated because it creates a dependency on a static
- * singleton context factory. Use {@link ContextFactory#enter()} or {@link
- * ContextFactory#call(ContextAction)} instead. If you subclass this class, consider using
- * {@link #Context(ContextFactory)} constructor instead in the subclasses' constructors.
- */
- @Deprecated
- public Context() {
- this(ContextFactory.getGlobal());
- }
-
- /**
- * Creates a new context. Provided as a preferred super constructor for subclasses in place of
- * the deprecated default public constructor.
- *
- * @param factory the context factory associated with this context (most likely, the one that
- * created the context). Can not be null. The context features are inherited from the
- * factory, and the context will also otherwise use its factory's services.
- * @throws IllegalArgumentException if factory parameter is null.
- */
- protected Context(ContextFactory factory) {
- if (factory == null) {
- throw new IllegalArgumentException("factory == null");
- }
- this.factory = factory;
- version = VERSION_DEFAULT;
- optimizationLevel = codegenClass != null ? 0 : -1;
- maximumInterpreterStackDepth = Integer.MAX_VALUE;
- }
-
- /**
- * Get the current Context.
- *
- *
The current Context is per-thread; this method looks up the Context associated with the
- * current thread.
- *
- *
- *
- * @return the Context associated with the current thread, or null if no context is associated
- * with the current thread.
- * @see ContextFactory#enterContext()
- * @see ContextFactory#call(ContextAction)
- */
- public static Context getCurrentContext() {
- Object helper = VMBridge.instance.getThreadContextHelper();
- return VMBridge.instance.getContext(helper);
- }
-
- /**
- * Same as calling {@link ContextFactory#enterContext()} on the global ContextFactory instance.
- *
- * @return a Context associated with the current thread
- * @see #getCurrentContext()
- * @see #exit()
- * @see #call(ContextAction)
- */
- public static Context enter() {
- return enter(null, ContextFactory.getGlobal());
- }
-
- /**
- * Get a Context associated with the current thread, using the given Context if need be.
- *
- *
The same as enter() except that cx is associated with the
- * current thread and returned if the current thread has no associated context and cx
- * is not associated with any other thread.
- *
- * @param cx a Context to associate with the thread if possible
- * @return a Context associated with the current thread
- * @deprecated use {@link ContextFactory#enterContext(Context)} instead as this method relies on
- * usage of a static singleton "global" ContextFactory.
- * @see ContextFactory#enterContext(Context)
- * @see ContextFactory#call(ContextAction)
- */
- @Deprecated
- public static Context enter(Context cx) {
- return enter(cx, ContextFactory.getGlobal());
- }
-
- static final Context enter(Context cx, ContextFactory factory) {
- Object helper = VMBridge.instance.getThreadContextHelper();
- Context old = VMBridge.instance.getContext(helper);
- if (old != null) {
- cx = old;
- } else {
- if (cx == null) {
- cx = factory.makeContext();
- if (cx.enterCount != 0) {
- throw new IllegalStateException(
- "factory.makeContext() returned Context instance already associated with some thread");
- }
- factory.onContextCreated(cx);
- if (factory.isSealed() && !cx.isSealed()) {
- cx.seal(null);
- }
- } else {
- if (cx.enterCount != 0) {
- throw new IllegalStateException(
- "can not use Context instance already associated with some thread");
- }
- }
- VMBridge.instance.setContext(helper, cx);
- }
- ++cx.enterCount;
- return cx;
- }
-
- /**
- * Exit a block of code requiring a Context.
- *
- *
Calling exit() will remove the association between the current thread and a
- * Context if the prior call to {@link ContextFactory#enterContext()} on this thread newly
- * associated a Context with this thread. Once the current thread no longer has an associated
- * Context, it cannot be used to execute JavaScript until it is again associated with a Context.
- *
- * @see ContextFactory#enterContext()
- */
- public static void exit() {
- Object helper = VMBridge.instance.getThreadContextHelper();
- Context cx = VMBridge.instance.getContext(helper);
- if (cx == null) {
- throw new IllegalStateException("Calling Context.exit without previous Context.enter");
- }
- if (cx.enterCount < 1) Kit.codeBug();
- if (--cx.enterCount == 0) {
- VMBridge.instance.setContext(helper, null);
- cx.factory.onContextReleased(cx);
- }
- }
-
- @Override
- public void close() {
- exit();
- }
-
- /**
- * Call {@link ContextAction#run(Context cx)} using the Context instance associated with the
- * current thread. If no Context is associated with the thread, then
- * ContextFactory.getGlobal().makeContext() will be called to construct new Context
- * instance. The instance will be temporary associated with the thread during call to {@link
- * ContextAction#run(Context)}.
- *
- * @deprecated use {@link ContextFactory#call(ContextAction)} instead as this method relies on
- * usage of a static singleton "global" ContextFactory.
- * @return The result of {@link ContextAction#run(Context)}.
- */
- @Deprecated
- public static T call(ContextAction action) {
- return call(ContextFactory.getGlobal(), action);
- }
-
- /**
- * Call {@link Callable#call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args)}
- * using the Context instance associated with the current thread. If no Context is associated
- * with the thread, then {@link ContextFactory#makeContext()} will be called to construct new
- * Context instance. The instance will be temporary associated with the thread during call to
- * {@link ContextAction#run(Context)}.
- *
- *
It is allowed but not advisable to use null for factory argument in which
- * case the global static singleton ContextFactory instance will be used to create new context
- * instances.
- *
- * @see ContextFactory#call(ContextAction)
- */
- public static Object call(
- ContextFactory factory,
- final Callable callable,
- final Scriptable scope,
- final Scriptable thisObj,
- final Object[] args) {
- if (factory == null) {
- factory = ContextFactory.getGlobal();
- }
- return call(factory, cx -> callable.call(cx, scope, thisObj, args));
- }
-
- /** The method implements {@link ContextFactory#call(ContextAction)} logic. */
- static T call(ContextFactory factory, ContextAction action) {
- Context cx = enter(null, factory);
- try {
- return action.run(cx);
- } finally {
- exit();
- }
- }
-
- /**
- * @deprecated
- * @see ContextFactory#addListener(org.mozilla.javascript.ContextFactory.Listener)
- * @see ContextFactory#getGlobal()
- */
- @Deprecated
- public static void addContextListener(ContextListener listener) {
- // Special workaround for the debugger
- String DBG = "org.mozilla.javascript.tools.debugger.Main";
- if (DBG.equals(listener.getClass().getName())) {
- Class> cl = listener.getClass();
- Class> factoryClass = Kit.classOrNull("org.mozilla.javascript.ContextFactory");
- Class>[] sig = {factoryClass};
- Object[] args = {ContextFactory.getGlobal()};
- try {
- Method m = cl.getMethod("attachTo", sig);
- m.invoke(listener, args);
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
- return;
- }
-
- ContextFactory.getGlobal().addListener(listener);
- }
-
- /**
- * @deprecated
- * @see ContextFactory#removeListener(org.mozilla.javascript.ContextFactory.Listener)
- * @see ContextFactory#getGlobal()
- */
- @Deprecated
- public static void removeContextListener(ContextListener listener) {
- ContextFactory.getGlobal().addListener(listener);
- }
-
- /** Return {@link ContextFactory} instance used to create this Context. */
- public final ContextFactory getFactory() {
- return factory;
- }
-
- /**
- * Checks if this is a sealed Context. A sealed Context instance does not allow to modify any of
- * its properties and will throw an exception on any such attempt.
- *
- * @see #seal(Object sealKey)
- */
- public final boolean isSealed() {
- return sealed;
- }
-
- /**
- * Seal this Context object so any attempt to modify any of its properties including calling
- * {@link #enter()} and {@link #exit()} methods will throw an exception.
- *
- *
If sealKey is not null, calling {@link #unseal(Object sealKey)} with the same
- * key unseals the object. If sealKey is null, unsealing is no longer possible.
- *
- * @see #isSealed()
- * @see #unseal(Object)
- */
- public final void seal(Object sealKey) {
- if (sealed) onSealedMutation();
- sealed = true;
- this.sealKey = sealKey;
- }
-
- /**
- * Unseal previously sealed Context object. The sealKey argument should not be null
- * and should match sealKey suplied with the last call to {@link #seal(Object)} or
- * an exception will be thrown.
- *
- * @see #isSealed()
- * @see #seal(Object sealKey)
- */
- public final void unseal(Object sealKey) {
- if (sealKey == null) throw new IllegalArgumentException();
- if (this.sealKey != sealKey) throw new IllegalArgumentException();
- if (!sealed) throw new IllegalStateException();
- sealed = false;
- this.sealKey = null;
- }
-
- static void onSealedMutation() {
- throw new IllegalStateException();
- }
-
- /**
- * Get the current language version.
- *
- *
The language version number affects JavaScript semantics as detailed in the overview
- * documentation.
- *
- * @return an integer that is one of VERSION_1_0, VERSION_1_1, etc.
- */
- public final int getLanguageVersion() {
- return version;
- }
-
- /**
- * Set the language version.
- *
- *
Setting the language version will affect functions and scripts compiled subsequently. See
- * the overview documentation for version-specific behavior.
- *
- * @param version the version as specified by VERSION_1_0, VERSION_1_1, etc.
- */
- public void setLanguageVersion(int version) {
- if (sealed) onSealedMutation();
- checkLanguageVersion(version);
- Object listeners = propertyListeners;
- if (listeners != null && version != this.version) {
- firePropertyChangeImpl(
- listeners,
- languageVersionProperty,
- Integer.valueOf(this.version),
- Integer.valueOf(version));
- }
- this.version = version;
- }
-
- public static boolean isValidLanguageVersion(int version) {
- switch (version) {
- case VERSION_DEFAULT:
- case VERSION_1_0:
- case VERSION_1_1:
- case VERSION_1_2:
- case VERSION_1_3:
- case VERSION_1_4:
- case VERSION_1_5:
- case VERSION_1_6:
- case VERSION_1_7:
- case VERSION_1_8:
- case VERSION_ES6:
- return true;
- }
- return false;
- }
-
- public static void checkLanguageVersion(int version) {
- if (isValidLanguageVersion(version)) {
- return;
- }
- throw new IllegalArgumentException("Bad language version: " + version);
- }
-
- /**
- * Get the implementation version.
- *
- *
The implementation version is of the form
- *
- *
- * "name langVerreleaserelNum date"
- *
- *
- * where name is the name of the product, langVer is the language version,
- * relNum is the release number, and date is the release date for that specific
- * release in the form "yyyy mm dd".
- *
- * @return a string that encodes the product, language version, release number, and date.
- */
- public final String getImplementationVersion() {
- return ImplementationVersion.get();
- }
-
- /**
- * Get the current error reporter.
- *
- * @see org.mozilla.javascript.ErrorReporter
- */
- public final ErrorReporter getErrorReporter() {
- if (errorReporter == null) {
- return DefaultErrorReporter.instance;
- }
- return errorReporter;
- }
-
- /**
- * Change the current error reporter.
- *
- * @return the previous error reporter
- * @see org.mozilla.javascript.ErrorReporter
- */
- public final ErrorReporter setErrorReporter(ErrorReporter reporter) {
- if (sealed) onSealedMutation();
- if (reporter == null) throw new IllegalArgumentException();
- ErrorReporter old = getErrorReporter();
- if (reporter == old) {
- return old;
- }
- Object listeners = propertyListeners;
- if (listeners != null) {
- firePropertyChangeImpl(
- listeners, errorReporterProperty,
- old, reporter);
- }
- this.errorReporter = reporter;
- return old;
- }
-
- /**
- * Get the current locale. Returns the default locale if none has been set.
- *
- * @see java.util.Locale
- */
- public final Locale getLocale() {
- if (locale == null) locale = Locale.getDefault();
- return locale;
- }
-
- /**
- * Set the current locale.
- *
- * @see java.util.Locale
- */
- public final Locale setLocale(Locale loc) {
- if (sealed) onSealedMutation();
- Locale result = locale;
- locale = loc;
- return result;
- }
-
- /**
- * Register an object to receive notifications when a bound property has changed
- *
- * @see java.beans.PropertyChangeEvent
- * @see #removePropertyChangeListener(java.beans.PropertyChangeListener)
- * @param l the listener
- */
- public final void addPropertyChangeListener(PropertyChangeListener l) {
- if (sealed) onSealedMutation();
- propertyListeners = Kit.addListener(propertyListeners, l);
- }
-
- /**
- * Remove an object from the list of objects registered to receive notification of changes to a
- * bounded property
- *
- * @see java.beans.PropertyChangeEvent
- * @see #addPropertyChangeListener(java.beans.PropertyChangeListener)
- * @param l the listener
- */
- public final void removePropertyChangeListener(PropertyChangeListener l) {
- if (sealed) onSealedMutation();
- propertyListeners = Kit.removeListener(propertyListeners, l);
- }
-
- /**
- * Notify any registered listeners that a bounded property has changed
- *
- * @see #addPropertyChangeListener(java.beans.PropertyChangeListener)
- * @see #removePropertyChangeListener(java.beans.PropertyChangeListener)
- * @see java.beans.PropertyChangeListener
- * @see java.beans.PropertyChangeEvent
- * @param property the bound property
- * @param oldValue the old value
- * @param newValue the new value
- */
- final void firePropertyChange(String property, Object oldValue, Object newValue) {
- Object listeners = propertyListeners;
- if (listeners != null) {
- firePropertyChangeImpl(listeners, property, oldValue, newValue);
- }
- }
-
- private void firePropertyChangeImpl(
- Object listeners, String property, Object oldValue, Object newValue) {
- for (int i = 0; ; ++i) {
- Object l = Kit.getListener(listeners, i);
- if (l == null) break;
- if (l instanceof PropertyChangeListener) {
- PropertyChangeListener pcl = (PropertyChangeListener) l;
- pcl.propertyChange(new PropertyChangeEvent(this, property, oldValue, newValue));
- }
- }
- }
-
- /**
- * Report a warning using the error reporter for the current thread.
- *
- * @param message the warning message to report
- * @param sourceName a string describing the source, such as a filename
- * @param lineno the starting line number
- * @param lineSource the text of the line (may be null)
- * @param lineOffset the offset into lineSource where problem was detected
- * @see org.mozilla.javascript.ErrorReporter
- */
- public static void reportWarning(
- String message, String sourceName, int lineno, String lineSource, int lineOffset) {
- Context cx = Context.getContext();
- if (cx.hasFeature(FEATURE_WARNING_AS_ERROR))
- reportError(message, sourceName, lineno, lineSource, lineOffset);
- else cx.getErrorReporter().warning(message, sourceName, lineno, lineSource, lineOffset);
- }
-
- /**
- * Report a warning using the error reporter for the current thread.
- *
- * @param message the warning message to report
- * @see org.mozilla.javascript.ErrorReporter
- */
- public static void reportWarning(String message) {
- int[] linep = {0};
- String filename = getSourcePositionFromStack(linep);
- Context.reportWarning(message, filename, linep[0], null, 0);
- }
-
- public static void reportWarning(String message, Throwable t) {
- int[] linep = {0};
- String filename = getSourcePositionFromStack(linep);
- Writer sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
- pw.println(message);
- t.printStackTrace(pw);
- pw.flush();
- Context.reportWarning(sw.toString(), filename, linep[0], null, 0);
- }
-
- /**
- * Report an error using the error reporter for the current thread.
- *
- * @param message the error message to report
- * @param sourceName a string describing the source, such as a filename
- * @param lineno the starting line number
- * @param lineSource the text of the line (may be null)
- * @param lineOffset the offset into lineSource where problem was detected
- * @see org.mozilla.javascript.ErrorReporter
- */
- public static void reportError(
- String message, String sourceName, int lineno, String lineSource, int lineOffset) {
- Context cx = getCurrentContext();
- if (cx != null) {
- cx.getErrorReporter().error(message, sourceName, lineno, lineSource, lineOffset);
- } else {
- throw new EvaluatorException(message, sourceName, lineno, lineSource, lineOffset);
- }
- }
-
- /**
- * Report an error using the error reporter for the current thread.
- *
- * @param message the error message to report
- * @see org.mozilla.javascript.ErrorReporter
- */
- public static void reportError(String message) {
- int[] linep = {0};
- String filename = getSourcePositionFromStack(linep);
- Context.reportError(message, filename, linep[0], null, 0);
- }
-
- /**
- * Report a runtime error using the error reporter for the current thread.
- *
- * @param message the error message to report
- * @param sourceName a string describing the source, such as a filename
- * @param lineno the starting line number
- * @param lineSource the text of the line (may be null)
- * @param lineOffset the offset into lineSource where problem was detected
- * @return a runtime exception that will be thrown to terminate the execution of the script
- * @see org.mozilla.javascript.ErrorReporter
- */
- public static EvaluatorException reportRuntimeError(
- String message, String sourceName, int lineno, String lineSource, int lineOffset) {
- Context cx = getCurrentContext();
- if (cx != null) {
- return cx.getErrorReporter()
- .runtimeError(message, sourceName, lineno, lineSource, lineOffset);
- }
- throw new EvaluatorException(message, sourceName, lineno, lineSource, lineOffset);
- }
-
- static EvaluatorException reportRuntimeErrorById(String messageId, Object... args) {
- String msg = ScriptRuntime.getMessageById(messageId, args);
- return reportRuntimeError(msg);
- }
-
- /** @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead */
- @Deprecated
- static EvaluatorException reportRuntimeError0(String messageId) {
- String msg = ScriptRuntime.getMessageById(messageId);
- return reportRuntimeError(msg);
- }
-
- /** @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead */
- @Deprecated
- static EvaluatorException reportRuntimeError1(String messageId, Object arg1) {
- String msg = ScriptRuntime.getMessageById(messageId, arg1);
- return reportRuntimeError(msg);
- }
-
- /** @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead */
- @Deprecated
- static EvaluatorException reportRuntimeError2(String messageId, Object arg1, Object arg2) {
- String msg = ScriptRuntime.getMessageById(messageId, arg1, arg2);
- return reportRuntimeError(msg);
- }
-
- /** @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead */
- @Deprecated
- static EvaluatorException reportRuntimeError3(
- String messageId, Object arg1, Object arg2, Object arg3) {
- String msg = ScriptRuntime.getMessageById(messageId, arg1, arg2, arg3);
- return reportRuntimeError(msg);
- }
-
- /** @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead */
- @Deprecated
- static EvaluatorException reportRuntimeError4(
- String messageId, Object arg1, Object arg2, Object arg3, Object arg4) {
- String msg = ScriptRuntime.getMessageById(messageId, arg1, arg2, arg3, arg4);
- return reportRuntimeError(msg);
- }
-
- /**
- * Report a runtime error using the error reporter for the current thread.
- *
- * @param message the error message to report
- * @see org.mozilla.javascript.ErrorReporter
- */
- public static EvaluatorException reportRuntimeError(String message) {
- int[] linep = {0};
- String filename = getSourcePositionFromStack(linep);
- return Context.reportRuntimeError(message, filename, linep[0], null, 0);
- }
-
- /**
- * Initialize the standard objects.
- *
- *
Creates instances of the standard objects and their constructors (Object, String, Number,
- * Date, etc.), setting up 'scope' to act as a global object as in ECMA 15.1.
- *
- *
This method must be called to initialize a scope before scripts can be evaluated in that
- * scope.
- *
- *
This method does not affect the Context it is called upon.
- *
- * @return the initialized scope
- */
- public final ScriptableObject initStandardObjects() {
- return initStandardObjects(null, false);
- }
-
- /**
- * Initialize the standard objects, leaving out those that offer access directly to Java
- * classes. This sets up "scope" to have access to all the standard JavaScript classes, but does
- * not create global objects for any top-level Java packages. In addition, the "Packages,"
- * "JavaAdapter," and "JavaImporter" classes, and the "getClass" function, are not initialized.
- *
- *
The result of this function is a scope that may be safely used in a "sandbox" environment
- * where it is not desirable to give access to Java code from JavaScript.
- *
- *
Creates instances of the standard objects and their constructors (Object, String, Number,
- * Date, etc.), setting up 'scope' to act as a global object as in ECMA 15.1.
- *
- *
This method must be called to initialize a scope before scripts can be evaluated in that
- * scope.
- *
- *
This method does not affect the Context it is called upon.
- *
- * @return the initialized scope
- */
- public final ScriptableObject initSafeStandardObjects() {
- return initSafeStandardObjects(null, false);
- }
-
- /**
- * Initialize the standard objects.
- *
- *
Creates instances of the standard objects and their constructors (Object, String, Number,
- * Date, etc.), setting up 'scope' to act as a global object as in ECMA 15.1.
- *
- *
This method must be called to initialize a scope before scripts can be evaluated in that
- * scope.
- *
- *
This method does not affect the Context it is called upon.
- *
- * @param scope the scope to initialize, or null, in which case a new object will be created to
- * serve as the scope
- * @return the initialized scope. The method returns the value of the scope argument if it is
- * not null or newly allocated scope object which is an instance {@link ScriptableObject}.
- */
- public final Scriptable initStandardObjects(ScriptableObject scope) {
- return initStandardObjects(scope, false);
- }
-
- /**
- * Initialize the standard objects, leaving out those that offer access directly to Java
- * classes. This sets up "scope" to have access to all the standard JavaScript classes, but does
- * not create global objects for any top-level Java packages. In addition, the "Packages,"
- * "JavaAdapter," and "JavaImporter" classes, and the "getClass" function, are not initialized.
- *
- *
The result of this function is a scope that may be safely used in a "sandbox" environment
- * where it is not desirable to give access to Java code from JavaScript.
- *
- *
Creates instances of the standard objects and their constructors (Object, String, Number,
- * Date, etc.), setting up 'scope' to act as a global object as in ECMA 15.1.
- *
- *
This method must be called to initialize a scope before scripts can be evaluated in that
- * scope.
- *
- *
This method does not affect the Context it is called upon.
- *
- * @param scope the scope to initialize, or null, in which case a new object will be created to
- * serve as the scope
- * @return the initialized scope. The method returns the value of the scope argument if it is
- * not null or newly allocated scope object which is an instance {@link ScriptableObject}.
- */
- public final Scriptable initSafeStandardObjects(ScriptableObject scope) {
- return initSafeStandardObjects(scope, false);
- }
-
- /**
- * Initialize the standard objects.
- *
- *
Creates instances of the standard objects and their constructors (Object, String, Number,
- * Date, etc.), setting up 'scope' to act as a global object as in ECMA 15.1.
- *
- *
This method must be called to initialize a scope before scripts can be evaluated in that
- * scope.
- *
- *
This method does not affect the Context it is called upon.
- *
- *
This form of the method also allows for creating "sealed" standard objects. An object that
- * is sealed cannot have properties added, changed, or removed. This is useful to create a
- * "superglobal" that can be shared among several top-level objects. Note that sealing is not
- * allowed in the current ECMA/ISO language specification, but is likely for the next version.
- *
- * @param scope the scope to initialize, or null, in which case a new object will be created to
- * serve as the scope
- * @param sealed whether or not to create sealed standard objects that cannot be modified.
- * @return the initialized scope. The method returns the value of the scope argument if it is
- * not null or newly allocated scope object.
- * @since 1.4R3
- */
- public ScriptableObject initStandardObjects(ScriptableObject scope, boolean sealed) {
- return ScriptRuntime.initStandardObjects(this, scope, sealed);
- }
-
- /**
- * Initialize the standard objects, leaving out those that offer access directly to Java
- * classes. This sets up "scope" to have access to all the standard JavaScript classes, but does
- * not create global objects for any top-level Java packages. In addition, the "Packages,"
- * "JavaAdapter," and "JavaImporter" classes, and the "getClass" function, are not initialized.
- *
- *
The result of this function is a scope that may be safely used in a "sandbox" environment
- * where it is not desirable to give access to Java code from JavaScript.
- *
- *
Creates instances of the standard objects and their constructors (Object, String, Number,
- * Date, etc.), setting up 'scope' to act as a global object as in ECMA 15.1.
- *
- *
This method must be called to initialize a scope before scripts can be evaluated in that
- * scope.
- *
- *
This method does not affect the Context it is called upon.
- *
- *
This form of the method also allows for creating "sealed" standard objects. An object that
- * is sealed cannot have properties added, changed, or removed. This is useful to create a
- * "superglobal" that can be shared among several top-level objects. Note that sealing is not
- * allowed in the current ECMA/ISO language specification, but is likely for the next version.
- *
- * @param scope the scope to initialize, or null, in which case a new object will be created to
- * serve as the scope
- * @param sealed whether or not to create sealed standard objects that cannot be modified.
- * @return the initialized scope. The method returns the value of the scope argument if it is
- * not null or newly allocated scope object.
- * @since 1.7.6
- */
- public ScriptableObject initSafeStandardObjects(ScriptableObject scope, boolean sealed) {
- return ScriptRuntime.initSafeStandardObjects(this, scope, sealed);
- }
-
- /** Get the singleton object that represents the JavaScript Undefined value. */
- public static Object getUndefinedValue() {
- return Undefined.instance;
- }
-
- /**
- * Evaluate a JavaScript source string.
- *
- *
The provided source name and line number are used for error messages and for producing
- * debug information.
- *
- * @param scope the scope to execute in
- * @param source the JavaScript source
- * @param sourceName a string describing the source, such as a filename
- * @param lineno the starting line number
- * @param securityDomain an arbitrary object that specifies security information about the
- * origin or owner of the script. For implementations that don't care about security, this
- * value may be null.
- * @return the result of evaluating the string
- * @see org.mozilla.javascript.SecurityController
- */
- public final Object evaluateString(
- Scriptable scope, String source, String sourceName, int lineno, Object securityDomain) {
- Script script = compileString(source, sourceName, lineno, securityDomain);
- if (script != null) {
- return script.exec(this, scope);
- }
- return null;
- }
-
- /**
- * Evaluate a reader as JavaScript source.
- *
- *
All characters of the reader are consumed.
- *
- * @param scope the scope to execute in
- * @param in the Reader to get JavaScript source from
- * @param sourceName a string describing the source, such as a filename
- * @param lineno the starting line number
- * @param securityDomain an arbitrary object that specifies security information about the
- * origin or owner of the script. For implementations that don't care about security, this
- * value may be null.
- * @return the result of evaluating the source
- * @exception IOException if an IOException was generated by the Reader
- */
- public final Object evaluateReader(
- Scriptable scope, Reader in, String sourceName, int lineno, Object securityDomain)
- throws IOException {
- Script script = compileReader(scope, in, sourceName, lineno, securityDomain);
- if (script != null) {
- return script.exec(this, scope);
- }
- return null;
- }
-
- /**
- * Execute script that may pause execution by capturing a continuation. Caller must be prepared
- * to catch a ContinuationPending exception and resume execution by calling {@link
- * #resumeContinuation(Object, Scriptable, Object)}.
- *
- * @param script The script to execute. Script must have been compiled with interpreted mode
- * (optimization level -1)
- * @param scope The scope to execute the script against
- * @throws ContinuationPending if the script calls a function that results in a call to {@link
- * #captureContinuation()}
- * @since 1.7 Release 2
- */
- public Object executeScriptWithContinuations(Script script, Scriptable scope)
- throws ContinuationPending {
- if (!(script instanceof InterpretedFunction)
- || !((InterpretedFunction) script).isScript()) {
- // Can only be applied to scripts
- throw new IllegalArgumentException(
- "Script argument was not"
- + " a script or was not created by interpreted mode ");
- }
- return callFunctionWithContinuations(
- (InterpretedFunction) script, scope, ScriptRuntime.emptyArgs);
- }
-
- /**
- * Call function that may pause execution by capturing a continuation. Caller must be prepared
- * to catch a ContinuationPending exception and resume execution by calling {@link
- * #resumeContinuation(Object, Scriptable, Object)}.
- *
- * @param function The function to call. The function must have been compiled with interpreted
- * mode (optimization level -1)
- * @param scope The scope to execute the script against
- * @param args The arguments for the function
- * @throws ContinuationPending if the script calls a function that results in a call to {@link
- * #captureContinuation()}
- * @since 1.7 Release 2
- */
- public Object callFunctionWithContinuations(Callable function, Scriptable scope, Object[] args)
- throws ContinuationPending {
- if (!(function instanceof InterpretedFunction)) {
- // Can only be applied to scripts
- throw new IllegalArgumentException(
- "Function argument was not" + " created by interpreted mode ");
- }
- if (ScriptRuntime.hasTopCall(this)) {
- throw new IllegalStateException(
- "Cannot have any pending top "
- + "calls when executing a script with continuations");
- }
- // Annotate so we can check later to ensure no java code in
- // intervening frames
- isContinuationsTopCall = true;
- return ScriptRuntime.doTopCall(function, this, scope, scope, args, isTopLevelStrict);
- }
-
- /**
- * Capture a continuation from the current execution. The execution must have been started via a
- * call to {@link #executeScriptWithContinuations(Script, Scriptable)} or {@link
- * #callFunctionWithContinuations(Callable, Scriptable, Object[])}. This implies that the code
- * calling this method must have been called as a function from the JavaScript script. Also,
- * there cannot be any non-JavaScript code between the JavaScript frames (e.g., a call to
- * eval()). The ContinuationPending exception returned must be thrown.
- *
- * @return A ContinuationPending exception that must be thrown
- * @since 1.7 Release 2
- */
- public ContinuationPending captureContinuation() {
- return new ContinuationPending(Interpreter.captureContinuation(this));
- }
-
- /**
- * Restarts execution of the JavaScript suspended at the call to {@link #captureContinuation()}.
- * Execution of the code will resume with the functionResult as the result of the call that
- * captured the continuation. Execution of the script will either conclude normally and the
- * result returned, another continuation will be captured and thrown, or the script will
- * terminate abnormally and throw an exception.
- *
- * @param continuation The value returned by {@link ContinuationPending#getContinuation()}
- * @param functionResult This value will appear to the code being resumed as the result of the
- * function that captured the continuation
- * @throws ContinuationPending if another continuation is captured before the code terminates
- * @since 1.7 Release 2
- */
- public Object resumeContinuation(Object continuation, Scriptable scope, Object functionResult)
- throws ContinuationPending {
- Object[] args = {functionResult};
- return Interpreter.restartContinuation(
- (org.mozilla.javascript.NativeContinuation) continuation, this, scope, args);
- }
-
- /**
- * Check whether a string is ready to be compiled.
- *
- *
stringIsCompilableUnit is intended to support interactive compilation of JavaScript. If
- * compiling the string would result in an error that might be fixed by appending more source,
- * this method returns false. In every other case, it returns true.
- *
- *
Interactive shells may accumulate source lines, using this method after each new line is
- * appended to check whether the statement being entered is complete.
- *
- * @param source the source buffer to check
- * @return whether the source is ready for compilation
- * @since 1.4 Release 2
- */
- public final boolean stringIsCompilableUnit(String source) {
- boolean errorseen = false;
- CompilerEnvirons compilerEnv = new CompilerEnvirons();
- compilerEnv.initFromContext(this);
- // no source name or source text manager, because we're just
- // going to throw away the result.
- compilerEnv.setGeneratingSource(false);
- Parser p = new Parser(compilerEnv, DefaultErrorReporter.instance);
- try {
- p.parse(source, null, 1);
- } catch (EvaluatorException ee) {
- errorseen = true;
- }
- // Return false only if an error occurred as a result of reading past
- // the end of the file, i.e. if the source could be fixed by
- // appending more source.
- return !(errorseen && p.eof());
- }
-
- /**
- * @deprecated
- * @see #compileReader(Reader in, String sourceName, int lineno, Object securityDomain)
- */
- @Deprecated
- public final Script compileReader(
- Scriptable scope, Reader in, String sourceName, int lineno, Object securityDomain)
- throws IOException {
- return compileReader(in, sourceName, lineno, securityDomain);
- }
-
- /**
- * Compiles the source in the given reader.
- *
- *
Returns a script that may later be executed. Will consume all the source in the reader.
- *
- * @param in the input reader
- * @param sourceName a string describing the source, such as a filename
- * @param lineno the starting line number for reporting errors
- * @param securityDomain an arbitrary object that specifies security information about the
- * origin or owner of the script. For implementations that don't care about security, this
- * value may be null.
- * @return a script that may later be executed
- * @exception IOException if an IOException was generated by the Reader
- * @see org.mozilla.javascript.Script
- */
- public final Script compileReader(
- Reader in, String sourceName, int lineno, Object securityDomain) throws IOException {
- if (lineno < 0) {
- // For compatibility IllegalArgumentException can not be thrown here
- lineno = 0;
- }
-
- return (Script)
- compileImpl(
- null,
- Kit.readReader(in),
- sourceName,
- lineno,
- securityDomain,
- false,
- null,
- null);
- }
-
- /**
- * Compiles the source in the given string.
- *
- *
Returns a script that may later be executed.
- *
- * @param source the source string
- * @param sourceName a string describing the source, such as a filename
- * @param lineno the starting line number for reporting errors. Use 0 if the line number is
- * unknown.
- * @param securityDomain an arbitrary object that specifies security information about the
- * origin or owner of the script. For implementations that don't care about security, this
- * value may be null.
- * @return a script that may later be executed
- * @see org.mozilla.javascript.Script
- */
- public final Script compileString(
- String source, String sourceName, int lineno, Object securityDomain) {
- if (lineno < 0) {
- // For compatibility IllegalArgumentException can not be thrown here
- lineno = 0;
- }
- return compileString(source, null, null, sourceName, lineno, securityDomain);
- }
-
- final Script compileString(
- String source,
- Evaluator compiler,
- ErrorReporter compilationErrorReporter,
- String sourceName,
- int lineno,
- Object securityDomain) {
- try {
- return (Script)
- compileImpl(
- null,
- source,
- sourceName,
- lineno,
- securityDomain,
- false,
- compiler,
- compilationErrorReporter);
- } catch (IOException ioe) {
- // Should not happen when dealing with source as string
- throw new RuntimeException(ioe);
- }
- }
-
- /**
- * Compile a JavaScript function.
- *
- *
The function source must be a function definition as defined by ECMA (e.g., "function f(a)
- * { return a; }").
- *
- * @param scope the scope to compile relative to
- * @param source the function definition source
- * @param sourceName a string describing the source, such as a filename
- * @param lineno the starting line number
- * @param securityDomain an arbitrary object that specifies security information about the
- * origin or owner of the script. For implementations that don't care about security, this
- * value may be null.
- * @return a Function that may later be called
- * @see org.mozilla.javascript.Function
- */
- public final Function compileFunction(
- Scriptable scope, String source, String sourceName, int lineno, Object securityDomain) {
- return compileFunction(scope, source, null, null, sourceName, lineno, securityDomain);
- }
-
- final Function compileFunction(
- Scriptable scope,
- String source,
- Evaluator compiler,
- ErrorReporter compilationErrorReporter,
- String sourceName,
- int lineno,
- Object securityDomain) {
- try {
- return (Function)
- compileImpl(
- scope,
- source,
- sourceName,
- lineno,
- securityDomain,
- true,
- compiler,
- compilationErrorReporter);
- } catch (IOException ioe) {
- // Should never happen because we just made the reader
- // from a String
- throw new RuntimeException(ioe);
- }
- }
-
- /**
- * Decompile the script.
- *
- *
The canonical source of the script is returned.
- *
- * @param script the script to decompile
- * @param indent the number of spaces to indent the result
- * @return a string representing the script source
- */
- public final String decompileScript(Script script, int indent) {
- NativeFunction scriptImpl = (NativeFunction) script;
- return scriptImpl.decompile(indent, 0);
- }
-
- /**
- * Decompile a JavaScript Function.
- *
- *
Decompiles a previously compiled JavaScript function object to canonical source.
- *
- *
Returns function body of '[native code]' if no decompilation information is available.
- *
- * @param fun the JavaScript function to decompile
- * @param indent the number of spaces to indent the result
- * @return a string representing the function source
- */
- public final String decompileFunction(Function fun, int indent) {
- if (fun instanceof BaseFunction) return ((BaseFunction) fun).decompile(indent, 0);
-
- return "function " + fun.getClassName() + "() {\n\t[native code]\n}\n";
- }
-
- /**
- * Decompile the body of a JavaScript Function.
- *
- *
Decompiles the body a previously compiled JavaScript Function object to canonical source,
- * omitting the function header and trailing brace.
- *
- *
Returns '[native code]' if no decompilation information is available.
- *
- * @param fun the JavaScript function to decompile
- * @param indent the number of spaces to indent the result
- * @return a string representing the function body source.
- */
- public final String decompileFunctionBody(Function fun, int indent) {
- if (fun instanceof BaseFunction) {
- BaseFunction bf = (BaseFunction) fun;
- return bf.decompile(indent, Decompiler.ONLY_BODY_FLAG);
- }
- // ALERT: not sure what the right response here is.
- return "[native code]\n";
- }
-
- /**
- * Create a new JavaScript object.
- *
- *
Equivalent to evaluating "new Object()".
- *
- * @param scope the scope to search for the constructor and to evaluate against
- * @return the new object
- */
- public Scriptable newObject(Scriptable scope) {
- NativeObject result = new NativeObject();
- ScriptRuntime.setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Object);
- return result;
- }
-
- /**
- * Create a new JavaScript object by executing the named constructor.
- *
- *
The call newObject(scope, "Foo") is equivalent to evaluating "new Foo()".
- *
- * @param scope the scope to search for the constructor and to evaluate against
- * @param constructorName the name of the constructor to call
- * @return the new object
- */
- public Scriptable newObject(Scriptable scope, String constructorName) {
- return newObject(scope, constructorName, ScriptRuntime.emptyArgs);
- }
-
- /**
- * Creates a new JavaScript object by executing the named constructor.
- *
- *
Searches scope for the named constructor, calls it with the given arguments,
- * and returns the result.
- *
- *
- *
- * is equivalent to evaluating "new Foo('a', 'b')", assuming that the Foo constructor has been
- * defined in scope.
- *
- * @param scope The scope to search for the constructor and to evaluate against
- * @param constructorName the name of the constructor to call
- * @param args the array of arguments for the constructor
- * @return the new object
- */
- public Scriptable newObject(Scriptable scope, String constructorName, Object[] args) {
- return ScriptRuntime.newObject(this, scope, constructorName, args);
- }
-
- /**
- * Create an array with a specified initial length.
- *
- *
- *
- * @param scope the scope to create the object in
- * @param length the initial length (JavaScript arrays may have additional properties added
- * dynamically).
- * @return the new array object
- */
- public Scriptable newArray(Scriptable scope, int length) {
- NativeArray result = new NativeArray(length);
- ScriptRuntime.setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Array);
- return result;
- }
-
- /**
- * Create an array with a set of initial elements.
- *
- * @param scope the scope to create the object in.
- * @param elements the initial elements. Each object in this array must be an acceptable
- * JavaScript type and type of array should be exactly Object[], not SomeObjectSubclass[].
- * @return the new array object.
- */
- public Scriptable newArray(Scriptable scope, Object[] elements) {
- if (elements.getClass().getComponentType() != ScriptRuntime.ObjectClass)
- throw new IllegalArgumentException();
- NativeArray result = new NativeArray(elements);
- ScriptRuntime.setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Array);
- return result;
- }
-
- /**
- * Get the elements of a JavaScript array.
- *
- *
If the object defines a length property convertible to double number, then the number is
- * converted Uint32 value as defined in Ecma 9.6 and Java array of that size is allocated. The
- * array is initialized with the values obtained by calling get() on object for each value of i
- * in [0,length-1]. If there is not a defined value for a property the Undefined value is used
- * to initialize the corresponding element in the array. The Java array is then returned. If the
- * object doesn't define a length property or it is not a number, empty array is returned.
- *
- * @param object the JavaScript array or array-like object
- * @return a Java array of objects
- * @since 1.4 release 2
- */
- public final Object[] getElements(Scriptable object) {
- return ScriptRuntime.getArrayElements(object);
- }
-
- /**
- * Convert the value to a JavaScript boolean value.
- *
- *
See ECMA 9.2.
- *
- * @param value a JavaScript value
- * @return the corresponding boolean value converted using the ECMA rules
- */
- public static boolean toBoolean(Object value) {
- return ScriptRuntime.toBoolean(value);
- }
-
- /**
- * Convert the value to a JavaScript Number value.
- *
- *
Returns a Java double for the JavaScript Number.
- *
- *
See ECMA 9.3.
- *
- * @param value a JavaScript value
- * @return the corresponding double value converted using the ECMA rules
- */
- public static double toNumber(Object value) {
- return ScriptRuntime.toNumber(value);
- }
-
- /**
- * Convert the value to a JavaScript String value.
- *
- *
See ECMA 9.8.
- *
- *
- *
- * @param value a JavaScript value
- * @return the corresponding String value converted using the ECMA rules
- */
- public static String toString(Object value) {
- return ScriptRuntime.toString(value);
- }
-
- /**
- * Convert the value to an JavaScript object value.
- *
- *
Note that a scope must be provided to look up the constructors for Number, Boolean, and
- * String.
- *
- *
See ECMA 9.9.
- *
- *
Additionally, arbitrary Java objects and classes will be wrapped in a Scriptable object
- * with its Java fields and methods reflected as JavaScript properties of the object.
- *
- * @param value any Java object
- * @param scope global scope containing constructors for Number, Boolean, and String
- * @return new JavaScript object
- */
- public static Scriptable toObject(Object value, Scriptable scope) {
- return ScriptRuntime.toObject(scope, value);
- }
-
- /**
- * @deprecated
- * @see #toObject(Object, Scriptable)
- */
- @Deprecated
- public static Scriptable toObject(Object value, Scriptable scope, Class> staticType) {
- return ScriptRuntime.toObject(scope, value);
- }
-
- /**
- * Convenient method to convert java value to its closest representation in JavaScript.
- *
- *
If value is an instance of String, Number, Boolean, Function or Scriptable, it is returned
- * as it and will be treated as the corresponding JavaScript type of string, number, boolean,
- * function and object.
- *
- *
Note that for Number instances during any arithmetic operation in JavaScript the engine
- * will always use the result of Number.doubleValue() resulting in a precision loss
- * if the number can not fit into double.
- *
- *
If value is an instance of Character, it will be converted to string of length 1 and its
- * JavaScript type will be string.
- *
- *
The rest of values will be wrapped as LiveConnect objects by calling {@link
- * WrapFactory#wrap(Context cx, Scriptable scope, Object obj, Class staticType)} as in:
- *
- *
- *
- * @param value any Java object
- * @param scope top scope object
- * @return value suitable to pass to any API that takes JavaScript values.
- */
- public static Object javaToJS(Object value, Scriptable scope) {
- return javaToJS(value, scope, null);
- }
-
- /**
- * Convenient method to convert java value to its closest representation in JavaScript.
- *
- *
If value is an instance of String, Number, Boolean, Function or Scriptable, it is returned
- * as it and will be treated as the corresponding JavaScript type of string, number, boolean,
- * function and object.
- *
- *
Note that for Number instances during any arithmetic operation in JavaScript the engine
- * will always use the result of Number.doubleValue() resulting in a precision loss
- * if the number can not fit into double.
- *
- *
If value is an instance of Character, it will be converted to string of length 1 and its
- * JavaScript type will be string.
- *
- *
The rest of values will be wrapped as LiveConnect objects by calling {@link
- * WrapFactory#wrap(Context cx, Scriptable scope, Object obj, Class staticType)} as in:
- *
- *
- *
- * @param value any Java object
- * @param scope top scope object
- * @param cx context to use for wrapping LiveConnect objects
- * @return value suitable to pass to any API that takes JavaScript values.
- */
- public static Object javaToJS(Object value, Scriptable scope, Context cx) {
- if (value instanceof String
- || value instanceof Number
- || value instanceof Boolean
- || value instanceof Scriptable) {
- return value;
- } else if (value instanceof Character) {
- return String.valueOf(((Character) value).charValue());
- } else {
- if (cx == null) {
- cx = Context.getContext();
- }
- return cx.getWrapFactory().wrap(cx, scope, value, null);
- }
- }
-
- /**
- * Convert a JavaScript value into the desired type. Uses the semantics defined with
- * LiveConnect3 and throws an Illegal argument exception if the conversion cannot be performed.
- *
- * @param value the JavaScript value to convert
- * @param desiredType the Java type to convert to. Primitive Java types are represented using
- * the TYPE fields in the corresponding wrapper class in java.lang.
- * @return the converted value
- * @throws EvaluatorException if the conversion cannot be performed
- */
- public static Object jsToJava(Object value, Class> desiredType) throws EvaluatorException {
- return NativeJavaObject.coerceTypeImpl(desiredType, value);
- }
-
- /**
- * @deprecated
- * @see #jsToJava(Object, Class)
- * @throws IllegalArgumentException if the conversion cannot be performed. Note that {@link
- * #jsToJava(Object, Class)} throws {@link EvaluatorException} instead.
- */
- @Deprecated
- public static Object toType(Object value, Class> desiredType)
- throws IllegalArgumentException {
- try {
- return jsToJava(value, desiredType);
- } catch (EvaluatorException ex) {
- throw new IllegalArgumentException(ex.getMessage(), ex);
- }
- }
-
- /**
- * Returns the javaToJSONConverter for this Context.
- *
- *
The converter is used by the JSON.stringify method for Java objects other than instances
- * of {@link java.util.Map Map}, {@link java.util.Collection Collection}, or {@link
- * java.lang.Object Object[]}.
- *
- *
The default converter if unset will convert Java Objects to their toString() value.
- *
- * @return javaToJSONConverter for this Context
- */
- public UnaryOperator