diff --git a/build.gradle b/build.gradle index e93e097c..933573b1 100644 --- a/build.gradle +++ b/build.gradle @@ -19,6 +19,10 @@ repositories { maven { url 'https://repo.opencollab.dev/maven-snapshots/' } + maven { + name "innercoreSnapshots" + url "https://maven.inner-core.org/snapshots" + } } configurations { @@ -39,8 +43,7 @@ dependencies { // https://central.sonatype.com/artifact/com.google.code.gson/gson/versions implementation group: 'com.google.code.gson', name: 'gson', version: '2.7' - // https://central.sonatype.com/artifact/org.mozilla/rhino/versions - // implementation group: 'org.mozilla', name: 'rhino', version: '1.7.14' + implementation group: 'org.mozilla.innercore', name: 'rhino', version: '1.7.15.1' // https://central.sonatype.com/artifact/org.json/json/versions implementation group: 'org.json', name: 'json', version: '20231013' diff --git a/codegen/src/main/java/org/innercore/codegen/JavaBindingGenerator.java b/codegen/src/main/java/org/innercore/codegen/JavaBindingGenerator.java index bf62b634..5bbb4387 100644 --- a/codegen/src/main/java/org/innercore/codegen/JavaBindingGenerator.java +++ b/codegen/src/main/java/org/innercore/codegen/JavaBindingGenerator.java @@ -989,7 +989,7 @@ private String convertScriptableToJava(VarDecl arg, String expr, BindingOverload return expr + " instanceof Wrapper ? (long) ((Wrapper) " + expr + ").unwrap() : ((Number) " + expr + ").longValue()"; } else if (arg.type.equals(BASE_TYPE_STRING)) { - return expr + " instanceof String ? " + expr + ".toString() : (" + expr + " != null && " + expr + return expr + " instanceof CharSequence ? " + expr + ".toString() : (" + expr + " != null && " + expr + " != Undefined.instance ? ScriptRuntime.toString(" + expr + ") : " + nullCase + ")"; } else if (arg.type.equals(BASE_TYPE_BOOL)) { return expr + " instanceof Boolean ? (Boolean) " + expr + " : ScriptRuntime.toBoolean(" + expr + ")"; diff --git a/src/main/java/com/reider745/InnerCoreServer.java b/src/main/java/com/reider745/InnerCoreServer.java index 3876c0f8..6186ff6a 100644 --- a/src/main/java/com/reider745/InnerCoreServer.java +++ b/src/main/java/com/reider745/InnerCoreServer.java @@ -10,6 +10,7 @@ import com.reider745.commands.CommandsHelper; import com.reider745.event.EventListener; import com.reider745.event.InnerCorePlugin; +import com.reider745.hooks.ZoteClassLoaderPool; import com.reider745.hooks.item.RuntimeItemsHooks; import com.reider745.hooks.SnowfallEverywhere; import com.reider745.item.CustomItem; @@ -35,6 +36,7 @@ import com.zhekasmirnov.mcpe161.InnerCore; import org.json.JSONObject; import org.mozilla.javascript.Context; +import org.mozilla.javascript.ScriptRuntime; import java.io.*; import java.net.HttpURLConnection; @@ -200,6 +202,11 @@ static File unpackResources(String targetPath, String outputPath) { public void preload(Server server) throws Exception { final long startupMillis = System.currentTimeMillis(); + org.mozilla.javascript.Logger.DEBUG = Logger::debug; + Class.forName("org.mozilla.javascript.Context"); + ScriptRuntime.GET_MESSAGES = () -> FileTools.getAssetInputStream("rhino/Messages.properties"); + Context.BUILD_LOADER = ZoteClassLoaderPool::new; + Thread.currentThread().setPriority(Thread.MAX_PRIORITY); server.getLogger().info("Initiating target directory '" + server.getDataPath() + "'"); // BiomesHooks.init(); diff --git a/src/main/java/com/zhekasmirnov/innercore/mod/executable/Executable.java b/src/main/java/com/zhekasmirnov/innercore/mod/executable/Executable.java index 117641ae..276aa117 100644 --- a/src/main/java/com/zhekasmirnov/innercore/mod/executable/Executable.java +++ b/src/main/java/com/zhekasmirnov/innercore/mod/executable/Executable.java @@ -2,6 +2,7 @@ import com.reider745.InnerCoreServer; import com.reider745.hooks.ZoteClassLoaderPool; +import com.zhekasmirnov.horizon.runtime.logger.Logger; import com.zhekasmirnov.innercore.api.NativeJavaScript; import com.zhekasmirnov.innercore.api.log.ICLog; import com.zhekasmirnov.innercore.api.mod.API; diff --git a/src/main/java/org/mozilla/classfile/ByteCode.java b/src/main/java/org/mozilla/classfile/ByteCode.java deleted file mode 100644 index 5d62edcb..00000000 --- a/src/main/java/org/mozilla/classfile/ByteCode.java +++ /dev/null @@ -1,255 +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; - -/** - * This class provides opcode values expected by the JVM in Java class files. - * - * It also provides tables for internal use by the ClassFileWriter. - * - * @author Roger Lawrence - */ -public class ByteCode { - - /** - * The byte opcodes defined by the Java Virtual Machine. - */ - public static final int - NOP = 0x00, - ACONST_NULL = 0x01, - ICONST_M1 = 0x02, - ICONST_0 = 0x03, - ICONST_1 = 0x04, - ICONST_2 = 0x05, - ICONST_3 = 0x06, - ICONST_4 = 0x07, - ICONST_5 = 0x08, - LCONST_0 = 0x09, - LCONST_1 = 0x0A, - FCONST_0 = 0x0B, - FCONST_1 = 0x0C, - FCONST_2 = 0x0D, - DCONST_0 = 0x0E, - DCONST_1 = 0x0F, - BIPUSH = 0x10, - SIPUSH = 0x11, - LDC = 0x12, - LDC_W = 0x13, - LDC2_W = 0x14, - ILOAD = 0x15, - LLOAD = 0x16, - FLOAD = 0x17, - DLOAD = 0x18, - ALOAD = 0x19, - ILOAD_0 = 0x1A, - ILOAD_1 = 0x1B, - ILOAD_2 = 0x1C, - ILOAD_3 = 0x1D, - LLOAD_0 = 0x1E, - LLOAD_1 = 0x1F, - LLOAD_2 = 0x20, - LLOAD_3 = 0x21, - FLOAD_0 = 0x22, - FLOAD_1 = 0x23, - FLOAD_2 = 0x24, - FLOAD_3 = 0x25, - DLOAD_0 = 0x26, - DLOAD_1 = 0x27, - DLOAD_2 = 0x28, - DLOAD_3 = 0x29, - ALOAD_0 = 0x2A, - ALOAD_1 = 0x2B, - ALOAD_2 = 0x2C, - ALOAD_3 = 0x2D, - IALOAD = 0x2E, - LALOAD = 0x2F, - FALOAD = 0x30, - DALOAD = 0x31, - AALOAD = 0x32, - BALOAD = 0x33, - CALOAD = 0x34, - SALOAD = 0x35, - ISTORE = 0x36, - LSTORE = 0x37, - FSTORE = 0x38, - DSTORE = 0x39, - ASTORE = 0x3A, - ISTORE_0 = 0x3B, - ISTORE_1 = 0x3C, - ISTORE_2 = 0x3D, - ISTORE_3 = 0x3E, - LSTORE_0 = 0x3F, - LSTORE_1 = 0x40, - LSTORE_2 = 0x41, - LSTORE_3 = 0x42, - FSTORE_0 = 0x43, - FSTORE_1 = 0x44, - FSTORE_2 = 0x45, - FSTORE_3 = 0x46, - DSTORE_0 = 0x47, - DSTORE_1 = 0x48, - DSTORE_2 = 0x49, - DSTORE_3 = 0x4A, - ASTORE_0 = 0x4B, - ASTORE_1 = 0x4C, - ASTORE_2 = 0x4D, - ASTORE_3 = 0x4E, - IASTORE = 0x4F, - LASTORE = 0x50, - FASTORE = 0x51, - DASTORE = 0x52, - AASTORE = 0x53, - BASTORE = 0x54, - CASTORE = 0x55, - SASTORE = 0x56, - POP = 0x57, - POP2 = 0x58, - DUP = 0x59, - DUP_X1 = 0x5A, - DUP_X2 = 0x5B, - DUP2 = 0x5C, - DUP2_X1 = 0x5D, - DUP2_X2 = 0x5E, - SWAP = 0x5F, - IADD = 0x60, - LADD = 0x61, - FADD = 0x62, - DADD = 0x63, - ISUB = 0x64, - LSUB = 0x65, - FSUB = 0x66, - DSUB = 0x67, - IMUL = 0x68, - LMUL = 0x69, - FMUL = 0x6A, - DMUL = 0x6B, - IDIV = 0x6C, - LDIV = 0x6D, - FDIV = 0x6E, - DDIV = 0x6F, - IREM = 0x70, - LREM = 0x71, - FREM = 0x72, - DREM = 0x73, - INEG = 0x74, - LNEG = 0x75, - FNEG = 0x76, - DNEG = 0x77, - ISHL = 0x78, - LSHL = 0x79, - ISHR = 0x7A, - LSHR = 0x7B, - IUSHR = 0x7C, - LUSHR = 0x7D, - IAND = 0x7E, - LAND = 0x7F, - IOR = 0x80, - LOR = 0x81, - IXOR = 0x82, - LXOR = 0x83, - IINC = 0x84, - I2L = 0x85, - I2F = 0x86, - I2D = 0x87, - L2I = 0x88, - L2F = 0x89, - L2D = 0x8A, - F2I = 0x8B, - F2L = 0x8C, - F2D = 0x8D, - D2I = 0x8E, - D2L = 0x8F, - D2F = 0x90, - I2B = 0x91, - I2C = 0x92, - I2S = 0x93, - LCMP = 0x94, - FCMPL = 0x95, - FCMPG = 0x96, - DCMPL = 0x97, - DCMPG = 0x98, - IFEQ = 0x99, - IFNE = 0x9A, - IFLT = 0x9B, - IFGE = 0x9C, - IFGT = 0x9D, - IFLE = 0x9E, - IF_ICMPEQ = 0x9F, - IF_ICMPNE = 0xA0, - IF_ICMPLT = 0xA1, - IF_ICMPGE = 0xA2, - IF_ICMPGT = 0xA3, - IF_ICMPLE = 0xA4, - IF_ACMPEQ = 0xA5, - IF_ACMPNE = 0xA6, - GOTO = 0xA7, - JSR = 0xA8, - RET = 0xA9, - TABLESWITCH = 0xAA, - LOOKUPSWITCH = 0xAB, - IRETURN = 0xAC, - LRETURN = 0xAD, - FRETURN = 0xAE, - DRETURN = 0xAF, - ARETURN = 0xB0, - RETURN = 0xB1, - GETSTATIC = 0xB2, - PUTSTATIC = 0xB3, - GETFIELD = 0xB4, - PUTFIELD = 0xB5, - INVOKEVIRTUAL = 0xB6, - INVOKESPECIAL = 0xB7, - INVOKESTATIC = 0xB8, - INVOKEINTERFACE = 0xB9, - INVOKEDYNAMIC = 0xBA, - NEW = 0xBB, - NEWARRAY = 0xBC, - ANEWARRAY = 0xBD, - ARRAYLENGTH = 0xBE, - ATHROW = 0xBF, - CHECKCAST = 0xC0, - INSTANCEOF = 0xC1, - MONITORENTER = 0xC2, - MONITOREXIT = 0xC3, - WIDE = 0xC4, - MULTIANEWARRAY = 0xC5, - IFNULL = 0xC6, - IFNONNULL = 0xC7, - GOTO_W = 0xC8, - JSR_W = 0xC9, - BREAKPOINT = 0xCA, - - IMPDEP1 = 0xFE, - IMPDEP2 = 0xFF; - - /** - * Types for the NEWARRAY opcode. - */ - public static final byte - T_BOOLEAN = 4, - T_CHAR = 5, - T_FLOAT = 6, - T_DOUBLE = 7, - T_BYTE = 8, - T_SHORT = 9, - T_INT = 10, - T_LONG = 11; - - /* - * Types for MethodHandle - */ - public static final byte - MH_GETFIELD = 1, - MH_GETSTATIC = 2, - MH_PUTFIELD = 3, - MH_PUTSTATIC = 4, - MH_INVOKEVIRTUAL = 5, - MH_INVOKESTATIC = 6, - MH_INVOKESPECIAL = 7, - MH_NEWINVOKESPECIAL = 8, - MH_INVOKEINTERFACE = 9; -} \ No newline at end of file diff --git a/src/main/java/org/mozilla/classfile/ClassFileField.java b/src/main/java/org/mozilla/classfile/ClassFileField.java deleted file mode 100644 index 1c518ef8..00000000 --- a/src/main/java/org/mozilla/classfile/ClassFileField.java +++ /dev/null @@ -1,63 +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 ClassFileField -{ - ClassFileField(short nameIndex, short typeIndex, short flags) - { - itsNameIndex = nameIndex; - itsTypeIndex = typeIndex; - itsFlags = flags; - itsHasAttributes = false; - } - - void setAttributes(short attr1, short attr2, short attr3, int index) - { - itsHasAttributes = true; - itsAttr1 = attr1; - itsAttr2 = attr2; - itsAttr3 = attr3; - itsIndex = index; - } - - int write(byte[] data, int offset) - { - offset = ClassFileWriter.putInt16(itsFlags, data, offset); - offset = ClassFileWriter.putInt16(itsNameIndex, data, offset); - offset = ClassFileWriter.putInt16(itsTypeIndex, data, offset); - if (!itsHasAttributes) { - // write 0 attributes - offset = ClassFileWriter.putInt16(0, data, offset); - } else { - offset = ClassFileWriter.putInt16(1, data, offset); - offset = ClassFileWriter.putInt16(itsAttr1, data, offset); - offset = ClassFileWriter.putInt16(itsAttr2, data, offset); - offset = ClassFileWriter.putInt16(itsAttr3, data, offset); - offset = ClassFileWriter.putInt16(itsIndex, data, offset); - } - return offset; - } - - int getWriteSize() - { - int size = 2 * 3; - if (!itsHasAttributes) { - size += 2; - } else { - size += 2 + 2 * 4; - } - return size; - } - - private short itsNameIndex; - private short itsTypeIndex; - private short itsFlags; - private boolean itsHasAttributes; - private short itsAttr1, itsAttr2, itsAttr3; - private int itsIndex; -} \ No newline at end of file diff --git a/src/main/java/org/mozilla/classfile/ClassFileMethod.java b/src/main/java/org/mozilla/classfile/ClassFileMethod.java deleted file mode 100644 index 675a7a52..00000000 --- a/src/main/java/org/mozilla/classfile/ClassFileMethod.java +++ /dev/null @@ -1,66 +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 ClassFileMethod -{ - ClassFileMethod(String name, short nameIndex, String type, short typeIndex, - short flags) - { - itsName = name; - itsNameIndex = nameIndex; - itsType = type; - itsTypeIndex = typeIndex; - itsFlags = flags; - } - - void setCodeAttribute(byte codeAttribute[]) - { - itsCodeAttribute = codeAttribute; - } - - int write(byte[] data, int offset) - { - offset = ClassFileWriter.putInt16(itsFlags, data, offset); - offset = ClassFileWriter.putInt16(itsNameIndex, data, offset); - offset = ClassFileWriter.putInt16(itsTypeIndex, data, offset); - // Code attribute only - offset = ClassFileWriter.putInt16(1, data, offset); - System.arraycopy(itsCodeAttribute, 0, data, offset, - itsCodeAttribute.length); - offset += itsCodeAttribute.length; - return offset; - } - - int getWriteSize() - { - return 2 * 4 + itsCodeAttribute.length; - } - - String getName() - { - return itsName; - } - - String getType() - { - return itsType; - } - - short getFlags() - { - return itsFlags; - } - - private String itsName; - private String itsType; - private short itsNameIndex; - private short itsTypeIndex; - private short itsFlags; - private byte[] itsCodeAttribute; - -} diff --git a/src/main/java/org/mozilla/classfile/ClassFileWriter.java b/src/main/java/org/mozilla/classfile/ClassFileWriter.java deleted file mode 100644 index 092fed9f..00000000 --- a/src/main/java/org/mozilla/classfile/ClassFileWriter.java +++ /dev/null @@ -1,4520 +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 java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Arrays; -import org.mozilla.javascript.Kit; -import org.mozilla.javascript.ObjArray; -import org.mozilla.javascript.UintMap; - -/** - * ClassFileWriter - * - *

A ClassFileWriter is used to write a Java class file. Methods are provided to create fields - * and methods, and within methods to write Java bytecodes. - * - * @author Roger Lawrence - */ -public class ClassFileWriter { - - /** - * Thrown for cases where the error in generating the class file is due to a program size - * constraints rather than a likely bug in the compiler. - */ - public static class ClassFileFormatException extends RuntimeException { - - private static final long serialVersionUID = 1263998431033790599L; - - ClassFileFormatException(String message) { - super(message); - } - } - - /** - * Construct a ClassFileWriter for a class. - * - * @param className the name of the class to write, including full package qualification. - * @param superClassName the name of the superclass of the class to write, including full - * package qualification. - * @param sourceFileName the name of the source file to use for producing debug information, or - * null if debug information is not desired - */ - public ClassFileWriter(String className, String superClassName, String sourceFileName) { - generatedClassName = className; - itsConstantPool = new ConstantPool(this); - itsThisClassIndex = itsConstantPool.addClass(className); - itsSuperClassIndex = itsConstantPool.addClass(superClassName); - if (sourceFileName != null) - itsSourceFileNameIndex = itsConstantPool.addUtf8(sourceFileName); - // All "new" implementations are supposed to output ACC_SUPER as a - // class flag. This is specified in the first JVM spec, so it should - // be old enough that it's okay to always set it. - itsFlags = ACC_PUBLIC | ACC_SUPER; - } - - public final String getClassName() { - return generatedClassName; - } - - /** - * Add an interface implemented by this class. - * - *

This method may be called multiple times for classes that implement multiple interfaces. - * - * @param interfaceName a name of an interface implemented by the class being written, including - * full package qualification. - */ - public void addInterface(String interfaceName) { - short interfaceIndex = itsConstantPool.addClass(interfaceName); - itsInterfaces.add(Short.valueOf(interfaceIndex)); - } - - public static final short ACC_PUBLIC = 0x0001, - ACC_PRIVATE = 0x0002, - ACC_PROTECTED = 0x0004, - ACC_STATIC = 0x0008, - ACC_FINAL = 0x0010, - ACC_SUPER = 0x0020, - ACC_SYNCHRONIZED = 0x0020, - ACC_VOLATILE = 0x0040, - ACC_TRANSIENT = 0x0080, - ACC_NATIVE = 0x0100, - ACC_ABSTRACT = 0x0400; - - /** - * Set the class's flags. - * - *

Flags must be a set of the following flags, bitwise or'd together: ACC_PUBLIC ACC_PRIVATE - * ACC_PROTECTED ACC_FINAL ACC_ABSTRACT TODO: check that this is the appropriate set - * - * @param flags the set of class flags to set - */ - public void setFlags(short flags) { - itsFlags = flags; - } - - static String getSlashedForm(String name) { - return name.replace('.', '/'); - } - - /** - * Convert Java class name in dot notation into "Lname-with-dots-replaced-by-slashes;" form - * suitable for use as JVM type signatures. - */ - public static String classNameToSignature(String name) { - int nameLength = name.length(); - int colonPos = 1 + nameLength; - char[] buf = new char[colonPos + 1]; - buf[0] = 'L'; - buf[colonPos] = ';'; - name.getChars(0, nameLength, buf, 1); - for (int i = 1; i != colonPos; ++i) { - if (buf[i] == '.') { - buf[i] = '/'; - } - } - return new String(buf, 0, colonPos + 1); - } - - /** - * Add a field to the class. - * - * @param fieldName the name of the field - * @param type the type of the field using ... - * @param flags the attributes of the field, such as ACC_PUBLIC, etc. bitwise or'd together - */ - public void addField(String fieldName, String type, short flags) { - short fieldNameIndex = itsConstantPool.addUtf8(fieldName); - short typeIndex = itsConstantPool.addUtf8(type); - itsFields.add(new ClassFileField(fieldNameIndex, typeIndex, flags)); - } - - /** - * Add a field to the class. - * - * @param fieldName the name of the field - * @param type the type of the field using ... - * @param flags the attributes of the field, such as ACC_PUBLIC, etc. bitwise or'd together - * @param value an initial integral value - */ - public void addField(String fieldName, String type, short flags, int value) { - short fieldNameIndex = itsConstantPool.addUtf8(fieldName); - short typeIndex = itsConstantPool.addUtf8(type); - ClassFileField field = new ClassFileField(fieldNameIndex, typeIndex, flags); - field.setAttributes( - itsConstantPool.addUtf8("ConstantValue"), - (short) 0, - (short) 0, - itsConstantPool.addConstant(value)); - itsFields.add(field); - } - - /** - * Add a field to the class. - * - * @param fieldName the name of the field - * @param type the type of the field using ... - * @param flags the attributes of the field, such as ACC_PUBLIC, etc. bitwise or'd together - * @param value an initial long value - */ - public void addField(String fieldName, String type, short flags, long value) { - short fieldNameIndex = itsConstantPool.addUtf8(fieldName); - short typeIndex = itsConstantPool.addUtf8(type); - ClassFileField field = new ClassFileField(fieldNameIndex, typeIndex, flags); - field.setAttributes( - itsConstantPool.addUtf8("ConstantValue"), - (short) 0, - (short) 2, - itsConstantPool.addConstant(value)); - itsFields.add(field); - } - - /** - * Add a field to the class. - * - * @param fieldName the name of the field - * @param type the type of the field using ... - * @param flags the attributes of the field, such as ACC_PUBLIC, etc. bitwise or'd together - * @param value an initial double value - */ - public void addField(String fieldName, String type, short flags, double value) { - short fieldNameIndex = itsConstantPool.addUtf8(fieldName); - short typeIndex = itsConstantPool.addUtf8(type); - ClassFileField field = new ClassFileField(fieldNameIndex, typeIndex, flags); - field.setAttributes( - itsConstantPool.addUtf8("ConstantValue"), - (short) 0, - (short) 2, - itsConstantPool.addConstant(value)); - itsFields.add(field); - } - - /** - * Add Information about java variable to use when generating the local variable table. - * - * @param name variable name. - * @param type variable type as bytecode descriptor string. - * @param startPC the starting bytecode PC where this variable is live, or -1 if it does not - * have a Java register. - * @param register the Java register number of variable or -1 if it does not have a Java - * register. - */ - public void addVariableDescriptor(String name, String type, int startPC, int register) { - int nameIndex = itsConstantPool.addUtf8(name); - int descriptorIndex = itsConstantPool.addUtf8(type); - int[] chunk = {nameIndex, descriptorIndex, startPC, register}; - if (itsVarDescriptors == null) { - itsVarDescriptors = new ObjArray(); - } - itsVarDescriptors.add(chunk); - } - - /** - * Add a method and begin adding code. - * - *

This method must be called before other methods for adding code, exception tables, etc. - * can be invoked. - * - * @param methodName the name of the method - * @param type a string representing the type - * @param flags the attributes of the field, such as ACC_PUBLIC, etc. bitwise or'd together - */ - public void startMethod(String methodName, String type, short flags) { - short methodNameIndex = itsConstantPool.addUtf8(methodName); - short typeIndex = itsConstantPool.addUtf8(type); - itsCurrentMethod = new ClassFileMethod(methodName, methodNameIndex, type, typeIndex, flags); - itsJumpFroms = new UintMap(); - itsMethods.add(itsCurrentMethod); - addSuperBlockStart(0); - } - - /** - * Complete generation of the method. - * - *

After this method is called, no more code can be added to the method begun with - * startMethod. - * - * @param maxLocals the maximum number of local variable slots (a.k.a. Java registers) used by - * the method - */ - public void stopMethod(short maxLocals) { - if (itsCurrentMethod == null) throw new IllegalStateException("No method to stop"); - - fixLabelGotos(); - - itsMaxLocals = maxLocals; - - StackMapTable stackMap = null; - if (GenerateStackMap) { - finalizeSuperBlockStarts(); - stackMap = new StackMapTable(); - stackMap.generate(); - } - - int lineNumberTableLength = 0; - if (itsLineNumberTable != null) { - // 6 bytes for the attribute header - // 2 bytes for the line number count - // 4 bytes for each entry - lineNumberTableLength = 6 + 2 + (itsLineNumberTableTop * 4); - } - - int variableTableLength = 0; - if (itsVarDescriptors != null) { - // 6 bytes for the attribute header - // 2 bytes for the variable count - // 10 bytes for each entry - variableTableLength = 6 + 2 + (itsVarDescriptors.size() * 10); - } - - int stackMapTableLength = 0; - if (stackMap != null) { - int stackMapWriteSize = stackMap.computeWriteSize(); - if (stackMapWriteSize > 0) { - stackMapTableLength = 6 + stackMapWriteSize; - } - } - - int attrLength = - 2 - + // attribute_name_index - 4 - + // attribute_length - 2 - + // max_stack - 2 - + // max_locals - 4 - + // code_length - itsCodeBufferTop - + 2 - + // exception_table_length - (itsExceptionTableTop * 8) - + 2 - + // attributes_count - lineNumberTableLength - + variableTableLength - + stackMapTableLength; - - if (attrLength > 65536) { - // See http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html, - // section 4.10, "The amount of code per non-native, non-abstract - // method is limited to 65536 bytes... - throw new ClassFileFormatException("generated bytecode for method exceeds 64K limit."); - } - byte[] codeAttribute = new byte[attrLength]; - int index = 0; - int codeAttrIndex = itsConstantPool.addUtf8("Code"); - index = putInt16(codeAttrIndex, codeAttribute, index); - attrLength -= 6; // discount the attribute header - index = putInt32(attrLength, codeAttribute, index); - index = putInt16(itsMaxStack, codeAttribute, index); - index = putInt16(itsMaxLocals, codeAttribute, index); - index = putInt32(itsCodeBufferTop, codeAttribute, index); - System.arraycopy(itsCodeBuffer, 0, codeAttribute, index, itsCodeBufferTop); - index += itsCodeBufferTop; - - if (itsExceptionTableTop > 0) { - index = putInt16(itsExceptionTableTop, codeAttribute, index); - for (int i = 0; i < itsExceptionTableTop; i++) { - ExceptionTableEntry ete = itsExceptionTable[i]; - int startPC = getLabelPC(ete.itsStartLabel); - int endPC = getLabelPC(ete.itsEndLabel); - int handlerPC = getLabelPC(ete.itsHandlerLabel); - short catchType = ete.itsCatchType; - if (startPC == -1) throw new IllegalStateException("start label not defined"); - if (endPC == -1) throw new IllegalStateException("end label not defined"); - if (handlerPC == -1) throw new IllegalStateException("handler label not defined"); - - // no need to cast to short here, the putInt16 uses only - // the short part of the int - index = putInt16(startPC, codeAttribute, index); - index = putInt16(endPC, codeAttribute, index); - index = putInt16(handlerPC, codeAttribute, index); - index = putInt16(catchType, codeAttribute, index); - } - } else { - // write 0 as exception table length - index = putInt16(0, codeAttribute, index); - } - - int attributeCount = 0; - if (itsLineNumberTable != null) attributeCount++; - if (itsVarDescriptors != null) attributeCount++; - if (stackMapTableLength > 0) { - attributeCount++; - } - index = putInt16(attributeCount, codeAttribute, index); - - if (itsLineNumberTable != null) { - int lineNumberTableAttrIndex = itsConstantPool.addUtf8("LineNumberTable"); - index = putInt16(lineNumberTableAttrIndex, codeAttribute, index); - int tableAttrLength = 2 + (itsLineNumberTableTop * 4); - index = putInt32(tableAttrLength, codeAttribute, index); - index = putInt16(itsLineNumberTableTop, codeAttribute, index); - for (int i = 0; i < itsLineNumberTableTop; i++) { - index = putInt32(itsLineNumberTable[i], codeAttribute, index); - } - } - - if (itsVarDescriptors != null) { - int variableTableAttrIndex = itsConstantPool.addUtf8("LocalVariableTable"); - index = putInt16(variableTableAttrIndex, codeAttribute, index); - int varCount = itsVarDescriptors.size(); - int tableAttrLength = 2 + (varCount * 10); - index = putInt32(tableAttrLength, codeAttribute, index); - index = putInt16(varCount, codeAttribute, index); - for (int i = 0; i < varCount; i++) { - int[] chunk = (int[]) itsVarDescriptors.get(i); - int nameIndex = chunk[0]; - int descriptorIndex = chunk[1]; - int startPC = chunk[2]; - int register = chunk[3]; - int length = itsCodeBufferTop - startPC; - - index = putInt16(startPC, codeAttribute, index); - index = putInt16(length, codeAttribute, index); - index = putInt16(nameIndex, codeAttribute, index); - index = putInt16(descriptorIndex, codeAttribute, index); - index = putInt16(register, codeAttribute, index); - } - } - - if (stackMapTableLength > 0) { - int stackMapTableAttrIndex = itsConstantPool.addUtf8("StackMapTable"); - index = putInt16(stackMapTableAttrIndex, codeAttribute, index); - index = stackMap.write(codeAttribute, index); - } - - itsCurrentMethod.setCodeAttribute(codeAttribute); - - itsExceptionTable = null; - itsExceptionTableTop = 0; - itsLineNumberTableTop = 0; - itsCodeBufferTop = 0; - itsCurrentMethod = null; - itsMaxStack = 0; - itsStackTop = 0; - itsLabelTableTop = 0; - itsFixupTableTop = 0; - itsVarDescriptors = null; - itsSuperBlockStarts = null; - itsSuperBlockStartsTop = 0; - itsJumpFroms = null; - } - - /** - * Add the single-byte opcode to the current method. - * - * @param theOpCode the opcode of the bytecode - */ - public void add(int theOpCode) { - if (opcodeCount(theOpCode) != 0) throw new IllegalArgumentException("Unexpected operands"); - int newStack = itsStackTop + stackChange(theOpCode); - if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack); - if (DEBUGCODE) System.out.println("Add " + bytecodeStr(theOpCode)); - addToCodeBuffer(theOpCode); - itsStackTop = (short) newStack; - if (newStack > itsMaxStack) itsMaxStack = (short) newStack; - if (DEBUGSTACK) { - System.out.println("After " + bytecodeStr(theOpCode) + " stack = " + itsStackTop); - } - if (theOpCode == ByteCode.ATHROW) { - addSuperBlockStart(itsCodeBufferTop); - } - } - - /** - * Add a single-operand opcode to the current method. - * - * @param theOpCode the opcode of the bytecode - * @param theOperand the operand of the bytecode - */ - public void add(int theOpCode, int theOperand) { - if (DEBUGCODE) { - System.out.println( - "Add " + bytecodeStr(theOpCode) + ", " + Integer.toHexString(theOperand)); - } - int newStack = itsStackTop + stackChange(theOpCode); - if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack); - - switch (theOpCode) { - case ByteCode.GOTO: - // This is necessary because dead code is seemingly being - // generated and Sun's verifier is expecting type state to be - // placed even at dead blocks of code. - addSuperBlockStart(itsCodeBufferTop + 3); - // fall through... - case ByteCode.IFEQ: - case ByteCode.IFNE: - case ByteCode.IFLT: - case ByteCode.IFGE: - case ByteCode.IFGT: - case ByteCode.IFLE: - case ByteCode.IF_ICMPEQ: - case ByteCode.IF_ICMPNE: - case ByteCode.IF_ICMPLT: - case ByteCode.IF_ICMPGE: - case ByteCode.IF_ICMPGT: - case ByteCode.IF_ICMPLE: - case ByteCode.IF_ACMPEQ: - case ByteCode.IF_ACMPNE: - case ByteCode.JSR: - case ByteCode.IFNULL: - case ByteCode.IFNONNULL: - { - if ((theOperand & 0x80000000) != 0x80000000) { - if ((theOperand < 0) || (theOperand > 65535)) - throw new IllegalArgumentException("Bad label for branch"); - } - int branchPC = itsCodeBufferTop; - addToCodeBuffer(theOpCode); - if ((theOperand & 0x80000000) != 0x80000000) { - // hard displacement - addToCodeInt16(theOperand); - int target = theOperand + branchPC; - addSuperBlockStart(target); - itsJumpFroms.put(target, branchPC); - } else { // a label - int targetPC = getLabelPC(theOperand); - if (DEBUGLABELS) { - int theLabel = theOperand & 0x7FFFFFFF; - System.out.println( - "Fixing branch to " - + theLabel - + " at " - + targetPC - + " from " - + branchPC); - } - if (targetPC != -1) { - int offset = targetPC - branchPC; - addToCodeInt16(offset); - addSuperBlockStart(targetPC); - itsJumpFroms.put(targetPC, branchPC); - } else { - addLabelFixup(theOperand, branchPC + 1); - addToCodeInt16(0); - } - } - } - break; - - case ByteCode.BIPUSH: - if ((byte) theOperand != theOperand) - throw new IllegalArgumentException("out of range byte"); - addToCodeBuffer(theOpCode); - addToCodeBuffer((byte) theOperand); - break; - - case ByteCode.SIPUSH: - if ((short) theOperand != theOperand) - throw new IllegalArgumentException("out of range short"); - addToCodeBuffer(theOpCode); - addToCodeInt16(theOperand); - break; - - case ByteCode.NEWARRAY: - if (!(0 <= theOperand && theOperand < 256)) - throw new IllegalArgumentException("out of range index"); - addToCodeBuffer(theOpCode); - addToCodeBuffer(theOperand); - break; - - case ByteCode.GETFIELD: - case ByteCode.PUTFIELD: - if (!(0 <= theOperand && theOperand < 65536)) - throw new IllegalArgumentException("out of range field"); - addToCodeBuffer(theOpCode); - addToCodeInt16(theOperand); - break; - - case ByteCode.LDC: - case ByteCode.LDC_W: - case ByteCode.LDC2_W: - if (!(0 <= theOperand && theOperand < 65536)) - throw new ClassFileFormatException("out of range index"); - if (theOperand >= 256 - || theOpCode == ByteCode.LDC_W - || theOpCode == ByteCode.LDC2_W) { - if (theOpCode == ByteCode.LDC) { - addToCodeBuffer(ByteCode.LDC_W); - } else { - addToCodeBuffer(theOpCode); - } - addToCodeInt16(theOperand); - } else { - addToCodeBuffer(theOpCode); - addToCodeBuffer(theOperand); - } - break; - - case ByteCode.RET: - case ByteCode.ILOAD: - case ByteCode.LLOAD: - case ByteCode.FLOAD: - case ByteCode.DLOAD: - case ByteCode.ALOAD: - case ByteCode.ISTORE: - case ByteCode.LSTORE: - case ByteCode.FSTORE: - case ByteCode.DSTORE: - case ByteCode.ASTORE: - if (theOperand < 0 || 65536 <= theOperand) - throw new ClassFileFormatException("out of range variable"); - if (theOperand >= 256) { - addToCodeBuffer(ByteCode.WIDE); - addToCodeBuffer(theOpCode); - addToCodeInt16(theOperand); - } else { - addToCodeBuffer(theOpCode); - addToCodeBuffer(theOperand); - } - break; - - default: - throw new IllegalArgumentException("Unexpected opcode for 1 operand"); - } - - itsStackTop = (short) newStack; - if (newStack > itsMaxStack) itsMaxStack = (short) newStack; - if (DEBUGSTACK) { - System.out.println("After " + bytecodeStr(theOpCode) + " stack = " + itsStackTop); - } - } - - /** - * Generate the load constant bytecode for the given integer. - * - * @param k the constant - */ - public void addLoadConstant(int k) { - switch (k) { - case 0: - add(ByteCode.ICONST_0); - break; - case 1: - add(ByteCode.ICONST_1); - break; - case 2: - add(ByteCode.ICONST_2); - break; - case 3: - add(ByteCode.ICONST_3); - break; - case 4: - add(ByteCode.ICONST_4); - break; - case 5: - add(ByteCode.ICONST_5); - break; - default: - add(ByteCode.LDC, itsConstantPool.addConstant(k)); - break; - } - } - - /** - * Generate the load constant bytecode for the given long. - * - * @param k the constant - */ - public void addLoadConstant(long k) { - add(ByteCode.LDC2_W, itsConstantPool.addConstant(k)); - } - - /** - * Generate the load constant bytecode for the given float. - * - * @param k the constant - */ - public void addLoadConstant(float k) { - add(ByteCode.LDC, itsConstantPool.addConstant(k)); - } - - /** - * Generate the load constant bytecode for the given double. - * - * @param k the constant - */ - public void addLoadConstant(double k) { - add(ByteCode.LDC2_W, itsConstantPool.addConstant(k)); - } - - /** - * Generate the load constant bytecode for the given string. - * - * @param k the constant - */ - public void addLoadConstant(String k) { - add(ByteCode.LDC, itsConstantPool.addConstant(k)); - } - - /** - * Add the given two-operand bytecode to the current method. - * - * @param theOpCode the opcode of the bytecode - * @param theOperand1 the first operand of the bytecode - * @param theOperand2 the second operand of the bytecode - */ - public void add(int theOpCode, int theOperand1, int theOperand2) { - if (DEBUGCODE) { - System.out.println( - "Add " - + bytecodeStr(theOpCode) - + ", " - + Integer.toHexString(theOperand1) - + ", " - + Integer.toHexString(theOperand2)); - } - int newStack = itsStackTop + stackChange(theOpCode); - if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack); - - if (theOpCode == ByteCode.IINC) { - if (theOperand1 < 0 || 65536 <= theOperand1) - throw new ClassFileFormatException("out of range variable"); - if (theOperand2 < 0 || 65536 <= theOperand2) - throw new ClassFileFormatException("out of range increment"); - - if (theOperand1 > 255 || theOperand2 > 127) { - addToCodeBuffer(ByteCode.WIDE); - addToCodeBuffer(ByteCode.IINC); - addToCodeInt16(theOperand1); - addToCodeInt16(theOperand2); - } else { - addToCodeBuffer(ByteCode.IINC); - addToCodeBuffer(theOperand1); - addToCodeBuffer(theOperand2); - } - } else if (theOpCode == ByteCode.MULTIANEWARRAY) { - if (!(0 <= theOperand1 && theOperand1 < 65536)) - throw new IllegalArgumentException("out of range index"); - if (!(0 <= theOperand2 && theOperand2 < 256)) - throw new IllegalArgumentException("out of range dimensions"); - - addToCodeBuffer(ByteCode.MULTIANEWARRAY); - addToCodeInt16(theOperand1); - addToCodeBuffer(theOperand2); - } else { - throw new IllegalArgumentException("Unexpected opcode for 2 operands"); - } - itsStackTop = (short) newStack; - if (newStack > itsMaxStack) itsMaxStack = (short) newStack; - if (DEBUGSTACK) { - System.out.println("After " + bytecodeStr(theOpCode) + " stack = " + itsStackTop); - } - } - - public void add(int theOpCode, String className) { - if (DEBUGCODE) { - System.out.println("Add " + bytecodeStr(theOpCode) + ", " + className); - } - int newStack = itsStackTop + stackChange(theOpCode); - if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack); - switch (theOpCode) { - case ByteCode.NEW: - case ByteCode.ANEWARRAY: - case ByteCode.CHECKCAST: - case ByteCode.INSTANCEOF: - { - short classIndex = itsConstantPool.addClass(className); - addToCodeBuffer(theOpCode); - addToCodeInt16(classIndex); - } - break; - - default: - throw new IllegalArgumentException("bad opcode for class reference"); - } - itsStackTop = (short) newStack; - if (newStack > itsMaxStack) itsMaxStack = (short) newStack; - if (DEBUGSTACK) { - System.out.println("After " + bytecodeStr(theOpCode) + " stack = " + itsStackTop); - } - } - - public void add(int theOpCode, String className, String fieldName, String fieldType) { - if (DEBUGCODE) { - System.out.println( - "Add " - + bytecodeStr(theOpCode) - + ", " - + className - + ", " - + fieldName - + ", " - + fieldType); - } - int newStack = itsStackTop + stackChange(theOpCode); - char fieldTypeChar = fieldType.charAt(0); - int fieldSize = (fieldTypeChar == 'J' || fieldTypeChar == 'D') ? 2 : 1; - switch (theOpCode) { - case ByteCode.GETFIELD: - case ByteCode.GETSTATIC: - newStack += fieldSize; - break; - case ByteCode.PUTSTATIC: - case ByteCode.PUTFIELD: - newStack -= fieldSize; - break; - default: - throw new IllegalArgumentException("bad opcode for field reference"); - } - if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack); - short fieldRefIndex = itsConstantPool.addFieldRef(className, fieldName, fieldType); - addToCodeBuffer(theOpCode); - addToCodeInt16(fieldRefIndex); - - itsStackTop = (short) newStack; - if (newStack > itsMaxStack) itsMaxStack = (short) newStack; - if (DEBUGSTACK) { - System.out.println("After " + bytecodeStr(theOpCode) + " stack = " + itsStackTop); - } - } - - public void addInvoke(int theOpCode, String className, String methodName, String methodType) { - if (DEBUGCODE) { - System.out.println( - "Add " - + bytecodeStr(theOpCode) - + ", " - + className - + ", " - + methodName - + ", " - + methodType); - } - int parameterInfo = sizeOfParameters(methodType); - int parameterCount = parameterInfo >>> 16; - int stackDiff = (short) parameterInfo; - - int newStack = itsStackTop + stackDiff; - newStack += stackChange(theOpCode); // adjusts for 'this' - if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack); - - switch (theOpCode) { - case ByteCode.INVOKEVIRTUAL: - case ByteCode.INVOKESPECIAL: - case ByteCode.INVOKESTATIC: - case ByteCode.INVOKEINTERFACE: - { - addToCodeBuffer(theOpCode); - if (theOpCode == ByteCode.INVOKEINTERFACE) { - short ifMethodRefIndex = - itsConstantPool.addInterfaceMethodRef( - className, methodName, methodType); - addToCodeInt16(ifMethodRefIndex); - addToCodeBuffer(parameterCount + 1); - addToCodeBuffer(0); - } else { - short methodRefIndex = - itsConstantPool.addMethodRef(className, methodName, methodType); - addToCodeInt16(methodRefIndex); - } - } - break; - - default: - throw new IllegalArgumentException("bad opcode for method reference"); - } - itsStackTop = (short) newStack; - if (newStack > itsMaxStack) itsMaxStack = (short) newStack; - if (DEBUGSTACK) { - System.out.println("After " + bytecodeStr(theOpCode) + " stack = " + itsStackTop); - } - } - - public void addInvokeDynamic( - String methodName, String methodType, MHandle bsm, Object... bsmArgs) { - if (DEBUGCODE) { - System.out.println("Add invokedynamic, " + methodName + ", " + methodType); - } - // JDK 1.7 major class file version is required for invokedynamic - if (MajorVersion < 51) { - throw new RuntimeException( - "Please build and run with JDK 1.7 for invokedynamic support"); - } - - int parameterInfo = sizeOfParameters(methodType); - // int parameterCount = parameterInfo >>> 16; - int stackDiff = (short) parameterInfo; - - int newStack = itsStackTop + stackDiff; - if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack); - - BootstrapEntry bsmEntry = new BootstrapEntry(bsm, bsmArgs); - - if (itsBootstrapMethods == null) { - itsBootstrapMethods = new ObjArray(); - } - int bootstrapIndex = itsBootstrapMethods.indexOf(bsmEntry); - if (bootstrapIndex == -1) { - bootstrapIndex = itsBootstrapMethods.size(); - itsBootstrapMethods.add(bsmEntry); - itsBootstrapMethodsLength += bsmEntry.code.length; - } - - short invokedynamicIndex = - itsConstantPool.addInvokeDynamic(methodName, methodType, bootstrapIndex); - - addToCodeBuffer(ByteCode.INVOKEDYNAMIC); - addToCodeInt16(invokedynamicIndex); - addToCodeInt16(0); - - itsStackTop = (short) newStack; - if (newStack > itsMaxStack) itsMaxStack = (short) newStack; - if (DEBUGSTACK) { - System.out.println("After invokedynamic stack = " + itsStackTop); - } - } - - /** - * Generate code to load the given integer on stack. - * - * @param k the constant - */ - public void addPush(int k) { - if ((byte) k == k) { - if (k == -1) { - add(ByteCode.ICONST_M1); - } else if (0 <= k && k <= 5) { - add((byte) (ByteCode.ICONST_0 + k)); - } else { - add(ByteCode.BIPUSH, (byte) k); - } - } else if ((short) k == k) { - add(ByteCode.SIPUSH, (short) k); - } else { - addLoadConstant(k); - } - } - - public void addPush(boolean k) { - add(k ? ByteCode.ICONST_1 : ByteCode.ICONST_0); - } - - /** - * Generate code to load the given long on stack. - * - * @param k the constant - */ - public void addPush(long k) { - int ik = (int) k; - if (ik == k) { - addPush(ik); - add(ByteCode.I2L); - } else { - addLoadConstant(k); - } - } - - /** - * Generate code to load the given double on stack. - * - * @param k the constant - */ - public void addPush(double k) { - if (k == 0.0) { - // zero - add(ByteCode.DCONST_0); - if (1.0 / k < 0) { - // Negative zero - add(ByteCode.DNEG); - } - } else if (k == 1.0 || k == -1.0) { - add(ByteCode.DCONST_1); - if (k < 0) { - add(ByteCode.DNEG); - } - } else { - addLoadConstant(k); - } - } - - /** - * Generate the code to leave on stack the given string even if the string encoding exeeds the - * class file limit for single string constant - * - * @param k the constant - */ - public void addPush(String k) { - int length = k.length(); - int limit = itsConstantPool.getUtfEncodingLimit(k, 0, length); - if (limit == length) { - addLoadConstant(k); - return; - } - // Split string into picies fitting the UTF limit and generate code for - // StringBuilder sb = new StringBuilder(length); - // sb.append(loadConstant(piece_1)); - // ... - // sb.append(loadConstant(piece_N)); - // sb.toString(); - final String SB = "java/lang/StringBuilder"; - add(ByteCode.NEW, SB); - add(ByteCode.DUP); - addPush(length); - addInvoke(ByteCode.INVOKESPECIAL, SB, "", "(I)V"); - int cursor = 0; - for (; ; ) { - add(ByteCode.DUP); - String s = k.substring(cursor, limit); - addLoadConstant(s); - addInvoke( - ByteCode.INVOKEVIRTUAL, - SB, - "append", - "(Ljava/lang/String;)Ljava/lang/StringBuilder;"); - add(ByteCode.POP); - if (limit == length) { - break; - } - cursor = limit; - limit = itsConstantPool.getUtfEncodingLimit(k, limit, length); - } - addInvoke(ByteCode.INVOKEVIRTUAL, SB, "toString", "()Ljava/lang/String;"); - } - - /** - * Check if k fits limit on string constant size imposed by class file format. - * - * @param k the string constant - */ - public boolean isUnderStringSizeLimit(String k) { - return itsConstantPool.isUnderUtfEncodingLimit(k); - } - - /** - * Store integer from stack top into the given local. - * - * @param local number of local register - */ - public void addIStore(int local) { - xop(ByteCode.ISTORE_0, ByteCode.ISTORE, local); - } - - /** - * Store long from stack top into the given local. - * - * @param local number of local register - */ - public void addLStore(int local) { - xop(ByteCode.LSTORE_0, ByteCode.LSTORE, local); - } - - /** - * Store float from stack top into the given local. - * - * @param local number of local register - */ - public void addFStore(int local) { - xop(ByteCode.FSTORE_0, ByteCode.FSTORE, local); - } - - /** - * Store double from stack top into the given local. - * - * @param local number of local register - */ - public void addDStore(int local) { - xop(ByteCode.DSTORE_0, ByteCode.DSTORE, local); - } - - /** - * Store object from stack top into the given local. - * - * @param local number of local register - */ - public void addAStore(int local) { - xop(ByteCode.ASTORE_0, ByteCode.ASTORE, local); - } - - /** - * Load integer from the given local into stack. - * - * @param local number of local register - */ - public void addILoad(int local) { - xop(ByteCode.ILOAD_0, ByteCode.ILOAD, local); - } - - /** - * Load long from the given local into stack. - * - * @param local number of local register - */ - public void addLLoad(int local) { - xop(ByteCode.LLOAD_0, ByteCode.LLOAD, local); - } - - /** - * Load float from the given local into stack. - * - * @param local number of local register - */ - public void addFLoad(int local) { - xop(ByteCode.FLOAD_0, ByteCode.FLOAD, local); - } - - /** - * Load double from the given local into stack. - * - * @param local number of local register - */ - public void addDLoad(int local) { - xop(ByteCode.DLOAD_0, ByteCode.DLOAD, local); - } - - /** - * Load object from the given local into stack. - * - * @param local number of local register - */ - public void addALoad(int local) { - xop(ByteCode.ALOAD_0, ByteCode.ALOAD, local); - } - - /** Load "this" into stack. */ - public void addLoadThis() { - add(ByteCode.ALOAD_0); - } - - private void xop(int shortOp, int op, int local) { - switch (local) { - case 0: - add(shortOp); - break; - case 1: - add(shortOp + 1); - break; - case 2: - add(shortOp + 2); - break; - case 3: - add(shortOp + 3); - break; - default: - add(op, local); - } - } - - public int addTableSwitch(int low, int high) { - if (DEBUGCODE) { - System.out.println("Add " + bytecodeStr(ByteCode.TABLESWITCH) + " " + low + " " + high); - } - if (low > high) throw new ClassFileFormatException("Bad bounds: " + low + ' ' + high); - - int newStack = itsStackTop + stackChange(ByteCode.TABLESWITCH); - if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack); - - int entryCount = high - low + 1; - int padSize = 3 & ~itsCodeBufferTop; // == 3 - itsCodeBufferTop % 4 - - int N = addReservedCodeSpace(1 + padSize + 4 * (1 + 2 + entryCount)); - int switchStart = N; - itsCodeBuffer[N++] = (byte) ByteCode.TABLESWITCH; - while (padSize != 0) { - itsCodeBuffer[N++] = 0; - --padSize; - } - N += 4; // skip default offset - N = putInt32(low, itsCodeBuffer, N); - putInt32(high, itsCodeBuffer, N); - - itsStackTop = (short) newStack; - if (newStack > itsMaxStack) itsMaxStack = (short) newStack; - if (DEBUGSTACK) { - System.out.println( - "After " + bytecodeStr(ByteCode.TABLESWITCH) + " stack = " + itsStackTop); - } - - return switchStart; - } - - public final void markTableSwitchDefault(int switchStart) { - addSuperBlockStart(itsCodeBufferTop); - itsJumpFroms.put(itsCodeBufferTop, switchStart); - setTableSwitchJump(switchStart, -1, itsCodeBufferTop); - } - - public final void markTableSwitchCase(int switchStart, int caseIndex) { - addSuperBlockStart(itsCodeBufferTop); - itsJumpFroms.put(itsCodeBufferTop, switchStart); - setTableSwitchJump(switchStart, caseIndex, itsCodeBufferTop); - } - - public final void markTableSwitchCase(int switchStart, int caseIndex, int stackTop) { - if (!(0 <= stackTop && stackTop <= itsMaxStack)) - throw new IllegalArgumentException("Bad stack index: " + stackTop); - itsStackTop = (short) stackTop; - addSuperBlockStart(itsCodeBufferTop); - itsJumpFroms.put(itsCodeBufferTop, switchStart); - setTableSwitchJump(switchStart, caseIndex, itsCodeBufferTop); - } - - /** - * Set a jump case for a tableswitch instruction. The jump target should be marked as a super - * block start for stack map generation. - */ - public void setTableSwitchJump(int switchStart, int caseIndex, int jumpTarget) { - if (jumpTarget < 0 || itsCodeBufferTop < jumpTarget) - throw new IllegalArgumentException("Bad jump target: " + jumpTarget); - if (caseIndex < -1) throw new IllegalArgumentException("Bad case index: " + caseIndex); - - int padSize = 3 & ~switchStart; // == 3 - switchStart % 4 - int caseOffset; - if (caseIndex < 0) { - // default label - caseOffset = switchStart + 1 + padSize; - } else { - caseOffset = switchStart + 1 + padSize + 4 * (3 + caseIndex); - } - if (switchStart < 0 || itsCodeBufferTop - 4 * 4 - padSize - 1 < switchStart) { - throw new IllegalArgumentException( - switchStart - + " is outside a possible range of tableswitch" - + " in already generated code"); - } - if ((0xFF & itsCodeBuffer[switchStart]) != ByteCode.TABLESWITCH) { - throw new IllegalArgumentException( - switchStart + " is not offset of tableswitch statement"); - } - if (caseOffset < 0 || itsCodeBufferTop < caseOffset + 4) { - // caseIndex >= -1 does not guarantee that caseOffset >= 0 due - // to a possible overflow. - throw new ClassFileFormatException("Too big case index: " + caseIndex); - } - // ALERT: perhaps check against case bounds? - putInt32(jumpTarget - switchStart, itsCodeBuffer, caseOffset); - } - - public int acquireLabel() { - int top = itsLabelTableTop; - if (itsLabelTable == null || top == itsLabelTable.length) { - if (itsLabelTable == null) { - itsLabelTable = new int[MIN_LABEL_TABLE_SIZE]; - } else { - int[] tmp = new int[itsLabelTable.length * 2]; - System.arraycopy(itsLabelTable, 0, tmp, 0, top); - itsLabelTable = tmp; - } - } - itsLabelTableTop = top + 1; - itsLabelTable[top] = -1; - return top | 0x80000000; - } - - public void markLabel(int label) { - if (!(label < 0)) throw new IllegalArgumentException("Bad label, no biscuit"); - - label &= 0x7FFFFFFF; - if (label > itsLabelTableTop) throw new IllegalArgumentException("Bad label"); - - if (itsLabelTable[label] != -1) { - throw new IllegalStateException("Can only mark label once"); - } - - itsLabelTable[label] = itsCodeBufferTop; - } - - public void markLabel(int label, short stackTop) { - markLabel(label); - itsStackTop = stackTop; - } - - public void markHandler(int theLabel) { - itsStackTop = 1; - markLabel(theLabel); - } - - public int getLabelPC(int label) { - if (!(label < 0)) throw new IllegalArgumentException("Bad label, no biscuit"); - label &= 0x7FFFFFFF; - if (!(label < itsLabelTableTop)) throw new IllegalArgumentException("Bad label"); - return itsLabelTable[label]; - } - - private void addLabelFixup(int label, int fixupSite) { - if (!(label < 0)) throw new IllegalArgumentException("Bad label, no biscuit"); - label &= 0x7FFFFFFF; - if (!(label < itsLabelTableTop)) throw new IllegalArgumentException("Bad label"); - int top = itsFixupTableTop; - if (itsFixupTable == null || top == itsFixupTable.length) { - if (itsFixupTable == null) { - itsFixupTable = new long[MIN_FIXUP_TABLE_SIZE]; - } else { - long[] tmp = new long[itsFixupTable.length * 2]; - System.arraycopy(itsFixupTable, 0, tmp, 0, top); - itsFixupTable = tmp; - } - } - itsFixupTableTop = top + 1; - itsFixupTable[top] = ((long) label << 32) | fixupSite; - } - - private void fixLabelGotos() { - byte[] codeBuffer = itsCodeBuffer; - for (int i = 0; i < itsFixupTableTop; i++) { - long fixup = itsFixupTable[i]; - int label = (int) (fixup >> 32); - int fixupSite = (int) fixup; - int pc = itsLabelTable[label]; - if (pc == -1) { - // Unlocated label - throw new RuntimeException("unlocated label"); - } - // -1 to get delta from instruction start - addSuperBlockStart(pc); - itsJumpFroms.put(pc, fixupSite - 1); - int offset = pc - (fixupSite - 1); - if ((short) offset != offset) { - throw new ClassFileFormatException("Program too complex: too big jump offset"); - } - codeBuffer[fixupSite] = (byte) (offset >> 8); - codeBuffer[fixupSite + 1] = (byte) offset; - } - itsFixupTableTop = 0; - } - - /** - * Get the current offset into the code of the current method. - * - * @return an integer representing the offset - */ - public int getCurrentCodeOffset() { - return itsCodeBufferTop; - } - - public short getStackTop() { - return itsStackTop; - } - - public void setStackTop(short n) { - itsStackTop = n; - } - - public void adjustStackTop(int delta) { - int newStack = itsStackTop + delta; - if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack); - itsStackTop = (short) newStack; - if (newStack > itsMaxStack) itsMaxStack = (short) newStack; - if (DEBUGSTACK) { - System.out.println( - "After " + "adjustStackTop(" + delta + ")" + " stack = " + itsStackTop); - } - } - - private void addToCodeBuffer(int b) { - int N = addReservedCodeSpace(1); - itsCodeBuffer[N] = (byte) b; - } - - private void addToCodeInt16(int value) { - int N = addReservedCodeSpace(2); - putInt16(value, itsCodeBuffer, N); - } - - private int addReservedCodeSpace(int size) { - if (itsCurrentMethod == null) throw new IllegalArgumentException("No method to add to"); - int oldTop = itsCodeBufferTop; - int newTop = oldTop + size; - if (newTop > itsCodeBuffer.length) { - int newSize = itsCodeBuffer.length * 2; - if (newTop > newSize) { - newSize = newTop; - } - byte[] tmp = new byte[newSize]; - System.arraycopy(itsCodeBuffer, 0, tmp, 0, oldTop); - itsCodeBuffer = tmp; - } - itsCodeBufferTop = newTop; - return oldTop; - } - - public void addExceptionHandler( - int startLabel, int endLabel, int handlerLabel, String catchClassName) { - if ((startLabel & 0x80000000) != 0x80000000) - throw new IllegalArgumentException("Bad startLabel"); - if ((endLabel & 0x80000000) != 0x80000000) - throw new IllegalArgumentException("Bad endLabel"); - if ((handlerLabel & 0x80000000) != 0x80000000) - throw new IllegalArgumentException("Bad handlerLabel"); - - /* - * If catchClassName is null, use 0 for the catch_type_index; which - * means catch everything. (Even when the verifier has let you throw - * something other than a Throwable.) - */ - short catch_type_index = - (catchClassName == null) ? 0 : itsConstantPool.addClass(catchClassName); - ExceptionTableEntry newEntry = - new ExceptionTableEntry(startLabel, endLabel, handlerLabel, catch_type_index); - int N = itsExceptionTableTop; - if (N == 0) { - itsExceptionTable = new ExceptionTableEntry[ExceptionTableSize]; - } else if (N == itsExceptionTable.length) { - ExceptionTableEntry[] tmp = new ExceptionTableEntry[N * 2]; - System.arraycopy(itsExceptionTable, 0, tmp, 0, N); - itsExceptionTable = tmp; - } - itsExceptionTable[N] = newEntry; - itsExceptionTableTop = N + 1; - } - - public void addLineNumberEntry(short lineNumber) { - if (itsCurrentMethod == null) throw new IllegalArgumentException("No method to stop"); - int N = itsLineNumberTableTop; - if (N == 0) { - itsLineNumberTable = new int[LineNumberTableSize]; - } else if (N == itsLineNumberTable.length) { - int[] tmp = new int[N * 2]; - System.arraycopy(itsLineNumberTable, 0, tmp, 0, N); - itsLineNumberTable = tmp; - } - itsLineNumberTable[N] = (itsCodeBufferTop << 16) + lineNumber; - itsLineNumberTableTop = N + 1; - } - - /** - * A stack map table is a code attribute introduced in Java 6 that gives type information at key - * points in the method body (namely, at the beginning of each super block after the first). - * Each frame of a stack map table contains the state of local variable and operand stack for a - * given super block. - */ - final class StackMapTable { - - StackMapTable() { - superBlocks = null; - locals = stack = null; - workList = null; - rawStackMap = null; - localsTop = 0; - stackTop = 0; - workListTop = 0; - rawStackMapTop = 0; - wide = false; - } - - void generate() { - superBlocks = new SuperBlock[itsSuperBlockStartsTop]; - int[] initialLocals = createInitialLocals(); - - for (int i = 0; i < itsSuperBlockStartsTop; i++) { - int start = itsSuperBlockStarts[i]; - int end; - if (i == itsSuperBlockStartsTop - 1) { - end = itsCodeBufferTop; - } else { - end = itsSuperBlockStarts[i + 1]; - } - superBlocks[i] = new SuperBlock(i, start, end, initialLocals); - } - - if (DEBUGSTACKMAP) { - System.out.println("super blocks: "); - for (int i = 0; i < superBlocks.length && superBlocks[i] != null; i++) { - System.out.println( - "sb " - + i - + ": [" - + superBlocks[i].getStart() - + ", " - + superBlocks[i].getEnd() - + ")"); - } - } - - verify(); - - if (DEBUGSTACKMAP) { - System.out.println("type information:"); - for (int i = 0; i < superBlocks.length; i++) { - SuperBlock sb = superBlocks[i]; - System.out.println("sb " + i + ":"); - TypeInfo.print(sb.getLocals(), sb.getStack(), itsConstantPool); - } - } - } - - private SuperBlock getSuperBlockFromOffset(int offset) { - int startIdx = - Arrays.binarySearch(itsSuperBlockStarts, 0, itsSuperBlockStartsTop, offset); - - if (startIdx < 0) { - // if offset was not found, insertion point is returned (See - // Arrays.binarySearch) - // we convert it back to the matching superblock. - startIdx = -startIdx - 2; - } - if (startIdx < itsSuperBlockStartsTop) { - SuperBlock sb = superBlocks[startIdx]; - // check, if it is really the matching one - if (offset < sb.getStart() || offset >= sb.getEnd()) Kit.codeBug(); - return sb; - } - throw new IllegalArgumentException("bad offset: " + offset); - } - - /** - * Determine whether or not an opcode is an actual end to a super block. This includes any - * returns or unconditional jumps. - */ - private boolean isSuperBlockEnd(int opcode) { - switch (opcode) { - case ByteCode.ARETURN: - case ByteCode.FRETURN: - case ByteCode.IRETURN: - case ByteCode.LRETURN: - case ByteCode.RETURN: - case ByteCode.ATHROW: - case ByteCode.GOTO: - case ByteCode.GOTO_W: - case ByteCode.TABLESWITCH: - case ByteCode.LOOKUPSWITCH: - return true; - default: - return false; - } - } - - /** - * Get the target super block of a branch instruction. - * - * @param bci the index of the branch instruction in the code buffer - */ - private SuperBlock getBranchTarget(int bci) { - int target; - if ((itsCodeBuffer[bci] & 0xFF) == ByteCode.GOTO_W) { - target = bci + getOperand(bci + 1, 4); - } else { - target = bci + (short) getOperand(bci + 1, 2); - } - return getSuperBlockFromOffset(target); - } - - /** Determine whether or not an opcode is a conditional or unconditional jump. */ - private boolean isBranch(int opcode) { - switch (opcode) { - case ByteCode.GOTO: - case ByteCode.GOTO_W: - case ByteCode.IFEQ: - case ByteCode.IFGE: - case ByteCode.IFGT: - case ByteCode.IFLE: - case ByteCode.IFLT: - case ByteCode.IFNE: - case ByteCode.IFNONNULL: - case ByteCode.IFNULL: - case ByteCode.IF_ACMPEQ: - case ByteCode.IF_ACMPNE: - case ByteCode.IF_ICMPEQ: - case ByteCode.IF_ICMPGE: - case ByteCode.IF_ICMPGT: - case ByteCode.IF_ICMPLE: - case ByteCode.IF_ICMPLT: - case ByteCode.IF_ICMPNE: - return true; - default: - return false; - } - } - - private int getOperand(int offset) { - return getOperand(offset, 1); - } - - /** - * Extract a logical operand from the byte code. - * - *

This is used, for example, to get branch offsets. - */ - private int getOperand(int start, int size) { - int result = 0; - if (size > 4) { - throw new IllegalArgumentException("bad operand size"); - } - for (int i = 0; i < size; i++) { - result = (result << 8) | (itsCodeBuffer[start + i] & 0xFF); - } - return result; - } - - /** - * Calculate initial local variable and op stack types for each super block in the method. - */ - private void verify() { - int[] initialLocals = createInitialLocals(); - superBlocks[0].merge( - initialLocals, initialLocals.length, new int[0], 0, itsConstantPool); - - // Start from the top of the method and queue up block dependencies - // as they come along. - workList = new SuperBlock[] {superBlocks[0]}; - workListTop = 1; - executeWorkList(); - - // Replace dead code with no-ops. - for (int i = 0; i < superBlocks.length; i++) { - SuperBlock sb = superBlocks[i]; - if (!sb.isInitialized()) { - killSuperBlock(sb); - } - } - executeWorkList(); - } - - /** - * Replace the contents of a super block with no-ops. - * - *

The above description is not strictly true; the last instruction is an athrow - * instruction. This technique is borrowed from ASM's developer guide: - * http://asm.ow2.org/doc/developer-guide.html#deadcode - * - *

The proposed algorithm fills a block with nop, ending it with an athrow. The stack map - * generated would be empty locals with an exception on the stack. In theory, it shouldn't - * matter what the locals are, as long as the stack has an exception for the athrow bit. - * However, it turns out that if the code being modified falls into an exception handler, it - * causes problems. Therefore, if it does, then we steal the locals from the exception - * block. - * - *

If the block itself is an exception handler, we remove it from the exception table to - * simplify block dependencies. - */ - private void killSuperBlock(SuperBlock sb) { - int[] locals = new int[0]; - int[] stack = new int[] {TypeInfo.OBJECT("java/lang/Throwable", itsConstantPool)}; - - // If the super block is handled by any exception handler, use its - // locals as the killed block's locals. Ignore uninitialized - // handlers, because they will also be killed and removed from the - // exception table. - int sbStart = sb.getStart(); - for (int i = 0; i < itsExceptionTableTop; i++) { - ExceptionTableEntry ete = itsExceptionTable[i]; - int eteStart = getLabelPC(ete.itsStartLabel); - // this is "hot" code and it has been optimized so that - // there are not too many function calls - if (sbStart > eteStart && sbStart < getLabelPC(ete.itsEndLabel)) { - int handlerPC = getLabelPC(ete.itsHandlerLabel); - SuperBlock handlerSB = getSuperBlockFromOffset(handlerPC); - locals = handlerSB.getLocals(); - if (handlerSB.isInitialized()) { - break; - } else { - continue; - } - } - if (eteStart > sbStart && eteStart < sb.getEnd()) { - int handlerPC = getLabelPC(ete.itsHandlerLabel); - SuperBlock handlerSB = getSuperBlockFromOffset(handlerPC); - if (handlerSB.isInitialized()) { - locals = handlerSB.getLocals(); - break; - } - } - } - - // Remove any exception table entry whose handler is the killed - // block. This removes block dependencies to make stack maps for - // dead blocks easier to create. - for (int i = 0; i < itsExceptionTableTop; i++) { - ExceptionTableEntry ete = itsExceptionTable[i]; - int eteStart = getLabelPC(ete.itsStartLabel); - if (eteStart == sb.getStart() || getLabelPC(ete.itsHandlerLabel) == sb.getStart()) { - for (int j = i + 1; j < itsExceptionTableTop; j++) { - itsExceptionTable[j - 1] = itsExceptionTable[j]; - } - itsExceptionTableTop--; - i--; - } - } - - sb.merge(locals, locals.length, stack, stack.length, itsConstantPool); - - int end = sb.getEnd() - 1; - itsCodeBuffer[end] = (byte) ByteCode.ATHROW; - for (int bci = sb.getStart(); bci < end; bci++) { - itsCodeBuffer[bci] = (byte) ByteCode.NOP; - } - } - - private void executeWorkList() { - while (workListTop > 0) { - SuperBlock work = workList[--workListTop]; - work.setInQueue(false); - locals = work.getLocals(); - stack = work.getStack(); - localsTop = locals.length; - stackTop = stack.length; - executeBlock(work); - } - } - - /** Simulate the local variable and op stack for a super block. */ - private void executeBlock(SuperBlock work) { - int bc = 0; - int next = 0; - - if (DEBUGSTACKMAP) { - System.out.println("working on sb " + work.getIndex()); - System.out.println("initial type state:"); - TypeInfo.print(locals, localsTop, stack, stackTop, itsConstantPool); - } - - int etStart = 0; - int etEnd = itsExceptionTableTop; - if (itsExceptionTableTop > 1) { - // determine the relevant search range in the exception table. - // this will reduce the search time if we have many exception - // blocks. There may be false positives in the range, but in - // most cases, this code does a good job, which leads in to - // fewer checks in the double-for-loop. - etStart = Integer.MAX_VALUE; - etEnd = 0; - for (int i = 0; i < itsExceptionTableTop; i++) { - ExceptionTableEntry ete = itsExceptionTable[i]; - // we have found an entry, that overlaps with our work block - if (work.getEnd() >= getLabelPC(ete.itsStartLabel) - && work.getStart() < getLabelPC(ete.itsEndLabel)) { - etStart = Math.min(etStart, i); - etEnd = Math.max(etEnd, i + 1); - } - } - if (DEBUGSTACK) { - if (etStart == 0 && etEnd == itsExceptionTableTop) { - System.out.println( - "lookup size " + itsExceptionTableTop + ": could not be reduced"); - } else if (etStart < 0) { - System.out.println( - "lookup size " + itsExceptionTableTop + ": reduced completely"); - } else { - System.out.println( - "lookup size " - + itsExceptionTableTop - + ": reduced to " - + (etEnd - etStart)); - } - } - } - - for (int bci = work.getStart(); bci < work.getEnd(); bci += next) { - bc = itsCodeBuffer[bci] & 0xFF; - next = execute(bci); - - // If we have a branch to some super block, we need to merge - // the current state of the local table and op stack with what's - // currently stored as the initial state of the super block. If - // something actually changed, we need to add it to the work - // list. - if (isBranch(bc)) { - SuperBlock targetSB = getBranchTarget(bci); - if (DEBUGSTACKMAP) { - System.out.println( - "sb " - + work.getIndex() - + " points to sb " - + targetSB.getIndex() - + " (offset " - + bci - + " -> " - + targetSB.getStart() - + ")"); - System.out.println("type state at " + bci + ":"); - TypeInfo.print(locals, localsTop, stack, stackTop, itsConstantPool); - } - flowInto(targetSB); - if (DEBUGSTACKMAP) { - System.out.println("type state of " + targetSB + " after merge:"); - TypeInfo.print(targetSB.getLocals(), targetSB.getStack(), itsConstantPool); - } - } else if (bc == ByteCode.TABLESWITCH) { - int switchStart = bci + 1 + (3 & ~bci); // 3 - bci % 4 - int defaultOffset = getOperand(switchStart, 4); - SuperBlock targetSB = getSuperBlockFromOffset(bci + defaultOffset); - if (DEBUGSTACK) { - System.out.println( - "merging sb " - + work.getIndex() - + " with sb " - + targetSB.getIndex()); - } - flowInto(targetSB); - int low = getOperand(switchStart + 4, 4); - int high = getOperand(switchStart + 8, 4); - int numCases = high - low + 1; - int caseBase = switchStart + 12; - for (int i = 0; i < numCases; i++) { - int label = bci + getOperand(caseBase + 4 * i, 4); - targetSB = getSuperBlockFromOffset(label); - if (DEBUGSTACKMAP) { - System.out.println( - "merging sb " - + work.getIndex() - + " with sb " - + targetSB.getIndex()); - } - flowInto(targetSB); - } - } - - for (int i = etStart; i < etEnd; i++) { - ExceptionTableEntry ete = itsExceptionTable[i]; - int startPC = getLabelPC(ete.itsStartLabel); - int endPC = getLabelPC(ete.itsEndLabel); - if (bci < startPC || bci >= endPC) { - continue; - } - int handlerPC = getLabelPC(ete.itsHandlerLabel); - SuperBlock sb = getSuperBlockFromOffset(handlerPC); - int exceptionType; - - if (ete.itsCatchType == 0) { - exceptionType = - TypeInfo.OBJECT(itsConstantPool.addClass("java/lang/Throwable")); - } else { - exceptionType = TypeInfo.OBJECT(ete.itsCatchType); - } - sb.merge(locals, localsTop, new int[] {exceptionType}, 1, itsConstantPool); - addToWorkList(sb); - } - } - - if (DEBUGSTACKMAP) { - System.out.println("end of sb " + work.getIndex() + ":"); - TypeInfo.print(locals, localsTop, stack, stackTop, itsConstantPool); - } - - // Check the last instruction to see if it is a true end of a - // super block (ie., if the instruction is a return). If it - // isn't, we need to continue processing the next chunk. - if (!isSuperBlockEnd(bc)) { - int nextIndex = work.getIndex() + 1; - if (nextIndex < superBlocks.length) { - if (DEBUGSTACKMAP) { - System.out.println( - "continuing from sb " + work.getIndex() + " into sb " + nextIndex); - } - flowInto(superBlocks[nextIndex]); - } - } - } - - /** - * Perform a merge of type state and add the super block to the work list if the merge - * changed anything. - */ - private void flowInto(SuperBlock sb) { - if (sb.merge(locals, localsTop, stack, stackTop, itsConstantPool)) { - addToWorkList(sb); - } - } - - private void addToWorkList(SuperBlock sb) { - if (!sb.isInQueue()) { - sb.setInQueue(true); - sb.setInitialized(true); - if (workListTop == workList.length) { - SuperBlock[] tmp = new SuperBlock[workListTop * 2]; - System.arraycopy(workList, 0, tmp, 0, workListTop); - workList = tmp; - } - workList[workListTop++] = sb; - } - } - - /** - * Execute a single byte code instruction. - * - * @param bci the index of the byte code instruction to execute - * @return the length of the byte code instruction - */ - private int execute(int bci) { - int bc = itsCodeBuffer[bci] & 0xFF; - int type, type2, index; - int length = 0; - long lType, lType2; - String className; - - switch (bc) { - case ByteCode.NOP: - case ByteCode.IINC: - case ByteCode.GOTO: - case ByteCode.GOTO_W: - // No change - break; - case ByteCode.CHECKCAST: - pop(); - push(TypeInfo.OBJECT(getOperand(bci + 1, 2))); - break; - case ByteCode.IASTORE: // pop; pop; pop - case ByteCode.LASTORE: - case ByteCode.FASTORE: - case ByteCode.DASTORE: - case ByteCode.AASTORE: - case ByteCode.BASTORE: - case ByteCode.CASTORE: - case ByteCode.SASTORE: - pop(); - // fall through - case ByteCode.PUTFIELD: // pop; pop - case ByteCode.IF_ICMPEQ: - case ByteCode.IF_ICMPNE: - case ByteCode.IF_ICMPLT: - case ByteCode.IF_ICMPGE: - case ByteCode.IF_ICMPGT: - case ByteCode.IF_ICMPLE: - case ByteCode.IF_ACMPEQ: - case ByteCode.IF_ACMPNE: - pop(); - // fall through - case ByteCode.IFEQ: // pop - case ByteCode.IFNE: - case ByteCode.IFLT: - case ByteCode.IFGE: - case ByteCode.IFGT: - case ByteCode.IFLE: - case ByteCode.IFNULL: - case ByteCode.IFNONNULL: - case ByteCode.POP: - case ByteCode.MONITORENTER: - case ByteCode.MONITOREXIT: - case ByteCode.PUTSTATIC: - pop(); - break; - case ByteCode.POP2: - pop2(); - break; - case ByteCode.ACONST_NULL: - push(TypeInfo.NULL); - break; - case ByteCode.IALOAD: // pop; pop; push(INTEGER) - case ByteCode.BALOAD: - case ByteCode.CALOAD: - case ByteCode.SALOAD: - case ByteCode.IADD: - case ByteCode.ISUB: - case ByteCode.IMUL: - case ByteCode.IDIV: - case ByteCode.IREM: - case ByteCode.ISHL: - case ByteCode.ISHR: - case ByteCode.IUSHR: - case ByteCode.IAND: - case ByteCode.IOR: - case ByteCode.IXOR: - case ByteCode.LCMP: - case ByteCode.FCMPL: - case ByteCode.FCMPG: - case ByteCode.DCMPL: - case ByteCode.DCMPG: - pop(); - // fall through - case ByteCode.INEG: // pop; push(INTEGER) - case ByteCode.L2I: - case ByteCode.F2I: - case ByteCode.D2I: - case ByteCode.I2B: - case ByteCode.I2C: - case ByteCode.I2S: - case ByteCode.ARRAYLENGTH: - case ByteCode.INSTANCEOF: - pop(); - // fall through - case ByteCode.ICONST_M1: // push(INTEGER) - case ByteCode.ICONST_0: - case ByteCode.ICONST_1: - case ByteCode.ICONST_2: - case ByteCode.ICONST_3: - case ByteCode.ICONST_4: - case ByteCode.ICONST_5: - case ByteCode.ILOAD: - case ByteCode.ILOAD_0: - case ByteCode.ILOAD_1: - case ByteCode.ILOAD_2: - case ByteCode.ILOAD_3: - case ByteCode.BIPUSH: - case ByteCode.SIPUSH: - push(TypeInfo.INTEGER); - break; - case ByteCode.LALOAD: // pop; pop; push(LONG) - case ByteCode.LADD: - case ByteCode.LSUB: - case ByteCode.LMUL: - case ByteCode.LDIV: - case ByteCode.LREM: - case ByteCode.LSHL: - case ByteCode.LSHR: - case ByteCode.LUSHR: - case ByteCode.LAND: - case ByteCode.LOR: - case ByteCode.LXOR: - pop(); - // fall through - case ByteCode.LNEG: // pop; push(LONG) - case ByteCode.I2L: - case ByteCode.F2L: - case ByteCode.D2L: - pop(); - // fall through - case ByteCode.LCONST_0: // push(LONG) - case ByteCode.LCONST_1: - case ByteCode.LLOAD: - case ByteCode.LLOAD_0: - case ByteCode.LLOAD_1: - case ByteCode.LLOAD_2: - case ByteCode.LLOAD_3: - push(TypeInfo.LONG); - break; - case ByteCode.FALOAD: // pop; pop; push(FLOAT) - case ByteCode.FADD: - case ByteCode.FSUB: - case ByteCode.FMUL: - case ByteCode.FDIV: - case ByteCode.FREM: - pop(); - // fall through - case ByteCode.FNEG: // pop; push(FLOAT) - case ByteCode.I2F: - case ByteCode.L2F: - case ByteCode.D2F: - pop(); - // fall through - case ByteCode.FCONST_0: // push(FLOAT) - case ByteCode.FCONST_1: - case ByteCode.FCONST_2: - case ByteCode.FLOAD: - case ByteCode.FLOAD_0: - case ByteCode.FLOAD_1: - case ByteCode.FLOAD_2: - case ByteCode.FLOAD_3: - push(TypeInfo.FLOAT); - break; - case ByteCode.DALOAD: // pop; pop; push(DOUBLE) - case ByteCode.DADD: - case ByteCode.DSUB: - case ByteCode.DMUL: - case ByteCode.DDIV: - case ByteCode.DREM: - pop(); - // fall through - case ByteCode.DNEG: // pop; push(DOUBLE) - case ByteCode.I2D: - case ByteCode.L2D: - case ByteCode.F2D: - pop(); - // fall through - case ByteCode.DCONST_0: // push(DOUBLE) - case ByteCode.DCONST_1: - case ByteCode.DLOAD: - case ByteCode.DLOAD_0: - case ByteCode.DLOAD_1: - case ByteCode.DLOAD_2: - case ByteCode.DLOAD_3: - push(TypeInfo.DOUBLE); - break; - case ByteCode.ISTORE: - executeStore(getOperand(bci + 1, wide ? 2 : 1), TypeInfo.INTEGER); - break; - case ByteCode.ISTORE_0: - case ByteCode.ISTORE_1: - case ByteCode.ISTORE_2: - case ByteCode.ISTORE_3: - executeStore(bc - ByteCode.ISTORE_0, TypeInfo.INTEGER); - break; - case ByteCode.LSTORE: - executeStore(getOperand(bci + 1, wide ? 2 : 1), TypeInfo.LONG); - break; - case ByteCode.LSTORE_0: - case ByteCode.LSTORE_1: - case ByteCode.LSTORE_2: - case ByteCode.LSTORE_3: - executeStore(bc - ByteCode.LSTORE_0, TypeInfo.LONG); - break; - case ByteCode.FSTORE: - executeStore(getOperand(bci + 1, wide ? 2 : 1), TypeInfo.FLOAT); - break; - case ByteCode.FSTORE_0: - case ByteCode.FSTORE_1: - case ByteCode.FSTORE_2: - case ByteCode.FSTORE_3: - executeStore(bc - ByteCode.FSTORE_0, TypeInfo.FLOAT); - break; - case ByteCode.DSTORE: - executeStore(getOperand(bci + 1, wide ? 2 : 1), TypeInfo.DOUBLE); - break; - case ByteCode.DSTORE_0: - case ByteCode.DSTORE_1: - case ByteCode.DSTORE_2: - case ByteCode.DSTORE_3: - executeStore(bc - ByteCode.DSTORE_0, TypeInfo.DOUBLE); - break; - case ByteCode.ALOAD: - executeALoad(getOperand(bci + 1, wide ? 2 : 1)); - break; - case ByteCode.ALOAD_0: - case ByteCode.ALOAD_1: - case ByteCode.ALOAD_2: - case ByteCode.ALOAD_3: - executeALoad(bc - ByteCode.ALOAD_0); - break; - case ByteCode.ASTORE: - executeAStore(getOperand(bci + 1, wide ? 2 : 1)); - break; - case ByteCode.ASTORE_0: - case ByteCode.ASTORE_1: - case ByteCode.ASTORE_2: - case ByteCode.ASTORE_3: - executeAStore(bc - ByteCode.ASTORE_0); - break; - case ByteCode.IRETURN: - case ByteCode.LRETURN: - case ByteCode.FRETURN: - case ByteCode.DRETURN: - case ByteCode.ARETURN: - case ByteCode.RETURN: - clearStack(); - break; - case ByteCode.ATHROW: - type = pop(); - clearStack(); - push(type); - break; - case ByteCode.SWAP: - type = pop(); - type2 = pop(); - push(type); - push(type2); - break; - case ByteCode.LDC: - case ByteCode.LDC_W: - case ByteCode.LDC2_W: - if (bc == ByteCode.LDC) { - index = getOperand(bci + 1); - } else { - index = getOperand(bci + 1, 2); - } - byte constType = itsConstantPool.getConstantType(index); - switch (constType) { - case ConstantPool.CONSTANT_Double: - push(TypeInfo.DOUBLE); - break; - case ConstantPool.CONSTANT_Float: - push(TypeInfo.FLOAT); - break; - case ConstantPool.CONSTANT_Long: - push(TypeInfo.LONG); - break; - case ConstantPool.CONSTANT_Integer: - push(TypeInfo.INTEGER); - break; - case ConstantPool.CONSTANT_String: - push(TypeInfo.OBJECT("java/lang/String", itsConstantPool)); - break; - default: - throw new IllegalArgumentException("bad const type " + constType); - } - break; - case ByteCode.NEW: - push(TypeInfo.UNINITIALIZED_VARIABLE(bci)); - break; - case ByteCode.NEWARRAY: - pop(); - char componentType = arrayTypeToName(itsCodeBuffer[bci + 1]); - index = itsConstantPool.addClass("[" + componentType); - push(TypeInfo.OBJECT((short) index)); - break; - case ByteCode.ANEWARRAY: - index = getOperand(bci + 1, 2); - className = (String) itsConstantPool.getConstantData(index); - pop(); - push(TypeInfo.OBJECT("[L" + className + ';', itsConstantPool)); - break; - case ByteCode.INVOKEVIRTUAL: - case ByteCode.INVOKESPECIAL: - case ByteCode.INVOKESTATIC: - case ByteCode.INVOKEINTERFACE: - index = getOperand(bci + 1, 2); - FieldOrMethodRef m = (FieldOrMethodRef) itsConstantPool.getConstantData(index); - String methodType = m.getType(); - String methodName = m.getName(); - int parameterCount = sizeOfParameters(methodType) >>> 16; - for (int i = 0; i < parameterCount; i++) { - pop(); - } - if (bc != ByteCode.INVOKESTATIC) { - int instType = pop(); - int tag = TypeInfo.getTag(instType); - if (tag == TypeInfo.UNINITIALIZED_VARIABLE(0) - || tag == TypeInfo.UNINITIALIZED_THIS) { - if ("".equals(methodName)) { - int newType; - if (tag == TypeInfo.UNINITIALIZED_VARIABLE(0)) { - newType = TypeInfo.OBJECT(m.getClassName(), itsConstantPool); - } else { - newType = TypeInfo.OBJECT(itsThisClassIndex); - } - initializeTypeInfo(instType, newType); - } else { - throw new IllegalStateException("bad instance"); - } - } - } - int rParen = methodType.indexOf(')'); - String returnType = methodType.substring(rParen + 1); - returnType = descriptorToInternalName(returnType); - if (!returnType.equals("V")) { - push(TypeInfo.fromType(returnType, itsConstantPool)); - } - break; - case ByteCode.INVOKEDYNAMIC: - index = getOperand(bci + 1, 2); - methodType = (String) itsConstantPool.getConstantData(index); - parameterCount = sizeOfParameters(methodType) >>> 16; - for (int i = 0; i < parameterCount; i++) { - pop(); - } - rParen = methodType.indexOf(')'); - returnType = methodType.substring(rParen + 1); - returnType = descriptorToInternalName(returnType); - if (!returnType.equals("V")) { - push(TypeInfo.fromType(returnType, itsConstantPool)); - } - break; - case ByteCode.GETFIELD: - pop(); - // fall through - case ByteCode.GETSTATIC: - index = getOperand(bci + 1, 2); - FieldOrMethodRef f = (FieldOrMethodRef) itsConstantPool.getConstantData(index); - String fieldType = descriptorToInternalName(f.getType()); - push(TypeInfo.fromType(fieldType, itsConstantPool)); - break; - case ByteCode.DUP: - type = pop(); - push(type); - push(type); - break; - case ByteCode.DUP_X1: - type = pop(); - type2 = pop(); - push(type); - push(type2); - push(type); - break; - case ByteCode.DUP_X2: - type = pop(); - lType = pop2(); - push(type); - push2(lType); - push(type); - break; - case ByteCode.DUP2: - lType = pop2(); - push2(lType); - push2(lType); - break; - case ByteCode.DUP2_X1: - lType = pop2(); - type = pop(); - push2(lType); - push(type); - push2(lType); - break; - case ByteCode.DUP2_X2: - lType = pop2(); - lType2 = pop2(); - push2(lType); - push2(lType2); - push2(lType); - break; - case ByteCode.TABLESWITCH: - int switchStart = bci + 1 + (3 & ~bci); - int low = getOperand(switchStart + 4, 4); - int high = getOperand(switchStart + 8, 4); - length = 4 * (high - low + 4) + switchStart - bci; - pop(); - break; - case ByteCode.AALOAD: - pop(); - int typeIndex = pop() >>> 8; - className = (String) itsConstantPool.getConstantData(typeIndex); - String arrayType = className; - if (arrayType.charAt(0) != '[') { - throw new IllegalStateException("bad array type"); - } - String elementDesc = arrayType.substring(1); - String elementType = descriptorToInternalName(elementDesc); - typeIndex = itsConstantPool.addClass(elementType); - push(TypeInfo.OBJECT(typeIndex)); - break; - case ByteCode.WIDE: - // Alters behaviour of next instruction - wide = true; - break; - case ByteCode.MULTIANEWARRAY: - case ByteCode.LOOKUPSWITCH: - // Currently not used in any part of Rhino, so ignore it - case ByteCode.JSR: // TODO: JSR is deprecated - case ByteCode.RET: - case ByteCode.JSR_W: - default: - throw new IllegalArgumentException("bad opcode: " + bc); - } - - if (length == 0) { - length = opcodeLength(bc, wide); - } - if (wide && bc != ByteCode.WIDE) { - wide = false; - } - return length; - } - - private void executeALoad(int localIndex) { - int type = getLocal(localIndex); - int tag = TypeInfo.getTag(type); - if (tag == TypeInfo.OBJECT_TAG - || tag == TypeInfo.UNINITIALIZED_THIS - || tag == TypeInfo.UNINITIALIZED_VAR_TAG - || tag == TypeInfo.NULL) { - push(type); - } else { - throw new IllegalStateException( - "bad local variable type: " + type + " at index: " + localIndex); - } - } - - private void executeAStore(int localIndex) { - setLocal(localIndex, pop()); - } - - private void executeStore(int localIndex, int typeInfo) { - pop(); - setLocal(localIndex, typeInfo); - } - - /** - * Change an UNINITIALIZED_OBJECT or UNINITIALIZED_THIS to the proper type of the object. - * This occurs when the proper constructor is invoked. - */ - private void initializeTypeInfo(int prevType, int newType) { - initializeTypeInfo(prevType, newType, locals, localsTop); - initializeTypeInfo(prevType, newType, stack, stackTop); - } - - private void initializeTypeInfo(int prevType, int newType, int[] data, int dataTop) { - for (int i = 0; i < dataTop; i++) { - if (data[i] == prevType) { - data[i] = newType; - } - } - } - - private int getLocal(int localIndex) { - if (localIndex < localsTop) { - return locals[localIndex]; - } - return TypeInfo.TOP; - } - - private void setLocal(int localIndex, int typeInfo) { - if (localIndex >= localsTop) { - int[] tmp = new int[localIndex + 1]; - System.arraycopy(locals, 0, tmp, 0, localsTop); - locals = tmp; - localsTop = localIndex + 1; - } - locals[localIndex] = typeInfo; - } - - private void push(int typeInfo) { - if (stackTop == stack.length) { - int[] tmp = new int[Math.max(stackTop * 2, 4)]; - System.arraycopy(stack, 0, tmp, 0, stackTop); - stack = tmp; - } - stack[stackTop++] = typeInfo; - } - - private int pop() { - return stack[--stackTop]; - } - - /** - * Push two words onto the op stack. - * - *

This is only meant to be used as a complement to pop2(), and both methods are helpers - * for the more complex DUP operations. - */ - private void push2(long typeInfo) { - push((int) (typeInfo & 0xFFFFFF)); - typeInfo >>>= 32; - if (typeInfo != 0) { - push((int) (typeInfo & 0xFFFFFF)); - } - } - - /** - * Pop two words from the op stack. - * - *

If the top of the stack is a DOUBLE or LONG, then the bottom 32 bits reflects the - * appropriate type and the top 32 bits are 0. Otherwise, the top 32 bits are the first word - * on the stack and the lower 32 bits are the second word on the stack. - */ - private long pop2() { - long type = pop(); - if (TypeInfo.isTwoWords((int) type)) { - return type; - } - return type << 32 | (pop() & 0xFFFFFF); - } - - private void clearStack() { - stackTop = 0; - } - - /** - * Compute the output size of the stack map table. - * - *

Because this would share much in common with actual writing of the stack map table, we - * instead just write the stack map table to a buffer and return the size from it. The - * buffer is later used in the actual writing of bytecode. - */ - int computeWriteSize() { - // Allocate a buffer that can handle the worst case size of the - // stack map to prevent lots of reallocations. - int writeSize = getWorstCaseWriteSize(); - rawStackMap = new byte[writeSize]; - computeRawStackMap(); - return rawStackMapTop + 2; - } - - int write(byte[] data, int offset) { - offset = putInt32(rawStackMapTop + 2, data, offset); - offset = putInt16(superBlocks.length - 1, data, offset); - System.arraycopy(rawStackMap, 0, data, offset, rawStackMapTop); - return offset + rawStackMapTop; - } - - /** Compute a space-optimal stack map table. */ - private void computeRawStackMap() { - SuperBlock prev = superBlocks[0]; - int[] prevLocals = prev.getTrimmedLocals(); - int prevOffset = -1; - for (int i = 1; i < superBlocks.length; i++) { - SuperBlock current = superBlocks[i]; - int[] currentLocals = current.getTrimmedLocals(); - int[] currentStack = current.getStack(); - int offsetDelta = current.getStart() - prevOffset - 1; - - if (currentStack.length == 0) { - int last = - prevLocals.length > currentLocals.length - ? currentLocals.length - : prevLocals.length; - int delta = Math.abs(prevLocals.length - currentLocals.length); - int j; - // Compare locals until one is different or the end of a - // local variable array is reached - for (j = 0; j < last; j++) { - if (prevLocals[j] != currentLocals[j]) { - break; - } - } - if (j == currentLocals.length && delta == 0) { - // All of the compared locals are equal and the local - // arrays are of equal size - writeSameFrame(offsetDelta); - } else if (j == currentLocals.length && delta <= 3) { - // All of the compared locals are equal and the current - // frame has less locals than the previous frame - writeChopFrame(delta, offsetDelta); - } else if (j == prevLocals.length && delta <= 3) { - // All of the compared locals are equal and the current - // frame has more locals than the previous frame - writeAppendFrame(currentLocals, delta, offsetDelta); - } else { - // Not all locals were compared were equal, so a full - // frame is necessary - writeFullFrame(currentLocals, currentStack, offsetDelta); - } - } else if (currentStack.length == 1) { - if (Arrays.equals(prevLocals, currentLocals)) { - writeSameLocalsOneStackItemFrame(currentStack, offsetDelta); - } else { - // Output a full frame, since no other frame types have - // one operand stack item. - writeFullFrame(currentLocals, currentStack, offsetDelta); - } - } else { - // Any stack map frame that has more than one operand stack - // item has to be a full frame. All other frame types have - // at most one item on the stack. - writeFullFrame(currentLocals, currentStack, offsetDelta); - } - - prev = current; - prevLocals = currentLocals; - prevOffset = current.getStart(); - } - } - - /** - * Get the worst case write size of the stack map table. - * - *

This computes how much full frames would take, if each full frame contained the - * maximum number of locals and stack operands, and each verification type was 3 bytes. - */ - private int getWorstCaseWriteSize() { - return (superBlocks.length - 1) * (7 + itsMaxLocals * 3 + itsMaxStack * 3); - } - - private void writeSameFrame(int offsetDelta) { - if (offsetDelta <= 63) { - // Output a same_frame frame. Despite the name, - // the operand stack may differ, but the current - // operand stack must be empty. - rawStackMap[rawStackMapTop++] = (byte) offsetDelta; - } else { - // Output a same_frame_extended frame. Similar to - // the above, except with a larger offset delta. - rawStackMap[rawStackMapTop++] = (byte) 251; - rawStackMapTop = putInt16(offsetDelta, rawStackMap, rawStackMapTop); - } - } - - private void writeSameLocalsOneStackItemFrame(int[] stack, int offsetDelta) { - if (offsetDelta <= 63) { - // Output a same_locals_1_stack_item frame. Similar - // to same_frame, only with one item on the operand - // stack instead of zero. - rawStackMap[rawStackMapTop++] = (byte) (64 + offsetDelta); - } else { - // Output a same_locals_1_stack_item_extended frame. - // Similar to same_frame_extended, only with one - // item on the operand stack instead of zero. - rawStackMap[rawStackMapTop++] = (byte) 247; - rawStackMapTop = putInt16(offsetDelta, rawStackMap, rawStackMapTop); - } - writeType(stack[0]); - } - - private void writeFullFrame(int[] locals, int[] stack, int offsetDelta) { - rawStackMap[rawStackMapTop++] = (byte) 255; - rawStackMapTop = putInt16(offsetDelta, rawStackMap, rawStackMapTop); - rawStackMapTop = putInt16(locals.length, rawStackMap, rawStackMapTop); - writeTypes(locals); - rawStackMapTop = putInt16(stack.length, rawStackMap, rawStackMapTop); - writeTypes(stack); - } - - private void writeAppendFrame(int[] locals, int localsDelta, int offsetDelta) { - int start = locals.length - localsDelta; - rawStackMap[rawStackMapTop++] = (byte) (251 + localsDelta); - rawStackMapTop = putInt16(offsetDelta, rawStackMap, rawStackMapTop); - rawStackMapTop = writeTypes(locals, start); - } - - private void writeChopFrame(int localsDelta, int offsetDelta) { - rawStackMap[rawStackMapTop++] = (byte) (251 - localsDelta); - rawStackMapTop = putInt16(offsetDelta, rawStackMap, rawStackMapTop); - } - - private int writeTypes(int[] types) { - return writeTypes(types, 0); - } - - private int writeTypes(int[] types, int start) { - for (int i = start; i < types.length; i++) { - rawStackMapTop = writeType(types[i]); - } - return rawStackMapTop; - } - - private int writeType(int type) { - int tag = type & 0xFF; - rawStackMap[rawStackMapTop++] = (byte) tag; - if (tag == TypeInfo.OBJECT_TAG || tag == TypeInfo.UNINITIALIZED_VAR_TAG) { - rawStackMapTop = putInt16(type >>> 8, rawStackMap, rawStackMapTop); - } - return rawStackMapTop; - } - - // Intermediate operand stack and local variable state. During - // execution of a block, these are initialized to copies of the initial - // block type state and are modified by the actual stack/local - // emulation. - private int[] locals; - private int localsTop; - private int[] stack; - private int stackTop; - - private SuperBlock[] workList; - private int workListTop; - - private SuperBlock[] superBlocks; - - private byte[] rawStackMap; - private int rawStackMapTop; - - private boolean wide; - - static final boolean DEBUGSTACKMAP = false; - } - - /** Convert a newarray operand into an internal type. */ - private static char arrayTypeToName(int type) { - switch (type) { - case ByteCode.T_BOOLEAN: - return 'Z'; - case ByteCode.T_CHAR: - return 'C'; - case ByteCode.T_FLOAT: - return 'F'; - case ByteCode.T_DOUBLE: - return 'D'; - case ByteCode.T_BYTE: - return 'B'; - case ByteCode.T_SHORT: - return 'S'; - case ByteCode.T_INT: - return 'I'; - case ByteCode.T_LONG: - return 'J'; - default: - throw new IllegalArgumentException("bad operand"); - } - } - - /** - * Convert a class descriptor into an internal name. - * - *

For example, descriptor Ljava/lang/Object; becomes java/lang/Object. - */ - private static String classDescriptorToInternalName(String descriptor) { - return descriptor.substring(1, descriptor.length() - 1); - } - - /** - * Convert a non-method type descriptor into an internal type. - * - * @param descriptor the simple type descriptor to convert - */ - private static String descriptorToInternalName(String descriptor) { - switch (descriptor.charAt(0)) { - case 'B': - case 'C': - case 'D': - case 'F': - case 'I': - case 'J': - case 'S': - case 'Z': - case 'V': - case '[': - return descriptor; - case 'L': - return classDescriptorToInternalName(descriptor); - default: - throw new IllegalArgumentException("bad descriptor:" + descriptor); - } - } - - /** - * Compute the initial local variable array for the current method. - * - *

Creates an array of the size of the method's max locals, regardless of the number of - * parameters in the method. - */ - private int[] createInitialLocals() { - int[] initialLocals = new int[itsMaxLocals]; - int localsTop = 0; - // Instance methods require the first local variable in the array - // to be "this". However, if the method being created is a - // constructor, aka the method is , then the type of "this" - // should be StackMapTable.UNINITIALIZED_THIS - if ((itsCurrentMethod.getFlags() & ACC_STATIC) == 0) { - if ("".equals(itsCurrentMethod.getName())) { - initialLocals[localsTop++] = TypeInfo.UNINITIALIZED_THIS; - } else { - initialLocals[localsTop++] = TypeInfo.OBJECT(itsThisClassIndex); - } - } - - // No error checking should be necessary, sizeOfParameters does this - String type = itsCurrentMethod.getType(); - int lParenIndex = type.indexOf('('); - int rParenIndex = type.indexOf(')'); - if (lParenIndex != 0 || rParenIndex < 0) { - throw new IllegalArgumentException("bad method type"); - } - int start = lParenIndex + 1; - StringBuilder paramType = new StringBuilder(); - while (start < rParenIndex) { - switch (type.charAt(start)) { - case 'B': - case 'C': - case 'D': - case 'F': - case 'I': - case 'J': - case 'S': - case 'Z': - paramType.append(type.charAt(start)); - ++start; - break; - case 'L': - int end = type.indexOf(';', start) + 1; - String name = type.substring(start, end); - paramType.append(name); - start = end; - break; - case '[': - paramType.append('['); - ++start; - continue; - } - String internalType = descriptorToInternalName(paramType.toString()); - int typeInfo = TypeInfo.fromType(internalType, itsConstantPool); - initialLocals[localsTop++] = typeInfo; - if (TypeInfo.isTwoWords(typeInfo)) { - localsTop++; - } - paramType.setLength(0); - } - return initialLocals; - } - - /** - * Write the class file to the OutputStream. - * - * @param oStream the stream to write to - * @throws IOException if writing to the stream produces an exception - */ - public void write(OutputStream oStream) throws IOException { - byte[] array = toByteArray(); - oStream.write(array); - } - - private int getWriteSize() { - int size = 0; - - if (itsSourceFileNameIndex != 0) { - itsConstantPool.addUtf8("SourceFile"); - } - - size += 8; // writeLong(FileHeaderConstant); - size += itsConstantPool.getWriteSize(); - size += 2; // writeShort(itsFlags); - size += 2; // writeShort(itsThisClassIndex); - size += 2; // writeShort(itsSuperClassIndex); - size += 2; // writeShort(itsInterfaces.size()); - size += 2 * itsInterfaces.size(); - - size += 2; // writeShort(itsFields.size()); - for (int i = 0; i < itsFields.size(); i++) { - size += ((ClassFileField) (itsFields.get(i))).getWriteSize(); - } - - size += 2; // writeShort(itsMethods.size()); - for (int i = 0; i < itsMethods.size(); i++) { - size += ((ClassFileMethod) (itsMethods.get(i))).getWriteSize(); - } - - size += 2; // writeShort(1); attributes count, could be zero - if (itsSourceFileNameIndex != 0) { - size += 2; // writeShort(sourceFileAttributeNameIndex); - size += 4; // writeInt(2); - size += 2; // writeShort(itsSourceFileNameIndex); - } - if (itsBootstrapMethods != null) { - size += 2; // writeShort(bootstrapMethodsAttrNameIndex); - size += 4; // writeInt(itsBootstrapMethodsLength); - size += 2; // writeShort(bootstrapMethods.size()); - size += itsBootstrapMethodsLength; - } - - return size; - } - - /** Get the class file as array of bytesto the OutputStream. */ - public byte[] toByteArray() { - short bootstrapMethodsAttrNameIndex = 0; - int attributeCount = 0; - - short sourceFileAttributeNameIndex = 0; - if (itsBootstrapMethods != null) { - ++attributeCount; - bootstrapMethodsAttrNameIndex = itsConstantPool.addUtf8("BootstrapMethods"); - } - - if (itsSourceFileNameIndex != 0) { - ++attributeCount; - sourceFileAttributeNameIndex = itsConstantPool.addUtf8("SourceFile"); - } - - // Don't calculate the data size until we know how many bootstrap - // methods there will be. - int offset = 0; - int dataSize = getWriteSize(); - byte[] data = new byte[dataSize]; - - offset = putInt32(FileHeaderConstant, data, offset); - offset = putInt16(MinorVersion, data, offset); - offset = putInt16(MajorVersion, data, offset); - offset = itsConstantPool.write(data, offset); - offset = putInt16(itsFlags, data, offset); - offset = putInt16(itsThisClassIndex, data, offset); - offset = putInt16(itsSuperClassIndex, data, offset); - offset = putInt16(itsInterfaces.size(), data, offset); - for (int i = 0; i < itsInterfaces.size(); i++) { - int interfaceIndex = ((Short) (itsInterfaces.get(i))).shortValue(); - offset = putInt16(interfaceIndex, data, offset); - } - offset = putInt16(itsFields.size(), data, offset); - for (int i = 0; i < itsFields.size(); i++) { - ClassFileField field = (ClassFileField) itsFields.get(i); - offset = field.write(data, offset); - } - offset = putInt16(itsMethods.size(), data, offset); - for (int i = 0; i < itsMethods.size(); i++) { - ClassFileMethod method = (ClassFileMethod) itsMethods.get(i); - offset = method.write(data, offset); - } - offset = putInt16(attributeCount, data, offset); // attributes count - if (itsBootstrapMethods != null) { - offset = putInt16(bootstrapMethodsAttrNameIndex, data, offset); - offset = putInt32(itsBootstrapMethodsLength + 2, data, offset); - offset = putInt16(itsBootstrapMethods.size(), data, offset); - for (int i = 0; i < itsBootstrapMethods.size(); i++) { - BootstrapEntry entry = (BootstrapEntry) itsBootstrapMethods.get(i); - System.arraycopy(entry.code, 0, data, offset, entry.code.length); - offset += entry.code.length; - } - } - if (itsSourceFileNameIndex != 0) { - offset = putInt16(sourceFileAttributeNameIndex, data, offset); - offset = putInt32(2, data, offset); - offset = putInt16(itsSourceFileNameIndex, data, offset); - } - - if (offset != dataSize) { - // Check getWriteSize is consistent with write! - throw new RuntimeException(); - } - - return data; - } - - static int putInt64(long value, byte[] array, int offset) { - offset = putInt32((int) (value >>> 32), array, offset); - return putInt32((int) value, array, offset); - } - - private static void badStack(int value) { - String s; - if (value < 0) { - s = "Stack underflow: " + value; - } else { - s = "Too big stack: " + value; - } - throw new IllegalStateException(s); - } - - /* - Really weird. Returns an int with # parameters in hi 16 bits, and - stack difference removal of parameters from stack and pushing the - result (it does not take into account removal of this in case of - non-static methods). - If Java really supported references we wouldn't have to be this - perverted. - */ - private static int sizeOfParameters(String pString) { - int length = pString.length(); - int rightParenthesis = pString.lastIndexOf(')'); - if (3 <= length /* minimal signature takes at least 3 chars: ()V */ - && pString.charAt(0) == '(' - && 1 <= rightParenthesis - && rightParenthesis + 1 < length) { - boolean ok = true; - int index = 1; - int stackDiff = 0; - int count = 0; - stringLoop: - while (index != rightParenthesis) { - switch (pString.charAt(index)) { - default: - ok = false; - break stringLoop; - case 'J': - case 'D': - --stackDiff; - // fall through - case 'B': - case 'S': - case 'C': - case 'I': - case 'Z': - case 'F': - --stackDiff; - ++count; - ++index; - continue; - case '[': - ++index; - int c = pString.charAt(index); - while (c == '[') { - ++index; - c = pString.charAt(index); - } - switch (c) { - default: - ok = false; - break stringLoop; - case 'J': - case 'D': - case 'B': - case 'S': - case 'C': - case 'I': - case 'Z': - case 'F': - --stackDiff; - ++count; - ++index; - continue; - case 'L': - // fall through - } - // fall through - case 'L': - { - --stackDiff; - ++count; - ++index; - int semicolon = pString.indexOf(';', index); - if (!(index + 1 <= semicolon && semicolon < rightParenthesis)) { - ok = false; - break stringLoop; - } - index = semicolon + 1; - continue; - } - } - } - if (ok) { - switch (pString.charAt(rightParenthesis + 1)) { - default: - ok = false; - break; - case 'J': - case 'D': - ++stackDiff; - // fall through - case 'B': - case 'S': - case 'C': - case 'I': - case 'Z': - case 'F': - case 'L': - case '[': - ++stackDiff; - // fall through - case 'V': - break; - } - if (ok) { - return ((count << 16) | (0xFFFF & stackDiff)); - } - } - } - throw new IllegalArgumentException("Bad parameter signature: " + pString); - } - - static int putInt16(int value, byte[] array, int offset) { - array[offset + 0] = (byte) (value >>> 8); - array[offset + 1] = (byte) value; - return offset + 2; - } - - static int putInt32(int value, byte[] array, int offset) { - array[offset + 0] = (byte) (value >>> 24); - array[offset + 1] = (byte) (value >>> 16); - array[offset + 2] = (byte) (value >>> 8); - array[offset + 3] = (byte) value; - return offset + 4; - } - - /** - * Size of a bytecode instruction, counting the opcode and its operands. - * - *

This is different from opcodeCount, since opcodeCount counts logical operands. - */ - private static int opcodeLength(int opcode, boolean wide) { - switch (opcode) { - case ByteCode.AALOAD: - case ByteCode.AASTORE: - case ByteCode.ACONST_NULL: - case ByteCode.ALOAD_0: - case ByteCode.ALOAD_1: - case ByteCode.ALOAD_2: - case ByteCode.ALOAD_3: - case ByteCode.ARETURN: - case ByteCode.ARRAYLENGTH: - case ByteCode.ASTORE_0: - case ByteCode.ASTORE_1: - case ByteCode.ASTORE_2: - case ByteCode.ASTORE_3: - case ByteCode.ATHROW: - case ByteCode.BALOAD: - case ByteCode.BASTORE: - case ByteCode.BREAKPOINT: - case ByteCode.CALOAD: - case ByteCode.CASTORE: - case ByteCode.D2F: - case ByteCode.D2I: - case ByteCode.D2L: - case ByteCode.DADD: - case ByteCode.DALOAD: - case ByteCode.DASTORE: - case ByteCode.DCMPG: - case ByteCode.DCMPL: - case ByteCode.DCONST_0: - case ByteCode.DCONST_1: - case ByteCode.DDIV: - case ByteCode.DLOAD_0: - case ByteCode.DLOAD_1: - case ByteCode.DLOAD_2: - case ByteCode.DLOAD_3: - case ByteCode.DMUL: - case ByteCode.DNEG: - case ByteCode.DREM: - case ByteCode.DRETURN: - case ByteCode.DSTORE_0: - case ByteCode.DSTORE_1: - case ByteCode.DSTORE_2: - case ByteCode.DSTORE_3: - case ByteCode.DSUB: - case ByteCode.DUP: - case ByteCode.DUP2: - case ByteCode.DUP2_X1: - case ByteCode.DUP2_X2: - case ByteCode.DUP_X1: - case ByteCode.DUP_X2: - case ByteCode.F2D: - case ByteCode.F2I: - case ByteCode.F2L: - case ByteCode.FADD: - case ByteCode.FALOAD: - case ByteCode.FASTORE: - case ByteCode.FCMPG: - case ByteCode.FCMPL: - case ByteCode.FCONST_0: - case ByteCode.FCONST_1: - case ByteCode.FCONST_2: - case ByteCode.FDIV: - case ByteCode.FLOAD_0: - case ByteCode.FLOAD_1: - case ByteCode.FLOAD_2: - case ByteCode.FLOAD_3: - case ByteCode.FMUL: - case ByteCode.FNEG: - case ByteCode.FREM: - case ByteCode.FRETURN: - case ByteCode.FSTORE_0: - case ByteCode.FSTORE_1: - case ByteCode.FSTORE_2: - case ByteCode.FSTORE_3: - case ByteCode.FSUB: - case ByteCode.I2B: - case ByteCode.I2C: - case ByteCode.I2D: - case ByteCode.I2F: - case ByteCode.I2L: - case ByteCode.I2S: - case ByteCode.IADD: - case ByteCode.IALOAD: - case ByteCode.IAND: - case ByteCode.IASTORE: - case ByteCode.ICONST_0: - case ByteCode.ICONST_1: - case ByteCode.ICONST_2: - case ByteCode.ICONST_3: - case ByteCode.ICONST_4: - case ByteCode.ICONST_5: - case ByteCode.ICONST_M1: - case ByteCode.IDIV: - case ByteCode.ILOAD_0: - case ByteCode.ILOAD_1: - case ByteCode.ILOAD_2: - case ByteCode.ILOAD_3: - case ByteCode.IMPDEP1: - case ByteCode.IMPDEP2: - case ByteCode.IMUL: - case ByteCode.INEG: - case ByteCode.IOR: - case ByteCode.IREM: - case ByteCode.IRETURN: - case ByteCode.ISHL: - case ByteCode.ISHR: - case ByteCode.ISTORE_0: - case ByteCode.ISTORE_1: - case ByteCode.ISTORE_2: - case ByteCode.ISTORE_3: - case ByteCode.ISUB: - case ByteCode.IUSHR: - case ByteCode.IXOR: - case ByteCode.L2D: - case ByteCode.L2F: - case ByteCode.L2I: - case ByteCode.LADD: - case ByteCode.LALOAD: - case ByteCode.LAND: - case ByteCode.LASTORE: - case ByteCode.LCMP: - case ByteCode.LCONST_0: - case ByteCode.LCONST_1: - case ByteCode.LDIV: - case ByteCode.LLOAD_0: - case ByteCode.LLOAD_1: - case ByteCode.LLOAD_2: - case ByteCode.LLOAD_3: - case ByteCode.LMUL: - case ByteCode.LNEG: - case ByteCode.LOR: - case ByteCode.LREM: - case ByteCode.LRETURN: - case ByteCode.LSHL: - case ByteCode.LSHR: - case ByteCode.LSTORE_0: - case ByteCode.LSTORE_1: - case ByteCode.LSTORE_2: - case ByteCode.LSTORE_3: - case ByteCode.LSUB: - case ByteCode.LUSHR: - case ByteCode.LXOR: - case ByteCode.MONITORENTER: - case ByteCode.MONITOREXIT: - case ByteCode.NOP: - case ByteCode.POP: - case ByteCode.POP2: - case ByteCode.RETURN: - case ByteCode.SALOAD: - case ByteCode.SASTORE: - case ByteCode.SWAP: - case ByteCode.WIDE: - return 1; - case ByteCode.BIPUSH: - case ByteCode.LDC: - case ByteCode.NEWARRAY: - return 2; - case ByteCode.ALOAD: - case ByteCode.ASTORE: - case ByteCode.DLOAD: - case ByteCode.DSTORE: - case ByteCode.FLOAD: - case ByteCode.FSTORE: - case ByteCode.ILOAD: - case ByteCode.ISTORE: - case ByteCode.LLOAD: - case ByteCode.LSTORE: - case ByteCode.RET: - return wide ? 3 : 2; - - case ByteCode.ANEWARRAY: - case ByteCode.CHECKCAST: - case ByteCode.GETFIELD: - case ByteCode.GETSTATIC: - case ByteCode.GOTO: - case ByteCode.IFEQ: - case ByteCode.IFGE: - case ByteCode.IFGT: - case ByteCode.IFLE: - case ByteCode.IFLT: - case ByteCode.IFNE: - case ByteCode.IFNONNULL: - case ByteCode.IFNULL: - case ByteCode.IF_ACMPEQ: - case ByteCode.IF_ACMPNE: - case ByteCode.IF_ICMPEQ: - case ByteCode.IF_ICMPGE: - case ByteCode.IF_ICMPGT: - case ByteCode.IF_ICMPLE: - case ByteCode.IF_ICMPLT: - case ByteCode.IF_ICMPNE: - case ByteCode.INSTANCEOF: - case ByteCode.INVOKESPECIAL: - case ByteCode.INVOKESTATIC: - case ByteCode.INVOKEVIRTUAL: - case ByteCode.JSR: - case ByteCode.LDC_W: - case ByteCode.LDC2_W: - case ByteCode.NEW: - case ByteCode.PUTFIELD: - case ByteCode.PUTSTATIC: - case ByteCode.SIPUSH: - return 3; - - case ByteCode.IINC: - return wide ? 5 : 3; - - case ByteCode.MULTIANEWARRAY: - return 4; - - case ByteCode.GOTO_W: - case ByteCode.INVOKEINTERFACE: - case ByteCode.INVOKEDYNAMIC: - case ByteCode.JSR_W: - return 5; - - /* - case ByteCode.LOOKUPSWITCH: - case ByteCode.TABLESWITCH: - return -1; - */ - } - throw new IllegalArgumentException("Bad opcode: " + opcode); - } - - /** Number of operands accompanying the opcode. */ - private static int opcodeCount(int opcode) { - switch (opcode) { - case ByteCode.AALOAD: - case ByteCode.AASTORE: - case ByteCode.ACONST_NULL: - case ByteCode.ALOAD_0: - case ByteCode.ALOAD_1: - case ByteCode.ALOAD_2: - case ByteCode.ALOAD_3: - case ByteCode.ARETURN: - case ByteCode.ARRAYLENGTH: - case ByteCode.ASTORE_0: - case ByteCode.ASTORE_1: - case ByteCode.ASTORE_2: - case ByteCode.ASTORE_3: - case ByteCode.ATHROW: - case ByteCode.BALOAD: - case ByteCode.BASTORE: - case ByteCode.BREAKPOINT: - case ByteCode.CALOAD: - case ByteCode.CASTORE: - case ByteCode.D2F: - case ByteCode.D2I: - case ByteCode.D2L: - case ByteCode.DADD: - case ByteCode.DALOAD: - case ByteCode.DASTORE: - case ByteCode.DCMPG: - case ByteCode.DCMPL: - case ByteCode.DCONST_0: - case ByteCode.DCONST_1: - case ByteCode.DDIV: - case ByteCode.DLOAD_0: - case ByteCode.DLOAD_1: - case ByteCode.DLOAD_2: - case ByteCode.DLOAD_3: - case ByteCode.DMUL: - case ByteCode.DNEG: - case ByteCode.DREM: - case ByteCode.DRETURN: - case ByteCode.DSTORE_0: - case ByteCode.DSTORE_1: - case ByteCode.DSTORE_2: - case ByteCode.DSTORE_3: - case ByteCode.DSUB: - case ByteCode.DUP: - case ByteCode.DUP2: - case ByteCode.DUP2_X1: - case ByteCode.DUP2_X2: - case ByteCode.DUP_X1: - case ByteCode.DUP_X2: - case ByteCode.F2D: - case ByteCode.F2I: - case ByteCode.F2L: - case ByteCode.FADD: - case ByteCode.FALOAD: - case ByteCode.FASTORE: - case ByteCode.FCMPG: - case ByteCode.FCMPL: - case ByteCode.FCONST_0: - case ByteCode.FCONST_1: - case ByteCode.FCONST_2: - case ByteCode.FDIV: - case ByteCode.FLOAD_0: - case ByteCode.FLOAD_1: - case ByteCode.FLOAD_2: - case ByteCode.FLOAD_3: - case ByteCode.FMUL: - case ByteCode.FNEG: - case ByteCode.FREM: - case ByteCode.FRETURN: - case ByteCode.FSTORE_0: - case ByteCode.FSTORE_1: - case ByteCode.FSTORE_2: - case ByteCode.FSTORE_3: - case ByteCode.FSUB: - case ByteCode.I2B: - case ByteCode.I2C: - case ByteCode.I2D: - case ByteCode.I2F: - case ByteCode.I2L: - case ByteCode.I2S: - case ByteCode.IADD: - case ByteCode.IALOAD: - case ByteCode.IAND: - case ByteCode.IASTORE: - case ByteCode.ICONST_0: - case ByteCode.ICONST_1: - case ByteCode.ICONST_2: - case ByteCode.ICONST_3: - case ByteCode.ICONST_4: - case ByteCode.ICONST_5: - case ByteCode.ICONST_M1: - case ByteCode.IDIV: - case ByteCode.ILOAD_0: - case ByteCode.ILOAD_1: - case ByteCode.ILOAD_2: - case ByteCode.ILOAD_3: - case ByteCode.IMPDEP1: - case ByteCode.IMPDEP2: - case ByteCode.IMUL: - case ByteCode.INEG: - case ByteCode.IOR: - case ByteCode.IREM: - case ByteCode.IRETURN: - case ByteCode.ISHL: - case ByteCode.ISHR: - case ByteCode.ISTORE_0: - case ByteCode.ISTORE_1: - case ByteCode.ISTORE_2: - case ByteCode.ISTORE_3: - case ByteCode.ISUB: - case ByteCode.IUSHR: - case ByteCode.IXOR: - case ByteCode.L2D: - case ByteCode.L2F: - case ByteCode.L2I: - case ByteCode.LADD: - case ByteCode.LALOAD: - case ByteCode.LAND: - case ByteCode.LASTORE: - case ByteCode.LCMP: - case ByteCode.LCONST_0: - case ByteCode.LCONST_1: - case ByteCode.LDIV: - case ByteCode.LLOAD_0: - case ByteCode.LLOAD_1: - case ByteCode.LLOAD_2: - case ByteCode.LLOAD_3: - case ByteCode.LMUL: - case ByteCode.LNEG: - case ByteCode.LOR: - case ByteCode.LREM: - case ByteCode.LRETURN: - case ByteCode.LSHL: - case ByteCode.LSHR: - case ByteCode.LSTORE_0: - case ByteCode.LSTORE_1: - case ByteCode.LSTORE_2: - case ByteCode.LSTORE_3: - case ByteCode.LSUB: - case ByteCode.LUSHR: - case ByteCode.LXOR: - case ByteCode.MONITORENTER: - case ByteCode.MONITOREXIT: - case ByteCode.NOP: - case ByteCode.POP: - case ByteCode.POP2: - case ByteCode.RETURN: - case ByteCode.SALOAD: - case ByteCode.SASTORE: - case ByteCode.SWAP: - case ByteCode.WIDE: - return 0; - case ByteCode.ALOAD: - case ByteCode.ANEWARRAY: - case ByteCode.ASTORE: - case ByteCode.BIPUSH: - case ByteCode.CHECKCAST: - case ByteCode.DLOAD: - case ByteCode.DSTORE: - case ByteCode.FLOAD: - case ByteCode.FSTORE: - case ByteCode.GETFIELD: - case ByteCode.GETSTATIC: - case ByteCode.GOTO: - case ByteCode.GOTO_W: - case ByteCode.IFEQ: - case ByteCode.IFGE: - case ByteCode.IFGT: - case ByteCode.IFLE: - case ByteCode.IFLT: - case ByteCode.IFNE: - case ByteCode.IFNONNULL: - case ByteCode.IFNULL: - case ByteCode.IF_ACMPEQ: - case ByteCode.IF_ACMPNE: - case ByteCode.IF_ICMPEQ: - case ByteCode.IF_ICMPGE: - case ByteCode.IF_ICMPGT: - case ByteCode.IF_ICMPLE: - case ByteCode.IF_ICMPLT: - case ByteCode.IF_ICMPNE: - case ByteCode.ILOAD: - case ByteCode.INSTANCEOF: - case ByteCode.INVOKEINTERFACE: - case ByteCode.INVOKESPECIAL: - case ByteCode.INVOKESTATIC: - case ByteCode.INVOKEVIRTUAL: - case ByteCode.ISTORE: - case ByteCode.JSR: - case ByteCode.JSR_W: - case ByteCode.LDC: - case ByteCode.LDC2_W: - case ByteCode.LDC_W: - case ByteCode.LLOAD: - case ByteCode.LSTORE: - case ByteCode.NEW: - case ByteCode.NEWARRAY: - case ByteCode.PUTFIELD: - case ByteCode.PUTSTATIC: - case ByteCode.RET: - case ByteCode.SIPUSH: - return 1; - - case ByteCode.IINC: - case ByteCode.MULTIANEWARRAY: - return 2; - - case ByteCode.LOOKUPSWITCH: - case ByteCode.TABLESWITCH: - return -1; - } - throw new IllegalArgumentException("Bad opcode: " + opcode); - } - - /** The effect on the operand stack of a given opcode. */ - private static int stackChange(int opcode) { - // For INVOKE... accounts only for popping this (unless static), - // ignoring parameters and return type - switch (opcode) { - case ByteCode.DASTORE: - case ByteCode.LASTORE: - return -4; - - case ByteCode.AASTORE: - case ByteCode.BASTORE: - case ByteCode.CASTORE: - case ByteCode.DCMPG: - case ByteCode.DCMPL: - case ByteCode.FASTORE: - case ByteCode.IASTORE: - case ByteCode.LCMP: - case ByteCode.SASTORE: - return -3; - - case ByteCode.DADD: - case ByteCode.DDIV: - case ByteCode.DMUL: - case ByteCode.DREM: - case ByteCode.DRETURN: - case ByteCode.DSTORE: - case ByteCode.DSTORE_0: - case ByteCode.DSTORE_1: - case ByteCode.DSTORE_2: - case ByteCode.DSTORE_3: - case ByteCode.DSUB: - case ByteCode.IF_ACMPEQ: - case ByteCode.IF_ACMPNE: - case ByteCode.IF_ICMPEQ: - case ByteCode.IF_ICMPGE: - case ByteCode.IF_ICMPGT: - case ByteCode.IF_ICMPLE: - case ByteCode.IF_ICMPLT: - case ByteCode.IF_ICMPNE: - case ByteCode.LADD: - case ByteCode.LAND: - case ByteCode.LDIV: - case ByteCode.LMUL: - case ByteCode.LOR: - case ByteCode.LREM: - case ByteCode.LRETURN: - case ByteCode.LSTORE: - case ByteCode.LSTORE_0: - case ByteCode.LSTORE_1: - case ByteCode.LSTORE_2: - case ByteCode.LSTORE_3: - case ByteCode.LSUB: - case ByteCode.LXOR: - case ByteCode.POP2: - return -2; - - case ByteCode.AALOAD: - case ByteCode.ARETURN: - case ByteCode.ASTORE: - case ByteCode.ASTORE_0: - case ByteCode.ASTORE_1: - case ByteCode.ASTORE_2: - case ByteCode.ASTORE_3: - case ByteCode.ATHROW: - case ByteCode.BALOAD: - case ByteCode.CALOAD: - case ByteCode.D2F: - case ByteCode.D2I: - case ByteCode.FADD: - case ByteCode.FALOAD: - case ByteCode.FCMPG: - case ByteCode.FCMPL: - case ByteCode.FDIV: - case ByteCode.FMUL: - case ByteCode.FREM: - case ByteCode.FRETURN: - case ByteCode.FSTORE: - case ByteCode.FSTORE_0: - case ByteCode.FSTORE_1: - case ByteCode.FSTORE_2: - case ByteCode.FSTORE_3: - case ByteCode.FSUB: - case ByteCode.GETFIELD: - case ByteCode.IADD: - case ByteCode.IALOAD: - case ByteCode.IAND: - case ByteCode.IDIV: - case ByteCode.IFEQ: - case ByteCode.IFGE: - case ByteCode.IFGT: - case ByteCode.IFLE: - case ByteCode.IFLT: - case ByteCode.IFNE: - case ByteCode.IFNONNULL: - case ByteCode.IFNULL: - case ByteCode.IMUL: - case ByteCode.INVOKEINTERFACE: // - case ByteCode.INVOKESPECIAL: // but needs to account for - case ByteCode.INVOKEVIRTUAL: // pops 'this' (unless static) - case ByteCode.IOR: - case ByteCode.IREM: - case ByteCode.IRETURN: - case ByteCode.ISHL: - case ByteCode.ISHR: - case ByteCode.ISTORE: - case ByteCode.ISTORE_0: - case ByteCode.ISTORE_1: - case ByteCode.ISTORE_2: - case ByteCode.ISTORE_3: - case ByteCode.ISUB: - case ByteCode.IUSHR: - case ByteCode.IXOR: - case ByteCode.L2F: - case ByteCode.L2I: - case ByteCode.LOOKUPSWITCH: - case ByteCode.LSHL: - case ByteCode.LSHR: - case ByteCode.LUSHR: - case ByteCode.MONITORENTER: - case ByteCode.MONITOREXIT: - case ByteCode.POP: - case ByteCode.PUTFIELD: - case ByteCode.SALOAD: - case ByteCode.TABLESWITCH: - return -1; - - case ByteCode.ANEWARRAY: - case ByteCode.ARRAYLENGTH: - case ByteCode.BREAKPOINT: - case ByteCode.CHECKCAST: - case ByteCode.D2L: - case ByteCode.DALOAD: - case ByteCode.DNEG: - case ByteCode.F2I: - case ByteCode.FNEG: - case ByteCode.GETSTATIC: - case ByteCode.GOTO: - case ByteCode.GOTO_W: - case ByteCode.I2B: - case ByteCode.I2C: - case ByteCode.I2F: - case ByteCode.I2S: - case ByteCode.IINC: - case ByteCode.IMPDEP1: - case ByteCode.IMPDEP2: - case ByteCode.INEG: - case ByteCode.INSTANCEOF: - case ByteCode.INVOKESTATIC: - case ByteCode.INVOKEDYNAMIC: - case ByteCode.L2D: - case ByteCode.LALOAD: - case ByteCode.LNEG: - case ByteCode.NEWARRAY: - case ByteCode.NOP: - case ByteCode.PUTSTATIC: - case ByteCode.RET: - case ByteCode.RETURN: - case ByteCode.SWAP: - case ByteCode.WIDE: - return 0; - - case ByteCode.ACONST_NULL: - case ByteCode.ALOAD: - case ByteCode.ALOAD_0: - case ByteCode.ALOAD_1: - case ByteCode.ALOAD_2: - case ByteCode.ALOAD_3: - case ByteCode.BIPUSH: - case ByteCode.DUP: - case ByteCode.DUP_X1: - case ByteCode.DUP_X2: - case ByteCode.F2D: - case ByteCode.F2L: - case ByteCode.FCONST_0: - case ByteCode.FCONST_1: - case ByteCode.FCONST_2: - case ByteCode.FLOAD: - case ByteCode.FLOAD_0: - case ByteCode.FLOAD_1: - case ByteCode.FLOAD_2: - case ByteCode.FLOAD_3: - case ByteCode.I2D: - case ByteCode.I2L: - case ByteCode.ICONST_0: - case ByteCode.ICONST_1: - case ByteCode.ICONST_2: - case ByteCode.ICONST_3: - case ByteCode.ICONST_4: - case ByteCode.ICONST_5: - case ByteCode.ICONST_M1: - case ByteCode.ILOAD: - case ByteCode.ILOAD_0: - case ByteCode.ILOAD_1: - case ByteCode.ILOAD_2: - case ByteCode.ILOAD_3: - case ByteCode.JSR: - case ByteCode.JSR_W: - case ByteCode.LDC: - case ByteCode.LDC_W: - case ByteCode.MULTIANEWARRAY: - case ByteCode.NEW: - case ByteCode.SIPUSH: - return 1; - - case ByteCode.DCONST_0: - case ByteCode.DCONST_1: - case ByteCode.DLOAD: - case ByteCode.DLOAD_0: - case ByteCode.DLOAD_1: - case ByteCode.DLOAD_2: - case ByteCode.DLOAD_3: - case ByteCode.DUP2: - case ByteCode.DUP2_X1: - case ByteCode.DUP2_X2: - case ByteCode.LCONST_0: - case ByteCode.LCONST_1: - case ByteCode.LDC2_W: - case ByteCode.LLOAD: - case ByteCode.LLOAD_0: - case ByteCode.LLOAD_1: - case ByteCode.LLOAD_2: - case ByteCode.LLOAD_3: - return 2; - } - throw new IllegalArgumentException("Bad opcode: " + opcode); - } - - /* - * Number of bytes of operands generated after the opcode. - * Not in use currently. - */ - /* - int extra(int opcode) - { - switch (opcode) { - case ByteCode.AALOAD: - case ByteCode.AASTORE: - case ByteCode.ACONST_NULL: - case ByteCode.ALOAD_0: - case ByteCode.ALOAD_1: - case ByteCode.ALOAD_2: - case ByteCode.ALOAD_3: - case ByteCode.ARETURN: - case ByteCode.ARRAYLENGTH: - case ByteCode.ASTORE_0: - case ByteCode.ASTORE_1: - case ByteCode.ASTORE_2: - case ByteCode.ASTORE_3: - case ByteCode.ATHROW: - case ByteCode.BALOAD: - case ByteCode.BASTORE: - case ByteCode.BREAKPOINT: - case ByteCode.CALOAD: - case ByteCode.CASTORE: - case ByteCode.D2F: - case ByteCode.D2I: - case ByteCode.D2L: - case ByteCode.DADD: - case ByteCode.DALOAD: - case ByteCode.DASTORE: - case ByteCode.DCMPG: - case ByteCode.DCMPL: - case ByteCode.DCONST_0: - case ByteCode.DCONST_1: - case ByteCode.DDIV: - case ByteCode.DLOAD_0: - case ByteCode.DLOAD_1: - case ByteCode.DLOAD_2: - case ByteCode.DLOAD_3: - case ByteCode.DMUL: - case ByteCode.DNEG: - case ByteCode.DREM: - case ByteCode.DRETURN: - case ByteCode.DSTORE_0: - case ByteCode.DSTORE_1: - case ByteCode.DSTORE_2: - case ByteCode.DSTORE_3: - case ByteCode.DSUB: - case ByteCode.DUP2: - case ByteCode.DUP2_X1: - case ByteCode.DUP2_X2: - case ByteCode.DUP: - case ByteCode.DUP_X1: - case ByteCode.DUP_X2: - case ByteCode.F2D: - case ByteCode.F2I: - case ByteCode.F2L: - case ByteCode.FADD: - case ByteCode.FALOAD: - case ByteCode.FASTORE: - case ByteCode.FCMPG: - case ByteCode.FCMPL: - case ByteCode.FCONST_0: - case ByteCode.FCONST_1: - case ByteCode.FCONST_2: - case ByteCode.FDIV: - case ByteCode.FLOAD_0: - case ByteCode.FLOAD_1: - case ByteCode.FLOAD_2: - case ByteCode.FLOAD_3: - case ByteCode.FMUL: - case ByteCode.FNEG: - case ByteCode.FREM: - case ByteCode.FRETURN: - case ByteCode.FSTORE_0: - case ByteCode.FSTORE_1: - case ByteCode.FSTORE_2: - case ByteCode.FSTORE_3: - case ByteCode.FSUB: - case ByteCode.I2B: - case ByteCode.I2C: - case ByteCode.I2D: - case ByteCode.I2F: - case ByteCode.I2L: - case ByteCode.I2S: - case ByteCode.IADD: - case ByteCode.IALOAD: - case ByteCode.IAND: - case ByteCode.IASTORE: - case ByteCode.ICONST_0: - case ByteCode.ICONST_1: - case ByteCode.ICONST_2: - case ByteCode.ICONST_3: - case ByteCode.ICONST_4: - case ByteCode.ICONST_5: - case ByteCode.ICONST_M1: - case ByteCode.IDIV: - case ByteCode.ILOAD_0: - case ByteCode.ILOAD_1: - case ByteCode.ILOAD_2: - case ByteCode.ILOAD_3: - case ByteCode.IMPDEP1: - case ByteCode.IMPDEP2: - case ByteCode.IMUL: - case ByteCode.INEG: - case ByteCode.IOR: - case ByteCode.IREM: - case ByteCode.IRETURN: - case ByteCode.ISHL: - case ByteCode.ISHR: - case ByteCode.ISTORE_0: - case ByteCode.ISTORE_1: - case ByteCode.ISTORE_2: - case ByteCode.ISTORE_3: - case ByteCode.ISUB: - case ByteCode.IUSHR: - case ByteCode.IXOR: - case ByteCode.L2D: - case ByteCode.L2F: - case ByteCode.L2I: - case ByteCode.LADD: - case ByteCode.LALOAD: - case ByteCode.LAND: - case ByteCode.LASTORE: - case ByteCode.LCMP: - case ByteCode.LCONST_0: - case ByteCode.LCONST_1: - case ByteCode.LDIV: - case ByteCode.LLOAD_0: - case ByteCode.LLOAD_1: - case ByteCode.LLOAD_2: - case ByteCode.LLOAD_3: - case ByteCode.LMUL: - case ByteCode.LNEG: - case ByteCode.LOR: - case ByteCode.LREM: - case ByteCode.LRETURN: - case ByteCode.LSHL: - case ByteCode.LSHR: - case ByteCode.LSTORE_0: - case ByteCode.LSTORE_1: - case ByteCode.LSTORE_2: - case ByteCode.LSTORE_3: - case ByteCode.LSUB: - case ByteCode.LUSHR: - case ByteCode.LXOR: - case ByteCode.MONITORENTER: - case ByteCode.MONITOREXIT: - case ByteCode.NOP: - case ByteCode.POP2: - case ByteCode.POP: - case ByteCode.RETURN: - case ByteCode.SALOAD: - case ByteCode.SASTORE: - case ByteCode.SWAP: - case ByteCode.WIDE: - return 0; - - case ByteCode.ALOAD: - case ByteCode.ASTORE: - case ByteCode.BIPUSH: - case ByteCode.DLOAD: - case ByteCode.DSTORE: - case ByteCode.FLOAD: - case ByteCode.FSTORE: - case ByteCode.ILOAD: - case ByteCode.ISTORE: - case ByteCode.LDC: - case ByteCode.LLOAD: - case ByteCode.LSTORE: - case ByteCode.NEWARRAY: - case ByteCode.RET: - return 1; - - case ByteCode.ANEWARRAY: - case ByteCode.CHECKCAST: - case ByteCode.GETFIELD: - case ByteCode.GETSTATIC: - case ByteCode.GOTO: - case ByteCode.IFEQ: - case ByteCode.IFGE: - case ByteCode.IFGT: - case ByteCode.IFLE: - case ByteCode.IFLT: - case ByteCode.IFNE: - case ByteCode.IFNONNULL: - case ByteCode.IFNULL: - case ByteCode.IF_ACMPEQ: - case ByteCode.IF_ACMPNE: - case ByteCode.IF_ICMPEQ: - case ByteCode.IF_ICMPGE: - case ByteCode.IF_ICMPGT: - case ByteCode.IF_ICMPLE: - case ByteCode.IF_ICMPLT: - case ByteCode.IF_ICMPNE: - case ByteCode.IINC: - case ByteCode.INSTANCEOF: - case ByteCode.INVOKEINTERFACE: - case ByteCode.INVOKESPECIAL: - case ByteCode.INVOKESTATIC: - case ByteCode.INVOKEVIRTUAL: - case ByteCode.JSR: - case ByteCode.LDC2_W: - case ByteCode.LDC_W: - case ByteCode.NEW: - case ByteCode.PUTFIELD: - case ByteCode.PUTSTATIC: - case ByteCode.SIPUSH: - return 2; - - case ByteCode.MULTIANEWARRAY: - return 3; - - case ByteCode.GOTO_W: - case ByteCode.JSR_W: - return 4; - - case ByteCode.LOOKUPSWITCH: // depends on alignment - case ByteCode.TABLESWITCH: // depends on alignment - return -1; - } - throw new IllegalArgumentException("Bad opcode: "+opcode); - } - */ - - @SuppressWarnings("unused") - private static String bytecodeStr(int code) { - if (DEBUGSTACK || DEBUGCODE) { - switch (code) { - case ByteCode.NOP: - return "nop"; - case ByteCode.ACONST_NULL: - return "aconst_null"; - case ByteCode.ICONST_M1: - return "iconst_m1"; - case ByteCode.ICONST_0: - return "iconst_0"; - case ByteCode.ICONST_1: - return "iconst_1"; - case ByteCode.ICONST_2: - return "iconst_2"; - case ByteCode.ICONST_3: - return "iconst_3"; - case ByteCode.ICONST_4: - return "iconst_4"; - case ByteCode.ICONST_5: - return "iconst_5"; - case ByteCode.LCONST_0: - return "lconst_0"; - case ByteCode.LCONST_1: - return "lconst_1"; - case ByteCode.FCONST_0: - return "fconst_0"; - case ByteCode.FCONST_1: - return "fconst_1"; - case ByteCode.FCONST_2: - return "fconst_2"; - case ByteCode.DCONST_0: - return "dconst_0"; - case ByteCode.DCONST_1: - return "dconst_1"; - case ByteCode.BIPUSH: - return "bipush"; - case ByteCode.SIPUSH: - return "sipush"; - case ByteCode.LDC: - return "ldc"; - case ByteCode.LDC_W: - return "ldc_w"; - case ByteCode.LDC2_W: - return "ldc2_w"; - case ByteCode.ILOAD: - return "iload"; - case ByteCode.LLOAD: - return "lload"; - case ByteCode.FLOAD: - return "fload"; - case ByteCode.DLOAD: - return "dload"; - case ByteCode.ALOAD: - return "aload"; - case ByteCode.ILOAD_0: - return "iload_0"; - case ByteCode.ILOAD_1: - return "iload_1"; - case ByteCode.ILOAD_2: - return "iload_2"; - case ByteCode.ILOAD_3: - return "iload_3"; - case ByteCode.LLOAD_0: - return "lload_0"; - case ByteCode.LLOAD_1: - return "lload_1"; - case ByteCode.LLOAD_2: - return "lload_2"; - case ByteCode.LLOAD_3: - return "lload_3"; - case ByteCode.FLOAD_0: - return "fload_0"; - case ByteCode.FLOAD_1: - return "fload_1"; - case ByteCode.FLOAD_2: - return "fload_2"; - case ByteCode.FLOAD_3: - return "fload_3"; - case ByteCode.DLOAD_0: - return "dload_0"; - case ByteCode.DLOAD_1: - return "dload_1"; - case ByteCode.DLOAD_2: - return "dload_2"; - case ByteCode.DLOAD_3: - return "dload_3"; - case ByteCode.ALOAD_0: - return "aload_0"; - case ByteCode.ALOAD_1: - return "aload_1"; - case ByteCode.ALOAD_2: - return "aload_2"; - case ByteCode.ALOAD_3: - return "aload_3"; - case ByteCode.IALOAD: - return "iaload"; - case ByteCode.LALOAD: - return "laload"; - case ByteCode.FALOAD: - return "faload"; - case ByteCode.DALOAD: - return "daload"; - case ByteCode.AALOAD: - return "aaload"; - case ByteCode.BALOAD: - return "baload"; - case ByteCode.CALOAD: - return "caload"; - case ByteCode.SALOAD: - return "saload"; - case ByteCode.ISTORE: - return "istore"; - case ByteCode.LSTORE: - return "lstore"; - case ByteCode.FSTORE: - return "fstore"; - case ByteCode.DSTORE: - return "dstore"; - case ByteCode.ASTORE: - return "astore"; - case ByteCode.ISTORE_0: - return "istore_0"; - case ByteCode.ISTORE_1: - return "istore_1"; - case ByteCode.ISTORE_2: - return "istore_2"; - case ByteCode.ISTORE_3: - return "istore_3"; - case ByteCode.LSTORE_0: - return "lstore_0"; - case ByteCode.LSTORE_1: - return "lstore_1"; - case ByteCode.LSTORE_2: - return "lstore_2"; - case ByteCode.LSTORE_3: - return "lstore_3"; - case ByteCode.FSTORE_0: - return "fstore_0"; - case ByteCode.FSTORE_1: - return "fstore_1"; - case ByteCode.FSTORE_2: - return "fstore_2"; - case ByteCode.FSTORE_3: - return "fstore_3"; - case ByteCode.DSTORE_0: - return "dstore_0"; - case ByteCode.DSTORE_1: - return "dstore_1"; - case ByteCode.DSTORE_2: - return "dstore_2"; - case ByteCode.DSTORE_3: - return "dstore_3"; - case ByteCode.ASTORE_0: - return "astore_0"; - case ByteCode.ASTORE_1: - return "astore_1"; - case ByteCode.ASTORE_2: - return "astore_2"; - case ByteCode.ASTORE_3: - return "astore_3"; - case ByteCode.IASTORE: - return "iastore"; - case ByteCode.LASTORE: - return "lastore"; - case ByteCode.FASTORE: - return "fastore"; - case ByteCode.DASTORE: - return "dastore"; - case ByteCode.AASTORE: - return "aastore"; - case ByteCode.BASTORE: - return "bastore"; - case ByteCode.CASTORE: - return "castore"; - case ByteCode.SASTORE: - return "sastore"; - case ByteCode.POP: - return "pop"; - case ByteCode.POP2: - return "pop2"; - case ByteCode.DUP: - return "dup"; - case ByteCode.DUP_X1: - return "dup_x1"; - case ByteCode.DUP_X2: - return "dup_x2"; - case ByteCode.DUP2: - return "dup2"; - case ByteCode.DUP2_X1: - return "dup2_x1"; - case ByteCode.DUP2_X2: - return "dup2_x2"; - case ByteCode.SWAP: - return "swap"; - case ByteCode.IADD: - return "iadd"; - case ByteCode.LADD: - return "ladd"; - case ByteCode.FADD: - return "fadd"; - case ByteCode.DADD: - return "dadd"; - case ByteCode.ISUB: - return "isub"; - case ByteCode.LSUB: - return "lsub"; - case ByteCode.FSUB: - return "fsub"; - case ByteCode.DSUB: - return "dsub"; - case ByteCode.IMUL: - return "imul"; - case ByteCode.LMUL: - return "lmul"; - case ByteCode.FMUL: - return "fmul"; - case ByteCode.DMUL: - return "dmul"; - case ByteCode.IDIV: - return "idiv"; - case ByteCode.LDIV: - return "ldiv"; - case ByteCode.FDIV: - return "fdiv"; - case ByteCode.DDIV: - return "ddiv"; - case ByteCode.IREM: - return "irem"; - case ByteCode.LREM: - return "lrem"; - case ByteCode.FREM: - return "frem"; - case ByteCode.DREM: - return "drem"; - case ByteCode.INEG: - return "ineg"; - case ByteCode.LNEG: - return "lneg"; - case ByteCode.FNEG: - return "fneg"; - case ByteCode.DNEG: - return "dneg"; - case ByteCode.ISHL: - return "ishl"; - case ByteCode.LSHL: - return "lshl"; - case ByteCode.ISHR: - return "ishr"; - case ByteCode.LSHR: - return "lshr"; - case ByteCode.IUSHR: - return "iushr"; - case ByteCode.LUSHR: - return "lushr"; - case ByteCode.IAND: - return "iand"; - case ByteCode.LAND: - return "land"; - case ByteCode.IOR: - return "ior"; - case ByteCode.LOR: - return "lor"; - case ByteCode.IXOR: - return "ixor"; - case ByteCode.LXOR: - return "lxor"; - case ByteCode.IINC: - return "iinc"; - case ByteCode.I2L: - return "i2l"; - case ByteCode.I2F: - return "i2f"; - case ByteCode.I2D: - return "i2d"; - case ByteCode.L2I: - return "l2i"; - case ByteCode.L2F: - return "l2f"; - case ByteCode.L2D: - return "l2d"; - case ByteCode.F2I: - return "f2i"; - case ByteCode.F2L: - return "f2l"; - case ByteCode.F2D: - return "f2d"; - case ByteCode.D2I: - return "d2i"; - case ByteCode.D2L: - return "d2l"; - case ByteCode.D2F: - return "d2f"; - case ByteCode.I2B: - return "i2b"; - case ByteCode.I2C: - return "i2c"; - case ByteCode.I2S: - return "i2s"; - case ByteCode.LCMP: - return "lcmp"; - case ByteCode.FCMPL: - return "fcmpl"; - case ByteCode.FCMPG: - return "fcmpg"; - case ByteCode.DCMPL: - return "dcmpl"; - case ByteCode.DCMPG: - return "dcmpg"; - case ByteCode.IFEQ: - return "ifeq"; - case ByteCode.IFNE: - return "ifne"; - case ByteCode.IFLT: - return "iflt"; - case ByteCode.IFGE: - return "ifge"; - case ByteCode.IFGT: - return "ifgt"; - case ByteCode.IFLE: - return "ifle"; - case ByteCode.IF_ICMPEQ: - return "if_icmpeq"; - case ByteCode.IF_ICMPNE: - return "if_icmpne"; - case ByteCode.IF_ICMPLT: - return "if_icmplt"; - case ByteCode.IF_ICMPGE: - return "if_icmpge"; - case ByteCode.IF_ICMPGT: - return "if_icmpgt"; - case ByteCode.IF_ICMPLE: - return "if_icmple"; - case ByteCode.IF_ACMPEQ: - return "if_acmpeq"; - case ByteCode.IF_ACMPNE: - return "if_acmpne"; - case ByteCode.GOTO: - return "goto"; - case ByteCode.JSR: - return "jsr"; - case ByteCode.RET: - return "ret"; - case ByteCode.TABLESWITCH: - return "tableswitch"; - case ByteCode.LOOKUPSWITCH: - return "lookupswitch"; - case ByteCode.IRETURN: - return "ireturn"; - case ByteCode.LRETURN: - return "lreturn"; - case ByteCode.FRETURN: - return "freturn"; - case ByteCode.DRETURN: - return "dreturn"; - case ByteCode.ARETURN: - return "areturn"; - case ByteCode.RETURN: - return "return"; - case ByteCode.GETSTATIC: - return "getstatic"; - case ByteCode.PUTSTATIC: - return "putstatic"; - case ByteCode.GETFIELD: - return "getfield"; - case ByteCode.PUTFIELD: - return "putfield"; - case ByteCode.INVOKEVIRTUAL: - return "invokevirtual"; - case ByteCode.INVOKESPECIAL: - return "invokespecial"; - case ByteCode.INVOKESTATIC: - return "invokestatic"; - case ByteCode.INVOKEINTERFACE: - return "invokeinterface"; - case ByteCode.INVOKEDYNAMIC: - return "invokedynamic"; - case ByteCode.NEW: - return "new"; - case ByteCode.NEWARRAY: - return "newarray"; - case ByteCode.ANEWARRAY: - return "anewarray"; - case ByteCode.ARRAYLENGTH: - return "arraylength"; - case ByteCode.ATHROW: - return "athrow"; - case ByteCode.CHECKCAST: - return "checkcast"; - case ByteCode.INSTANCEOF: - return "instanceof"; - case ByteCode.MONITORENTER: - return "monitorenter"; - case ByteCode.MONITOREXIT: - return "monitorexit"; - case ByteCode.WIDE: - return "wide"; - case ByteCode.MULTIANEWARRAY: - return "multianewarray"; - case ByteCode.IFNULL: - return "ifnull"; - case ByteCode.IFNONNULL: - return "ifnonnull"; - case ByteCode.GOTO_W: - return "goto_w"; - case ByteCode.JSR_W: - return "jsr_w"; - case ByteCode.BREAKPOINT: - return "breakpoint"; - - case ByteCode.IMPDEP1: - return "impdep1"; - case ByteCode.IMPDEP2: - return "impdep2"; - } - } - return ""; - } - - final char[] getCharBuffer(int minimalSize) { - if (minimalSize > tmpCharBuffer.length) { - int newSize = tmpCharBuffer.length * 2; - if (minimalSize > newSize) { - newSize = minimalSize; - } - tmpCharBuffer = new char[newSize]; - } - return tmpCharBuffer; - } - - /** - * Add a pc as the start of super block. - * - *

A pc is the beginning of a super block if: - pc == 0 - it is the target of a branch - * instruction - it is the beginning of an exception handler - it is directly after an - * unconditional jump - */ - private void addSuperBlockStart(int pc) { - if (GenerateStackMap) { - if (itsSuperBlockStarts == null) { - itsSuperBlockStarts = new int[SuperBlockStartsSize]; - } else if (itsSuperBlockStarts.length == itsSuperBlockStartsTop) { - int[] tmp = new int[itsSuperBlockStartsTop * 2]; - System.arraycopy(itsSuperBlockStarts, 0, tmp, 0, itsSuperBlockStartsTop); - itsSuperBlockStarts = tmp; - } - itsSuperBlockStarts[itsSuperBlockStartsTop++] = pc; - } - } - - /** - * Sort the list of recorded super block starts and remove duplicates. - * - *

Also adds exception handling blocks as block starts, since there is no explicit control - * flow to these. Used for stack map table generation. - */ - private void finalizeSuperBlockStarts() { - if (GenerateStackMap) { - for (int i = 0; i < itsExceptionTableTop; i++) { - ExceptionTableEntry ete = itsExceptionTable[i]; - int handlerPC = getLabelPC(ete.itsHandlerLabel); - addSuperBlockStart(handlerPC); - } - Arrays.sort(itsSuperBlockStarts, 0, itsSuperBlockStartsTop); - int prev = itsSuperBlockStarts[0]; - int copyTo = 1; - for (int i = 1; i < itsSuperBlockStartsTop; i++) { - int curr = itsSuperBlockStarts[i]; - if (prev != curr) { - if (copyTo != i) { - itsSuperBlockStarts[copyTo] = curr; - } - copyTo++; - prev = curr; - } - } - itsSuperBlockStartsTop = copyTo; - if (itsSuperBlockStarts[copyTo - 1] == itsCodeBufferTop) { - itsSuperBlockStartsTop--; - } - } - } - - private int[] itsSuperBlockStarts = null; - private int itsSuperBlockStartsTop = 0; - private static final int SuperBlockStartsSize = 4; - - // Used to find blocks of code with no dependencies (aka dead code). - // Necessary for generating type information for dead code, which is - // expected by the Sun verifier. It is only necessary to store a single - // jump source to determine if a block is reachable or not. - private UintMap itsJumpFroms = null; - - private static final int LineNumberTableSize = 16; - private static final int ExceptionTableSize = 4; - - private static final int MajorVersion; - private static final int MinorVersion; - private static final boolean GenerateStackMap; - - static { - // Figure out which classfile version should be generated. This assumes - // that the runtime used to compile the JavaScript files is the same as - // the one used to run them. This is important because there are cases - // when bytecode is generated at runtime, where it is not easy to pass - // along what version is necessary. Instead, we grab the version numbers - // from the bytecode of this class and use that. - // - // Based on the version numbers we scrape, we can also determine what - // bytecode features we need. For example, Java 6 bytecode (classfile - // version 50) should have stack maps generated. - InputStream is = null; - int major = 48, minor = 0; - try { - is = ClassFileWriter.class.getResourceAsStream("ClassFileWriter.class"); - if (is == null) { - is = - ClassLoader.getSystemResourceAsStream( - "org/mozilla/classfile/ClassFileWriter.class"); - } - byte[] header = new byte[8]; - // read loop is required since JDK7 will only provide 2 bytes - // on the first read() - see bug #630111 - int read = 0; - while (read < 8) { - int c = is.read(header, read, 8 - read); - if (c < 0) throw new IOException(); - read += c; - } - minor = (header[4] << 8) | (header[5] & 0xff); - major = (header[6] << 8) | (header[7] & 0xff); - } catch (Exception e) { - // Unable to get class file, use default bytecode version - } finally { - MinorVersion = minor; - MajorVersion = major; - GenerateStackMap = major >= 50; - if (is != null) { - try { - is.close(); - } catch (IOException e) { - } - } - } - } - - final class BootstrapEntry { - - final byte[] code; - - BootstrapEntry(ClassFileWriter.MHandle bsm, Object... bsmArgs) { - int length = 2 + 2 + bsmArgs.length * 2; - code = new byte[length]; - putInt16(itsConstantPool.addMethodHandle(bsm), code, 0); - putInt16(bsmArgs.length, code, 2); - for (int i = 0; i < bsmArgs.length; i++) { - putInt16(itsConstantPool.addConstant(bsmArgs[i]), code, 4 + i * 2); - } - } - - @Override - public boolean equals(Object obj) { - return obj instanceof BootstrapEntry - && Arrays.equals(code, ((BootstrapEntry) obj).code); - } - - @Override - public int hashCode() { - return ~Arrays.hashCode(code); - } - } - - public static final class MHandle { - - final byte tag; - final String owner; - final String name; - final String desc; - - public MHandle(byte tag, String owner, String name, String desc) { - this.tag = tag; - this.owner = owner; - this.name = name; - this.desc = desc; - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof MHandle)) { - return false; - } - MHandle mh = (MHandle) obj; - return tag == mh.tag - && owner.equals(mh.owner) - && name.equals(mh.name) - && desc.equals(mh.desc); - } - - @Override - public int hashCode() { - return tag + owner.hashCode() * name.hashCode() * desc.hashCode(); - } - - @Override - public String toString() { - return owner + '.' + name + desc + " (" + tag + ')'; - } - } - - private static final int FileHeaderConstant = 0xCAFEBABE; - // Set DEBUG flags to true to get better checking and progress info. - private static final boolean DEBUGSTACK = false; - private static final boolean DEBUGLABELS = false; - private static final boolean DEBUGCODE = false; - - private String generatedClassName; - - private ExceptionTableEntry[] itsExceptionTable; - - private int itsExceptionTableTop; - - private int[] itsLineNumberTable; // pack start_pc & line_number together - private int itsLineNumberTableTop; - - private byte[] itsCodeBuffer = new byte[256]; - private int itsCodeBufferTop; - - private ConstantPool itsConstantPool; - - private ClassFileMethod itsCurrentMethod; - private short itsStackTop; - - private short itsMaxStack; - private short itsMaxLocals; - - private ObjArray itsMethods = new ObjArray(); - private ObjArray itsFields = new ObjArray(); - private ObjArray itsInterfaces = new ObjArray(); - - private short itsFlags; - private short itsThisClassIndex; - private short itsSuperClassIndex; - private short itsSourceFileNameIndex; - - private static final int MIN_LABEL_TABLE_SIZE = 32; - private int[] itsLabelTable; - private int itsLabelTableTop; - - // itsFixupTable[i] = (label_index << 32) | fixup_site - private static final int MIN_FIXUP_TABLE_SIZE = 40; - private long[] itsFixupTable; - private int itsFixupTableTop; - private ObjArray itsVarDescriptors; - private ObjArray itsBootstrapMethods; - private int itsBootstrapMethodsLength = 0; - - private char[] tmpCharBuffer = new char[64]; -} 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 - *

- */ -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: - *

- * 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. - * - *

1. A entry has priority over method. - * - *

map.put("put", "abc"); map.put; // abc map.put("put", "efg"); // ERROR - * - *

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 langVer release relNum 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. - * - *

The code - * - *

-     * Object[] args = { "a", "b" };
-     * newObject(scope, "Foo", args)
- * - * 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: - * - *

-     *    Context cx = Context.getCurrentContext();
-     *    return cx.getWrapFactory().wrap(cx, scope, value, null);
-     * 
- * - * @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: - * - *

-     *    return cx.getWrapFactory().wrap(cx, scope, value, null);
-     * 
- * - * @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 getJavaToJSONConverter() { - if (javaToJSONConverter == null) { - return JavaToJSONConverters.STRING; - } - return javaToJSONConverter; - } - - /** - * Sets 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[]}. - * - *

Objects returned by the converter will converted with {@link #javaToJS(Object, - * Scriptable)} and then stringified themselves. - * - * @param javaToJSONConverter - * @throws IllegalArgumentException if javaToJSONConverter is null - */ - public void setJavaToJSONConverter(UnaryOperator javaToJSONConverter) - throws IllegalArgumentException { - if (javaToJSONConverter == null) { - throw new IllegalArgumentException("javaToJSONConverter == null"); - } - this.javaToJSONConverter = javaToJSONConverter; - } - - /** - * Rethrow the exception wrapping it as the script runtime exception. Unless the exception is - * instance of {@link EcmaError} or {@link EvaluatorException} it will be wrapped as {@link - * WrappedException}, a subclass of {@link EvaluatorException}. The resulting exception object - * always contains source name and line number of script that triggered exception. - * - *

This method always throws an exception, its return value is provided only for convenience - * to allow a usage like: - * - *

-     * throw Context.throwAsScriptRuntimeEx(ex);
-     * 
- * - * to indicate that code after the method is unreachable. - * - * @throws EvaluatorException - * @throws EcmaError - */ - public static RuntimeException throwAsScriptRuntimeEx(Throwable e) { - while ((e instanceof InvocationTargetException)) { - e = ((InvocationTargetException) e).getTargetException(); - } - // special handling of Error so scripts would not catch them - if (e instanceof Error) { - Context cx = getContext(); - if (cx == null || !cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)) { - throw (Error) e; - } - } - if (e instanceof RhinoException) { - throw (RhinoException) e; - } - throw new WrappedException(e); - } - - /** - * Tell whether debug information is being generated. - * - * @since 1.3 - */ - public final boolean isGeneratingDebug() { - return generatingDebug; - } - - /** - * Specify whether or not debug information should be generated. - * - *

Setting the generation of debug information on will set the optimization level to zero. - * - * @since 1.3 - */ - public final void setGeneratingDebug(boolean generatingDebug) { - if (sealed) onSealedMutation(); - generatingDebugChanged = true; - if (generatingDebug && getOptimizationLevel() > 0) setOptimizationLevel(0); - this.generatingDebug = generatingDebug; - } - - /** - * Tell whether source information is being generated. - * - * @since 1.3 - */ - public final boolean isGeneratingSource() { - return generatingSource; - } - - /** - * 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. - * - * @since 1.3 - */ - public final void setGeneratingSource(boolean generatingSource) { - if (sealed) onSealedMutation(); - this.generatingSource = generatingSource; - } - - /** - * Get the current optimization level. - * - *

The optimization level is expressed as an integer between -1 and 9. - * - * @since 1.3 - */ - public final int getOptimizationLevel() { - return optimizationLevel; - } - - /** - * Set the current optimization level. - * - *

The optimization level is expected to be an integer between -1 and 9. Any negative values - * will be interpreted as -1, and any values greater than 9 will be interpreted as 9. An - * optimization level of -1 indicates that interpretive mode will always be used. Levels 0 - * through 9 indicate that class files may be generated. Higher optimization levels trade off - * compile time performance for runtime performance. The optimizer level can't be set greater - * than -1 if the optimizer package doesn't exist at run time. - * - * @param optimizationLevel an integer indicating the level of optimization to perform - * @since 1.3 - */ - public final void setOptimizationLevel(int optimizationLevel) { - if (sealed) onSealedMutation(); - if (optimizationLevel == -2) { - // To be compatible with Cocoon fork - optimizationLevel = -1; - } - checkOptimizationLevel(optimizationLevel); - if (codegenClass == null) optimizationLevel = -1; - this.optimizationLevel = optimizationLevel; - } - - public static boolean isValidOptimizationLevel(int optimizationLevel) { - return -1 <= optimizationLevel && optimizationLevel <= 9; - } - - public static void checkOptimizationLevel(int optimizationLevel) { - if (isValidOptimizationLevel(optimizationLevel)) { - return; - } - throw new IllegalArgumentException( - "Optimization level outside [-1..9]: " + optimizationLevel); - } - - /** - * Returns the maximum stack depth (in terms of number of call frames) allowed in a single - * invocation of interpreter. If the set depth would be exceeded, the interpreter will throw an - * EvaluatorException in the script. Defaults to Integer.MAX_VALUE. The setting only has effect - * for interpreted functions (those compiled with optimization level set to -1). As the - * interpreter doesn't use the Java stack but rather manages its own stack in the heap memory, a - * runaway recursion in interpreted code would eventually consume all available memory and cause - * OutOfMemoryError instead of a StackOverflowError limited to only a single thread. This - * setting helps prevent such situations. - * - * @return The current maximum interpreter stack depth. - */ - public final int getMaximumInterpreterStackDepth() { - return maximumInterpreterStackDepth; - } - - /** - * Sets the maximum stack depth (in terms of number of call frames) allowed in a single - * invocation of interpreter. If the set depth would be exceeded, the interpreter will throw an - * EvaluatorException in the script. Defaults to Integer.MAX_VALUE. The setting only has effect - * for interpreted functions (those compiled with optimization level set to -1). As the - * interpreter doesn't use the Java stack but rather manages its own stack in the heap memory, a - * runaway recursion in interpreted code would eventually consume all available memory and cause - * OutOfMemoryError instead of a StackOverflowError limited to only a single thread. This - * setting helps prevent such situations. - * - * @param max the new maximum interpreter stack depth - * @throws IllegalStateException if this context's optimization level is not -1 - * @throws IllegalArgumentException if the new depth is not at least 1 - */ - public final void setMaximumInterpreterStackDepth(int max) { - if (sealed) onSealedMutation(); - if (optimizationLevel != -1) { - throw new IllegalStateException( - "Cannot set maximumInterpreterStackDepth when optimizationLevel != -1"); - } - if (max < 1) { - throw new IllegalArgumentException( - "Cannot set maximumInterpreterStackDepth to less than 1"); - } - maximumInterpreterStackDepth = max; - } - - /** - * Set the security controller for this context. - * - *

SecurityController may only be set if it is currently null and {@link - * SecurityController#hasGlobal()} is false. Otherwise a SecurityException is - * thrown. - * - * @param controller a SecurityController object - * @throws SecurityException if there is already a SecurityController object for this Context or - * globally installed. - * @see SecurityController#initGlobal(SecurityController controller) - * @see SecurityController#hasGlobal() - */ - public final void setSecurityController(SecurityController controller) { - if (sealed) onSealedMutation(); - if (controller == null) throw new IllegalArgumentException(); - if (securityController != null) { - throw new SecurityException("Can not overwrite existing SecurityController object"); - } - if (SecurityController.hasGlobal()) { - throw new SecurityException( - "Can not overwrite existing global SecurityController object"); - } - securityController = controller; - } - - /** - * Set the LiveConnect access filter for this context. - * - *

{@link ClassShutter} may only be set if it is currently null. Otherwise a - * SecurityException is thrown. - * - * @param shutter a ClassShutter object - * @throws SecurityException if there is already a ClassShutter object for this Context - */ - public final synchronized void setClassShutter(ClassShutter shutter) { - if (sealed) onSealedMutation(); - if (shutter == null) throw new IllegalArgumentException(); - if (hasClassShutter) { - throw new SecurityException("Cannot overwrite existing " + "ClassShutter object"); - } - classShutter = shutter; - hasClassShutter = true; - } - - final synchronized ClassShutter getClassShutter() { - return classShutter; - } - - public interface ClassShutterSetter { - public void setClassShutter(ClassShutter shutter); - - public ClassShutter getClassShutter(); - } - - public final synchronized ClassShutterSetter getClassShutterSetter() { - if (hasClassShutter) return null; - hasClassShutter = true; - return new ClassShutterSetter() { - - @Override - public void setClassShutter(ClassShutter shutter) { - classShutter = shutter; - } - - @Override - public ClassShutter getClassShutter() { - return classShutter; - } - }; - } - - /** - * Get a value corresponding to a key. - * - *

Since the Context is associated with a thread it can be used to maintain values that can - * be later retrieved using the current thread. - * - *

Note that the values are maintained with the Context, so if the Context is disassociated - * from the thread the values cannot be retrieved. Also, if private data is to be maintained in - * this manner the key should be a java.lang.Object whose reference is not divulged to untrusted - * code. - * - * @param key the key used to lookup the value - * @return a value previously stored using putThreadLocal. - */ - public final Object getThreadLocal(Object key) { - if (threadLocalMap == null) return null; - return threadLocalMap.get(key); - } - - /** - * Put a value that can later be retrieved using a given key. - * - *

- * - * @param key the key used to index the value - * @param value the value to save - */ - public final synchronized void putThreadLocal(Object key, Object value) { - if (sealed) onSealedMutation(); - if (threadLocalMap == null) threadLocalMap = new HashMap(); - threadLocalMap.put(key, value); - } - - /** - * Remove values from thread-local storage. - * - * @param key the key for the entry to remove. - * @since 1.5 release 2 - */ - public final void removeThreadLocal(Object key) { - if (sealed) onSealedMutation(); - if (threadLocalMap == null) return; - threadLocalMap.remove(key); - } - - /** - * @deprecated - * @see ClassCache#get(Scriptable) - * @see ClassCache#setCachingEnabled(boolean) - */ - @Deprecated - public static void setCachingEnabled(boolean cachingEnabled) {} - - /** - * Set a WrapFactory for this Context. - * - *

The WrapFactory allows custom object wrapping behavior for Java object manipulated with - * JavaScript. - * - * @see WrapFactory - * @since 1.5 Release 4 - */ - public final void setWrapFactory(WrapFactory wrapFactory) { - if (sealed) onSealedMutation(); - if (wrapFactory == null) throw new IllegalArgumentException(); - this.wrapFactory = wrapFactory; - } - - /** - * Return the current WrapFactory, or null if none is defined. - * - * @see WrapFactory - * @since 1.5 Release 4 - */ - public final WrapFactory getWrapFactory() { - if (wrapFactory == null) { - wrapFactory = new WrapFactory(); - } - return wrapFactory; - } - - /** - * Return the current debugger. - * - * @return the debugger, or null if none is attached. - */ - public final Debugger getDebugger() { - return debugger; - } - - /** - * Return the debugger context data associated with current context. - * - * @return the debugger data, or null if debugger is not attached - */ - public final Object getDebuggerContextData() { - return debuggerData; - } - - /** - * Set the associated debugger. - * - * @param debugger the debugger to be used on callbacks from the engine. - * @param contextData arbitrary object that debugger can use to store per Context data. - */ - public final void setDebugger(Debugger debugger, Object contextData) { - if (sealed) onSealedMutation(); - this.debugger = debugger; - debuggerData = contextData; - } - - /** - * Return DebuggableScript instance if any associated with the script. If callable supports - * DebuggableScript implementation, the method returns it. Otherwise null is returned. - */ - public static DebuggableScript getDebuggableView(Script script) { - if (script instanceof NativeFunction) { - return ((NativeFunction) script).getDebuggableView(); - } - return null; - } - - /** - * Controls certain aspects of script semantics. Should be overwritten to alter default - * behavior. - * - *

The default implementation calls {@link ContextFactory#hasFeature(Context cx, int - * featureIndex)} that allows to customize Context behavior without introducing Context - * subclasses. {@link ContextFactory} documentation gives an example of hasFeature - * implementation. - * - * @param featureIndex feature index to check - * @return true if the featureIndex feature is turned on - * @see #FEATURE_NON_ECMA_GET_YEAR - * @see #FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME - * @see #FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER - * @see #FEATURE_TO_STRING_AS_SOURCE - * @see #FEATURE_PARENT_PROTO_PROPRTIES - * @see #FEATURE_E4X - * @see #FEATURE_DYNAMIC_SCOPE - * @see #FEATURE_STRICT_VARS - * @see #FEATURE_STRICT_EVAL - * @see #FEATURE_LOCATION_INFORMATION_IN_ERROR - * @see #FEATURE_STRICT_MODE - * @see #FEATURE_WARNING_AS_ERROR - * @see #FEATURE_ENHANCED_JAVA_ACCESS - */ - public boolean hasFeature(int featureIndex) { - ContextFactory f = getFactory(); - return f.hasFeature(this, featureIndex); - } - - /** - * Returns an object which specifies an E4X implementation to use within this Context - * . Note that the XMLLib.Factory interface should be considered experimental. - * - *

The default implementation uses the implementation provided by this Context's - * {@link ContextFactory}. - * - * @return An XMLLib.Factory. Should not return null if {@link #FEATURE_E4X} is - * enabled. See {@link #hasFeature}. - */ - public XMLLib.Factory getE4xImplementationFactory() { - return getFactory().getE4xImplementationFactory(); - } - - /** - * Get threshold of executed instructions counter that triggers call to - * observeInstructionCount(). When the threshold is zero, instruction counting is - * disabled, otherwise each time the run-time executes at least the threshold value of script - * instructions, observeInstructionCount() will be called. - */ - public final int getInstructionObserverThreshold() { - return instructionThreshold; - } - - /** - * Set threshold of executed instructions counter that triggers call to - * observeInstructionCount(). When the threshold is zero, instruction counting is - * disabled, otherwise each time the run-time executes at least the threshold value of script - * instructions, observeInstructionCount() will be called.
- * Note that the meaning of "instruction" is not guaranteed to be consistent between compiled - * and interpretive modes: executing a given script or function in the different modes will - * result in different instruction counts against the threshold. {@link - * #setGenerateObserverCount} is called with true if threshold is greater than - * zero, false otherwise. - * - * @param threshold The instruction threshold - */ - public final void setInstructionObserverThreshold(int threshold) { - if (sealed) onSealedMutation(); - if (threshold < 0) throw new IllegalArgumentException(); - instructionThreshold = threshold; - setGenerateObserverCount(threshold > 0); - } - - /** - * 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; - } - - /** - * Allow application to monitor counter of executed script instructions in Context subclasses. - * Run-time calls this when instruction counting is enabled and the counter reaches limit set by - * setInstructionObserverThreshold(). The method is useful to observe long running - * scripts and if necessary to terminate them. - * - *

The default implementation calls {@link ContextFactory#observeInstructionCount(Context cx, - * int instructionCount)} that allows to customize Context behavior without introducing Context - * subclasses. - * - * @param instructionCount amount of script instruction executed since last call to - * observeInstructionCount - * @throws Error to terminate the script - * @see #setOptimizationLevel(int) - */ - protected void observeInstructionCount(int instructionCount) { - ContextFactory f = getFactory(); - f.observeInstructionCount(this, instructionCount); - } - - /** - * Create class loader for generated classes. The method calls {@link - * ContextFactory#createClassLoader(ClassLoader)} using the result of {@link #getFactory()}. - */ - public GeneratedClassLoader createClassLoader(ClassLoader parent) { - ContextFactory f = getFactory(); - return f.createClassLoader(parent); - } - - public final ClassLoader getApplicationClassLoader() { - if (applicationClassLoader == null) { - ContextFactory f = getFactory(); - ClassLoader loader = f.getApplicationClassLoader(); - if (loader == null) { - ClassLoader threadLoader = Thread.currentThread().getContextClassLoader(); - if (threadLoader != null && Kit.testIfCanLoadRhinoClasses(threadLoader)) { - // Thread.getContextClassLoader is not cached since - // its caching prevents it from GC which may lead to - // a memory leak and hides updates to - // Thread.getContextClassLoader - - // # ZOTE CORE MODIFIED PART - return new ZoteClassLoaderPool(threadLoader); - // # END - } - // Thread.getContextClassLoader can not load Rhino classes, - // try to use the loader of ContextFactory or Context - // subclasses. - Class fClass = f.getClass(); - if (fClass != ScriptRuntime.ContextFactoryClass) { - loader = fClass.getClassLoader(); - } else { - loader = getClass().getClassLoader(); - } - } - - // # ZOTE CORE MODIFIED PART - applicationClassLoader = new ZoteClassLoaderPool(loader); - // # END - } - return applicationClassLoader; - } - - public final void setApplicationClassLoader(ClassLoader loader) { - if (sealed) onSealedMutation(); - if (loader == null) { - // restore default behaviour - applicationClassLoader = null; - return; - } - if (!Kit.testIfCanLoadRhinoClasses(loader)) { - throw new IllegalArgumentException("Loader can not resolve Rhino classes"); - } - applicationClassLoader = loader; - } - - /** - * Add a task that will be executed at the end of the current operation. The various "evaluate" - * functions will all call this before exiting to ensure that all microtasks run to completion. - * Otherwise, callers should call "processMicrotasks" to run them all. This feature is primarily - * used to implement Promises. The microtask queue is not thread-safe. - */ - public void enqueueMicrotask(Runnable task) { - microtasks.add(task); - } - - /** - * Run all the microtasks for the current context to completion. This is called by the various - * "evaluate" functions. Frameworks that call Function objects directly should call this - * function to ensure that everything completes if they want all Promises to eventually resolve. - * This function is idempotent, but the microtask queue is not thread-safe. - */ - public void processMicrotasks() { - Runnable head; - do { - head = microtasks.poll(); - if (head != null) { - head.run(); - } - } while (head != null); - } - - /** - * Control whether to track unhandled promise rejections. If "track" is set to true, then the - * tracker returned by "getUnhandledPromiseTracker" must be periodically used to process the - * queue of unhandled promise rejections, or a memory leak may result. - * - * @param track if true, then track unhandled promise rejections - */ - public void setTrackUnhandledPromiseRejections(boolean track) { - unhandledPromises.enable(track); - } - - /** - * Return the object used to track unhandled promise rejections. - * - * @return the tracker object - */ - public UnhandledRejectionTracker getUnhandledPromiseTracker() { - return unhandledPromises; - } - - /* ******** end of API ********* */ - - /** Internal method that reports an error for missing calls to enter(). */ - static Context getContext() { - Context cx = getCurrentContext(); - if (cx == null) { - throw new RuntimeException("No Context associated with current Thread"); - } - return cx; - } - - protected Object compileImpl( - Scriptable scope, - String sourceString, - String sourceName, - int lineno, - Object securityDomain, - boolean returnFunction, - Evaluator compiler, - ErrorReporter compilationErrorReporter) - throws IOException { - if (sourceName == null) { - sourceName = "unnamed script"; - } - if (securityDomain != null && getSecurityController() == null) { - throw new IllegalArgumentException( - "securityDomain should be null if setSecurityController() was never called"); - } - - // scope should be given if and only if compiling function - if (!(scope == null ^ returnFunction)) Kit.codeBug(); - - CompilerEnvirons compilerEnv = new CompilerEnvirons(); - compilerEnv.initFromContext(this); - if (compilationErrorReporter == null) { - compilationErrorReporter = compilerEnv.getErrorReporter(); - } - - ScriptNode tree = - parse( - sourceString, - sourceName, - lineno, - compilerEnv, - compilationErrorReporter, - returnFunction); - - Object bytecode; - try { - if (compiler == null) { - compiler = createCompiler(); - } - - bytecode = compiler.compile(compilerEnv, tree, tree.getEncodedSource(), returnFunction); - } catch (ClassFileFormatException e) { - // we hit some class file limit, fall back to interpreter or report - - // we have to recreate the tree because the compile call might have changed the tree - // already - tree = - parse( - sourceString, - sourceName, - lineno, - compilerEnv, - compilationErrorReporter, - returnFunction); - - compiler = createInterpreter(); - bytecode = compiler.compile(compilerEnv, tree, tree.getEncodedSource(), returnFunction); - } - - if (debugger != null) { - if (sourceString == null) Kit.codeBug(); - if (bytecode instanceof DebuggableScript) { - DebuggableScript dscript = (DebuggableScript) bytecode; - notifyDebugger_r(this, dscript, sourceString); - } else { - throw new RuntimeException("NOT SUPPORTED"); - } - } - - Object result; - if (returnFunction) { - result = compiler.createFunctionObject(this, scope, bytecode, securityDomain); - } else { - result = compiler.createScriptObject(bytecode, securityDomain); - } - - return result; - } - - private ScriptNode parse( - String sourceString, - String sourceName, - int lineno, - CompilerEnvirons compilerEnv, - ErrorReporter compilationErrorReporter, - boolean returnFunction) - throws IOException { - Parser p = new Parser(compilerEnv, compilationErrorReporter); - if (returnFunction) { - p.calledByCompileFunction = true; - } - if (isStrictMode()) { - p.setDefaultUseStrictDirective(true); - } - - AstRoot ast = p.parse(sourceString, sourceName, lineno); - if (returnFunction) { - // parser no longer adds function to script node - if (!(ast.getFirstChild() != null && ast.getFirstChild().getType() == Token.FUNCTION)) { - // XXX: the check just looks for the first child - // and allows for more nodes after it for compatibility - // with sources like function() {};;; - throw new IllegalArgumentException( - "compileFunction only accepts source with single JS function: " - + sourceString); - } - } - - IRFactory irf = new IRFactory(compilerEnv, compilationErrorReporter); - ScriptNode tree = irf.transformTree(ast); - return tree; - } - - private static void notifyDebugger_r(Context cx, DebuggableScript dscript, String debugSource) { - cx.debugger.handleCompilationDone(cx, dscript, debugSource); - for (int i = 0; i != dscript.getFunctionCount(); ++i) { - notifyDebugger_r(cx, dscript.getFunction(i), debugSource); - } - } - - private static Class codegenClass = - Kit.classOrNull("org.mozilla.javascript.optimizer.Codegen"); - private static Class interpreterClass = - Kit.classOrNull("org.mozilla.javascript.Interpreter"); - - private Evaluator createCompiler() { - Evaluator result = null; - if (optimizationLevel >= 0 && codegenClass != null) { - result = (Evaluator) Kit.newInstanceOrNull(codegenClass); - } - if (result == null) { - result = createInterpreter(); - } - return result; - } - - static Evaluator createInterpreter() { - return (Evaluator) Kit.newInstanceOrNull(interpreterClass); - } - - static String getSourcePositionFromStack(int[] linep) { - Context cx = getCurrentContext(); - if (cx == null) return null; - if (cx.lastInterpreterFrame != null) { - Evaluator evaluator = createInterpreter(); - if (evaluator != null) return evaluator.getSourcePositionFromStack(cx, linep); - } - /** - * A bit of a hack, but the only way to get filename and line number from an enclosing - * frame. - */ - StackTraceElement[] stackTrace = new Throwable().getStackTrace(); - for (StackTraceElement st : stackTrace) { - String file = st.getFileName(); - if (!(file == null || file.endsWith(".java"))) { - int line = st.getLineNumber(); - if (line >= 0) { - linep[0] = line; - return file; - } - } - } - - return null; - } - - RegExpProxy getRegExpProxy() { - if (regExpProxy == null) { - Class cl = Kit.classOrNull("org.mozilla.javascript.regexp.RegExpImpl"); - if (cl != null) { - regExpProxy = (RegExpProxy) Kit.newInstanceOrNull(cl); - } - } - return regExpProxy; - } - - final boolean isVersionECMA1() { - return version == VERSION_DEFAULT || version >= VERSION_1_3; - } - - // The method must NOT be public or protected - SecurityController getSecurityController() { - SecurityController global = SecurityController.global(); - if (global != null) { - return global; - } - return securityController; - } - - public final boolean isGeneratingDebugChanged() { - return generatingDebugChanged; - } - - /** - * Add a name to the list of names forcing the creation of real activation objects for - * functions. - * - * @param name the name of the object to add to the list - */ - public void addActivationName(String name) { - if (sealed) onSealedMutation(); - if (activationNames == null) activationNames = new HashSet(); - activationNames.add(name); - } - - /** - * Check whether the name is in the list of names of objects forcing the creation of activation - * objects. - * - * @param name the name of the object to test - * @return true if an function activation object is needed. - */ - public final boolean isActivationNeeded(String name) { - return activationNames != null && activationNames.contains(name); - } - - /** - * Remove a name from the list of names forcing the creation of real activation objects for - * functions. - * - * @param name the name of the object to remove from the list - */ - public void removeActivationName(String name) { - if (sealed) onSealedMutation(); - if (activationNames != null) activationNames.remove(name); - } - - public final boolean isStrictMode() { - return isTopLevelStrict - || (currentActivationCall != null && currentActivationCall.isStrict); - } - - public static boolean isCurrentContextStrict() { - Context cx = getCurrentContext(); - if (cx == null) { - return false; - } - return cx.isStrictMode(); - } - - private final ContextFactory factory; - private boolean sealed; - private Object sealKey; - - Scriptable topCallScope; - boolean isContinuationsTopCall; - NativeCall currentActivationCall; - XMLLib cachedXMLLib; - BaseFunction typeErrorThrower; - - // for Objects, Arrays to tag themselves as being printed out, - // so they don't print themselves out recursively. - // Use ObjToIntMap instead of java.util.HashSet for JDK 1.1 compatibility - ObjToIntMap iterating; - - Object interpreterSecurityDomain; - - int version; - - private SecurityController securityController; - private boolean hasClassShutter; - private ClassShutter classShutter; - private ErrorReporter errorReporter; - RegExpProxy regExpProxy; - private Locale locale; - private boolean generatingDebug; - private boolean generatingDebugChanged; - private boolean generatingSource = true; - boolean useDynamicScope; - private int optimizationLevel; - private int maximumInterpreterStackDepth; - private WrapFactory wrapFactory; - Debugger debugger; - private Object debuggerData; - private int enterCount; - private Object propertyListeners; - private Map threadLocalMap; - private ClassLoader applicationClassLoader; - private UnaryOperator javaToJSONConverter; - private final ArrayDeque microtasks = new ArrayDeque<>(); - private final UnhandledRejectionTracker unhandledPromises = new UnhandledRejectionTracker(); - - /** This is the list of names of objects forcing the creation of function activation records. */ - Set activationNames; - - // For the interpreter to store the last frame for error reports etc. - Object lastInterpreterFrame; - - // For the interpreter to store information about previous invocations - // interpreter invocations - ObjArray previousInterpreterInvocations; - - // For instruction counting (interpreter only) - int instructionCount; - int instructionThreshold; - - // It can be used to return the second uint32 result from function - long scratchUint32; - - // It can be used to return the second Scriptable result from function - Scriptable scratchScriptable; - - // Generate an observer count on compiled code - public boolean generateObserverCount = false; - - boolean isTopLevelStrict; -} diff --git a/src/main/java/org/mozilla/javascript/ContextAction.java b/src/main/java/org/mozilla/javascript/ContextAction.java deleted file mode 100644 index dc05f6d1..00000000 --- a/src/main/java/org/mozilla/javascript/ContextAction.java +++ /dev/null @@ -1,27 +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; - -/** - * Interface to represent arbitrary action that requires to have Context - * object associated with the current thread for its execution. - * @param T the type of the return value of action execution - */ -public interface ContextAction -{ - /** - * Execute action using the supplied Context instance. - * When Rhino runtime calls the method, cx will be associated - * with the current thread as active context. - * - * @see ContextFactory#call(ContextAction) - */ - public T run(Context cx); -} - diff --git a/src/main/java/org/mozilla/javascript/ContextFactory.java b/src/main/java/org/mozilla/javascript/ContextFactory.java deleted file mode 100644 index 829d6a8b..00000000 --- a/src/main/java/org/mozilla/javascript/ContextFactory.java +++ /dev/null @@ -1,550 +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.security.AccessController; -import java.security.PrivilegedAction; - -/** - * Factory class that Rhino runtime uses to create new {@link Context} instances. A - * ContextFactory can also notify listeners about context creation and release. - * - *

When the Rhino runtime needs to create new {@link Context} instance during execution of {@link - * Context#enter()} or {@link Context}, it will call {@link #makeContext()} of the current global - * ContextFactory. See {@link #getGlobal()} and {@link #initGlobal(ContextFactory)}. - * - *

It is also possible to use explicit ContextFactory instances for Context creation. This is - * useful to have a set of independent Rhino runtime instances under single JVM. See {@link - * #call(ContextAction)}. - * - *

The following example demonstrates Context customization to terminate scripts running more - * then 10 seconds and to provide better compatibility with JavaScript code using MSIE-specific - * features. - * - *

- * import org.mozilla.javascript.*;
- *
- * class MyFactory extends ContextFactory
- * {
- *
- *     // Custom {@link Context} to store execution time.
- *     private static class MyContext extends Context
- *     {
- *         long startTime;
- *     }
- *
- *     static {
- *         // Initialize GlobalFactory with custom factory
- *         ContextFactory.initGlobal(new MyFactory());
- *     }
- *
- *     // Override {@link #makeContext()}
- *     protected Context makeContext()
- *     {
- *         MyContext cx = new MyContext();
- *         // Make Rhino runtime to call observeInstructionCount
- *         // each 10000 bytecode instructions
- *         cx.setInstructionObserverThreshold(10000);
- *         return cx;
- *     }
- *
- *     // Override {@link #hasFeature(Context, int)}
- *     public boolean hasFeature(Context cx, int featureIndex)
- *     {
- *         // Turn on maximum compatibility with MSIE scripts
- *         switch (featureIndex) {
- *             case {@link Context#FEATURE_NON_ECMA_GET_YEAR}:
- *                 return true;
- *
- *             case {@link Context#FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME}:
- *                 return true;
- *
- *             case {@link Context#FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER}:
- *                 return true;
- *
- *             case {@link Context#FEATURE_PARENT_PROTO_PROPERTIES}:
- *                 return false;
- *         }
- *         return super.hasFeature(cx, featureIndex);
- *     }
- *
- *     // Override {@link #observeInstructionCount(Context, int)}
- *     protected void observeInstructionCount(Context cx, int instructionCount)
- *     {
- *         MyContext mcx = (MyContext)cx;
- *         long currentTime = System.currentTimeMillis();
- *         if (currentTime - mcx.startTime > 10*1000) {
- *             // More then 10 seconds from Context creation time:
- *             // it is time to stop the script.
- *             // Throw Error instance to ensure that script will never
- *             // get control back through catch or finally.
- *             throw new Error();
- *         }
- *     }
- *
- *     // Override {@link #doTopCall(Callable,
- * Context, Scriptable,
- * Scriptable, Object[])}
- *     protected Object doTopCall(Callable callable,
- *                                Context cx, Scriptable scope,
- *                                Scriptable thisObj, Object[] args)
- *     {
- *         MyContext mcx = (MyContext)cx;
- *         mcx.startTime = System.currentTimeMillis();
- *
- *         return super.doTopCall(callable, cx, scope, thisObj, args);
- *     }
- *
- * }
- * 
- */ -public class ContextFactory { - private static volatile boolean hasCustomGlobal; - private static ContextFactory global = new ContextFactory(); - - private volatile boolean sealed; - - private final Object listenersLock = new Object(); - private volatile Object listeners; - private boolean disabledListening; - private ClassLoader applicationClassLoader; - - /** Listener of {@link Context} creation and release events. */ - public interface Listener { - /** Notify about newly created {@link Context} object. */ - public void contextCreated(Context cx); - - /** - * Notify that the specified {@link Context} instance is no longer associated with the - * current thread. - */ - public void contextReleased(Context cx); - } - - /** - * Get global ContextFactory. - * - * @see #hasExplicitGlobal() - * @see #initGlobal(ContextFactory) - */ - public static ContextFactory getGlobal() { - return global; - } - - /** - * Check if global factory was set. Return true to indicate that {@link - * #initGlobal(ContextFactory)} was already called and false to indicate that the global factory - * was not explicitly set. - * - * @see #getGlobal() - * @see #initGlobal(ContextFactory) - */ - public static boolean hasExplicitGlobal() { - return hasCustomGlobal; - } - - /** - * Set global ContextFactory. The method can only be called once. - * - * @see #getGlobal() - * @see #hasExplicitGlobal() - */ - public static synchronized void initGlobal(ContextFactory factory) { - if (factory == null) { - throw new IllegalArgumentException(); - } - if (hasCustomGlobal) { - throw new IllegalStateException(); - } - hasCustomGlobal = true; - global = factory; - } - - public interface GlobalSetter { - public void setContextFactoryGlobal(ContextFactory factory); - - public ContextFactory getContextFactoryGlobal(); - } - - public static synchronized GlobalSetter getGlobalSetter() { - if (hasCustomGlobal) { - throw new IllegalStateException(); - } - hasCustomGlobal = true; - class GlobalSetterImpl implements GlobalSetter { - @Override - public void setContextFactoryGlobal(ContextFactory factory) { - global = factory == null ? new ContextFactory() : factory; - } - - @Override - public ContextFactory getContextFactoryGlobal() { - return global; - } - } - return new GlobalSetterImpl(); - } - - /** - * Create new {@link Context} instance to be associated with the current thread. This is a - * callback method used by Rhino to create {@link Context} instance when it is necessary to - * associate one with the current execution thread. makeContext() is allowed to - * call {@link Context#seal(Object)} on the result to prevent {@link Context} changes by hostile - * scripts or applets. - */ - protected Context makeContext() { - return new Context(this); - } - - /** - * Implementation of {@link Context#hasFeature(int featureIndex)}. This can be used to customize - * {@link Context} without introducing additional subclasses. - */ - protected boolean hasFeature(Context cx, int featureIndex) { - int version; - switch (featureIndex) { - case Context.FEATURE_NON_ECMA_GET_YEAR: - /* - * During the great date rewrite of 1.3, we tried to track the - * evolving ECMA standard, which then had a definition of - * getYear which always subtracted 1900. Which we - * implemented, not realizing that it was incompatible with - * the old behavior... now, rather than thrash the behavior - * yet again, we've decided to leave it with the - 1900 - * behavior and point people to the getFullYear method. But - * we try to protect existing scripts that have specified a - * version... - */ - version = cx.getLanguageVersion(); - return (version == Context.VERSION_1_0 - || version == Context.VERSION_1_1 - || version == Context.VERSION_1_2); - - case Context.FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME: - return false; - - case Context.FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER: - return true; - - case Context.FEATURE_TO_STRING_AS_SOURCE: - version = cx.getLanguageVersion(); - return version == Context.VERSION_1_2; - - case Context.FEATURE_PARENT_PROTO_PROPERTIES: - return true; - - case Context.FEATURE_E4X: - version = cx.getLanguageVersion(); - return (version == Context.VERSION_DEFAULT || version >= Context.VERSION_1_6); - - case Context.FEATURE_DYNAMIC_SCOPE: - return false; - - case Context.FEATURE_STRICT_VARS: - return false; - - case Context.FEATURE_STRICT_EVAL: - return false; - - case Context.FEATURE_LOCATION_INFORMATION_IN_ERROR: - return false; - - case Context.FEATURE_STRICT_MODE: - return false; - - case Context.FEATURE_WARNING_AS_ERROR: - return false; - - case Context.FEATURE_ENHANCED_JAVA_ACCESS: - return false; - - case Context.FEATURE_V8_EXTENSIONS: - return true; - - case Context.FEATURE_OLD_UNDEF_NULL_THIS: - return cx.getLanguageVersion() <= Context.VERSION_1_7; - - case Context.FEATURE_ENUMERATE_IDS_FIRST: - return cx.getLanguageVersion() >= Context.VERSION_ES6; - - case Context.FEATURE_THREAD_SAFE_OBJECTS: - return false; - - case Context.FEATURE_INTEGER_WITHOUT_DECIMAL_PLACE: - return false; - - case Context.FEATURE_LITTLE_ENDIAN: - return false; - - case Context.FEATURE_ENABLE_XML_SECURE_PARSING: - return true; - - case Context.FEATURE_ENABLE_JAVA_MAP_ACCESS: - return false; - } - // It is a bug to call the method with unknown featureIndex - throw new IllegalArgumentException(String.valueOf(featureIndex)); - } - - private static boolean isDom3Present() { - Class nodeClass = Kit.classOrNull("org.w3c.dom.Node"); - if (nodeClass == null) return false; - // Check to see whether DOM3 is present; use a new method defined in - // DOM3 that is vital to our implementation - try { - nodeClass.getMethod("getUserData", new Class[] {String.class}); - return true; - } catch (NoSuchMethodException e) { - return false; - } - } - - /** - * Provides a default {@link org.mozilla.javascript.xml.XMLLib.Factory XMLLib.Factory} to be - * used by the Context instances produced by this factory. See {@link - * Context#getE4xImplementationFactory} for details. - * - *

May return null, in which case E4X functionality is not supported in Rhino. - * - *

The default implementation now prefers the DOM3 E4X implementation. - */ - protected org.mozilla.javascript.xml.XMLLib.Factory getE4xImplementationFactory() { - // Must provide default implementation, rather than abstract method, - // so that past implementors of ContextFactory do not fail at runtime - // upon invocation of this method. - // Note that the default implementation returns null if we - // neither have XMLBeans nor a DOM3 implementation present. - - if (isDom3Present()) { - return org.mozilla.javascript.xml.XMLLib.Factory.create( - "org.mozilla.javascript.xmlimpl.XMLLibImpl"); - } - return null; - } - - /** - * Create class loader for generated classes. This method creates an instance of the default - * implementation of {@link GeneratedClassLoader}. Rhino uses this interface to load generated - * JVM classes when no {@link SecurityController} is installed. Application can override the - * method to provide custom class loading. - */ - protected GeneratedClassLoader createClassLoader(final ClassLoader parent) { - return AccessController.doPrivileged( - new PrivilegedAction() { - @Override - public DefiningClassLoader run() { - return new DefiningClassLoader(parent); - } - }); - } - - /** - * Get ClassLoader to use when searching for Java classes. Unless it was explicitly initialized - * with {@link #initApplicationClassLoader(ClassLoader)} the method returns null to indicate - * that Thread.getContextClassLoader() should be used. - */ - public final ClassLoader getApplicationClassLoader() { - return applicationClassLoader; - } - - /** - * Set explicit class loader to use when searching for Java classes. - * - * @see #getApplicationClassLoader() - */ - public final void initApplicationClassLoader(ClassLoader loader) { - if (loader == null) throw new IllegalArgumentException("loader is null"); - if (!Kit.testIfCanLoadRhinoClasses(loader)) - throw new IllegalArgumentException("Loader can not resolve Rhino classes"); - - if (this.applicationClassLoader != null) - throw new IllegalStateException("applicationClassLoader can only be set once"); - checkNotSealed(); - - this.applicationClassLoader = loader; - } - - /** - * Execute top call to script or function. When the runtime is about to execute a script or - * function that will create the first stack frame with scriptable code, it calls this method to - * perform the real call. In this way execution of any script happens inside this function. - */ - protected Object doTopCall( - Callable callable, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - Object result = callable.call(cx, scope, thisObj, args); - return result instanceof ConsString ? result.toString() : result; - } - - /** - * Implementation of {@link Context#observeInstructionCount(int instructionCount)}. This can be - * used to customize {@link Context} without introducing additional subclasses. - */ - protected void observeInstructionCount(Context cx, int instructionCount) {} - - protected void onContextCreated(Context cx) { - Object listeners = this.listeners; - for (int i = 0; ; ++i) { - Listener l = (Listener) Kit.getListener(listeners, i); - if (l == null) break; - l.contextCreated(cx); - } - } - - protected void onContextReleased(Context cx) { - Object listeners = this.listeners; - for (int i = 0; ; ++i) { - Listener l = (Listener) Kit.getListener(listeners, i); - if (l == null) break; - l.contextReleased(cx); - } - } - - public final void addListener(Listener listener) { - checkNotSealed(); - synchronized (listenersLock) { - if (disabledListening) { - throw new IllegalStateException(); - } - listeners = Kit.addListener(listeners, listener); - } - } - - public final void removeListener(Listener listener) { - checkNotSealed(); - synchronized (listenersLock) { - if (disabledListening) { - throw new IllegalStateException(); - } - listeners = Kit.removeListener(listeners, listener); - } - } - - /** The method is used only to implement Context.disableStaticContextListening() */ - final void disableContextListening() { - checkNotSealed(); - synchronized (listenersLock) { - disabledListening = true; - listeners = null; - } - } - - /** - * Checks if this is a sealed ContextFactory. - * - * @see #seal() - */ - public final boolean isSealed() { - return sealed; - } - - /** - * Seal this ContextFactory so any attempt to modify it like to add or remove its listeners will - * throw an exception. - * - * @see #isSealed() - */ - public final void seal() { - checkNotSealed(); - sealed = true; - } - - protected final void checkNotSealed() { - if (sealed) throw new IllegalStateException(); - } - - /** - * Call {@link ContextAction#run(Context cx)} using the {@link Context} instance associated with - * the current thread. If no Context is associated with the thread, then {@link #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)}. - * - * @see ContextFactory#call(ContextAction) - * @see Context#call(ContextFactory factory, Callable callable, Scriptable scope, Scriptable - * thisObj, Object[] args) - */ - public final T call(ContextAction action) { - return Context.call(this, action); - } - - /** - * Get a context associated with the current thread, creating one if need be. The Context stores - * the execution state of the JavaScript engine, so it is required that the context be entered - * before execution may begin. Once a thread has entered a Context, then getCurrentContext() may - * be called to find the context that is associated with the current thread. - * - *

Calling enterContext() will return either the Context currently associated - * with the thread, or will create a new context and associate it with the current thread. Each - * call to enterContext() must have a matching call to {@link Context#exit()}. - * - *

-     *      Context cx = contextFactory.enterContext();
-     *      try {
-     *          ...
-     *          cx.evaluateString(...);
-     *      } finally {
-     *          Context.exit();
-     *      }
-     * 
- * - * Instead of using enterContext(), exit() pair consider using {@link - * #call(ContextAction)} which guarantees proper association of Context instances with the - * current thread. With this method the above example becomes: - * - *
-     *      ContextFactory.call(new ContextAction() {
-     *          public Object run(Context cx) {
-     *              ...
-     *              cx.evaluateString(...);
-     *              return null;
-     *          }
-     *      });
-     * 
- * - * @return a Context associated with the current thread - * @see Context#getCurrentContext() - * @see Context#exit() - * @see #call(ContextAction) - */ - public Context enterContext() { - return enterContext(null); - } - - /** - * @deprecated use {@link #enterContext()} instead - * @return a Context associated with the current thread - */ - @Deprecated - public final Context enter() { - return enterContext(null); - } - - /** @deprecated Use {@link Context#exit()} instead. */ - @Deprecated - public final void exit() { - Context.exit(); - } - - /** - * Get a Context associated with the current thread, using the given Context if need be. - * - *

The same as enterContext() 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 - * @see #enterContext() - * @see #call(ContextAction) - * @throws IllegalStateException if cx is already associated with a different - * thread - */ - public final Context enterContext(Context cx) { - return Context.enter(cx, this); - } -} diff --git a/src/main/java/org/mozilla/javascript/ContextListener.java b/src/main/java/org/mozilla/javascript/ContextListener.java deleted file mode 100644 index 3e97ef70..00000000 --- a/src/main/java/org/mozilla/javascript/ContextListener.java +++ /dev/null @@ -1,31 +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; - -/** - * @deprecated Embeddings that wish to customize newly created - * {@link Context} instances should implement - * {@link ContextFactory.Listener}. - */ -@Deprecated -public interface ContextListener extends ContextFactory.Listener -{ - - /** - * @deprecated Rhino runtime never calls the method. - */ - @Deprecated - public void contextEntered(Context cx); - - /** - * @deprecated Rhino runtime never calls the method. - */ - @Deprecated - public void contextExited(Context cx); -} diff --git a/src/main/java/org/mozilla/javascript/ContinuationPending.java b/src/main/java/org/mozilla/javascript/ContinuationPending.java deleted file mode 100644 index 72d488d2..00000000 --- a/src/main/java/org/mozilla/javascript/ContinuationPending.java +++ /dev/null @@ -1,76 +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; - -/** - * Exception thrown by - * {@link org.mozilla.javascript.Context#executeScriptWithContinuations(Script, Scriptable)} - * and {@link org.mozilla.javascript.Context#callFunctionWithContinuations(Callable, Scriptable, Object[])} - * when execution encounters a continuation captured by - * {@link org.mozilla.javascript.Context#captureContinuation()}. - * Exception will contain the captured state needed to restart the continuation - * with {@link org.mozilla.javascript.Context#resumeContinuation(Object, Scriptable, Object)}. - * @author Norris Boyd - */ -public class ContinuationPending extends RuntimeException { - private static final long serialVersionUID = 4956008116771118856L; - private NativeContinuation continuationState; - private Object applicationState; - - /** - * Construct a ContinuationPending exception. Internal call only; - * users of the API should get continuations created on their behalf by - * calling {@link org.mozilla.javascript.Context#executeScriptWithContinuations(Script, Scriptable)} - * and {@link org.mozilla.javascript.Context#callFunctionWithContinuations(Callable, Scriptable, Object[])} - * Creating subclasses allowed. - * @param continuationState Internal Continuation object - */ - protected ContinuationPending(NativeContinuation continuationState) { - this.continuationState = continuationState; - } - - /** - * Get continuation object. The only - * use for this object is to be passed to - * {@link org.mozilla.javascript.Context#resumeContinuation(Object, Scriptable, Object)}. - * @return continuation object - */ - public Object getContinuation() { - return continuationState; - } - - /** - * Set continuation object. Allows subclasses to modify the internal state. - * @param continuation object - */ - public void setContinuation(NativeContinuation continuation) { - this.continuationState = continuation; - } - - /** - * @return internal continuation state - */ - NativeContinuation getContinuationState() { - return continuationState; - } - - /** - * Store an arbitrary object that applications can use to associate - * their state with the continuation. - * @param applicationState arbitrary application state - */ - public void setApplicationState(Object applicationState) { - this.applicationState = applicationState; - } - - /** - * @return arbitrary application state - */ - public Object getApplicationState() { - return applicationState; - } -} diff --git a/src/main/java/org/mozilla/javascript/DToA.java b/src/main/java/org/mozilla/javascript/DToA.java deleted file mode 100644 index 195243de..00000000 --- a/src/main/java/org/mozilla/javascript/DToA.java +++ /dev/null @@ -1,1226 +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/. */ - -/**************************************************************** - * - * The author of this software is David M. Gay. - * - * Copyright (c) 1991, 2000, 2001 by Lucent Technologies. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY - * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - * - ***************************************************************/ - -package org.mozilla.javascript; - -import java.math.BigInteger; - -class DToA { - - - private static char BASEDIGIT(int digit) { - return (char)((digit >= 10) ? 'a' - 10 + digit : '0' + digit); - } - - static final int - DTOSTR_STANDARD = 0, /* Either fixed or exponential format; round-trip */ - DTOSTR_STANDARD_EXPONENTIAL = 1, /* Always exponential format; round-trip */ - DTOSTR_FIXED = 2, /* Round to digits after the decimal point; exponential if number is large */ - DTOSTR_EXPONENTIAL = 3, /* Always exponential format; significant digits */ - DTOSTR_PRECISION = 4; /* Either fixed or exponential format; significant digits */ - - - private static final int Frac_mask = 0xfffff; - private static final int Exp_shift = 20; - private static final int Exp_msk1 = 0x100000; - - private static final long Frac_maskL = 0xfffffffffffffL; - private static final int Exp_shiftL = 52; - private static final long Exp_msk1L = 0x10000000000000L; - - private static final int Bias = 1023; - private static final int P = 53; - - private static final int Exp_shift1 = 20; - private static final int Exp_mask = 0x7ff00000; - private static final int Exp_mask_shifted = 0x7ff; - private static final int Bndry_mask = 0xfffff; - private static final int Log2P = 1; - - private static final int Sign_bit = 0x80000000; - private static final int Exp_11 = 0x3ff00000; - private static final int Ten_pmax = 22; - private static final int Quick_max = 14; - private static final int Bletch = 0x10; - private static final int Frac_mask1 = 0xfffff; - private static final int Int_max = 14; - private static final int n_bigtens = 5; - - - private static final double tens[] = { - 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, - 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, - 1e20, 1e21, 1e22 - }; - - private static final double bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; - - private static int lo0bits(int y) - { - int k; - int x = y; - - if ((x & 7) != 0) { - if ((x & 1) != 0) - return 0; - if ((x & 2) != 0) { - return 1; - } - return 2; - } - k = 0; - if ((x & 0xffff) == 0) { - k = 16; - x >>>= 16; - } - if ((x & 0xff) == 0) { - k += 8; - x >>>= 8; - } - if ((x & 0xf) == 0) { - k += 4; - x >>>= 4; - } - if ((x & 0x3) == 0) { - k += 2; - x >>>= 2; - } - if ((x & 1) == 0) { - k++; - x >>>= 1; - if ((x & 1) == 0) - return 32; - } - return k; - } - - /* Return the number (0 through 32) of most significant zero bits in x. */ - private static int hi0bits(int x) - { - int k = 0; - - if ((x & 0xffff0000) == 0) { - k = 16; - x <<= 16; - } - if ((x & 0xff000000) == 0) { - k += 8; - x <<= 8; - } - if ((x & 0xf0000000) == 0) { - k += 4; - x <<= 4; - } - if ((x & 0xc0000000) == 0) { - k += 2; - x <<= 2; - } - if ((x & 0x80000000) == 0) { - k++; - if ((x & 0x40000000) == 0) - return 32; - } - return k; - } - - private static void stuffBits(byte bits[], int offset, int val) - { - bits[offset] = (byte)(val >> 24); - bits[offset + 1] = (byte)(val >> 16); - bits[offset + 2] = (byte)(val >> 8); - bits[offset + 3] = (byte)(val); - } - - /* Convert d into the form b*2^e, where b is an odd integer. b is the returned - * Bigint and e is the returned binary exponent. Return the number of significant - * bits in b in bits. d must be finite and nonzero. */ - private static BigInteger d2b(double d, int[] e, int[] bits) - { - byte dbl_bits[]; - int i, k, y, z, de; - long dBits = Double.doubleToLongBits(d); - int d0 = (int)(dBits >>> 32); - int d1 = (int)(dBits); - - z = d0 & Frac_mask; - d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ - - if ((de = (d0 >>> Exp_shift)) != 0) - z |= Exp_msk1; - - if ((y = d1) != 0) { - dbl_bits = new byte[8]; - k = lo0bits(y); - y >>>= k; - if (k != 0) { - stuffBits(dbl_bits, 4, y | z << (32 - k)); - z >>= k; - } - else - stuffBits(dbl_bits, 4, y); - stuffBits(dbl_bits, 0, z); - i = (z != 0) ? 2 : 1; - } - else { - // JS_ASSERT(z); - dbl_bits = new byte[4]; - k = lo0bits(z); - z >>>= k; - stuffBits(dbl_bits, 0, z); - k += 32; - i = 1; - } - if (de != 0) { - e[0] = de - Bias - (P-1) + k; - bits[0] = P - k; - } - else { - e[0] = de - Bias - (P-1) + 1 + k; - bits[0] = 32*i - hi0bits(z); - } - return new BigInteger(dbl_bits); - } - - static String JS_dtobasestr(int base, double d) - { - if (!(2 <= base && base <= 36)) - throw new IllegalArgumentException("Bad base: "+base); - - /* Check for Infinity and NaN */ - if (Double.isNaN(d)) { - return "NaN"; - } else if (Double.isInfinite(d)) { - return (d > 0.0) ? "Infinity" : "-Infinity"; - } else if (d == 0) { - // ALERT: should it distinguish -0.0 from +0.0 ? - return "0"; - } - - boolean negative; - if (d >= 0.0) { - negative = false; - } else { - negative = true; - d = -d; - } - - /* Get the integer part of d including '-' sign. */ - String intDigits; - - double dfloor = Math.floor(d); - long lfloor = (long)dfloor; - if (lfloor == dfloor) { - // int part fits long - intDigits = Long.toString((negative) ? -lfloor : lfloor, base); - } else { - // BigInteger should be used - long floorBits = Double.doubleToLongBits(dfloor); - int exp = (int)(floorBits >> Exp_shiftL) & Exp_mask_shifted; - long mantissa; - if (exp == 0) { - mantissa = (floorBits & Frac_maskL) << 1; - } else { - mantissa = (floorBits & Frac_maskL) | Exp_msk1L; - } - if (negative) { - mantissa = -mantissa; - } - exp -= 1075; - BigInteger x = BigInteger.valueOf(mantissa); - if (exp > 0) { - x = x.shiftLeft(exp); - } else if (exp < 0) { - x = x.shiftRight(-exp); - } - intDigits = x.toString(base); - } - - if (d == dfloor) { - // No fraction part - return intDigits; - } - /* We have a fraction. */ - - StringBuilder buffer; /* The output string */ - int digit; - double df; /* The fractional part of d */ - BigInteger b; - - buffer = new StringBuilder(); - buffer.append(intDigits).append('.'); - df = d - dfloor; - - long dBits = Double.doubleToLongBits(d); - int word0 = (int)(dBits >> 32); - int word1 = (int)(dBits); - - int[] e = new int[1]; - int[] bbits = new int[1]; - - b = d2b(df, e, bbits); -// JS_ASSERT(e < 0); - /* At this point df = b * 2^e. e must be less than zero because 0 < df < 1. */ - - int s2 = -(word0 >>> Exp_shift1 & Exp_mask >> Exp_shift1); - if (s2 == 0) - s2 = -1; - s2 += Bias + P; - /* 1/2^s2 = (nextDouble(d) - d)/2 */ -// JS_ASSERT(-s2 < e); - BigInteger mlo = BigInteger.valueOf(1); - BigInteger mhi = mlo; - if ((word1 == 0) && ((word0 & Bndry_mask) == 0) - && ((word0 & (Exp_mask & Exp_mask << 1)) != 0)) { - /* The special case. Here we want to be within a quarter of the last input - significant digit instead of one half of it when the output string's value is less than d. */ - s2 += Log2P; - mhi = BigInteger.valueOf(1< df = b/2^s2 > 0; - * (d - prevDouble(d))/2 = mlo/2^s2; - * (nextDouble(d) - d)/2 = mhi/2^s2. */ - BigInteger bigBase = BigInteger.valueOf(base); - - boolean done = false; - do { - b = b.multiply(bigBase); - BigInteger[] divResult = b.divideAndRemainder(s); - b = divResult[1]; - digit = (char)(divResult[0].intValue()); - if (mlo == mhi) - mlo = mhi = mlo.multiply(bigBase); - else { - mlo = mlo.multiply(bigBase); - mhi = mhi.multiply(bigBase); - } - - /* Do we yet have the shortest string that will round to d? */ - int j = b.compareTo(mlo); - /* j is b/2^s2 compared with mlo/2^s2. */ - BigInteger delta = s.subtract(mhi); - int j1 = (delta.signum() <= 0) ? 1 : b.compareTo(delta); - /* j1 is b/2^s2 compared with 1 - mhi/2^s2. */ - if (j1 == 0 && ((word1 & 1) == 0)) { - if (j > 0) - digit++; - done = true; - } else - if (j < 0 || (j == 0 && ((word1 & 1) == 0))) { - if (j1 > 0) { - /* Either dig or dig+1 would work here as the least significant digit. - Use whichever would produce an output value closer to d. */ - b = b.shiftLeft(1); - j1 = b.compareTo(s); - if (j1 > 0) /* The even test (|| (j1 == 0 && (digit & 1))) is not here because it messes up odd base output - * such as 3.5 in base 3. */ - digit++; - } - done = true; - } else if (j1 > 0) { - digit++; - done = true; - } -// JS_ASSERT(digit < (uint32)base); - buffer.append(BASEDIGIT(digit)); - } while (!done); - - return buffer.toString(); - } - - /* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. - * - * Inspired by "How to Print Floating-Point Numbers Accurately" by - * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 92-101]. - * - * Modifications: - * 1. Rather than iterating, we use a simple numeric overestimate - * to determine k = floor(log10(d)). We scale relevant - * quantities using O(log2(k)) rather than O(k) multiplications. - * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't - * try to generate digits strictly left to right. Instead, we - * compute with fewer bits and propagate the carry if necessary - * when rounding the final digit up. This is often faster. - * 3. Under the assumption that input will be rounded nearest, - * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. - * That is, we allow equality in stopping tests when the - * round-nearest rule will give the same floating-point value - * as would satisfaction of the stopping test with strict - * inequality. - * 4. We remove common factors of powers of 2 from relevant - * quantities. - * 5. When converting floating-point integers less than 1e16, - * we use floating-point arithmetic rather than resorting - * to multiple-precision integers. - * 6. When asked to produce fewer than 15 digits, we first try - * to get by with floating-point arithmetic; we resort to - * multiple-precision integer arithmetic only if we cannot - * guarantee that the floating-point calculation has given - * the correctly rounded result. For k requested digits and - * "uniformly" distributed input, the probability is - * something like 10^(k-15) that we must resort to the Long - * calculation. - */ - - static int word0(double d) - { - long dBits = Double.doubleToLongBits(d); - return (int)(dBits >> 32); - } - - static double setWord0(double d, int i) - { - long dBits = Double.doubleToLongBits(d); - dBits = ((long)i << 32) | (dBits & 0x0FFFFFFFFL); - return Double.longBitsToDouble(dBits); - } - - static int word1(double d) - { - long dBits = Double.doubleToLongBits(d); - return (int)(dBits); - } - - /* Return b * 5^k. k must be nonnegative. */ - // XXXX the C version built a cache of these - static BigInteger pow5mult(BigInteger b, int k) - { - return b.multiply(BigInteger.valueOf(5).pow(k)); - } - - static boolean roundOff(StringBuilder buf) - { - int i = buf.length(); - while (i != 0) { - --i; - char c = buf.charAt(i); - if (c != '9') { - buf.setCharAt(i, (char)(c + 1)); - buf.setLength(i + 1); - return false; - } - } - buf.setLength(0); - return true; - } - - /* Always emits at least one digit. */ - /* If biasUp is set, then rounding in modes 2 and 3 will round away from zero - * when the number is exactly halfway between two representable values. For example, - * rounding 2.5 to zero digits after the decimal point will return 3 and not 2. - * 2.49 will still round to 2, and 2.51 will still round to 3. */ - /* bufsize should be at least 20 for modes 0 and 1. For the other modes, - * bufsize should be two greater than the maximum number of output characters expected. */ - static int - JS_dtoa(double d, int mode, boolean biasUp, int ndigits, - boolean[] sign, StringBuilder buf) - { - /* Arguments ndigits, decpt, sign are similar to those - of ecvt and fcvt; trailing zeros are suppressed from - the returned string. If not null, *rve is set to point - to the end of the return value. If d is +-Infinity or NaN, - then *decpt is set to 9999. - - mode: - 0 ==> shortest string that yields d when read in - and rounded to nearest. - 1 ==> like 0, but with Steele & White stopping rule; - e.g. with IEEE P754 arithmetic , mode 0 gives - 1e23 whereas mode 1 gives 9.999999999999999e22. - 2 ==> max(1,ndigits) significant digits. This gives a - return value similar to that of ecvt, except - that trailing zeros are suppressed. - 3 ==> through ndigits past the decimal point. This - gives a return value similar to that from fcvt, - except that trailing zeros are suppressed, and - ndigits can be negative. - 4-9 should give the same return values as 2-3, i.e., - 4 <= mode <= 9 ==> same return as mode - 2 + (mode & 1). These modes are mainly for - debugging; often they run slower but sometimes - faster than modes 2-3. - 4,5,8,9 ==> left-to-right digit generation. - 6-9 ==> don't try fast floating-point estimate - (if applicable). - - Values of mode other than 0-9 are treated as mode 0. - - Sufficient space is allocated to the return value - to hold the suppressed trailing zeros. - */ - - int b2, b5, i, ieps, ilim, ilim0, ilim1, - j, j1, k, k0, m2, m5, s2, s5; - char dig; - long L; - long x; - BigInteger b, b1, delta, mlo, mhi, S; - int[] be = new int[1]; - int[] bbits = new int[1]; - double d2, ds, eps; - boolean spec_case, denorm, k_check, try_quick, leftright; - - if ((word0(d) & Sign_bit) != 0) { - /* set sign for everything, including 0's and NaNs */ - sign[0] = true; - // word0(d) &= ~Sign_bit; /* clear sign bit */ - d = setWord0(d, word0(d) & ~Sign_bit); - } - else - sign[0] = false; - - if ((word0(d) & Exp_mask) == Exp_mask) { - /* Infinity or NaN */ - buf.append(((word1(d) == 0) && ((word0(d) & Frac_mask) == 0)) ? "Infinity" : "NaN"); - return 9999; - } - if (d == 0) { -// no_digits: - buf.setLength(0); - buf.append('0'); /* copy "0" to buffer */ - return 1; - } - - b = d2b(d, be, bbits); - if ((i = (word0(d) >>> Exp_shift1 & (Exp_mask>>Exp_shift1))) != 0) { - d2 = setWord0(d, (word0(d) & Frac_mask1) | Exp_11); - /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 - * log10(x) = log(x) / log(10) - * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) - * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) - * - * This suggests computing an approximation k to log10(d) by - * - * k = (i - Bias)*0.301029995663981 - * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); - * - * We want k to be too large rather than too small. - * The error in the first-order Taylor series approximation - * is in our favor, so we just round up the constant enough - * to compensate for any error in the multiplication of - * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, - * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, - * adding 1e-13 to the constant term more than suffices. - * Hence we adjust the constant term to 0.1760912590558. - * (We could get a more accurate k by invoking log10, - * but this is probably not worthwhile.) - */ - i -= Bias; - denorm = false; - } - else { - /* d is denormalized */ - i = bbits[0] + be[0] + (Bias + (P-1) - 1); - x = (i > 32) - ? ((long) word0(d)) << (64 - i) | word1(d) >>> (i - 32) - : ((long) word1(d)) << (32 - i); -// d2 = x; -// word0(d2) -= 31*Exp_msk1; /* adjust exponent */ - d2 = setWord0(x, word0(x) - 31*Exp_msk1); - i -= (Bias + (P-1) - 1) + 1; - denorm = true; - } - /* At this point d = f*2^i, where 1 <= f < 2. d2 is an approximation of f. */ - ds = (d2-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; - k = (int)ds; - if (ds < 0.0 && ds != k) - k--; /* want k = floor(ds) */ - k_check = true; - if (k >= 0 && k <= Ten_pmax) { - if (d < tens[k]) - k--; - k_check = false; - } - /* At this point floor(log10(d)) <= k <= floor(log10(d))+1. - If k_check is zero, we're guaranteed that k = floor(log10(d)). */ - j = bbits[0] - i - 1; - /* At this point d = b/2^j, where b is an odd integer. */ - if (j >= 0) { - b2 = 0; - s2 = j; - } - else { - b2 = -j; - s2 = 0; - } - if (k >= 0) { - b5 = 0; - s5 = k; - s2 += k; - } - else { - b2 -= k; - b5 = -k; - s5 = 0; - } - /* At this point d/10^k = (b * 2^b2 * 5^b5) / (2^s2 * 5^s5), where b is an odd integer, - b2 >= 0, b5 >= 0, s2 >= 0, and s5 >= 0. */ - if (mode < 0 || mode > 9) - mode = 0; - try_quick = true; - if (mode > 5) { - mode -= 4; - try_quick = false; - } - leftright = true; - ilim = ilim1 = 0; - switch(mode) { - case 0: - case 1: - ilim = ilim1 = -1; - i = 18; - ndigits = 0; - break; - case 2: - leftright = false; - /* fall through */ - case 4: - if (ndigits <= 0) - ndigits = 1; - ilim = ilim1 = i = ndigits; - break; - case 3: - leftright = false; - /* fall through */ - case 5: - i = ndigits + k + 1; - ilim = i; - ilim1 = i - 1; - if (i <= 0) - i = 1; - } - /* ilim is the maximum number of significant digits we want, based on k and ndigits. */ - /* ilim1 is the maximum number of significant digits we want, based on k and ndigits, - when it turns out that k was computed too high by one. */ - - boolean fast_failed = false; - if (ilim >= 0 && ilim <= Quick_max && try_quick) { - - /* Try to get by with floating-point arithmetic. */ - - i = 0; - d2 = d; - k0 = k; - ilim0 = ilim; - ieps = 2; /* conservative */ - /* Divide d by 10^k, keeping track of the roundoff error and avoiding overflows. */ - if (k > 0) { - ds = tens[k&0xf]; - j = k >> 4; - if ((j & Bletch) != 0) { - /* prevent overflows */ - j &= Bletch - 1; - d /= bigtens[n_bigtens-1]; - ieps++; - } - for(; (j != 0); j >>= 1, i++) - if ((j & 1) != 0) { - ieps++; - ds *= bigtens[i]; - } - d /= ds; - } - else if ((j1 = -k) != 0) { - d *= tens[j1 & 0xf]; - for(j = j1 >> 4; (j != 0); j >>= 1, i++) - if ((j & 1) != 0) { - ieps++; - d *= bigtens[i]; - } - } - /* Check that k was computed correctly. */ - if (k_check && d < 1.0 && ilim > 0) { - if (ilim1 <= 0) - fast_failed = true; - else { - ilim = ilim1; - k--; - d *= 10.; - ieps++; - } - } - /* eps bounds the cumulative error. */ -// eps = ieps*d + 7.0; -// word0(eps) -= (P-1)*Exp_msk1; - eps = ieps*d + 7.0; - eps = setWord0(eps, word0(eps) - (P-1)*Exp_msk1); - if (ilim == 0) { - S = mhi = null; - d -= 5.0; - if (d > eps) { - buf.append('1'); - k++; - return k + 1; - } - if (d < -eps) { - buf.setLength(0); - buf.append('0'); /* copy "0" to buffer */ - return 1; - } - fast_failed = true; - } - if (!fast_failed) { - fast_failed = true; - if (leftright) { - /* Use Steele & White method of only - * generating digits needed. - */ - eps = 0.5/tens[ilim-1] - eps; - for(i = 0;;) { - L = (long)d; - d -= L; - buf.append((char)('0' + L)); - if (d < eps) { - return k + 1; - } - if (1.0 - d < eps) { -// goto bump_up; - char lastCh; - while (true) { - lastCh = buf.charAt(buf.length() - 1); - buf.setLength(buf.length() - 1); - if (lastCh != '9') break; - if (buf.length() == 0) { - k++; - lastCh = '0'; - break; - } - } - buf.append((char)(lastCh + 1)); - return k + 1; - } - if (++i >= ilim) - break; - eps *= 10.0; - d *= 10.0; - } - } - else { - /* Generate ilim digits, then fix them up. */ - eps *= tens[ilim-1]; - for(i = 1;; i++, d *= 10.0) { - L = (long)d; - d -= L; - buf.append((char)('0' + L)); - if (i == ilim) { - if (d > 0.5 + eps) { -// goto bump_up; - char lastCh; - while (true) { - lastCh = buf.charAt(buf.length() - 1); - buf.setLength(buf.length() - 1); - if (lastCh != '9') break; - if (buf.length() == 0) { - k++; - lastCh = '0'; - break; - } - } - buf.append((char)(lastCh + 1)); - return k + 1; - } - else - if (d < 0.5 - eps) { - stripTrailingZeroes(buf); -// while(*--s == '0') ; -// s++; - return k + 1; - } - break; - } - } - } - } - if (fast_failed) { - buf.setLength(0); - d = d2; - k = k0; - ilim = ilim0; - } - } - - /* Do we have a "small" integer? */ - - if (be[0] >= 0 && k <= Int_max) { - /* Yes. */ - ds = tens[k]; - if (ndigits < 0 && ilim <= 0) { - S = mhi = null; - if (ilim < 0 || d < 5*ds || (!biasUp && d == 5*ds)) { - buf.setLength(0); - buf.append('0'); /* copy "0" to buffer */ - return 1; - } - buf.append('1'); - k++; - return k + 1; - } - for(i = 1;; i++) { - L = (long) (d / ds); - d -= L*ds; - buf.append((char)('0' + L)); - if (i == ilim) { - d += d; - if ((d > ds) || (d == ds && (((L & 1) != 0) || biasUp))) { -// bump_up: -// while(*--s == '9') -// if (s == buf) { -// k++; -// *s = '0'; -// break; -// } -// ++*s++; - char lastCh; - while (true) { - lastCh = buf.charAt(buf.length() - 1); - buf.setLength(buf.length() - 1); - if (lastCh != '9') break; - if (buf.length() == 0) { - k++; - lastCh = '0'; - break; - } - } - buf.append((char)(lastCh + 1)); - } - break; - } - d *= 10.0; - if (d == 0) - break; - } - return k + 1; - } - - m2 = b2; - m5 = b5; - mhi = mlo = null; - if (leftright) { - if (mode < 2) { - i = (denorm) ? be[0] + (Bias + (P-1) - 1 + 1) : 1 + P - bbits[0]; - /* i is 1 plus the number of trailing zero bits in d's significand. Thus, - (2^m2 * 5^m5) / (2^(s2+i) * 5^s5) = (1/2 lsb of d)/10^k. */ - } - else { - j = ilim - 1; - if (m5 >= j) - m5 -= j; - else { - s5 += j -= m5; - b5 += j; - m5 = 0; - } - if ((i = ilim) < 0) { - m2 -= i; - i = 0; - } - /* (2^m2 * 5^m5) / (2^(s2+i) * 5^s5) = (1/2 * 10^(1-ilim))/10^k. */ - } - b2 += i; - s2 += i; - mhi = BigInteger.valueOf(1); - /* (mhi * 2^m2 * 5^m5) / (2^s2 * 5^s5) = one-half of last printed (when mode >= 2) or - input (when mode < 2) significant digit, divided by 10^k. */ - } - /* We still have d/10^k = (b * 2^b2 * 5^b5) / (2^s2 * 5^s5). Reduce common factors in - b2, m2, and s2 without changing the equalities. */ - if (m2 > 0 && s2 > 0) { - i = (m2 < s2) ? m2 : s2; - b2 -= i; - m2 -= i; - s2 -= i; - } - - /* Fold b5 into b and m5 into mhi. */ - if (b5 > 0) { - if (leftright) { - if (m5 > 0) { - mhi = pow5mult(mhi, m5); - b1 = mhi.multiply(b); - b = b1; - } - if ((j = b5 - m5) != 0) - b = pow5mult(b, j); - } - else - b = pow5mult(b, b5); - } - /* Now we have d/10^k = (b * 2^b2) / (2^s2 * 5^s5) and - (mhi * 2^m2) / (2^s2 * 5^s5) = one-half of last printed or input significant digit, divided by 10^k. */ - - S = BigInteger.valueOf(1); - if (s5 > 0) - S = pow5mult(S, s5); - /* Now we have d/10^k = (b * 2^b2) / (S * 2^s2) and - (mhi * 2^m2) / (S * 2^s2) = one-half of last printed or input significant digit, divided by 10^k. */ - - /* Check for special case that d is a normalized power of 2. */ - spec_case = false; - if (mode < 2) { - if ( (word1(d) == 0) && ((word0(d) & Bndry_mask) == 0) - && ((word0(d) & (Exp_mask & Exp_mask << 1)) != 0) - ) { - /* The special case. Here we want to be within a quarter of the last input - significant digit instead of one half of it when the decimal output string's value is less than d. */ - b2 += Log2P; - s2 += Log2P; - spec_case = true; - } - } - - /* Arrange for convenient computation of quotients: - * shift left if necessary so divisor has 4 leading 0 bits. - * - * Perhaps we should just compute leading 28 bits of S once - * and for all and pass them and a shift to quorem, so it - * can do shifts and ors to compute the numerator for q. - */ - byte [] S_bytes = S.toByteArray(); - int S_hiWord = 0; - for (int idx = 0; idx < 4; idx++) { - S_hiWord = (S_hiWord << 8); - if (idx < S_bytes.length) - S_hiWord |= (S_bytes[idx] & 0xFF); - } - if ((i = (((s5 != 0) ? 32 - hi0bits(S_hiWord) : 1) + s2) & 0x1f) != 0) - i = 32 - i; - /* i is the number of leading zero bits in the most significant word of S*2^s2. */ - if (i > 4) { - i -= 4; - b2 += i; - m2 += i; - s2 += i; - } - else if (i < 4) { - i += 28; - b2 += i; - m2 += i; - s2 += i; - } - /* Now S*2^s2 has exactly four leading zero bits in its most significant word. */ - if (b2 > 0) - b = b.shiftLeft(b2); - if (s2 > 0) - S = S.shiftLeft(s2); - /* Now we have d/10^k = b/S and - (mhi * 2^m2) / S = maximum acceptable error, divided by 10^k. */ - if (k_check) { - if (b.compareTo(S) < 0) { - k--; - b = b.multiply(BigInteger.valueOf(10)); /* we botched the k estimate */ - if (leftright) - mhi = mhi.multiply(BigInteger.valueOf(10)); - ilim = ilim1; - } - } - /* At this point 1 <= d/10^k = b/S < 10. */ - - if (ilim <= 0 && mode > 2) { - /* We're doing fixed-mode output and d is less than the minimum nonzero output in this mode. - Output either zero or the minimum nonzero output depending on which is closer to d. */ - if ((ilim < 0 ) - || ((i = b.compareTo(S = S.multiply(BigInteger.valueOf(5)))) < 0) - || ((i == 0 && !biasUp))) { - /* Always emit at least one digit. If the number appears to be zero - using the current mode, then emit one '0' digit and set decpt to 1. */ - /*no_digits: - k = -1 - ndigits; - goto ret; */ - buf.setLength(0); - buf.append('0'); /* copy "0" to buffer */ - return 1; -// goto no_digits; - } -// one_digit: - buf.append('1'); - k++; - return k + 1; - } - if (leftright) { - if (m2 > 0) - mhi = mhi.shiftLeft(m2); - - /* Compute mlo -- check for special case - * that d is a normalized power of 2. - */ - - mlo = mhi; - if (spec_case) { - mhi = mlo; - mhi = mhi.shiftLeft(Log2P); - } - /* mlo/S = maximum acceptable error, divided by 10^k, if the output is less than d. */ - /* mhi/S = maximum acceptable error, divided by 10^k, if the output is greater than d. */ - - for(i = 1;;i++) { - BigInteger[] divResult = b.divideAndRemainder(S); - b = divResult[1]; - dig = (char)(divResult[0].intValue() + '0'); - /* Do we yet have the shortest decimal string - * that will round to d? - */ - j = b.compareTo(mlo); - /* j is b/S compared with mlo/S. */ - delta = S.subtract(mhi); - j1 = (delta.signum() <= 0) ? 1 : b.compareTo(delta); - /* j1 is b/S compared with 1 - mhi/S. */ - if ((j1 == 0) && (mode == 0) && ((word1(d) & 1) == 0)) { - if (dig == '9') { - buf.append('9'); - if (roundOff(buf)) { - k++; - buf.append('1'); - } - return k + 1; -// goto round_9_up; - } - if (j > 0) - dig++; - buf.append(dig); - return k + 1; - } - if ((j < 0) - || ((j == 0) - && (mode == 0) - && ((word1(d) & 1) == 0) - )) { - if (j1 > 0) { - /* Either dig or dig+1 would work here as the least significant decimal digit. - Use whichever would produce a decimal value closer to d. */ - b = b.shiftLeft(1); - j1 = b.compareTo(S); - if (((j1 > 0) || (j1 == 0 && (((dig & 1) == 1) || biasUp))) - && (dig++ == '9')) { - buf.append('9'); - if (roundOff(buf)) { - k++; - buf.append('1'); - } - return k + 1; -// goto round_9_up; - } - } - buf.append(dig); - return k + 1; - } - if (j1 > 0) { - if (dig == '9') { /* possible if i == 1 */ -// round_9_up: -// *s++ = '9'; -// goto roundoff; - buf.append('9'); - if (roundOff(buf)) { - k++; - buf.append('1'); - } - return k + 1; - } - buf.append((char)(dig + 1)); - return k + 1; - } - buf.append(dig); - if (i == ilim) - break; - b = b.multiply(BigInteger.valueOf(10)); - if (mlo == mhi) - mlo = mhi = mhi.multiply(BigInteger.valueOf(10)); - else { - mlo = mlo.multiply(BigInteger.valueOf(10)); - mhi = mhi.multiply(BigInteger.valueOf(10)); - } - } - } - else - for(i = 1;; i++) { -// (char)(dig = quorem(b,S) + '0'); - BigInteger[] divResult = b.divideAndRemainder(S); - b = divResult[1]; - dig = (char)(divResult[0].intValue() + '0'); - buf.append(dig); - if (i >= ilim) - break; - b = b.multiply(BigInteger.valueOf(10)); - } - - /* Round off last digit */ - - b = b.shiftLeft(1); - j = b.compareTo(S); - if ((j > 0) || (j == 0 && (((dig & 1) == 1) || biasUp))) { -// roundoff: -// while(*--s == '9') -// if (s == buf) { -// k++; -// *s++ = '1'; -// goto ret; -// } -// ++*s++; - if (roundOff(buf)) { - k++; - buf.append('1'); - return k + 1; - } - } - else { - stripTrailingZeroes(buf); -// while(*--s == '0') ; -// s++; - } -// ret: -// Bfree(S); -// if (mhi) { -// if (mlo && mlo != mhi) -// Bfree(mlo); -// Bfree(mhi); -// } -// ret1: -// Bfree(b); -// JS_ASSERT(s < buf + bufsize); - return k + 1; - } - - private static void - stripTrailingZeroes(StringBuilder buf) - { -// while(*--s == '0') ; -// s++; - int bl = buf.length(); - while(bl-->0 && buf.charAt(bl) == '0') { - // empty - } - buf.setLength(bl + 1); - } - - /* Mapping of JSDToStrMode -> JS_dtoa mode */ - private static final int dtoaModes[] = { - 0, /* DTOSTR_STANDARD */ - 0, /* DTOSTR_STANDARD_EXPONENTIAL, */ - 3, /* DTOSTR_FIXED, */ - 2, /* DTOSTR_EXPONENTIAL, */ - 2}; /* DTOSTR_PRECISION */ - - static void - JS_dtostr(StringBuilder buffer, int mode, int precision, double d) - { - int decPt; /* Position of decimal point relative to first digit returned by JS_dtoa */ - boolean[] sign = new boolean[1]; /* true if the sign bit was set in d */ - int nDigits; /* Number of significand digits returned by JS_dtoa */ - -// JS_ASSERT(bufferSize >= (size_t)(mode <= DTOSTR_STANDARD_EXPONENTIAL ? DTOSTR_STANDARD_BUFFER_SIZE : -// DTOSTR_VARIABLE_BUFFER_SIZE(precision))); - - if (mode == DTOSTR_FIXED && (d >= 1e21 || d <= -1e21)) - mode = DTOSTR_STANDARD; /* Change mode here rather than below because the buffer may not be large enough to hold a large integer. */ - - decPt = JS_dtoa(d, dtoaModes[mode], mode >= DTOSTR_FIXED, precision, sign, buffer); - nDigits = buffer.length(); - - /* If Infinity, -Infinity, or NaN, return the string regardless of the mode. */ - if (decPt != 9999) { - boolean exponentialNotation = false; - int minNDigits = 0; /* Minimum number of significand digits required by mode and precision */ - int p; - - switch (mode) { - case DTOSTR_STANDARD: - if (decPt < -5 || decPt > 21) - exponentialNotation = true; - else - minNDigits = decPt; - break; - - case DTOSTR_FIXED: - if (precision >= 0) - minNDigits = decPt + precision; - else - minNDigits = decPt; - break; - - case DTOSTR_EXPONENTIAL: -// JS_ASSERT(precision > 0); - minNDigits = precision; - /* fall through */ - case DTOSTR_STANDARD_EXPONENTIAL: - exponentialNotation = true; - break; - - case DTOSTR_PRECISION: -// JS_ASSERT(precision > 0); - minNDigits = precision; - if (decPt < -5 || decPt > precision) - exponentialNotation = true; - break; - } - - /* If the number has fewer than minNDigits, pad it with zeros at the end */ - if (nDigits < minNDigits) { - p = minNDigits; - nDigits = minNDigits; - do { - buffer.append('0'); - } while (buffer.length() != p); - } - - if (exponentialNotation) { - /* Insert a decimal point if more than one significand digit */ - if (nDigits != 1) { - buffer.insert(1, '.'); - } - buffer.append('e'); - if ((decPt - 1) >= 0) - buffer.append('+'); - buffer.append(decPt - 1); -// JS_snprintf(numEnd, bufferSize - (numEnd - buffer), "e%+d", decPt-1); - } else if (decPt != nDigits) { - /* Some kind of a fraction in fixed notation */ -// JS_ASSERT(decPt <= nDigits); - if (decPt > 0) { - /* dd...dd . dd...dd */ - buffer.insert(decPt, '.'); - } else { - /* 0 . 00...00dd...dd */ - for (int i = 0; i < 1 - decPt; i++) - buffer.insert(0, '0'); - buffer.insert(1, '.'); - } - } - } - - /* If negative and neither -0.0 nor NaN, output a leading '-'. */ - if (sign[0] && - !(word0(d) == Sign_bit && word1(d) == 0) && - !((word0(d) & Exp_mask) == Exp_mask && - ((word1(d) != 0) || ((word0(d) & Frac_mask) != 0)))) { - buffer.insert(0, '-'); - } - } - -} - diff --git a/src/main/java/org/mozilla/javascript/Decompiler.java b/src/main/java/org/mozilla/javascript/Decompiler.java deleted file mode 100644 index 34640d31..00000000 --- a/src/main/java/org/mozilla/javascript/Decompiler.java +++ /dev/null @@ -1,893 +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 org.mozilla.javascript.ast.FunctionNode; - -/** - * The following class save decompilation information about the source. Source information is - * returned from the parser as a String associated with function nodes and with the toplevel script. - * When saved in the constant pool of a class, this string will be UTF-8 encoded, and token values - * will occupy a single byte. - * - *

Source is saved (mostly) as token numbers. The tokens saved pretty much correspond to the - * token stream of a 'canonical' representation of the input program, as directed by the parser. - * (There were a few cases where tokens could have been left out where decompiler could easily - * reconstruct them, but I left them in for clarity). (I also looked adding source collection to - * TokenStream instead, where I could have limited the changes to a few lines in getToken... but - * this wouldn't have saved any space in the resulting source representation, and would have meant - * that I'd have to duplicate parser logic in the decompiler to disambiguate situations where - * newlines are important.) The function decompile expands the tokens back into their string - * representations, using simple lookahead to correct spacing and indentation. - * - *

Assignments are saved as two-token pairs (Token.ASSIGN, op). Number tokens are stored inline, - * as a NUMBER token, a character representing the type, and either 1 or 4 characters representing - * the bit-encoding of the number. String types NAME, STRING and OBJECT are currently stored as a - * token type, followed by a character giving the length of the string (assumed to be less than - * 2^16), followed by the characters of the string inlined into the source string. Changing this to - * some reference to to the string in the compiled class' constant pool would probably save a lot of - * space... but would require some method of deriving the final constant pool entry from information - * available at parse time. - */ -public class Decompiler { - /** - * Flag to indicate that the decompilation should omit the function header and trailing brace. - */ - public static final int ONLY_BODY_FLAG = 1 << 0; - - /** Flag to indicate that the decompilation generates toSource result. */ - public static final int TO_SOURCE_FLAG = 1 << 1; - - /** Decompilation property to specify initial ident value. */ - public static final int INITIAL_INDENT_PROP = 1; - - /** Decompilation property to specify default identation offset. */ - public static final int INDENT_GAP_PROP = 2; - - /** Decompilation property to specify identation offset for case labels. */ - public static final int CASE_GAP_PROP = 3; - - // Marker to denote the last RC of function so it can be distinguished from - // the last RC of object literals in case of function expressions - private static final int FUNCTION_END = Token.LAST_TOKEN + 1; - - String getEncodedSource() { - return sourceToString(0); - } - - int getCurrentOffset() { - return sourceTop; - } - - int markFunctionStart(int functionType) { - int savedOffset = getCurrentOffset(); - if (functionType != FunctionNode.ARROW_FUNCTION) { - addToken(Token.FUNCTION); - append((char) functionType); - } - return savedOffset; - } - - int markFunctionEnd(int functionStart) { - int offset = getCurrentOffset(); - append((char) FUNCTION_END); - return offset; - } - - void addToken(int token) { - if (!(0 <= token && token <= Token.LAST_TOKEN)) throw new IllegalArgumentException(); - - append((char) token); - } - - void addEOL(int token) { - if (!(0 <= token && token <= Token.LAST_TOKEN)) throw new IllegalArgumentException(); - - append((char) token); - append((char) Token.EOL); - } - - void addName(String str) { - addToken(Token.NAME); - appendString(str); - } - - void addString(String str) { - addToken(Token.STRING); - appendString(str); - } - - void addTemplateLiteral(String str) { - addToken(Token.TEMPLATE_CHARS); - appendString(str); - } - - void addRegexp(String regexp, String flags) { - addToken(Token.REGEXP); - appendString('/' + regexp + '/' + flags); - } - - void addNumber(double n) { - addToken(Token.NUMBER); - - /* encode the number in the source stream. - * Save as NUMBER type (char | char char char char) - * where type is - * 'D' - double, 'S' - short, 'J' - long. - - * We need to retain float vs. integer type info to keep the - * behavior of liveconnect type-guessing the same after - * decompilation. (Liveconnect tries to present 1.0 to Java - * as a float/double) - * OPT: This is no longer true. We could compress the format. - - * This may not be the most space-efficient encoding; - * the chars created below may take up to 3 bytes in - * constant pool UTF-8 encoding, so a Double could take - * up to 12 bytes. - */ - - long lbits = (long) n; - if (lbits != n) { - // if it's floating point, save as a Double bit pattern. - // (12/15/97 our scanner only returns Double for f.p.) - lbits = Double.doubleToLongBits(n); - append('D'); - append((char) (lbits >> 48)); - append((char) (lbits >> 32)); - append((char) (lbits >> 16)); - append((char) lbits); - } else { - // we can ignore negative values, bc they're already prefixed - // by NEG - if (lbits < 0) Kit.codeBug(); - - // will it fit in a char? - // this gives a short encoding for integer values up to 2^16. - if (lbits <= Character.MAX_VALUE) { - append('S'); - append((char) lbits); - } else { // Integral, but won't fit in a char. Store as a long. - append('J'); - append((char) (lbits >> 48)); - append((char) (lbits >> 32)); - append((char) (lbits >> 16)); - append((char) lbits); - } - } - } - - void addBigInt(BigInteger n) { - addToken(Token.BIGINT); - appendString(n.toString()); - } - - private void appendString(String str) { - int L = str.length(); - int lengthEncodingSize = 1; - if (L >= 0x8000) { - lengthEncodingSize = 2; - } - int nextTop = sourceTop + lengthEncodingSize + L; - if (nextTop > sourceBuffer.length) { - increaseSourceCapacity(nextTop); - } - if (L >= 0x8000) { - // Use 2 chars to encode strings exceeding 32K, were the highest - // bit in the first char indicates presence of the next byte - sourceBuffer[sourceTop] = (char) (0x8000 | (L >>> 16)); - ++sourceTop; - } - sourceBuffer[sourceTop] = (char) L; - ++sourceTop; - str.getChars(0, L, sourceBuffer, sourceTop); - sourceTop = nextTop; - } - - private void append(char c) { - if (sourceTop == sourceBuffer.length) { - increaseSourceCapacity(sourceTop + 1); - } - sourceBuffer[sourceTop] = c; - ++sourceTop; - } - - private void increaseSourceCapacity(int minimalCapacity) { - // Call this only when capacity increase is must - if (minimalCapacity <= sourceBuffer.length) Kit.codeBug(); - int newCapacity = sourceBuffer.length * 2; - if (newCapacity < minimalCapacity) { - newCapacity = minimalCapacity; - } - char[] tmp = new char[newCapacity]; - System.arraycopy(sourceBuffer, 0, tmp, 0, sourceTop); - sourceBuffer = tmp; - } - - private String sourceToString(int offset) { - if (offset < 0 || sourceTop < offset) Kit.codeBug(); - return new String(sourceBuffer, offset, sourceTop - offset); - } - - /** - * Decompile the source information associated with this js function/script back into a string. - * For the most part, this just means translating tokens back to their string representations; - * there's a little bit of lookahead logic to decide the proper spacing/indentation. Most of the - * work in mapping the original source to the prettyprinted decompiled version is done by the - * parser. - * - * @param source encoded source tree presentation - * @param flags flags to select output format - * @param properties indentation properties - */ - public static String decompile(String source, int flags, UintMap properties) { - int length = source.length(); - if (length == 0) { - return ""; - } - - int indent = properties.getInt(INITIAL_INDENT_PROP, 0); - if (indent < 0) throw new IllegalArgumentException(); - int indentGap = properties.getInt(INDENT_GAP_PROP, 4); - if (indentGap < 0) throw new IllegalArgumentException(); - int caseGap = properties.getInt(CASE_GAP_PROP, 2); - if (caseGap < 0) throw new IllegalArgumentException(); - - StringBuilder result = new StringBuilder(); - boolean justFunctionBody = (0 != (flags & Decompiler.ONLY_BODY_FLAG)); - boolean toSource = (0 != (flags & Decompiler.TO_SOURCE_FLAG)); - - // Spew tokens in source, for debugging. - // as TYPE number char - if (printSource) { - System.err.println("length:" + length); - for (int i = 0; i < length; ++i) { - // Note that tokenToName will fail unless Context.printTrees - // is true. - String tokenname = null; - if (Token.printNames) { - tokenname = Token.name(source.charAt(i)); - } - if (tokenname == null) { - tokenname = "---"; - } - String pad = tokenname.length() > 7 ? "\t" : "\t\t"; - System.err.println( - tokenname - + pad - + (int) source.charAt(i) - + "\t'" - + ScriptRuntime.escapeString(source.substring(i, i + 1)) - + "'"); - } - System.err.println(); - } - - int braceNesting = 0; - boolean afterFirstEOL = false; - int i = 0; - int topFunctionType; - if (source.charAt(i) == Token.SCRIPT) { - ++i; - topFunctionType = -1; - } else { - topFunctionType = source.charAt(i + 1); - } - - if (!toSource) { - // add an initial newline to exactly match js. - result.append('\n'); - for (int j = 0; j < indent; j++) result.append(' '); - } else { - if (topFunctionType == FunctionNode.FUNCTION_EXPRESSION) { - result.append('('); - } - } - - while (i < length) { - switch (source.charAt(i)) { - case Token.GET: - case Token.SET: - case Token.METHOD: - if (source.charAt(i) == Token.GET) { - result.append("get "); - } else if (source.charAt(i) == Token.SET) { - result.append("set "); - } - ++i; - i = printSourceString(source, i + 1, false, result); - // Now increment one more to get past the FUNCTION token - ++i; - break; - - case Token.NAME: - case Token.REGEXP: // re-wrapped in '/'s in parser... - i = printSourceString(source, i + 1, false, result); - continue; - - case Token.STRING: - i = printSourceString(source, i + 1, true, result); - continue; - - case Token.NUMBER: - i = printSourceNumber(source, i + 1, result); - continue; - - case Token.BIGINT: - i = printSourceBigInt(source, i + 1, result); - continue; - - case Token.TRUE: - result.append("true"); - break; - - case Token.FALSE: - result.append("false"); - break; - - case Token.NULL: - result.append("null"); - break; - - case Token.THIS: - result.append("this"); - break; - - case Token.FUNCTION: - ++i; // skip function type - result.append("function "); - break; - - case FUNCTION_END: - // Do nothing - break; - - case Token.COMMA: - result.append(", "); - break; - - case Token.LC: - ++braceNesting; - if (Token.EOL == getNext(source, length, i)) indent += indentGap; - result.append('{'); - break; - - case Token.RC: - { - --braceNesting; - /* don't print the closing RC if it closes the - * toplevel function and we're called from - * decompileFunctionBody. - */ - if (justFunctionBody && braceNesting == 0) break; - - result.append('}'); - switch (getNext(source, length, i)) { - case Token.EOL: - case FUNCTION_END: - indent -= indentGap; - break; - case Token.WHILE: - case Token.ELSE: - indent -= indentGap; - result.append(' '); - break; - } - break; - } - case Token.LP: - result.append('('); - break; - - case Token.RP: - result.append(')'); - if (Token.LC == getNext(source, length, i)) result.append(' '); - break; - - case Token.LB: - result.append('['); - break; - - case Token.RB: - result.append(']'); - break; - - case Token.EOL: - { - if (toSource) break; - boolean newLine = true; - if (!afterFirstEOL) { - afterFirstEOL = true; - if (justFunctionBody) { - /* throw away just added 'function name(...) {' - * and restore the original indent - */ - result.setLength(0); - indent -= indentGap; - newLine = false; - } - } - if (newLine) { - result.append('\n'); - } - - /* add indent if any tokens remain, - * less setback if next token is - * a label, case or default. - */ - if (i + 1 < length) { - int less = 0; - int nextToken = source.charAt(i + 1); - if (nextToken == Token.CASE || nextToken == Token.DEFAULT) { - less = indentGap - caseGap; - } else if (nextToken == Token.RC) { - less = indentGap; - } - - /* elaborate check against label... skip past a - * following inlined NAME and look for a COLON. - */ - else if (nextToken == Token.NAME) { - int afterName = getSourceStringEnd(source, i + 2); - if (source.charAt(afterName) == Token.COLON) less = indentGap; - } - - for (; less < indent; less++) result.append(' '); - } - break; - } - case Token.DOT: - result.append('.'); - break; - - case Token.NEW: - result.append("new "); - break; - - case Token.DELPROP: - result.append("delete "); - break; - - case Token.IF: - result.append("if "); - break; - - case Token.ELSE: - result.append("else "); - break; - - case Token.FOR: - result.append("for "); - break; - - case Token.IN: - result.append(" in "); - break; - - case Token.WITH: - result.append("with "); - break; - - case Token.WHILE: - result.append("while "); - break; - - case Token.DO: - result.append("do "); - break; - - case Token.TRY: - result.append("try "); - break; - - case Token.CATCH: - result.append("catch "); - break; - - case Token.FINALLY: - result.append("finally "); - break; - - case Token.THROW: - result.append("throw "); - break; - - case Token.SWITCH: - result.append("switch "); - break; - - case Token.BREAK: - result.append("break"); - if (Token.NAME == getNext(source, length, i)) result.append(' '); - break; - - case Token.CONTINUE: - result.append("continue"); - if (Token.NAME == getNext(source, length, i)) result.append(' '); - break; - - case Token.CASE: - result.append("case "); - break; - - case Token.DEFAULT: - result.append("default"); - break; - - case Token.RETURN: - result.append("return"); - if (Token.SEMI != getNext(source, length, i)) result.append(' '); - break; - - case Token.VAR: - result.append("var "); - break; - - case Token.LET: - result.append("let "); - break; - - case Token.SEMI: - result.append(';'); - if (Token.EOL != getNext(source, length, i)) { - // separators in FOR - result.append(' '); - } - break; - - case Token.ASSIGN: - result.append(" = "); - break; - - case Token.ASSIGN_ADD: - result.append(" += "); - break; - - case Token.ASSIGN_SUB: - result.append(" -= "); - break; - - case Token.ASSIGN_MUL: - result.append(" *= "); - break; - - case Token.ASSIGN_DIV: - result.append(" /= "); - break; - - case Token.ASSIGN_MOD: - result.append(" %= "); - break; - - case Token.ASSIGN_BITOR: - result.append(" |= "); - break; - - case Token.ASSIGN_BITXOR: - result.append(" ^= "); - break; - - case Token.ASSIGN_BITAND: - result.append(" &= "); - break; - - case Token.ASSIGN_LSH: - result.append(" <<= "); - break; - - case Token.ASSIGN_RSH: - result.append(" >>= "); - break; - - case Token.ASSIGN_URSH: - result.append(" >>>= "); - break; - - case Token.HOOK: - result.append(" ? "); - break; - - case Token.OBJECTLIT: - // pun OBJECTLIT to mean colon in objlit property - // initialization. - // This needs to be distinct from COLON in the general case - // to distinguish from the colon in a ternary... which needs - // different spacing. - result.append(": "); - break; - - case Token.COLON: - if (Token.EOL == getNext(source, length, i)) - // it's the end of a label - result.append(':'); - else - // it's the middle part of a ternary - result.append(" : "); - break; - - case Token.OR: - result.append(" || "); - break; - - case Token.AND: - result.append(" && "); - break; - - case Token.BITOR: - result.append(" | "); - break; - - case Token.BITXOR: - result.append(" ^ "); - break; - - case Token.BITAND: - result.append(" & "); - break; - - case Token.SHEQ: - result.append(" === "); - break; - - case Token.SHNE: - result.append(" !== "); - break; - - case Token.EQ: - result.append(" == "); - break; - - case Token.NE: - result.append(" != "); - break; - - case Token.LE: - result.append(" <= "); - break; - - case Token.LT: - result.append(" < "); - break; - - case Token.GE: - result.append(" >= "); - break; - - case Token.GT: - result.append(" > "); - break; - - case Token.INSTANCEOF: - result.append(" instanceof "); - break; - - case Token.LSH: - result.append(" << "); - break; - - case Token.RSH: - result.append(" >> "); - break; - - case Token.URSH: - result.append(" >>> "); - break; - - case Token.TYPEOF: - result.append("typeof "); - break; - - case Token.VOID: - result.append("void "); - break; - - case Token.CONST: - result.append("const "); - break; - - case Token.YIELD: - result.append("yield "); - break; - - case Token.YIELD_STAR: - result.append("yield *"); - break; - - case Token.NOT: - result.append('!'); - break; - - case Token.BITNOT: - result.append('~'); - break; - - case Token.POS: - result.append('+'); - break; - - case Token.NEG: - result.append('-'); - break; - - case Token.INC: - result.append("++"); - break; - - case Token.DEC: - result.append("--"); - break; - - case Token.ADD: - result.append(" + "); - break; - - case Token.SUB: - result.append(" - "); - break; - - case Token.MUL: - result.append(" * "); - break; - - case Token.DIV: - result.append(" / "); - break; - - case Token.MOD: - result.append(" % "); - break; - - case Token.EXP: - result.append(" ** "); - break; - - case Token.COLONCOLON: - result.append("::"); - break; - - case Token.DOTDOT: - result.append(".."); - break; - - case Token.DOTQUERY: - result.append(".("); - break; - - case Token.XMLATTR: - result.append('@'); - break; - - case Token.DEBUGGER: - result.append("debugger;\n"); - break; - - case Token.ARROW: - result.append(" => "); - break; - - case Token.TEMPLATE_LITERAL: - result.append("`"); - break; - - case Token.TEMPLATE_LITERAL_SUBST: - result.append("${"); - break; - - case Token.TEMPLATE_CHARS: - i = printSourceString(source, i + 1, false, result); - continue; - - default: - // If we don't know how to decompile it, raise an exception. - throw new RuntimeException("Token: " + Token.name(source.charAt(i))); - } - ++i; - } - - if (!toSource) { - // add that trailing newline if it's an outermost function. - if (!justFunctionBody) result.append('\n'); - } else { - if (topFunctionType == FunctionNode.FUNCTION_EXPRESSION) { - result.append(')'); - } - } - - return result.toString(); - } - - private static int getNext(String source, int length, int i) { - return (i + 1 < length) ? source.charAt(i + 1) : Token.EOF; - } - - private static int getSourceStringEnd(String source, int offset) { - return printSourceString(source, offset, false, null); - } - - private static int printSourceString( - String source, int offset, boolean asQuotedString, StringBuilder sb) { - int length = source.charAt(offset); - ++offset; - if ((0x8000 & length) != 0) { - length = ((0x7FFF & length) << 16) | source.charAt(offset); - ++offset; - } - if (sb != null) { - String str = source.substring(offset, offset + length); - if (!asQuotedString) { - sb.append(str); - } else { - sb.append('"'); - sb.append(ScriptRuntime.escapeString(str)); - sb.append('"'); - } - } - return offset + length; - } - - private static int printSourceNumber(String source, int offset, StringBuilder sb) { - double number = 0.0; - char type = source.charAt(offset); - ++offset; - if (type == 'S') { - if (sb != null) { - int ival = source.charAt(offset); - number = ival; - } - ++offset; - } else if (type == 'J' || type == 'D') { - if (sb != null) { - long lbits; - lbits = (long) source.charAt(offset) << 48; - lbits |= (long) source.charAt(offset + 1) << 32; - lbits |= (long) source.charAt(offset + 2) << 16; - lbits |= source.charAt(offset + 3); - if (type == 'J') { - number = lbits; - } else { - number = Double.longBitsToDouble(lbits); - } - } - offset += 4; - } else { - // Bad source - throw new RuntimeException(); - } - if (sb != null) { - sb.append(ScriptRuntime.numberToString(number, 10)); - } - return offset; - } - - /** - * @see #printSourceString(String source, int offset, boolean asQuotedString, StringBuilder sb) - */ - private static int printSourceBigInt(String source, int offset, StringBuilder sb) { - int length = source.charAt(offset); - ++offset; - if ((0x8000 & length) != 0) { - length = ((0x7FFF & length) << 16) | source.charAt(offset); - ++offset; - } - if (sb != null) { - String str = source.substring(offset, offset + length); - sb.append(str); - sb.append('n'); - } - return offset + length; - } - - private char[] sourceBuffer = new char[128]; - - // Per script/function source buffer top: parent source does not include a - // nested functions source and uses function index as a reference instead. - private int sourceTop; - - // whether to do a debug print of the source information, when decompiling. - private static final boolean printSource = false; -} diff --git a/src/main/java/org/mozilla/javascript/DefaultErrorReporter.java b/src/main/java/org/mozilla/javascript/DefaultErrorReporter.java deleted file mode 100644 index 6111576e..00000000 --- a/src/main/java/org/mozilla/javascript/DefaultErrorReporter.java +++ /dev/null @@ -1,81 +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; - -/** - * This is the default error reporter for JavaScript. - * - * @author Norris Boyd - */ -class DefaultErrorReporter implements ErrorReporter -{ - static final DefaultErrorReporter instance = new DefaultErrorReporter(); - - private boolean forEval; - private ErrorReporter chainedReporter; - - private DefaultErrorReporter() { } - - static ErrorReporter forEval(ErrorReporter reporter) - { - DefaultErrorReporter r = new DefaultErrorReporter(); - r.forEval = true; - r.chainedReporter = reporter; - return r; - } - - @Override - public void warning(String message, String sourceURI, int line, - String lineText, int lineOffset) - { - if (chainedReporter != null) { - chainedReporter.warning( - message, sourceURI, line, lineText, lineOffset); - } else { - // Do nothing - } - } - - @Override - public void error(String message, String sourceURI, int line, - String lineText, int lineOffset) - { - if (forEval) { - // Assume error message strings that start with "TypeError: " - // should become TypeError exceptions. A bit of a hack, but we - // don't want to change the ErrorReporter interface. - String error = "SyntaxError"; - final String TYPE_ERROR_NAME = "TypeError"; - final String DELIMETER = ": "; - final String prefix = TYPE_ERROR_NAME + DELIMETER; - if (message.startsWith(prefix)) { - error = TYPE_ERROR_NAME; - message = message.substring(prefix.length()); - } - throw ScriptRuntime.constructError(error, message, sourceURI, - line, lineText, lineOffset); - } - if (chainedReporter != null) { - chainedReporter.error( - message, sourceURI, line, lineText, lineOffset); - } else { - throw runtimeError( - message, sourceURI, line, lineText, lineOffset); - } - } - - @Override - public EvaluatorException runtimeError(String message, String sourceURI, - int line, String lineText, - int lineOffset) - { - if (chainedReporter != null) { - return chainedReporter.runtimeError(message, sourceURI, line, lineText, lineOffset); - } - return new EvaluatorException(message, sourceURI, line, lineText, lineOffset); - } -} diff --git a/src/main/java/org/mozilla/javascript/DefiningClassLoader.java b/src/main/java/org/mozilla/javascript/DefiningClassLoader.java deleted file mode 100644 index a0fe1d64..00000000 --- a/src/main/java/org/mozilla/javascript/DefiningClassLoader.java +++ /dev/null @@ -1,56 +0,0 @@ -/* 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; - -/** - * Load generated classes. - * - * @author Norris Boyd - */ -public class DefiningClassLoader extends ClassLoader - implements GeneratedClassLoader -{ - public DefiningClassLoader() { - this.parentLoader = getClass().getClassLoader(); - } - - public DefiningClassLoader(ClassLoader parentLoader) { - this.parentLoader = parentLoader; - } - - @Override - public Class defineClass(String name, byte[] data) { - // Use our own protection domain for the generated classes. - // TODO: we might want to use a separate protection domain for classes - // compiled from scripts, based on where the script was loaded from. - return super.defineClass(name, data, 0, data.length, - SecurityUtilities.getProtectionDomain(getClass())); - } - - @Override - public void linkClass(Class cl) { - resolveClass(cl); - } - - @Override - public Class loadClass(String name, boolean resolve) - throws ClassNotFoundException - { - Class cl = findLoadedClass(name); - if (cl == null) { - if (parentLoader != null) { - cl = parentLoader.loadClass(name); - } else { - cl = findSystemClass(name); - } - } - if (resolve) { - resolveClass(cl); - } - return cl; - } - - private final ClassLoader parentLoader; -} diff --git a/src/main/java/org/mozilla/javascript/Delegator.java b/src/main/java/org/mozilla/javascript/Delegator.java deleted file mode 100644 index 255a55c9..00000000 --- a/src/main/java/org/mozilla/javascript/Delegator.java +++ /dev/null @@ -1,307 +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; - -/** - * This is a helper class for implementing wrappers around Scriptable - * objects. It implements the Function interface and delegates all - * invocations to a delegee Scriptable object. The normal use of this - * class involves creating a sub-class and overriding one or more of - * the methods. - * - * A useful application is the implementation of interceptors, - * pre/post conditions, debugging. - * - * @see Function - * @see Scriptable - * @author Matthias Radestock - */ - -public class Delegator - implements Function, SymbolScriptable { - - protected Scriptable obj = null; - - /** - * Create a Delegator prototype. - * - * This constructor should only be used for creating prototype - * objects of Delegator. - * - * @see org.mozilla.javascript.Delegator#construct - */ - public Delegator() { - } - - /** - * Create a new Delegator that forwards requests to a delegee - * Scriptable object. - * - * @param obj the delegee - * @see org.mozilla.javascript.Scriptable - */ - public Delegator(Scriptable obj) { - this.obj = obj; - } - - /** - * Crete new Delegator instance. - * The default implementation calls this.getClass().newInstance(). - * - * @see #construct(Context cx, Scriptable scope, Object[] args) - */ - protected Delegator newInstance() - { - try { - return this.getClass().newInstance(); - } catch (Exception ex) { - throw Context.throwAsScriptRuntimeEx(ex); - } - } - - /** - * Retrieve the delegee. - * - * @return the delegee - */ - public Scriptable getDelegee() { - return obj; - } - - /** - * Set the delegee. - * - * @param obj the delegee - * @see org.mozilla.javascript.Scriptable - */ - public void setDelegee(Scriptable obj) { - this.obj = obj; - } - - /** - * @see org.mozilla.javascript.Scriptable#getClassName - */ - @Override - public String getClassName() { - return getDelegee().getClassName(); - } - - /** - * @see org.mozilla.javascript.Scriptable#get(String, Scriptable) - */ - @Override - public Object get(String name, Scriptable start) { - return getDelegee().get(name,start); - } - - @Override - public Object get(Symbol key, Scriptable start) { - final Scriptable delegee = getDelegee(); - if (delegee instanceof SymbolScriptable) { - return ((SymbolScriptable) delegee).get(key, start); - } - return Scriptable.NOT_FOUND; - } - - /** - * @see org.mozilla.javascript.Scriptable#get(int, Scriptable) - */ - @Override - public Object get(int index, Scriptable start) { - return getDelegee().get(index,start); - } - - /** - * @see org.mozilla.javascript.Scriptable#has(String, Scriptable) - */ - @Override - public boolean has(String name, Scriptable start) { - return getDelegee().has(name,start); - } - - @Override - public boolean has(Symbol key, Scriptable start) { - final Scriptable delegee = getDelegee(); - if (delegee instanceof SymbolScriptable) { - return ((SymbolScriptable) delegee).has(key, start); - } - return false; - } - - /** - * @see org.mozilla.javascript.Scriptable#has(int, Scriptable) - */ - @Override - public boolean has(int index, Scriptable start) { - return getDelegee().has(index,start); - } - - /** - * @see org.mozilla.javascript.Scriptable#put(String, Scriptable, Object) - */ - @Override - public void put(String name, Scriptable start, Object value) { - getDelegee().put(name,start,value); - } - - /** - * @see org.mozilla.javascript.SymbolScriptable#put(Symbol, Scriptable, Object) - */ - @Override - public void put(Symbol symbol, Scriptable start, Object value) { - final Scriptable delegee = getDelegee(); - if (delegee instanceof SymbolScriptable) { - ((SymbolScriptable) delegee).put(symbol, start, value); - } - } - - /** - * @see org.mozilla.javascript.Scriptable#put(int, Scriptable, Object) - */ - @Override - public void put(int index, Scriptable start, Object value) { - getDelegee().put(index,start,value); - } - - /** - * @see org.mozilla.javascript.Scriptable#delete(String) - */ - @Override - public void delete(String name) { - getDelegee().delete(name); - } - - @Override - public void delete(Symbol key) { - final Scriptable delegee = getDelegee(); - if (delegee instanceof SymbolScriptable) { - ((SymbolScriptable) delegee).delete(key); - } - } - - /** - * @see org.mozilla.javascript.Scriptable#delete(int) - */ - @Override - public void delete(int index) { - getDelegee().delete(index); - } - - /** - * @see org.mozilla.javascript.Scriptable#getPrototype - */ - @Override - public Scriptable getPrototype() { - return getDelegee().getPrototype(); - } - - /** - * @see org.mozilla.javascript.Scriptable#setPrototype - */ - @Override - public void setPrototype(Scriptable prototype) { - getDelegee().setPrototype(prototype); - } - - /** - * @see org.mozilla.javascript.Scriptable#getParentScope - */ - @Override - public Scriptable getParentScope() { - return getDelegee().getParentScope(); - } - - /** - * @see org.mozilla.javascript.Scriptable#setParentScope - */ - @Override - public void setParentScope(Scriptable parent) { - getDelegee().setParentScope(parent); - } - - /** - * @see org.mozilla.javascript.Scriptable#getIds - */ - @Override - public Object[] getIds() { - return getDelegee().getIds(); - } - - /** - * Note that this method does not get forwarded to the delegee if - * the hint parameter is null, - * ScriptRuntime.ScriptableClass or - * ScriptRuntime.FunctionClass. Instead the object - * itself is returned. - * - * @param hint the type hint - * @return the default value - * - * @see org.mozilla.javascript.Scriptable#getDefaultValue - */ - @Override - public Object getDefaultValue(Class hint) { - return (hint == null || - hint == ScriptRuntime.ScriptableClass || - hint == ScriptRuntime.FunctionClass) ? - this : getDelegee().getDefaultValue(hint); - } - - /** - * @see org.mozilla.javascript.Scriptable#hasInstance - */ - @Override - public boolean hasInstance(Scriptable instance) { - return getDelegee().hasInstance(instance); - } - - /** - * @see org.mozilla.javascript.Function#call - */ - @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, - Object[] args) - { - return ((Function)getDelegee()).call(cx,scope,thisObj,args); - } - - /** - * Note that if the delegee is null, - * this method creates a new instance of the Delegator itself - * rathert than forwarding the call to the - * delegee. This permits the use of Delegator - * prototypes. - * - * @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 - * - * @see Function#construct(Context, Scriptable, Object[]) - */ - @Override - public Scriptable construct(Context cx, Scriptable scope, Object[] args) - { - Scriptable myDelegee = getDelegee(); - if (myDelegee == null) { - //this little trick allows us to declare prototype objects for Delegators - Delegator n = newInstance(); - Scriptable delegee; - if (args.length == 0) { - delegee = new NativeObject(); - } else { - delegee = ScriptRuntime.toObject(cx, scope, args[0]); - } - n.setDelegee(delegee); - return n; - } - return ((Function)myDelegee).construct(cx, scope, args); - } -} diff --git a/src/main/java/org/mozilla/javascript/ES6Generator.java b/src/main/java/org/mozilla/javascript/ES6Generator.java deleted file mode 100644 index 94d6cac6..00000000 --- a/src/main/java/org/mozilla/javascript/ES6Generator.java +++ /dev/null @@ -1,460 +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; - -public final class ES6Generator extends IdScriptableObject { - private static final long serialVersionUID = 1645892441041347273L; - - private static final Object GENERATOR_TAG = "Generator"; - - static ES6Generator init(ScriptableObject scope, boolean sealed) { - - ES6Generator prototype = new ES6Generator(); - if (scope != null) { - prototype.setParentScope(scope); - prototype.setPrototype(getObjectPrototype(scope)); - } - prototype.activatePrototypeMap(MAX_PROTOTYPE_ID); - if (sealed) { - prototype.sealObject(); - } - - // Need to access Generator prototype when constructing - // Generator instances, but don't have a generator constructor - // to use to find the prototype. Use the "associateValue" - // approach instead. - if (scope != null) { - scope.associateValue(GENERATOR_TAG, prototype); - } - - return prototype; - } - - /** Only for constructing the prototype object. */ - private ES6Generator() {} - - public ES6Generator(Scriptable scope, NativeFunction function, Object savedState) { - this.function = function; - this.savedState = savedState; - // Set parent and prototype properties. Since we don't have a - // "Generator" constructor in the top scope, we stash the - // prototype in the top scope's associated value. - Scriptable top = ScriptableObject.getTopLevelScope(scope); - this.setParentScope(top); - ES6Generator prototype = - (ES6Generator) ScriptableObject.getTopScopeValue(top, GENERATOR_TAG); - this.setPrototype(prototype); - } - - @Override - public String getClassName() { - return "Generator"; - } - - @Override - protected void initPrototypeId(int id) { - if (id == SymbolId_iterator) { - initPrototypeMethod(GENERATOR_TAG, id, SymbolKey.ITERATOR, "[Symbol.iterator]", 0); - return; - } - - String s; - int arity; - switch (id) { - case Id_next: - arity = 1; - s = "next"; - break; - case Id_return: - arity = 1; - s = "return"; - break; - case Id_throw: - arity = 1; - s = "throw"; - break; - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - initPrototypeMethod(GENERATOR_TAG, id, s, arity); - } - - @Override - public Object execIdCall( - IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!f.hasTag(GENERATOR_TAG)) { - return super.execIdCall(f, cx, scope, thisObj, args); - } - int id = f.methodId(); - - ES6Generator generator = ensureType(thisObj, ES6Generator.class, f); - Object value = args.length >= 1 ? args[0] : Undefined.instance; - - switch (id) { - case Id_return: - if (generator.delegee == null) { - return generator.resumeAbruptLocal( - cx, scope, NativeGenerator.GENERATOR_CLOSE, value); - } - return generator.resumeDelegeeReturn(cx, scope, value); - case Id_next: - if (generator.delegee == null) { - return generator.resumeLocal(cx, scope, value); - } - return generator.resumeDelegee(cx, scope, value); - case Id_throw: - if (generator.delegee == null) { - return generator.resumeAbruptLocal( - cx, scope, NativeGenerator.GENERATOR_THROW, value); - } - return generator.resumeDelegeeThrow(cx, scope, value); - case SymbolId_iterator: - return thisObj; - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - } - - private Scriptable resumeDelegee(Context cx, Scriptable scope, Object value) { - try { - // Be super-careful and only pass an arg to next if it expects one - Object[] nextArgs = - Undefined.instance.equals(value) - ? ScriptRuntime.emptyArgs - : new Object[] {value}; - - Callable nextFn = - ScriptRuntime.getPropFunctionAndThis( - delegee, ES6Iterator.NEXT_METHOD, cx, scope); - Scriptable nextThis = ScriptRuntime.lastStoredScriptable(cx); - Object nr = nextFn.call(cx, scope, nextThis, nextArgs); - - Scriptable nextResult = ScriptableObject.ensureScriptable(nr); - if (ScriptRuntime.isIteratorDone(cx, nextResult)) { - // Iterator is "done". - delegee = null; - // Return a result to the original generator - return resumeLocal( - cx, - scope, - ScriptableObject.getProperty(nextResult, ES6Iterator.VALUE_PROPERTY)); - } - // Otherwise, we have a normal result and should continue - return nextResult; - - } catch (RhinoException re) { - // Exceptions from the delegee should be handled by the enclosing - // generator, including if they're because functions can't be found. - delegee = null; - return resumeAbruptLocal(cx, scope, NativeGenerator.GENERATOR_THROW, re); - } - } - - private Scriptable resumeDelegeeThrow(Context cx, Scriptable scope, Object value) { - boolean returnCalled = false; - try { - // Delegate to "throw" method. If it's not defined we'll get an error here. - Callable throwFn = ScriptRuntime.getPropFunctionAndThis(delegee, "throw", cx, scope); - Scriptable nextThis = ScriptRuntime.lastStoredScriptable(cx); - Object throwResult = throwFn.call(cx, scope, nextThis, new Object[] {value}); - - if (ScriptRuntime.isIteratorDone(cx, throwResult)) { - // Iterator is "done". - try { - // Return a result to the original generator, but first optionally call "return" - returnCalled = true; - callReturnOptionally(cx, scope, Undefined.instance); - } finally { - delegee = null; - } - return resumeLocal( - cx, - scope, - ScriptRuntime.getObjectProp( - throwResult, ES6Iterator.VALUE_PROPERTY, cx, scope)); - } - // Otherwise, we have a normal result and should continue - return ensureScriptable(throwResult); - - } catch (RhinoException re) { - // Handle all exceptions, including missing methods, by delegating to original. - try { - if (!returnCalled) { - try { - callReturnOptionally(cx, scope, Undefined.instance); - } catch (RhinoException re2) { - return resumeAbruptLocal(cx, scope, NativeGenerator.GENERATOR_THROW, re2); - } - } - } finally { - delegee = null; - } - return resumeAbruptLocal(cx, scope, NativeGenerator.GENERATOR_THROW, re); - } - } - - private Scriptable resumeDelegeeReturn(Context cx, Scriptable scope, Object value) { - try { - // Call "return" but don't throw if it can't be found - Object retResult = callReturnOptionally(cx, scope, value); - if (retResult != null) { - if (ScriptRuntime.isIteratorDone(cx, retResult)) { - // Iterator is "done". - delegee = null; - // Return a result to the original generator - return resumeAbruptLocal( - cx, - scope, - NativeGenerator.GENERATOR_CLOSE, - ScriptRuntime.getObjectPropNoWarn( - retResult, ES6Iterator.VALUE_PROPERTY, cx, scope)); - } else { - // Not actually done yet! - return ensureScriptable(retResult); - } - } - - // No "return" -- let the original iterator return the value. - delegee = null; - return resumeAbruptLocal(cx, scope, NativeGenerator.GENERATOR_CLOSE, value); - - } catch (RhinoException re) { - // Exceptions from the delegee should be handled by the enclosing - // generator, including if they're because functions can't be found. - delegee = null; - return resumeAbruptLocal(cx, scope, NativeGenerator.GENERATOR_THROW, re); - } - } - - private Scriptable resumeLocal(Context cx, Scriptable scope, Object value) { - if (state == State.COMPLETED) { - return ES6Iterator.makeIteratorResult(cx, scope, Boolean.TRUE); - } - if (state == State.EXECUTING) { - throw ScriptRuntime.typeErrorById("msg.generator.executing"); - } - - Scriptable result = ES6Iterator.makeIteratorResult(cx, scope, Boolean.FALSE); - state = State.EXECUTING; - - try { - Object r = - function.resumeGenerator( - cx, scope, NativeGenerator.GENERATOR_SEND, savedState, value); - - if (r instanceof YieldStarResult) { - // This special result tells us that we are executing a "yield *" - state = State.SUSPENDED_YIELD; - YieldStarResult ysResult = (YieldStarResult) r; - try { - delegee = ScriptRuntime.callIterator(ysResult.getResult(), cx, scope); - } catch (RhinoException re) { - // Need to handle exceptions if the iterator cannot be called. - return resumeAbruptLocal(cx, scope, NativeGenerator.GENERATOR_THROW, re); - } - - Scriptable delResult; - try { - // Re-execute but update state in case we end up back here - // Value shall be Undefined based on the very complex spec! - delResult = resumeDelegee(cx, scope, Undefined.instance); - } finally { - state = State.EXECUTING; - } - if (ScriptRuntime.isIteratorDone(cx, delResult)) { - state = State.COMPLETED; - } - return delResult; - } - - ScriptableObject.putProperty(result, ES6Iterator.VALUE_PROPERTY, r); - - } catch (NativeGenerator.GeneratorClosedException gce) { - state = State.COMPLETED; - } catch (JavaScriptException jse) { - state = State.COMPLETED; - if (jse.getValue() instanceof NativeIterator.StopIteration) { - ScriptableObject.putProperty( - result, - ES6Iterator.VALUE_PROPERTY, - ((NativeIterator.StopIteration) jse.getValue()).getValue()); - } else { - lineNumber = jse.lineNumber(); - lineSource = jse.lineSource(); - if (jse.getValue() instanceof RhinoException) { - throw (RhinoException) jse.getValue(); - } - throw jse; - } - } catch (RhinoException re) { - lineNumber = re.lineNumber(); - lineSource = re.lineSource(); - throw re; - } finally { - if (state == State.COMPLETED) { - ScriptableObject.putProperty(result, ES6Iterator.DONE_PROPERTY, Boolean.TRUE); - } else { - state = State.SUSPENDED_YIELD; - } - } - return result; - } - - private Scriptable resumeAbruptLocal(Context cx, Scriptable scope, int op, Object value) { - if (state == State.EXECUTING) { - throw ScriptRuntime.typeErrorById("msg.generator.executing"); - } - if (state == State.SUSPENDED_START) { - // Throw right away if we never started - state = State.COMPLETED; - } - - Scriptable result = ES6Iterator.makeIteratorResult(cx, scope, Boolean.FALSE); - if (state == State.COMPLETED) { - if (op == NativeGenerator.GENERATOR_THROW) { - throw new JavaScriptException(value, lineSource, lineNumber); - } - ScriptableObject.putProperty(result, ES6Iterator.DONE_PROPERTY, Boolean.TRUE); - return result; - } - - state = State.EXECUTING; - - Object throwValue = value; - if (op == NativeGenerator.GENERATOR_CLOSE) { - if (!(value instanceof NativeGenerator.GeneratorClosedException)) { - throwValue = new NativeGenerator.GeneratorClosedException(); - } - } else { - if (value instanceof JavaScriptException) { - throwValue = ((JavaScriptException) value).getValue(); - } else if (value instanceof RhinoException) { - throwValue = ScriptRuntime.wrapException((Throwable) value, scope, cx); - } - } - - try { - Object r = function.resumeGenerator(cx, scope, op, savedState, throwValue); - ScriptableObject.putProperty(result, ES6Iterator.VALUE_PROPERTY, r); - // If we get here without an exception we can still run. - state = State.SUSPENDED_YIELD; - - } catch (NativeGenerator.GeneratorClosedException gce) { - state = State.COMPLETED; - } catch (JavaScriptException jse) { - state = State.COMPLETED; - if (jse.getValue() instanceof NativeIterator.StopIteration) { - ScriptableObject.putProperty( - result, - ES6Iterator.VALUE_PROPERTY, - ((NativeIterator.StopIteration) jse.getValue()).getValue()); - } else { - lineNumber = jse.lineNumber(); - lineSource = jse.lineSource(); - if (jse.getValue() instanceof RhinoException) { - throw (RhinoException) jse.getValue(); - } - throw jse; - } - } catch (RhinoException re) { - state = State.COMPLETED; - lineNumber = re.lineNumber(); - lineSource = re.lineSource(); - throw re; - } finally { - // After an abrupt completion we are always, umm, complete, - // and we will never delegate to the delegee again - if (state == State.COMPLETED) { - delegee = null; - ScriptableObject.putProperty(result, ES6Iterator.DONE_PROPERTY, Boolean.TRUE); - } - } - return result; - } - - private Object callReturnOptionally(Context cx, Scriptable scope, Object value) { - Object[] retArgs = - Undefined.instance.equals(value) ? ScriptRuntime.emptyArgs : new Object[] {value}; - // Delegate to "return" method. If it's not defined we ignore it - Object retFnObj = - ScriptRuntime.getObjectPropNoWarn(delegee, ES6Iterator.RETURN_METHOD, cx, scope); - if (!Undefined.instance.equals(retFnObj)) { - if (!(retFnObj instanceof Callable)) { - throw ScriptRuntime.typeErrorById( - "msg.isnt.function", - ES6Iterator.RETURN_METHOD, - ScriptRuntime.typeof(retFnObj)); - } - return ((Callable) retFnObj).call(cx, scope, ensureScriptable(delegee), retArgs); - } - return null; - } - - @Override - protected int findPrototypeId(Symbol k) { - if (SymbolKey.ITERATOR.equals(k)) { - return SymbolId_iterator; - } - return 0; - } - - @Override - protected int findPrototypeId(String s) { - int id; - L0: - { - id = 0; - String X = null; - int s_length = s.length(); - if (s_length == 4) { - X = "next"; - id = Id_next; - } else if (s_length == 5) { - X = "throw"; - id = Id_throw; - } else if (s_length == 6) { - X = "return"; - id = Id_return; - } - if (X != null && X != s && !X.equals(s)) id = 0; - break L0; - } - return id; - } - - private static final int Id_next = 1, - Id_return = 2, - Id_throw = 3, - SymbolId_iterator = 4, - MAX_PROTOTYPE_ID = SymbolId_iterator; - - private NativeFunction function; - private Object savedState; - private String lineSource; - private int lineNumber; - private State state = State.SUSPENDED_START; - private Object delegee; - - enum State { - SUSPENDED_START, - SUSPENDED_YIELD, - EXECUTING, - COMPLETED - } - - public static final class YieldStarResult { - private Object result; - - public YieldStarResult(Object result) { - this.result = result; - } - - Object getResult() { - return result; - } - } -} diff --git a/src/main/java/org/mozilla/javascript/ES6Iterator.java b/src/main/java/org/mozilla/javascript/ES6Iterator.java deleted file mode 100644 index 26d3f3fc..00000000 --- a/src/main/java/org/mozilla/javascript/ES6Iterator.java +++ /dev/null @@ -1,147 +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; - -public abstract class ES6Iterator extends IdScriptableObject { - - private static final long serialVersionUID = 2438373029140003950L; - - protected static void init(ScriptableObject scope, boolean sealed, IdScriptableObject prototype, String tag) { - if (scope != null) { - prototype.setParentScope(scope); - prototype.setPrototype(getObjectPrototype(scope)); - } - prototype.activatePrototypeMap(MAX_PROTOTYPE_ID); - if (sealed) { - prototype.sealObject(); - } - - // Need to access Iterator prototype when constructing - // Iterator instances, but don't have a iterator constructor - // to use to find the prototype. Use the "associateValue" - // approach instead. - if (scope != null) { - scope.associateValue(tag, prototype); - } - } - - protected boolean exhausted = false; - private String tag; - - protected ES6Iterator() {} - - protected ES6Iterator(Scriptable scope, String tag) { - // Set parent and prototype properties. Since we don't have a - // "Iterator" constructor in the top scope, we stash the - // prototype in the top scope's associated value. - this.tag = tag; - Scriptable top = ScriptableObject.getTopLevelScope(scope); - this.setParentScope(top); - IdScriptableObject prototype = (IdScriptableObject) - ScriptableObject.getTopScopeValue(top, tag); - setPrototype(prototype); - } - - @Override - protected void initPrototypeId(int id) - { - switch (id) { - case Id_next: - initPrototypeMethod(getTag(), id, NEXT_METHOD, 0); - return; - case SymbolId_iterator: - initPrototypeMethod(getTag(), id, SymbolKey.ITERATOR, "[Symbol.iterator]", DONTENUM | READONLY); - return; - case SymbolId_toStringTag: - initPrototypeValue(SymbolId_toStringTag, SymbolKey.TO_STRING_TAG, getClassName(), DONTENUM | READONLY); - return; - default: throw new IllegalArgumentException(String.valueOf(id)); - } - } - - @Override - public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) - { - if (!f.hasTag(getTag())) { - return super.execIdCall(f, cx, scope, thisObj, args); - } - int id = f.methodId(); - - ES6Iterator iterator = ensureType(thisObj, ES6Iterator.class, f); - - switch (id) { - case Id_next: - return iterator.next(cx, scope); - case SymbolId_iterator: - return iterator; - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - } - - @Override - protected int findPrototypeId(Symbol k) { - if (SymbolKey.ITERATOR.equals(k)) { - return SymbolId_iterator; - } else if (SymbolKey.TO_STRING_TAG.equals(k)) { - return SymbolId_toStringTag; - } - return 0; - } - - @Override - protected int findPrototypeId(String s) { - if (NEXT_METHOD.equals(s)) { - return Id_next; - } - return 0; - } - - abstract protected boolean isDone(Context cx, Scriptable scope); - - abstract protected Object nextValue(Context cx, Scriptable scope); - - protected Object next(Context cx, Scriptable scope) { - Object value = Undefined.instance; - boolean done = isDone(cx, scope) || this.exhausted; - if (!done) { - value = nextValue(cx, scope); - } else { - this.exhausted = true; - } - return makeIteratorResult(cx, scope, Boolean.valueOf(done), value); - } - - protected String getTag() { - return tag; - } - - // 25.1.1.3 The IteratorResult Interface - static Scriptable makeIteratorResult(Context cx, Scriptable scope, Boolean done) { - return makeIteratorResult(cx, scope, done, Undefined.instance); - } - - static Scriptable makeIteratorResult(Context cx, Scriptable scope, Boolean done, Object value) { - final Scriptable iteratorResult = cx.newObject(scope); - ScriptableObject.putProperty(iteratorResult, VALUE_PROPERTY, value); - ScriptableObject.putProperty(iteratorResult, DONE_PROPERTY, done); - return iteratorResult; - } - - private static final int - Id_next = 1, - SymbolId_iterator = 2, - SymbolId_toStringTag = 3, - MAX_PROTOTYPE_ID = SymbolId_toStringTag; - - public static final String NEXT_METHOD = "next"; - public static final String DONE_PROPERTY = "done"; - public static final String RETURN_PROPERTY = "return"; - public static final String VALUE_PROPERTY = "value"; - public static final String RETURN_METHOD = "return"; -} diff --git a/src/main/java/org/mozilla/javascript/EcmaError.java b/src/main/java/org/mozilla/javascript/EcmaError.java deleted file mode 100644 index 11c24a8f..00000000 --- a/src/main/java/org/mozilla/javascript/EcmaError.java +++ /dev/null @@ -1,135 +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; - -/** - * The class of exceptions raised by the engine as described in - * ECMA edition 3. See section 15.11.6 in particular. - */ -public class EcmaError extends RhinoException -{ - private static final long serialVersionUID = -6261226256957286699L; - - private String errorName; - private String errorMessage; - - /** - * Create an exception with the specified detail message. - * - * Errors internal to the JavaScript engine will simply throw a - * RuntimeException. - * - * @param sourceName the name of the source responsible for the error - * @param lineNumber the line number of the source - * @param columnNumber the columnNumber of the source (may be zero if - * unknown) - * @param lineSource the source of the line containing the error (may be - * null if unknown) - */ - EcmaError(String errorName, String errorMessage, - String sourceName, int lineNumber, - String lineSource, int columnNumber) - { - recordErrorOrigin(sourceName, lineNumber, lineSource, columnNumber); - this.errorName = errorName; - this.errorMessage = errorMessage; - } - - /** - * @deprecated EcmaError error instances should not be constructed - * explicitly since they are generated by the engine. - */ - @Deprecated - public EcmaError(Scriptable nativeError, String sourceName, - int lineNumber, int columnNumber, String lineSource) - { - this("InternalError", ScriptRuntime.toString(nativeError), - sourceName, lineNumber, lineSource, columnNumber); - } - - @Override - public String details() - { - return errorName+": "+errorMessage; - } - - /** - * Gets the name of the error. - * - * ECMA edition 3 defines the following - * errors: EvalError, RangeError, ReferenceError, - * SyntaxError, TypeError, and URIError. Additional error names - * may be added in the future. - * - * See ECMA edition 3, 15.11.7.9. - * - * @return the name of the error. - */ - public String getName() - { - return errorName; - } - - /** - * Gets the message corresponding to the error. - * - * See ECMA edition 3, 15.11.7.10. - * - * @return an implementation-defined string describing the error. - */ - public String getErrorMessage() - { - return errorMessage; - } - - /** - * @deprecated Use {@link RhinoException#sourceName()} from the super class. - */ - @Deprecated - public String getSourceName() - { - return sourceName(); - } - - /** - * @deprecated Use {@link RhinoException#lineNumber()} from the super class. - */ - @Deprecated - public int getLineNumber() - { - return lineNumber(); - } - - /** - * @deprecated - * Use {@link RhinoException#columnNumber()} from the super class. - */ - @Deprecated - public int getColumnNumber() { - return columnNumber(); - } - - /** - * @deprecated Use {@link RhinoException#lineSource()} from the super class. - */ - @Deprecated - public String getLineSource() { - return lineSource(); - } - - /** - * @deprecated - * Always returns null. - */ - @Deprecated - public Scriptable getErrorObject() - { - return null; - } -} diff --git a/src/main/java/org/mozilla/javascript/EmbeddedSlotMap.java b/src/main/java/org/mozilla/javascript/EmbeddedSlotMap.java deleted file mode 100644 index b37befd0..00000000 --- a/src/main/java/org/mozilla/javascript/EmbeddedSlotMap.java +++ /dev/null @@ -1,284 +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; - -/* - * This class implements the SlotMap interface using an embedded hash table. This hash table - * has the minimum overhead needed to get the job done. In particular, it embeds the Slot - * directly into the hash table rather than creating an intermediate object, which seems - * to have a measurable performance benefit. - */ - -import java.util.Iterator; -import java.util.NoSuchElementException; -import java.util.Objects; - -public class EmbeddedSlotMap implements SlotMap { - - private Slot[] slots; - - // gateways into the definition-order linked list of slots - private Slot firstAdded; - private Slot lastAdded; - - private int count; - - // initial slot array size, must be a power of 2 - private static final int INITIAL_SLOT_SIZE = 4; - - private static final class Iter implements Iterator { - private Slot next; - - Iter(Slot slot) { - next = slot; - } - - @Override - public boolean hasNext() { - return next != null; - } - - @Override - public Slot next() { - Slot ret = next; - if (ret == null) { - throw new NoSuchElementException(); - } - next = next.orderedNext; - return ret; - } - } - - public EmbeddedSlotMap() {} - - @Override - public int size() { - return count; - } - - @Override - public boolean isEmpty() { - return count == 0; - } - - @Override - public Iterator iterator() { - return new Iter(firstAdded); - } - - /** Locate the slot with the given name or index. */ - @Override - public Slot query(Object key, int index) { - if (slots == null) { - return null; - } - - int indexOrHash = (key != null ? key.hashCode() : index); - int slotIndex = getSlotIndex(slots.length, indexOrHash); - for (Slot slot = slots[slotIndex]; slot != null; slot = slot.next) { - if (indexOrHash == slot.indexOrHash && Objects.equals(slot.name, key)) { - return slot; - } - } - return null; - } - - /** - * Locate the slot with given name or index, and create a new one if necessary. - * - * @param key either a String or a Symbol object that identifies the property - * @param index index or 0 if slot holds property name. - */ - @Override - public Slot modify(Object key, int index, int attributes) { - final int indexOrHash = (key != null ? key.hashCode() : index); - Slot slot; - - if (slots != null) { - final int slotIndex = getSlotIndex(slots.length, indexOrHash); - for (slot = slots[slotIndex]; slot != null; slot = slot.next) { - if (indexOrHash == slot.indexOrHash && Objects.equals(slot.name, key)) { - break; - } - } - if (slot != null) { - return slot; - } - } - - // A new slot has to be inserted. - return createSlot(key, indexOrHash, attributes); - } - - private Slot createSlot(Object key, int indexOrHash, int attributes) { - if (count == 0) { - // Always throw away old slots if any on empty insert. - slots = new Slot[INITIAL_SLOT_SIZE]; - } - - // Check if the table is not too full before inserting. - if (4 * (count + 1) > 3 * slots.length) { - // table size must be a power of 2 -- always grow by x2! - Slot[] newSlots = new Slot[slots.length * 2]; - copyTable(slots, newSlots); - slots = newSlots; - } - - Slot newSlot = new Slot(key, indexOrHash, attributes); - insertNewSlot(newSlot); - return newSlot; - } - - @Override - public void replace(Slot oldSlot, Slot newSlot) { - final int insertPos = getSlotIndex(slots.length, oldSlot.indexOrHash); - Slot prev = slots[insertPos]; - Slot tmpSlot = prev; - // Find original slot and previous one in hash table - while (tmpSlot != null) { - if (tmpSlot == oldSlot) { - break; - } - prev = tmpSlot; - tmpSlot = tmpSlot.next; - } - - // It's an error to call this when the slot isn't already there - assert (tmpSlot == oldSlot); - - // add new slot to hash table - if (prev == oldSlot) { - slots[insertPos] = newSlot; - } else { - prev.next = newSlot; - } - newSlot.next = oldSlot.next; - - // Replace new slot in linked list, keeping same order - if (oldSlot == firstAdded) { - firstAdded = newSlot; - } else { - Slot ps = firstAdded; - while ((ps != null) && (ps.orderedNext != oldSlot)) { - ps = ps.orderedNext; - } - if (ps != null) { - ps.orderedNext = newSlot; - } - } - newSlot.orderedNext = oldSlot.orderedNext; - if (oldSlot == lastAdded) { - lastAdded = newSlot; - } - } - - @Override - public void add(Slot newSlot) { - if (slots == null) { - slots = new Slot[INITIAL_SLOT_SIZE]; - } - insertNewSlot(newSlot); - } - - private void insertNewSlot(Slot newSlot) { - ++count; - // add new slot to linked list - if (lastAdded != null) { - lastAdded.orderedNext = newSlot; - } - if (firstAdded == null) { - firstAdded = newSlot; - } - lastAdded = newSlot; - // add new slot to hash table, return it - addKnownAbsentSlot(slots, newSlot); - } - - @Override - public void remove(Object key, int index) { - int indexOrHash = (key != null ? key.hashCode() : index); - - if (count != 0) { - final int slotIndex = getSlotIndex(slots.length, indexOrHash); - Slot prev = slots[slotIndex]; - Slot slot = prev; - while (slot != null) { - if (slot.indexOrHash == indexOrHash && Objects.equals(slot.name, key)) { - break; - } - prev = slot; - slot = slot.next; - } - if (slot != null) { - // non-configurable - if ((slot.getAttributes() & ScriptableObject.PERMANENT) != 0) { - Context cx = Context.getContext(); - if (cx.isStrictMode()) { - throw ScriptRuntime.typeErrorById( - "msg.delete.property.with.configurable.false", key); - } - return; - } - count--; - // remove slot from hash table - if (prev == slot) { - slots[slotIndex] = slot.next; - } else { - prev.next = slot.next; - } - - // remove from ordered list. Previously this was done lazily in - // getIds() but delete is an infrequent operation so O(n) - // should be ok - - // ordered list always uses the actual slot - if (slot == firstAdded) { - prev = null; - firstAdded = slot.orderedNext; - } else { - prev = firstAdded; - while (prev.orderedNext != slot) { - prev = prev.orderedNext; - } - prev.orderedNext = slot.orderedNext; - } - if (slot == lastAdded) { - lastAdded = prev; - } - } - } - } - - private static void copyTable(Slot[] oldSlots, Slot[] newSlots) { - for (Slot slot : oldSlots) { - while (slot != null) { - Slot nextSlot = slot.next; - slot.next = null; - addKnownAbsentSlot(newSlots, slot); - slot = nextSlot; - } - } - } - - /** - * Add slot with keys that are known to absent from the table. This is an optimization to use - * when inserting into empty table, after table growth or during deserialization. - */ - private static void addKnownAbsentSlot(Slot[] addSlots, Slot slot) { - final int insertPos = getSlotIndex(addSlots.length, slot.indexOrHash); - Slot old = addSlots[insertPos]; - addSlots[insertPos] = slot; - slot.next = old; - } - - private static int getSlotIndex(int tableSize, int indexOrHash) { - // This is a Java trick to efficiently "mod" the hash code by the table size. - // It only works if the table size is a power of 2. - // The performance improvement is measurable. - return indexOrHash & (tableSize - 1); - } -} diff --git a/src/main/java/org/mozilla/javascript/EqualObjectGraphs.java b/src/main/java/org/mozilla/javascript/EqualObjectGraphs.java deleted file mode 100644 index 2dbd0594..00000000 --- a/src/main/java/org/mozilla/javascript/EqualObjectGraphs.java +++ /dev/null @@ -1,321 +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.Arrays; -import java.util.IdentityHashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.SortedMap; -import java.util.TreeMap; - -import org.mozilla.javascript.debug.DebuggableObject; - -/** - * An object that implements deep equality test of objects, including their - * reference graph topology, that is in addition to establishing by-value - * equality of objects, it also establishes that their reachable object graphs - * have identical shape. It is capable of custom-comparing a wide range of - * various objects, including various Rhino Scriptables, Java arrays, Java - * Lists, and to some degree Java Maps and Sets (sorted Maps are okay, as well - * as Sets with elements that can be sorted using their Comparable - * implementation, and Maps whose keysets work the same). The requirement for - * sortable maps and sets is to ensure deterministic order of traversal, which - * is necessary for establishing structural equality of object graphs. - * - * An instance of this object is stateful in that it memoizes pairs of objects - * that already compared equal, so reusing an instance for repeated equality - * tests of potentially overlapping object graph is beneficial for performance - * as long as all equality test invocations returns true. Reuse is not advised - * after an equality test returned false since there is a heuristic in comparing - * cyclic data structures that can memoize false equalities if two cyclic data - * structures end up being unequal. - */ -final class EqualObjectGraphs { - private static final ThreadLocal instance = new ThreadLocal<>(); - - // Object pairs already known to be equal. Used to short-circuit repeated traversals of objects reachable through - // different paths as well as to detect structural inequality. - private final Map knownEquals = new IdentityHashMap<>(); - // Currently compared objects; used to avoid infinite recursion over cyclic object graphs. - private final Map currentlyCompared = new IdentityHashMap<>(); - - static T withThreadLocal(java.util.function.Function action) { - final EqualObjectGraphs currEq = instance.get(); - if (currEq == null) { - final EqualObjectGraphs eq = new EqualObjectGraphs(); - instance.set(eq); - try { - return action.apply(eq); - } finally { - instance.set(null); - } - } - return action.apply(currEq); - } - - boolean equalGraphs(Object o1, Object o2) { - if (o1 == o2) { - return true; - } else if (o1 == null || o2 == null) { - return false; - } - - final Object curr2 = currentlyCompared.get(o1); - if (curr2 == o2) { - // Provisionally afford that if we're already recursively comparing - // (o1, o2) that they'll be equal. NOTE: this is the heuristic - // mentioned in the class JavaDoc that can drive memoizing false - // equalities if cyclic data structures end up being unequal. - // While it would be possible to fix that with additional code, the - // usual usage of equality comparisons is short-circuit-on-false anyway, - // so this edge case should not arise in normal usage and the additional - // code complexity to guard against it is not worth it. - return true; - } else if (curr2 != null) { - // If we're already recursively comparing o1 to some other object, - // this comparison is structurally false. - return false; - } - - final Object prev2 = knownEquals.get(o1); - if (prev2 == o2) { - // o1 known to be equal to o2. - return true; - } else if (prev2 != null) { - // o1 known to be equal to something other than o2. - return false; - } - - final Object prev1 = knownEquals.get(o2); - assert prev1 != o1; // otherwise we would've already returned at prev2 == o2 - if (prev1 != null) { - // o2 known to be equal to something other than o1. - return false; - } - - currentlyCompared.put(o1, o2); - final boolean eq = equalGraphsNoMemo(o1, o2); - if (eq) { - knownEquals.put(o1, o2); - knownEquals.put(o2, o1); - } - currentlyCompared.remove(o1); - return eq; - } - - private boolean equalGraphsNoMemo(Object o1, Object o2) { - if (o1 instanceof Wrapper) { - return o2 instanceof Wrapper && equalGraphs(((Wrapper)o1).unwrap(), ((Wrapper)o2).unwrap()); - } else if (o1 instanceof Scriptable) { - return o2 instanceof Scriptable && equalScriptables((Scriptable)o1, (Scriptable)o2); - } else if (o1 instanceof ConsString) { - return ((ConsString)o1).toString().equals(o2); - } else if (o2 instanceof ConsString) { - return o1.equals(((ConsString)o2).toString()); - } else if (o1 instanceof SymbolKey) { - return o2 instanceof SymbolKey && equalGraphs(((SymbolKey)o1).getName(), ((SymbolKey)o2).getName()); - } else if (o1 instanceof Object[]) { - return o2 instanceof Object[] && equalObjectArrays((Object[])o1, (Object[])o2); - } else if (o1.getClass().isArray()) { - return Objects.deepEquals(o1, o2); - } else if (o1 instanceof List) { - return o2 instanceof List && equalLists((List)o1, (List)o2); - } else if (o1 instanceof Map) { - return o2 instanceof Map && equalMaps((Map)o1, (Map)o2); - } else if (o1 instanceof Set) { - return o2 instanceof Set && equalSets((Set)o1, (Set)o2); - } else if (o1 instanceof NativeGlobal) { - return o2 instanceof NativeGlobal; // stateless objects - } else if (o1 instanceof JavaAdapter) { - return o2 instanceof JavaAdapter; // stateless objects - } else if (o1 instanceof NativeJavaTopPackage) { - return o2 instanceof NativeJavaTopPackage; // stateless objects - } - - // Fallback case for everything else. - return o1.equals(o2); - } - - private boolean equalScriptables(final Scriptable s1, final Scriptable s2) { - final Object[] ids1 = getSortedIds(s1); - final Object[] ids2 = getSortedIds(s2); - if (!equalObjectArrays(ids1, ids2)) { - return false; - } - final int l = ids1.length; - for(int i = 0; i < l; ++i) { - if (!equalGraphs(getValue(s1, ids1[i]), getValue(s2, ids2[i]))) { - return false; - } - } - if (!equalGraphs(s1.getPrototype(), s2.getPrototype())) { - return false; - } else if (!equalGraphs(s1.getParentScope(), s2.getParentScope())) { - return false; - } - - // Handle special Scriptable implementations - if (s1 instanceof NativeContinuation) { - return s2 instanceof NativeContinuation && NativeContinuation.equalImplementations((NativeContinuation)s1, (NativeContinuation)s2); - } else if (s1 instanceof NativeJavaPackage) { - return s1.equals(s2); // Overridden appropriately - } else if (s1 instanceof IdFunctionObject) { - return s2 instanceof IdFunctionObject && IdFunctionObject.equalObjectGraphs((IdFunctionObject)s1, (IdFunctionObject)s2, this); - } else if (s1 instanceof InterpretedFunction) { - return s2 instanceof InterpretedFunction && equalInterpretedFunctions((InterpretedFunction)s1, (InterpretedFunction)s2); - } else if (s1 instanceof ArrowFunction) { - return s2 instanceof ArrowFunction && ArrowFunction.equalObjectGraphs((ArrowFunction)s1, (ArrowFunction)s2, this); - } else if (s1 instanceof BoundFunction) { - return s2 instanceof BoundFunction && BoundFunction.equalObjectGraphs((BoundFunction)s1, (BoundFunction)s2, this); - } else if (s1 instanceof NativeSymbol) { - return s2 instanceof NativeSymbol && equalGraphs(((NativeSymbol)s1).getKey(), ((NativeSymbol)s2).getKey()); - } - return true; - } - - private boolean equalObjectArrays(final Object[] a1, final Object[] a2) { - if (a1.length != a2.length) { - return false; - } - for(int i = 0; i < a1.length; ++i) { - if (!equalGraphs(a1[i], a2[i])) { - return false; - } - } - return true; - } - - private boolean equalLists(final List l1, final List l2) { - if (l1.size() != l2.size()) { - return false; - } - final Iterator i1 = l1.iterator(); - final Iterator i2 = l2.iterator(); - while(i1.hasNext() && i2.hasNext()) { - if (!equalGraphs(i1.next(), i2.next())) { - return false; - } - } - assert !(i1.hasNext() || i2.hasNext()); - return true; - } - - @SuppressWarnings("rawtypes") - private boolean equalMaps(final Map m1, final Map m2) { - if (m1.size() != m2.size()) { - return false; - } - final Iterator i1 = sortedEntries(m1); - final Iterator i2 = sortedEntries(m2); - - while(i1.hasNext() && i2.hasNext()) { - final Map.Entry kv1 = i1.next(); - final Map.Entry kv2 = i2.next(); - if (!(equalGraphs(kv1.getKey(), kv2.getKey()) && equalGraphs(kv1.getValue(), kv2.getValue()))) { - return false; - } - } - assert !(i1.hasNext() || i2.hasNext()); - // TODO: assert linked maps traversal order? - return true; - - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private static Iterator sortedEntries(final Map m) { - // Yes, this throws ClassCastException if the keys aren't comparable. That's okay. We only support maps with - // deterministic traversal order. - final Map sortedMap = (m instanceof SortedMap ? m : new TreeMap(m)); - return sortedMap.entrySet().iterator(); - } - - private boolean equalSets(final Set s1, final Set s2) { - return equalObjectArrays(sortedSet(s1), sortedSet(s2)); - } - - private static Object[] sortedSet(final Set s) { - final Object[] a = s.toArray(); - Arrays.sort(a); // ClassCastException possible - return a; - } - - private static boolean equalInterpretedFunctions(final InterpretedFunction f1, final InterpretedFunction f2) { - return Objects.equals(f1.getEncodedSource(), f2.getEncodedSource()); - } - - // Sort IDs deterministically - private static Object[] getSortedIds(final Scriptable s) { - final Object[] ids = getIds(s); - Arrays.sort(ids, (a, b) -> { - if (a instanceof Integer) { - if (b instanceof Integer) { - return ((Integer)a).compareTo((Integer)b); - } else if (b instanceof String || b instanceof Symbol) { - return -1; // ints before strings or symbols - } - } else if (a instanceof String) { - if (b instanceof String) { - return ((String)a).compareTo((String)b); - } else if (b instanceof Integer) { - return 1; // strings after ints - } else if (b instanceof Symbol) { - return -1; // strings before symbols - } - } else if (a instanceof Symbol) { - if (b instanceof Symbol) { - // As long as people bother to reasonably name their symbols, - // this will work. If there's clashes in symbol names (e.g. - // lots of unnamed symbols) it can lead to false inequalities. - return getSymbolName((Symbol)a).compareTo(getSymbolName((Symbol)b)); - } else if (b instanceof Integer || b instanceof String) { - return 1; // symbols after ints and strings - } - } - // We can only compare Rhino key types: Integer, String, Symbol - throw new ClassCastException(); - }); - return ids; - } - - private static String getSymbolName(final Symbol s) { - if (s instanceof SymbolKey) { - return ((SymbolKey)s).getName(); - } else if (s instanceof NativeSymbol) { - return ((NativeSymbol)s).getKey().getName(); - } else { - // We can only handle native Rhino Symbol types - throw new ClassCastException(); - } - } - - private static Object[] getIds(final Scriptable s) { - if (s instanceof ScriptableObject) { - // Grabs symbols too - return ((ScriptableObject)s).getIds(true, true); - } else if (s instanceof DebuggableObject) { - return ((DebuggableObject)s).getAllIds(); - } else { - return s.getIds(); - } - } - - private static Object getValue(final Scriptable s, final Object id) { - if (id instanceof Symbol) { - return ScriptableObject.getProperty(s, (Symbol)id); - } else if (id instanceof Integer) { - return ScriptableObject.getProperty(s, ((Integer)id).intValue()); - } else if (id instanceof String) { - return ScriptableObject.getProperty(s, (String)id); - } else { - throw new ClassCastException(); - } - } -} diff --git a/src/main/java/org/mozilla/javascript/ErrorReporter.java b/src/main/java/org/mozilla/javascript/ErrorReporter.java deleted file mode 100644 index f65f454b..00000000 --- a/src/main/java/org/mozilla/javascript/ErrorReporter.java +++ /dev/null @@ -1,74 +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; - -/** - * This is interface defines a protocol for the reporting of - * errors during JavaScript translation or execution. - * - * @author Norris Boyd - */ - -public interface ErrorReporter { - - /** - * Report a warning. - * - * The implementing class may choose to ignore the warning - * if it desires. - * - * @param message a String describing the warning - * @param sourceName a String describing the JavaScript source - * where the warning occured; typically a filename or URL - * @param line the line number associated with the warning - * @param lineSource the text of the line (may be null) - * @param lineOffset the offset into lineSource where problem was detected - */ - void warning(String message, String sourceName, int line, - String lineSource, int lineOffset); - - /** - * Report an error. - * - * The implementing class is free to throw an exception if - * it desires. - * - * If execution has not yet begun, the JavaScript engine is - * free to find additional errors rather than terminating - * the translation. It will not execute a script that had - * errors, however. - * - * @param message a String describing the error - * @param sourceName a String describing the JavaScript source - * where the error occured; typically a filename or URL - * @param line the line number associated with the error - * @param lineSource the text of the line (may be null) - * @param lineOffset the offset into lineSource where problem was detected - */ - void error(String message, String sourceName, int line, - String lineSource, int lineOffset); - - /** - * Creates an EvaluatorException that may be thrown. - * - * runtimeErrors, unlike errors, will always terminate the - * current script. - * - * @param message a String describing the error - * @param sourceName a String describing the JavaScript source - * where the error occured; typically a filename or URL - * @param line the line number associated with the error - * @param lineSource the text of the line (may be null) - * @param lineOffset the offset into lineSource where problem was detected - * @return an EvaluatorException that will be thrown. - */ - EvaluatorException runtimeError(String message, String sourceName, - int line, String lineSource, - int lineOffset); -} diff --git a/src/main/java/org/mozilla/javascript/Evaluator.java b/src/main/java/org/mozilla/javascript/Evaluator.java deleted file mode 100644 index 42215e36..00000000 --- a/src/main/java/org/mozilla/javascript/Evaluator.java +++ /dev/null @@ -1,96 +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.List; - -import org.mozilla.javascript.ast.ScriptNode; - -/** - * Abstraction of evaluation, which can be implemented either by an - * interpreter or compiler. - */ -public interface Evaluator { - - /** - * Compile the script or function from intermediate representation - * tree into an executable form. - * - * @param compilerEnv Compiler environment - * @param tree parse tree - * @param encodedSource encoding of the source code for decompilation - * @param returnFunction if true, compiling a function - * @return an opaque object that can be passed to either - * createFunctionObject or createScriptObject, depending on the - * value of returnFunction - */ - public Object compile(CompilerEnvirons compilerEnv, - ScriptNode tree, - String encodedSource, - boolean returnFunction); - - /** - * Create a function object. - * - * @param cx Current context - * @param scope scope of the function - * @param bytecode opaque object returned by compile - * @param staticSecurityDomain security domain - * @return Function object that can be called - */ - public Function createFunctionObject(Context cx, Scriptable scope, - Object bytecode, Object staticSecurityDomain); - - /** - * Create a script object. - * - * @param bytecode opaque object returned by compile - * @param staticSecurityDomain security domain - * @return Script object that can be evaluated - */ - public Script createScriptObject(Object bytecode, - Object staticSecurityDomain); - - /** - * Capture stack information from the given exception. - * @param ex an exception thrown during execution - */ - public void captureStackInfo(RhinoException ex); - - /** - * Get the source position information by examining the stack. - * @param cx Context - * @param linep Array object of length >= 1; getSourcePositionFromStack - * will assign the line number to linep[0]. - * @return the name of the file or other source container - */ - public String getSourcePositionFromStack(Context cx, int[] linep); - - /** - * Given a native stack trace, patch it with script-specific source - * and line information - * @param ex exception - * @param nativeStackTrace the native stack trace - * @return patched stack trace - */ - public String getPatchedStack(RhinoException ex, - String nativeStackTrace); - - /** - * Get the script stack for the given exception - * @param ex exception from execution - * @return list of strings for the stack trace - */ - public List getScriptStack(RhinoException ex); - - /** - * Mark the given script to indicate it was created by a call to - * eval() or to a Function constructor. - * @param script script to mark as from eval - */ - public void setEvalScriptFlag(Script script); -} diff --git a/src/main/java/org/mozilla/javascript/EvaluatorException.java b/src/main/java/org/mozilla/javascript/EvaluatorException.java deleted file mode 100644 index f1510519..00000000 --- a/src/main/java/org/mozilla/javascript/EvaluatorException.java +++ /dev/null @@ -1,95 +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 of exceptions thrown by the JavaScript engine. - */ -public class EvaluatorException extends RhinoException -{ - private static final long serialVersionUID = -8743165779676009808L; - - public EvaluatorException(String detail) - { - super(detail); - } - - /** - * Create an exception with the specified detail message. - * - * Errors internal to the JavaScript engine will simply throw a - * RuntimeException. - * - * @param detail the error message - * @param sourceName the name of the source reponsible for the error - * @param lineNumber the line number of the source - */ - public EvaluatorException(String detail, String sourceName, - int lineNumber) - { - this(detail, sourceName, lineNumber, null, 0); - } - - /** - * Create an exception with the specified detail message. - * - * Errors internal to the JavaScript engine will simply throw a - * RuntimeException. - * - * @param detail the error message - * @param sourceName the name of the source responsible for the error - * @param lineNumber the line number of the source - * @param columnNumber the columnNumber of the source (may be zero if - * unknown) - * @param lineSource the source of the line containing the error (may be - * null if unknown) - */ - public EvaluatorException(String detail, String sourceName, int lineNumber, - String lineSource, int columnNumber) - { - super(detail); - recordErrorOrigin(sourceName, lineNumber, lineSource, columnNumber); - } - - /** - * @deprecated Use {@link RhinoException#sourceName()} from the super class. - */ - @Deprecated - public String getSourceName() - { - return sourceName(); - } - - /** - * @deprecated Use {@link RhinoException#lineNumber()} from the super class. - */ - @Deprecated - public int getLineNumber() - { - return lineNumber(); - } - - /** - * @deprecated Use {@link RhinoException#columnNumber()} from the super class. - */ - @Deprecated - public int getColumnNumber() - { - return columnNumber(); - } - - /** - * @deprecated Use {@link RhinoException#lineSource()} from the super class. - */ - @Deprecated - public String getLineSource() - { - return lineSource(); - } - -} diff --git a/src/main/java/org/mozilla/javascript/ExternalArrayData.java b/src/main/java/org/mozilla/javascript/ExternalArrayData.java deleted file mode 100644 index 0b5fe461..00000000 --- a/src/main/java/org/mozilla/javascript/ExternalArrayData.java +++ /dev/null @@ -1,32 +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; - -/** - * Implement this interface in order to allow external data to be attached to a ScriptableObject. - */ - -public interface ExternalArrayData -{ - /** - * Return the element at the specified index. The result must be a type that is valid in JavaScript: - * Number, String, or Scriptable. This method will not be called unless "index" is in - * range. - */ - Object getArrayElement(int index); - - /** - * Set the element at the specified index. This method will not be called unless "index" is in - * range. The method must check that "value" is a valid type, and convert it if necessary. - */ - void setArrayElement(int index, Object value); - - /** - * Return the length of the array. - */ - int getArrayLength(); -} diff --git a/src/main/java/org/mozilla/javascript/Function.java b/src/main/java/org/mozilla/javascript/Function.java deleted file mode 100644 index 4d702318..00000000 --- a/src/main/java/org/mozilla/javascript/Function.java +++ /dev/null @@ -1,48 +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; - -/** - * This is interface that all functions in JavaScript must implement. The interface provides for - * calling functions and constructors. - * - * @see org.mozilla.javascript.Scriptable - * @author Norris Boyd - */ -public interface Function extends Scriptable, Callable, Constructable { - /** - * Call the function. - * - *

Note that the array of arguments is not guaranteed to have length greater than 0. - * - * @param cx the current Context for this thread - * @param scope the scope to execute the function relative to. This is set to the value returned - * by getParentScope() except when the function is called from a closure. - * @param thisObj the JavaScript this object - * @param args the array of arguments - * @return the result of the call - */ - @Override - Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args); - - /** - * 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 - */ - @Override - Scriptable construct(Context cx, Scriptable scope, Object[] args); -} diff --git a/src/main/java/org/mozilla/javascript/FunctionObject.java b/src/main/java/org/mozilla/javascript/FunctionObject.java deleted file mode 100644 index 72ca2262..00000000 --- a/src/main/java/org/mozilla/javascript/FunctionObject.java +++ /dev/null @@ -1,497 +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.io.IOException; -import java.io.ObjectInputStream; -import java.lang.reflect.Constructor; -import java.lang.reflect.Member; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import org.mozilla.javascript.commonjs.module.ModuleScope; - -public class FunctionObject extends BaseFunction { - private static final long serialVersionUID = -5332312783643935019L; - - /** - * Create a JavaScript function object from a Java method. - * - *

The member argument must be either a java.lang.reflect.Method or a - * java.lang.reflect.Constructor and must match one of two forms. - * - *

The first form is a member with zero or more parameters of the following types: Object, - * String, boolean, Scriptable, int, or double. The Long type is not supported because the - * double representation of a long (which is the EMCA-mandated storage type for Numbers) may - * lose precision. If the member is a Method, the return value must be void or one of the types - * allowed for parameters. - * - *

The runtime will perform appropriate conversions based upon the type of the parameter. A - * parameter type of Object specifies that no conversions are to be done. A parameter of type - * String will use Context.toString to convert arguments. Similarly, parameters of type double, - * boolean, and Scriptable will cause Context.toNumber, Context.toBoolean, and Context.toObject, - * respectively, to be called. - * - *

If the method is not static, the Java 'this' value will correspond to the JavaScript - * 'this' value. Any attempt to call the function with a 'this' value that is not of the right - * Java type will result in an error. - * - *

The second form is the variable arguments (or "varargs") form. If the FunctionObject will - * be used as a constructor, the member must have the following parameters - * - *

-     *      (Context cx, Object[] args, Function ctorObj,
-     *       boolean inNewExpr)
- * - * and if it is a Method, be static and return an Object result. - * - *

Otherwise, if the FunctionObject will not be used to define a constructor, the - * member must be a static Method with parameters - * - *

-     *      (Context cx, Scriptable thisObj, Object[] args,
-     *       Function funObj) 
- * - * and an Object result. - * - *

When the function varargs form is called as part of a function call, the args - * parameter contains the arguments, with thisObj set to the JavaScript 'this' - * value. funObj is the function object for the invoked function. - * - *

When the constructor varargs form is called or invoked while evaluating a new - * expression, args contains the arguments, ctorObj refers to this - * FunctionObject, and inNewExpr is true if and only if a new - * expression caused the call. This supports defining a function that has different behavior - * when called as a constructor than when invoked as a normal function call. (For example, the - * Boolean constructor, when called as a function, will convert to boolean rather than creating - * a new object.) - * - *

- * - * @param name the name of the function - * @param methodOrConstructor a java.lang.reflect.Method or a java.lang.reflect.Constructor that - * defines the object - * @param scope enclosing scope of function - * @see org.mozilla.javascript.Scriptable - */ - public FunctionObject(String name, Member methodOrConstructor, Scriptable scope) { - if (methodOrConstructor instanceof Constructor) { - member = new MemberBox((Constructor) methodOrConstructor); - isStatic = true; // well, doesn't take a 'this' - } else { - member = new MemberBox((Method) methodOrConstructor); - isStatic = member.isStatic(); - } - String methodName = member.getName(); - this.functionName = name; - Class[] types = member.argTypes; - int arity = types.length; - if (arity == 4 && (types[1].isArray() || types[2].isArray())) { - // Either variable args or an error. - if (types[1].isArray()) { - if (!isStatic - || types[0] != ScriptRuntime.ContextClass - || types[1].getComponentType() != ScriptRuntime.ObjectClass - || types[2] != ScriptRuntime.FunctionClass - || types[3] != Boolean.TYPE) { - throw Context.reportRuntimeErrorById("msg.varargs.ctor", methodName); - } - parmsLength = VARARGS_CTOR; - } else { - if (!isStatic - || types[0] != ScriptRuntime.ContextClass - || types[1] != ScriptRuntime.ScriptableClass - || types[2].getComponentType() != ScriptRuntime.ObjectClass - || types[3] != ScriptRuntime.FunctionClass) { - throw Context.reportRuntimeErrorById("msg.varargs.fun", methodName); - } - parmsLength = VARARGS_METHOD; - } - } else { - parmsLength = arity; - if (arity > 0) { - typeTags = new byte[arity]; - for (int i = 0; i != arity; ++i) { - int tag = getTypeTag(types[i]); - if (tag == JAVA_UNSUPPORTED_TYPE) { - throw Context.reportRuntimeErrorById( - "msg.bad.parms", types[i].getName(), methodName); - } - typeTags[i] = (byte) tag; - } - } - } - - if (member.isMethod()) { - Method method = member.method(); - Class returnType = method.getReturnType(); - if (returnType == Void.TYPE) { - hasVoidReturn = true; - } else { - returnTypeTag = getTypeTag(returnType); - } - } else { - Class ctorType = member.getDeclaringClass(); - if (!ScriptRuntime.ScriptableClass.isAssignableFrom(ctorType)) { - throw Context.reportRuntimeErrorById("msg.bad.ctor.return", ctorType.getName()); - } - } - - ScriptRuntime.setFunctionProtoAndParent(this, scope); - } - - /** - * @return One of JAVA_*_TYPE constants to indicate desired type or {@link - * #JAVA_UNSUPPORTED_TYPE} if the convertion is not possible - */ - public static int getTypeTag(Class type) { - if (type == ScriptRuntime.StringClass) return JAVA_STRING_TYPE; - if (type == ScriptRuntime.IntegerClass || type == Integer.TYPE) return JAVA_INT_TYPE; - if (type == ScriptRuntime.BooleanClass || type == Boolean.TYPE) return JAVA_BOOLEAN_TYPE; - if (type == ScriptRuntime.DoubleClass || type == Double.TYPE) return JAVA_DOUBLE_TYPE; - if (ScriptRuntime.ScriptableClass.isAssignableFrom(type)) return JAVA_SCRIPTABLE_TYPE; - if (type == ScriptRuntime.ObjectClass) return JAVA_OBJECT_TYPE; - - // Note that the long type is not supported; see the javadoc for - // the constructor for this class - - return JAVA_UNSUPPORTED_TYPE; - } - - public static Object convertArg(Context cx, Scriptable scope, Object arg, int typeTag) { - switch (typeTag) { - case JAVA_STRING_TYPE: - if (arg instanceof String) return arg; - return ScriptRuntime.toString(arg); - case JAVA_INT_TYPE: - if (arg instanceof Integer) return arg; - return Integer.valueOf(ScriptRuntime.toInt32(arg)); - case JAVA_BOOLEAN_TYPE: - if (arg instanceof Boolean) return arg; - return ScriptRuntime.toBoolean(arg) ? Boolean.TRUE : Boolean.FALSE; - case JAVA_DOUBLE_TYPE: - if (arg instanceof Double) return arg; - return Double.valueOf(ScriptRuntime.toNumber(arg)); - case JAVA_SCRIPTABLE_TYPE: - return ScriptRuntime.toObjectOrNull(cx, arg, scope); - case JAVA_OBJECT_TYPE: - if (arg instanceof ConsString) return arg.toString(); - return arg; - default: - throw new IllegalArgumentException(); - } - } - - /** - * Return the value defined by the method used to construct the object (number of parameters of - * the method, or 1 if the method is a "varargs" form). - */ - @Override - public int getArity() { - return parmsLength < 0 ? 1 : parmsLength; - } - - /** Return the same value as {@link #getArity()}. */ - @Override - public int getLength() { - return getArity(); - } - - @Override - public String getFunctionName() { - return (functionName == null) ? "" : functionName; - } - - /** Get Java method or constructor this function represent. */ - public Member getMethodOrConstructor() { - if (member.isMethod()) { - return member.method(); - } else { - return member.ctor(); - } - } - - static Method findSingleMethod(Method[] methods, String name) { - Method found = null; - for (int i = 0, N = methods.length; i != N; ++i) { - Method method = methods[i]; - if (method != null && name.equals(method.getName())) { - if (found != null) { - throw Context.reportRuntimeErrorById( - "msg.no.overload", name, method.getDeclaringClass().getName()); - } - found = method; - } - } - return found; - } - - /** - * Returns all public methods declared by the specified class. This excludes inherited methods. - * - * @param clazz the class from which to pull public declared methods - * @return the public methods declared in the specified class - * @see Class#getDeclaredMethods() - */ - static Method[] getMethodList(Class clazz) { - Method[] methods = null; - try { - // getDeclaredMethods may be rejected by the security manager - // but getMethods is more expensive - if (!sawSecurityException) methods = clazz.getDeclaredMethods(); - } catch (SecurityException e) { - // If we get an exception once, give up on getDeclaredMethods - sawSecurityException = true; - } - if (methods == null) { - methods = clazz.getMethods(); - } - int count = 0; - for (int i = 0; i < methods.length; i++) { - if (sawSecurityException - ? methods[i].getDeclaringClass() != clazz - : !Modifier.isPublic(methods[i].getModifiers())) { - methods[i] = null; - } else { - count++; - } - } - Method[] result = new Method[count]; - int j = 0; - for (int i = 0; i < methods.length; i++) { - if (methods[i] != null) result[j++] = methods[i]; - } - return result; - } - - /** - * Define this function as a JavaScript constructor. - * - *

Sets up the "prototype" and "constructor" properties. Also calls setParent and - * setPrototype with appropriate values. Then adds the function object as a property of the - * given scope, using prototype.getClassName() as the name of the property. - * - * @param scope the scope in which to define the constructor (typically the global object) - * @param prototype the prototype object - * @see org.mozilla.javascript.Scriptable#setParentScope - * @see org.mozilla.javascript.Scriptable#setPrototype - * @see org.mozilla.javascript.Scriptable#getClassName - */ - public void addAsConstructor(Scriptable scope, Scriptable prototype) { - initAsConstructor(scope, prototype); - defineProperty(scope, prototype.getClassName(), this, ScriptableObject.DONTENUM); - } - - void initAsConstructor(Scriptable scope, Scriptable prototype) { - ScriptRuntime.setFunctionProtoAndParent(this, scope); - setImmunePrototypeProperty(prototype); - - prototype.setParentScope(this); - - defineProperty( - prototype, - "constructor", - this, - ScriptableObject.DONTENUM | ScriptableObject.PERMANENT | ScriptableObject.READONLY); - setParentScope(scope); - } - - /** - * @deprecated Use {@link #getTypeTag(Class)} and {@link #convertArg(Context, Scriptable, - * Object, int)} for type conversion. - */ - @Deprecated - public static Object convertArg(Context cx, Scriptable scope, Object arg, Class desired) { - int tag = getTypeTag(desired); - if (tag == JAVA_UNSUPPORTED_TYPE) { - throw Context.reportRuntimeErrorById("msg.cant.convert", desired.getName()); - } - return convertArg(cx, scope, arg, tag); - } - - /** - * Performs conversions on argument types if needed and invokes the underlying Java method or - * constructor. - * - *

Implements Function.call. - * - * @see org.mozilla.javascript.Function#call( Context, Scriptable, Scriptable, Object[]) - */ - @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - Object result; - boolean checkMethodResult = false; - int argsLength = args.length; - - if (parmsLength < 0) { - for (int i = 0; i < argsLength; i++) { - // flatten cons-strings before passing them as arguments - if (args[i] instanceof ConsString) { - args[i] = args[i].toString(); - } - } - - if (parmsLength == VARARGS_METHOD) { - Object[] invokeArgs = {cx, thisObj, args, this}; - result = member.invoke(null, invokeArgs); - checkMethodResult = true; - } else { - boolean inNewExpr = (thisObj == null); - Boolean b = inNewExpr ? Boolean.TRUE : Boolean.FALSE; - Object[] invokeArgs = {cx, args, this, b}; - result = - (member.isCtor()) - ? member.newInstance(invokeArgs) - : member.invoke(null, invokeArgs); - } - - } else { - if (!isStatic) { - Class clazz = member.getDeclaringClass(); - if (thisObj instanceof Delegator) { - thisObj = ((Delegator) thisObj).getDelegee(); - } - if (!clazz.isInstance(thisObj)) { - boolean compatible = false; - if (thisObj == scope || thisObj instanceof ModuleScope) { - Scriptable parentScope = getParentScope(); - if (scope != parentScope) { - // Call with dynamic scope for standalone function, - // use parentScope as thisObj - compatible = clazz.isInstance(parentScope); - if (compatible) { - thisObj = parentScope; - } - } - } - if (!compatible) { - // Couldn't find an object to call this on. - throw ScriptRuntime.typeErrorById("msg.incompat.call", functionName); - } - } - } - - Object[] invokeArgs; - if (parmsLength == argsLength) { - // Do not allocate new argument array if java arguments are - // the same as the original js ones. - invokeArgs = args; - for (int i = 0; i != parmsLength; ++i) { - Object arg = args[i]; - Object converted = convertArg(cx, scope, arg, typeTags[i]); - if (arg != converted) { - if (invokeArgs == args) { - invokeArgs = args.clone(); - } - invokeArgs[i] = converted; - } - } - } else if (parmsLength == 0) { - invokeArgs = ScriptRuntime.emptyArgs; - } else { - invokeArgs = new Object[parmsLength]; - for (int i = 0; i != parmsLength; ++i) { - Object arg = (i < argsLength) ? args[i] : Undefined.instance; - invokeArgs[i] = convertArg(cx, scope, arg, typeTags[i]); - } - } - - if (member.isMethod()) { - result = member.invoke(thisObj, invokeArgs); - checkMethodResult = true; - } else { - result = member.newInstance(invokeArgs); - } - } - - if (checkMethodResult) { - if (hasVoidReturn) { - result = Undefined.instance; - } else if (returnTypeTag == JAVA_UNSUPPORTED_TYPE) { - result = cx.getWrapFactory().wrap(cx, scope, result, null); - } - // XXX: the code assumes that if returnTypeTag == JAVA_OBJECT_TYPE - // then the Java method did a proper job of converting the - // result to JS primitive or Scriptable to avoid - // potentially costly Context.javaToJS call. - } - - return result; - } - - /** - * Return new {@link Scriptable} instance using the default constructor for the class of the - * underlying Java method. Return null to indicate that the call method should be used to create - * new objects. - */ - @Override - public Scriptable createObject(Context cx, Scriptable scope) { - if (member.isCtor() || parmsLength == VARARGS_CTOR) { - return null; - } - Scriptable result; - try { - result = (Scriptable) member.getDeclaringClass().newInstance(); - } catch (Exception ex) { - throw Context.throwAsScriptRuntimeEx(ex); - } - - result.setPrototype(getClassPrototype()); - result.setParentScope(getParentScope()); - return result; - } - - boolean isVarArgsMethod() { - return parmsLength == VARARGS_METHOD; - } - - boolean isVarArgsConstructor() { - return parmsLength == VARARGS_CTOR; - } - - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - if (parmsLength > 0) { - Class[] types = member.argTypes; - typeTags = new byte[parmsLength]; - for (int i = 0; i != parmsLength; ++i) { - typeTags[i] = (byte) getTypeTag(types[i]); - } - } - if (member.isMethod()) { - Method method = member.method(); - Class returnType = method.getReturnType(); - if (returnType == Void.TYPE) { - hasVoidReturn = true; - } else { - returnTypeTag = getTypeTag(returnType); - } - } - } - - private static final short VARARGS_METHOD = -1; - private static final short VARARGS_CTOR = -2; - - private static boolean sawSecurityException; - - public static final int JAVA_UNSUPPORTED_TYPE = 0; - public static final int JAVA_STRING_TYPE = 1; - public static final int JAVA_INT_TYPE = 2; - public static final int JAVA_BOOLEAN_TYPE = 3; - public static final int JAVA_DOUBLE_TYPE = 4; - public static final int JAVA_SCRIPTABLE_TYPE = 5; - public static final int JAVA_OBJECT_TYPE = 6; - - MemberBox member; - private String functionName; - private transient byte[] typeTags; - private int parmsLength; - private transient boolean hasVoidReturn; - private transient int returnTypeTag; - private boolean isStatic; -} diff --git a/src/main/java/org/mozilla/javascript/GeneratedClassLoader.java b/src/main/java/org/mozilla/javascript/GeneratedClassLoader.java deleted file mode 100644 index c7450862..00000000 --- a/src/main/java/org/mozilla/javascript/GeneratedClassLoader.java +++ /dev/null @@ -1,34 +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; - -/** - * Interface to define classes from generated byte code. - */ -public interface GeneratedClassLoader { - - /** - * Define a new Java class. - * Classes created via this method should have the same class loader. - * - * @param name fully qualified class name - * @param data class byte code - * @return new class object - */ - public Class defineClass(String name, byte[] data); - - /** - * Link the given class. - * - * @param cl Class instance returned from the previous call to - * {@link #defineClass(String, byte[])} - * @see java.lang.ClassLoader - */ - public void linkClass(Class cl); -} diff --git a/src/main/java/org/mozilla/javascript/HashSlotMap.java b/src/main/java/org/mozilla/javascript/HashSlotMap.java deleted file mode 100644 index 3b7d861b..00000000 --- a/src/main/java/org/mozilla/javascript/HashSlotMap.java +++ /dev/null @@ -1,97 +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.Iterator; -import java.util.LinkedHashMap; - -/** - * This class implements the SlotMap interface using a java.util.HashMap. This class has more - * overhead than EmbeddedSlotMap, especially because it puts each "Slot" inside an intermediate - * object. However it is much more resistant to large number of hash collisions than EmbeddedSlotMap - * and therefore we use this implementation when an object gains a large number of properties. - */ -public class HashSlotMap implements SlotMap { - - private final LinkedHashMap map = new LinkedHashMap<>(); - - @Override - public int size() { - return map.size(); - } - - @Override - public boolean isEmpty() { - return map.isEmpty(); - } - - @Override - public Slot query(Object key, int index) { - Object name = makeKey(key, index); - return map.get(name); - } - - @Override - public Slot modify(Object key, int index, int attributes) { - Object name = makeKey(key, index); - Slot slot = map.get(name); - if (slot != null) { - return slot; - } - - return createSlot(key, index, attributes); - } - - @Override - public void replace(Slot oldSlot, Slot newSlot) { - Object name = makeKey(oldSlot); - map.put(name, newSlot); - } - - private Slot createSlot(Object key, int index, int attributes) { - Slot newSlot = new Slot(key, index, attributes); - add(newSlot); - return newSlot; - } - - @Override - public void add(Slot newSlot) { - Object name = makeKey(newSlot); - map.put(name, newSlot); - } - - @Override - public void remove(Object key, int index) { - Object name = makeKey(key, index); - Slot slot = map.get(name); - if (slot != null) { - // non-configurable - if ((slot.getAttributes() & ScriptableObject.PERMANENT) != 0) { - Context cx = Context.getContext(); - if (cx.isStrictMode()) { - throw ScriptRuntime.typeErrorById( - "msg.delete.property.with.configurable.false", key); - } - return; - } - map.remove(name); - } - } - - @Override - public Iterator iterator() { - return map.values().iterator(); - } - - private Object makeKey(Object name, int index) { - return name == null ? String.valueOf(index) : name; - } - - private Object makeKey(Slot slot) { - return slot.name == null ? String.valueOf(slot.indexOrHash) : slot.name; - } -} diff --git a/src/main/java/org/mozilla/javascript/Hashtable.java b/src/main/java/org/mozilla/javascript/Hashtable.java deleted file mode 100644 index 79dd8728..00000000 --- a/src/main/java/org/mozilla/javascript/Hashtable.java +++ /dev/null @@ -1,317 +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.math.BigInteger; -import java.util.HashMap; -import java.util.Iterator; -import java.util.NoSuchElementException; - -/** - * This generic hash table class is used by Set and Map. It uses a standard HashMap for storing keys - * and values so that we can handle lots of hash collisions if necessary, and a doubly-linked list - * to support the iterator capability. - * - *

This second one is important because JavaScript handling of the iterator is completely - * different from the way that Java does it. In Java an attempt to modify a collection on a HashMap - * or LinkedHashMap while iterating through it (except by using the "remove" method on the Iterator - * object itself) results in a ConcurrentModificationException. JavaScript Maps and Sets explicitly - * allow the collection to be modified, or even cleared completely, while iterators exist, and even - * lets an iterator keep on iterating on a collection that was empty when it was created.. - */ -public class Hashtable implements Serializable, Iterable { - - private static final long serialVersionUID = -7151554912419543747L; - private final HashMap map = new HashMap<>(); - private Entry first = null; - private Entry last = null; - - /** - * One entry in the hash table. Override equals and hashcode because this is another area in - * which JavaScript and Java differ. This entry also becomes a node in the linked list. - */ - public static final class Entry implements Serializable { - private static final long serialVersionUID = 4086572107122965503L; - protected Object key; - protected Object value; - protected boolean deleted; - protected Entry next; - protected Entry prev; - private final int hashCode; - - Entry() { - hashCode = 0; - } - - Entry(Object k, Object value) { - if (k instanceof Number) { - if (k instanceof Double || k instanceof BigInteger) { - // BigInteger needs to retain its own type, due to - // "If Type(x) is different from Type(y), return false." in - // ecma262/multipage/abstract-operations.html#sec-samevaluezero - key = k; - } else { - // Hash comparison won't work if we don't do this - key = Double.valueOf(((Number) k).doubleValue()); - } - } else if (k instanceof ConsString) { - key = k.toString(); - } else { - key = k; - } - - if (key == null) { - hashCode = 0; - } else if (k.equals(ScriptRuntime.negativeZeroObj)) { - hashCode = 0; - } else { - hashCode = key.hashCode(); - } - - this.value = value; - } - - public Object key() { - return key; - } - - public Object value() { - return value; - } - - /** Zero out key and value and return old value. */ - void clear() { - key = Undefined.instance; - value = Undefined.instance; - deleted = true; - } - - @Override - public int hashCode() { - return hashCode; - } - - @Override - public boolean equals(Object o) { - if (o == null) { - return false; - } - try { - return ScriptRuntime.sameZero(key, ((Entry) o).key); - } catch (ClassCastException cce) { - return false; - } - } - } - - private static Entry makeDummy() { - final Entry d = new Entry(); - d.clear(); - return d; - } - - public int size() { - return map.size(); - } - - public void put(Object key, Object value) { - final Entry nv = new Entry(key, value); - final Entry ev = map.putIfAbsent(nv, nv); - if (ev == null) { - // New value -- insert to end of doubly-linked list - if (first == null) { - first = last = nv; - } else { - last.next = nv; - nv.prev = last; - last = nv; - } - } else { - // Update the existing value and keep it in the same place in the list - ev.value = value; - } - } - - /** - * @deprecated use getEntry(Object key) instead because this returns null if the entry was not - * found or the value of the entry is null - */ - public Object get(Object key) { - final Entry e = new Entry(key, null); - final Entry v = map.get(e); - if (v == null) { - return null; - } - return v.value; - } - - public Entry getEntry(Object key) { - final Entry e = new Entry(key, null); - return map.get(e); - } - - public boolean has(Object key) { - final Entry e = new Entry(key, null); - return map.containsKey(e); - } - - /** - * @deprecated use deleteEntry(Object key) instead because this returns null if the entry was - * not found or the value of the entry is null - */ - public Object delete(Object key) { - final Entry e = new Entry(key, null); - final Entry v = map.remove(e); - if (v == null) { - return null; - } - - // To keep existing iterators moving forward as specified in EC262, - // we will remove the "prev" pointers from the list but leave the "next" - // pointers intact. Once we do that, then the only things pointing to - // the deleted nodes are existing iterators. Once those are gone, then - // these objects will be GCed. - // This way, new iterators will not "see" the deleted elements, and - // existing iterators will continue from wherever they left off to - // continue iterating in insertion order. - if (v == first) { - if (v == last) { - // Removing the only element. Leave it as a dummy or existing iterators - // will never stop. - v.clear(); - v.prev = null; - } else { - first = v.next; - first.prev = null; - if (first.next != null) { - first.next.prev = first; - } - } - } else { - final Entry prev = v.prev; - prev.next = v.next; - v.prev = null; - if (v.next != null) { - v.next.prev = prev; - } else { - assert (v == last); - last = prev; - } - } - // Still clear the node in case it is in the chain of some iterator - final Object ret = v.value; - v.clear(); - return ret; - } - - public boolean deleteEntry(Object key) { - final Entry e = new Entry(key, null); - final Entry v = map.remove(e); - if (v == null) { - return false; - } - - // To keep existing iterators moving forward as specified in EC262, - // we will remove the "prev" pointers from the list but leave the "next" - // pointers intact. Once we do that, then the only things pointing to - // the deleted nodes are existing iterators. Once those are gone, then - // these objects will be GCed. - // This way, new iterators will not "see" the deleted elements, and - // existing iterators will continue from wherever they left off to - // continue iterating in insertion order. - if (v == first) { - if (v == last) { - // Removing the only element. Leave it as a dummy or existing iterators - // will never stop. - v.clear(); - v.prev = null; - } else { - first = v.next; - first.prev = null; - if (first.next != null) { - first.next.prev = first; - } - } - } else { - final Entry prev = v.prev; - prev.next = v.next; - v.prev = null; - if (v.next != null) { - v.next.prev = prev; - } else { - assert (v == last); - last = prev; - } - } - // Still clear the node in case it is in the chain of some iterator - v.clear(); - return true; - } - - public void clear() { - // Zero out all the entries so that existing iterators will skip them all - Iterator it = iterator(); - it.forEachRemaining(Entry::clear); - - // Replace the existing list with a dummy, and make it the last node - // of the current list. If new nodes are added now, existing iterators - // will drive forward right into the new list. If they are not, then - // nothing is referencing the old list and it'll get GCed. - if (first != null) { - Entry dummy = makeDummy(); - last.next = dummy; - first = last = dummy; - } - - // Now we can clear the actual hashtable! - map.clear(); - } - - public Iterator iterator() { - return new Iter(first); - } - - // The iterator for this class works directly on the linked list so that it implements - // the specified iteration behavior, which is very different from Java. - private static final class Iter implements Iterator { - private Entry pos; - - Iter(Entry start) { - // Keep the logic simpler by having a dummy at the start - Entry dummy = makeDummy(); - dummy.next = start; - this.pos = dummy; - } - - private void skipDeleted() { - // Skip forward past deleted elements, which could appear due to - // "delete" or a "clear" operation after this iterator was created. - // End up just before the next non-deleted node. - while ((pos.next != null) && pos.next.deleted) { - pos = pos.next; - } - } - - @Override - public boolean hasNext() { - skipDeleted(); - return ((pos != null) && (pos.next != null)); - } - - @Override - public Entry next() { - skipDeleted(); - if ((pos == null) || (pos.next == null)) { - throw new NoSuchElementException(); - } - final Entry e = pos.next; - pos = pos.next; - return e; - } - } -} diff --git a/src/main/java/org/mozilla/javascript/IRFactory.java b/src/main/java/org/mozilla/javascript/IRFactory.java deleted file mode 100644 index 60611adf..00000000 --- a/src/main/java/org/mozilla/javascript/IRFactory.java +++ /dev/null @@ -1,2552 +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.ArrayList; -import java.util.List; -import org.mozilla.javascript.ast.ArrayComprehension; -import org.mozilla.javascript.ast.ArrayComprehensionLoop; -import org.mozilla.javascript.ast.ArrayLiteral; -import org.mozilla.javascript.ast.Assignment; -import org.mozilla.javascript.ast.AstNode; -import org.mozilla.javascript.ast.AstRoot; -import org.mozilla.javascript.ast.BigIntLiteral; -import org.mozilla.javascript.ast.Block; -import org.mozilla.javascript.ast.BreakStatement; -import org.mozilla.javascript.ast.CatchClause; -import org.mozilla.javascript.ast.ConditionalExpression; -import org.mozilla.javascript.ast.ContinueStatement; -import org.mozilla.javascript.ast.DestructuringForm; -import org.mozilla.javascript.ast.DoLoop; -import org.mozilla.javascript.ast.ElementGet; -import org.mozilla.javascript.ast.EmptyExpression; -import org.mozilla.javascript.ast.ExpressionStatement; -import org.mozilla.javascript.ast.ForInLoop; -import org.mozilla.javascript.ast.ForLoop; -import org.mozilla.javascript.ast.FunctionCall; -import org.mozilla.javascript.ast.FunctionNode; -import org.mozilla.javascript.ast.GeneratorExpression; -import org.mozilla.javascript.ast.GeneratorExpressionLoop; -import org.mozilla.javascript.ast.IfStatement; -import org.mozilla.javascript.ast.InfixExpression; -import org.mozilla.javascript.ast.Jump; -import org.mozilla.javascript.ast.KeywordLiteral; -import org.mozilla.javascript.ast.Label; -import org.mozilla.javascript.ast.LabeledStatement; -import org.mozilla.javascript.ast.LetNode; -import org.mozilla.javascript.ast.Loop; -import org.mozilla.javascript.ast.Name; -import org.mozilla.javascript.ast.NewExpression; -import org.mozilla.javascript.ast.NumberLiteral; -import org.mozilla.javascript.ast.ObjectLiteral; -import org.mozilla.javascript.ast.ObjectProperty; -import org.mozilla.javascript.ast.ParenthesizedExpression; -import org.mozilla.javascript.ast.PropertyGet; -import org.mozilla.javascript.ast.RegExpLiteral; -import org.mozilla.javascript.ast.ReturnStatement; -import org.mozilla.javascript.ast.Scope; -import org.mozilla.javascript.ast.ScriptNode; -import org.mozilla.javascript.ast.StringLiteral; -import org.mozilla.javascript.ast.SwitchCase; -import org.mozilla.javascript.ast.SwitchStatement; -import org.mozilla.javascript.ast.Symbol; -import org.mozilla.javascript.ast.TaggedTemplateLiteral; -import org.mozilla.javascript.ast.TemplateCharacters; -import org.mozilla.javascript.ast.TemplateLiteral; -import org.mozilla.javascript.ast.ThrowStatement; -import org.mozilla.javascript.ast.TryStatement; -import org.mozilla.javascript.ast.UnaryExpression; -import org.mozilla.javascript.ast.UpdateExpression; -import org.mozilla.javascript.ast.VariableDeclaration; -import org.mozilla.javascript.ast.VariableInitializer; -import org.mozilla.javascript.ast.WhileLoop; -import org.mozilla.javascript.ast.WithStatement; -import org.mozilla.javascript.ast.XmlDotQuery; -import org.mozilla.javascript.ast.XmlElemRef; -import org.mozilla.javascript.ast.XmlExpression; -import org.mozilla.javascript.ast.XmlFragment; -import org.mozilla.javascript.ast.XmlLiteral; -import org.mozilla.javascript.ast.XmlMemberGet; -import org.mozilla.javascript.ast.XmlPropRef; -import org.mozilla.javascript.ast.XmlRef; -import org.mozilla.javascript.ast.XmlString; -import org.mozilla.javascript.ast.Yield; - -/** - * This class rewrites the parse tree into an IR suitable for codegen. - * - * @see Node - * @author Mike McCabe - * @author Norris Boyd - */ -public final class IRFactory extends Parser { - private static final int LOOP_DO_WHILE = 0; - private static final int LOOP_WHILE = 1; - private static final int LOOP_FOR = 2; - - private static final int ALWAYS_TRUE_BOOLEAN = 1; - private static final int ALWAYS_FALSE_BOOLEAN = -1; - - private Decompiler decompiler = new Decompiler(); - - public IRFactory() { - super(); - } - - public IRFactory(CompilerEnvirons env) { - this(env, env.getErrorReporter()); - } - - public IRFactory(CompilerEnvirons env, ErrorReporter errorReporter) { - super(env, errorReporter); - } - - /** - * Transforms the tree into a lower-level IR suitable for codegen. Optionally generates the - * encoded source. - */ - public ScriptNode transformTree(AstRoot root) { - currentScriptOrFn = root; - this.inUseStrictDirective = root.isInStrictMode(); - int sourceStartOffset = decompiler.getCurrentOffset(); - - if (Token.printTrees) { - System.out.println("IRFactory.transformTree"); - System.out.println(root.debugPrint()); - } - ScriptNode script = (ScriptNode) transform(root); - - int sourceEndOffset = decompiler.getCurrentOffset(); - script.setEncodedSourceBounds(sourceStartOffset, sourceEndOffset); - - if (compilerEnv.isGeneratingSource()) { - script.setEncodedSource(decompiler.getEncodedSource()); - } - - decompiler = null; - return script; - } - - // Might want to convert this to polymorphism - move transform* - // functions into the AstNode subclasses. OTOH that would make - // IR transformation part of the public AST API - desirable? - // Another possibility: create AstTransformer interface and adapter. - public Node transform(AstNode node) { - switch (node.getType()) { - case Token.ARRAYCOMP: - return transformArrayComp((ArrayComprehension) node); - case Token.ARRAYLIT: - return transformArrayLiteral((ArrayLiteral) node); - case Token.BIGINT: - return transformBigInt((BigIntLiteral) node); - case Token.BLOCK: - return transformBlock(node); - case Token.BREAK: - return transformBreak((BreakStatement) node); - case Token.CALL: - return transformFunctionCall((FunctionCall) node); - case Token.CONTINUE: - return transformContinue((ContinueStatement) node); - case Token.DO: - return transformDoLoop((DoLoop) node); - case Token.EMPTY: - case Token.COMMENT: - return node; - case Token.FOR: - if (node instanceof ForInLoop) { - return transformForInLoop((ForInLoop) node); - } - return transformForLoop((ForLoop) node); - case Token.FUNCTION: - return transformFunction((FunctionNode) node); - case Token.GENEXPR: - return transformGenExpr((GeneratorExpression) node); - case Token.GETELEM: - return transformElementGet((ElementGet) node); - case Token.GETPROP: - return transformPropertyGet((PropertyGet) node); - case Token.HOOK: - return transformCondExpr((ConditionalExpression) node); - case Token.IF: - return transformIf((IfStatement) node); - - case Token.TRUE: - case Token.FALSE: - case Token.THIS: - case Token.NULL: - case Token.DEBUGGER: - return transformLiteral(node); - - case Token.NAME: - return transformName((Name) node); - case Token.NUMBER: - return transformNumber((NumberLiteral) node); - case Token.NEW: - return transformNewExpr((NewExpression) node); - case Token.OBJECTLIT: - return transformObjectLiteral((ObjectLiteral) node); - case Token.TEMPLATE_LITERAL: - return transformTemplateLiteral((TemplateLiteral) node); - case Token.TAGGED_TEMPLATE_LITERAL: - return transformTemplateLiteralCall((TaggedTemplateLiteral) node); - case Token.REGEXP: - return transformRegExp((RegExpLiteral) node); - case Token.RETURN: - return transformReturn((ReturnStatement) node); - case Token.SCRIPT: - return transformScript((ScriptNode) node); - case Token.STRING: - return transformString((StringLiteral) node); - case Token.SWITCH: - return transformSwitch((SwitchStatement) node); - case Token.THROW: - return transformThrow((ThrowStatement) node); - case Token.TRY: - return transformTry((TryStatement) node); - case Token.WHILE: - return transformWhileLoop((WhileLoop) node); - case Token.WITH: - return transformWith((WithStatement) node); - case Token.YIELD: - case Token.YIELD_STAR: - return transformYield((Yield) node); - default: - if (node instanceof ExpressionStatement) { - return transformExprStmt((ExpressionStatement) node); - } - if (node instanceof Assignment) { - return transformAssignment((Assignment) node); - } - if (node instanceof UnaryExpression) { - return transformUnary((UnaryExpression) node); - } - if (node instanceof UpdateExpression) { - return transformUpdate((UpdateExpression) node); - } - if (node instanceof XmlMemberGet) { - return transformXmlMemberGet((XmlMemberGet) node); - } - if (node instanceof InfixExpression) { - return transformInfix((InfixExpression) node); - } - if (node instanceof VariableDeclaration) { - return transformVariables((VariableDeclaration) node); - } - if (node instanceof ParenthesizedExpression) { - return transformParenExpr((ParenthesizedExpression) node); - } - if (node instanceof LabeledStatement) { - return transformLabeledStatement((LabeledStatement) node); - } - if (node instanceof LetNode) { - return transformLetNode((LetNode) node); - } - if (node instanceof XmlRef) { - return transformXmlRef((XmlRef) node); - } - if (node instanceof XmlLiteral) { - return transformXmlLiteral((XmlLiteral) node); - } - throw new IllegalArgumentException("Can't transform: " + node); - } - } - - private Node transformArrayComp(ArrayComprehension node) { - // An array comprehension expression such as - // - // [expr for (x in foo) for each ([y, z] in bar) if (cond)] - // - // is rewritten approximately as - // - // new Scope(ARRAYCOMP) { - // new Node(BLOCK) { - // let tmp1 = new Array; - // for (let x in foo) { - // for each (let tmp2 in bar) { - // if (cond) { - // tmp1.push([y, z] = tmp2, expr); - // } - // } - // } - // } - // createName(tmp1) - // } - - int lineno = node.getLineno(); - Scope scopeNode = createScopeNode(Token.ARRAYCOMP, lineno); - String arrayName = currentScriptOrFn.getNextTempName(); - pushScope(scopeNode); - try { - defineSymbol(Token.LET, arrayName, false); - Node block = new Node(Token.BLOCK, lineno); - Node newArray = createCallOrNew(Token.NEW, createName("Array")); - Node init = - new Node( - Token.EXPR_VOID, - createAssignment(Token.ASSIGN, createName(arrayName), newArray), - lineno); - block.addChildToBack(init); - block.addChildToBack(arrayCompTransformHelper(node, arrayName)); - scopeNode.addChildToBack(block); - scopeNode.addChildToBack(createName(arrayName)); - return scopeNode; - } finally { - popScope(); - } - } - - private Node arrayCompTransformHelper(ArrayComprehension node, String arrayName) { - decompiler.addToken(Token.LB); - int lineno = node.getLineno(); - Node expr = transform(node.getResult()); - - List loops = node.getLoops(); - int numLoops = loops.size(); - - // Walk through loops, collecting and defining their iterator symbols. - Node[] iterators = new Node[numLoops]; - Node[] iteratedObjs = new Node[numLoops]; - - for (int i = 0; i < numLoops; i++) { - ArrayComprehensionLoop acl = loops.get(i); - decompiler.addName(" "); - decompiler.addToken(Token.FOR); - if (acl.isForEach()) { - decompiler.addName("each "); - } - decompiler.addToken(Token.LP); - - AstNode iter = acl.getIterator(); - String name = null; - if (iter.getType() == Token.NAME) { - name = iter.getString(); - decompiler.addName(name); - } else { - // destructuring assignment - decompile(iter); - name = currentScriptOrFn.getNextTempName(); - defineSymbol(Token.LP, name, false); - expr = - createBinary( - Token.COMMA, - createAssignment(Token.ASSIGN, iter, createName(name)), - expr); - } - Node init = createName(name); - // Define as a let since we want the scope of the variable to - // be restricted to the array comprehension - defineSymbol(Token.LET, name, false); - iterators[i] = init; - - if (acl.isForOf()) { - decompiler.addName("of "); - } else { - decompiler.addToken(Token.IN); - } - iteratedObjs[i] = transform(acl.getIteratedObject()); - decompiler.addToken(Token.RP); - } - - // generate code for tmpArray.push(body) - Node call = - createCallOrNew( - Token.CALL, createPropertyGet(createName(arrayName), null, "push", 0)); - - Node body = new Node(Token.EXPR_VOID, call, lineno); - - if (node.getFilter() != null) { - decompiler.addName(" "); - decompiler.addToken(Token.IF); - decompiler.addToken(Token.LP); - body = createIf(transform(node.getFilter()), body, null, lineno); - decompiler.addToken(Token.RP); - } - - // Now walk loops in reverse to build up the body statement. - int pushed = 0; - try { - for (int i = numLoops - 1; i >= 0; i--) { - ArrayComprehensionLoop acl = loops.get(i); - Scope loop = - createLoopNode( - null, // no label - acl.getLineno()); - pushScope(loop); - pushed++; - body = - createForIn( - Token.LET, - loop, - iterators[i], - iteratedObjs[i], - body, - acl.isForEach(), - acl.isForOf()); - } - } finally { - for (int i = 0; i < pushed; i++) { - popScope(); - } - } - - decompiler.addToken(Token.RB); - - // Now that we've accumulated any destructuring forms, - // add expr to the call node; it's pushed on each iteration. - call.addChildToBack(expr); - return body; - } - - private Node transformArrayLiteral(ArrayLiteral node) { - if (node.isDestructuring()) { - return node; - } - decompiler.addToken(Token.LB); - List elems = node.getElements(); - Node array = new Node(Token.ARRAYLIT); - List skipIndexes = null; - for (int i = 0; i < elems.size(); ++i) { - AstNode elem = elems.get(i); - if (elem.getType() != Token.EMPTY) { - array.addChildToBack(transform(elem)); - } else { - if (skipIndexes == null) { - skipIndexes = new ArrayList(); - } - skipIndexes.add(Integer.valueOf(i)); - } - if (i < elems.size() - 1) decompiler.addToken(Token.COMMA); - } - decompiler.addToken(Token.RB); - array.putIntProp(Node.DESTRUCTURING_ARRAY_LENGTH, node.getDestructuringLength()); - if (skipIndexes != null) { - int[] skips = new int[skipIndexes.size()]; - for (int i = 0; i < skipIndexes.size(); i++) skips[i] = skipIndexes.get(i).intValue(); - array.putProp(Node.SKIP_INDEXES_PROP, skips); - } - return array; - } - - private Node transformAssignment(Assignment node) { - AstNode right = node.getRight(); - AstNode left = removeParens(node.getLeft()); - left = transformAssignmentLeft(node, left, right); - - Node target = null; - if (isDestructuring(left)) { - decompile(left); - target = left; - } else { - target = transform(left); - } - - decompiler.addToken(node.getType()); - return createAssignment(node.getType(), target, transform(right)); - } - - private AstNode transformAssignmentLeft(Assignment node, AstNode left, AstNode right) { - if (right.getType() == Token.NULL - && node.getType() == Token.ASSIGN - && left instanceof Name - && right instanceof KeywordLiteral) { - - String identifier = ((Name) left).getIdentifier(); - for (AstNode p = node.getParent(); p != null; p = p.getParent()) { - if (p instanceof FunctionNode) { - Name functionName = ((FunctionNode) p).getFunctionName(); - if (functionName != null && functionName.getIdentifier().equals(identifier)) { - PropertyGet propertyGet = new PropertyGet(); - KeywordLiteral thisKeyword = new KeywordLiteral(); - thisKeyword.setType(Token.THIS); - propertyGet.setLeft(thisKeyword); - propertyGet.setRight(left); - node.setLeft(propertyGet); - return propertyGet; - } - } - } - } - return left; - } - - private Node transformBigInt(BigIntLiteral node) { - decompiler.addBigInt(node.getBigInt()); - return node; - } - - private Node transformBlock(AstNode node) { - if (node instanceof Scope) { - pushScope((Scope) node); - } - try { - List kids = new ArrayList(); - for (Node kid : node) { - kids.add(transform((AstNode) kid)); - } - node.removeChildren(); - for (Node kid : kids) { - node.addChildToBack(kid); - } - return node; - } finally { - if (node instanceof Scope) { - popScope(); - } - } - } - - private Node transformBreak(BreakStatement node) { - decompiler.addToken(Token.BREAK); - if (node.getBreakLabel() != null) { - decompiler.addName(node.getBreakLabel().getIdentifier()); - } - decompiler.addEOL(Token.SEMI); - return node; - } - - private Node transformCondExpr(ConditionalExpression node) { - Node test = transform(node.getTestExpression()); - decompiler.addToken(Token.HOOK); - Node ifTrue = transform(node.getTrueExpression()); - decompiler.addToken(Token.COLON); - Node ifFalse = transform(node.getFalseExpression()); - return createCondExpr(test, ifTrue, ifFalse); - } - - private Node transformContinue(ContinueStatement node) { - decompiler.addToken(Token.CONTINUE); - if (node.getLabel() != null) { - decompiler.addName(node.getLabel().getIdentifier()); - } - decompiler.addEOL(Token.SEMI); - return node; - } - - private Node transformDoLoop(DoLoop loop) { - loop.setType(Token.LOOP); - pushScope(loop); - try { - decompiler.addToken(Token.DO); - decompiler.addEOL(Token.LC); - Node body = transform(loop.getBody()); - decompiler.addToken(Token.RC); - decompiler.addToken(Token.WHILE); - decompiler.addToken(Token.LP); - Node cond = transform(loop.getCondition()); - decompiler.addToken(Token.RP); - decompiler.addEOL(Token.SEMI); - return createLoop(loop, LOOP_DO_WHILE, body, cond, null, null); - } finally { - popScope(); - } - } - - private Node transformElementGet(ElementGet node) { - // OPT: could optimize to createPropertyGet - // iff elem is string that can not be number - Node target = transform(node.getTarget()); - decompiler.addToken(Token.LB); - Node element = transform(node.getElement()); - decompiler.addToken(Token.RB); - return new Node(Token.GETELEM, target, element); - } - - private Node transformExprStmt(ExpressionStatement node) { - Node expr = transform(node.getExpression()); - decompiler.addEOL(Token.SEMI); - return new Node(node.getType(), expr, node.getLineno()); - } - - private Node transformForInLoop(ForInLoop loop) { - decompiler.addToken(Token.FOR); - if (loop.isForEach()) decompiler.addName("each "); - decompiler.addToken(Token.LP); - - loop.setType(Token.LOOP); - pushScope(loop); - try { - int declType = -1; - AstNode iter = loop.getIterator(); - if (iter instanceof VariableDeclaration) { - declType = ((VariableDeclaration) iter).getType(); - } - Node lhs = transform(iter); - if (loop.isForOf()) { - decompiler.addName("of "); - } else { - decompiler.addToken(Token.IN); - } - Node obj = transform(loop.getIteratedObject()); - decompiler.addToken(Token.RP); - decompiler.addEOL(Token.LC); - Node body = transform(loop.getBody()); - decompiler.addEOL(Token.RC); - return createForIn(declType, loop, lhs, obj, body, loop.isForEach(), loop.isForOf()); - } finally { - popScope(); - } - } - - private Node transformForLoop(ForLoop loop) { - decompiler.addToken(Token.FOR); - decompiler.addToken(Token.LP); - loop.setType(Token.LOOP); - // XXX: Can't use pushScope/popScope here since 'createFor' may split - // the scope - Scope savedScope = currentScope; - currentScope = loop; - try { - Node init = transform(loop.getInitializer()); - decompiler.addToken(Token.SEMI); - Node test = transform(loop.getCondition()); - decompiler.addToken(Token.SEMI); - Node incr = transform(loop.getIncrement()); - decompiler.addToken(Token.RP); - decompiler.addEOL(Token.LC); - Node body = transform(loop.getBody()); - decompiler.addEOL(Token.RC); - return createFor(loop, init, test, incr, body); - } finally { - currentScope = savedScope; - } - } - - private Node transformFunction(FunctionNode fn) { - int functionType = fn.getFunctionType(); - int start = decompiler.markFunctionStart(functionType); - Node mexpr = decompileFunctionHeader(fn); - int index = currentScriptOrFn.addFunction(fn); - - PerFunctionVariables savedVars = new PerFunctionVariables(fn); - try { - // If we start needing to record much more codegen metadata during - // function parsing, we should lump it all into a helper class. - Node destructuring = (Node) fn.getProp(Node.DESTRUCTURING_PARAMS); - fn.removeProp(Node.DESTRUCTURING_PARAMS); - - int lineno = fn.getBody().getLineno(); - ++nestingOfFunction; // only for body, not params - Node body = transform(fn.getBody()); - - if (!fn.isExpressionClosure()) { - decompiler.addToken(Token.RC); - } - fn.setEncodedSourceBounds(start, decompiler.markFunctionEnd(start)); - - if (functionType != FunctionNode.FUNCTION_EXPRESSION && !fn.isExpressionClosure()) { - // Add EOL only if function is not part of expression - // since it gets SEMI + EOL from Statement in that case - decompiler.addToken(Token.EOL); - } - - if (destructuring != null) { - body.addChildToFront(new Node(Token.EXPR_VOID, destructuring, lineno)); - } - - int syntheticType = fn.getFunctionType(); - Node pn = initFunction(fn, index, body, syntheticType); - if (mexpr != null) { - pn = createAssignment(Token.ASSIGN, mexpr, pn); - if (syntheticType != FunctionNode.FUNCTION_EXPRESSION) { - pn = createExprStatementNoReturn(pn, fn.getLineno()); - } - } - return pn; - - } finally { - --nestingOfFunction; - savedVars.restore(); - } - } - - private Node transformFunctionCall(FunctionCall node) { - Node call = createCallOrNew(Token.CALL, transform(node.getTarget())); - call.setLineno(node.getLineno()); - decompiler.addToken(Token.LP); - List args = node.getArguments(); - for (int i = 0; i < args.size(); i++) { - AstNode arg = args.get(i); - call.addChildToBack(transform(arg)); - if (i < args.size() - 1) { - decompiler.addToken(Token.COMMA); - } - } - decompiler.addToken(Token.RP); - return call; - } - - private Node transformGenExpr(GeneratorExpression node) { - Node pn; - - FunctionNode fn = new FunctionNode(); - fn.setSourceName(currentScriptOrFn.getNextTempName()); - fn.setIsGenerator(); - fn.setFunctionType(FunctionNode.FUNCTION_EXPRESSION); - fn.setRequiresActivation(); - - int functionType = fn.getFunctionType(); - int start = decompiler.markFunctionStart(functionType); - Node mexpr = decompileFunctionHeader(fn); - int index = currentScriptOrFn.addFunction(fn); - - PerFunctionVariables savedVars = new PerFunctionVariables(fn); - try { - // If we start needing to record much more codegen metadata during - // function parsing, we should lump it all into a helper class. - Node destructuring = (Node) fn.getProp(Node.DESTRUCTURING_PARAMS); - fn.removeProp(Node.DESTRUCTURING_PARAMS); - - int lineno = node.lineno; - ++nestingOfFunction; // only for body, not params - Node body = genExprTransformHelper(node); - - if (!fn.isExpressionClosure()) { - decompiler.addToken(Token.RC); - } - fn.setEncodedSourceBounds(start, decompiler.markFunctionEnd(start)); - - if (functionType != FunctionNode.FUNCTION_EXPRESSION && !fn.isExpressionClosure()) { - // Add EOL only if function is not part of expression - // since it gets SEMI + EOL from Statement in that case - decompiler.addToken(Token.EOL); - } - - if (destructuring != null) { - body.addChildToFront(new Node(Token.EXPR_VOID, destructuring, lineno)); - } - - int syntheticType = fn.getFunctionType(); - pn = initFunction(fn, index, body, syntheticType); - if (mexpr != null) { - pn = createAssignment(Token.ASSIGN, mexpr, pn); - if (syntheticType != FunctionNode.FUNCTION_EXPRESSION) { - pn = createExprStatementNoReturn(pn, fn.getLineno()); - } - } - } finally { - --nestingOfFunction; - savedVars.restore(); - } - - Node call = createCallOrNew(Token.CALL, pn); - call.setLineno(node.getLineno()); - decompiler.addToken(Token.LP); - decompiler.addToken(Token.RP); - return call; - } - - private Node genExprTransformHelper(GeneratorExpression node) { - decompiler.addToken(Token.LP); - int lineno = node.getLineno(); - Node expr = transform(node.getResult()); - - List loops = node.getLoops(); - int numLoops = loops.size(); - - // Walk through loops, collecting and defining their iterator symbols. - Node[] iterators = new Node[numLoops]; - Node[] iteratedObjs = new Node[numLoops]; - - for (int i = 0; i < numLoops; i++) { - GeneratorExpressionLoop acl = loops.get(i); - decompiler.addName(" "); - decompiler.addToken(Token.FOR); - decompiler.addToken(Token.LP); - - AstNode iter = acl.getIterator(); - String name = null; - if (iter.getType() == Token.NAME) { - name = iter.getString(); - decompiler.addName(name); - } else { - // destructuring assignment - decompile(iter); - name = currentScriptOrFn.getNextTempName(); - defineSymbol(Token.LP, name, false); - expr = - createBinary( - Token.COMMA, - createAssignment(Token.ASSIGN, iter, createName(name)), - expr); - } - Node init = createName(name); - // Define as a let since we want the scope of the variable to - // be restricted to the array comprehension - defineSymbol(Token.LET, name, false); - iterators[i] = init; - - if (acl.isForOf()) { - decompiler.addName("of "); - } else { - decompiler.addToken(Token.IN); - } - iteratedObjs[i] = transform(acl.getIteratedObject()); - decompiler.addToken(Token.RP); - } - - // generate code for tmpArray.push(body) - Node yield = new Node(Token.YIELD, expr, node.getLineno()); - - Node body = new Node(Token.EXPR_VOID, yield, lineno); - - if (node.getFilter() != null) { - decompiler.addName(" "); - decompiler.addToken(Token.IF); - decompiler.addToken(Token.LP); - body = createIf(transform(node.getFilter()), body, null, lineno); - decompiler.addToken(Token.RP); - } - - // Now walk loops in reverse to build up the body statement. - int pushed = 0; - try { - for (int i = numLoops - 1; i >= 0; i--) { - GeneratorExpressionLoop acl = loops.get(i); - Scope loop = - createLoopNode( - null, // no label - acl.getLineno()); - pushScope(loop); - pushed++; - body = - createForIn( - Token.LET, - loop, - iterators[i], - iteratedObjs[i], - body, - acl.isForEach(), - acl.isForOf()); - } - } finally { - for (int i = 0; i < pushed; i++) { - popScope(); - } - } - - decompiler.addToken(Token.RP); - - return body; - } - - private Node transformIf(IfStatement n) { - decompiler.addToken(Token.IF); - decompiler.addToken(Token.LP); - Node cond = transform(n.getCondition()); - decompiler.addToken(Token.RP); - decompiler.addEOL(Token.LC); - Node ifTrue = transform(n.getThenPart()); - Node ifFalse = null; - if (n.getElsePart() != null) { - decompiler.addToken(Token.RC); - decompiler.addToken(Token.ELSE); - decompiler.addEOL(Token.LC); - ifFalse = transform(n.getElsePart()); - } - decompiler.addEOL(Token.RC); - return createIf(cond, ifTrue, ifFalse, n.getLineno()); - } - - private Node transformInfix(InfixExpression node) { - Node left = transform(node.getLeft()); - decompiler.addToken(node.getType()); - Node right = transform(node.getRight()); - if (node instanceof XmlDotQuery) { - decompiler.addToken(Token.RP); - } - return createBinary(node.getType(), left, right); - } - - private Node transformLabeledStatement(LabeledStatement ls) { - Label label = ls.getFirstLabel(); - List

The IRFactory tries to express as much as possible in the tree; the responsibilities - * remaining for Codegen are to add the Java handlers: (Either (but not both) of TARGET and - * FINALLY might not be defined) - * - *

- a catch handler for javascript exceptions that unwraps the exception onto the stack and - * GOTOes to the catch target - * - *

- a finally handler - * - *

... and a goto to GOTO around these handlers. - */ - private Node createTryCatchFinally( - Node tryBlock, Node catchBlocks, Node finallyBlock, int lineno) { - boolean hasFinally = - (finallyBlock != null) - && (finallyBlock.getType() != Token.BLOCK || finallyBlock.hasChildren()); - - // short circuit - if (tryBlock.getType() == Token.BLOCK && !tryBlock.hasChildren() && !hasFinally) { - return tryBlock; - } - - boolean hasCatch = catchBlocks.hasChildren(); - - // short circuit - if (!hasFinally && !hasCatch) { - // bc finally might be an empty block... - return tryBlock; - } - - Node handlerBlock = new Node(Token.LOCAL_BLOCK); - Jump pn = new Jump(Token.TRY, tryBlock, lineno); - pn.putProp(Node.LOCAL_BLOCK_PROP, handlerBlock); - - if (hasCatch) { - // jump around catch code - Node endCatch = Node.newTarget(); - pn.addChildToBack(makeJump(Token.GOTO, endCatch)); - - // make a TARGET for the catch that the tcf node knows about - Node catchTarget = Node.newTarget(); - pn.target = catchTarget; - // mark it - pn.addChildToBack(catchTarget); - - // - // Given - // - // try { - // tryBlock; - // } catch (e if condition1) { - // something1; - // ... - // - // } catch (e if conditionN) { - // somethingN; - // } catch (e) { - // somethingDefault; - // } - // - // rewrite as - // - // try { - // tryBlock; - // goto after_catch: - // } catch (x) { - // with (newCatchScope(e, x)) { - // if (condition1) { - // something1; - // goto after_catch; - // } - // } - // ... - // with (newCatchScope(e, x)) { - // if (conditionN) { - // somethingN; - // goto after_catch; - // } - // } - // with (newCatchScope(e, x)) { - // somethingDefault; - // goto after_catch; - // } - // } - // after_catch: - // - // If there is no default catch, then the last with block - // arround "somethingDefault;" is replaced by "rethrow;" - - // It is assumed that catch handler generation will store - // exeception object in handlerBlock register - - // Block with local for exception scope objects - Node catchScopeBlock = new Node(Token.LOCAL_BLOCK); - - // expects catchblocks children to be (cond block) pairs. - Node cb = catchBlocks.getFirstChild(); - boolean hasDefault = false; - int scopeIndex = 0; - while (cb != null) { - int catchLineNo = cb.getLineno(); - - Node name = cb.getFirstChild(); - Node cond = name.getNext(); - Node catchStatement = cond.getNext(); - cb.removeChild(name); - cb.removeChild(cond); - cb.removeChild(catchStatement); - - // Add goto to the catch statement to jump out of catch - // but prefix it with LEAVEWITH since try..catch produces - // "with"code in order to limit the scope of the exception - // object. - catchStatement.addChildToBack(new Node(Token.LEAVEWITH)); - catchStatement.addChildToBack(makeJump(Token.GOTO, endCatch)); - - // Create condition "if" when present - Node condStmt; - if (cond.getType() == Token.EMPTY) { - condStmt = catchStatement; - hasDefault = true; - } else { - condStmt = createIf(cond, catchStatement, null, catchLineNo); - } - - // Generate code to create the scope object and store - // it in catchScopeBlock register - Node catchScope = new Node(Token.CATCH_SCOPE, name, createUseLocal(handlerBlock)); - catchScope.putProp(Node.LOCAL_BLOCK_PROP, catchScopeBlock); - catchScope.putIntProp(Node.CATCH_SCOPE_PROP, scopeIndex); - catchScopeBlock.addChildToBack(catchScope); - - // Add with statement based on catch scope object - catchScopeBlock.addChildToBack( - createWith(createUseLocal(catchScopeBlock), condStmt, catchLineNo)); - - // move to next cb - cb = cb.getNext(); - ++scopeIndex; - } - pn.addChildToBack(catchScopeBlock); - if (!hasDefault) { - // Generate code to rethrow if no catch clause was executed - Node rethrow = new Node(Token.RETHROW); - rethrow.putProp(Node.LOCAL_BLOCK_PROP, handlerBlock); - pn.addChildToBack(rethrow); - } - - pn.addChildToBack(endCatch); - } - - if (hasFinally) { - Node finallyTarget = Node.newTarget(); - pn.setFinally(finallyTarget); - - // add jsr finally to the try block - pn.addChildToBack(makeJump(Token.JSR, finallyTarget)); - - // jump around finally code - Node finallyEnd = Node.newTarget(); - pn.addChildToBack(makeJump(Token.GOTO, finallyEnd)); - - pn.addChildToBack(finallyTarget); - Node fBlock = new Node(Token.FINALLY, finallyBlock); - fBlock.putProp(Node.LOCAL_BLOCK_PROP, handlerBlock); - pn.addChildToBack(fBlock); - - pn.addChildToBack(finallyEnd); - } - handlerBlock.addChildToBack(pn); - return handlerBlock; - } - - private Node createWith(Node obj, Node body, int lineno) { - setRequiresActivation(); - Node result = new Node(Token.BLOCK, lineno); - result.addChildToBack(new Node(Token.ENTERWITH, obj)); - Node bodyNode = new Node(Token.WITH, body, lineno); - result.addChildrenToBack(bodyNode); - result.addChildToBack(new Node(Token.LEAVEWITH)); - return result; - } - - private static Node createIf(Node cond, Node ifTrue, Node ifFalse, int lineno) { - int condStatus = isAlwaysDefinedBoolean(cond); - if (condStatus == ALWAYS_TRUE_BOOLEAN) { - return ifTrue; - } else if (condStatus == ALWAYS_FALSE_BOOLEAN) { - if (ifFalse != null) { - return ifFalse; - } - // Replace if (false) xxx by empty block - return new Node(Token.BLOCK, lineno); - } - - Node result = new Node(Token.BLOCK, lineno); - Node ifNotTarget = Node.newTarget(); - Jump IFNE = new Jump(Token.IFNE, cond); - IFNE.target = ifNotTarget; - - result.addChildToBack(IFNE); - result.addChildrenToBack(ifTrue); - - if (ifFalse != null) { - Node endTarget = Node.newTarget(); - result.addChildToBack(makeJump(Token.GOTO, endTarget)); - result.addChildToBack(ifNotTarget); - result.addChildrenToBack(ifFalse); - result.addChildToBack(endTarget); - } else { - result.addChildToBack(ifNotTarget); - } - - return result; - } - - private static Node createCondExpr(Node cond, Node ifTrue, Node ifFalse) { - int condStatus = isAlwaysDefinedBoolean(cond); - if (condStatus == ALWAYS_TRUE_BOOLEAN) { - return ifTrue; - } else if (condStatus == ALWAYS_FALSE_BOOLEAN) { - return ifFalse; - } - return new Node(Token.HOOK, cond, ifTrue, ifFalse); - } - - private static Node createUnary(int nodeType, Node child) { - int childType = child.getType(); - switch (nodeType) { - case Token.DELPROP: - { - Node n; - if (childType == Token.NAME) { - // Transform Delete(Name "a") - // to Delete(Bind("a"), String("a")) - child.setType(Token.BINDNAME); - Node left = child; - Node right = Node.newString(child.getString()); - n = new Node(nodeType, left, right); - } else if (childType == Token.GETPROP || childType == Token.GETELEM) { - Node left = child.getFirstChild(); - Node right = child.getLastChild(); - child.removeChild(left); - child.removeChild(right); - n = new Node(nodeType, left, right); - } else if (childType == Token.GET_REF) { - Node ref = child.getFirstChild(); - child.removeChild(ref); - n = new Node(Token.DEL_REF, ref); - } else { - // Always evaluate delete operand, see ES5 11.4.1 & bug #726121 - n = new Node(nodeType, new Node(Token.TRUE), child); - } - return n; - } - case Token.TYPEOF: - if (childType == Token.NAME) { - child.setType(Token.TYPEOFNAME); - return child; - } - break; - case Token.BITNOT: - if (childType == Token.NUMBER) { - int value = ScriptRuntime.toInt32(child.getDouble()); - child.setDouble(~value); - return child; - } - break; - case Token.NEG: - if (childType == Token.NUMBER) { - child.setDouble(-child.getDouble()); - return child; - } - break; - case Token.NOT: - { - int status = isAlwaysDefinedBoolean(child); - if (status != 0) { - int type; - if (status == ALWAYS_TRUE_BOOLEAN) { - type = Token.FALSE; - } else { - type = Token.TRUE; - } - if (childType == Token.TRUE || childType == Token.FALSE) { - child.setType(type); - return child; - } - return new Node(type); - } - break; - } - } - return new Node(nodeType, child); - } - - private Node createCallOrNew(int nodeType, Node child) { - int type = Node.NON_SPECIALCALL; - if (child.getType() == Token.NAME) { - String name = child.getString(); - if (name.equals("eval")) { - type = Node.SPECIALCALL_EVAL; - } else if (name.equals("With")) { - type = Node.SPECIALCALL_WITH; - } - } else if (child.getType() == Token.GETPROP) { - String name = child.getLastChild().getString(); - if (name.equals("eval")) { - type = Node.SPECIALCALL_EVAL; - } - } - Node node = new Node(nodeType, child); - if (type != Node.NON_SPECIALCALL) { - // Calls to these functions require activation objects. - setRequiresActivation(); - node.putIntProp(Node.SPECIALCALL_PROP, type); - } - return node; - } - - private static Node createIncDec(int nodeType, boolean post, Node child) { - child = makeReference(child); - int childType = child.getType(); - - switch (childType) { - case Token.NAME: - case Token.GETPROP: - case Token.GETELEM: - case Token.GET_REF: - { - Node n = new Node(nodeType, child); - int incrDecrMask = 0; - if (nodeType == Token.DEC) { - incrDecrMask |= Node.DECR_FLAG; - } - if (post) { - incrDecrMask |= Node.POST_FLAG; - } - n.putIntProp(Node.INCRDECR_PROP, incrDecrMask); - return n; - } - } - throw Kit.codeBug(); - } - - private Node createPropertyGet( - Node target, String namespace, String name, int memberTypeFlags) { - if (namespace == null && memberTypeFlags == 0) { - if (target == null) { - return createName(name); - } - checkActivationName(name, Token.GETPROP); - if (ScriptRuntime.isSpecialProperty(name)) { - Node ref = new Node(Token.REF_SPECIAL, target); - ref.putProp(Node.NAME_PROP, name); - return new Node(Token.GET_REF, ref); - } - return new Node(Token.GETPROP, target, Node.newString(name)); - } - Node elem = Node.newString(name); - memberTypeFlags |= Node.PROPERTY_FLAG; - return createMemberRefGet(target, namespace, elem, memberTypeFlags); - } - - /** - * @param target the node before the LB - * @param namespace optional namespace - * @param elem the node in the brackets - * @param memberTypeFlags E4X flags - */ - private Node createElementGet(Node target, String namespace, Node elem, int memberTypeFlags) { - // OPT: could optimize to createPropertyGet - // iff elem is string that can not be number - if (namespace == null && memberTypeFlags == 0) { - // stand-alone [aaa] as primary expression is array literal - // declaration and should not come here! - if (target == null) throw Kit.codeBug(); - return new Node(Token.GETELEM, target, elem); - } - return createMemberRefGet(target, namespace, elem, memberTypeFlags); - } - - private Node createMemberRefGet(Node target, String namespace, Node elem, int memberTypeFlags) { - Node nsNode = null; - if (namespace != null) { - // See 11.1.2 in ECMA 357 - if (namespace.equals("*")) { - nsNode = new Node(Token.NULL); - } else { - nsNode = createName(namespace); - } - } - Node ref; - if (target == null) { - if (namespace == null) { - ref = new Node(Token.REF_NAME, elem); - } else { - ref = new Node(Token.REF_NS_NAME, nsNode, elem); - } - } else { - if (namespace == null) { - ref = new Node(Token.REF_MEMBER, target, elem); - } else { - ref = new Node(Token.REF_NS_MEMBER, target, nsNode, elem); - } - } - if (memberTypeFlags != 0) { - ref.putIntProp(Node.MEMBER_TYPE_PROP, memberTypeFlags); - } - return new Node(Token.GET_REF, ref); - } - - private static Node createBinary(int nodeType, Node left, Node right) { - switch (nodeType) { - case Token.ADD: - // numerical addition and string concatenation - if (left.type == Token.STRING) { - String s2; - if (right.type == Token.STRING) { - s2 = right.getString(); - } else if (right.type == Token.NUMBER) { - s2 = ScriptRuntime.numberToString(right.getDouble(), 10); - } else { - break; - } - String s1 = left.getString(); - left.setString(s1.concat(s2)); - return left; - } else if (left.type == Token.NUMBER) { - if (right.type == Token.NUMBER) { - left.setDouble(left.getDouble() + right.getDouble()); - return left; - } else if (right.type == Token.STRING) { - String s1, s2; - s1 = ScriptRuntime.numberToString(left.getDouble(), 10); - s2 = right.getString(); - right.setString(s1.concat(s2)); - return right; - } - } - // can't do anything if we don't know both types - since - // 0 + object is supposed to call toString on the object and do - // string concantenation rather than addition - break; - - case Token.SUB: - // numerical subtraction - if (left.type == Token.NUMBER) { - double ld = left.getDouble(); - if (right.type == Token.NUMBER) { - // both numbers - left.setDouble(ld - right.getDouble()); - return left; - } else if (ld == 0.0) { - // first 0: 0-x -> -x - return new Node(Token.NEG, right); - } - } else if (right.type == Token.NUMBER) { - if (right.getDouble() == 0.0) { - // second 0: x - 0 -> +x - // can not make simply x because x - 0 must be number - return new Node(Token.POS, left); - } - } - break; - - case Token.MUL: - // numerical multiplication - if (left.type == Token.NUMBER) { - double ld = left.getDouble(); - if (right.type == Token.NUMBER) { - // both numbers - left.setDouble(ld * right.getDouble()); - return left; - } else if (ld == 1.0) { - // first 1: 1 * x -> +x - return new Node(Token.POS, right); - } - } else if (right.type == Token.NUMBER) { - if (right.getDouble() == 1.0) { - // second 1: x * 1 -> +x - // can not make simply x because x - 0 must be number - return new Node(Token.POS, left); - } - } - // can't do x*0: Infinity * 0 gives NaN, not 0 - break; - - case Token.DIV: - // number division - if (right.type == Token.NUMBER) { - double rd = right.getDouble(); - if (left.type == Token.NUMBER) { - // both constants -- just divide, trust Java to handle x/0 - left.setDouble(left.getDouble() / rd); - return left; - } else if (rd == 1.0) { - // second 1: x/1 -> +x - // not simply x to force number convertion - return new Node(Token.POS, left); - } - } - break; - - case Token.AND: - { - // Since x && y gives x, not false, when Boolean(x) is false, - // and y, not Boolean(y), when Boolean(x) is true, x && y - // can only be simplified if x is defined. See bug 309957. - - int leftStatus = isAlwaysDefinedBoolean(left); - if (leftStatus == ALWAYS_FALSE_BOOLEAN) { - // if the first one is false, just return it - return left; - } else if (leftStatus == ALWAYS_TRUE_BOOLEAN) { - // if first is true, set to second - return right; - } - break; - } - - case Token.OR: - { - // Since x || y gives x, not true, when Boolean(x) is true, - // and y, not Boolean(y), when Boolean(x) is false, x || y - // can only be simplified if x is defined. See bug 309957. - - int leftStatus = isAlwaysDefinedBoolean(left); - if (leftStatus == ALWAYS_TRUE_BOOLEAN) { - // if the first one is true, just return it - return left; - } else if (leftStatus == ALWAYS_FALSE_BOOLEAN) { - // if first is false, set to second - return right; - } - break; - } - } - - return new Node(nodeType, left, right); - } - - private Node createAssignment(int assignType, Node left, Node right) { - Node ref = makeReference(left); - if (ref == null) { - if (left.getType() == Token.ARRAYLIT || left.getType() == Token.OBJECTLIT) { - if (assignType != Token.ASSIGN) { - reportError("msg.bad.destruct.op"); - return right; - } - return createDestructuringAssignment(-1, left, right); - } - reportError("msg.bad.assign.left"); - return right; - } - left = ref; - - int assignOp; - switch (assignType) { - case Token.ASSIGN: - return simpleAssignment(left, right); - case Token.ASSIGN_BITOR: - assignOp = Token.BITOR; - break; - case Token.ASSIGN_BITXOR: - assignOp = Token.BITXOR; - break; - case Token.ASSIGN_BITAND: - assignOp = Token.BITAND; - break; - case Token.ASSIGN_LSH: - assignOp = Token.LSH; - break; - case Token.ASSIGN_RSH: - assignOp = Token.RSH; - break; - case Token.ASSIGN_URSH: - assignOp = Token.URSH; - break; - case Token.ASSIGN_ADD: - assignOp = Token.ADD; - break; - case Token.ASSIGN_SUB: - assignOp = Token.SUB; - break; - case Token.ASSIGN_MUL: - assignOp = Token.MUL; - break; - case Token.ASSIGN_DIV: - assignOp = Token.DIV; - break; - case Token.ASSIGN_MOD: - assignOp = Token.MOD; - break; - case Token.ASSIGN_EXP: - assignOp = Token.EXP; - break; - default: - throw Kit.codeBug(); - } - - int nodeType = left.getType(); - switch (nodeType) { - case Token.NAME: - { - Node op = new Node(assignOp, left, right); - Node lvalueLeft = Node.newString(Token.BINDNAME, left.getString()); - return new Node(Token.SETNAME, lvalueLeft, op); - } - case Token.GETPROP: - case Token.GETELEM: - { - Node obj = left.getFirstChild(); - Node id = left.getLastChild(); - - int type = nodeType == Token.GETPROP ? Token.SETPROP_OP : Token.SETELEM_OP; - - Node opLeft = new Node(Token.USE_STACK); - Node op = new Node(assignOp, opLeft, right); - return new Node(type, obj, id, op); - } - case Token.GET_REF: - { - ref = left.getFirstChild(); - checkMutableReference(ref); - Node opLeft = new Node(Token.USE_STACK); - Node op = new Node(assignOp, opLeft, right); - return new Node(Token.SET_REF_OP, ref, op); - } - } - - throw Kit.codeBug(); - } - - private static Node createUseLocal(Node localBlock) { - if (Token.LOCAL_BLOCK != localBlock.getType()) throw Kit.codeBug(); - Node result = new Node(Token.LOCAL_LOAD); - result.putProp(Node.LOCAL_BLOCK_PROP, localBlock); - return result; - } - - private static Jump makeJump(int type, Node target) { - Jump n = new Jump(type); - n.target = target; - return n; - } - - private static Node makeReference(Node node) { - int type = node.getType(); - switch (type) { - case Token.NAME: - case Token.GETPROP: - case Token.GETELEM: - case Token.GET_REF: - return node; - case Token.CALL: - node.setType(Token.REF_CALL); - return new Node(Token.GET_REF, node); - } - // Signal caller to report error - return null; - } - - // Check if Node always mean true or false in boolean context - private static int isAlwaysDefinedBoolean(Node node) { - switch (node.getType()) { - case Token.FALSE: - case Token.NULL: - return ALWAYS_FALSE_BOOLEAN; - case Token.TRUE: - return ALWAYS_TRUE_BOOLEAN; - case Token.NUMBER: - { - double num = node.getDouble(); - if (!Double.isNaN(num) && num != 0.0) { - return ALWAYS_TRUE_BOOLEAN; - } - return ALWAYS_FALSE_BOOLEAN; - } - } - return 0; - } - - // Check if node is the target of a destructuring bind. - boolean isDestructuring(Node n) { - return n instanceof DestructuringForm && ((DestructuringForm) n).isDestructuring(); - } - - Node decompileFunctionHeader(FunctionNode fn) { - Node mexpr = null; - if (fn.getFunctionName() != null) { - decompiler.addName(fn.getName()); - } else if (fn.getMemberExprNode() != null) { - mexpr = transform(fn.getMemberExprNode()); - } - boolean isArrow = fn.getFunctionType() == FunctionNode.ARROW_FUNCTION; - boolean noParen = isArrow && fn.getLp() == -1; - if (!noParen) { - decompiler.addToken(Token.LP); - } - List params = fn.getParams(); - for (int i = 0; i < params.size(); i++) { - decompile(params.get(i)); - if (i < params.size() - 1) { - decompiler.addToken(Token.COMMA); - } - } - if (!noParen) { - decompiler.addToken(Token.RP); - } - if (isArrow) { - decompiler.addToken(Token.ARROW); - } - if (!fn.isExpressionClosure()) { - decompiler.addEOL(Token.LC); - } - return mexpr; - } - - void decompile(AstNode node) { - switch (node.getType()) { - case Token.ARRAYLIT: - decompileArrayLiteral((ArrayLiteral) node); - break; - case Token.OBJECTLIT: - decompileObjectLiteral((ObjectLiteral) node); - break; - case Token.STRING: - decompiler.addString(((StringLiteral) node).getValue()); - break; - case Token.NAME: - decompiler.addName(((Name) node).getIdentifier()); - break; - case Token.NUMBER: - decompiler.addNumber(((NumberLiteral) node).getNumber()); - break; - case Token.BIGINT: - decompiler.addBigInt(((BigIntLiteral) node).getBigInt()); - break; - case Token.GETPROP: - decompilePropertyGet((PropertyGet) node); - break; - case Token.EMPTY: - break; - case Token.GETELEM: - decompileElementGet((ElementGet) node); - break; - case Token.THIS: - decompiler.addToken(node.getType()); - break; - default: - Kit.codeBug("unexpected token: " + Token.typeToName(node.getType())); - } - } - - // used for destructuring forms, since we don't transform() them - void decompileArrayLiteral(ArrayLiteral node) { - decompiler.addToken(Token.LB); - List elems = node.getElements(); - int size = elems.size(); - for (int i = 0; i < size; i++) { - AstNode elem = elems.get(i); - decompile(elem); - if (i < size - 1) { - decompiler.addToken(Token.COMMA); - } - } - decompiler.addToken(Token.RB); - } - - // only used for destructuring forms - void decompileObjectLiteral(ObjectLiteral node) { - decompiler.addToken(Token.LC); - List props = node.getElements(); - int size = props.size(); - for (int i = 0; i < size; i++) { - ObjectProperty prop = props.get(i); - boolean shorthandPropertyName = - Boolean.TRUE.equals(prop.getProp(Node.SHORTHAND_PROPERTY_NAME)); - decompile(prop.getLeft()); - if (!shorthandPropertyName) { - decompiler.addToken(Token.COLON); - decompile(prop.getRight()); - } - if (i < size - 1) { - decompiler.addToken(Token.COMMA); - } - } - decompiler.addToken(Token.RC); - } - - // only used for destructuring forms - void decompilePropertyGet(PropertyGet node) { - decompile(node.getTarget()); - decompiler.addToken(Token.DOT); - decompile(node.getProperty()); - } - - // only used for destructuring forms - void decompileElementGet(ElementGet node) { - decompile(node.getTarget()); - decompiler.addToken(Token.LB); - decompile(node.getElement()); - decompiler.addToken(Token.RB); - } -} diff --git a/src/main/java/org/mozilla/javascript/Icode.java b/src/main/java/org/mozilla/javascript/Icode.java deleted file mode 100644 index fe32583f..00000000 --- a/src/main/java/org/mozilla/javascript/Icode.java +++ /dev/null @@ -1,329 +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; - -/** Additional interpreter-specific codes */ -abstract class Icode { - - static final int - - // delete operator used on a name - Icode_DELNAME = 0, - - // Stack: ... value1 -> ... value1 value1 - Icode_DUP = -1, - - // Stack: ... value2 value1 -> ... value2 value1 value2 value1 - Icode_DUP2 = -2, - - // Stack: ... value2 value1 -> ... value1 value2 - Icode_SWAP = -3, - - // Stack: ... value1 -> ... - Icode_POP = -4, - - // Store stack top into return register and then pop it - Icode_POP_RESULT = -5, - - // To jump conditionally and pop additional stack value - Icode_IFEQ_POP = -6, - - // various types of ++/-- - Icode_VAR_INC_DEC = -7, - Icode_NAME_INC_DEC = -8, - Icode_PROP_INC_DEC = -9, - Icode_ELEM_INC_DEC = -10, - Icode_REF_INC_DEC = -11, - - // load/save scope from/to local - Icode_SCOPE_LOAD = -12, - Icode_SCOPE_SAVE = -13, - Icode_TYPEOFNAME = -14, - - // helper for function calls - Icode_NAME_AND_THIS = -15, - Icode_PROP_AND_THIS = -16, - Icode_ELEM_AND_THIS = -17, - Icode_VALUE_AND_THIS = -18, - - // Create closure object for nested functions - Icode_CLOSURE_EXPR = -19, - Icode_CLOSURE_STMT = -20, - - // Special calls - Icode_CALLSPECIAL = -21, - - // To return undefined value - Icode_RETUNDEF = -22, - - // Exception handling implementation - Icode_GOSUB = -23, - Icode_STARTSUB = -24, - Icode_RETSUB = -25, - - // To indicating a line number change in icodes. - Icode_LINE = -26, - - // To store shorts and ints inline - Icode_SHORTNUMBER = -27, - Icode_INTNUMBER = -28, - - // To create and populate array to hold values for [] and {} literals - Icode_LITERAL_NEW = -29, - Icode_LITERAL_SET = -30, - - // Array literal with skipped index like [1,,2] - Icode_SPARE_ARRAYLIT = -31, - - // Load index register to prepare for the following index operation - Icode_REG_IND_C0 = -32, - Icode_REG_IND_C1 = -33, - Icode_REG_IND_C2 = -34, - Icode_REG_IND_C3 = -35, - Icode_REG_IND_C4 = -36, - Icode_REG_IND_C5 = -37, - Icode_REG_IND1 = -38, - Icode_REG_IND2 = -39, - Icode_REG_IND4 = -40, - - // Load string register to prepare for the following string operation - Icode_REG_STR_C0 = -41, - Icode_REG_STR_C1 = -42, - Icode_REG_STR_C2 = -43, - Icode_REG_STR_C3 = -44, - Icode_REG_STR1 = -45, - Icode_REG_STR2 = -46, - Icode_REG_STR4 = -47, - - // Version of getvar/setvar that read var index directly from bytecode - Icode_GETVAR1 = -48, - Icode_SETVAR1 = -49, - - // Load undefined - Icode_UNDEF = -50, - Icode_ZERO = -51, - Icode_ONE = -52, - - // entrance and exit from .() - Icode_ENTERDQ = -53, - Icode_LEAVEDQ = -54, - Icode_TAIL_CALL = -55, - - // Clear local to allow GC its context - Icode_LOCAL_CLEAR = -56, - - // Literal get/set - Icode_LITERAL_GETTER = -57, - Icode_LITERAL_SETTER = -58, - - // const - Icode_SETCONST = -59, - Icode_SETCONSTVAR = -60, - Icode_SETCONSTVAR1 = -61, - - // Generator opcodes (along with Token.YIELD) - Icode_GENERATOR = -62, - Icode_GENERATOR_END = -63, - Icode_DEBUGGER = -64, - Icode_GENERATOR_RETURN = -65, - Icode_YIELD_STAR = -66, - - // Load BigInt register to prepare for the following BigInt operation - Icode_REG_BIGINT_C0 = -67, - Icode_REG_BIGINT_C1 = -68, - Icode_REG_BIGINT_C2 = -69, - Icode_REG_BIGINT_C3 = -70, - Icode_REG_BIGINT1 = -71, - Icode_REG_BIGINT2 = -72, - Icode_REG_BIGINT4 = -73, - - // Call to GetTemplateLiteralCallSite - Icode_TEMPLATE_LITERAL_CALLSITE = -74, - - // Last icode - MIN_ICODE = -74; - - static String bytecodeName(int bytecode) { - if (!validBytecode(bytecode)) { - throw new IllegalArgumentException(String.valueOf(bytecode)); - } - - if (!Token.printICode) { - return String.valueOf(bytecode); - } - - if (validTokenCode(bytecode)) { - return Token.name(bytecode); - } - - switch (bytecode) { - case Icode_DUP: - return "DUP"; - case Icode_DUP2: - return "DUP2"; - case Icode_SWAP: - return "SWAP"; - case Icode_POP: - return "POP"; - case Icode_POP_RESULT: - return "POP_RESULT"; - case Icode_IFEQ_POP: - return "IFEQ_POP"; - case Icode_VAR_INC_DEC: - return "VAR_INC_DEC"; - case Icode_NAME_INC_DEC: - return "NAME_INC_DEC"; - case Icode_PROP_INC_DEC: - return "PROP_INC_DEC"; - case Icode_ELEM_INC_DEC: - return "ELEM_INC_DEC"; - case Icode_REF_INC_DEC: - return "REF_INC_DEC"; - case Icode_SCOPE_LOAD: - return "SCOPE_LOAD"; - case Icode_SCOPE_SAVE: - return "SCOPE_SAVE"; - case Icode_TYPEOFNAME: - return "TYPEOFNAME"; - case Icode_NAME_AND_THIS: - return "NAME_AND_THIS"; - case Icode_PROP_AND_THIS: - return "PROP_AND_THIS"; - case Icode_ELEM_AND_THIS: - return "ELEM_AND_THIS"; - case Icode_VALUE_AND_THIS: - return "VALUE_AND_THIS"; - case Icode_CLOSURE_EXPR: - return "CLOSURE_EXPR"; - case Icode_CLOSURE_STMT: - return "CLOSURE_STMT"; - case Icode_CALLSPECIAL: - return "CALLSPECIAL"; - case Icode_RETUNDEF: - return "RETUNDEF"; - case Icode_GOSUB: - return "GOSUB"; - case Icode_STARTSUB: - return "STARTSUB"; - case Icode_RETSUB: - return "RETSUB"; - case Icode_LINE: - return "LINE"; - case Icode_SHORTNUMBER: - return "SHORTNUMBER"; - case Icode_INTNUMBER: - return "INTNUMBER"; - case Icode_LITERAL_NEW: - return "LITERAL_NEW"; - case Icode_LITERAL_SET: - return "LITERAL_SET"; - case Icode_SPARE_ARRAYLIT: - return "SPARE_ARRAYLIT"; - case Icode_REG_IND_C0: - return "REG_IND_C0"; - case Icode_REG_IND_C1: - return "REG_IND_C1"; - case Icode_REG_IND_C2: - return "REG_IND_C2"; - case Icode_REG_IND_C3: - return "REG_IND_C3"; - case Icode_REG_IND_C4: - return "REG_IND_C4"; - case Icode_REG_IND_C5: - return "REG_IND_C5"; - case Icode_REG_IND1: - return "LOAD_IND1"; - case Icode_REG_IND2: - return "LOAD_IND2"; - case Icode_REG_IND4: - return "LOAD_IND4"; - case Icode_REG_STR_C0: - return "REG_STR_C0"; - case Icode_REG_STR_C1: - return "REG_STR_C1"; - case Icode_REG_STR_C2: - return "REG_STR_C2"; - case Icode_REG_STR_C3: - return "REG_STR_C3"; - case Icode_REG_STR1: - return "LOAD_STR1"; - case Icode_REG_STR2: - return "LOAD_STR2"; - case Icode_REG_STR4: - return "LOAD_STR4"; - case Icode_GETVAR1: - return "GETVAR1"; - case Icode_SETVAR1: - return "SETVAR1"; - case Icode_UNDEF: - return "UNDEF"; - case Icode_ZERO: - return "ZERO"; - case Icode_ONE: - return "ONE"; - case Icode_ENTERDQ: - return "ENTERDQ"; - case Icode_LEAVEDQ: - return "LEAVEDQ"; - case Icode_TAIL_CALL: - return "TAIL_CALL"; - case Icode_LOCAL_CLEAR: - return "LOCAL_CLEAR"; - case Icode_LITERAL_GETTER: - return "LITERAL_GETTER"; - case Icode_LITERAL_SETTER: - return "LITERAL_SETTER"; - case Icode_SETCONST: - return "SETCONST"; - case Icode_SETCONSTVAR: - return "SETCONSTVAR"; - case Icode_SETCONSTVAR1: - return "SETCONSTVAR1"; - case Icode_GENERATOR: - return "GENERATOR"; - case Icode_GENERATOR_END: - return "GENERATOR_END"; - case Icode_DEBUGGER: - return "DEBUGGER"; - case Icode_GENERATOR_RETURN: - return "GENERATOR_RETURN"; - case Icode_YIELD_STAR: - return "YIELD_STAR"; - case Icode_REG_BIGINT_C0: - return "REG_BIGINT_C0"; - case Icode_REG_BIGINT_C1: - return "REG_BIGINT_C1"; - case Icode_REG_BIGINT_C2: - return "REG_BIGINT_C2"; - case Icode_REG_BIGINT_C3: - return "REG_BIGINT_C3"; - case Icode_REG_BIGINT1: - return "LOAD_BIGINT1"; - case Icode_REG_BIGINT2: - return "LOAD_BIGINT2"; - case Icode_REG_BIGINT4: - return "LOAD_BIGINT4"; - case Icode_TEMPLATE_LITERAL_CALLSITE: - return "TEMPLATE_LITERAL_CALLSITE"; - } - - // icode without name - throw new IllegalStateException(String.valueOf(bytecode)); - } - - static boolean validIcode(int icode) { - return MIN_ICODE <= icode && icode <= 0; - } - - static boolean validTokenCode(int token) { - return Token.FIRST_BYTECODE_TOKEN <= token && token <= Token.LAST_BYTECODE_TOKEN; - } - - static boolean validBytecode(int bytecode) { - return validIcode(bytecode) || validTokenCode(bytecode); - } -} diff --git a/src/main/java/org/mozilla/javascript/IdFunctionCall.java b/src/main/java/org/mozilla/javascript/IdFunctionCall.java deleted file mode 100644 index ffe85164..00000000 --- a/src/main/java/org/mozilla/javascript/IdFunctionCall.java +++ /dev/null @@ -1,23 +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; - -/** - * Master for id-based functions that knows their properties and how to - * execute them. - */ -public interface IdFunctionCall -{ - /** - * 'thisObj' will be null if invoked as constructor, in which case - * instance of Scriptable should be returned - */ - public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args); - -} - diff --git a/src/main/java/org/mozilla/javascript/IdFunctionObject.java b/src/main/java/org/mozilla/javascript/IdFunctionObject.java deleted file mode 100644 index 79cd8324..00000000 --- a/src/main/java/org/mozilla/javascript/IdFunctionObject.java +++ /dev/null @@ -1,171 +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 class IdFunctionObject extends BaseFunction -{ - - private static final long serialVersionUID = -5332312783643935019L; - - public IdFunctionObject(IdFunctionCall idcall, Object tag, int id, int arity) - { - if (arity < 0) - throw new IllegalArgumentException(); - - this.idcall = idcall; - this.tag = tag; - this.methodId = id; - this.arity = arity; - } - - public IdFunctionObject(IdFunctionCall idcall, Object tag, int id, - String name, int arity, Scriptable scope) - { - super(scope, null); - - if (arity < 0) - throw new IllegalArgumentException(); - if (name == null) - throw new IllegalArgumentException(); - - this.idcall = idcall; - this.tag = tag; - this.methodId = id; - this.arity = arity; - this.functionName = name; - } - - public void initFunction(String name, Scriptable scope) - { - if (name == null) throw new IllegalArgumentException(); - if (scope == null) throw new IllegalArgumentException(); - this.functionName = name; - setParentScope(scope); - } - - public final boolean hasTag(Object tag) - { - return tag == null ? this.tag == null : tag.equals(this.tag); - } - - public Object getTag() { - return tag; - } - - public final int methodId() - { - return methodId; - } - - public final void markAsConstructor(Scriptable prototypeProperty) - { - useCallAsConstructor = true; - setImmunePrototypeProperty(prototypeProperty); - } - - public final void addAsProperty(Scriptable target) - { - ScriptableObject.defineProperty(target, functionName, this, - ScriptableObject.DONTENUM); - } - - public void exportAsScopeProperty() - { - addAsProperty(getParentScope()); - } - - @Override - public Scriptable getPrototype() - { - // Lazy initialization of prototype: for native functions this - // may not be called at all - Scriptable proto = super.getPrototype(); - if (proto == null) { - proto = getFunctionPrototype(getParentScope()); - setPrototype(proto); - } - return proto; - } - - @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, - Object[] args) - { - return idcall.execIdCall(this, cx, scope, thisObj, args); - } - - @Override - public Scriptable createObject(Context cx, Scriptable scope) - { - if (useCallAsConstructor) { - return null; - } - // Throw error if not explicitly coded to be used as constructor, - // to satisfy ECMAScript standard (see bugzilla 202019). - // To follow current (2003-05-01) SpiderMonkey behavior, change it to: - // return super.createObject(cx, scope); - throw ScriptRuntime.typeErrorById("msg.not.ctor", functionName); - } - - @Override - 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("() { "); - } - sb.append("[native code for "); - if (idcall instanceof Scriptable) { - Scriptable sobj = (Scriptable)idcall; - sb.append(sobj.getClassName()); - sb.append('.'); - } - sb.append(getFunctionName()); - sb.append(", arity="); - sb.append(getArity()); - sb.append(justbody ? "]\n" : "] }\n"); - return sb.toString(); - } - - @Override - public int getArity() - { - return arity; - } - - @Override - public int getLength() { return getArity(); } - - @Override - public String getFunctionName() - { - return (functionName == null) ? "" : functionName; - } - - public final RuntimeException unknown() - { - // It is program error to call id-like methods for unknown function - return new IllegalArgumentException( - "BAD FUNCTION ID="+methodId+" MASTER="+idcall); - } - - static boolean equalObjectGraphs(IdFunctionObject f1, IdFunctionObject f2, EqualObjectGraphs eq) { - return f1.methodId == f2.methodId && f1.hasTag(f2.tag) && eq.equalGraphs(f1.idcall, f2.idcall); - } - - private final IdFunctionCall idcall; - private final Object tag; - private final int methodId; - private int arity; - private boolean useCallAsConstructor; - private String functionName; -} diff --git a/src/main/java/org/mozilla/javascript/IdFunctionObjectES6.java b/src/main/java/org/mozilla/javascript/IdFunctionObjectES6.java deleted file mode 100644 index d78ad5f1..00000000 --- a/src/main/java/org/mozilla/javascript/IdFunctionObjectES6.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.mozilla.javascript; - -public class IdFunctionObjectES6 extends IdFunctionObject{ - - private static final long serialVersionUID = -8023088662589035261L; - - public IdFunctionObjectES6(IdFunctionCall idcall, Object tag, int id, String name, int arity, Scriptable scope) { - super(idcall, tag, id, name, arity, scope); - } - - private static final int Id_length = 1, Id_name = 3; - private boolean myLength = true; - private boolean myName = true; - - @Override - protected int findInstanceIdInfo(String s) { - if (s.equals("length")) return instanceIdInfo(READONLY | DONTENUM, Id_length); - else if (s.equals("name")) return instanceIdInfo(READONLY | DONTENUM, Id_name); - return super.findInstanceIdInfo(s); - } - - @Override - protected Object getInstanceIdValue(int id) { - if (id == Id_length && !myLength) { - return NOT_FOUND; - } else if (id == Id_name && !myName) { - return NOT_FOUND; - } - return super.getInstanceIdValue(id); - } - - @Override - protected void setInstanceIdValue(int id, Object value) { - if (id == Id_length && value == NOT_FOUND) { - this.myLength = false; - return; - } else if (id == Id_name && value == NOT_FOUND) { - this.myName = false; - return; - } - super.setInstanceIdValue(id, value); - } -} diff --git a/src/main/java/org/mozilla/javascript/IdScriptableObject.java b/src/main/java/org/mozilla/javascript/IdScriptableObject.java deleted file mode 100644 index bc7f9637..00000000 --- a/src/main/java/org/mozilla/javascript/IdScriptableObject.java +++ /dev/null @@ -1,991 +0,0 @@ -/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; 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.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; - -/** - * Base class for native object implementation that uses IdFunctionObject to export its methods to - * script via <class-name>.prototype object. - * - *

Any descendant should implement at least the following methods: findInstanceIdInfo - * getInstanceIdName execIdCall methodArity - * - *

To define non-function properties, the descendant should override getInstanceIdValue - * setInstanceIdValue to get/set property value and provide its default attributes. - * - *

To customize initialization of constructor and prototype objects, descendant may override - * scopeInit or fillConstructorProperties methods. - */ -public abstract class IdScriptableObject extends ScriptableObject implements IdFunctionCall { - private static final long serialVersionUID = -3744239272168621609L; - private transient PrototypeValues prototypeValues; - - private static final class PrototypeValues implements Serializable { - private static final long serialVersionUID = 3038645279153854371L; - - private static final int NAME_SLOT = 1; - private static final int SLOT_SPAN = 2; - - private IdScriptableObject obj; - private int maxId; - private Object[] valueArray; - private short[] attributeArray; - - // The following helps to avoid creation of valueArray during runtime - // initialization for common case of "constructor" property - int constructorId; - private IdFunctionObject constructor; - private short constructorAttrs; - - PrototypeValues(IdScriptableObject obj, int maxId) { - if (obj == null) throw new IllegalArgumentException(); - if (maxId < 1) throw new IllegalArgumentException(); - this.obj = obj; - this.maxId = maxId; - } - - final int getMaxId() { - return maxId; - } - - final void initValue(int id, String name, Object value, int attributes) { - if (!(1 <= id && id <= maxId)) throw new IllegalArgumentException(); - if (name == null) throw new IllegalArgumentException(); - if (value == NOT_FOUND) throw new IllegalArgumentException(); - ScriptableObject.checkValidAttributes(attributes); - if (obj.findPrototypeId(name) != id) throw new IllegalArgumentException(name); - - if (id == constructorId) { - if (!(value instanceof IdFunctionObject)) { - throw new IllegalArgumentException( - "consructor should be initialized with IdFunctionObject"); - } - constructor = (IdFunctionObject) value; - constructorAttrs = (short) attributes; - return; - } - - initSlot(id, name, value, attributes); - } - - final void initValue(int id, Symbol key, Object value, int attributes) { - if (!(1 <= id && id <= maxId)) throw new IllegalArgumentException(); - if (key == null) throw new IllegalArgumentException(); - if (value == NOT_FOUND) throw new IllegalArgumentException(); - ScriptableObject.checkValidAttributes(attributes); - if (obj.findPrototypeId(key) != id) throw new IllegalArgumentException(key.toString()); - - if (id == constructorId) { - if (!(value instanceof IdFunctionObject)) { - throw new IllegalArgumentException( - "consructor should be initialized with IdFunctionObject"); - } - constructor = (IdFunctionObject) value; - constructorAttrs = (short) attributes; - return; - } - - initSlot(id, key, value, attributes); - } - - private void initSlot(int id, Object name, Object value, int attributes) { - Object[] array = valueArray; - if (array == null) throw new IllegalStateException(); - - if (value == null) { - value = UniqueTag.NULL_VALUE; - } - int index = (id - 1) * SLOT_SPAN; - synchronized (this) { - Object value2 = array[index]; - if (value2 == null) { - array[index] = value; - array[index + NAME_SLOT] = name; - attributeArray[id - 1] = (short) attributes; - } else { - if (!name.equals(array[index + NAME_SLOT])) throw new IllegalStateException(); - } - } - } - - final IdFunctionObject createPrecachedConstructor() { - if (constructorId != 0) throw new IllegalStateException(); - constructorId = obj.findPrototypeId("constructor"); - if (constructorId == 0) { - throw new IllegalStateException("No id for constructor property"); - } - obj.initPrototypeId(constructorId); - if (constructor == null) { - throw new IllegalStateException( - obj.getClass().getName() - + ".initPrototypeId() did not " - + "initialize id=" - + constructorId); - } - constructor.initFunction(obj.getClassName(), ScriptableObject.getTopLevelScope(obj)); - constructor.markAsConstructor(obj); - return constructor; - } - - final int findId(String name) { - return obj.findPrototypeId(name); - } - - final int findId(Symbol key) { - return obj.findPrototypeId(key); - } - - final boolean has(int id) { - Object[] array = valueArray; - if (array == null) { - // Not yet initialized, assume all exists - return true; - } - int valueSlot = (id - 1) * SLOT_SPAN; - Object value = array[valueSlot]; - if (value == null) { - // The particular entry has not been yet initialized - return true; - } - return value != NOT_FOUND; - } - - final Object get(int id) { - Object value = ensureId(id); - if (value == UniqueTag.NULL_VALUE) { - value = null; - } - return value; - } - - final void set(int id, Scriptable start, Object value) { - if (value == NOT_FOUND) throw new IllegalArgumentException(); - ensureId(id); - int attr = attributeArray[id - 1]; - if ((attr & READONLY) == 0) { - if (start == obj) { - if (value == null) { - value = UniqueTag.NULL_VALUE; - } - int valueSlot = (id - 1) * SLOT_SPAN; - synchronized (this) { - valueArray[valueSlot] = value; - } - } else { - int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; - Object name = valueArray[nameSlot]; - if (name instanceof Symbol) { - if (start instanceof SymbolScriptable) { - ((SymbolScriptable) start).put((Symbol) name, start, value); - } - } else { - start.put((String) name, start, value); - } - } - } - } - - final void delete(int id) { - ensureId(id); - int attr = attributeArray[id - 1]; - // non-configurable - if ((attr & PERMANENT) != 0) { - Context cx = Context.getContext(); - if (cx.isStrictMode()) { - int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; - String name = (String) valueArray[nameSlot]; - throw ScriptRuntime.typeErrorById( - "msg.delete.property.with.configurable.false", name); - } - } else { - int valueSlot = (id - 1) * SLOT_SPAN; - synchronized (this) { - valueArray[valueSlot] = NOT_FOUND; - attributeArray[id - 1] = EMPTY; - } - } - } - - final int getAttributes(int id) { - ensureId(id); - return attributeArray[id - 1]; - } - - final void setAttributes(int id, int attributes) { - ScriptableObject.checkValidAttributes(attributes); - ensureId(id); - synchronized (this) { - attributeArray[id - 1] = (short) attributes; - } - } - - final Object[] getNames(boolean getAll, boolean getSymbols, Object[] extraEntries) { - Object[] names = null; - int count = 0; - for (int id = 1; id <= maxId; ++id) { - Object value = ensureId(id); - if (getAll || (attributeArray[id - 1] & DONTENUM) == 0) { - if (value != NOT_FOUND) { - int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; - Object name = valueArray[nameSlot]; - if (name instanceof String) { - if (names == null) { - names = new Object[maxId]; - } - names[count++] = name; - } else if (getSymbols && (name instanceof Symbol)) { - if (names == null) { - names = new Object[maxId]; - } - names[count++] = name.toString(); - } - } - } - } - if (count == 0) { - return extraEntries; - } else if (extraEntries == null || extraEntries.length == 0) { - if (count != names.length) { - Object[] tmp = new Object[count]; - System.arraycopy(names, 0, tmp, 0, count); - names = tmp; - } - return names; - } else { - int extra = extraEntries.length; - Object[] tmp = new Object[extra + count]; - System.arraycopy(extraEntries, 0, tmp, 0, extra); - System.arraycopy(names, 0, tmp, extra, count); - return tmp; - } - } - - private Object ensureId(int id) { - Object[] array = valueArray; - if (array == null) { - synchronized (this) { - array = valueArray; - if (array == null) { - array = new Object[maxId * SLOT_SPAN]; - valueArray = array; - attributeArray = new short[maxId]; - } - } - } - int valueSlot = (id - 1) * SLOT_SPAN; - Object value = array[valueSlot]; - if (value == null) { - if (id == constructorId) { - initSlot(constructorId, "constructor", constructor, constructorAttrs); - constructor = null; // no need to refer it any longer - } else { - obj.initPrototypeId(id); - } - value = array[valueSlot]; - if (value == null) { - throw new IllegalStateException( - obj.getClass().getName() - + ".initPrototypeId(int id) " - + "did not initialize id=" - + id); - } - } - return value; - } - } - - public IdScriptableObject() {} - - public IdScriptableObject(Scriptable scope, Scriptable prototype) { - super(scope, prototype); - } - - protected final boolean defaultHas(String name) { - return super.has(name, this); - } - - protected final Object defaultGet(String name) { - return super.get(name, this); - } - - protected final void defaultPut(String name, Object value) { - super.put(name, this, value); - } - - @Override - public boolean has(String name, Scriptable start) { - int info = findInstanceIdInfo(name); - if (info != 0) { - int attr = (info >>> 16); - if ((attr & PERMANENT) != 0) { - return true; - } - int id = (info & 0xFFFF); - return NOT_FOUND != getInstanceIdValue(id); - } - if (prototypeValues != null) { - int id = prototypeValues.findId(name); - if (id != 0) { - return prototypeValues.has(id); - } - } - return super.has(name, start); - } - - @Override - public boolean has(Symbol key, Scriptable start) { - int info = findInstanceIdInfo(key); - if (info != 0) { - int attr = (info >>> 16); - if ((attr & PERMANENT) != 0) { - return true; - } - int id = (info & 0xFFFF); - return NOT_FOUND != getInstanceIdValue(id); - } - if (prototypeValues != null) { - int id = prototypeValues.findId(key); - if (id != 0) { - return prototypeValues.has(id); - } - } - return super.has(key, start); - } - - @Override - public Object get(String name, Scriptable start) { - // Check for slot first for performance. This is a very hot code - // path that should be further optimized. - Object value = super.get(name, start); - if (value != NOT_FOUND) { - return value; - } - int info = findInstanceIdInfo(name); - if (info != 0) { - int id = (info & 0xFFFF); - value = getInstanceIdValue(id); - if (value != NOT_FOUND) return value; - } - if (prototypeValues != null) { - int id = prototypeValues.findId(name); - if (id != 0) { - value = prototypeValues.get(id); - if (value != NOT_FOUND) return value; - } - } - return NOT_FOUND; - } - - @Override - public Object get(Symbol key, Scriptable start) { - Object value = super.get(key, start); - if (value != NOT_FOUND) { - return value; - } - int info = findInstanceIdInfo(key); - if (info != 0) { - int id = (info & 0xFFFF); - value = getInstanceIdValue(id); - if (value != NOT_FOUND) return value; - } - if (prototypeValues != null) { - int id = prototypeValues.findId(key); - if (id != 0) { - value = prototypeValues.get(id); - if (value != NOT_FOUND) return value; - } - } - return NOT_FOUND; - } - - @Override - public void put(String name, Scriptable start, Object value) { - int info = findInstanceIdInfo(name); - if (info != 0) { - if (start == this && isSealed()) { - throw Context.reportRuntimeErrorById("msg.modify.sealed", name); - } - int attr = (info >>> 16); - if ((attr & READONLY) == 0) { - if (start == this) { - int id = (info & 0xFFFF); - setInstanceIdValue(id, value); - } else { - start.put(name, start, value); - } - } - return; - } - if (prototypeValues != null) { - int id = prototypeValues.findId(name); - if (id != 0) { - if (start == this && isSealed()) { - throw Context.reportRuntimeErrorById("msg.modify.sealed", name); - } - prototypeValues.set(id, start, value); - return; - } - } - super.put(name, start, value); - } - - @Override - public void put(Symbol key, Scriptable start, Object value) { - int info = findInstanceIdInfo(key); - if (info != 0) { - if (start == this && isSealed()) { - throw Context.reportRuntimeErrorById("msg.modify.sealed"); - } - int attr = (info >>> 16); - if ((attr & READONLY) == 0) { - if (start == this) { - int id = (info & 0xFFFF); - setInstanceIdValue(id, value); - } else { - ensureSymbolScriptable(start).put(key, start, value); - } - } - return; - } - if (prototypeValues != null) { - int id = prototypeValues.findId(key); - if (id != 0) { - if (start == this && isSealed()) { - throw Context.reportRuntimeErrorById("msg.modify.sealed"); - } - prototypeValues.set(id, start, value); - return; - } - } - super.put(key, start, value); - } - - @Override - public void delete(String name) { - int info = findInstanceIdInfo(name); - if (info != 0) { - // Let the super class to throw exceptions for sealed objects - if (!isSealed()) { - int attr = (info >>> 16); - // non-configurable - if ((attr & PERMANENT) != 0) { - Context cx = Context.getContext(); - if (cx.isStrictMode()) { - throw ScriptRuntime.typeErrorById( - "msg.delete.property.with.configurable.false", name); - } - } else { - int id = (info & 0xFFFF); - setInstanceIdValue(id, NOT_FOUND); - } - return; - } - } - if (prototypeValues != null) { - int id = prototypeValues.findId(name); - if (id != 0) { - if (!isSealed()) { - prototypeValues.delete(id); - } - return; - } - } - super.delete(name); - } - - @Override - public void delete(Symbol key) { - int info = findInstanceIdInfo(key); - if (info != 0) { - // Let the super class to throw exceptions for sealed objects - if (!isSealed()) { - int attr = (info >>> 16); - // non-configurable - if ((attr & PERMANENT) != 0) { - Context cx = Context.getContext(); - if (cx.isStrictMode()) { - throw ScriptRuntime.typeErrorById( - "msg.delete.property.with.configurable.false"); - } - } else { - int id = (info & 0xFFFF); - setInstanceIdValue(id, NOT_FOUND); - } - return; - } - } - if (prototypeValues != null) { - int id = prototypeValues.findId(key); - if (id != 0) { - if (!isSealed()) { - prototypeValues.delete(id); - } - return; - } - } - super.delete(key); - } - - @Override - public int getAttributes(String name) { - int info = findInstanceIdInfo(name); - if (info != 0) { - int attr = (info >>> 16); - return attr; - } - if (prototypeValues != null) { - int id = prototypeValues.findId(name); - if (id != 0) { - return prototypeValues.getAttributes(id); - } - } - return super.getAttributes(name); - } - - @Override - public int getAttributes(Symbol key) { - int info = findInstanceIdInfo(key); - if (info != 0) { - int attr = (info >>> 16); - return attr; - } - if (prototypeValues != null) { - int id = prototypeValues.findId(key); - if (id != 0) { - return prototypeValues.getAttributes(id); - } - } - return super.getAttributes(key); - } - - @Override - public void setAttributes(String name, int attributes) { - ScriptableObject.checkValidAttributes(attributes); - int info = findInstanceIdInfo(name); - if (info != 0) { - int id = (info & 0xFFFF); - int currentAttributes = (info >>> 16); - if (attributes != currentAttributes) { - setInstanceIdAttributes(id, attributes); - } - return; - } - if (prototypeValues != null) { - int id = prototypeValues.findId(name); - if (id != 0) { - prototypeValues.setAttributes(id, attributes); - return; - } - } - super.setAttributes(name, attributes); - } - - @Override - Object[] getIds(boolean getNonEnumerable, boolean getSymbols) { - Object[] result = super.getIds(getNonEnumerable, getSymbols); - - if (prototypeValues != null) { - result = prototypeValues.getNames(getNonEnumerable, getSymbols, result); - } - - int maxInstanceId = getMaxInstanceId(); - if (maxInstanceId != 0) { - Object[] ids = null; - int count = 0; - - for (int id = maxInstanceId; id != 0; --id) { - String name = getInstanceIdName(id); - int info = findInstanceIdInfo(name); - if (info != 0) { - int attr = (info >>> 16); - if ((attr & PERMANENT) == 0) { - if (NOT_FOUND == getInstanceIdValue(id)) { - continue; - } - } - if (getNonEnumerable || (attr & DONTENUM) == 0) { - if (count == 0) { - // Need extra room for no more then [1..id] names - ids = new Object[id]; - } - ids[count++] = name; - } - } - } - if (count != 0) { - if (result.length == 0 && ids.length == count) { - result = ids; - } else { - Object[] tmp = new Object[result.length + count]; - System.arraycopy(result, 0, tmp, 0, result.length); - System.arraycopy(ids, 0, tmp, result.length, count); - result = tmp; - } - } - } - return result; - } - - /** Get maximum id findInstanceIdInfo can generate. */ - protected int getMaxInstanceId() { - return 0; - } - - protected static int instanceIdInfo(int attributes, int id) { - return (attributes << 16) | id; - } - - /** - * Map name to id of instance property. Should return 0 if not found or the result of {@link - * #instanceIdInfo(int, int)}. - */ - protected int findInstanceIdInfo(String name) { - return 0; - } - - /** - * Map name to id of instance property. Should return 0 if not found or the result of {@link - * #instanceIdInfo(int, int)}. - */ - protected int findInstanceIdInfo(Symbol key) { - return 0; - } - - /** Map id back to property name it defines. */ - protected String getInstanceIdName(int id) { - throw new IllegalArgumentException(String.valueOf(id)); - } - - /** - * Get id value. * If id value is constant, descendant can call cacheIdValue to store * value in - * the permanent cache. * Default implementation creates IdFunctionObject instance for given id - * * and cache its value - */ - protected Object getInstanceIdValue(int id) { - throw new IllegalStateException(String.valueOf(id)); - } - - /** - * Set or delete id value. If value == NOT_FOUND , the implementation should make sure that the - * following getInstanceIdValue return NOT_FOUND. - */ - protected void setInstanceIdValue(int id, Object value) { - throw new IllegalStateException(String.valueOf(id)); - } - - /** - * Update the attributes of the given instance property. Classes which want to support changing - * property attributes via Object.defineProperty must override this method. The default - * implementation throws InternalError. - * - * @param id the instance property id - * @param attr the new attribute bitset - */ - protected void setInstanceIdAttributes(int id, int attr) { - throw ScriptRuntime.constructError( - "InternalError", - "Changing attributes not supported for " - + getClassName() - + " " - + getInstanceIdName(id) - + " property"); - } - - /** - * 'thisObj' will be null if invoked as constructor, in which case * instance of Scriptable - * should be returned. - */ - @Override - public Object execIdCall( - IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - throw f.unknown(); - } - - public final IdFunctionObject exportAsJSClass( - int maxPrototypeId, Scriptable scope, boolean sealed) { - // Set scope and prototype unless this is top level scope itself - if (scope != this && scope != null) { - setParentScope(scope); - setPrototype(getObjectPrototype(scope)); - } - - activatePrototypeMap(maxPrototypeId); - IdFunctionObject ctor = prototypeValues.createPrecachedConstructor(); - if (sealed) { - sealObject(); - } - fillConstructorProperties(ctor); - if (sealed) { - ctor.sealObject(); - } - ctor.exportAsScopeProperty(); - return ctor; - } - - public final boolean hasPrototypeMap() { - return prototypeValues != null; - } - - public final void activatePrototypeMap(int maxPrototypeId) { - PrototypeValues values = new PrototypeValues(this, maxPrototypeId); - synchronized (this) { - if (prototypeValues != null) throw new IllegalStateException(); - prototypeValues = values; - } - } - - public final IdFunctionObject initPrototypeMethod(Object tag, int id, String name, int arity) { - return initPrototypeMethod(tag, id, name, name, arity); - } - - public final IdFunctionObject initPrototypeMethod( - Object tag, int id, String propertyName, String functionName, int arity) { - Scriptable scope = ScriptableObject.getTopLevelScope(this); - IdFunctionObject function = - newIdFunction( - tag, id, functionName != null ? functionName : propertyName, arity, scope); - prototypeValues.initValue(id, propertyName, function, DONTENUM); - return function; - } - - public final IdFunctionObject initPrototypeMethod( - Object tag, int id, Symbol key, String functionName, int arity) { - Scriptable scope = ScriptableObject.getTopLevelScope(this); - IdFunctionObject function = newIdFunction(tag, id, functionName, arity, scope); - prototypeValues.initValue(id, key, function, DONTENUM); - return function; - } - - public final void initPrototypeConstructor(IdFunctionObject f) { - int id = prototypeValues.constructorId; - if (id == 0) throw new IllegalStateException(); - if (f.methodId() != id) throw new IllegalArgumentException(); - if (isSealed()) { - f.sealObject(); - } - prototypeValues.initValue(id, "constructor", f, DONTENUM); - } - - public final void initPrototypeValue(int id, String name, Object value, int attributes) { - prototypeValues.initValue(id, name, value, attributes); - } - - public final void initPrototypeValue(int id, Symbol key, Object value, int attributes) { - prototypeValues.initValue(id, key, value, attributes); - } - - protected void initPrototypeId(int id) { - throw new IllegalStateException(String.valueOf(id)); - } - - protected int findPrototypeId(String name) { - throw new IllegalStateException(name); - } - - protected int findPrototypeId(Symbol key) { - return 0; - } - - protected void fillConstructorProperties(IdFunctionObject ctor) {} - - protected void addIdFunctionProperty( - Scriptable obj, Object tag, int id, String name, int arity) { - Scriptable scope = ScriptableObject.getTopLevelScope(obj); - IdFunctionObject f = newIdFunction(tag, id, name, arity, scope); - f.addAsProperty(obj); - } - - /** - * Utility method to check the type and do the cast or throw an incompatible call error. - * Possible usage would be to have a private function like realThis: - * - *

-     *  private static NativeSomething realThis(Scriptable thisObj, IdFunctionObject f)
-     *  {
-     *      return ensureType(thisObj, NativeSomething.class, f);
-     * }
-     * 
- * - * @param obj the object to check/cast - * @param clazz the target type - * @param f function that is attempting to convert 'this' object. - * @return obj casted to the target type - * @throws EcmaError if the cast failed. - */ - @SuppressWarnings("unchecked") - protected static T ensureType(Object obj, Class clazz, IdFunctionObject f) { - if (clazz.isInstance(obj)) { - return (T) obj; - } - if (obj == null) { - throw ScriptRuntime.typeErrorById( - "msg.incompat.call.details", f.getFunctionName(), "null", clazz.getName()); - } - throw ScriptRuntime.typeErrorById( - "msg.incompat.call.details", - f.getFunctionName(), - obj.getClass().getName(), - clazz.getName()); - } - - private IdFunctionObject newIdFunction( - Object tag, int id, String name, int arity, Scriptable scope) { - IdFunctionObject function = null; - if (Context.getContext().getLanguageVersion() < Context.VERSION_ES6) { - function = new IdFunctionObject(this, tag, id, name, arity, scope); - } else { - function = new IdFunctionObjectES6(this, tag, id, name, arity, scope); - } - - if (isSealed()) { - function.sealObject(); - } - return function; - } - - @Override - protected void defineOwnProperty( - Context cx, Object key, ScriptableObject desc, boolean checkValid) { - if (key instanceof String) { - String name = (String) key; - int info = findInstanceIdInfo(name); - if (info != 0) { - int id = (info & 0xFFFF); - if (isAccessorDescriptor(desc)) { - delete(id); // it will be replaced with a slot - } else { - checkPropertyDefinition(desc); - ScriptableObject current = getOwnPropertyDescriptor(cx, key); - checkPropertyChange(name, current, desc); - int attr = (info >>> 16); - Object value = getProperty(desc, "value"); - if (value != NOT_FOUND && (attr & READONLY) == 0) { - Object currentValue = getInstanceIdValue(id); - if (!sameValue(value, currentValue)) { - setInstanceIdValue(id, value); - } - } - setAttributes(name, applyDescriptorToAttributeBitset(attr, desc)); - return; - } - } - if (prototypeValues != null) { - int id = prototypeValues.findId(name); - if (id != 0) { - if (isAccessorDescriptor(desc)) { - prototypeValues.delete(id); // it will be replaced with a slot - } else { - checkPropertyDefinition(desc); - ScriptableObject current = getOwnPropertyDescriptor(cx, key); - checkPropertyChange(name, current, desc); - int attr = prototypeValues.getAttributes(id); - Object value = getProperty(desc, "value"); - if (value != NOT_FOUND && (attr & READONLY) == 0) { - Object currentValue = prototypeValues.get(id); - if (!sameValue(value, currentValue)) { - prototypeValues.set(id, this, value); - } - } - prototypeValues.setAttributes( - id, applyDescriptorToAttributeBitset(attr, desc)); - - // Handle the regular slot that was created if this property was previously - // replaced - // with an accessor descriptor. - if (super.has(name, this)) { - super.delete(name); - } - - return; - } - } - } - } - super.defineOwnProperty(cx, key, desc, checkValid); - } - - @Override - protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { - ScriptableObject desc = super.getOwnPropertyDescriptor(cx, id); - if (desc == null) { - if (id instanceof String) { - desc = getBuiltInDescriptor((String) id); - } else if (ScriptRuntime.isSymbol(id)) { - desc = getBuiltInDescriptor(((NativeSymbol) id).getKey()); - } - } - return desc; - } - - private ScriptableObject getBuiltInDescriptor(String name) { - Object value = null; - int attr = EMPTY; - - Scriptable scope = getParentScope(); - if (scope == null) { - scope = this; - } - - int info = findInstanceIdInfo(name); - if (info != 0) { - int id = (info & 0xFFFF); - value = getInstanceIdValue(id); - attr = (info >>> 16); - return buildDataDescriptor(scope, value, attr); - } - if (prototypeValues != null) { - int id = prototypeValues.findId(name); - if (id != 0) { - value = prototypeValues.get(id); - attr = prototypeValues.getAttributes(id); - return buildDataDescriptor(scope, value, attr); - } - } - return null; - } - - private ScriptableObject getBuiltInDescriptor(Symbol key) { - Object value = null; - int attr = EMPTY; - - Scriptable scope = getParentScope(); - if (scope == null) { - scope = this; - } - - if (prototypeValues != null) { - int id = prototypeValues.findId(key); - if (id != 0) { - value = prototypeValues.get(id); - attr = prototypeValues.getAttributes(id); - return buildDataDescriptor(scope, value, attr); - } - } - return null; - } - - private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { - stream.defaultReadObject(); - int maxPrototypeId = stream.readInt(); - if (maxPrototypeId != 0) { - activatePrototypeMap(maxPrototypeId); - } - } - - private void writeObject(ObjectOutputStream stream) throws IOException { - stream.defaultWriteObject(); - int maxPrototypeId = 0; - if (prototypeValues != null) { - maxPrototypeId = prototypeValues.getMaxId(); - } - stream.writeInt(maxPrototypeId); - } -} diff --git a/src/main/java/org/mozilla/javascript/ImplementationVersion.java b/src/main/java/org/mozilla/javascript/ImplementationVersion.java deleted file mode 100644 index 77513745..00000000 --- a/src/main/java/org/mozilla/javascript/ImplementationVersion.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.mozilla.javascript; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.Enumeration; -import java.util.jar.Attributes; -import java.util.jar.Manifest; - -/** - * This class is a singleton that just exists to serve up the implementation version. This should - * encourage that it's safely but lazily loaded just once per VM. - */ -public class ImplementationVersion { - - private String versionString; - - private static final ImplementationVersion version = new ImplementationVersion(); - - public static String get() { - return version.versionString; - } - - private ImplementationVersion() { - Enumeration urls; - try { - urls = - ImplementationVersion.class - .getClassLoader() - .getResources("META-INF/MANIFEST.MF"); - } catch (IOException ioe) { - return; - } - - // There will be many manifests in the world -- enumerate all of them until we find the - // right one. - while (urls.hasMoreElements()) { - URL metaUrl = urls.nextElement(); - try (InputStream is = metaUrl.openStream()) { - Manifest mf = new Manifest(is); - Attributes attrs = mf.getMainAttributes(); - if ("Mozilla Rhino".equals(attrs.getValue("Implementation-Title"))) { - StringBuilder buf = new StringBuilder(23); - buf.append("Rhino ").append(attrs.getValue("Implementation-Version")); - String builtDate = attrs.getValue("Built-Date"); - if (builtDate != null) { - builtDate = builtDate.replaceAll("-", " "); - buf.append(' ').append(builtDate); - } - versionString = buf.toString(); - return; - } - } catch (IOException e) { - // Ignore this unlikely event - } - } - // We are probably in a IDE - versionString = "Rhino Snapshot"; - } -} diff --git a/src/main/java/org/mozilla/javascript/ImporterTopLevel.java b/src/main/java/org/mozilla/javascript/ImporterTopLevel.java deleted file mode 100644 index 49d5bdce..00000000 --- a/src/main/java/org/mozilla/javascript/ImporterTopLevel.java +++ /dev/null @@ -1,302 +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; - -/** - * Class ImporterTopLevel - * - *

This class defines a ScriptableObject that can be instantiated as a top-level ("global") - * object to provide functionality similar to Java's "import" statement. - * - *

This class can be used to create a top-level scope using the following code: - * - *

- *  Scriptable scope = new ImporterTopLevel(cx);
- * 
- * - * Then JavaScript code will have access to the following methods: - * - *
    - *
  • importClass - will "import" a class by making its unqualified name available as a property - * of the top-level scope - *
  • importPackage - will "import" all the classes of the package by searching for unqualified - * names as classes qualified by the given package. - *
- * - * The following code from the shell illustrates this use: - * - *
- * js> importClass(java.io.File)
- * js> f = new File('help.txt')
- * help.txt
- * js> importPackage(java.util)
- * js> v = new Vector()
- * []
- * 
- * - * @author Norris Boyd - */ -public class ImporterTopLevel extends TopLevel { - private static final long serialVersionUID = -9095380847465315412L; - - private static final Object IMPORTER_TAG = "Importer"; - - public ImporterTopLevel() {} - - public ImporterTopLevel(Context cx) { - this(cx, false); - } - - public ImporterTopLevel(Context cx, boolean sealed) { - initStandardObjects(cx, sealed); - } - - @Override - public String getClassName() { - return (topScopeFlag) ? "global" : "JavaImporter"; - } - - public static void init(Context cx, Scriptable scope, boolean sealed) { - ImporterTopLevel obj = new ImporterTopLevel(); - obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); - } - - public void initStandardObjects(Context cx, boolean sealed) { - // Assume that Context.initStandardObjects initialize JavaImporter - // property lazily so the above init call is not yet called - cx.initStandardObjects(this, sealed); - topScopeFlag = true; - // If seal is true then exportAsJSClass(cx, seal) would seal - // this obj. Since this is scope as well, it would not allow - // to add variables. - IdFunctionObject ctor = exportAsJSClass(MAX_PROTOTYPE_ID, this, false); - if (sealed) { - ctor.sealObject(); - } - // delete "constructor" defined by exportAsJSClass so "constructor" - // name would refer to Object.constructor - // and not to JavaImporter.prototype.constructor. - delete("constructor"); - } - - @Override - public boolean has(String name, Scriptable start) { - return super.has(name, start) || getPackageProperty(name, start) != NOT_FOUND; - } - - @Override - public Object get(String name, Scriptable start) { - Object result = super.get(name, start); - if (result != NOT_FOUND) return result; - result = getPackageProperty(name, start); - return result; - } - - private Object getPackageProperty(String name, Scriptable start) { - Object result = NOT_FOUND; - Scriptable scope = start; - if (topScopeFlag) { - scope = ScriptableObject.getTopLevelScope(scope); - } - Object[] elements = getNativeJavaPackages(scope); - if (elements == null) { - return result; - } - for (int i = 0; i < elements.length; i++) { - NativeJavaPackage p = (NativeJavaPackage) elements[i]; - Object v = p.getPkgProperty(name, start, false); - if (v != null && !(v instanceof NativeJavaPackage)) { - if (result == NOT_FOUND) { - result = v; - } else { - throw Context.reportRuntimeErrorById( - "msg.ambig.import", result.toString(), v.toString()); - } - } - } - - return result; - } - - private static Object[] getNativeJavaPackages(Scriptable scope) { - // retrive the native java packages stored in top scope. - synchronized (scope) { - if (scope instanceof ScriptableObject) { - ScriptableObject so = (ScriptableObject) scope; - ObjArray importedPackages = (ObjArray) so.getAssociatedValue(AKEY); - if (importedPackages != null) { - return importedPackages.toArray(); - } - } - } - return null; - } - - /** @deprecated Kept only for compatibility. */ - @Deprecated - public void importPackage(Context cx, Scriptable thisObj, Object[] args, Function funObj) { - js_importPackage(this, args); - } - - private Object js_construct(Scriptable scope, Object[] args) { - ImporterTopLevel result = new ImporterTopLevel(); - for (int i = 0; i != args.length; ++i) { - Object arg = args[i]; - if (arg instanceof NativeJavaClass) { - ImporterTopLevel.importClass(result, (NativeJavaClass) arg); - } else if (arg instanceof NativeJavaPackage) { - ImporterTopLevel.importPackage(result, (NativeJavaPackage) arg); - } else { - throw Context.reportRuntimeErrorById( - "msg.not.class.not.pkg", Context.toString(arg)); - } - } - // set explicitly prototype and scope - // as otherwise in top scope mode BaseFunction.construct - // would keep them set to null. It also allow to use - // JavaImporter without new and still get properly - // initialized object. - result.setParentScope(scope); - result.setPrototype(this); - return result; - } - - private static Object js_importClass(Scriptable scope, Object[] args) { - for (int i = 0; i != args.length; i++) { - Object arg = args[i]; - if (!(arg instanceof NativeJavaClass)) { - throw Context.reportRuntimeErrorById("msg.not.class", Context.toString(arg)); - } - importClass(scope, (NativeJavaClass) arg); - } - return Undefined.instance; - } - - private static Object js_importPackage(ScriptableObject scope, Object[] args) { - for (int i = 0; i != args.length; i++) { - Object arg = args[i]; - if (!(arg instanceof NativeJavaPackage)) { - throw Context.reportRuntimeErrorById("msg.not.pkg", Context.toString(arg)); - } - importPackage(scope, (NativeJavaPackage) arg); - } - return Undefined.instance; - } - - private static void importPackage(ScriptableObject scope, NativeJavaPackage pkg) { - if (pkg == null) { - return; - } - synchronized (scope) { - ObjArray importedPackages = (ObjArray) scope.getAssociatedValue(AKEY); - if (importedPackages == null) { - importedPackages = new ObjArray(); - scope.associateValue(AKEY, importedPackages); - } - for (int j = 0; j != importedPackages.size(); j++) { - if (pkg.equals(importedPackages.get(j))) { - return; - } - } - importedPackages.add(pkg); - } - } - - private static void importClass(Scriptable scope, NativeJavaClass cl) { - String s = cl.getClassObject().getName(); - String n = s.substring(s.lastIndexOf('.') + 1); - Object val = scope.get(n, scope); - if (val != NOT_FOUND && val != cl) { - throw Context.reportRuntimeErrorById("msg.prop.defined", n); - } - // defineProperty(n, cl, DONTENUM); - scope.put(n, scope, cl); - } - - @Override - protected void initPrototypeId(int id) { - String s; - int arity; - switch (id) { - case Id_constructor: - arity = 0; - s = "constructor"; - break; - case Id_importClass: - arity = 1; - s = "importClass"; - break; - case Id_importPackage: - arity = 1; - s = "importPackage"; - break; - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - initPrototypeMethod(IMPORTER_TAG, id, s, arity); - } - - @Override - public Object execIdCall( - IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!f.hasTag(IMPORTER_TAG)) { - return super.execIdCall(f, cx, scope, thisObj, args); - } - int id = f.methodId(); - switch (id) { - case Id_constructor: - return js_construct(scope, args); - - case Id_importClass: - return js_importClass(realScope(scope, thisObj, f), args); - - case Id_importPackage: - return js_importPackage(realScope(scope, thisObj, f), args); - } - throw new IllegalArgumentException(String.valueOf(id)); - } - - private ScriptableObject realScope(Scriptable scope, Scriptable thisObj, IdFunctionObject f) { - if (topScopeFlag) { - // when used as top scope importPackage and importClass are global - // function that ignore thisObj. We use the the top level scope - // which might not be the same as 'this' when used shared scopes - thisObj = ScriptableObject.getTopLevelScope(scope); - } - return ensureType(thisObj, ScriptableObject.class, f); - } - - @Override - protected int findPrototypeId(String s) { - int id; - switch (s) { - case "constructor": - id = Id_constructor; - break; - case "importClass": - id = Id_importClass; - break; - case "importPackage": - id = Id_importPackage; - break; - default: - id = 0; - break; - } - return id; - } - - private static final int Id_constructor = 1, - Id_importClass = 2, - Id_importPackage = 3, - MAX_PROTOTYPE_ID = 3; - - private static final String AKEY = "importedPackages"; - private boolean topScopeFlag; -} diff --git a/src/main/java/org/mozilla/javascript/InterfaceAdapter.java b/src/main/java/org/mozilla/javascript/InterfaceAdapter.java deleted file mode 100644 index 1b2a86f4..00000000 --- a/src/main/java/org/mozilla/javascript/InterfaceAdapter.java +++ /dev/null @@ -1,175 +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.lang.reflect.Method; -import java.lang.reflect.Modifier; - -import java.util.HashSet; - -/** - * Adapter to use JS function as implementation of Java interfaces with - * single method or multiple methods with the same signature. - */ -public class InterfaceAdapter -{ - private final Object proxyHelper; - - /** - * Make glue object implementing interface cl that will - * call the supplied JS function when called. - * Only interfaces were all methods have the same signature is supported. - * - * @return The glue object or null if cl is not interface or - * has methods with different signatures. - */ - static Object create(Context cx, Class cl, ScriptableObject object) - { - if (!cl.isInterface()) throw new IllegalArgumentException(); - - Scriptable topScope = ScriptRuntime.getTopCallScope(cx); - ClassCache cache = ClassCache.get(topScope); - InterfaceAdapter adapter; - adapter = (InterfaceAdapter)cache.getInterfaceAdapter(cl); - ContextFactory cf = cx.getFactory(); - if (adapter == null) { - if (object instanceof Callable) { - Method[] methods = cl.getMethods(); - // Check if interface can be implemented by a single function. - // We allow this if the interface has only one method or multiple - // methods with the same name (in which case they'd result in - // the same function to be invoked anyway). - HashSet functionalMethodNames = new HashSet<>(); - HashSet defaultMethodNames = new HashSet<>(); - for (Method method : methods) { - // there are multiple methods in the interface we inspect - // only abstract ones, they must all have the same name. - if (isFunctionalMethodCandidate(method)) { - functionalMethodNames.add(method.getName()); - if (functionalMethodNames.size() > 1) { - break; - } - } else { - defaultMethodNames.add(method.getName()); - } - } - - boolean canConvert = - (functionalMethodNames.size() == 1) || - (functionalMethodNames.isEmpty() && defaultMethodNames.size() == 1); - // There is no abstract method or there are multiple methods. - if (!canConvert) { - if (functionalMethodNames.isEmpty() && defaultMethodNames.isEmpty()) { - throw Context.reportRuntimeErrorById( - "msg.no.empty.interface.conversion", - cl.getName()); - } else { - throw Context.reportRuntimeErrorById( - "msg.no.function.interface.conversion", - cl.getName()); - } - } - } - adapter = new InterfaceAdapter(cf, cl); - cache.cacheInterfaceAdapter(cl, adapter); - } - return VMBridge.instance.newInterfaceProxy( - adapter.proxyHelper, cf, adapter, object, topScope); - } - - /** - * We have to ignore java8 default methods and methods like 'equals', 'hashCode' - * and 'toString' as it occurs for example in the Comparator interface. - * - * @return true, if the function - */ - private static boolean isFunctionalMethodCandidate(Method method) { - if (method.getName().equals("equals") - || method.getName().equals("hashCode") - || method.getName().equals("toString")) { - // it should be safe to ignore them as there is also a special - // case for these methods in VMBridge_jdk18.newInterfaceProxy - return false; - } else { - return Modifier.isAbstract(method.getModifiers()); - } - } - - private InterfaceAdapter(ContextFactory cf, Class cl) - { - this.proxyHelper - = VMBridge.instance.getInterfaceProxyHelper( - cf, new Class[] { cl }); - } - - public Object invoke(ContextFactory cf, - final Object target, - final Scriptable topScope, - final Object thisObject, - final Method method, - final Object[] args) - { - return cf.call(cx -> invokeImpl(cx, target, topScope, thisObject, method, args)); - } - - Object invokeImpl(Context cx, - Object target, - Scriptable topScope, - Object thisObject, - Method method, - Object[] args) - { - Callable function; - if (target instanceof Callable) { - function = (Callable)target; - } else { - Scriptable s = (Scriptable)target; - String methodName = method.getName(); - Object value = ScriptableObject.getProperty(s, methodName); - if (value == Scriptable.NOT_FOUND) { - // We really should throw an error here, but for the sake of - // compatibility with JavaAdapter we silently ignore undefined - // methods. - Context.reportWarning(ScriptRuntime.getMessageById( - "msg.undefined.function.interface", methodName)); - Class resultType = method.getReturnType(); - if (resultType == Void.TYPE) { - return null; - } - return Context.jsToJava(null, resultType); - } - if (!(value instanceof Callable)) { - throw Context.reportRuntimeErrorById( - "msg.not.function.interface",methodName); - } - function = (Callable)value; - } - WrapFactory wf = cx.getWrapFactory(); - if (args == null) { - args = ScriptRuntime.emptyArgs; - } else { - for (int i = 0, N = args.length; i != N; ++i) { - Object arg = args[i]; - // neutralize wrap factory java primitive wrap feature - if (!(arg instanceof String || arg instanceof Number - || arg instanceof Boolean)) { - args[i] = wf.wrap(cx, topScope, arg, null); - } - } - } - Scriptable thisObj = wf.wrapAsJavaObject(cx, topScope, thisObject, null); - - Object result = function.call(cx, topScope, thisObj, args); - Class javaResultType = method.getReturnType(); - if (javaResultType == Void.TYPE) { - result = null; - } else { - result = Context.jsToJava(result, javaResultType); - } - return result; - } -} diff --git a/src/main/java/org/mozilla/javascript/InterpretedFunction.java b/src/main/java/org/mozilla/javascript/InterpretedFunction.java deleted file mode 100644 index 83a80ddb..00000000 --- a/src/main/java/org/mozilla/javascript/InterpretedFunction.java +++ /dev/null @@ -1,167 +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.debug.DebuggableScript; - -final class InterpretedFunction extends NativeFunction implements Script { - private static final long serialVersionUID = 541475680333911468L; - - InterpreterData idata; - SecurityController securityController; - Object securityDomain; - - private InterpretedFunction(InterpreterData idata, Object staticSecurityDomain) { - this.idata = idata; - - // Always get Context from the current thread to - // avoid security breaches via passing mangled Context instances - // with bogus SecurityController - Context cx = Context.getContext(); - SecurityController sc = cx.getSecurityController(); - Object dynamicDomain; - if (sc != null) { - dynamicDomain = sc.getDynamicSecurityDomain(staticSecurityDomain); - } else { - if (staticSecurityDomain != null) { - throw new IllegalArgumentException(); - } - dynamicDomain = null; - } - - this.securityController = sc; - this.securityDomain = dynamicDomain; - } - - private InterpretedFunction(InterpretedFunction parent, int index) { - this.idata = parent.idata.itsNestedFunctions[index]; - this.securityController = parent.securityController; - this.securityDomain = parent.securityDomain; - } - - /** Create script from compiled bytecode. */ - static InterpretedFunction createScript(InterpreterData idata, Object staticSecurityDomain) { - InterpretedFunction f; - f = new InterpretedFunction(idata, staticSecurityDomain); - return f; - } - - /** Create function compiled from Function(...) constructor. */ - static InterpretedFunction createFunction( - Context cx, Scriptable scope, InterpreterData idata, Object staticSecurityDomain) { - InterpretedFunction f; - f = new InterpretedFunction(idata, staticSecurityDomain); - f.initScriptFunction(cx, scope, f.idata.isES6Generator); - return f; - } - - /** Create function embedded in script or another function. */ - static InterpretedFunction createFunction( - Context cx, Scriptable scope, InterpretedFunction parent, int index) { - InterpretedFunction f = new InterpretedFunction(parent, index); - f.initScriptFunction(cx, scope, f.idata.isES6Generator); - return f; - } - - @Override - public String getFunctionName() { - return (idata.itsName == null) ? "" : idata.itsName; - } - - /** - * Calls the function. - * - * @param cx the current context - * @param scope the scope used for the call - * @param thisObj the value of "this" - * @param args function arguments. Must not be null. You can use {@link ScriptRuntime#emptyArgs} - * to pass empty arguments. - * @return the result of the function call. - */ - @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!ScriptRuntime.hasTopCall(cx)) { - return ScriptRuntime.doTopCall(this, cx, scope, thisObj, args, idata.isStrict); - } - return Interpreter.interpret(this, cx, scope, thisObj, args); - } - - @Override - public Object exec(Context cx, Scriptable scope) { - if (!isScript()) { - // Can only be applied to scripts - throw new IllegalStateException(); - } - Object ret; - if (!ScriptRuntime.hasTopCall(cx)) { - // It will go through "call" path. but they are equivalent - ret = - ScriptRuntime.doTopCall( - this, cx, scope, scope, ScriptRuntime.emptyArgs, idata.isStrict); - } else { - ret = Interpreter.interpret(this, cx, scope, scope, ScriptRuntime.emptyArgs); - } - cx.processMicrotasks(); - return ret; - } - - public boolean isScript() { - return idata.itsFunctionType == 0; - } - - @Override - public String getEncodedSource() { - return Interpreter.getEncodedSource(idata); - } - - @Override - public DebuggableScript getDebuggableView() { - return idata; - } - - @Override - public Object resumeGenerator( - Context cx, Scriptable scope, int operation, Object state, Object value) { - return Interpreter.resumeGenerator(cx, scope, operation, state, value); - } - - @Override - protected int getLanguageVersion() { - return idata.languageVersion; - } - - @Override - protected int getParamCount() { - return idata.argCount; - } - - @Override - protected int getParamAndVarCount() { - return idata.argNames.length; - } - - @Override - protected String getParamOrVarName(int index) { - return idata.argNames[index]; - } - - @Override - protected boolean getParamOrVarConst(int index) { - return idata.argIsConst[index]; - } - - boolean hasFunctionNamed(String name) { - for (int f = 0; f < idata.getFunctionCount(); f++) { - InterpreterData functionData = (InterpreterData) idata.getFunction(f); - if (!functionData.declaredAsFunctionExpression - && name.equals(functionData.getFunctionName())) { - return false; - } - } - return true; - } -} diff --git a/src/main/java/org/mozilla/javascript/Interpreter.java b/src/main/java/org/mozilla/javascript/Interpreter.java deleted file mode 100644 index bf20a7bb..00000000 --- a/src/main/java/org/mozilla/javascript/Interpreter.java +++ /dev/null @@ -1,3647 +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 static org.mozilla.javascript.UniqueTag.DOUBLE_MARK; - -import java.io.PrintStream; -import java.io.Serializable; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import org.mozilla.javascript.ScriptRuntime.NoSuchMethodShim; -import org.mozilla.javascript.ast.FunctionNode; -import org.mozilla.javascript.ast.ScriptNode; -import org.mozilla.javascript.debug.DebugFrame; - -public final class Interpreter extends Icode implements Evaluator { - // data for parsing - InterpreterData itsData; - - static final int EXCEPTION_TRY_START_SLOT = 0; - static final int EXCEPTION_TRY_END_SLOT = 1; - static final int EXCEPTION_HANDLER_SLOT = 2; - static final int EXCEPTION_TYPE_SLOT = 3; - static final int EXCEPTION_LOCAL_SLOT = 4; - static final int EXCEPTION_SCOPE_SLOT = 5; - // SLOT_SIZE: space for try start/end, handler, start, handler type, - // exception local and scope local - static final int EXCEPTION_SLOT_SIZE = 6; - - /** Class to hold data corresponding to one interpreted call stack frame. */ - private static class CallFrame implements Cloneable, Serializable { - private static final long serialVersionUID = -2843792508994958978L; - - // fields marked "final" in a comment are effectively final except when they're modified - // immediately after cloning. - - /*final*/ CallFrame parentFrame; - // amount of stack frames before this one on the interpretation stack - /*final*/ int frameIndex; - // If true indicates read-only frame that is a part of continuation - boolean frozen; - - final InterpretedFunction fnOrScript; - final InterpreterData idata; - - // Stack structure - // stack[0 <= i < localShift]: arguments and local variables - // stack[localShift <= i <= emptyStackTop]: used for local temporaries - // stack[emptyStackTop < i < stack.length]: stack data - // sDbl[i]: if stack[i] is UniqueTag.DOUBLE_MARK, sDbl[i] holds the number value - - /*final*/ Object[] stack; - /*final*/ int[] stackAttributes; - /*final*/ double[] sDbl; - - final CallFrame varSource; // defaults to this unless continuation frame - final int localShift; - final int emptyStackTop; - - final DebugFrame debuggerFrame; - final boolean useActivation; - boolean isContinuationsTopFrame; - - final Scriptable thisObj; - - // The values that change during interpretation - - Object result; - double resultDbl; - int pc; - int pcPrevBranch; - int pcSourceLineStart; - Scriptable scope; - - int savedStackTop; - int savedCallOp; - Object throwable; - - CallFrame( - Context cx, - Scriptable thisObj, - InterpretedFunction fnOrScript, - CallFrame parentFrame) { - idata = fnOrScript.idata; - - debuggerFrame = cx.debugger != null ? cx.debugger.getFrame(cx, idata) : null; - useActivation = debuggerFrame != null || idata.itsNeedsActivation; - - emptyStackTop = idata.itsMaxVars + idata.itsMaxLocals - 1; - this.fnOrScript = fnOrScript; - varSource = this; - localShift = idata.itsMaxVars; - this.thisObj = thisObj; - - this.parentFrame = parentFrame; - frameIndex = (parentFrame == null) ? 0 : parentFrame.frameIndex + 1; - if (frameIndex > cx.getMaximumInterpreterStackDepth()) { - throw Context.reportRuntimeError("Exceeded maximum stack depth"); - } - - // Initialize initial values of variables that change during - // interpretation. - result = Undefined.instance; - pcSourceLineStart = idata.firstLinePC; - - savedStackTop = emptyStackTop; - } - - void initializeArgs( - Context cx, - Scriptable callerScope, - Object[] args, - double[] argsDbl, - int argShift, - int argCount) { - if (useActivation) { - // Copy args to new array to pass to enterActivationFunction - // or debuggerFrame.onEnter - if (argsDbl != null) { - args = getArgsArray(args, argsDbl, argShift, argCount); - } - argShift = 0; - argsDbl = null; - } - - if (idata.itsFunctionType != 0) { - scope = fnOrScript.getParentScope(); - - if (useActivation) { - if (idata.itsFunctionType == FunctionNode.ARROW_FUNCTION) { - scope = - ScriptRuntime.createArrowFunctionActivation( - fnOrScript, scope, args, idata.isStrict); - } else { - scope = - ScriptRuntime.createFunctionActivation( - fnOrScript, scope, args, idata.isStrict); - } - } - } else { - scope = callerScope; - ScriptRuntime.initScript( - fnOrScript, thisObj, cx, scope, fnOrScript.idata.evalScriptFlag); - } - - if (idata.itsNestedFunctions != null) { - if (idata.itsFunctionType != 0 && !idata.itsNeedsActivation) Kit.codeBug(); - for (int i = 0; i < idata.itsNestedFunctions.length; i++) { - InterpreterData fdata = idata.itsNestedFunctions[i]; - if (fdata.itsFunctionType == FunctionNode.FUNCTION_STATEMENT) { - initFunction(cx, scope, fnOrScript, i); - } - } - } - - final int maxFrameArray = idata.itsMaxFrameArray; - // TODO: move this check into InterpreterData construction - if (maxFrameArray != emptyStackTop + idata.itsMaxStack + 1) Kit.codeBug(); - - // Initialize args, vars, locals and stack - - stack = new Object[maxFrameArray]; - stackAttributes = new int[maxFrameArray]; - sDbl = new double[maxFrameArray]; - - int varCount = idata.getParamAndVarCount(); - for (int i = 0; i < varCount; i++) { - if (idata.getParamOrVarConst(i)) stackAttributes[i] = ScriptableObject.CONST; - } - int definedArgs = idata.argCount; - if (definedArgs > argCount) { - definedArgs = argCount; - } - - // Fill the frame structure - - System.arraycopy(args, argShift, stack, 0, definedArgs); - if (argsDbl != null) { - System.arraycopy(argsDbl, argShift, sDbl, 0, definedArgs); - } - for (int i = definedArgs; i != idata.itsMaxVars; ++i) { - stack[i] = Undefined.instance; - } - } - - CallFrame cloneFrozen() { - if (!frozen) Kit.codeBug(); - - CallFrame copy; - try { - copy = (CallFrame) clone(); - } catch (CloneNotSupportedException ex) { - throw new IllegalStateException(); - } - - // clone stack but keep varSource to point to values - // from this frame to share variables. - - copy.stack = stack.clone(); - copy.stackAttributes = stackAttributes.clone(); - copy.sDbl = sDbl.clone(); - - copy.frozen = false; - return copy; - } - - @Override - public boolean equals(Object other) { - // Overridden for semantic equality comparison. These objects - // are typically exposed as NativeContinuation.implementation, - // comparing them allows establishing whether the continuations - // are semantically equal. - if (other instanceof CallFrame) { - // If the call is not within a Context with a top call, we force - // one. It is required as some objects within fully initialized - // global scopes (notably, XMLLibImpl) need to have a top scope - // in order to evaluate their attributes. - final Context cx = Context.enter(); - try { - if (ScriptRuntime.hasTopCall(cx)) { - return equalsInTopScope(other).booleanValue(); - } - final Scriptable top = ScriptableObject.getTopLevelScope(scope); - return ((Boolean) - ScriptRuntime.doTopCall( - (Callable) - (c, scope, thisObj, args) -> - equalsInTopScope(other), - cx, - top, - top, - ScriptRuntime.emptyArgs, - isStrictTopFrame())) - .booleanValue(); - } finally { - Context.exit(); - } - } - return false; - } - - @Override - public int hashCode() { - // Overridden for consistency with equals. - // Trying to strike a balance between speed of calculation and - // distribution. Not hashing stack variables as those could have - // unbounded computational cost and limit it to topmost 8 frames. - int depth = 0; - CallFrame f = this; - int h = 0; - do { - h = 31 * (31 * h + f.pc) + f.idata.icodeHashCode(); - f = f.parentFrame; - } while (f != null && depth++ < 8); - return h; - } - - private Boolean equalsInTopScope(Object other) { - return EqualObjectGraphs.withThreadLocal(eq -> equals(this, (CallFrame) other, eq)); - } - - private boolean isStrictTopFrame() { - CallFrame f = this; - for (; ; ) { - final CallFrame p = f.parentFrame; - if (p == null) { - return f.idata.isStrict; - } - f = p; - } - } - - private static Boolean equals(CallFrame f1, CallFrame f2, EqualObjectGraphs equal) { - // Iterative instead of recursive, as interpreter stack depth can - // be larger than JVM stack depth. - for (; ; ) { - if (f1 == f2) { - return Boolean.TRUE; - } else if (f1 == null || f2 == null) { - return Boolean.FALSE; - } else if (!f1.fieldsEqual(f2, equal)) { - return Boolean.FALSE; - } else { - f1 = f1.parentFrame; - f2 = f2.parentFrame; - } - } - } - - private boolean fieldsEqual(CallFrame other, EqualObjectGraphs equal) { - return frameIndex == other.frameIndex - && pc == other.pc - && compareIdata(idata, other.idata) - && equal.equalGraphs(varSource.stack, other.varSource.stack) - && Arrays.equals(varSource.sDbl, other.varSource.sDbl) - && equal.equalGraphs(thisObj, other.thisObj) - && equal.equalGraphs(fnOrScript, other.fnOrScript) - && equal.equalGraphs(scope, other.scope); - } - } - - private static boolean compareIdata(InterpreterData i1, InterpreterData i2) { - return i1 == i2 || Objects.equals(getEncodedSource(i1), getEncodedSource(i2)); - } - - private static final class ContinuationJump implements Serializable { - private static final long serialVersionUID = 7687739156004308247L; - - CallFrame capturedFrame; - CallFrame branchFrame; - Object result; - double resultDbl; - - ContinuationJump(NativeContinuation c, CallFrame current) { - this.capturedFrame = (CallFrame) c.getImplementation(); - if (this.capturedFrame == null || current == null) { - // Continuation and current execution does not share - // any frames if there is nothing to capture or - // if there is no currently executed frames - this.branchFrame = null; - } else { - // Search for branch frame where parent frame chains starting - // from captured and current meet. - CallFrame chain1 = this.capturedFrame; - CallFrame chain2 = current; - - // First work parents of chain1 or chain2 until the same - // frame depth. - int diff = chain1.frameIndex - chain2.frameIndex; - if (diff != 0) { - if (diff < 0) { - // swap to make sure that - // chain1.frameIndex > chain2.frameIndex and diff > 0 - chain1 = current; - chain2 = this.capturedFrame; - diff = -diff; - } - do { - chain1 = chain1.parentFrame; - } while (--diff != 0); - if (chain1.frameIndex != chain2.frameIndex) Kit.codeBug(); - } - - // Now walk parents in parallel until a shared frame is found - // or until the root is reached. - while (chain1 != chain2 && chain1 != null) { - chain1 = chain1.parentFrame; - chain2 = chain2.parentFrame; - } - - this.branchFrame = chain1; - if (this.branchFrame != null && !this.branchFrame.frozen) Kit.codeBug(); - } - } - } - - private static CallFrame captureFrameForGenerator(CallFrame frame) { - frame.frozen = true; - CallFrame result = frame.cloneFrozen(); - frame.frozen = false; - - // now isolate this frame from its previous context - result.parentFrame = null; - result.frameIndex = 0; - - return result; - } - - static { - // Checks for byte code consistencies, good compiler can eliminate them - - if (Token.LAST_BYTECODE_TOKEN > 127) { - String str = "Violation of Token.LAST_BYTECODE_TOKEN <= 127"; - System.err.println(str); - throw new IllegalStateException(str); - } - if (MIN_ICODE < -128) { - String str = "Violation of Interpreter.MIN_ICODE >= -128"; - System.err.println(str); - throw new IllegalStateException(str); - } - } - - @Override - public Object compile( - CompilerEnvirons compilerEnv, - ScriptNode tree, - String encodedSource, - boolean returnFunction) { - CodeGenerator cgen = new CodeGenerator(); - itsData = cgen.compile(compilerEnv, tree, encodedSource, returnFunction); - return itsData; - } - - @Override - public Script createScriptObject(Object bytecode, Object staticSecurityDomain) { - if (bytecode != itsData) { - Kit.codeBug(); - } - return InterpretedFunction.createScript(itsData, staticSecurityDomain); - } - - @Override - public void setEvalScriptFlag(Script script) { - ((InterpretedFunction) script).idata.evalScriptFlag = true; - } - - @Override - public Function createFunctionObject( - Context cx, Scriptable scope, Object bytecode, Object staticSecurityDomain) { - if (bytecode != itsData) { - Kit.codeBug(); - } - return InterpretedFunction.createFunction(cx, scope, itsData, staticSecurityDomain); - } - - private static int getShort(byte[] iCode, int pc) { - return (iCode[pc] << 8) | (iCode[pc + 1] & 0xFF); - } - - private static int getIndex(byte[] iCode, int pc) { - return ((iCode[pc] & 0xFF) << 8) | (iCode[pc + 1] & 0xFF); - } - - private static int getInt(byte[] iCode, int pc) { - return (iCode[pc] << 24) - | ((iCode[pc + 1] & 0xFF) << 16) - | ((iCode[pc + 2] & 0xFF) << 8) - | (iCode[pc + 3] & 0xFF); - } - - private static int getExceptionHandler(CallFrame frame, boolean onlyFinally) { - int[] exceptionTable = frame.idata.itsExceptionTable; - if (exceptionTable == null) { - // No exception handlers - return -1; - } - - // Icode switch in the interpreter increments PC immediately - // and it is necessary to subtract 1 from the saved PC - // to point it before the start of the next instruction. - int pc = frame.pc - 1; - - // OPT: use binary search - int best = -1, bestStart = 0, bestEnd = 0; - for (int i = 0; i != exceptionTable.length; i += EXCEPTION_SLOT_SIZE) { - int start = exceptionTable[i + EXCEPTION_TRY_START_SLOT]; - int end = exceptionTable[i + EXCEPTION_TRY_END_SLOT]; - if (!(start <= pc && pc < end)) { - continue; - } - if (onlyFinally && exceptionTable[i + EXCEPTION_TYPE_SLOT] != 1) { - continue; - } - if (best >= 0) { - // Since handlers always nest and they never have shared end - // although they can share start it is sufficient to compare - // handlers ends - if (bestEnd < end) { - continue; - } - // Check the above assumption - if (bestStart > start) Kit.codeBug(); // should be nested - if (bestEnd == end) Kit.codeBug(); // no ens sharing - } - best = i; - bestStart = start; - bestEnd = end; - } - return best; - } - - static void dumpICode(InterpreterData idata) { - if (!Token.printICode) { - return; - } - - byte iCode[] = idata.itsICode; - int iCodeLength = iCode.length; - String[] strings = idata.itsStringTable; - BigInteger[] bigInts = idata.itsBigIntTable; - PrintStream out = System.out; - out.println("ICode dump, for " + idata.itsName + ", length = " + iCodeLength); - out.println("MaxStack = " + idata.itsMaxStack); - - int indexReg = 0; - for (int pc = 0; pc < iCodeLength; ) { - out.flush(); - out.print(" [" + pc + "] "); - int token = iCode[pc]; - int icodeLength = bytecodeSpan(token); - String tname = Icode.bytecodeName(token); - int old_pc = pc; - ++pc; - switch (token) { - default: - if (icodeLength != 1) Kit.codeBug(); - out.println(tname); - break; - - case Icode_GOSUB: - case Token.GOTO: - case Token.IFEQ: - case Token.IFNE: - case Icode_IFEQ_POP: - case Icode_LEAVEDQ: - { - int newPC = pc + getShort(iCode, pc) - 1; - out.println(tname + " " + newPC); - pc += 2; - break; - } - case Icode_VAR_INC_DEC: - case Icode_NAME_INC_DEC: - case Icode_PROP_INC_DEC: - case Icode_ELEM_INC_DEC: - case Icode_REF_INC_DEC: - { - int incrDecrType = iCode[pc]; - out.println(tname + " " + incrDecrType); - ++pc; - break; - } - - case Icode_CALLSPECIAL: - { - int callType = iCode[pc] & 0xFF; - boolean isNew = (iCode[pc + 1] != 0); - int line = getIndex(iCode, pc + 2); - out.println( - tname + " " + callType + " " + isNew + " " + indexReg + " " + line); - pc += 4; - break; - } - - case Token.CATCH_SCOPE: - { - boolean afterFisrtFlag = (iCode[pc] != 0); - out.println(tname + " " + afterFisrtFlag); - ++pc; - } - break; - case Token.REGEXP: - out.println(tname + " " + idata.itsRegExpLiterals[indexReg]); - break; - case Token.OBJECTLIT: - case Icode_SPARE_ARRAYLIT: - out.println(tname + " " + idata.literalIds[indexReg]); - break; - case Icode_CLOSURE_EXPR: - case Icode_CLOSURE_STMT: - out.println(tname + " " + idata.itsNestedFunctions[indexReg]); - break; - case Token.CALL: - case Icode_TAIL_CALL: - case Token.REF_CALL: - case Token.NEW: - out.println(tname + ' ' + indexReg); - break; - case Token.THROW: - case Token.YIELD: - case Icode_YIELD_STAR: - case Icode_GENERATOR: - case Icode_GENERATOR_END: - case Icode_GENERATOR_RETURN: - { - int line = getIndex(iCode, pc); - out.println(tname + " : " + line); - pc += 2; - break; - } - case Icode_SHORTNUMBER: - { - int value = getShort(iCode, pc); - out.println(tname + " " + value); - pc += 2; - break; - } - case Icode_INTNUMBER: - { - int value = getInt(iCode, pc); - out.println(tname + " " + value); - pc += 4; - break; - } - case Token.NUMBER: - { - double value = idata.itsDoubleTable[indexReg]; - out.println(tname + " " + value); - break; - } - case Icode_LINE: - { - int line = getIndex(iCode, pc); - out.println(tname + " : " + line); - pc += 2; - break; - } - case Icode_REG_STR1: - { - String str = strings[0xFF & iCode[pc]]; - out.println(tname + " \"" + str + '"'); - ++pc; - break; - } - case Icode_REG_STR2: - { - String str = strings[getIndex(iCode, pc)]; - out.println(tname + " \"" + str + '"'); - pc += 2; - break; - } - case Icode_REG_STR4: - { - String str = strings[getInt(iCode, pc)]; - out.println(tname + " \"" + str + '"'); - pc += 4; - break; - } - case Icode_REG_IND_C0: - indexReg = 0; - out.println(tname); - break; - case Icode_REG_IND_C1: - indexReg = 1; - out.println(tname); - break; - case Icode_REG_IND_C2: - indexReg = 2; - out.println(tname); - break; - case Icode_REG_IND_C3: - indexReg = 3; - out.println(tname); - break; - case Icode_REG_IND_C4: - indexReg = 4; - out.println(tname); - break; - case Icode_REG_IND_C5: - indexReg = 5; - out.println(tname); - break; - case Icode_REG_IND1: - { - indexReg = 0xFF & iCode[pc]; - out.println(tname + " " + indexReg); - ++pc; - break; - } - case Icode_REG_IND2: - { - indexReg = getIndex(iCode, pc); - out.println(tname + " " + indexReg); - pc += 2; - break; - } - case Icode_REG_IND4: - { - indexReg = getInt(iCode, pc); - out.println(tname + " " + indexReg); - pc += 4; - break; - } - case Icode_GETVAR1: - case Icode_SETVAR1: - case Icode_SETCONSTVAR1: - indexReg = iCode[pc]; - out.println(tname + " " + indexReg); - ++pc; - break; - // TODO: Icode_REG_STR_C0-3 is not dump. I made this the same it. - case Icode_REG_BIGINT_C0: - case Icode_REG_BIGINT_C1: - case Icode_REG_BIGINT_C2: - case Icode_REG_BIGINT_C3: - Kit.codeBug(); - break; - case Icode_REG_BIGINT1: - { - BigInteger bigInt = bigInts[0xFF & iCode[pc]]; - out.println(tname + " " + bigInt.toString() + 'n'); - ++pc; - break; - } - case Icode_REG_BIGINT2: - { - BigInteger bigInt = bigInts[getIndex(iCode, pc)]; - out.println(tname + " " + bigInt.toString() + 'n'); - pc += 2; - break; - } - case Icode_REG_BIGINT4: - { - BigInteger bigInt = bigInts[getInt(iCode, pc)]; - out.println(tname + " " + bigInt.toString() + 'n'); - pc += 4; - break; - } - } - if (old_pc + icodeLength != pc) Kit.codeBug(); - } - - int[] table = idata.itsExceptionTable; - if (table != null) { - out.println("Exception handlers: " + table.length / EXCEPTION_SLOT_SIZE); - for (int i = 0; i != table.length; i += EXCEPTION_SLOT_SIZE) { - int tryStart = table[i + EXCEPTION_TRY_START_SLOT]; - int tryEnd = table[i + EXCEPTION_TRY_END_SLOT]; - int handlerStart = table[i + EXCEPTION_HANDLER_SLOT]; - int type = table[i + EXCEPTION_TYPE_SLOT]; - int exceptionLocal = table[i + EXCEPTION_LOCAL_SLOT]; - - out.println( - " tryStart=" - + tryStart - + " tryEnd=" - + tryEnd - + " handlerStart=" - + handlerStart - + " type=" - + (type == 0 ? "catch" : "finally") - + " exceptionLocal=" - + exceptionLocal); - } - } - out.flush(); - } - - private static int bytecodeSpan(int bytecode) { - switch (bytecode) { - case Token.THROW: - case Token.YIELD: - case Icode_YIELD_STAR: - case Icode_GENERATOR: - case Icode_GENERATOR_END: - case Icode_GENERATOR_RETURN: - // source line - return 1 + 2; - - case Icode_GOSUB: - case Token.GOTO: - case Token.IFEQ: - case Token.IFNE: - case Icode_IFEQ_POP: - case Icode_LEAVEDQ: - // target pc offset - return 1 + 2; - - case Icode_CALLSPECIAL: - // call type - // is new - // line number - return 1 + 1 + 1 + 2; - - case Token.CATCH_SCOPE: - // scope flag - return 1 + 1; - - case Icode_VAR_INC_DEC: - case Icode_NAME_INC_DEC: - case Icode_PROP_INC_DEC: - case Icode_ELEM_INC_DEC: - case Icode_REF_INC_DEC: - // type of ++/-- - return 1 + 1; - - case Icode_SHORTNUMBER: - // short number - return 1 + 2; - - case Icode_INTNUMBER: - // int number - return 1 + 4; - - case Icode_REG_IND1: - // ubyte index - return 1 + 1; - - case Icode_REG_IND2: - // ushort index - return 1 + 2; - - case Icode_REG_IND4: - // int index - return 1 + 4; - - case Icode_REG_STR1: - // ubyte string index - return 1 + 1; - - case Icode_REG_STR2: - // ushort string index - return 1 + 2; - - case Icode_REG_STR4: - // int string index - return 1 + 4; - - case Icode_GETVAR1: - case Icode_SETVAR1: - case Icode_SETCONSTVAR1: - // byte var index - return 1 + 1; - - case Icode_LINE: - // line number - return 1 + 2; - } - if (!validBytecode(bytecode)) throw Kit.codeBug(); - return 1; - } - - static int[] getLineNumbers(InterpreterData data) { - UintMap presentLines = new UintMap(); - - byte[] iCode = data.itsICode; - int iCodeLength = iCode.length; - for (int pc = 0; pc != iCodeLength; ) { - int bytecode = iCode[pc]; - int span = bytecodeSpan(bytecode); - if (bytecode == Icode_LINE) { - if (span != 3) Kit.codeBug(); - int line = getIndex(iCode, pc + 1); - presentLines.put(line, 0); - } - pc += span; - } - - return presentLines.getKeys(); - } - - @Override - public void captureStackInfo(RhinoException ex) { - Context cx = Context.getCurrentContext(); - if (cx == null || cx.lastInterpreterFrame == null) { - // No interpreter invocations - ex.interpreterStackInfo = null; - ex.interpreterLineData = null; - return; - } - // has interpreter frame on the stack - CallFrame[] array; - if (cx.previousInterpreterInvocations == null - || cx.previousInterpreterInvocations.size() == 0) { - array = new CallFrame[1]; - } else { - int previousCount = cx.previousInterpreterInvocations.size(); - if (cx.previousInterpreterInvocations.peek() == cx.lastInterpreterFrame) { - // It can happen if exception was generated after - // frame was pushed to cx.previousInterpreterInvocations - // but before assignment to cx.lastInterpreterFrame. - // In this case frames has to be ignored. - --previousCount; - } - array = new CallFrame[previousCount + 1]; - cx.previousInterpreterInvocations.toArray(array); - } - array[array.length - 1] = (CallFrame) cx.lastInterpreterFrame; - - int interpreterFrameCount = 0; - for (int i = 0; i != array.length; ++i) { - interpreterFrameCount += 1 + array[i].frameIndex; - } - - int[] linePC = new int[interpreterFrameCount]; - // Fill linePC with pc positions from all interpreter frames. - // Start from the most nested frame - int linePCIndex = interpreterFrameCount; - for (int i = array.length; i != 0; ) { - --i; - CallFrame frame = array[i]; - while (frame != null) { - --linePCIndex; - linePC[linePCIndex] = frame.pcSourceLineStart; - frame = frame.parentFrame; - } - } - if (linePCIndex != 0) Kit.codeBug(); - - ex.interpreterStackInfo = array; - ex.interpreterLineData = linePC; - } - - @Override - public String getSourcePositionFromStack(Context cx, int[] linep) { - CallFrame frame = (CallFrame) cx.lastInterpreterFrame; - InterpreterData idata = frame.idata; - if (frame.pcSourceLineStart >= 0) { - linep[0] = getIndex(idata.itsICode, frame.pcSourceLineStart); - } else { - linep[0] = 0; - } - return idata.itsSourceFile; - } - - @Override - public String getPatchedStack(RhinoException ex, String nativeStackTrace) { - String tag = "org.mozilla.javascript.Interpreter.interpretLoop"; - StringBuilder sb = new StringBuilder(nativeStackTrace.length() + 1000); - String lineSeparator = SecurityUtilities.getSystemProperty("line.separator"); - - CallFrame[] array = (CallFrame[]) ex.interpreterStackInfo; - int[] linePC = ex.interpreterLineData; - int arrayIndex = array.length; - int linePCIndex = linePC.length; - int offset = 0; - while (arrayIndex != 0) { - --arrayIndex; - int pos = nativeStackTrace.indexOf(tag, offset); - if (pos < 0) { - break; - } - - // Skip tag length - pos += tag.length(); - // Skip until the end of line - for (; pos != nativeStackTrace.length(); ++pos) { - char c = nativeStackTrace.charAt(pos); - if (c == '\n' || c == '\r') { - break; - } - } - sb.append(nativeStackTrace.substring(offset, pos)); - offset = pos; - - CallFrame frame = array[arrayIndex]; - while (frame != null) { - if (linePCIndex == 0) Kit.codeBug(); - --linePCIndex; - InterpreterData idata = frame.idata; - sb.append(lineSeparator); - sb.append("\tat script"); - if (idata.itsName != null && idata.itsName.length() != 0) { - sb.append('.'); - sb.append(idata.itsName); - } - sb.append('('); - sb.append(idata.itsSourceFile); - int pc = linePC[linePCIndex]; - if (pc >= 0) { - // Include line info only if available - sb.append(':'); - sb.append(getIndex(idata.itsICode, pc)); - } - sb.append(')'); - frame = frame.parentFrame; - } - } - sb.append(nativeStackTrace.substring(offset)); - - return sb.toString(); - } - - @Override - public List getScriptStack(RhinoException ex) { - ScriptStackElement[][] stack = getScriptStackElements(ex); - List list = new ArrayList(stack.length); - String lineSeparator = SecurityUtilities.getSystemProperty("line.separator"); - for (ScriptStackElement[] group : stack) { - StringBuilder sb = new StringBuilder(); - for (ScriptStackElement elem : group) { - elem.renderJavaStyle(sb); - sb.append(lineSeparator); - } - list.add(sb.toString()); - } - return list; - } - - public ScriptStackElement[][] getScriptStackElements(RhinoException ex) { - if (ex.interpreterStackInfo == null) { - return null; - } - - List list = new ArrayList(); - - CallFrame[] array = (CallFrame[]) ex.interpreterStackInfo; - int[] linePC = ex.interpreterLineData; - int arrayIndex = array.length; - int linePCIndex = linePC.length; - while (arrayIndex != 0) { - --arrayIndex; - CallFrame frame = array[arrayIndex]; - List group = new ArrayList(); - while (frame != null) { - if (linePCIndex == 0) Kit.codeBug(); - --linePCIndex; - InterpreterData idata = frame.idata; - String fileName = idata.itsSourceFile; - String functionName = null; - int lineNumber = -1; - int pc = linePC[linePCIndex]; - if (pc >= 0) { - lineNumber = getIndex(idata.itsICode, pc); - } - if (idata.itsName != null && idata.itsName.length() != 0) { - functionName = idata.itsName; - } - frame = frame.parentFrame; - group.add(new ScriptStackElement(fileName, functionName, lineNumber)); - } - list.add(group.toArray(new ScriptStackElement[group.size()])); - } - return list.toArray(new ScriptStackElement[list.size()][]); - } - - static String getEncodedSource(InterpreterData idata) { - if (idata.encodedSource == null) { - return null; - } - return idata.encodedSource.substring(idata.encodedSourceStart, idata.encodedSourceEnd); - } - - private static void initFunction( - Context cx, Scriptable scope, InterpretedFunction parent, int index) { - InterpretedFunction fn; - fn = InterpretedFunction.createFunction(cx, scope, parent, index); - ScriptRuntime.initFunction( - cx, scope, fn, fn.idata.itsFunctionType, parent.idata.evalScriptFlag); - } - - static Object interpret( - InterpretedFunction ifun, - Context cx, - Scriptable scope, - Scriptable thisObj, - Object[] args) { - if (!ScriptRuntime.hasTopCall(cx)) Kit.codeBug(); - - if (cx.interpreterSecurityDomain != ifun.securityDomain) { - Object savedDomain = cx.interpreterSecurityDomain; - cx.interpreterSecurityDomain = ifun.securityDomain; - try { - return ifun.securityController.callWithDomain( - ifun.securityDomain, cx, ifun, scope, thisObj, args); - } finally { - cx.interpreterSecurityDomain = savedDomain; - } - } - - CallFrame frame = initFrame(cx, scope, thisObj, args, null, 0, args.length, ifun, null); - frame.isContinuationsTopFrame = cx.isContinuationsTopCall; - cx.isContinuationsTopCall = false; - - return interpretLoop(cx, frame, null); - } - - static class GeneratorState { - GeneratorState(int operation, Object value) { - this.operation = operation; - this.value = value; - } - - int operation; - Object value; - RuntimeException returnedException; - } - - public static Object resumeGenerator( - Context cx, Scriptable scope, int operation, Object savedState, Object value) { - CallFrame frame = (CallFrame) savedState; - GeneratorState generatorState = new GeneratorState(operation, value); - if (operation == NativeGenerator.GENERATOR_CLOSE) { - try { - return interpretLoop(cx, frame, generatorState); - } catch (RuntimeException e) { - // Only propagate exceptions other than closingException - if (e != value) throw e; - } - return Undefined.instance; - } - Object result = interpretLoop(cx, frame, generatorState); - if (generatorState.returnedException != null) throw generatorState.returnedException; - return result; - } - - public static Object restartContinuation( - NativeContinuation c, Context cx, Scriptable scope, Object[] args) { - if (!ScriptRuntime.hasTopCall(cx)) { - return ScriptRuntime.doTopCall(c, cx, scope, null, args, cx.isTopLevelStrict); - } - - Object arg; - if (args.length == 0) { - arg = Undefined.instance; - } else { - arg = args[0]; - } - - CallFrame capturedFrame = (CallFrame) c.getImplementation(); - if (capturedFrame == null) { - // No frames to restart - return arg; - } - - ContinuationJump cjump = new ContinuationJump(c, null); - - cjump.result = arg; - return interpretLoop(cx, null, cjump); - } - - private static Object interpretLoop(Context cx, CallFrame frame, Object throwable) { - // throwable holds exception object to rethrow or catch - // It is also used for continuation restart in which case - // it holds ContinuationJump - - final Object DBL_MRK = DOUBLE_MARK; - final Object undefined = Undefined.instance; - - final boolean instructionCounting = (cx.instructionThreshold != 0); - // arbitrary number to add to instructionCount when calling - // other functions - final int INVOCATION_COST = 100; - // arbitrary exception cost for instruction counting - final int EXCEPTION_COST = 100; - - String stringReg = null; - BigInteger bigIntReg = null; - int indexReg = -1; - - if (cx.lastInterpreterFrame != null) { - // save the top frame from the previous interpretLoop - // invocation on the stack - if (cx.previousInterpreterInvocations == null) { - cx.previousInterpreterInvocations = new ObjArray(); - } - cx.previousInterpreterInvocations.push(cx.lastInterpreterFrame); - } - - // When restarting continuation throwable is not null and to jump - // to the code that rewind continuation state indexReg should be set - // to -1. - // With the normal call throwable == null and indexReg == -1 allows to - // catch bugs with using indeReg to access array elements before - // initializing indexReg. - - GeneratorState generatorState = null; - if (throwable != null) { - if (throwable instanceof GeneratorState) { - generatorState = (GeneratorState) throwable; - - // reestablish this call frame - enterFrame(cx, frame, ScriptRuntime.emptyArgs, true); - throwable = null; - } else if (!(throwable instanceof ContinuationJump)) { - // It should be continuation - Kit.codeBug(); - } - } - - Object interpreterResult = null; - double interpreterResultDbl = 0.0; - - StateLoop: - for (; ; ) { - withoutExceptions: - try { - - if (throwable != null) { - // Need to return both 'frame' and 'throwable' from - // 'processThrowable', so just added a 'throwable' - // member in 'frame'. - frame = processThrowable(cx, throwable, frame, indexReg, instructionCounting); - throwable = frame.throwable; - frame.throwable = null; - } else { - if (generatorState == null && frame.frozen) Kit.codeBug(); - } - - // Use local variables for constant values in frame - // for faster access - Object[] stack = frame.stack; - double[] sDbl = frame.sDbl; - Object[] vars = frame.varSource.stack; - double[] varDbls = frame.varSource.sDbl; - int[] varAttributes = frame.varSource.stackAttributes; - byte[] iCode = frame.idata.itsICode; - String[] strings = frame.idata.itsStringTable; - BigInteger[] bigInts = frame.idata.itsBigIntTable; - - // Use local for stackTop as well. Since execption handlers - // can only exist at statement level where stack is empty, - // it is necessary to save/restore stackTop only across - // function calls and normal returns. - int stackTop = frame.savedStackTop; - - // Store new frame in cx which is used for error reporting etc. - cx.lastInterpreterFrame = frame; - - Loop: - for (; ; ) { - - // Exception handler assumes that PC is already incremented - // pass the instruction start when it searches the - // exception handler - int op = iCode[frame.pc++]; - jumplessRun: - { - - // Back indent to ease implementation reading - switch (op) { - case Icode_GENERATOR: - { - if (!frame.frozen) { - // First time encountering this opcode: create new generator - // object and return - frame.pc--; // we want to come back here when we resume - CallFrame generatorFrame = captureFrameForGenerator(frame); - generatorFrame.frozen = true; - if (cx.getLanguageVersion() >= Context.VERSION_ES6) { - frame.result = - new ES6Generator( - frame.scope, - generatorFrame.fnOrScript, - generatorFrame); - } else { - frame.result = - new NativeGenerator( - frame.scope, - generatorFrame.fnOrScript, - generatorFrame); - } - break Loop; - } - // We are now resuming execution. Fall through to YIELD case. - } - // fall through... - case Token.YIELD: - case Icode_YIELD_STAR: - { - if (!frame.frozen) { - return freezeGenerator( - cx, - frame, - stackTop, - generatorState, - op == Icode_YIELD_STAR); - } - Object obj = thawGenerator(frame, stackTop, generatorState, op); - if (obj != Scriptable.NOT_FOUND) { - throwable = obj; - break withoutExceptions; - } - continue Loop; - } - case Icode_GENERATOR_END: - { - // throw StopIteration - frame.frozen = true; - int sourceLine = getIndex(iCode, frame.pc); - generatorState.returnedException = - new JavaScriptException( - NativeIterator.getStopIterationObject( - frame.scope), - frame.idata.itsSourceFile, - sourceLine); - break Loop; - } - case Icode_GENERATOR_RETURN: - { - // throw StopIteration with the value of "return" - frame.frozen = true; - frame.result = stack[stackTop]; - frame.resultDbl = sDbl[stackTop]; - --stackTop; - - NativeIterator.StopIteration si = - new NativeIterator.StopIteration( - (frame.result == DOUBLE_MARK) - ? Double.valueOf(frame.resultDbl) - : frame.result); - - int sourceLine = getIndex(iCode, frame.pc); - generatorState.returnedException = - new JavaScriptException( - si, frame.idata.itsSourceFile, sourceLine); - break Loop; - } - case Token.THROW: - { - Object value = stack[stackTop]; - if (value == DBL_MRK) - value = ScriptRuntime.wrapNumber(sDbl[stackTop]); - --stackTop; - - int sourceLine = getIndex(iCode, frame.pc); - throwable = - new JavaScriptException( - value, frame.idata.itsSourceFile, sourceLine); - break withoutExceptions; - } - case Token.RETHROW: - { - indexReg += frame.localShift; - throwable = stack[indexReg]; - break withoutExceptions; - } - case Token.GE: - case Token.LE: - case Token.GT: - case Token.LT: - { - stackTop = doCompare(frame, op, stack, sDbl, stackTop); - continue Loop; - } - case Token.IN: - case Token.INSTANCEOF: - { - stackTop = doInOrInstanceof(cx, op, stack, sDbl, stackTop); - continue Loop; - } - case Token.EQ: - case Token.NE: - { - --stackTop; - boolean valBln = doEquals(stack, sDbl, stackTop); - valBln ^= (op == Token.NE); - stack[stackTop] = ScriptRuntime.wrapBoolean(valBln); - continue Loop; - } - case Token.SHEQ: - case Token.SHNE: - { - --stackTop; - boolean valBln = doShallowEquals(stack, sDbl, stackTop); - valBln ^= (op == Token.SHNE); - stack[stackTop] = ScriptRuntime.wrapBoolean(valBln); - continue Loop; - } - case Token.IFNE: - if (stack_boolean(frame, stackTop--)) { - frame.pc += 2; - continue Loop; - } - break jumplessRun; - case Token.IFEQ: - if (!stack_boolean(frame, stackTop--)) { - frame.pc += 2; - continue Loop; - } - break jumplessRun; - case Icode_IFEQ_POP: - if (!stack_boolean(frame, stackTop--)) { - frame.pc += 2; - continue Loop; - } - stack[stackTop--] = null; - break jumplessRun; - case Token.GOTO: - break jumplessRun; - case Icode_GOSUB: - ++stackTop; - stack[stackTop] = DBL_MRK; - sDbl[stackTop] = frame.pc + 2; - break jumplessRun; - case Icode_STARTSUB: - if (stackTop == frame.emptyStackTop + 1) { - // Call from Icode_GOSUB: store return PC address in the local - indexReg += frame.localShift; - stack[indexReg] = stack[stackTop]; - sDbl[indexReg] = sDbl[stackTop]; - --stackTop; - } else { - // Call from exception handler: exception object is already - // stored - // in the local - if (stackTop != frame.emptyStackTop) Kit.codeBug(); - } - continue Loop; - case Icode_RETSUB: - { - // indexReg: local to store return address - if (instructionCounting) { - addInstructionCount(cx, frame, 0); - } - indexReg += frame.localShift; - Object value = stack[indexReg]; - if (value != DBL_MRK) { - // Invocation from exception handler, restore object to - // rethrow - throwable = value; - break withoutExceptions; - } - // Normal return from GOSUB - frame.pc = (int) sDbl[indexReg]; - if (instructionCounting) { - frame.pcPrevBranch = frame.pc; - } - continue Loop; - } - case Icode_POP: - stack[stackTop] = null; - stackTop--; - continue Loop; - case Icode_POP_RESULT: - frame.result = stack[stackTop]; - frame.resultDbl = sDbl[stackTop]; - stack[stackTop] = null; - --stackTop; - continue Loop; - case Icode_DUP: - stack[stackTop + 1] = stack[stackTop]; - sDbl[stackTop + 1] = sDbl[stackTop]; - stackTop++; - continue Loop; - case Icode_DUP2: - stack[stackTop + 1] = stack[stackTop - 1]; - sDbl[stackTop + 1] = sDbl[stackTop - 1]; - stack[stackTop + 2] = stack[stackTop]; - sDbl[stackTop + 2] = sDbl[stackTop]; - stackTop += 2; - continue Loop; - case Icode_SWAP: - { - Object o = stack[stackTop]; - stack[stackTop] = stack[stackTop - 1]; - stack[stackTop - 1] = o; - double d = sDbl[stackTop]; - sDbl[stackTop] = sDbl[stackTop - 1]; - sDbl[stackTop - 1] = d; - continue Loop; - } - case Token.RETURN: - frame.result = stack[stackTop]; - frame.resultDbl = sDbl[stackTop]; - --stackTop; - break Loop; - case Token.RETURN_RESULT: - break Loop; - case Icode_RETUNDEF: - frame.result = undefined; - break Loop; - case Token.BITNOT: - { - stackTop = doBitNOT(frame, stack, sDbl, stackTop); - continue Loop; - } - case Token.BITAND: - case Token.BITOR: - case Token.BITXOR: - case Token.LSH: - case Token.RSH: - { - stackTop = doBitOp(frame, op, stack, sDbl, stackTop); - continue Loop; - } - case Token.URSH: - { - double lDbl = stack_double(frame, stackTop - 1); - int rIntValue = stack_int32(frame, stackTop) & 0x1F; - stack[--stackTop] = DBL_MRK; - sDbl[stackTop] = ScriptRuntime.toUint32(lDbl) >>> rIntValue; - continue Loop; - } - case Token.POS: - { - double rDbl = stack_double(frame, stackTop); - stack[stackTop] = DBL_MRK; - sDbl[stackTop] = rDbl; - continue Loop; - } - case Token.NEG: - { - Number rNum = stack_numeric(frame, stackTop); - Number rNegNum = ScriptRuntime.negate(rNum); - if (rNegNum instanceof BigInteger) { - stack[stackTop] = rNegNum; - } else { - stack[stackTop] = DBL_MRK; - sDbl[stackTop] = rNegNum.doubleValue(); - } - continue Loop; - } - case Token.ADD: - --stackTop; - doAdd(stack, sDbl, stackTop, cx); - continue Loop; - case Token.SUB: - case Token.MUL: - case Token.DIV: - case Token.MOD: - case Token.EXP: - { - stackTop = doArithmetic(frame, op, stack, sDbl, stackTop); - continue Loop; - } - case Token.NOT: - stack[stackTop] = - ScriptRuntime.wrapBoolean(!stack_boolean(frame, stackTop)); - continue Loop; - case Token.BINDNAME: - stack[++stackTop] = ScriptRuntime.bind(cx, frame.scope, stringReg); - continue Loop; - case Token.STRICT_SETNAME: - case Token.SETNAME: - { - Object rhs = stack[stackTop]; - if (rhs == DBL_MRK) - rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); - --stackTop; - Scriptable lhs = (Scriptable) stack[stackTop]; - stack[stackTop] = - op == Token.SETNAME - ? ScriptRuntime.setName( - lhs, rhs, cx, frame.scope, stringReg) - : ScriptRuntime.strictSetName( - lhs, rhs, cx, frame.scope, stringReg); - continue Loop; - } - case Icode_SETCONST: - { - Object rhs = stack[stackTop]; - if (rhs == DBL_MRK) - rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); - --stackTop; - Scriptable lhs = (Scriptable) stack[stackTop]; - stack[stackTop] = - ScriptRuntime.setConst(lhs, rhs, cx, stringReg); - continue Loop; - } - case Token.DELPROP: - case Icode_DELNAME: - { - stackTop = doDelName(cx, frame, op, stack, sDbl, stackTop); - continue Loop; - } - case Token.GETPROPNOWARN: - { - Object lhs = stack[stackTop]; - if (lhs == DBL_MRK) - lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); - stack[stackTop] = - ScriptRuntime.getObjectPropNoWarn( - lhs, stringReg, cx, frame.scope); - continue Loop; - } - case Token.GETPROP: - { - Object lhs = stack[stackTop]; - if (lhs == DBL_MRK) - lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); - stack[stackTop] = - ScriptRuntime.getObjectProp( - lhs, stringReg, cx, frame.scope); - continue Loop; - } - case Token.SETPROP: - { - Object rhs = stack[stackTop]; - if (rhs == DBL_MRK) - rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); - --stackTop; - Object lhs = stack[stackTop]; - if (lhs == DBL_MRK) - lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); - stack[stackTop] = - ScriptRuntime.setObjectProp( - lhs, stringReg, rhs, cx, frame.scope); - continue Loop; - } - case Icode_PROP_INC_DEC: - { - Object lhs = stack[stackTop]; - if (lhs == DBL_MRK) - lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); - stack[stackTop] = - ScriptRuntime.propIncrDecr( - lhs, - stringReg, - cx, - frame.scope, - iCode[frame.pc]); - ++frame.pc; - continue Loop; - } - case Token.GETELEM: - { - stackTop = doGetElem(cx, frame, stack, sDbl, stackTop); - continue Loop; - } - case Token.SETELEM: - { - stackTop = doSetElem(cx, frame, stack, sDbl, stackTop); - continue Loop; - } - case Icode_ELEM_INC_DEC: - { - stackTop = - doElemIncDec(cx, frame, iCode, stack, sDbl, stackTop); - continue Loop; - } - case Token.GET_REF: - { - Ref ref = (Ref) stack[stackTop]; - stack[stackTop] = ScriptRuntime.refGet(ref, cx); - continue Loop; - } - case Token.SET_REF: - { - Object value = stack[stackTop]; - if (value == DBL_MRK) - value = ScriptRuntime.wrapNumber(sDbl[stackTop]); - --stackTop; - Ref ref = (Ref) stack[stackTop]; - stack[stackTop] = - ScriptRuntime.refSet(ref, value, cx, frame.scope); - continue Loop; - } - case Token.DEL_REF: - { - Ref ref = (Ref) stack[stackTop]; - stack[stackTop] = ScriptRuntime.refDel(ref, cx); - continue Loop; - } - case Icode_REF_INC_DEC: - { - Ref ref = (Ref) stack[stackTop]; - stack[stackTop] = - ScriptRuntime.refIncrDecr( - ref, cx, frame.scope, iCode[frame.pc]); - ++frame.pc; - continue Loop; - } - case Token.LOCAL_LOAD: - ++stackTop; - indexReg += frame.localShift; - stack[stackTop] = stack[indexReg]; - sDbl[stackTop] = sDbl[indexReg]; - continue Loop; - case Icode_LOCAL_CLEAR: - indexReg += frame.localShift; - stack[indexReg] = null; - continue Loop; - case Icode_NAME_AND_THIS: - // stringReg: name - ++stackTop; - stack[stackTop] = - ScriptRuntime.getNameFunctionAndThis( - stringReg, cx, frame.scope); - ++stackTop; - stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); - continue Loop; - case Icode_PROP_AND_THIS: - { - Object obj = stack[stackTop]; - if (obj == DBL_MRK) - obj = ScriptRuntime.wrapNumber(sDbl[stackTop]); - // stringReg: property - stack[stackTop] = - ScriptRuntime.getPropFunctionAndThis( - obj, stringReg, cx, frame.scope); - ++stackTop; - stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); - continue Loop; - } - case Icode_ELEM_AND_THIS: - { - Object obj = stack[stackTop - 1]; - if (obj == DBL_MRK) - obj = ScriptRuntime.wrapNumber(sDbl[stackTop - 1]); - Object id = stack[stackTop]; - if (id == DBL_MRK) - id = ScriptRuntime.wrapNumber(sDbl[stackTop]); - stack[stackTop - 1] = - ScriptRuntime.getElemFunctionAndThis( - obj, id, cx, frame.scope); - stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); - continue Loop; - } - case Icode_VALUE_AND_THIS: - { - Object value = stack[stackTop]; - if (value == DBL_MRK) - value = ScriptRuntime.wrapNumber(sDbl[stackTop]); - stack[stackTop] = - ScriptRuntime.getValueFunctionAndThis(value, cx); - ++stackTop; - stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); - continue Loop; - } - case Icode_CALLSPECIAL: - { - if (instructionCounting) { - cx.instructionCount += INVOCATION_COST; - } - stackTop = - doCallSpecial( - cx, frame, stack, sDbl, stackTop, iCode, - indexReg); - continue Loop; - } - case Token.CALL: - case Icode_TAIL_CALL: - case Token.REF_CALL: - { - if (instructionCounting) { - cx.instructionCount += INVOCATION_COST; - } - // stack change: function thisObj arg0 .. argN -> result - // indexReg: number of arguments - stackTop -= 1 + indexReg; - - // CALL generation ensures that fun and funThisObj - // are already Scriptable and Callable objects respectively - Callable fun = (Callable) stack[stackTop]; - Scriptable funThisObj = (Scriptable) stack[stackTop + 1]; - if (op == Token.REF_CALL) { - Object[] outArgs = - getArgsArray(stack, sDbl, stackTop + 2, indexReg); - stack[stackTop] = - ScriptRuntime.callRef( - fun, funThisObj, - outArgs, cx); - continue Loop; - } - Scriptable calleeScope = frame.scope; - if (frame.useActivation) { - calleeScope = - ScriptableObject.getTopLevelScope(frame.scope); - } - if (fun instanceof InterpretedFunction) { - InterpretedFunction ifun = (InterpretedFunction) fun; - if (frame.fnOrScript.securityDomain - == ifun.securityDomain) { - CallFrame callParentFrame = frame; - if (op == Icode_TAIL_CALL) { - // In principle tail call can re-use the current - // frame and its stack arrays but it is hard to - // do properly. Any exceptions that can legally - // happen during frame re-initialization including - // StackOverflowException during innocent looking - // System.arraycopy may leave the current frame - // data corrupted leading to undefined behaviour - // in the catch code bellow that unwinds JS stack - // on exceptions. Then there is issue about frame - // release - // end exceptions there. - // To avoid frame allocation a released frame - // can be cached for re-use which would also benefit - // non-tail calls but it is not clear that this - // caching - // would gain in performance due to potentially - // bad interaction with GC. - callParentFrame = frame.parentFrame; - // Release the current frame. See Bug #344501 to see - // why - // it is being done here. - exitFrame(cx, frame, null); - } - CallFrame calleeFrame = - initFrame( - cx, - calleeScope, - funThisObj, - stack, - sDbl, - stackTop + 2, - indexReg, - ifun, - callParentFrame); - if (op != Icode_TAIL_CALL) { - frame.savedStackTop = stackTop; - frame.savedCallOp = op; - } - frame = calleeFrame; - continue StateLoop; - } - } - - if (fun instanceof NativeContinuation) { - // Jump to the captured continuation - ContinuationJump cjump; - cjump = - new ContinuationJump( - (NativeContinuation) fun, frame); - - // continuation result is the first argument if any - // of continuation call - if (indexReg == 0) { - cjump.result = undefined; - } else { - cjump.result = stack[stackTop + 2]; - cjump.resultDbl = sDbl[stackTop + 2]; - } - - // Start the real unwind job - throwable = cjump; - break withoutExceptions; - } - - if (fun instanceof IdFunctionObject) { - IdFunctionObject ifun = (IdFunctionObject) fun; - if (NativeContinuation.isContinuationConstructor(ifun)) { - frame.stack[stackTop] = - captureContinuation( - cx, frame.parentFrame, false); - continue Loop; - } - // Bug 405654 -- make best effort to keep Function.apply and - // Function.call within this interpreter loop invocation - if (BaseFunction.isApplyOrCall(ifun)) { - Callable applyCallable = - ScriptRuntime.getCallable(funThisObj); - if (applyCallable instanceof InterpretedFunction) { - InterpretedFunction iApplyCallable = - (InterpretedFunction) applyCallable; - if (frame.fnOrScript.securityDomain - == iApplyCallable.securityDomain) { - frame = - initFrameForApplyOrCall( - cx, - frame, - indexReg, - stack, - sDbl, - stackTop, - op, - calleeScope, - ifun, - iApplyCallable); - continue StateLoop; - } - } - } - } - - // Bug 447697 -- make best effort to keep __noSuchMethod__ - // within this - // interpreter loop invocation - if (fun instanceof NoSuchMethodShim) { - // get the shim and the actual method - NoSuchMethodShim noSuchMethodShim = (NoSuchMethodShim) fun; - Callable noSuchMethodMethod = - noSuchMethodShim.noSuchMethodMethod; - // if the method is in fact an InterpretedFunction - if (noSuchMethodMethod instanceof InterpretedFunction) { - InterpretedFunction ifun = - (InterpretedFunction) noSuchMethodMethod; - if (frame.fnOrScript.securityDomain - == ifun.securityDomain) { - frame = - initFrameForNoSuchMethod( - cx, - frame, - indexReg, - stack, - sDbl, - stackTop, - op, - funThisObj, - calleeScope, - noSuchMethodShim, - ifun); - continue StateLoop; - } - } - } - - cx.lastInterpreterFrame = frame; - frame.savedCallOp = op; - frame.savedStackTop = stackTop; - stack[stackTop] = - fun.call( - cx, - calleeScope, - funThisObj, - getArgsArray( - stack, sDbl, stackTop + 2, indexReg)); - - continue Loop; - } - case Token.NEW: - { - if (instructionCounting) { - cx.instructionCount += INVOCATION_COST; - } - // stack change: function arg0 .. argN -> newResult - // indexReg: number of arguments - stackTop -= indexReg; - - Object lhs = stack[stackTop]; - if (lhs instanceof InterpretedFunction) { - InterpretedFunction f = (InterpretedFunction) lhs; - if (frame.fnOrScript.securityDomain == f.securityDomain) { - Scriptable newInstance = - f.createObject(cx, frame.scope); - CallFrame calleeFrame = - initFrame( - cx, - frame.scope, - newInstance, - stack, - sDbl, - stackTop + 1, - indexReg, - f, - frame); - - stack[stackTop] = newInstance; - frame.savedStackTop = stackTop; - frame.savedCallOp = op; - frame = calleeFrame; - continue StateLoop; - } - } - if (!(lhs instanceof Function)) { - if (lhs == DBL_MRK) - lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); - throw ScriptRuntime.notFunctionError(lhs); - } - Function fun = (Function) lhs; - - if (fun instanceof IdFunctionObject) { - IdFunctionObject ifun = (IdFunctionObject) fun; - if (NativeContinuation.isContinuationConstructor(ifun)) { - frame.stack[stackTop] = - captureContinuation( - cx, frame.parentFrame, false); - continue Loop; - } - } - - Object[] outArgs = - getArgsArray(stack, sDbl, stackTop + 1, indexReg); - stack[stackTop] = fun.construct(cx, frame.scope, outArgs); - continue Loop; - } - case Token.TYPEOF: - { - Object lhs = stack[stackTop]; - if (lhs == DBL_MRK) - lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); - stack[stackTop] = ScriptRuntime.typeof(lhs); - continue Loop; - } - case Icode_TYPEOFNAME: - stack[++stackTop] = - ScriptRuntime.typeofName(frame.scope, stringReg); - continue Loop; - case Token.STRING: - stack[++stackTop] = stringReg; - continue Loop; - case Icode_SHORTNUMBER: - ++stackTop; - stack[stackTop] = DBL_MRK; - sDbl[stackTop] = getShort(iCode, frame.pc); - frame.pc += 2; - continue Loop; - case Icode_INTNUMBER: - ++stackTop; - stack[stackTop] = DBL_MRK; - sDbl[stackTop] = getInt(iCode, frame.pc); - frame.pc += 4; - continue Loop; - case Token.NUMBER: - ++stackTop; - stack[stackTop] = DBL_MRK; - sDbl[stackTop] = frame.idata.itsDoubleTable[indexReg]; - continue Loop; - case Token.BIGINT: - stack[++stackTop] = bigIntReg; - continue Loop; - case Token.NAME: - stack[++stackTop] = ScriptRuntime.name(cx, frame.scope, stringReg); - continue Loop; - case Icode_NAME_INC_DEC: - stack[++stackTop] = - ScriptRuntime.nameIncrDecr( - frame.scope, stringReg, cx, iCode[frame.pc]); - ++frame.pc; - continue Loop; - case Icode_SETCONSTVAR1: - indexReg = iCode[frame.pc++]; - // fallthrough - case Token.SETCONSTVAR: - stackTop = - doSetConstVar( - frame, - stack, - sDbl, - stackTop, - vars, - varDbls, - varAttributes, - indexReg); - continue Loop; - case Icode_SETVAR1: - indexReg = iCode[frame.pc++]; - // fallthrough - case Token.SETVAR: - stackTop = - doSetVar( - frame, - stack, - sDbl, - stackTop, - vars, - varDbls, - varAttributes, - indexReg); - continue Loop; - case Icode_GETVAR1: - indexReg = iCode[frame.pc++]; - // fallthrough - case Token.GETVAR: - stackTop = - doGetVar( - frame, stack, sDbl, stackTop, vars, varDbls, - indexReg); - continue Loop; - case Icode_VAR_INC_DEC: - { - stackTop = - doVarIncDec( - cx, - frame, - stack, - sDbl, - stackTop, - vars, - varDbls, - varAttributes, - indexReg); - continue Loop; - } - case Icode_ZERO: - ++stackTop; - stack[stackTop] = DBL_MRK; - sDbl[stackTop] = 0; - continue Loop; - case Icode_ONE: - ++stackTop; - stack[stackTop] = DBL_MRK; - sDbl[stackTop] = 1; - continue Loop; - case Token.NULL: - stack[++stackTop] = null; - continue Loop; - case Token.THIS: - stack[++stackTop] = frame.thisObj; - continue Loop; - case Token.THISFN: - stack[++stackTop] = frame.fnOrScript; - continue Loop; - case Token.FALSE: - stack[++stackTop] = Boolean.FALSE; - continue Loop; - case Token.TRUE: - stack[++stackTop] = Boolean.TRUE; - continue Loop; - case Icode_UNDEF: - stack[++stackTop] = undefined; - continue Loop; - case Token.ENTERWITH: - { - Object lhs = stack[stackTop]; - if (lhs == DBL_MRK) - lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); - --stackTop; - frame.scope = ScriptRuntime.enterWith(lhs, cx, frame.scope); - continue Loop; - } - case Token.LEAVEWITH: - frame.scope = ScriptRuntime.leaveWith(frame.scope); - continue Loop; - case Token.CATCH_SCOPE: - { - // stack top: exception object - // stringReg: name of exception variable - // indexReg: local for exception scope - --stackTop; - indexReg += frame.localShift; - - boolean afterFirstScope = (frame.idata.itsICode[frame.pc] != 0); - Throwable caughtException = (Throwable) stack[stackTop + 1]; - Scriptable lastCatchScope; - if (!afterFirstScope) { - lastCatchScope = null; - } else { - lastCatchScope = (Scriptable) stack[indexReg]; - } - stack[indexReg] = - ScriptRuntime.newCatchScope( - caughtException, - lastCatchScope, - stringReg, - cx, - frame.scope); - ++frame.pc; - continue Loop; - } - case Token.ENUM_INIT_KEYS: - case Token.ENUM_INIT_VALUES: - case Token.ENUM_INIT_ARRAY: - case Token.ENUM_INIT_VALUES_IN_ORDER: - { - Object lhs = stack[stackTop]; - if (lhs == DBL_MRK) - lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); - --stackTop; - indexReg += frame.localShift; - int enumType = - op == Token.ENUM_INIT_KEYS - ? ScriptRuntime.ENUMERATE_KEYS - : op == Token.ENUM_INIT_VALUES - ? ScriptRuntime.ENUMERATE_VALUES - : op == Token.ENUM_INIT_VALUES_IN_ORDER - ? ScriptRuntime - .ENUMERATE_VALUES_IN_ORDER - : ScriptRuntime.ENUMERATE_ARRAY; - stack[indexReg] = - ScriptRuntime.enumInit(lhs, cx, frame.scope, enumType); - continue Loop; - } - case Token.ENUM_NEXT: - case Token.ENUM_ID: - { - indexReg += frame.localShift; - Object val = stack[indexReg]; - ++stackTop; - stack[stackTop] = - (op == Token.ENUM_NEXT) - ? (Object) ScriptRuntime.enumNext(val) - : (Object) ScriptRuntime.enumId(val, cx); - continue Loop; - } - case Token.REF_SPECIAL: - { - // stringReg: name of special property - Object obj = stack[stackTop]; - if (obj == DBL_MRK) - obj = ScriptRuntime.wrapNumber(sDbl[stackTop]); - stack[stackTop] = - ScriptRuntime.specialRef( - obj, stringReg, cx, frame.scope); - continue Loop; - } - case Token.REF_MEMBER: - { - // indexReg: flags - stackTop = doRefMember(cx, stack, sDbl, stackTop, indexReg); - continue Loop; - } - case Token.REF_NS_MEMBER: - { - // indexReg: flags - stackTop = doRefNsMember(cx, stack, sDbl, stackTop, indexReg); - continue Loop; - } - case Token.REF_NAME: - { - // indexReg: flags - Object name = stack[stackTop]; - if (name == DBL_MRK) - name = ScriptRuntime.wrapNumber(sDbl[stackTop]); - stack[stackTop] = - ScriptRuntime.nameRef(name, cx, frame.scope, indexReg); - continue Loop; - } - case Token.REF_NS_NAME: - { - // indexReg: flags - stackTop = - doRefNsName(cx, frame, stack, sDbl, stackTop, indexReg); - continue Loop; - } - case Icode_SCOPE_LOAD: - indexReg += frame.localShift; - frame.scope = (Scriptable) stack[indexReg]; - continue Loop; - case Icode_SCOPE_SAVE: - indexReg += frame.localShift; - stack[indexReg] = frame.scope; - continue Loop; - case Icode_CLOSURE_EXPR: - InterpretedFunction fn = - InterpretedFunction.createFunction( - cx, frame.scope, frame.fnOrScript, indexReg); - if (fn.idata.itsFunctionType == FunctionNode.ARROW_FUNCTION) { - stack[++stackTop] = - new ArrowFunction(cx, frame.scope, fn, frame.thisObj); - } else { - stack[++stackTop] = fn; - } - continue Loop; - case Icode_CLOSURE_STMT: - initFunction(cx, frame.scope, frame.fnOrScript, indexReg); - continue Loop; - case Token.REGEXP: - Object re = frame.idata.itsRegExpLiterals[indexReg]; - stack[++stackTop] = ScriptRuntime.wrapRegExp(cx, frame.scope, re); - continue Loop; - case Icode_TEMPLATE_LITERAL_CALLSITE: - Object[] templateLiterals = frame.idata.itsTemplateLiterals; - stack[++stackTop] = - ScriptRuntime.getTemplateLiteralCallSite( - cx, frame.scope, templateLiterals, indexReg); - continue Loop; - case Icode_LITERAL_NEW: - // indexReg: number of values in the literal - ++stackTop; - stack[stackTop] = new int[indexReg]; - ++stackTop; - stack[stackTop] = new Object[indexReg]; - sDbl[stackTop] = 0; - continue Loop; - case Icode_LITERAL_SET: - { - Object value = stack[stackTop]; - if (value == DBL_MRK) - value = ScriptRuntime.wrapNumber(sDbl[stackTop]); - --stackTop; - int i = (int) sDbl[stackTop]; - ((Object[]) stack[stackTop])[i] = value; - sDbl[stackTop] = i + 1; - continue Loop; - } - case Icode_LITERAL_GETTER: - { - Object value = stack[stackTop]; - --stackTop; - int i = (int) sDbl[stackTop]; - ((Object[]) stack[stackTop])[i] = value; - ((int[]) stack[stackTop - 1])[i] = -1; - sDbl[stackTop] = i + 1; - continue Loop; - } - case Icode_LITERAL_SETTER: - { - Object value = stack[stackTop]; - --stackTop; - int i = (int) sDbl[stackTop]; - ((Object[]) stack[stackTop])[i] = value; - ((int[]) stack[stackTop - 1])[i] = +1; - sDbl[stackTop] = i + 1; - continue Loop; - } - case Token.ARRAYLIT: - case Icode_SPARE_ARRAYLIT: - case Token.OBJECTLIT: - { - Object[] data = (Object[]) stack[stackTop]; - --stackTop; - int[] getterSetters = (int[]) stack[stackTop]; - Object val; - if (op == Token.OBJECTLIT) { - Object[] ids = (Object[]) frame.idata.literalIds[indexReg]; - val = - ScriptRuntime.newObjectLiteral( - ids, data, getterSetters, cx, frame.scope); - } else { - int[] skipIndexces = null; - if (op == Icode_SPARE_ARRAYLIT) { - skipIndexces = (int[]) frame.idata.literalIds[indexReg]; - } - val = - ScriptRuntime.newArrayLiteral( - data, skipIndexces, cx, frame.scope); - } - stack[stackTop] = val; - continue Loop; - } - case Icode_ENTERDQ: - { - Object lhs = stack[stackTop]; - if (lhs == DBL_MRK) - lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); - --stackTop; - frame.scope = ScriptRuntime.enterDotQuery(lhs, frame.scope); - continue Loop; - } - case Icode_LEAVEDQ: - { - boolean valBln = stack_boolean(frame, stackTop); - Object x = ScriptRuntime.updateDotQuery(valBln, frame.scope); - if (x != null) { - stack[stackTop] = x; - frame.scope = ScriptRuntime.leaveDotQuery(frame.scope); - frame.pc += 2; - continue Loop; - } - // reset stack and PC to code after ENTERDQ - --stackTop; - break jumplessRun; - } - case Token.DEFAULTNAMESPACE: - { - Object value = stack[stackTop]; - if (value == DBL_MRK) - value = ScriptRuntime.wrapNumber(sDbl[stackTop]); - stack[stackTop] = ScriptRuntime.setDefaultNamespace(value, cx); - continue Loop; - } - case Token.ESCXMLATTR: - { - Object value = stack[stackTop]; - if (value != DBL_MRK) { - stack[stackTop] = - ScriptRuntime.escapeAttributeValue(value, cx); - } - continue Loop; - } - case Token.ESCXMLTEXT: - { - Object value = stack[stackTop]; - if (value != DBL_MRK) { - stack[stackTop] = ScriptRuntime.escapeTextValue(value, cx); - } - continue Loop; - } - case Icode_DEBUGGER: - if (frame.debuggerFrame != null) { - frame.debuggerFrame.onDebuggerStatement(cx); - } - continue Loop; - case Icode_LINE: - frame.pcSourceLineStart = frame.pc; - if (frame.debuggerFrame != null) { - int line = getIndex(iCode, frame.pc); - frame.debuggerFrame.onLineChange(cx, line); - } - frame.pc += 2; - continue Loop; - case Icode_REG_IND_C0: - indexReg = 0; - continue Loop; - case Icode_REG_IND_C1: - indexReg = 1; - continue Loop; - case Icode_REG_IND_C2: - indexReg = 2; - continue Loop; - case Icode_REG_IND_C3: - indexReg = 3; - continue Loop; - case Icode_REG_IND_C4: - indexReg = 4; - continue Loop; - case Icode_REG_IND_C5: - indexReg = 5; - continue Loop; - case Icode_REG_IND1: - indexReg = 0xFF & iCode[frame.pc]; - ++frame.pc; - continue Loop; - case Icode_REG_IND2: - indexReg = getIndex(iCode, frame.pc); - frame.pc += 2; - continue Loop; - case Icode_REG_IND4: - indexReg = getInt(iCode, frame.pc); - frame.pc += 4; - continue Loop; - case Icode_REG_STR_C0: - stringReg = strings[0]; - continue Loop; - case Icode_REG_STR_C1: - stringReg = strings[1]; - continue Loop; - case Icode_REG_STR_C2: - stringReg = strings[2]; - continue Loop; - case Icode_REG_STR_C3: - stringReg = strings[3]; - continue Loop; - case Icode_REG_STR1: - stringReg = strings[0xFF & iCode[frame.pc]]; - ++frame.pc; - continue Loop; - case Icode_REG_STR2: - stringReg = strings[getIndex(iCode, frame.pc)]; - frame.pc += 2; - continue Loop; - case Icode_REG_STR4: - stringReg = strings[getInt(iCode, frame.pc)]; - frame.pc += 4; - continue Loop; - case Icode_REG_BIGINT_C0: - bigIntReg = bigInts[0]; - continue Loop; - case Icode_REG_BIGINT_C1: - bigIntReg = bigInts[1]; - continue Loop; - case Icode_REG_BIGINT_C2: - bigIntReg = bigInts[2]; - continue Loop; - case Icode_REG_BIGINT_C3: - bigIntReg = bigInts[3]; - continue Loop; - case Icode_REG_BIGINT1: - bigIntReg = bigInts[0xFF & iCode[frame.pc]]; - ++frame.pc; - continue Loop; - case Icode_REG_BIGINT2: - bigIntReg = bigInts[getIndex(iCode, frame.pc)]; - frame.pc += 2; - continue Loop; - case Icode_REG_BIGINT4: - bigIntReg = bigInts[getInt(iCode, frame.pc)]; - frame.pc += 4; - continue Loop; - default: - dumpICode(frame.idata); - throw new RuntimeException( - "Unknown icode : " + op + " @ pc : " + (frame.pc - 1)); - } // end of interpreter switch - } // end of jumplessRun label block - - // This should be reachable only for jump implementation - // when pc points to encoded target offset - if (instructionCounting) { - addInstructionCount(cx, frame, 2); - } - int offset = getShort(iCode, frame.pc); - if (offset != 0) { - // -1 accounts for pc pointing to jump opcode + 1 - frame.pc += offset - 1; - } else { - frame.pc = frame.idata.longJumps.getExistingInt(frame.pc); - } - if (instructionCounting) { - frame.pcPrevBranch = frame.pc; - } - continue Loop; - } // end of Loop: for - - exitFrame(cx, frame, null); - interpreterResult = frame.result; - interpreterResultDbl = frame.resultDbl; - if (frame.parentFrame != null) { - frame = frame.parentFrame; - if (frame.frozen) { - frame = frame.cloneFrozen(); - } - setCallResult(frame, interpreterResult, interpreterResultDbl); - interpreterResult = null; // Help GC - continue StateLoop; - } - break StateLoop; - - } // end of interpreter withoutExceptions: try - catch (Throwable ex) { - if (throwable != null) { - // This is serious bug and it is better to track it ASAP - ex.printStackTrace(System.err); - throw new IllegalStateException(); - } - throwable = ex; - } - - // This should be reachable only after above catch or from - // finally when it needs to propagate exception or from - // explicit throw - if (throwable == null) Kit.codeBug(); - - // Exception type - final int EX_CATCH_STATE = 2; // Can execute JS catch - final int EX_FINALLY_STATE = 1; // Can execute JS finally - final int EX_NO_JS_STATE = 0; // Terminate JS execution - - int exState; - ContinuationJump cjump = null; - - if (generatorState != null - && generatorState.operation == NativeGenerator.GENERATOR_CLOSE - && throwable == generatorState.value) { - exState = EX_FINALLY_STATE; - } else if (throwable instanceof JavaScriptException) { - exState = EX_CATCH_STATE; - } else if (throwable instanceof EcmaError) { - // an offical ECMA error object, - exState = EX_CATCH_STATE; - } else if (throwable instanceof EvaluatorException) { - exState = EX_CATCH_STATE; - } else if (throwable instanceof ContinuationPending) { - exState = EX_NO_JS_STATE; - // # INNER CORE MODIFIED PART - } else if (throwable instanceof Error) { - exState = - cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS) - ? EX_CATCH_STATE - : EX_NO_JS_STATE; - } else if (throwable instanceof ContinuationJump) { - // It must be ContinuationJump - exState = EX_FINALLY_STATE; - cjump = (ContinuationJump) throwable; - } else if (throwable instanceof Exception) { - throwable = new WrappedException((Exception) throwable); - exState = EX_CATCH_STATE; - // # END - } else { - exState = - cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS) - ? EX_CATCH_STATE - : EX_FINALLY_STATE; - } - - if (instructionCounting) { - try { - addInstructionCount(cx, frame, EXCEPTION_COST); - } catch (RuntimeException ex) { - throwable = ex; - exState = EX_FINALLY_STATE; - } catch (Error ex) { - // Error from instruction counting - // => unconditionally terminate JS - throwable = ex; - cjump = null; - exState = EX_NO_JS_STATE; - } - } - if (frame.debuggerFrame != null && throwable instanceof RuntimeException) { - // Call debugger only for RuntimeException - RuntimeException rex = (RuntimeException) throwable; - try { - frame.debuggerFrame.onExceptionThrown(cx, rex); - } catch (Throwable ex) { - // Any exception from debugger - // => unconditionally terminate JS - throwable = ex; - cjump = null; - exState = EX_NO_JS_STATE; - } - } - - for (; ; ) { - if (exState != EX_NO_JS_STATE) { - boolean onlyFinally = (exState != EX_CATCH_STATE); - indexReg = getExceptionHandler(frame, onlyFinally); - if (indexReg >= 0) { - // We caught an exception, restart the loop - // with exception pending the processing at the loop - // start - continue StateLoop; - } - } - // No allowed exception handlers in this frame, unwind - // to parent and try to look there - - exitFrame(cx, frame, throwable); - - frame = frame.parentFrame; - if (frame == null) { - break; - } - if (cjump != null && cjump.branchFrame == frame) { - // Continuation branch point was hit, - // restart the state loop to reenter continuation - indexReg = -1; - continue StateLoop; - } - } - - // No more frames, rethrow the exception or deal with continuation - if (cjump != null) { - if (cjump.branchFrame != null) { - // The above loop should locate the top frame - Kit.codeBug(); - } - if (cjump.capturedFrame != null) { - // Restarting detached continuation - indexReg = -1; - continue StateLoop; - } - // Return continuation result to the caller - interpreterResult = cjump.result; - interpreterResultDbl = cjump.resultDbl; - throwable = null; - } - break StateLoop; - } // end of StateLoop: for(;;) - - // Do cleanups/restorations before the final return or throw - - if (cx.previousInterpreterInvocations != null - && cx.previousInterpreterInvocations.size() != 0) { - cx.lastInterpreterFrame = cx.previousInterpreterInvocations.pop(); - } else { - // It was the last interpreter frame on the stack - cx.lastInterpreterFrame = null; - // Force GC of the value cx.previousInterpreterInvocations - cx.previousInterpreterInvocations = null; - } - - if (throwable != null) { - if (throwable instanceof RuntimeException) { - throw (RuntimeException) throwable; - } - // Must be instance of Error or code bug - throw (Error) throwable; - } - - return (interpreterResult != DBL_MRK) - ? interpreterResult - : ScriptRuntime.wrapNumber(interpreterResultDbl); - } - - private static int doInOrInstanceof( - Context cx, int op, Object[] stack, double[] sDbl, int stackTop) { - Object rhs = stack[stackTop]; - if (rhs == DOUBLE_MARK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); - --stackTop; - Object lhs = stack[stackTop]; - if (lhs == DOUBLE_MARK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); - boolean valBln; - if (op == Token.IN) { - valBln = ScriptRuntime.in(lhs, rhs, cx); - } else { - valBln = ScriptRuntime.instanceOf(lhs, rhs, cx); - } - stack[stackTop] = ScriptRuntime.wrapBoolean(valBln); - return stackTop; - } - - private static int doCompare( - CallFrame frame, int op, Object[] stack, double[] sDbl, int stackTop) { - --stackTop; - Object rhs = stack[stackTop + 1]; - Object lhs = stack[stackTop]; - boolean valBln; - object_compare: - { - number_compare: - { - Number rNum, lNum; - if (rhs == DOUBLE_MARK) { - rNum = sDbl[stackTop + 1]; - lNum = stack_numeric(frame, stackTop); - } else if (lhs == DOUBLE_MARK) { - rNum = ScriptRuntime.toNumeric(rhs); - lNum = sDbl[stackTop]; - } else { - break number_compare; - } - valBln = ScriptRuntime.compare(lNum, rNum, op); - break object_compare; - } - valBln = ScriptRuntime.compare(lhs, rhs, op); - } - stack[stackTop] = ScriptRuntime.wrapBoolean(valBln); - return stackTop; - } - - private static int doBitOp( - CallFrame frame, int op, Object[] stack, double[] sDbl, int stackTop) { - Number lValue = stack_numeric(frame, stackTop - 1); - Number rValue = stack_numeric(frame, stackTop); - stackTop--; - - Number result = null; - switch (op) { - case Token.BITAND: - result = ScriptRuntime.bitwiseAND(lValue, rValue); - break; - case Token.BITOR: - result = ScriptRuntime.bitwiseOR(lValue, rValue); - break; - case Token.BITXOR: - result = ScriptRuntime.bitwiseXOR(lValue, rValue); - break; - case Token.LSH: - result = ScriptRuntime.leftShift(lValue, rValue); - break; - case Token.RSH: - result = ScriptRuntime.signedRightShift(lValue, rValue); - break; - } - - if (result instanceof BigInteger) { - stack[stackTop] = result; - } else { - stack[stackTop] = DOUBLE_MARK; - sDbl[stackTop] = result.doubleValue(); - } - return stackTop; - } - - private static int doBitNOT(CallFrame frame, Object[] stack, double[] sDbl, int stackTop) { - Number value = stack_numeric(frame, stackTop); - Number result = ScriptRuntime.bitwiseNOT(value); - if (result instanceof BigInteger) { - stack[stackTop] = result; - } else { - stack[stackTop] = DOUBLE_MARK; - sDbl[stackTop] = result.doubleValue(); - } - return stackTop; - } - - private static int doDelName( - Context cx, CallFrame frame, int op, Object[] stack, double[] sDbl, int stackTop) { - Object rhs = stack[stackTop]; - if (rhs == DOUBLE_MARK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); - --stackTop; - Object lhs = stack[stackTop]; - if (lhs == DOUBLE_MARK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); - stack[stackTop] = ScriptRuntime.delete(lhs, rhs, cx, frame.scope, op == Icode_DELNAME); - return stackTop; - } - - private static int doGetElem( - Context cx, CallFrame frame, Object[] stack, double[] sDbl, int stackTop) { - --stackTop; - Object lhs = stack[stackTop]; - if (lhs == DOUBLE_MARK) { - lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); - } - Object value; - Object id = stack[stackTop + 1]; - if (id != DOUBLE_MARK) { - value = ScriptRuntime.getObjectElem(lhs, id, cx, frame.scope); - } else { - double d = sDbl[stackTop + 1]; - value = ScriptRuntime.getObjectIndex(lhs, d, cx, frame.scope); - } - stack[stackTop] = value; - return stackTop; - } - - private static int doSetElem( - Context cx, CallFrame frame, Object[] stack, double[] sDbl, int stackTop) { - stackTop -= 2; - Object rhs = stack[stackTop + 2]; - if (rhs == DOUBLE_MARK) { - rhs = ScriptRuntime.wrapNumber(sDbl[stackTop + 2]); - } - Object lhs = stack[stackTop]; - if (lhs == DOUBLE_MARK) { - lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); - } - Object value; - Object id = stack[stackTop + 1]; - if (id != DOUBLE_MARK) { - value = ScriptRuntime.setObjectElem(lhs, id, rhs, cx, frame.scope); - } else { - double d = sDbl[stackTop + 1]; - value = ScriptRuntime.setObjectIndex(lhs, d, rhs, cx, frame.scope); - } - stack[stackTop] = value; - return stackTop; - } - - private static int doElemIncDec( - Context cx, - CallFrame frame, - byte[] iCode, - Object[] stack, - double[] sDbl, - int stackTop) { - Object rhs = stack[stackTop]; - if (rhs == DOUBLE_MARK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); - --stackTop; - Object lhs = stack[stackTop]; - if (lhs == DOUBLE_MARK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); - stack[stackTop] = ScriptRuntime.elemIncrDecr(lhs, rhs, cx, frame.scope, iCode[frame.pc]); - ++frame.pc; - return stackTop; - } - - private static int doCallSpecial( - Context cx, - CallFrame frame, - Object[] stack, - double[] sDbl, - int stackTop, - byte[] iCode, - int indexReg) { - int callType = iCode[frame.pc] & 0xFF; - boolean isNew = (iCode[frame.pc + 1] != 0); - int sourceLine = getIndex(iCode, frame.pc + 2); - - // indexReg: number of arguments - if (isNew) { - // stack change: function arg0 .. argN -> newResult - stackTop -= indexReg; - - Object function = stack[stackTop]; - if (function == DOUBLE_MARK) function = ScriptRuntime.wrapNumber(sDbl[stackTop]); - Object[] outArgs = getArgsArray(stack, sDbl, stackTop + 1, indexReg); - stack[stackTop] = - ScriptRuntime.newSpecial(cx, function, outArgs, frame.scope, callType); - } else { - // stack change: function thisObj arg0 .. argN -> result - stackTop -= 1 + indexReg; - - // Call code generation ensure that stack here - // is ... Callable Scriptable - Scriptable functionThis = (Scriptable) stack[stackTop + 1]; - Callable function = (Callable) stack[stackTop]; - Object[] outArgs = getArgsArray(stack, sDbl, stackTop + 2, indexReg); - stack[stackTop] = - ScriptRuntime.callSpecial( - cx, - function, - functionThis, - outArgs, - frame.scope, - frame.thisObj, - callType, - frame.idata.itsSourceFile, - sourceLine); - } - frame.pc += 4; - return stackTop; - } - - private static int doSetConstVar( - CallFrame frame, - Object[] stack, - double[] sDbl, - int stackTop, - Object[] vars, - double[] varDbls, - int[] varAttributes, - int indexReg) { - if (!frame.useActivation) { - if ((varAttributes[indexReg] & ScriptableObject.READONLY) == 0) { - throw Context.reportRuntimeErrorById( - "msg.var.redecl", frame.idata.argNames[indexReg]); - } - if ((varAttributes[indexReg] & ScriptableObject.UNINITIALIZED_CONST) != 0) { - vars[indexReg] = stack[stackTop]; - varAttributes[indexReg] &= ~ScriptableObject.UNINITIALIZED_CONST; - varDbls[indexReg] = sDbl[stackTop]; - } - } else { - Object val = stack[stackTop]; - if (val == DOUBLE_MARK) val = ScriptRuntime.wrapNumber(sDbl[stackTop]); - String stringReg = frame.idata.argNames[indexReg]; - if (frame.scope instanceof ConstProperties) { - ConstProperties cp = (ConstProperties) frame.scope; - cp.putConst(stringReg, frame.scope, val); - } else throw Kit.codeBug(); - } - return stackTop; - } - - private static int doSetVar( - CallFrame frame, - Object[] stack, - double[] sDbl, - int stackTop, - Object[] vars, - double[] varDbls, - int[] varAttributes, - int indexReg) { - if (!frame.useActivation) { - if ((varAttributes[indexReg] & ScriptableObject.READONLY) == 0) { - vars[indexReg] = stack[stackTop]; - varDbls[indexReg] = sDbl[stackTop]; - } - } else { - Object val = stack[stackTop]; - if (val == DOUBLE_MARK) val = ScriptRuntime.wrapNumber(sDbl[stackTop]); - String stringReg = frame.idata.argNames[indexReg]; - frame.scope.put(stringReg, frame.scope, val); - } - return stackTop; - } - - private static int doGetVar( - CallFrame frame, - Object[] stack, - double[] sDbl, - int stackTop, - Object[] vars, - double[] varDbls, - int indexReg) { - ++stackTop; - if (!frame.useActivation) { - stack[stackTop] = vars[indexReg]; - sDbl[stackTop] = varDbls[indexReg]; - } else { - String stringReg = frame.idata.argNames[indexReg]; - stack[stackTop] = frame.scope.get(stringReg, frame.scope); - } - return stackTop; - } - - private static int doVarIncDec( - Context cx, - CallFrame frame, - Object[] stack, - double[] sDbl, - int stackTop, - Object[] vars, - double[] varDbls, - int[] varAttributes, - int indexReg) { - // indexReg : varindex - ++stackTop; - int incrDecrMask = frame.idata.itsICode[frame.pc]; - if (!frame.useActivation) { - Object varValue = vars[indexReg]; - double d = 0.0; - BigInteger bi = null; - if (varValue == DOUBLE_MARK) { - d = varDbls[indexReg]; - } else { - Number num = ScriptRuntime.toNumeric(varValue); - if (num instanceof BigInteger) { - bi = (BigInteger) num; - } else { - d = num.doubleValue(); - } - } - if (bi == null) { - // double - double d2 = ((incrDecrMask & Node.DECR_FLAG) == 0) ? d + 1.0 : d - 1.0; - boolean post = ((incrDecrMask & Node.POST_FLAG) != 0); - if ((varAttributes[indexReg] & ScriptableObject.READONLY) == 0) { - if (varValue != DOUBLE_MARK) { - vars[indexReg] = DOUBLE_MARK; - } - varDbls[indexReg] = d2; - stack[stackTop] = DOUBLE_MARK; - sDbl[stackTop] = post ? d : d2; - } else { - if (post && varValue != DOUBLE_MARK) { - stack[stackTop] = varValue; - } else { - stack[stackTop] = DOUBLE_MARK; - sDbl[stackTop] = post ? d : d2; - } - } - } else { - // BigInt - BigInteger result; - if ((incrDecrMask & Node.DECR_FLAG) == 0) { - result = bi.add(BigInteger.ONE); - } else { - result = bi.subtract(BigInteger.ONE); - } - - boolean post = ((incrDecrMask & Node.POST_FLAG) != 0); - if ((varAttributes[indexReg] & ScriptableObject.READONLY) == 0) { - vars[indexReg] = result; - stack[stackTop] = post ? bi : result; - } else { - if (post && varValue != DOUBLE_MARK) { - stack[stackTop] = varValue; - } else { - stack[stackTop] = post ? bi : result; - } - } - } - } else { - String varName = frame.idata.argNames[indexReg]; - stack[stackTop] = ScriptRuntime.nameIncrDecr(frame.scope, varName, cx, incrDecrMask); - } - ++frame.pc; - return stackTop; - } - - private static int doRefMember( - Context cx, Object[] stack, double[] sDbl, int stackTop, int flags) { - Object elem = stack[stackTop]; - if (elem == DOUBLE_MARK) elem = ScriptRuntime.wrapNumber(sDbl[stackTop]); - --stackTop; - Object obj = stack[stackTop]; - if (obj == DOUBLE_MARK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop]); - stack[stackTop] = ScriptRuntime.memberRef(obj, elem, cx, flags); - return stackTop; - } - - private static int doRefNsMember( - Context cx, Object[] stack, double[] sDbl, int stackTop, int flags) { - Object elem = stack[stackTop]; - if (elem == DOUBLE_MARK) elem = ScriptRuntime.wrapNumber(sDbl[stackTop]); - --stackTop; - Object ns = stack[stackTop]; - if (ns == DOUBLE_MARK) ns = ScriptRuntime.wrapNumber(sDbl[stackTop]); - --stackTop; - Object obj = stack[stackTop]; - if (obj == DOUBLE_MARK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop]); - stack[stackTop] = ScriptRuntime.memberRef(obj, ns, elem, cx, flags); - return stackTop; - } - - private static int doRefNsName( - Context cx, CallFrame frame, Object[] stack, double[] sDbl, int stackTop, int flags) { - Object name = stack[stackTop]; - if (name == DOUBLE_MARK) name = ScriptRuntime.wrapNumber(sDbl[stackTop]); - --stackTop; - Object ns = stack[stackTop]; - if (ns == DOUBLE_MARK) ns = ScriptRuntime.wrapNumber(sDbl[stackTop]); - stack[stackTop] = ScriptRuntime.nameRef(ns, name, cx, frame.scope, flags); - return stackTop; - } - - /** Call __noSuchMethod__. */ - private static CallFrame initFrameForNoSuchMethod( - Context cx, - CallFrame frame, - int indexReg, - Object[] stack, - double[] sDbl, - int stackTop, - int op, - Scriptable funThisObj, - Scriptable calleeScope, - NoSuchMethodShim noSuchMethodShim, - InterpretedFunction ifun) { - // create an args array from the stack - Object[] argsArray = null; - // exactly like getArgsArray except that the first argument - // is the method name from the shim - int shift = stackTop + 2; - Object[] elements = new Object[indexReg]; - for (int i = 0; i < indexReg; ++i, ++shift) { - Object val = stack[shift]; - if (val == DOUBLE_MARK) { - val = ScriptRuntime.wrapNumber(sDbl[shift]); - } - elements[i] = val; - } - argsArray = new Object[2]; - argsArray[0] = noSuchMethodShim.methodName; - argsArray[1] = cx.newArray(calleeScope, elements); - - // exactly the same as if it's a regular InterpretedFunction - CallFrame callParentFrame = frame; - if (op == Icode_TAIL_CALL) { - callParentFrame = frame.parentFrame; - exitFrame(cx, frame, null); - } - // init the frame with the underlying method with the - // adjusted args array and shim's function - CallFrame calleeFrame = - initFrame( - cx, calleeScope, funThisObj, argsArray, null, 0, 2, ifun, callParentFrame); - if (op != Icode_TAIL_CALL) { - frame.savedStackTop = stackTop; - frame.savedCallOp = op; - } - return calleeFrame; - } - - private static boolean doEquals(Object[] stack, double[] sDbl, int stackTop) { - Object rhs = stack[stackTop + 1]; - Object lhs = stack[stackTop]; - if (rhs == DOUBLE_MARK) { - if (lhs == DOUBLE_MARK) { - return (sDbl[stackTop] == sDbl[stackTop + 1]); - } - return ScriptRuntime.eqNumber(sDbl[stackTop + 1], lhs); - } - if (lhs == DOUBLE_MARK) { - return ScriptRuntime.eqNumber(sDbl[stackTop], rhs); - } - return ScriptRuntime.eq(lhs, rhs); - } - - private static boolean doShallowEquals(Object[] stack, double[] sDbl, int stackTop) { - Object rhs = stack[stackTop + 1]; - Object lhs = stack[stackTop]; - final Object DBL_MRK = DOUBLE_MARK; - double rdbl, ldbl; - if (rhs == DBL_MRK) { - rdbl = sDbl[stackTop + 1]; - if (lhs == DBL_MRK) { - ldbl = sDbl[stackTop]; - } else if (lhs instanceof Number && !(lhs instanceof BigInteger)) { - ldbl = ((Number) lhs).doubleValue(); - } else { - return false; - } - } else if (lhs == DBL_MRK) { - ldbl = sDbl[stackTop]; - if (rhs instanceof Number && !(rhs instanceof BigInteger)) { - rdbl = ((Number) rhs).doubleValue(); - } else { - return false; - } - } else { - return ScriptRuntime.shallowEq(lhs, rhs); - } - return (ldbl == rdbl); - } - - private static CallFrame processThrowable( - Context cx, - Object throwable, - CallFrame frame, - int indexReg, - boolean instructionCounting) { - // Recovering from exception, indexReg contains - // the index of handler - - if (indexReg >= 0) { - // Normal exception handler, transfer - // control appropriately - - if (frame.frozen) { - // XXX Deal with exceptios!!! - frame = frame.cloneFrozen(); - } - - int[] table = frame.idata.itsExceptionTable; - - frame.pc = table[indexReg + EXCEPTION_HANDLER_SLOT]; - if (instructionCounting) { - frame.pcPrevBranch = frame.pc; - } - - frame.savedStackTop = frame.emptyStackTop; - int scopeLocal = frame.localShift + table[indexReg + EXCEPTION_SCOPE_SLOT]; - int exLocal = frame.localShift + table[indexReg + EXCEPTION_LOCAL_SLOT]; - frame.scope = (Scriptable) frame.stack[scopeLocal]; - frame.stack[exLocal] = throwable; - - throwable = null; - } else { - // Continuation restoration - ContinuationJump cjump = (ContinuationJump) throwable; - - // Clear throwable to indicate that exceptions are OK - throwable = null; - - if (cjump.branchFrame != frame) Kit.codeBug(); - - // Check that we have at least one frozen frame - // in the case of detached continuation restoration: - // unwind code ensure that - if (cjump.capturedFrame == null) Kit.codeBug(); - - // Need to rewind branchFrame, capturedFrame - // and all frames in between - int rewindCount = cjump.capturedFrame.frameIndex + 1; - if (cjump.branchFrame != null) { - rewindCount -= cjump.branchFrame.frameIndex; - } - - int enterCount = 0; - CallFrame[] enterFrames = null; - - CallFrame x = cjump.capturedFrame; - for (int i = 0; i != rewindCount; ++i) { - if (!x.frozen) Kit.codeBug(); - if (x.useActivation) { - if (enterFrames == null) { - // Allocate enough space to store the rest - // of rewind frames in case all of them - // would require to enter - enterFrames = new CallFrame[rewindCount - i]; - } - enterFrames[enterCount] = x; - ++enterCount; - } - x = x.parentFrame; - } - - while (enterCount != 0) { - // execute enter: walk enterFrames in the reverse - // order since they were stored starting from - // the capturedFrame, not branchFrame - --enterCount; - x = enterFrames[enterCount]; - enterFrame(cx, x, ScriptRuntime.emptyArgs, true); - } - - // Continuation jump is almost done: capturedFrame - // points to the call to the function that captured - // continuation, so clone capturedFrame and - // emulate return that function with the suplied result - frame = cjump.capturedFrame.cloneFrozen(); - setCallResult(frame, cjump.result, cjump.resultDbl); - // restart the execution - } - frame.throwable = throwable; - return frame; - } - - private static Object freezeGenerator( - Context cx, - CallFrame frame, - int stackTop, - GeneratorState generatorState, - boolean yieldStar) { - if (generatorState.operation == NativeGenerator.GENERATOR_CLOSE) { - // Error: no yields when generator is closing - throw ScriptRuntime.typeErrorById("msg.yield.closing"); - } - // return to our caller (which should be a method of NativeGenerator) - frame.frozen = true; - frame.result = frame.stack[stackTop]; - frame.resultDbl = frame.sDbl[stackTop]; - frame.savedStackTop = stackTop; - frame.pc--; // we want to come back here when we resume - ScriptRuntime.exitActivationFunction(cx); - final Object result = - (frame.result != DOUBLE_MARK) - ? frame.result - : ScriptRuntime.wrapNumber(frame.resultDbl); - if (yieldStar) { - return new ES6Generator.YieldStarResult(result); - } - return result; - } - - private static Object thawGenerator( - CallFrame frame, int stackTop, GeneratorState generatorState, int op) { - // we are resuming execution - frame.frozen = false; - int sourceLine = getIndex(frame.idata.itsICode, frame.pc); - frame.pc += 2; // skip line number data - if (generatorState.operation == NativeGenerator.GENERATOR_THROW) { - // processing a call to .throw(exception): must - // act as if exception was thrown from resumption point. - return new JavaScriptException( - generatorState.value, frame.idata.itsSourceFile, sourceLine); - } - if (generatorState.operation == NativeGenerator.GENERATOR_CLOSE) { - return generatorState.value; - } - if (generatorState.operation != NativeGenerator.GENERATOR_SEND) throw Kit.codeBug(); - if ((op == Token.YIELD) || (op == Icode_YIELD_STAR)) { - frame.stack[stackTop] = generatorState.value; - } - return Scriptable.NOT_FOUND; - } - - private static CallFrame initFrameForApplyOrCall( - Context cx, - CallFrame frame, - int indexReg, - Object[] stack, - double[] sDbl, - int stackTop, - int op, - Scriptable calleeScope, - IdFunctionObject ifun, - InterpretedFunction iApplyCallable) { - Scriptable applyThis; - if (indexReg != 0) { - Object obj = stack[stackTop + 2]; - if (obj == DOUBLE_MARK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop + 2]); - applyThis = ScriptRuntime.toObjectOrNull(cx, obj, frame.scope); - } else { - applyThis = null; - } - if (applyThis == null) { - // This covers the case of args[0] == (null|undefined) as well. - applyThis = ScriptRuntime.getTopCallScope(cx); - } - if (op == Icode_TAIL_CALL) { - exitFrame(cx, frame, null); - frame = frame.parentFrame; - } else { - frame.savedStackTop = stackTop; - frame.savedCallOp = op; - } - final CallFrame calleeFrame; - if (BaseFunction.isApply(ifun)) { - Object[] callArgs = - indexReg < 2 - ? ScriptRuntime.emptyArgs - : ScriptRuntime.getApplyArguments(cx, stack[stackTop + 3]); - calleeFrame = - initFrame( - cx, - calleeScope, - applyThis, - callArgs, - null, - 0, - callArgs.length, - iApplyCallable, - frame); - } else { - // Shift args left - for (int i = 1; i < indexReg; ++i) { - stack[stackTop + 1 + i] = stack[stackTop + 2 + i]; - sDbl[stackTop + 1 + i] = sDbl[stackTop + 2 + i]; - } - int argCount = indexReg < 2 ? 0 : indexReg - 1; - calleeFrame = - initFrame( - cx, - calleeScope, - applyThis, - stack, - sDbl, - stackTop + 2, - argCount, - iApplyCallable, - frame); - } - - return calleeFrame; - } - - private static CallFrame initFrame( - Context cx, - Scriptable callerScope, - Scriptable thisObj, - Object[] args, - double[] argsDbl, - int argShift, - int argCount, - InterpretedFunction fnOrScript, - CallFrame parentFrame) { - CallFrame frame = new CallFrame(cx, thisObj, fnOrScript, parentFrame); - frame.initializeArgs(cx, callerScope, args, argsDbl, argShift, argCount); - enterFrame(cx, frame, args, false); - return frame; - } - - private static void enterFrame( - Context cx, CallFrame frame, Object[] args, boolean continuationRestart) { - boolean usesActivation = frame.idata.itsNeedsActivation; - boolean isDebugged = frame.debuggerFrame != null; - if (usesActivation || isDebugged) { - Scriptable scope = frame.scope; - if (scope == null) { - Kit.codeBug(); - } else if (continuationRestart) { - // Walk the parent chain of frame.scope until a NativeCall is - // found. Normally, frame.scope is a NativeCall when called - // from initFrame() for a debugged or activatable function. - // However, when called from interpretLoop() as part of - // restarting a continuation, it can also be a NativeWith if - // the continuation was captured within a "with" or "catch" - // block ("catch" implicitly uses NativeWith to create a scope - // to expose the exception variable). - for (; ; ) { - if (scope instanceof NativeWith) { - scope = scope.getParentScope(); - if (scope == null - || (frame.parentFrame != null - && frame.parentFrame.scope == scope)) { - // If we get here, we didn't find a NativeCall in - // the call chain before reaching parent frame's - // scope. This should not be possible. - Kit.codeBug(); - break; // Never reached, but keeps the static analyzer - // happy about "scope" not being null 5 lines above. - } - } else { - break; - } - } - } - if (isDebugged) { - frame.debuggerFrame.onEnter(cx, scope, frame.thisObj, args); - } - // Enter activation only when itsNeedsActivation true, - // since debugger should not interfere with activation - // chaining - if (usesActivation) { - ScriptRuntime.enterActivationFunction(cx, scope); - } - } - } - - private static void exitFrame(Context cx, CallFrame frame, Object throwable) { - if (frame.idata.itsNeedsActivation) { - ScriptRuntime.exitActivationFunction(cx); - } - - if (frame.debuggerFrame != null) { - try { - if (throwable instanceof Throwable) { - frame.debuggerFrame.onExit(cx, true, throwable); - } else { - Object result; - ContinuationJump cjump = (ContinuationJump) throwable; - if (cjump == null) { - result = frame.result; - } else { - result = cjump.result; - } - if (result == DOUBLE_MARK) { - double resultDbl; - if (cjump == null) { - resultDbl = frame.resultDbl; - } else { - resultDbl = cjump.resultDbl; - } - result = ScriptRuntime.wrapNumber(resultDbl); - } - frame.debuggerFrame.onExit(cx, false, result); - } - } catch (Throwable ex) { - System.err.println("RHINO USAGE WARNING: onExit terminated with exception"); - ex.printStackTrace(System.err); - } - } - } - - private static void setCallResult(CallFrame frame, Object callResult, double callResultDbl) { - if (frame.savedCallOp == Token.CALL) { - frame.stack[frame.savedStackTop] = callResult; - frame.sDbl[frame.savedStackTop] = callResultDbl; - } else if (frame.savedCallOp == Token.NEW) { - // If construct returns scriptable, - // then it replaces on stack top saved original instance - // of the object. - if (callResult instanceof Scriptable) { - frame.stack[frame.savedStackTop] = callResult; - } - } else { - Kit.codeBug(); - } - frame.savedCallOp = 0; - } - - public static NativeContinuation captureContinuation(Context cx) { - if (cx.lastInterpreterFrame == null || !(cx.lastInterpreterFrame instanceof CallFrame)) { - throw new IllegalStateException("Interpreter frames not found"); - } - return captureContinuation(cx, (CallFrame) cx.lastInterpreterFrame, true); - } - - private static NativeContinuation captureContinuation( - Context cx, CallFrame frame, boolean requireContinuationsTopFrame) { - NativeContinuation c = new NativeContinuation(); - ScriptRuntime.setObjectProtoAndParent(c, ScriptRuntime.getTopCallScope(cx)); - - // Make sure that all frames are frozen - CallFrame x = frame; - CallFrame outermost = frame; - while (x != null && !x.frozen) { - x.frozen = true; - // Allow to GC unused stack space - for (int i = x.savedStackTop + 1; i != x.stack.length; ++i) { - // Allow to GC unused stack space - x.stack[i] = null; - x.stackAttributes[i] = ScriptableObject.EMPTY; - } - if (x.savedCallOp == Token.CALL) { - // the call will always overwrite the stack top with the result - x.stack[x.savedStackTop] = null; - } else { - if (x.savedCallOp != Token.NEW) Kit.codeBug(); - // the new operator uses stack top to store the constructed - // object so it shall not be cleared: see comments in - // setCallResult - } - outermost = x; - x = x.parentFrame; - } - - if (requireContinuationsTopFrame) { - while (outermost.parentFrame != null) outermost = outermost.parentFrame; - - if (!outermost.isContinuationsTopFrame) { - throw new IllegalStateException( - "Cannot capture continuation " - + "from JavaScript code not called directly by " - + "executeScriptWithContinuations or " - + "callFunctionWithContinuations"); - } - } - - c.initImplementation(frame); - return c; - } - - private static int stack_int32(CallFrame frame, int i) { - Object x = frame.stack[i]; - if (x == UniqueTag.DOUBLE_MARK) { - return ScriptRuntime.toInt32(frame.sDbl[i]); - } - return ScriptRuntime.toInt32(x); - } - - private static double stack_double(CallFrame frame, int i) { - Object x = frame.stack[i]; - if (x != UniqueTag.DOUBLE_MARK) { - return ScriptRuntime.toNumber(x); - } - return frame.sDbl[i]; - } - - private static Number stack_numeric(CallFrame frame, int i) { - Object x = frame.stack[i]; - if (x != UniqueTag.DOUBLE_MARK) { - return ScriptRuntime.toNumeric(x); - } - return frame.sDbl[i]; - } - - private static boolean stack_boolean(CallFrame frame, int i) { - Object x = frame.stack[i]; - if (Boolean.TRUE.equals(x)) { - return true; - } else if (Boolean.FALSE.equals(x)) { - return false; - } else if (x == UniqueTag.DOUBLE_MARK) { - double d = frame.sDbl[i]; - return !Double.isNaN(d) && d != 0.0; - } else if (x == null || x == Undefined.instance) { - return false; - } else if (x instanceof BigInteger) { - return !((BigInteger) x).equals(BigInteger.ZERO); - } else if (x instanceof Number) { - double d = ((Number) x).doubleValue(); - return (!Double.isNaN(d) && d != 0.0); - } else { - return ScriptRuntime.toBoolean(x); - } - } - - private static void doAdd(Object[] stack, double[] sDbl, int stackTop, Context cx) { - Object rhs = stack[stackTop + 1]; - Object lhs = stack[stackTop]; - double d; - boolean leftRightOrder; - if (rhs == DOUBLE_MARK) { - d = sDbl[stackTop + 1]; - if (lhs == DOUBLE_MARK) { - sDbl[stackTop] += d; - return; - } - leftRightOrder = true; - // fallthrough to object + number code - } else if (lhs == DOUBLE_MARK) { - d = sDbl[stackTop]; - lhs = rhs; - leftRightOrder = false; - // fallthrough to object + number code - } else { - if (lhs instanceof Scriptable || rhs instanceof Scriptable) { - stack[stackTop] = ScriptRuntime.add(lhs, rhs, cx); - - // the next two else if branches are a bit more tricky - // to reduce method calls - } else if (lhs instanceof CharSequence) { - if (rhs instanceof CharSequence) { - stack[stackTop] = new ConsString((CharSequence) lhs, (CharSequence) rhs); - } else { - stack[stackTop] = - new ConsString((CharSequence) lhs, ScriptRuntime.toCharSequence(rhs)); - } - } else if (rhs instanceof CharSequence) { - stack[stackTop] = - new ConsString(ScriptRuntime.toCharSequence(lhs), (CharSequence) rhs); - - } else { - Number lNum = (lhs instanceof Number) ? (Number) lhs : ScriptRuntime.toNumeric(lhs); - Number rNum = (rhs instanceof Number) ? (Number) rhs : ScriptRuntime.toNumeric(rhs); - - if (lNum instanceof BigInteger && rNum instanceof BigInteger) { - stack[stackTop] = ((BigInteger) lNum).add((BigInteger) rNum); - } else if (lNum instanceof BigInteger || rNum instanceof BigInteger) { - throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt"); - } else { - stack[stackTop] = DOUBLE_MARK; - sDbl[stackTop] = lNum.doubleValue() + rNum.doubleValue(); - } - } - return; - } - - // handle object(lhs) + number(d) code - if (lhs instanceof Scriptable) { - rhs = ScriptRuntime.wrapNumber(d); - if (!leftRightOrder) { - Object tmp = lhs; - lhs = rhs; - rhs = tmp; - } - stack[stackTop] = ScriptRuntime.add(lhs, rhs, cx); - } else if (lhs instanceof CharSequence) { - CharSequence rstr = ScriptRuntime.numberToString(d, 10); - if (leftRightOrder) { - stack[stackTop] = new ConsString((CharSequence) lhs, rstr); - } else { - stack[stackTop] = new ConsString(rstr, (CharSequence) lhs); - } - } else { - Number lNum = (lhs instanceof Number) ? (Number) lhs : ScriptRuntime.toNumeric(lhs); - if (lNum instanceof BigInteger) { - throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt"); - } else { - stack[stackTop] = DOUBLE_MARK; - sDbl[stackTop] = lNum.doubleValue() + d; - } - } - } - - private static int doArithmetic( - CallFrame frame, int op, Object[] stack, double[] sDbl, int stackTop) { - Number lNum = stack_numeric(frame, stackTop - 1); - Number rNum = stack_numeric(frame, stackTop); - --stackTop; - - Number result = null; - switch (op) { - case Token.SUB: - result = ScriptRuntime.subtract(lNum, rNum); - break; - case Token.MUL: - result = ScriptRuntime.multiply(lNum, rNum); - break; - case Token.DIV: - result = ScriptRuntime.divide(lNum, rNum); - break; - case Token.MOD: - result = ScriptRuntime.remainder(lNum, rNum); - break; - case Token.EXP: - result = ScriptRuntime.exponentiate(lNum, rNum); - break; - } - - if (result instanceof BigInteger) { - stack[stackTop] = result; - } else { - stack[stackTop] = DOUBLE_MARK; - sDbl[stackTop] = result.doubleValue(); - } - return stackTop; - } - - private static Object[] getArgsArray(Object[] stack, double[] sDbl, int shift, int count) { - if (count == 0) { - return ScriptRuntime.emptyArgs; - } - Object[] args = new Object[count]; - for (int i = 0; i != count; ++i, ++shift) { - Object val = stack[shift]; - if (val == UniqueTag.DOUBLE_MARK) { - val = ScriptRuntime.wrapNumber(sDbl[shift]); - } - args[i] = val; - } - return args; - } - - private static void addInstructionCount(Context cx, CallFrame frame, int extra) { - cx.instructionCount += frame.pc - frame.pcPrevBranch + extra; - if (cx.instructionCount > cx.instructionThreshold) { - cx.observeInstructionCount(cx.instructionCount); - cx.instructionCount = 0; - } - } -} diff --git a/src/main/java/org/mozilla/javascript/InterpreterData.java b/src/main/java/org/mozilla/javascript/InterpreterData.java deleted file mode 100644 index 8e95210d..00000000 --- a/src/main/java/org/mozilla/javascript/InterpreterData.java +++ /dev/null @@ -1,173 +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.math.BigInteger; -import java.util.Arrays; -import org.mozilla.javascript.debug.DebuggableScript; - -final class InterpreterData implements Serializable, DebuggableScript { - private static final long serialVersionUID = 5067677351589230234L; - - static final int INITIAL_MAX_ICODE_LENGTH = 1024; - static final int INITIAL_STRINGTABLE_SIZE = 64; - static final int INITIAL_NUMBERTABLE_SIZE = 64; - static final int INITIAL_BIGINTTABLE_SIZE = 64; - - InterpreterData( - int languageVersion, String sourceFile, String encodedSource, boolean isStrict) { - this.languageVersion = languageVersion; - this.itsSourceFile = sourceFile; - this.encodedSource = encodedSource; - this.isStrict = isStrict; - init(); - } - - InterpreterData(InterpreterData parent) { - this.parentData = parent; - this.languageVersion = parent.languageVersion; - this.itsSourceFile = parent.itsSourceFile; - this.encodedSource = parent.encodedSource; - this.isStrict = parent.isStrict; - init(); - } - - private void init() { - itsICode = new byte[INITIAL_MAX_ICODE_LENGTH]; - itsStringTable = new String[INITIAL_STRINGTABLE_SIZE]; - itsBigIntTable = new BigInteger[INITIAL_BIGINTTABLE_SIZE]; - } - - String itsName; - String itsSourceFile; - boolean itsNeedsActivation; - int itsFunctionType; - - String[] itsStringTable; - double[] itsDoubleTable; - BigInteger[] itsBigIntTable; - InterpreterData[] itsNestedFunctions; - Object[] itsRegExpLiterals; - Object[] itsTemplateLiterals; - - byte[] itsICode; - - int[] itsExceptionTable; - - int itsMaxVars; - int itsMaxLocals; - int itsMaxStack; - int itsMaxFrameArray; - - // see comments in NativeFuncion for definition of argNames and argCount - String[] argNames; - boolean[] argIsConst; - int argCount; - - int itsMaxCalleeArgs; - - String encodedSource; - int encodedSourceStart; - int encodedSourceEnd; - - int languageVersion; - - boolean isStrict; - boolean topLevel; - boolean isES6Generator; - - Object[] literalIds; - - UintMap longJumps; - - int firstLinePC = -1; // PC for the first LINE icode - - InterpreterData parentData; - - boolean evalScriptFlag; // true if script corresponds to eval() code - - private int icodeHashCode = 0; - - /** true if the function has been declared like "var foo = function() {...}" */ - boolean declaredAsVar; - - /** true if the function has been declared like "!function() {}". */ - boolean declaredAsFunctionExpression; - - @Override - public boolean isTopLevel() { - return topLevel; - } - - @Override - public boolean isFunction() { - return itsFunctionType != 0; - } - - @Override - public String getFunctionName() { - return itsName; - } - - @Override - public int getParamCount() { - return argCount; - } - - @Override - public int getParamAndVarCount() { - return argNames.length; - } - - @Override - public String getParamOrVarName(int index) { - return argNames[index]; - } - - public boolean getParamOrVarConst(int index) { - return argIsConst[index]; - } - - @Override - public String getSourceName() { - return itsSourceFile; - } - - @Override - public boolean isGeneratedScript() { - return ScriptRuntime.isGeneratedScript(itsSourceFile); - } - - @Override - public int[] getLineNumbers() { - return Interpreter.getLineNumbers(this); - } - - @Override - public int getFunctionCount() { - return (itsNestedFunctions == null) ? 0 : itsNestedFunctions.length; - } - - @Override - public DebuggableScript getFunction(int index) { - return itsNestedFunctions[index]; - } - - @Override - public DebuggableScript getParent() { - return parentData; - } - - public int icodeHashCode() { - int h = icodeHashCode; - if (h == 0) { - icodeHashCode = h = Arrays.hashCode(itsICode); - } - return h; - } -} diff --git a/src/main/java/org/mozilla/javascript/IteratorLikeIterable.java b/src/main/java/org/mozilla/javascript/IteratorLikeIterable.java deleted file mode 100644 index ee38c1e8..00000000 --- a/src/main/java/org/mozilla/javascript/IteratorLikeIterable.java +++ /dev/null @@ -1,109 +0,0 @@ -/* 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.Closeable; -import java.util.Iterator; -import java.util.NoSuchElementException; - -/** - * This is a class that makes it easier to iterate over "iterator-like" objects as defined in the - * ECMAScript spec. The caller is responsible for retrieving an object that implements the - * "iterator" pattern. This class will follow that pattern and throw appropriate JavaScript - * exceptions. - * - *

The pattern that the target class should follow is: * It must have a function property called - * "next" * The function must return an object with a boolean value called "done". * If "done" is - * true, then the returned object should also contain a "value" property. * If it has a function - * property called "return" then it will be called when the caller is done iterating. - */ -public class IteratorLikeIterable implements Iterable, Closeable { - private final Context cx; - private final Scriptable scope; - private final Callable next; - private final Callable returnFunc; - private final Scriptable iterator; - private boolean closed; - - public IteratorLikeIterable(Context cx, Scriptable scope, Object target) { - this.cx = cx; - this.scope = scope; - // This will throw if "next" is not a function or undefined - next = ScriptRuntime.getPropFunctionAndThis(target, ES6Iterator.NEXT_METHOD, cx, scope); - iterator = ScriptRuntime.lastStoredScriptable(cx); - Object retObj = - ScriptRuntime.getObjectPropNoWarn(target, ES6Iterator.RETURN_PROPERTY, cx, scope); - // We only care about "return" if it is not null or undefined - if ((retObj != null) && !Undefined.isUndefined(retObj)) { - if (!(retObj instanceof Callable)) { - throw ScriptRuntime.notFunctionError(target, retObj, ES6Iterator.RETURN_PROPERTY); - } - returnFunc = (Callable) retObj; - } else { - returnFunc = null; - } - } - - @Override - public void close() { - if (!closed) { - closed = true; - if (returnFunc != null) { - returnFunc.call(cx, scope, iterator, ScriptRuntime.emptyArgs); - } - } - } - - @Override - public Itr iterator() { - return new Itr(); - } - - public final class Itr implements Iterator { - private Object nextVal; - private boolean isDone; - - @Override - public boolean hasNext() { - if (isDone) { - return false; - } - Object val = next.call(cx, scope, iterator, ScriptRuntime.emptyArgs); - // This will throw if "val" is not an object. - // "getObjectPropNoWarn" won't, so do this as follows. - Object doneval = - ScriptableObject.getProperty( - ScriptableObject.ensureScriptable(val), ES6Iterator.DONE_PROPERTY); - if (doneval == Scriptable.NOT_FOUND) { - doneval = Undefined.instance; - } - // It's OK if done is undefined. - if (ScriptRuntime.toBoolean(doneval)) { - isDone = true; - return false; - } - nextVal = ScriptRuntime.getObjectPropNoWarn(val, ES6Iterator.VALUE_PROPERTY, cx, scope); - return true; - } - - @Override - public Object next() { - if (isDone) { - throw new NoSuchElementException(); - } - return nextVal; - } - - /** Find out if "hasNext" returned done without invoking the function again. */ - public boolean isDone() { - return isDone; - } - - /** Manually set "done." Used for exception handling in promises. */ - public void setDone(boolean done) { - this.isDone = done; - } - } -} diff --git a/src/main/java/org/mozilla/javascript/JavaAdapter.java b/src/main/java/org/mozilla/javascript/JavaAdapter.java deleted file mode 100644 index b22ad75b..00000000 --- a/src/main/java/org/mozilla/javascript/JavaAdapter.java +++ /dev/null @@ -1,1197 +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.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.security.CodeSource; -import java.security.ProtectionDomain; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Map; - -import org.mozilla.classfile.ByteCode; -import org.mozilla.classfile.ClassFileWriter; - -public final class JavaAdapter implements IdFunctionCall -{ - /** - * Provides a key with which to distinguish previously generated - * adapter classes stored in a hash table. - */ - static class JavaAdapterSignature - { - Class superClass; - Class[] interfaces; - ObjToIntMap names; - - JavaAdapterSignature(Class superClass, Class[] interfaces, - ObjToIntMap names) - { - this.superClass = superClass; - this.interfaces = interfaces; - this.names = names; - } - - @Override - public boolean equals(Object obj) - { - if (!(obj instanceof JavaAdapterSignature)) - return false; - JavaAdapterSignature sig = (JavaAdapterSignature) obj; - if (superClass != sig.superClass) - return false; - if (interfaces != sig.interfaces) { - if (interfaces.length != sig.interfaces.length) - return false; - for (int i=0; i < interfaces.length; i++) - if (interfaces[i] != sig.interfaces[i]) - return false; - } - if (names.size() != sig.names.size()) - return false; - ObjToIntMap.Iterator iter = new ObjToIntMap.Iterator(names); - for (iter.start(); !iter.done(); iter.next()) { - String name = (String)iter.getKey(); - int arity = iter.getValue(); - if (arity != sig.names.get(name, arity + 1)) - return false; - } - return true; - } - - @Override - public int hashCode() - { - return (superClass.hashCode() + Arrays.hashCode(interfaces)) ^ names.size(); - } - } - - public static void init(Context cx, Scriptable scope, boolean sealed) - { - JavaAdapter obj = new JavaAdapter(); - IdFunctionObject ctor = new IdFunctionObject(obj, FTAG, Id_JavaAdapter, - "JavaAdapter", 1, scope); - ctor.markAsConstructor(null); - if (sealed) { - ctor.sealObject(); - } - ctor.exportAsScopeProperty(); - } - - @Override - public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) - { - if (f.hasTag(FTAG)) { - if (f.methodId() == Id_JavaAdapter) { - return js_createAdapter(cx, scope, args); - } - } - throw f.unknown(); - } - - public static Object convertResult(Object result, Class c) - { - if (result == Undefined.instance && - (c != ScriptRuntime.ObjectClass && - c != ScriptRuntime.StringClass)) - { - // Avoid an error for an undefined value; return null instead. - return null; - } - return Context.jsToJava(result, c); - } - - public static Scriptable createAdapterWrapper(Scriptable obj, Object adapter) - { - Scriptable scope = ScriptableObject.getTopLevelScope(obj); - NativeJavaObject res = new NativeJavaObject(scope, adapter, null, true); - res.setPrototype(obj); - return res; - } - - public static Object getAdapterSelf(Class adapterClass, Object adapter) - throws NoSuchFieldException, IllegalAccessException - { - Field self = adapterClass.getDeclaredField("self"); - return self.get(adapter); - } - - static Object js_createAdapter(Context cx, Scriptable scope, Object[] args) - { - int N = args.length; - if (N == 0) { - throw ScriptRuntime.typeErrorById("msg.adapter.zero.args"); - } - - // Expected arguments: - // Any number of NativeJavaClass objects representing the super-class - // and/or interfaces to implement, followed by one NativeObject providing - // the implementation, followed by any number of arguments to pass on - // to the (super-class) constructor. - - int classCount; - for (classCount = 0; classCount < N - 1; classCount++) { - Object arg = args[classCount]; - // We explicitly test for NativeObject here since checking for - // instanceof ScriptableObject or !(instanceof NativeJavaClass) - // would fail for a Java class that isn't found in the class path - // as NativeJavaPackage extends ScriptableObject. - if (arg instanceof NativeObject) { - break; - } - if (!(arg instanceof NativeJavaClass)) { - throw ScriptRuntime.typeErrorById("msg.not.java.class.arg", - String.valueOf(classCount), - ScriptRuntime.toString(arg)); - } - } - Class superClass = null; - Class[] intfs = new Class[classCount]; - int interfaceCount = 0; - for (int i = 0; i < classCount; ++i) { - Class c = ((NativeJavaClass) args[i]).getClassObject(); - if (!c.isInterface()) { - if (superClass != null) { - throw ScriptRuntime.typeErrorById("msg.only.one.super", - superClass.getName(), c.getName()); - } - superClass = c; - } else { - intfs[interfaceCount++] = c; - } - } - - if (superClass == null) { - superClass = ScriptRuntime.ObjectClass; - } - - Class[] interfaces = new Class[interfaceCount]; - System.arraycopy(intfs, 0, interfaces, 0, interfaceCount); - // next argument is implementation, must be scriptable - Scriptable obj = ScriptableObject.ensureScriptable(args[classCount]); - - Class adapterClass = getAdapterClass(scope, superClass, interfaces, obj); - Object adapter; - - int argsCount = N - classCount - 1; - try { - if (argsCount > 0) { - // Arguments contain parameters for super-class constructor. - // We use the generic Java method lookup logic to find and - // invoke the right constructor. - Object[] ctorArgs = new Object[argsCount + 2]; - ctorArgs[0] = obj; - ctorArgs[1] = cx.getFactory(); - System.arraycopy(args, classCount + 1, ctorArgs, 2, argsCount); - // TODO: cache class wrapper? - NativeJavaClass classWrapper = new NativeJavaClass(scope, - adapterClass, true); - NativeJavaMethod ctors = classWrapper.members.ctors; - int index = ctors.findCachedFunction(cx, ctorArgs); - if (index < 0) { - String sig = NativeJavaMethod.scriptSignature(args); - throw Context.reportRuntimeErrorById( - "msg.no.java.ctor", adapterClass.getName(), sig); - } - - // Found the constructor, so try invoking it. - adapter = NativeJavaClass.constructInternal(ctorArgs, ctors.methods[index]); - } else { - Class[] ctorParms = { - ScriptRuntime.ScriptableClass, - ScriptRuntime.ContextFactoryClass - }; - Object[] ctorArgs = { obj, cx.getFactory() }; - adapter = adapterClass.getConstructor(ctorParms).newInstance(ctorArgs); - } - - Object self = getAdapterSelf(adapterClass, adapter); - // Return unwrapped JavaAdapter if it implements Scriptable - if (self instanceof Wrapper) { - Object unwrapped = ((Wrapper) self).unwrap(); - if (unwrapped instanceof Scriptable) { - if (unwrapped instanceof ScriptableObject) { - ScriptRuntime.setObjectProtoAndParent( - (ScriptableObject)unwrapped, scope); - } - return unwrapped; - } - } - return self; - } catch (Exception ex) { - throw Context.throwAsScriptRuntimeEx(ex); - } - } - - // Needed by NativeJavaObject serializer - public static void writeAdapterObject(Object javaObject, - ObjectOutputStream out) - throws IOException - { - Class cl = javaObject.getClass(); - out.writeObject(cl.getSuperclass().getName()); - - Class[] interfaces = cl.getInterfaces(); - String[] interfaceNames = new String[interfaces.length]; - - for (int i=0; i < interfaces.length; i++) - interfaceNames[i] = interfaces[i].getName(); - - out.writeObject(interfaceNames); - - try { - Object delegee = cl.getField("delegee").get(javaObject); - out.writeObject(delegee); - return; - } catch (IllegalAccessException e) { - } catch (NoSuchFieldException e) { - } - throw new IOException(); - } - - // Needed by NativeJavaObject de-serializer - public static Object readAdapterObject(Scriptable self, - ObjectInputStream in) - throws IOException, ClassNotFoundException - { - ContextFactory factory; - Context cx = Context.getCurrentContext(); - if (cx != null) { - factory = cx.getFactory(); - } else { - factory = null; - } - - Class superClass = Class.forName((String)in.readObject()); - - String[] interfaceNames = (String[])in.readObject(); - Class[] interfaces = new Class[interfaceNames.length]; - - for (int i=0; i < interfaceNames.length; i++) - interfaces[i] = Class.forName(interfaceNames[i]); - - Scriptable delegee = (Scriptable)in.readObject(); - - Class adapterClass = getAdapterClass(self, superClass, interfaces, - delegee); - - Class[] ctorParms = { - ScriptRuntime.ContextFactoryClass, - ScriptRuntime.ScriptableClass, - ScriptRuntime.ScriptableClass - }; - Object[] ctorArgs = { factory, delegee, self }; - try { - return adapterClass.getConstructor(ctorParms).newInstance(ctorArgs); - } catch(InstantiationException e) { - } catch(IllegalAccessException e) { - } catch(InvocationTargetException e) { - } catch(NoSuchMethodException e) { - } - - throw new ClassNotFoundException("adapter"); - } - - private static ObjToIntMap getObjectFunctionNames(Scriptable obj) - { - Object[] ids = ScriptableObject.getPropertyIds(obj); - ObjToIntMap map = new ObjToIntMap(ids.length); - for (int i = 0; i != ids.length; ++i) { - if (!(ids[i] instanceof String)) - continue; - String id = (String) ids[i]; - Object value = ScriptableObject.getProperty(obj, id); - if (value instanceof Function) { - Function f = (Function)value; - int length = ScriptRuntime.toInt32( - ScriptableObject.getProperty(f, "length")); - if (length < 0) { - length = 0; - } - map.put(id, length); - } - } - return map; - } - - private static Class getAdapterClass(Scriptable scope, Class superClass, - Class[] interfaces, Scriptable obj) - { - ClassCache cache = ClassCache.get(scope); - Map> generated - = cache.getInterfaceAdapterCacheMap(); - - ObjToIntMap names = getObjectFunctionNames(obj); - JavaAdapterSignature sig; - sig = new JavaAdapterSignature(superClass, interfaces, names); - Class adapterClass = generated.get(sig); - if (adapterClass == null) { - String adapterName = "adapter" + cache.newClassSerialNumber(); - byte[] code = createAdapterCode(names, adapterName, - superClass, interfaces, null); - - adapterClass = loadAdapterClass(adapterName, code); - if (cache.isCachingEnabled()) { - generated.put(sig, adapterClass); - } - } - return adapterClass; - } - - public static byte[] createAdapterCode(ObjToIntMap functionNames, - String adapterName, - Class superClass, - Class[] interfaces, - String scriptClassName) - { - ClassFileWriter cfw = new ClassFileWriter(adapterName, - superClass.getName(), - ""); - cfw.addField("factory", "Lorg/mozilla/javascript/ContextFactory;", - (short) (ClassFileWriter.ACC_PUBLIC | - ClassFileWriter.ACC_FINAL)); - cfw.addField("delegee", "Lorg/mozilla/javascript/Scriptable;", - (short) (ClassFileWriter.ACC_PUBLIC | - ClassFileWriter.ACC_FINAL)); - cfw.addField("self", "Lorg/mozilla/javascript/Scriptable;", - (short) (ClassFileWriter.ACC_PUBLIC | - ClassFileWriter.ACC_FINAL)); - int interfacesCount = interfaces == null ? 0 : interfaces.length; - for (int i=0; i < interfacesCount; i++) { - if (interfaces[i] != null) - cfw.addInterface(interfaces[i].getName()); - } - - String superName = superClass.getName().replace('.', '/'); - Constructor[] ctors = superClass.getDeclaredConstructors(); - for (Constructor ctor : ctors) { - int mod = ctor.getModifiers(); - if (Modifier.isPublic(mod) || Modifier.isProtected(mod)) { - generateCtor(cfw, adapterName, superName, ctor); - } - } - generateSerialCtor(cfw, adapterName, superName); - if (scriptClassName != null) { - generateEmptyCtor(cfw, adapterName, superName, scriptClassName); - } - - ObjToIntMap generatedOverrides = new ObjToIntMap(); - ObjToIntMap generatedMethods = new ObjToIntMap(); - - // generate methods to satisfy all specified interfaces. - for (int i = 0; i < interfacesCount; i++) { - Method[] methods = interfaces[i].getMethods(); - for (int j = 0; j < methods.length; j++) { - Method method = methods[j]; - int mods = method.getModifiers(); - if (Modifier.isStatic(mods) || Modifier.isFinal(mods) || method.isDefault()) { - continue; - } - String methodName = method.getName(); - Class[] argTypes = method.getParameterTypes(); - if (!functionNames.has(methodName)) { - try { - superClass.getMethod(methodName, argTypes); - // The class we're extending implements this method and - // the JavaScript object doesn't have an override. See - // bug 61226. - continue; - } catch (NoSuchMethodException e) { - // Not implemented by superclass; fall through - } - } - // make sure to generate only one instance of a particular - // method/signature. - String methodSignature = getMethodSignature(method, argTypes); - String methodKey = methodName + methodSignature; - if (! generatedOverrides.has(methodKey)) { - generateMethod(cfw, adapterName, methodName, argTypes, - method.getReturnType(), true); - generatedOverrides.put(methodKey, 0); - generatedMethods.put(methodName, 0); - } - } - } - - // Now, go through the superclass's methods, checking for abstract - // methods or additional methods to override. - - // generate any additional overrides that the object might contain. - Method[] methods = getOverridableMethods(superClass); - for (int j = 0; j < methods.length; j++) { - Method method = methods[j]; - int mods = method.getModifiers(); - // if a method is marked abstract, must implement it or the - // resulting class won't be instantiable. otherwise, if the object - // has a property of the same name, then an override is intended. - boolean isAbstractMethod = Modifier.isAbstract(mods); - String methodName = method.getName(); - if (isAbstractMethod || functionNames.has(methodName)) { - // make sure to generate only one instance of a particular - // method/signature. - Class[] argTypes = method.getParameterTypes(); - String methodSignature = getMethodSignature(method, argTypes); - String methodKey = methodName + methodSignature; - if (! generatedOverrides.has(methodKey)) { - generateMethod(cfw, adapterName, methodName, argTypes, - method.getReturnType(), true); - generatedOverrides.put(methodKey, 0); - generatedMethods.put(methodName, 0); - - // if a method was overridden, generate a "super$method" - // which lets the delegate call the superclass' version. - if (!isAbstractMethod) { - generateSuper(cfw, adapterName, superName, - methodName, methodSignature, - argTypes, method.getReturnType()); - } - } - } - } - - // Generate Java methods for remaining properties that are not - // overrides. - ObjToIntMap.Iterator iter = new ObjToIntMap.Iterator(functionNames); - for (iter.start(); !iter.done(); iter.next()) { - String functionName = (String)iter.getKey(); - if (generatedMethods.has(functionName)) - continue; - int length = iter.getValue(); - Class[] parms = new Class[length]; - for (int k=0; k < length; k++) - parms[k] = ScriptRuntime.ObjectClass; - generateMethod(cfw, adapterName, functionName, parms, - ScriptRuntime.ObjectClass, false); - } - return cfw.toByteArray(); - } - - static Method[] getOverridableMethods(Class clazz) - { - ArrayList list = new ArrayList(); - HashSet skip = new HashSet(); - // Check superclasses before interfaces so we always choose - // implemented methods over abstract ones, even if a subclass - // re-implements an interface already implemented in a superclass - // (e.g. java.util.ArrayList) - for (Class c = clazz; c != null; c = c.getSuperclass()) { - appendOverridableMethods(c, list, skip); - } - for (Class c = clazz; c != null; c = c.getSuperclass()) { - for (Class intf: c.getInterfaces()) - appendOverridableMethods(intf, list, skip); - } - return list.toArray(new Method[list.size()]); - } - - private static void appendOverridableMethods(Class c, - ArrayList list, HashSet skip) - { - Method[] methods=c.isInterface() ? c.getMethods() : c.getDeclaredMethods(); - - for (int i = 0; i < methods.length; i++) { - String methodKey = methods[i].getName() + - getMethodSignature(methods[i], - methods[i].getParameterTypes()); - if (skip.contains(methodKey)) - continue; // skip this method - int mods = methods[i].getModifiers(); - if (Modifier.isStatic(mods)) - continue; - if (Modifier.isFinal(mods)) { - // Make sure we don't add a final method to the list - // of overridable methods. - skip.add(methodKey); - continue; - } - if (Modifier.isPublic(mods) || Modifier.isProtected(mods)) { - list.add(methods[i]); - skip.add(methodKey); - } - } - } - - static Class loadAdapterClass(String className, byte[] classBytes) - { - Object staticDomain; - Class domainClass = SecurityController.getStaticSecurityDomainClass(); - if(domainClass == CodeSource.class || domainClass == ProtectionDomain.class) { - // use the calling script's security domain if available - ProtectionDomain protectionDomain = SecurityUtilities.getScriptProtectionDomain(); - if (protectionDomain == null) { - protectionDomain = JavaAdapter.class.getProtectionDomain(); - } - if(domainClass == CodeSource.class) { - staticDomain = protectionDomain == null ? null : protectionDomain.getCodeSource(); - } - else { - staticDomain = protectionDomain; - } - } - else { - staticDomain = null; - } - GeneratedClassLoader loader = SecurityController.createLoader(null, - staticDomain); - Class result = loader.defineClass(className, classBytes); - loader.linkClass(result); - return result; - } - - public static Function getFunction(Scriptable obj, String functionName) - { - Object x = ScriptableObject.getProperty(obj, functionName); - if (x == Scriptable.NOT_FOUND) { - // This method used to swallow the exception from calling - // an undefined method. People have come to depend on this - // somewhat dubious behavior. It allows people to avoid - // implementing listener methods that they don't care about, - // for instance. - return null; - } - if (!(x instanceof Function)) - throw ScriptRuntime.notFunctionError(x, functionName); - - return (Function)x; - } - - /** - * Utility method which dynamically binds a Context to the current thread, - * if none already exists. - */ - public static Object callMethod(ContextFactory factory, - final Scriptable thisObj, - final Function f, final Object[] args, - final long argsToWrap) - { - if (f == null) { - // See comments in getFunction - return null; - } - if (factory == null) { - factory = ContextFactory.getGlobal(); - } - - final Scriptable scope = f.getParentScope(); - if (argsToWrap == 0) { - return Context.call(factory, f, scope, thisObj, args); - } - - Context cx = Context.getCurrentContext(); - if (cx != null) { - return doCall(cx, scope, thisObj, f, args, argsToWrap); - } - return factory.call(cx2 -> doCall(cx2, scope, thisObj, f, args, argsToWrap)); - } - - private static Object doCall(Context cx, Scriptable scope, - Scriptable thisObj, Function f, - Object[] args, long argsToWrap) - { - // Wrap the rest of objects - for (int i = 0; i != args.length; ++i) { - if (0 != (argsToWrap & (1 << i))) { - Object arg = args[i]; - if (!(arg instanceof Scriptable)) { - args[i] = cx.getWrapFactory().wrap(cx, scope, arg, - null); - } - } - } - return f.call(cx, scope, thisObj, args); - } - - public static Scriptable runScript(final Script script) - { - return ContextFactory.getGlobal().call(cx -> { - ScriptableObject global = ScriptRuntime.getGlobal(cx); - script.exec(cx, global); - return global; - }); - } - - private static void generateCtor(ClassFileWriter cfw, String adapterName, - String superName, Constructor superCtor) - { - short locals = 3; // this + factory + delegee - Class[] parameters = superCtor.getParameterTypes(); - - // Note that we swapped arguments in app-facing constructors to avoid - // conflicting signatures with serial constructor defined below. - if (parameters.length == 0) { - cfw.startMethod("", - "(Lorg/mozilla/javascript/Scriptable;" - +"Lorg/mozilla/javascript/ContextFactory;)V", - ClassFileWriter.ACC_PUBLIC); - - // Invoke base class constructor - cfw.add(ByteCode.ALOAD_0); // this - cfw.addInvoke(ByteCode.INVOKESPECIAL, superName, "", "()V"); - } else { - StringBuilder sig = new StringBuilder( - "(Lorg/mozilla/javascript/Scriptable;" - +"Lorg/mozilla/javascript/ContextFactory;"); - int marker = sig.length(); // lets us reuse buffer for super signature - for (Class c : parameters) { - appendTypeString(sig, c); - } - sig.append(")V"); - cfw.startMethod("", sig.toString(), ClassFileWriter.ACC_PUBLIC); - - // Invoke base class constructor - cfw.add(ByteCode.ALOAD_0); // this - short paramOffset = 3; - for (Class parameter : parameters) { - paramOffset += generatePushParam(cfw, paramOffset, parameter); - } - locals = paramOffset; - sig.delete(1, marker); - cfw.addInvoke(ByteCode.INVOKESPECIAL, superName, "", sig.toString()); - } - - // Save parameter in instance variable "delegee" - cfw.add(ByteCode.ALOAD_0); // this - cfw.add(ByteCode.ALOAD_1); // first arg: Scriptable delegee - cfw.add(ByteCode.PUTFIELD, adapterName, "delegee", - "Lorg/mozilla/javascript/Scriptable;"); - - // Save parameter in instance variable "factory" - cfw.add(ByteCode.ALOAD_0); // this - cfw.add(ByteCode.ALOAD_2); // second arg: ContextFactory instance - cfw.add(ByteCode.PUTFIELD, adapterName, "factory", - "Lorg/mozilla/javascript/ContextFactory;"); - - cfw.add(ByteCode.ALOAD_0); // this for the following PUTFIELD for self - // create a wrapper object to be used as "this" in method calls - cfw.add(ByteCode.ALOAD_1); // the Scriptable delegee - cfw.add(ByteCode.ALOAD_0); // this - cfw.addInvoke(ByteCode.INVOKESTATIC, - "org/mozilla/javascript/JavaAdapter", - "createAdapterWrapper", - "(Lorg/mozilla/javascript/Scriptable;" - +"Ljava/lang/Object;" - +")Lorg/mozilla/javascript/Scriptable;"); - cfw.add(ByteCode.PUTFIELD, adapterName, "self", - "Lorg/mozilla/javascript/Scriptable;"); - - cfw.add(ByteCode.RETURN); - cfw.stopMethod(locals); - } - - private static void generateSerialCtor(ClassFileWriter cfw, - String adapterName, - String superName) - { - cfw.startMethod("", - "(Lorg/mozilla/javascript/ContextFactory;" - +"Lorg/mozilla/javascript/Scriptable;" - +"Lorg/mozilla/javascript/Scriptable;" - +")V", - ClassFileWriter.ACC_PUBLIC); - - // Invoke base class constructor - cfw.add(ByteCode.ALOAD_0); // this - cfw.addInvoke(ByteCode.INVOKESPECIAL, superName, "", "()V"); - - // Save parameter in instance variable "factory" - cfw.add(ByteCode.ALOAD_0); // this - cfw.add(ByteCode.ALOAD_1); // first arg: ContextFactory instance - cfw.add(ByteCode.PUTFIELD, adapterName, "factory", - "Lorg/mozilla/javascript/ContextFactory;"); - - // Save parameter in instance variable "delegee" - cfw.add(ByteCode.ALOAD_0); // this - cfw.add(ByteCode.ALOAD_2); // second arg: Scriptable delegee - cfw.add(ByteCode.PUTFIELD, adapterName, "delegee", - "Lorg/mozilla/javascript/Scriptable;"); - // save self - cfw.add(ByteCode.ALOAD_0); // this - cfw.add(ByteCode.ALOAD_3); // third arg: Scriptable self - cfw.add(ByteCode.PUTFIELD, adapterName, "self", - "Lorg/mozilla/javascript/Scriptable;"); - - cfw.add(ByteCode.RETURN); - cfw.stopMethod((short)4); // 4: this + factory + delegee + self - } - - private static void generateEmptyCtor(ClassFileWriter cfw, - String adapterName, - String superName, - String scriptClassName) - { - cfw.startMethod("", "()V", ClassFileWriter.ACC_PUBLIC); - - // Invoke base class constructor - cfw.add(ByteCode.ALOAD_0); // this - cfw.addInvoke(ByteCode.INVOKESPECIAL, superName, "", "()V"); - - // Set factory to null to use current global when necessary - cfw.add(ByteCode.ALOAD_0); - cfw.add(ByteCode.ACONST_NULL); - cfw.add(ByteCode.PUTFIELD, adapterName, "factory", - "Lorg/mozilla/javascript/ContextFactory;"); - - // Load script class - cfw.add(ByteCode.NEW, scriptClassName); - cfw.add(ByteCode.DUP); - cfw.addInvoke(ByteCode.INVOKESPECIAL, scriptClassName, "", "()V"); - - // Run script and save resulting scope - cfw.addInvoke(ByteCode.INVOKESTATIC, - "org/mozilla/javascript/JavaAdapter", - "runScript", - "(Lorg/mozilla/javascript/Script;" - +")Lorg/mozilla/javascript/Scriptable;"); - cfw.add(ByteCode.ASTORE_1); - - // Save the Scriptable in instance variable "delegee" - cfw.add(ByteCode.ALOAD_0); // this - cfw.add(ByteCode.ALOAD_1); // the Scriptable - cfw.add(ByteCode.PUTFIELD, adapterName, "delegee", - "Lorg/mozilla/javascript/Scriptable;"); - - cfw.add(ByteCode.ALOAD_0); // this for the following PUTFIELD for self - // create a wrapper object to be used as "this" in method calls - cfw.add(ByteCode.ALOAD_1); // the Scriptable - cfw.add(ByteCode.ALOAD_0); // this - cfw.addInvoke(ByteCode.INVOKESTATIC, - "org/mozilla/javascript/JavaAdapter", - "createAdapterWrapper", - "(Lorg/mozilla/javascript/Scriptable;" - +"Ljava/lang/Object;" - +")Lorg/mozilla/javascript/Scriptable;"); - cfw.add(ByteCode.PUTFIELD, adapterName, "self", - "Lorg/mozilla/javascript/Scriptable;"); - - cfw.add(ByteCode.RETURN); - cfw.stopMethod((short)2); // this + delegee - } - - /** - * Generates code to wrap Java arguments into Object[]. - * Non-primitive Java types are left as-is pending conversion - * in the helper method. Leaves the array object on the top of the stack. - */ - static void generatePushWrappedArgs(ClassFileWriter cfw, - Class[] argTypes, - int arrayLength) - { - // push arguments - cfw.addPush(arrayLength); - cfw.add(ByteCode.ANEWARRAY, "java/lang/Object"); - int paramOffset = 1; - for (int i = 0; i != argTypes.length; ++i) { - cfw.add(ByteCode.DUP); // duplicate array reference - cfw.addPush(i); - paramOffset += generateWrapArg(cfw, paramOffset, argTypes[i]); - cfw.add(ByteCode.AASTORE); - } - } - - /** - * Generates code to wrap Java argument into Object. - * Non-primitive Java types are left unconverted pending conversion - * in the helper method. Leaves the wrapper object on the top of the stack. - */ - private static int generateWrapArg(ClassFileWriter cfw, int paramOffset, - Class argType) - { - int size = 1; - if (!argType.isPrimitive()) { - cfw.add(ByteCode.ALOAD, paramOffset); - - } else if (argType == Boolean.TYPE) { - // wrap boolean values with java.lang.Boolean. - cfw.add(ByteCode.NEW, "java/lang/Boolean"); - cfw.add(ByteCode.DUP); - cfw.add(ByteCode.ILOAD, paramOffset); - cfw.addInvoke(ByteCode.INVOKESPECIAL, "java/lang/Boolean", - "", "(Z)V"); - - } else if (argType == Character.TYPE) { - // Create a string of length 1 using the character parameter. - cfw.add(ByteCode.ILOAD, paramOffset); - cfw.addInvoke(ByteCode.INVOKESTATIC, "java/lang/String", - "valueOf", "(C)Ljava/lang/String;"); - - } else { - // convert all numeric values to java.lang.Double. - cfw.add(ByteCode.NEW, "java/lang/Double"); - cfw.add(ByteCode.DUP); - String typeName = argType.getName(); - switch (typeName.charAt(0)) { - case 'b': - case 's': - case 'i': - // load an int value, convert to double. - cfw.add(ByteCode.ILOAD, paramOffset); - cfw.add(ByteCode.I2D); - break; - case 'l': - // load a long, convert to double. - cfw.add(ByteCode.LLOAD, paramOffset); - cfw.add(ByteCode.L2D); - size = 2; - break; - case 'f': - // load a float, convert to double. - cfw.add(ByteCode.FLOAD, paramOffset); - cfw.add(ByteCode.F2D); - break; - case 'd': - cfw.add(ByteCode.DLOAD, paramOffset); - size = 2; - break; - } - cfw.addInvoke(ByteCode.INVOKESPECIAL, "java/lang/Double", - "", "(D)V"); - } - return size; - } - - /** - * Generates code to convert a wrapped value type to a primitive type. - * Handles unwrapping java.lang.Boolean, and java.lang.Number types. - * Generates the appropriate RETURN bytecode. - */ - static void generateReturnResult(ClassFileWriter cfw, Class retType, - boolean callConvertResult) - { - // wrap boolean values with java.lang.Boolean, convert all other - // primitive values to java.lang.Double. - if (retType == Void.TYPE) { - cfw.add(ByteCode.POP); - cfw.add(ByteCode.RETURN); - - } else if (retType == Boolean.TYPE) { - cfw.addInvoke(ByteCode.INVOKESTATIC, - "org/mozilla/javascript/Context", - "toBoolean", "(Ljava/lang/Object;)Z"); - cfw.add(ByteCode.IRETURN); - - } else if (retType == Character.TYPE) { - // characters are represented as strings in JavaScript. - // return the first character. - // first convert the value to a string if possible. - cfw.addInvoke(ByteCode.INVOKESTATIC, - "org/mozilla/javascript/Context", - "toString", - "(Ljava/lang/Object;)Ljava/lang/String;"); - cfw.add(ByteCode.ICONST_0); - cfw.addInvoke(ByteCode.INVOKEVIRTUAL, "java/lang/String", - "charAt", "(I)C"); - cfw.add(ByteCode.IRETURN); - - } else if (retType.isPrimitive()) { - cfw.addInvoke(ByteCode.INVOKESTATIC, - "org/mozilla/javascript/Context", - "toNumber", "(Ljava/lang/Object;)D"); - String typeName = retType.getName(); - switch (typeName.charAt(0)) { - case 'b': - case 's': - case 'i': - cfw.add(ByteCode.D2I); - cfw.add(ByteCode.IRETURN); - break; - case 'l': - cfw.add(ByteCode.D2L); - cfw.add(ByteCode.LRETURN); - break; - case 'f': - cfw.add(ByteCode.D2F); - cfw.add(ByteCode.FRETURN); - break; - case 'd': - cfw.add(ByteCode.DRETURN); - break; - default: - throw new RuntimeException("Unexpected return type " + retType); - } - - } else { - String retTypeStr = retType.getName(); - if (callConvertResult) { - cfw.addLoadConstant(retTypeStr); - cfw.addInvoke(ByteCode.INVOKESTATIC, - "java/lang/Class", - "forName", - "(Ljava/lang/String;)Ljava/lang/Class;"); - - cfw.addInvoke(ByteCode.INVOKESTATIC, - "org/mozilla/javascript/JavaAdapter", - "convertResult", - "(Ljava/lang/Object;" - +"Ljava/lang/Class;" - +")Ljava/lang/Object;"); - } - // Now cast to return type - cfw.add(ByteCode.CHECKCAST, retTypeStr); - cfw.add(ByteCode.ARETURN); - } - } - - private static void generateMethod(ClassFileWriter cfw, String genName, - String methodName, Class[] parms, - Class returnType, boolean convertResult) - { - StringBuilder sb = new StringBuilder(); - int paramsEnd = appendMethodSignature(parms, returnType, sb); - String methodSignature = sb.toString(); - cfw.startMethod(methodName, methodSignature, - ClassFileWriter.ACC_PUBLIC); - - // Prepare stack to call method - - // push factory - cfw.add(ByteCode.ALOAD_0); - cfw.add(ByteCode.GETFIELD, genName, "factory", - "Lorg/mozilla/javascript/ContextFactory;"); - - // push self - cfw.add(ByteCode.ALOAD_0); - cfw.add(ByteCode.GETFIELD, genName, "self", - "Lorg/mozilla/javascript/Scriptable;"); - - // push function - cfw.add(ByteCode.ALOAD_0); - cfw.add(ByteCode.GETFIELD, genName, "delegee", - "Lorg/mozilla/javascript/Scriptable;"); - cfw.addPush(methodName); - cfw.addInvoke(ByteCode.INVOKESTATIC, - "org/mozilla/javascript/JavaAdapter", - "getFunction", - "(Lorg/mozilla/javascript/Scriptable;" - +"Ljava/lang/String;" - +")Lorg/mozilla/javascript/Function;"); - - // push arguments - generatePushWrappedArgs(cfw, parms, parms.length); - - // push bits to indicate which parameters should be wrapped - if (parms.length > 64) { - // If it will be an issue, then passing a static boolean array - // can be an option, but for now using simple bitmask - throw Context.reportRuntimeErrorById( - "JavaAdapter can not subclass methods with more then" - +" 64 arguments."); - } - long convertionMask = 0; - for (int i = 0; i != parms.length; ++i) { - if (!parms[i].isPrimitive()) { - convertionMask |= (1 << i); - } - } - cfw.addPush(convertionMask); - - // go through utility method, which creates a Context to run the - // method in. - cfw.addInvoke(ByteCode.INVOKESTATIC, - "org/mozilla/javascript/JavaAdapter", - "callMethod", - "(Lorg/mozilla/javascript/ContextFactory;" - +"Lorg/mozilla/javascript/Scriptable;" - +"Lorg/mozilla/javascript/Function;" - +"[Ljava/lang/Object;" - +"J" - +")Ljava/lang/Object;"); - - generateReturnResult(cfw, returnType, convertResult); - - cfw.stopMethod((short)paramsEnd); - } - - /** - * Generates code to push typed parameters onto the operand stack - * prior to a direct Java method call. - */ - private static int generatePushParam(ClassFileWriter cfw, int paramOffset, - Class paramType) - { - if (!paramType.isPrimitive()) { - cfw.addALoad(paramOffset); - return 1; - } - String typeName = paramType.getName(); - switch (typeName.charAt(0)) { - case 'z': - case 'b': - case 'c': - case 's': - case 'i': - // load an int value, convert to double. - cfw.addILoad(paramOffset); - return 1; - case 'l': - // load a long, convert to double. - cfw.addLLoad(paramOffset); - return 2; - case 'f': - // load a float, convert to double. - cfw.addFLoad(paramOffset); - return 1; - case 'd': - cfw.addDLoad(paramOffset); - return 2; - } - throw Kit.codeBug(); - } - - /** - * Generates code to return a Java type, after calling a Java method - * that returns the same type. - * Generates the appropriate RETURN bytecode. - */ - private static void generatePopResult(ClassFileWriter cfw, - Class retType) - { - if (retType.isPrimitive()) { - String typeName = retType.getName(); - switch (typeName.charAt(0)) { - case 'b': - case 'c': - case 's': - case 'i': - case 'z': - cfw.add(ByteCode.IRETURN); - break; - case 'l': - cfw.add(ByteCode.LRETURN); - break; - case 'f': - cfw.add(ByteCode.FRETURN); - break; - case 'd': - cfw.add(ByteCode.DRETURN); - break; - } - } else { - cfw.add(ByteCode.ARETURN); - } - } - - /** - * Generates a method called "super$methodName()" which can be called - * from JavaScript that is equivalent to calling "super.methodName()" - * from Java. Eventually, this may be supported directly in JavaScript. - */ - private static void generateSuper(ClassFileWriter cfw, - String genName, String superName, - String methodName, String methodSignature, - Class[] parms, Class returnType) - { - cfw.startMethod("super$" + methodName, methodSignature, - ClassFileWriter.ACC_PUBLIC); - - // push "this" - cfw.add(ByteCode.ALOAD, 0); - - // push the rest of the parameters. - int paramOffset = 1; - for (Class parm : parms) { - paramOffset += generatePushParam(cfw, paramOffset, parm); - } - - // call the superclass implementation of the method. - cfw.addInvoke(ByteCode.INVOKESPECIAL, - superName, - methodName, - methodSignature); - - // now, handle the return type appropriately. - Class retType = returnType; - if (!retType.equals(Void.TYPE)) { - generatePopResult(cfw, retType); - } else { - cfw.add(ByteCode.RETURN); - } - cfw.stopMethod((short)(paramOffset + 1)); - } - - /** - * Returns a fully qualified method name concatenated with its signature. - */ - private static String getMethodSignature(Method method, Class[] argTypes) - { - StringBuilder sb = new StringBuilder(); - appendMethodSignature(argTypes, method.getReturnType(), sb); - return sb.toString(); - } - - static int appendMethodSignature(Class[] argTypes, - Class returnType, - StringBuilder sb) - { - sb.append('('); - int firstLocal = 1 + argTypes.length; // includes this. - for (Class type : argTypes) { - appendTypeString(sb, type); - if (type == Long.TYPE || type == Double.TYPE) { - // adjust for double slot - ++firstLocal; - } - } - sb.append(')'); - appendTypeString(sb, returnType); - return firstLocal; - } - - private static StringBuilder appendTypeString(StringBuilder sb, Class type) - { - while (type.isArray()) { - sb.append('['); - type = type.getComponentType(); - } - if (type.isPrimitive()) { - char typeLetter; - if (type == Boolean.TYPE) { - typeLetter = 'Z'; - } else if (type == Long.TYPE) { - typeLetter = 'J'; - } else { - String typeName = type.getName(); - typeLetter = Character.toUpperCase(typeName.charAt(0)); - } - sb.append(typeLetter); - } else { - sb.append('L'); - sb.append(type.getName().replace('.', '/')); - sb.append(';'); - } - return sb; - } - - static int[] getArgsToConvert(Class[] argTypes) - { - int count = 0; - for (int i = 0; i != argTypes.length; ++i) { - if (!argTypes[i].isPrimitive()) - ++count; - } - if (count == 0) - return null; - int[] array = new int[count]; - count = 0; - for (int i = 0; i != argTypes.length; ++i) { - if (!argTypes[i].isPrimitive()) - array[count++] = i; - } - return array; - } - - private static final Object FTAG = "JavaAdapter"; - private static final int Id_JavaAdapter = 1; -} diff --git a/src/main/java/org/mozilla/javascript/JavaMembers.java b/src/main/java/org/mozilla/javascript/JavaMembers.java deleted file mode 100644 index b66ed8bc..00000000 --- a/src/main/java/org/mozilla/javascript/JavaMembers.java +++ /dev/null @@ -1,918 +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 static java.lang.reflect.Modifier.isProtected; -import static java.lang.reflect.Modifier.isPublic; - -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Member; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.security.AccessControlContext; -import java.security.AllPermission; -import java.security.Permission; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import javax.lang.model.SourceVersion; - -/** - * @author Mike Shaver - * @author Norris Boyd - * @see NativeJavaObject - * @see NativeJavaClass - */ -class JavaMembers { - - // # INNER CORE MODIFIED PART - private static boolean STRICT_REFLECTIVE_ACCESS = false; - static { - try { - STRICT_REFLECTIVE_ACCESS = SourceVersion.latestSupported().ordinal() > 8; - } catch (Throwable err) { - STRICT_REFLECTIVE_ACCESS = false; - } - } - // # END - - private static final Permission allPermission = new AllPermission(); - - JavaMembers(Scriptable scope, Class cl) { - this(scope, cl, false); - } - - JavaMembers(Scriptable scope, Class cl, boolean includeProtected) { - try { - Context cx = ContextFactory.getGlobal().enterContext(); - ClassShutter shutter = cx.getClassShutter(); - if (shutter != null && !shutter.visibleToScripts(cl.getName())) { - throw Context.reportRuntimeErrorById("msg.access.prohibited", cl.getName()); - } - this.members = new HashMap(); - this.staticMembers = new HashMap(); - this.cl = cl; - boolean includePrivate = cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS); - reflect(scope, includeProtected, includePrivate); - } finally { - Context.exit(); - } - } - - boolean has(String name, boolean isStatic) { - Map ht = isStatic ? staticMembers : members; - Object obj = ht.get(name); - if (obj != null) { - return true; - } - return findExplicitFunction(name, isStatic) != null; - } - - Object get(Scriptable scope, String name, Object javaObject, boolean isStatic) { - Map ht = isStatic ? staticMembers : members; - Object member = ht.get(name); - if (!isStatic && member == null) { - // Try to get static member from instance (LC3) - member = staticMembers.get(name); - } - if (member == null) { - member = - this.getExplicitFunction( - scope, name, - javaObject, isStatic); - if (member == null) return Scriptable.NOT_FOUND; - } - if (member instanceof Scriptable) { - return member; - } - Context cx = Context.getContext(); - Object rval; - Class type; - try { - if (member instanceof BeanProperty) { - BeanProperty bp = (BeanProperty) member; - if (bp.getter == null) return Scriptable.NOT_FOUND; - rval = bp.getter.invoke(javaObject, Context.emptyArgs); - type = bp.getter.method().getReturnType(); - } else { - Field field = (Field) member; - rval = field.get(isStatic ? null : javaObject); - type = field.getType(); - } - } catch (Exception ex) { - throw Context.throwAsScriptRuntimeEx(ex); - } - // Need to wrap the object before we return it. - scope = ScriptableObject.getTopLevelScope(scope); - return cx.getWrapFactory().wrap(cx, scope, rval, type); - } - - void put(Scriptable scope, String name, Object javaObject, Object value, boolean isStatic) { - Map ht = isStatic ? staticMembers : members; - Object member = ht.get(name); - if (!isStatic && member == null) { - // Try to get static member from instance (LC3) - member = staticMembers.get(name); - } - if (member == null) throw reportMemberNotFound(name); - if (member instanceof FieldAndMethods) { - FieldAndMethods fam = (FieldAndMethods) ht.get(name); - member = fam.field; - } - - // Is this a bean property "set"? - if (member instanceof BeanProperty) { - BeanProperty bp = (BeanProperty) member; - if (bp.setter == null) { - throw reportMemberNotFound(name); - } - // If there's only one setter or if the value is null, use the - // main setter. Otherwise, let the NativeJavaMethod decide which - // setter to use: - if (bp.setters == null || value == null) { - Class setType = bp.setter.argTypes[0]; - Object[] args = {Context.jsToJava(value, setType)}; - try { - bp.setter.invoke(javaObject, args); - } catch (Exception ex) { - throw Context.throwAsScriptRuntimeEx(ex); - } - } else { - Object[] args = {value}; - bp.setters.call( - Context.getContext(), - ScriptableObject.getTopLevelScope(scope), - scope, - args); - } - } else { - if (!(member instanceof Field)) { - String str = - (member == null) ? "msg.java.internal.private" : "msg.java.method.assign"; - throw Context.reportRuntimeErrorById(str, name); - } - Field field = (Field) member; - Object javaValue = Context.jsToJava(value, field.getType()); - try { - field.set(javaObject, javaValue); - } catch (IllegalAccessException accessEx) { - if ((field.getModifiers() & Modifier.FINAL) != 0) { - // treat Java final the same as JavaScript [[READONLY]] - return; - } - throw Context.throwAsScriptRuntimeEx(accessEx); - } catch (IllegalArgumentException argEx) { - throw Context.reportRuntimeErrorById( - "msg.java.internal.field.type", - value.getClass().getName(), - field, - javaObject.getClass().getName()); - } - } - } - - Object[] getIds(boolean isStatic) { - Map map = isStatic ? staticMembers : members; - return map.keySet().toArray(new Object[map.size()]); - } - - static String javaSignature(Class type) { - if (!type.isArray()) { - return type.getName(); - } - int arrayDimension = 0; - do { - ++arrayDimension; - type = type.getComponentType(); - } while (type.isArray()); - String name = type.getName(); - String suffix = "[]"; - if (arrayDimension == 1) { - return name.concat(suffix); - } - int length = name.length() + arrayDimension * suffix.length(); - StringBuilder sb = new StringBuilder(length); - sb.append(name); - while (arrayDimension != 0) { - --arrayDimension; - sb.append(suffix); - } - return sb.toString(); - } - - static String liveConnectSignature(Class[] argTypes) { - int N = argTypes.length; - if (N == 0) { - return "()"; - } - StringBuilder sb = new StringBuilder(); - sb.append('('); - for (int i = 0; i != N; ++i) { - if (i != 0) { - sb.append(','); - } - sb.append(javaSignature(argTypes[i])); - } - sb.append(')'); - return sb.toString(); - } - - private MemberBox findExplicitFunction(String name, boolean isStatic) { - int sigStart = name.indexOf('('); - if (sigStart < 0) { - return null; - } - - Map ht = isStatic ? staticMembers : members; - MemberBox[] methodsOrCtors = null; - boolean isCtor = (isStatic && sigStart == 0); - - if (isCtor) { - // Explicit request for an overloaded constructor - methodsOrCtors = ctors.methods; - } else { - // Explicit request for an overloaded method - String trueName = name.substring(0, sigStart); - Object obj = ht.get(trueName); - if (!isStatic && obj == null) { - // Try to get static member from instance (LC3) - obj = staticMembers.get(trueName); - } - if (obj instanceof NativeJavaMethod) { - NativeJavaMethod njm = (NativeJavaMethod) obj; - methodsOrCtors = njm.methods; - } - } - - if (methodsOrCtors != null) { - for (MemberBox methodsOrCtor : methodsOrCtors) { - Class[] type = methodsOrCtor.argTypes; - String sig = liveConnectSignature(type); - if (sigStart + sig.length() == name.length() - && name.regionMatches(sigStart, sig, 0, sig.length())) { - return methodsOrCtor; - } - } - } - - return null; - } - - private Object getExplicitFunction( - Scriptable scope, String name, Object javaObject, boolean isStatic) { - Map ht = isStatic ? staticMembers : members; - Object member = null; - MemberBox methodOrCtor = findExplicitFunction(name, isStatic); - - if (methodOrCtor != null) { - Scriptable prototype = ScriptableObject.getFunctionPrototype(scope); - - if (methodOrCtor.isCtor()) { - NativeJavaConstructor fun = new NativeJavaConstructor(methodOrCtor); - fun.setPrototype(prototype); - member = fun; - ht.put(name, fun); - } else { - String trueName = methodOrCtor.getName(); - member = ht.get(trueName); - - if (member instanceof NativeJavaMethod - && ((NativeJavaMethod) member).methods.length > 1) { - NativeJavaMethod fun = new NativeJavaMethod(methodOrCtor, name); - fun.setPrototype(prototype); - ht.put(name, fun); - member = fun; - } - } - } - - return member; - } - - /** - * Retrieves mapping of methods to accessible methods for a class. In case the class is not - * public, retrieves methods with same signature as its public methods from public superclasses - * and interfaces (if they exist). Basically upcasts every method to the nearest accessible - * method. - */ - private Method[] discoverAccessibleMethods( - Class clazz, boolean includeProtected, boolean includePrivate) { - Map map = new HashMap(); - discoverAccessibleMethods(clazz, map, includeProtected, includePrivate); - return map.values().toArray(new Method[map.size()]); - } - - private void discoverAccessibleMethods( - Class clazz, - Map map, - boolean includeProtected, - boolean includePrivate) { - if (isPublic(clazz.getModifiers()) || includePrivate) { - try { - if (includeProtected || includePrivate) { - while (clazz != null) { - try { - Method[] methods = clazz.getDeclaredMethods(); - for (Method method : methods) { - int mods = method.getModifiers(); - - if (isPublic(mods) || isProtected(mods) || includePrivate) { - MethodSignature sig = new MethodSignature(method); - if (!map.containsKey(sig)) { - if (includePrivate && !method.isAccessible()) - method.setAccessible(true); - map.put(sig, method); - } - } - } - Class[] interfaces = clazz.getInterfaces(); - for (Class intface : interfaces) { - discoverAccessibleMethods( - intface, map, includeProtected, includePrivate); - } - clazz = clazz.getSuperclass(); - } catch (SecurityException e) { - // Some security settings (i.e., applets) disallow - // access to Class.getDeclaredMethods. Fall back to - // Class.getMethods. - Method[] methods = clazz.getMethods(); - for (Method method : methods) { - MethodSignature sig = new MethodSignature(method); - if (!map.containsKey(sig)) map.put(sig, method); - } - break; // getMethods gets superclass methods, no - // need to loop any more - } - } - } else { - discoverPublicMethods(clazz, map); - } - return; - } catch (SecurityException e) { - Context.reportWarning( - "Could not discover accessible methods of class " - + clazz.getName() - + " due to lack of privileges, " - + "attemping superclasses/interfaces."); - // Fall through and attempt to discover superclass/interface - // methods - } - } - - Class[] interfaces = clazz.getInterfaces(); - for (Class intface : interfaces) { - discoverAccessibleMethods(intface, map, includeProtected, includePrivate); - } - Class superclass = clazz.getSuperclass(); - if (superclass != null) { - discoverAccessibleMethods(superclass, map, includeProtected, includePrivate); - } - } - - void discoverPublicMethods(Class clazz, Map map) { - Method[] methods = clazz.getMethods(); - for (Method method : methods) { - registerMethod(map, method); - } - } - - static void registerMethod(Map map, Method method) { - MethodSignature sig = new MethodSignature(method); - // Array may contain methods with same signature but different return value! - map.putIfAbsent(sig, method); - } - - static final class MethodSignature { - private final String name; - private final Class[] args; - - private MethodSignature(String name, Class[] args) { - this.name = name; - this.args = args; - } - - MethodSignature(Method method) { - this(method.getName(), method.getParameterTypes()); - } - - @Override - public boolean equals(Object o) { - if (o instanceof MethodSignature) { - MethodSignature ms = (MethodSignature) o; - return ms.name.equals(name) && Arrays.equals(args, ms.args); - } - return false; - } - - @Override - public int hashCode() { - return name.hashCode() ^ args.length; - } - } - - private void reflect(Scriptable scope, boolean includeProtected, boolean includePrivate) { - // We reflect methods first, because we want overloaded field/method - // names to be allocated to the NativeJavaMethod before the field - // gets in the way. - - Method[] methods = discoverAccessibleMethods(cl, includeProtected, includePrivate); - for (Method method : methods) { - int mods = method.getModifiers(); - boolean isStatic = Modifier.isStatic(mods); - Map ht = isStatic ? staticMembers : members; - String name = method.getName(); - - // # INNER CORE MODIFIED PART - // Patch methods - method = MembersPatch.override(cl, method); - // # END - - Object value = ht.get(name); - if (value == null) { - ht.put(name, method); - } else { - ObjArray overloadedMethods; - if (value instanceof ObjArray) { - overloadedMethods = (ObjArray) value; - } else { - if (!(value instanceof Method)) Kit.codeBug(); - // value should be instance of Method as at this stage - // staticMembers and members can only contain methods - overloadedMethods = new ObjArray(); - overloadedMethods.add(value); - ht.put(name, overloadedMethods); - } - overloadedMethods.add(method); - } - } - - // replace Method instances by wrapped NativeJavaMethod objects - // first in staticMembers and then in members - for (int tableCursor = 0; tableCursor != 2; ++tableCursor) { - boolean isStatic = (tableCursor == 0); - Map ht = isStatic ? staticMembers : members; - for (Map.Entry entry : ht.entrySet()) { - MemberBox[] methodBoxes; - Object value = entry.getValue(); - if (value instanceof Method) { - methodBoxes = new MemberBox[1]; - methodBoxes[0] = new MemberBox((Method) value); - } else { - ObjArray overloadedMethods = (ObjArray) value; - int N = overloadedMethods.size(); - if (N < 2) Kit.codeBug(); - methodBoxes = new MemberBox[N]; - for (int i = 0; i != N; ++i) { - Method method = (Method) overloadedMethods.get(i); - methodBoxes[i] = new MemberBox(method); - } - } - NativeJavaMethod fun = new NativeJavaMethod(methodBoxes); - if (scope != null) { - ScriptRuntime.setFunctionProtoAndParent(fun, scope); - } - ht.put(entry.getKey(), fun); - } - } - - // Reflect fields. - Field[] fields = getAccessibleFields(includeProtected, includePrivate); - for (Field field : fields) { - String name = field.getName(); - int mods = field.getModifiers(); - try { - boolean isStatic = Modifier.isStatic(mods); - Map ht = isStatic ? staticMembers : members; - Object member = ht.get(name); - if (member == null) { - ht.put(name, field); - } else if (member instanceof NativeJavaMethod) { - NativeJavaMethod method = (NativeJavaMethod) member; - FieldAndMethods fam = new FieldAndMethods(scope, method.methods, field); - Map fmht = - isStatic ? staticFieldAndMethods : fieldAndMethods; - if (fmht == null) { - fmht = new HashMap(); - if (isStatic) { - staticFieldAndMethods = fmht; - } else { - fieldAndMethods = fmht; - } - } - fmht.put(name, fam); - ht.put(name, fam); - } else if (member instanceof Field) { - Field oldField = (Field) member; - // If this newly reflected field shadows an inherited field, - // then replace it. Otherwise, since access to the field - // would be ambiguous from Java, no field should be - // reflected. - // For now, the first field found wins, unless another field - // explicitly shadows it. - if (oldField.getDeclaringClass().isAssignableFrom(field.getDeclaringClass())) { - ht.put(name, field); - } - } else { - // "unknown member type" - Kit.codeBug(); - } - } catch (SecurityException e) { - // skip this field - Context.reportWarning( - "Could not access field " - + name - + " of class " - + cl.getName() - + " due to lack of privileges."); - } - } - - // Create bean properties from corresponding get/set methods first for - // static members and then for instance members - for (int tableCursor = 0; tableCursor != 2; ++tableCursor) { - boolean isStatic = (tableCursor == 0); - Map ht = isStatic ? staticMembers : members; - - Map toAdd = new HashMap(); - - // Now, For each member, make "bean" properties. - for (String name : ht.keySet()) { - // Is this a getter? - boolean memberIsGetMethod = name.startsWith("get"); - boolean memberIsSetMethod = name.startsWith("set"); - boolean memberIsIsMethod = name.startsWith("is"); - if (memberIsGetMethod || memberIsIsMethod || memberIsSetMethod) { - // Double check name component. - String nameComponent = name.substring(memberIsIsMethod ? 2 : 3); - if (nameComponent.length() == 0) continue; - - // Make the bean property name. - String beanPropertyName = nameComponent; - char ch0 = nameComponent.charAt(0); - if (Character.isUpperCase(ch0)) { - if (nameComponent.length() == 1) { - beanPropertyName = nameComponent.toLowerCase(); - } else { - char ch1 = nameComponent.charAt(1); - if (!Character.isUpperCase(ch1)) { - beanPropertyName = - Character.toLowerCase(ch0) + nameComponent.substring(1); - } - } - } - - // If we already have a member by this name, don't do this - // property. - if (toAdd.containsKey(beanPropertyName)) continue; - Object v = ht.get(beanPropertyName); - if (v != null) { - // A private field shouldn't mask a public getter/setter - if (!includePrivate - || !(v instanceof Member) - || !Modifier.isPrivate(((Member) v).getModifiers())) { - - continue; - } - } - - // Find the getter method, or if there is none, the is- - // method. - MemberBox getter = null; - getter = findGetter(isStatic, ht, "get", nameComponent); - // If there was no valid getter, check for an is- method. - if (getter == null) { - getter = findGetter(isStatic, ht, "is", nameComponent); - } - - // setter - MemberBox setter = null; - NativeJavaMethod setters = null; - String setterName = "set".concat(nameComponent); - - if (ht.containsKey(setterName)) { - // Is this value a method? - Object member = ht.get(setterName); - if (member instanceof NativeJavaMethod) { - NativeJavaMethod njmSet = (NativeJavaMethod) member; - if (getter != null) { - // We have a getter. Now, do we have a matching - // setter? - Class type = getter.method().getReturnType(); - setter = extractSetMethod(type, njmSet.methods, isStatic); - } else { - // No getter, find any set method - setter = extractSetMethod(njmSet.methods, isStatic); - } - if (njmSet.methods.length > 1) { - setters = njmSet; - } - } - } - // Make the property. - BeanProperty bp = new BeanProperty(getter, setter, setters); - toAdd.put(beanPropertyName, bp); - } - } - - // Add the new bean properties. - ht.putAll(toAdd); - } - - // Reflect constructors - Constructor[] constructors = getAccessibleConstructors(includePrivate); - MemberBox[] ctorMembers = new MemberBox[constructors.length]; - for (int i = 0; i != constructors.length; ++i) { - ctorMembers[i] = new MemberBox(constructors[i]); - } - ctors = new NativeJavaMethod(ctorMembers, cl.getSimpleName()); - } - - private Constructor[] getAccessibleConstructors(boolean includePrivate) { - // The JVM currently doesn't allow changing access on java.lang.Class - // constructors, so don't try - if (includePrivate && cl != ScriptRuntime.ClassClass) { - try { - Constructor[] cons = cl.getDeclaredConstructors(); - AccessibleObject.setAccessible(cons, true); - - return cons; - } catch (SecurityException e) { - // Fall through to !includePrivate case - Context.reportWarning( - "Could not access constructor " - + " of class " - + cl.getName() - + " due to lack of privileges."); - } - } - return cl.getConstructors(); - } - - private Field[] getAccessibleFields(boolean includeProtected, boolean includePrivate) { - if (includePrivate || includeProtected) { - try { - List fieldsList = new ArrayList(); - Class currentClass = cl; - - while (currentClass != null) { - // get all declared fields in this class, make them - // accessible, and save - Field[] declared = currentClass.getDeclaredFields(); - for (Field field : declared) { - int mod = field.getModifiers(); - if (includePrivate || isPublic(mod) || isProtected(mod)) { - if (!field.isAccessible()) field.setAccessible(true); - fieldsList.add(field); - } - } - // walk up superclass chain. no need to deal specially with - // interfaces, since they can't have fields - currentClass = currentClass.getSuperclass(); - } - - return fieldsList.toArray(new Field[fieldsList.size()]); - } catch (SecurityException e) { - // fall through to !includePrivate case - } - } - return cl.getFields(); - } - - private static MemberBox findGetter( - boolean isStatic, Map ht, String prefix, String propertyName) { - String getterName = prefix.concat(propertyName); - if (ht.containsKey(getterName)) { - // Check that the getter is a method. - Object member = ht.get(getterName); - if (member instanceof NativeJavaMethod) { - NativeJavaMethod njmGet = (NativeJavaMethod) member; - return extractGetMethod(njmGet.methods, isStatic); - } - } - return null; - } - - private static MemberBox extractGetMethod(MemberBox[] methods, boolean isStatic) { - // Inspect the list of all MemberBox for the only one having no - // parameters - for (MemberBox method : methods) { - // Does getter method have an empty parameter list with a return - // value (eg. a getSomething() or isSomething())? - if (method.argTypes.length == 0 && (!isStatic || method.isStatic())) { - Class type = method.method().getReturnType(); - if (type != Void.TYPE) { - return method; - } - break; - } - } - return null; - } - - private static MemberBox extractSetMethod( - Class type, MemberBox[] methods, boolean isStatic) { - // - // Note: it may be preferable to allow NativeJavaMethod.findFunction() - // to find the appropriate setter; unfortunately, it requires an - // instance of the target arg to determine that. - // - - // Make two passes: one to find a method with direct type assignment, - // and one to find a widening conversion. - for (int pass = 1; pass <= 2; ++pass) { - for (MemberBox method : methods) { - if (!isStatic || method.isStatic()) { - Class[] params = method.argTypes; - if (params.length == 1) { - if (pass == 1) { - if (params[0] == type) { - return method; - } - } else { - if (pass != 2) Kit.codeBug(); - if (params[0].isAssignableFrom(type)) { - return method; - } - } - } - } - } - } - return null; - } - - private static MemberBox extractSetMethod(MemberBox[] methods, boolean isStatic) { - - for (MemberBox method : methods) { - if (!isStatic || method.isStatic()) { - if (method.method().getReturnType() == Void.TYPE) { - if (method.argTypes.length == 1) { - return method; - } - } - } - } - return null; - } - - Map getFieldAndMethodsObjects( - Scriptable scope, Object javaObject, boolean isStatic) { - Map ht = isStatic ? staticFieldAndMethods : fieldAndMethods; - if (ht == null) return null; - int len = ht.size(); - Map result = new HashMap(len); - for (FieldAndMethods fam : ht.values()) { - FieldAndMethods famNew = new FieldAndMethods(scope, fam.methods, fam.field); - famNew.javaObject = javaObject; - result.put(fam.field.getName(), famNew); - } - return result; - } - - static JavaMembers lookupClass( - Scriptable scope, Class dynamicType, Class staticType, boolean includeProtected) { - JavaMembers members; - ClassCache cache = ClassCache.get(scope); - Map ct = cache.getClassCacheMap(); - - Class cl = dynamicType; - Object secCtx = getSecurityContext(); - for (; ; ) { - members = ct.get(new ClassCache.CacheKey(cl, secCtx)); - if (members != null) { - if (cl != dynamicType) { - // member lookup for the original class failed because of - // missing privileges, cache the result so we don't try again - ct.put(new ClassCache.CacheKey(dynamicType, secCtx), members); - } - return members; - } - try { - members = createJavaMembers(cache.getAssociatedScope(), cl, includeProtected); - break; - } catch (SecurityException e) { - // Reflection may fail for objects that are in a restricted - // access package (e.g. sun.*). If we get a security - // exception, try again with the static type if it is interface. - // Otherwise, try superclass - if (staticType != null && staticType.isInterface()) { - cl = staticType; - staticType = null; // try staticType only once - } else { - Class parent = cl.getSuperclass(); - if (parent == null) { - if (cl.isInterface()) { - // last resort after failed staticType interface - parent = ScriptRuntime.ObjectClass; - } else { - throw e; - } - } - cl = parent; - } - } - } - - if (cache.isCachingEnabled()) { - ct.put(new ClassCache.CacheKey(cl, secCtx), members); - if (cl != dynamicType) { - // member lookup for the original class failed because of - // missing privileges, cache the result so we don't try again - ct.put(new ClassCache.CacheKey(dynamicType, secCtx), members); - } - } - return members; - } - - private static JavaMembers createJavaMembers( - Scriptable associatedScope, Class cl, boolean includeProtected) { - if (STRICT_REFLECTIVE_ACCESS) { - return new JavaMembers_jdk11(associatedScope, cl, includeProtected); - } else { - return new JavaMembers(associatedScope, cl, includeProtected); - } - } - - private static Object getSecurityContext() { - Object sec = null; - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - sec = sm.getSecurityContext(); - if (sec instanceof AccessControlContext) { - try { - ((AccessControlContext) sec).checkPermission(allPermission); - // if we have allPermission, we do not need to store the - // security object in the cache key - return null; - } catch (SecurityException e) { - } - } - } - return sec; - } - - RuntimeException reportMemberNotFound(String memberName) { - return Context.reportRuntimeErrorById( - "msg.java.member.not.found", cl.getName(), memberName); - } - - private Class cl; - private Map members; - private Map fieldAndMethods; - private Map staticMembers; - private Map staticFieldAndMethods; - NativeJavaMethod ctors; // we use NativeJavaMethod for ctor overload resolution -} - -class BeanProperty { - BeanProperty(MemberBox getter, MemberBox setter, NativeJavaMethod setters) { - this.getter = getter; - this.setter = setter; - this.setters = setters; - } - - MemberBox getter; - MemberBox setter; - NativeJavaMethod setters; -} - -class FieldAndMethods extends NativeJavaMethod { - private static final long serialVersionUID = -9222428244284796755L; - - FieldAndMethods(Scriptable scope, MemberBox[] methods, Field field) { - super(methods); - this.field = field; - setParentScope(scope); - setPrototype(ScriptableObject.getFunctionPrototype(scope)); - } - - @Override - public Object getDefaultValue(Class hint) { - if (hint == ScriptRuntime.FunctionClass) return this; - Object rval; - Class type; - try { - rval = field.get(javaObject); - type = field.getType(); - } catch (IllegalAccessException accEx) { - throw Context.reportRuntimeErrorById("msg.java.internal.private", field.getName()); - } - Context cx = Context.getContext(); - rval = cx.getWrapFactory().wrap(cx, this, rval, type); - if (rval instanceof Scriptable) { - rval = ((Scriptable) rval).getDefaultValue(hint); - } - return rval; - } - - Field field; - Object javaObject; -} diff --git a/src/main/java/org/mozilla/javascript/JavaMembers_jdk11.java b/src/main/java/org/mozilla/javascript/JavaMembers_jdk11.java deleted file mode 100644 index 673b9b33..00000000 --- a/src/main/java/org/mozilla/javascript/JavaMembers_jdk11.java +++ /dev/null @@ -1,118 +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.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.Map; - -/** Version of {@link JavaMembers} for modular JDKs. */ -class JavaMembers_jdk11 extends JavaMembers { - - JavaMembers_jdk11(Scriptable scope, Class cl, boolean includeProtected) { - super(scope, cl, includeProtected); - } - - @Override - void discoverPublicMethods(Class clazz, Map map) { - if (isExportedClass(clazz)) { - super.discoverPublicMethods(clazz, map); - } else { - Method[] methods = clazz.getMethods(); - for (Method method : methods) { - method = findAccessibleMethod(method); - registerMethod(map, method); - } - } - } - - private static boolean isExportedClass(Class clazz) { - /* - * We are going to invoke, using reflection, the approximate equivalent - * to the following Java 9 code: - * - * return clazz.getModule().isExported(clazz.getPackageName()); - */ - - // First, determine the package name - String pname; - Package pkg = clazz.getPackage(); - if (pkg == null) { - // Primitive types, arrays, proxy classes - if (!Proxy.isProxyClass(clazz)) { - // Should be a primitive type or array - return true; - } - String clName = clazz.getName(); - pname = clName.substring(0, clName.lastIndexOf('.')); - } else { - pname = pkg.getName(); - } - - // Obtain the module - Method getmodule; - Class cl = clazz.getClass(); - try { - getmodule = cl.getMethod("getModule"); - } catch (NoSuchMethodException e) { - // We are on non-modular Java - return true; - } - - Object module; - try { - module = getmodule.invoke(clazz); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - return false; - } - - Class moduleClass = module.getClass(); - - // Execute isExported() - boolean exported; - try { - Method isexported = moduleClass.getMethod("isExported", String.class); - exported = (boolean) isexported.invoke(module, pname); - } catch (NoSuchMethodException - | IllegalAccessException - | IllegalArgumentException - | InvocationTargetException e) { - // If we reached this part of the code, this cannot happen - exported = false; - } - return exported; - } - - private static Method findAccessibleMethod(Method method) { - Class cl = method.getDeclaringClass(); - final String methodName = method.getName(); - final Class[] methodTypes = method.getParameterTypes(); - topLoop: - do { - for (Class intface : cl.getInterfaces()) { - try { - method = intface.getMethod(methodName, methodTypes); - break topLoop; - } catch (NoSuchMethodException e) { - } - } - cl = cl.getSuperclass(); - if (cl == null) { - break; - } - if (isExportedClass(cl)) { - try { - method = cl.getMethod(methodName, methodTypes); - break; - } catch (NoSuchMethodException e) { - } - } - } while (true); - return method; - } -} diff --git a/src/main/java/org/mozilla/javascript/JavaScriptException.java b/src/main/java/org/mozilla/javascript/JavaScriptException.java deleted file mode 100644 index abf33922..00000000 --- a/src/main/java/org/mozilla/javascript/JavaScriptException.java +++ /dev/null @@ -1,101 +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; - -/** - * Java reflection of JavaScript exceptions. Instances of this class are thrown by the JavaScript - * 'throw' keyword. - * - * @author Mike McCabe - */ -public class JavaScriptException extends RhinoException { - private static final long serialVersionUID = -7666130513694669293L; - - /** - * @deprecated Use {@link WrappedException#WrappedException(Throwable)} to report exceptions in - * Java code. - */ - @Deprecated - public JavaScriptException(Object value) { - this(value, "", 0); - } - - /** - * Create a JavaScript exception wrapping the given JavaScript value - * - * @param value the JavaScript value thrown. - */ - public JavaScriptException(Object value, String sourceName, int lineNumber) { - recordErrorOrigin(sourceName, lineNumber, null, 0); - this.value = value; - // Fill in fileName and lineNumber automatically when not specified - // explicitly, see Bugzilla issue #342807 - if (value instanceof NativeError - && Context.getContext().hasFeature(Context.FEATURE_LOCATION_INFORMATION_IN_ERROR)) { - NativeError error = (NativeError) value; - if (!error.has("fileName", error)) { - error.put("fileName", error, sourceName); - } - if (!error.has("lineNumber", error)) { - error.put("lineNumber", error, Integer.valueOf(lineNumber)); - } - // set stack property, see bug #549604 - error.setStackProvider(this); - } - - // generate details string when exception is first created, - // since details() may be called later from a different thread - // (e.g. when printing failed test results), which - // would cause ScriptRuntime.toString to fail. - this.details = getDetails(); - } - - @Override - public String details() { - return this.details; - } - - public String getDetails() { - if (value == null) { - return "null"; - } else if (value instanceof NativeError) { - return value.toString(); - } - - try { - return ScriptRuntime.toString(value); - } catch (RuntimeException rte) { - // ScriptRuntime.toString may throw a RuntimeException - if (value instanceof Scriptable) { - return ScriptRuntime.defaultObjectToString((Scriptable) value); - } - return value.toString(); - } - } - - /** @return the value wrapped by this exception */ - public Object getValue() { - return value; - } - - /** @deprecated Use {@link RhinoException#sourceName()} from the super class. */ - @Deprecated - public String getSourceName() { - return sourceName(); - } - - /** @deprecated Use {@link RhinoException#lineNumber()} from the super class. */ - @Deprecated - public int getLineNumber() { - return lineNumber(); - } - - private Object value; - private String details; -} diff --git a/src/main/java/org/mozilla/javascript/JavaToJSONConverters.java b/src/main/java/org/mozilla/javascript/JavaToJSONConverters.java deleted file mode 100644 index b60b7fad..00000000 --- a/src/main/java/org/mozilla/javascript/JavaToJSONConverters.java +++ /dev/null @@ -1,86 +0,0 @@ -/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; 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.beans.BeanDescriptor; -import java.beans.BeanInfo; -import java.beans.IntrospectionException; -import java.beans.Introspector; -import java.beans.PropertyDescriptor; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.function.UnaryOperator; - -/** - * This class provides implementations of converters for Java objects to be used by the - * JSON.stringify method. - * - *

JSON.stringify will automatically convert instances of java.util.Map to javascript objects. - * Instances of java.util.Collection and java Arrays will be converted to javascript arrays. - * - *

This is a final effort at conversion for other java objects that appear as values, and may be - * preempted by objects which define a toJSON() method or by a replacer function passed to - * JSON.stringify. The return value will, in turn, be converted according to {@link - * Context#javaToJS} and stringified. - * - * @author Tony Germano - */ -public class JavaToJSONConverters { - - private JavaToJSONConverters() {} - - /** Convert Object to its toString() value. */ - public static final UnaryOperator STRING = o -> o.toString(); - - /** Always return undefined */ - public static final UnaryOperator UNDEFINED = o -> Undefined.instance; - - /** Always return an empty object */ - public static final UnaryOperator EMPTY_OBJECT = o -> Collections.EMPTY_MAP; - - /** Throw a TypeError naming the class that could not be converted */ - public static final UnaryOperator THROW_TYPE_ERROR = - o -> { - throw ScriptRuntime.typeErrorById( - "msg.json.cant.serialize", o.getClass().getName()); - }; - - /** - * Convert JavaBean to an object as long as it has at least one readable property - * - *

If unable to determine properties or if none exist, null is returned. This method can be - * called from other converters to provide an alternate value on a returned null. - */ - public static final UnaryOperator BEAN = - value -> { - BeanInfo beanInfo; - try { - beanInfo = Introspector.getBeanInfo(value.getClass(), Object.class); - } catch (IntrospectionException e) { - return null; - } - LinkedHashMap properties = new LinkedHashMap(); - for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors()) { - if (descriptor.getReadMethod() == null) continue; - Object propValue; - try { - propValue = descriptor.getReadMethod().invoke(value); - } catch (Exception e) { - continue; - } - properties.put(descriptor.getName(), propValue); - } - - if (properties.size() == 0) return null; - - LinkedHashMap obj = new LinkedHashMap(); - BeanDescriptor beanDescriptor = beanInfo.getBeanDescriptor(); - obj.put("beanClass", beanDescriptor.getBeanClass().getName()); - obj.put("properties", properties); - return obj; - }; -} diff --git a/src/main/java/org/mozilla/javascript/Kit.java b/src/main/java/org/mozilla/javascript/Kit.java deleted file mode 100644 index a2daae1a..00000000 --- a/src/main/java/org/mozilla/javascript/Kit.java +++ /dev/null @@ -1,371 +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.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.Reader; -import java.util.Map; - -/** Collection of utilities */ -public class Kit { - public static Class classOrNull(String className) { - try { - return Class.forName(className); - } catch (ClassNotFoundException ex) { - } catch (SecurityException ex) { - } catch (LinkageError ex) { - } catch (IllegalArgumentException e) { - // Can be thrown if name has characters that a class name - // can not contain - } - return null; - } - - /** Attempt to load the class of the given name. Note that the type parameter isn't checked. */ - public static Class classOrNull(ClassLoader loader, String className) { - try { - return loader.loadClass(className); - } catch (ClassNotFoundException ex) { - } catch (SecurityException ex) { - } catch (LinkageError ex) { - } catch (IllegalArgumentException e) { - // Can be thrown if name has characters that a class name - // can not contain - } - return null; - } - - static Object newInstanceOrNull(Class cl) { - try { - return cl.newInstance(); - } catch (SecurityException x) { - } catch (LinkageError ex) { - } catch (InstantiationException x) { - } catch (IllegalAccessException x) { - } - return null; - } - - /** Check that testClass is accessible from the given loader. */ - static boolean testIfCanLoadRhinoClasses(ClassLoader loader) { - Class testClass = ScriptRuntime.ContextFactoryClass; - Class x = Kit.classOrNull(loader, testClass.getName()); - if (x != testClass) { - // The check covers the case when x == null => - // loader does not know about testClass or the case - // when x != null && x != testClass => - // loader loads a class unrelated to testClass - return false; - } - return true; - } - - /** - * If character c is a hexadecimal digit, return accumulator * 16 plus - * corresponding number. Otherise return -1. - */ - public static int xDigitToInt(int c, int accumulator) { - check: - { - // Use 0..9 < A..Z < a..z - if (c <= '9') { - c -= '0'; - if (0 <= c) { - break check; - } - } else if (c <= 'F') { - if ('A' <= c) { - c -= ('A' - 10); - break check; - } - } else if (c <= 'f') { - if ('a' <= c) { - c -= ('a' - 10); - break check; - } - } - return -1; - } - return (accumulator << 4) | c; - } - - /** - * Add listener to bag of listeners. The function does not modify bag and - * return a new collection containing listener and all listeners from bag. Bag - * without listeners always represented as the null value. - * - *

Usage example: - * - *

-     *     private volatile Object changeListeners;
-     *
-     *     public void addMyListener(PropertyChangeListener l)
-     *     {
-     *         synchronized (this) {
-     *             changeListeners = Kit.addListener(changeListeners, l);
-     *         }
-     *     }
-     *
-     *     public void removeTextListener(PropertyChangeListener l)
-     *     {
-     *         synchronized (this) {
-     *             changeListeners = Kit.removeListener(changeListeners, l);
-     *         }
-     *     }
-     *
-     *     public void fireChangeEvent(Object oldValue, Object newValue)
-     *     {
-     *     // Get immune local copy
-     *         Object listeners = changeListeners;
-     *         if (listeners != null) {
-     *             PropertyChangeEvent e = new PropertyChangeEvent(
-     *                 this, "someProperty" oldValue, newValue);
-     *             for (int i = 0; ; ++i) {
-     *                 Object l = Kit.getListener(listeners, i);
-     *                 if (l == null)
-     *                     break;
-     *                 ((PropertyChangeListener)l).propertyChange(e);
-     *             }
-     *         }
-     *     }
-     * 
- * - * @param listener Listener to add to bag - * @param bag Current collection of listeners. - * @return A new bag containing all listeners from bag and listener. - * @see #removeListener(Object bag, Object listener) - * @see #getListener(Object bag, int index) - */ - public static Object addListener(Object bag, Object listener) { - if (listener == null) throw new IllegalArgumentException(); - if (listener instanceof Object[]) throw new IllegalArgumentException(); - - if (bag == null) { - bag = listener; - } else if (!(bag instanceof Object[])) { - bag = new Object[] {bag, listener}; - } else { - Object[] array = (Object[]) bag; - int L = array.length; - // bag has at least 2 elements if it is array - if (L < 2) throw new IllegalArgumentException(); - Object[] tmp = new Object[L + 1]; - System.arraycopy(array, 0, tmp, 0, L); - tmp[L] = listener; - bag = tmp; - } - - return bag; - } - - /** - * Remove listener from bag of listeners. The function does not modify bag - * and return a new collection containing all listeners from bag except listener. - * If bag does not contain listener, the function returns bag. - * - *

For usage example, see {@link #addListener(Object bag, Object listener)}. - * - * @param listener Listener to remove from bag - * @param bag Current collection of listeners. - * @return A new bag containing all listeners from bag except listener. - * @see #addListener(Object bag, Object listener) - * @see #getListener(Object bag, int index) - */ - public static Object removeListener(Object bag, Object listener) { - if (listener == null) throw new IllegalArgumentException(); - if (listener instanceof Object[]) throw new IllegalArgumentException(); - - if (bag == listener) { - bag = null; - } else if (bag instanceof Object[]) { - Object[] array = (Object[]) bag; - int L = array.length; - // bag has at least 2 elements if it is array - if (L < 2) throw new IllegalArgumentException(); - if (L == 2) { - if (array[1] == listener) { - bag = array[0]; - } else if (array[0] == listener) { - bag = array[1]; - } - } else { - int i = L; - do { - --i; - if (array[i] == listener) { - Object[] tmp = new Object[L - 1]; - System.arraycopy(array, 0, tmp, 0, i); - System.arraycopy(array, i + 1, tmp, i, L - (i + 1)); - bag = tmp; - break; - } - } while (i != 0); - } - } - - return bag; - } - - /** - * Get listener at index position in bag or null if index equals to number - * of listeners in bag. - * - *

For usage example, see {@link #addListener(Object bag, Object listener)}. - * - * @param bag Current collection of listeners. - * @param index Index of the listener to access. - * @return Listener at the given index or null. - * @see #addListener(Object bag, Object listener) - * @see #removeListener(Object bag, Object listener) - */ - public static Object getListener(Object bag, int index) { - if (index == 0) { - if (bag == null) return null; - if (!(bag instanceof Object[])) return bag; - Object[] array = (Object[]) bag; - // bag has at least 2 elements if it is array - if (array.length < 2) throw new IllegalArgumentException(); - return array[0]; - } else if (index == 1) { - if (!(bag instanceof Object[])) { - if (bag == null) throw new IllegalArgumentException(); - return null; - } - Object[] array = (Object[]) bag; - // the array access will check for index on its own - return array[1]; - } else { - // bag has to array - Object[] array = (Object[]) bag; - int L = array.length; - if (L < 2) throw new IllegalArgumentException(); - if (index == L) return null; - return array[index]; - } - } - - static Object initHash(Map h, Object key, Object initialValue) { - synchronized (h) { - Object current = h.get(key); - if (current == null) { - h.put(key, initialValue); - } else { - initialValue = current; - } - } - return initialValue; - } - - private static final class ComplexKey { - private Object key1; - private Object key2; - private int hash; - - ComplexKey(Object key1, Object key2) { - this.key1 = key1; - this.key2 = key2; - } - - @Override - public boolean equals(Object anotherObj) { - if (!(anotherObj instanceof ComplexKey)) return false; - ComplexKey another = (ComplexKey) anotherObj; - return key1.equals(another.key1) && key2.equals(another.key2); - } - - @Override - public int hashCode() { - if (hash == 0) { - hash = key1.hashCode() ^ key2.hashCode(); - } - return hash; - } - } - - public static Object makeHashKeyFromPair(Object key1, Object key2) { - if (key1 == null) throw new IllegalArgumentException(); - if (key2 == null) throw new IllegalArgumentException(); - return new ComplexKey(key1, key2); - } - - public static String readReader(Reader reader) throws IOException { - try (BufferedReader in = new BufferedReader(reader)) { - char[] cbuf = new char[1024]; - StringBuilder sb = new StringBuilder(1024); - int bytes_read; - while ((bytes_read = in.read(cbuf, 0, 1024)) != -1) { - sb.append(cbuf, 0, bytes_read); - } - return sb.toString(); - } - } - - public static byte[] readStream(InputStream is, int initialBufferCapacity) throws IOException { - if (initialBufferCapacity <= 0) { - throw new IllegalArgumentException( - "Bad initialBufferCapacity: " + initialBufferCapacity); - } - byte[] buffer = new byte[initialBufferCapacity]; - int cursor = 0; - for (; ; ) { - int n = is.read(buffer, cursor, buffer.length - cursor); - if (n < 0) { - break; - } - cursor += n; - if (cursor == buffer.length) { - int readahead = -1; - if (cursor == initialBufferCapacity) { - readahead = is.read(); - if (readahead < 0) { // Check for EOS - return buffer; - } - } - byte[] tmp = new byte[buffer.length * 2]; - System.arraycopy(buffer, 0, tmp, 0, cursor); - buffer = tmp; - if (readahead != -1) { - buffer[cursor++] = (byte) readahead; - readahead = -1; - } - } - } - if (cursor != buffer.length) { - byte[] tmp = new byte[cursor]; - System.arraycopy(buffer, 0, tmp, 0, cursor); - buffer = tmp; - } - return buffer; - } - - /** - * Throws RuntimeException to indicate failed assertion. The function never returns and its - * return type is RuntimeException only to be able to write throw Kit.codeBug() if - * plain Kit.codeBug() triggers unreachable code error. - */ - public static RuntimeException codeBug() throws RuntimeException { - RuntimeException ex = new IllegalStateException("FAILED ASSERTION"); - // Print stack trace ASAP - ex.printStackTrace(System.err); - throw ex; - } - - /** - * Throws RuntimeException to indicate failed assertion. The function never returns and its - * return type is RuntimeException only to be able to write throw Kit.codeBug() if - * plain Kit.codeBug() triggers unreachable code error. - */ - public static RuntimeException codeBug(String msg) throws RuntimeException { - msg = "FAILED ASSERTION: " + msg; - RuntimeException ex = new IllegalStateException(msg); - // Print stack trace ASAP - ex.printStackTrace(System.err); - throw ex; - } -} diff --git a/src/main/java/org/mozilla/javascript/LambdaConstructor.java b/src/main/java/org/mozilla/javascript/LambdaConstructor.java deleted file mode 100644 index 4c9ef8f4..00000000 --- a/src/main/java/org/mozilla/javascript/LambdaConstructor.java +++ /dev/null @@ -1,195 +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; - -/** - * This class implements a JavaScript function that may be used as a constructor by delegating to an - * interface that can be easily implemented as a lambda. The LambdaFunction class may be used to add - * functions to the prototype that are also implemented as lambdas. - * - *

In micro benchmarks (as of 2021) using this class to implement a built-in class is about 15% - * more efficient than using IdScriptableObject, and about 25% faster than using reflection via the - * ScriptableObject.defineClass() family of methods. Furthermore, it results in code that more - * directly maps to JavaScript idioms than either methods, it is much easier to implement than - * IdScriptableObject, and the lambda pattern makes it easier to maintain state in various ways that - * don't always map directly to the existing concepts. - */ -public class LambdaConstructor extends LambdaFunction { - - private static final long serialVersionUID = 2691205302914111400L; - - /** If this flag is set, the constructor may be invoked as an ordinary function */ - public static final int CONSTRUCTOR_FUNCTION = 1; - /** If this flag is set, the constructor may be invoked using "new" */ - public static final int CONSTRUCTOR_NEW = 1 << 1; - /** By default, the constructor may be invoked either way */ - public static final int CONSTRUCTOR_DEFAULT = CONSTRUCTOR_FUNCTION | CONSTRUCTOR_NEW; - - // Lambdas should not be serialized. - private final transient Constructable targetConstructor; - private final int flags; - - /** - * Create a new function that may be used as a constructor. The new object will have the - * Function prototype and no parent. The caller is responsible for binding this object to the - * appropriate scope. - * - * @param scope scope of the calling context - * @param name name of the function - * @param length the arity of the function - * @param target an object that implements the function in Java. Since Constructable is a - * single-function interface this will typically be implemented as a lambda. - */ - public LambdaConstructor(Scriptable scope, String name, int length, Constructable target) { - super(scope, name, length, null); - this.targetConstructor = target; - this.flags = CONSTRUCTOR_DEFAULT; - } - - /** - * Create a new function and control whether it may be invoked using new, as a function, or - * both. - */ - public LambdaConstructor( - Scriptable scope, String name, int length, int flags, Constructable target) { - super(scope, name, length, null); - this.targetConstructor = target; - this.flags = flags; - } - - @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if ((flags & CONSTRUCTOR_FUNCTION) == 0) { - throw ScriptRuntime.typeErrorById("msg.constructor.no.function", getFunctionName()); - } - return targetConstructor.construct(cx, scope, args); - } - - @Override - public Scriptable construct(Context cx, Scriptable scope, Object[] args) { - if ((flags & CONSTRUCTOR_NEW) == 0) { - throw ScriptRuntime.typeErrorById("msg.no.new", getFunctionName()); - } - Scriptable obj = targetConstructor.construct(cx, scope, args); - obj.setPrototype(getClassPrototype()); - obj.setParentScope(scope); - return obj; - } - - /** - * Define a function property on the prototype of the constructor using a LambdaFunction under - * the covers. - */ - public void definePrototypeMethod(Scriptable scope, String name, int length, Callable target) { - LambdaFunction f = new LambdaFunction(scope, name, length, target); - ScriptableObject proto = getPrototypeScriptable(); - proto.defineProperty(name, f, 0); - } - - /** - * Define a function property on the prototype of the constructor using a LambdaFunction under - * the covers. - */ - public void definePrototypeMethod( - Scriptable scope, - String name, - int length, - Callable target, - int attributes, - int propertyAttributes) { - LambdaFunction f = new LambdaFunction(scope, name, length, target); - f.setStandardPropertyAttributes(propertyAttributes); - ScriptableObject proto = getPrototypeScriptable(); - proto.defineProperty(name, f, attributes); - } - - /** Define a property that may be of any type on the prototype of this constructor. */ - public void definePrototypeProperty(String name, Object value, int attributes) { - ScriptableObject proto = getPrototypeScriptable(); - proto.defineProperty(name, value, attributes); - } - - public void definePrototypeProperty(Symbol key, Object value, int attributes) { - ScriptableObject proto = getPrototypeScriptable(); - proto.defineProperty(key, value, attributes); - } - - /** - * Define a function property directly on the constructor that is implemented under the covers - * by a LambdaFunction. - * - * @param name the key to use to look up the new function property, and also the value to return - * for the "name" property of the Function object - * @param length the value to return for the "length" property of the Function object - * @param target the target to call when the method is invoked - * @param attributes the attributes to set on the new property - */ - public void defineConstructorMethod( - Scriptable scope, String name, int length, Callable target, int attributes) { - LambdaFunction f = new LambdaFunction(scope, name, length, target); - defineProperty(name, f, attributes); - } - - /** - * Define a function property directly on the constructor that is implemented under the covers - * by a LambdaFunction. - * - * @param key the Symbol to use to look up the property - * @param name the value to return for the "name" property of the Function object - * @param length the value to return for the "length" property of the Function object - * @param target the target to call when the method is invoked - * @param attributes the attributes to set on the new property - */ - public void defineConstructorMethod( - Scriptable scope, - Symbol key, - String name, - int length, - Callable target, - int attributes) { - LambdaFunction f = new LambdaFunction(scope, name, length, target); - defineProperty(key, f, attributes); - } - - /** - * Define a function property directly on the constructor that is implemented under the covers - * by a LambdaFunction, and override the properties of its "name", "length", and "arity" - * properties. - */ - public void defineConstructorMethod( - Scriptable scope, - String name, - int length, - Callable target, - int attributes, - int propertyAttributes) { - LambdaFunction f = new LambdaFunction(scope, name, length, target); - f.setStandardPropertyAttributes(propertyAttributes); - defineProperty(name, f, attributes); - } - - /** - * A convenience method to convert JavaScript's "this" object into a target class and throw a - * TypeError if it does not match. This is useful for implementing lambda functions, as "this" - * in JavaScript doesn't necessarily map to an instance of the class. - */ - @SuppressWarnings("unchecked") - public static T convertThisObject(Scriptable thisObj, Class targetClass) { - if (!targetClass.isInstance(thisObj)) { - throw ScriptRuntime.typeErrorById("msg.this.not.instance"); - } - return (T) thisObj; - } - - private ScriptableObject getPrototypeScriptable() { - Object prop = getPrototypeProperty(); - if (!(prop instanceof ScriptableObject)) { - throw ScriptRuntime.typeError("Not properly a lambda constructor"); - } - return (ScriptableObject) prop; - } -} diff --git a/src/main/java/org/mozilla/javascript/LambdaFunction.java b/src/main/java/org/mozilla/javascript/LambdaFunction.java deleted file mode 100644 index d27e54f2..00000000 --- a/src/main/java/org/mozilla/javascript/LambdaFunction.java +++ /dev/null @@ -1,73 +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; - -/** - * This class implements a single JavaScript function that has the prototype of the built-in - * Function class, and which is implemented using a single function that can easily be implemented - * using a lambda expression. - */ -public class LambdaFunction extends BaseFunction { - - private static final long serialVersionUID = -8388132362854748293L; - - // The target is expected to be a lambda -- lambdas should not be serialized. - private final transient Callable target; - private final String name; - private final int length; - - /** - * Create a new function. The new object will have the Function prototype and no parent. The - * caller is responsible for binding this object to the appropriate scope. - * - * @param scope scope of the calling context - * @param name name of the function - * @param length the arity of the function - * @param target an object that implements the function in Java. Since Callable is a - * single-function interface this will typically be implemented as a lambda. - */ - public LambdaFunction(Scriptable scope, String name, int length, Callable target) { - this.target = target; - this.name = name; - this.length = length; - ScriptRuntime.setFunctionProtoAndParent(this, scope); - setupDefaultPrototype(); - } - - /** Create a new built-in function, with no name, and no default prototype. */ - public LambdaFunction(Scriptable scope, int length, Callable target) { - this.target = target; - this.length = length; - this.name = ""; - ScriptRuntime.setFunctionProtoAndParent(this, scope); - } - - @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - return target.call(cx, scope, thisObj, args); - } - - @Override - public Scriptable construct(Context cx, Scriptable scope, Object[] args) { - throw ScriptRuntime.typeErrorById("msg.no.new", getFunctionName()); - } - - @Override - public int getLength() { - return length; - } - - @Override - public int getArity() { - return length; - } - - @Override - public String getFunctionName() { - return name; - } -} diff --git a/src/main/java/org/mozilla/javascript/LambdaSlot.java b/src/main/java/org/mozilla/javascript/LambdaSlot.java deleted file mode 100644 index 599ebde7..00000000 --- a/src/main/java/org/mozilla/javascript/LambdaSlot.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.mozilla.javascript; - -import java.util.function.Consumer; -import java.util.function.Supplier; - -/** - * This is a specialization of property access using some lambda functions. It behaves exactly like - * any other slot that has only a value, but instead of getting the value directly, it comes from - * calling the functions. This makes it different from GetterSlot, which lets the user see directly - * that there is a getter or a setter function involved. This makes this class useful for - * implementing properties that behave like any other JavaScript property but which are implemented - * using some native functionality without using reflection. - */ -public class LambdaSlot extends Slot { - private static final long serialVersionUID = -3046681698806493052L; - - LambdaSlot(Slot oldSlot) { - super(oldSlot); - } - - transient Supplier getter; - transient Consumer setter; - - @Override - boolean isValueSlot() { - return false; - } - - @Override - boolean isSetterSlot() { - return false; - } - - @Override - ScriptableObject getPropertyDescriptor(Context cx, Scriptable scope) { - ScriptableObject desc = (ScriptableObject) cx.newObject(scope); - if (getter != null) { - desc.defineProperty("value", getter.get(), ScriptableObject.EMPTY); - } else { - desc.defineProperty("value", value, ScriptableObject.EMPTY); - } - desc.setCommonDescriptorProperties(getAttributes(), true); - return desc; - } - - @Override - public boolean setValue(Object value, Scriptable owner, Scriptable start) { - if (setter != null) { - if (owner == start) { - setter.accept(value); - return true; - } - return false; - } - return super.setValue(value, owner, start); - } - - @Override - public Object getValue(Scriptable start) { - if (getter != null) { - return getter.get(); - } - return super.getValue(start); - } -} diff --git a/src/main/java/org/mozilla/javascript/LazilyLoadedCtor.java b/src/main/java/org/mozilla/javascript/LazilyLoadedCtor.java deleted file mode 100644 index b9d0a1ed..00000000 --- a/src/main/java/org/mozilla/javascript/LazilyLoadedCtor.java +++ /dev/null @@ -1,132 +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.lang.reflect.InvocationTargetException; -import java.security.AccessController; -import java.security.PrivilegedAction; - -/** - * Avoid loading classes unless they are used. - * - *

This improves startup time and average memory usage. - */ -public final class LazilyLoadedCtor implements Serializable { - private static final long serialVersionUID = 1L; - private static final int STATE_BEFORE_INIT = 0; - private static final int STATE_INITIALIZING = 1; - private static final int STATE_WITH_VALUE = 2; - - private final ScriptableObject scope; - private final String propertyName; - private final String className; - private final boolean sealed; - private final boolean privileged; - private Object initializedValue; - private int state; - - public LazilyLoadedCtor(ScriptableObject scope, String propertyName, - String className, boolean sealed) - { - this(scope, propertyName, className, sealed, false); - } - - LazilyLoadedCtor(ScriptableObject scope, String propertyName, - String className, boolean sealed, boolean privileged) - { - - this.scope = scope; - this.propertyName = propertyName; - this.className = className; - this.sealed = sealed; - this.privileged = privileged; - this.state = STATE_BEFORE_INIT; - - scope.addLazilyInitializedValue(propertyName, 0, this, - ScriptableObject.DONTENUM); - } - - void init() - { - synchronized (this) { - if (state == STATE_INITIALIZING) - throw new IllegalStateException( - "Recursive initialization for "+propertyName); - if (state == STATE_BEFORE_INIT) { - state = STATE_INITIALIZING; - // Set value now to have something to set in finally block if - // buildValue throws. - Object value = Scriptable.NOT_FOUND; - try { - value = buildValue(); - } finally { - initializedValue = value; - state = STATE_WITH_VALUE; - } - } - } - } - - Object getValue() - { - if (state != STATE_WITH_VALUE) - throw new IllegalStateException(propertyName); - return initializedValue; - } - - private Object buildValue() - { - if(privileged) - { - return AccessController.doPrivileged(new PrivilegedAction() - { - @Override - public Object run() - { - return buildValue0(); - } - }); - } - return buildValue0(); - } - - private Object buildValue0() - { - Class cl = cast(Kit.classOrNull(className)); - if (cl != null) { - try { - Object value = ScriptableObject.buildClassCtor(scope, cl, - sealed, false); - if (value != null) { - return value; - } - // cl has own static initializer which is expected - // to set the property on its own. - value = scope.get(propertyName, scope); - if (value != Scriptable.NOT_FOUND) - return value; - } catch (InvocationTargetException ex) { - Throwable target = ex.getTargetException(); - if (target instanceof RuntimeException) { - throw (RuntimeException)target; - } - } catch (RhinoException ex) { - } catch (InstantiationException ex) { - } catch (IllegalAccessException ex) { - } catch (SecurityException ex) { - } - } - return Scriptable.NOT_FOUND; - } - - @SuppressWarnings({"unchecked"}) - private static Class cast(Class cl) { - return (Class)cl; - } - -} diff --git a/src/main/java/org/mozilla/javascript/LazyLoadSlot.java b/src/main/java/org/mozilla/javascript/LazyLoadSlot.java deleted file mode 100644 index 43ba0710..00000000 --- a/src/main/java/org/mozilla/javascript/LazyLoadSlot.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.mozilla.javascript; - -/** - * This is a specialization of Slot to store values that are retrieved via calls to script - * functions. It's used to load built-in objects more efficiently. - */ -public class LazyLoadSlot extends Slot { - LazyLoadSlot(Slot oldSlot) { - super(oldSlot); - } - - @Override - public Object getValue(Scriptable start) { - Object val = this.value; - if (val instanceof LazilyLoadedCtor) { - LazilyLoadedCtor initializer = (LazilyLoadedCtor) val; - try { - initializer.init(); - } finally { - this.value = val = initializer.getValue(); - } - } - return val; - } -} diff --git a/src/main/java/org/mozilla/javascript/MemberBox.java b/src/main/java/org/mozilla/javascript/MemberBox.java deleted file mode 100644 index b8c95c73..00000000 --- a/src/main/java/org/mozilla/javascript/MemberBox.java +++ /dev/null @@ -1,398 +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.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Member; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; - -/** - * Wrapper class for Method and Constructor instances to cache getParameterTypes() results, recover - * from IllegalAccessException in some cases and provide serialization support. - * - * @author Igor Bukanov - */ -final class MemberBox implements Serializable { - private static final long serialVersionUID = 6358550398665688245L; - - private transient Member memberObject; - transient Class[] argTypes; - transient boolean vararg; - - transient Function asGetterFunction; - transient Function asSetterFunction; - transient Object delegateTo; - - MemberBox(Method method) { - init(method); - } - - MemberBox(Constructor constructor) { - init(constructor); - } - - private void init(Method method) { - this.memberObject = method; - this.argTypes = method.getParameterTypes(); - this.vararg = method.isVarArgs(); - } - - private void init(Constructor constructor) { - this.memberObject = constructor; - this.argTypes = constructor.getParameterTypes(); - this.vararg = constructor.isVarArgs(); - } - - Method method() { - return (Method) memberObject; - } - - Constructor ctor() { - return (Constructor) memberObject; - } - - Member member() { - return memberObject; - } - - boolean isMethod() { - return memberObject instanceof Method; - } - - boolean isCtor() { - return memberObject instanceof Constructor; - } - - boolean isStatic() { - return Modifier.isStatic(memberObject.getModifiers()); - } - - boolean isPublic() { - return Modifier.isPublic(memberObject.getModifiers()); - } - - String getName() { - return memberObject.getName(); - } - - Class getDeclaringClass() { - return memberObject.getDeclaringClass(); - } - - String toJavaDeclaration() { - StringBuilder sb = new StringBuilder(); - if (isMethod()) { - Method method = method(); - sb.append(method.getReturnType()); - sb.append(' '); - sb.append(method.getName()); - } else { - Constructor ctor = ctor(); - String name = ctor.getDeclaringClass().getName(); - int lastDot = name.lastIndexOf('.'); - if (lastDot >= 0) { - name = name.substring(lastDot + 1); - } - sb.append(name); - } - sb.append(JavaMembers.liveConnectSignature(argTypes)); - return sb.toString(); - } - - @Override - public String toString() { - return memberObject.toString(); - } - - /** Function returned by calls to __lookupGetter__ */ - Function asGetterFunction(final String name, final Scriptable scope) { - // Note: scope is the scriptable this function is related to; therefore this function - // is constant for this member box. - // Because of this we can cache the function in the attribute - if (asGetterFunction == null) { - asGetterFunction = - new BaseFunction(scope, ScriptableObject.getFunctionPrototype(scope)) { - @Override - public Object call( - Context cx, - Scriptable callScope, - Scriptable thisObj, - Object[] originalArgs) { - MemberBox nativeGetter = MemberBox.this; - Object getterThis; - Object[] args; - if (nativeGetter.delegateTo == null) { - getterThis = thisObj; - args = ScriptRuntime.emptyArgs; - } else { - getterThis = nativeGetter.delegateTo; - args = new Object[] {thisObj}; - } - return nativeGetter.invoke(getterThis, args); - } - - @Override - public String getFunctionName() { - return name; - } - }; - } - return asGetterFunction; - } - - /** Function returned by calls to __lookupSetter__ */ - Function asSetterFunction(final String name, final Scriptable scope) { - // Note: scope is the scriptable this function is related to; therefore this function - // is constant for this member box. - // Because of this we can cache the function in the attribute - if (asSetterFunction == null) { - asSetterFunction = - new BaseFunction(scope, ScriptableObject.getFunctionPrototype(scope)) { - @Override - public Object call( - Context cx, - Scriptable callScope, - Scriptable thisObj, - Object[] originalArgs) { - MemberBox nativeSetter = MemberBox.this; - Object setterThis; - Object[] args; - Object value = - originalArgs.length > 0 ? originalArgs[0] : Undefined.instance; - if (nativeSetter.delegateTo == null) { - setterThis = thisObj; - args = new Object[] {value}; - } else { - setterThis = nativeSetter.delegateTo; - args = new Object[] {thisObj, value}; - } - return nativeSetter.invoke(setterThis, args); - } - - @Override - public String getFunctionName() { - return name; - } - }; - } - return asSetterFunction; - } - - Object invoke(Object target, Object[] args) { - Method method = method(); - - // handle delegators - if (target instanceof Delegator) { - target = ((Delegator) target).getDelegee(); - } - for (int i = 0; i < args.length; ++i) { - if (args[i] instanceof Delegator) { - args[i] = ((Delegator) args[i]).getDelegee(); - } - } - - try { - try { - return method.invoke(target, args); - } catch (IllegalAccessException ex) { - Method accessible = searchAccessibleMethod(method, argTypes); - if (accessible != null) { - memberObject = accessible; - method = accessible; - } else { - if (!VMBridge.instance.tryToMakeAccessible(method)) { - throw Context.throwAsScriptRuntimeEx(ex); - } - } - // Retry after recovery - return method.invoke(target, args); - } - } catch (InvocationTargetException ite) { - // Must allow ContinuationPending exceptions to propagate unhindered - Throwable e = ite; - do { - e = ((InvocationTargetException) e).getTargetException(); - } while ((e instanceof InvocationTargetException)); - if (e instanceof ContinuationPending) throw (ContinuationPending) e; - throw Context.throwAsScriptRuntimeEx(e); - } catch (Exception ex) { - throw Context.throwAsScriptRuntimeEx(ex); - } - } - - Object newInstance(Object[] args) { - Constructor ctor = ctor(); - try { - try { - return ctor.newInstance(args); - } catch (IllegalAccessException ex) { - if (!VMBridge.instance.tryToMakeAccessible(ctor)) { - throw Context.throwAsScriptRuntimeEx(ex); - } - } - return ctor.newInstance(args); - } catch (Exception ex) { - throw Context.throwAsScriptRuntimeEx(ex); - } - } - - private static Method searchAccessibleMethod(Method method, Class[] params) { - int modifiers = method.getModifiers(); - if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) { - Class c = method.getDeclaringClass(); - if (!Modifier.isPublic(c.getModifiers())) { - String name = method.getName(); - Class[] intfs = c.getInterfaces(); - for (int i = 0, N = intfs.length; i != N; ++i) { - Class intf = intfs[i]; - if (Modifier.isPublic(intf.getModifiers())) { - try { - return intf.getMethod(name, params); - } catch (NoSuchMethodException ex) { - } catch (SecurityException ex) { - } - } - } - for (; ; ) { - c = c.getSuperclass(); - if (c == null) { - break; - } - if (Modifier.isPublic(c.getModifiers())) { - try { - Method m = c.getMethod(name, params); - int mModifiers = m.getModifiers(); - if (Modifier.isPublic(mModifiers) && !Modifier.isStatic(mModifiers)) { - return m; - } - } catch (NoSuchMethodException ex) { - } catch (SecurityException ex) { - } - } - } - } - } - return null; - } - - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - Member member = readMember(in); - if (member instanceof Method) { - init((Method) member); - } else { - init((Constructor) member); - } - } - - private void writeObject(ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - writeMember(out, memberObject); - } - - /** - * Writes a Constructor or Method object. - * - *

Methods and Constructors are not serializable, so we must serialize information about the - * class, the name, and the parameters and recreate upon deserialization. - */ - private static void writeMember(ObjectOutputStream out, Member member) throws IOException { - if (member == null) { - out.writeBoolean(false); - return; - } - out.writeBoolean(true); - if (!(member instanceof Method || member instanceof Constructor)) - throw new IllegalArgumentException("not Method or Constructor"); - out.writeBoolean(member instanceof Method); - out.writeObject(member.getName()); - out.writeObject(member.getDeclaringClass()); - if (member instanceof Method) { - writeParameters(out, ((Method) member).getParameterTypes()); - } else { - writeParameters(out, ((Constructor) member).getParameterTypes()); - } - } - - /** Reads a Method or a Constructor from the stream. */ - private static Member readMember(ObjectInputStream in) - throws IOException, ClassNotFoundException { - if (!in.readBoolean()) return null; - boolean isMethod = in.readBoolean(); - String name = (String) in.readObject(); - Class declaring = (Class) in.readObject(); - Class[] parms = readParameters(in); - try { - if (isMethod) { - return declaring.getMethod(name, parms); - } - return declaring.getConstructor(parms); - } catch (NoSuchMethodException e) { - throw new IOException("Cannot find member: " + e); - } - } - - private static final Class[] primitives = { - Boolean.TYPE, - Byte.TYPE, - Character.TYPE, - Double.TYPE, - Float.TYPE, - Integer.TYPE, - Long.TYPE, - Short.TYPE, - Void.TYPE - }; - - /** - * Writes an array of parameter types to the stream. - * - *

Requires special handling because primitive types cannot be found upon deserialization by - * the default Java implementation. - */ - private static void writeParameters(ObjectOutputStream out, Class[] parms) - throws IOException { - out.writeShort(parms.length); - outer: - for (int i = 0; i < parms.length; i++) { - Class parm = parms[i]; - boolean primitive = parm.isPrimitive(); - out.writeBoolean(primitive); - if (!primitive) { - out.writeObject(parm); - continue; - } - for (int j = 0; j < primitives.length; j++) { - if (parm.equals(primitives[j])) { - out.writeByte(j); - continue outer; - } - } - throw new IllegalArgumentException("Primitive " + parm + " not found"); - } - } - - /** Reads an array of parameter types from the stream. */ - private static Class[] readParameters(ObjectInputStream in) - throws IOException, ClassNotFoundException { - Class[] result = new Class[in.readShort()]; - for (int i = 0; i < result.length; i++) { - if (!in.readBoolean()) { - result[i] = (Class) in.readObject(); - continue; - } - result[i] = primitives[in.readByte()]; - } - return result; - } -} diff --git a/src/main/java/org/mozilla/javascript/MembersPatch.java b/src/main/java/org/mozilla/javascript/MembersPatch.java deleted file mode 100644 index 44648f1a..00000000 --- a/src/main/java/org/mozilla/javascript/MembersPatch.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.mozilla.javascript; - -import com.zhekasmirnov.horizon.runtime.logger.Logger; - -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.HashMap; - -public class MembersPatch { - private static HashMap overrides = new HashMap<>(); - - public static void addOverride(String from, String to){ - Logger.debug("MEMBERS_PATCH", "Adding override " + from + " -> " + to); - overrides.put(from, to); - } - - static String getOverride(String from){ - String result = overrides.get(from); - if(result != null){ - return result; - } else { - return from; - } - } - - static Method override(Class cl, Method method){ - String path = cl.getName() + "." + method.getName(); - - if(overrides.containsKey(path)){ - String overridenPath = overrides.get(path); - int ind = overridenPath.lastIndexOf("."); - try{ - Class overridenClass = Class.forName(overridenPath.substring(0, ind)); - Method overridenMethod = overridenClass.getMethod(overridenPath.substring(ind + 1), method.getParameterTypes()); - Logger.debug("MEMBERS_PATCH", "Successfully overrided method " + path); - return overridenMethod; - } catch(ClassNotFoundException | SecurityException e) { - throw new RuntimeException("Exception occured while trying to override existing method. ", e); - } catch (NoSuchMethodException ignored) { - Logger.debug("MEMBERS_PATCH", "Could't override method " + path + " with arguments types " - + Arrays.toString(method.getParameterTypes())); - } - } - - return method; - } -} \ No newline at end of file diff --git a/src/main/java/org/mozilla/javascript/NativeArray.java b/src/main/java/org/mozilla/javascript/NativeArray.java deleted file mode 100644 index 28143dd0..00000000 --- a/src/main/java/org/mozilla/javascript/NativeArray.java +++ /dev/null @@ -1,2637 +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 static org.mozilla.javascript.ScriptRuntimeES6.requireObjectCoercible; - -import java.io.Serializable; -import java.util.AbstractList; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.ConcurrentModificationException; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; -import java.util.NoSuchElementException; -import org.mozilla.javascript.regexp.NativeRegExp; -import org.mozilla.javascript.xml.XMLObject; - -/** - * This class implements the Array native object. - * - * @author Norris Boyd - * @author Mike McCabe - */ -public class NativeArray extends IdScriptableObject implements List { - private static final long serialVersionUID = 7331366857676127338L; - - /* - * Optimization possibilities and open issues: - * - Long vs. double schizophrenia. I suspect it might be better - * to use double throughout. - * - * - Functions that need a new Array call "new Array" in the - * current scope rather than using a hardwired constructor; - * "Array" could be redefined. It turns out that js calls the - * equivalent of "new Array" in the current scope, except that it - * always gets at least an object back, even when Array == null. - */ - - private static final Object ARRAY_TAG = "Array"; - private static final Long NEGATIVE_ONE = Long.valueOf(-1); - - static void init(Scriptable scope, boolean sealed) { - NativeArray obj = new NativeArray(0); - obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); - } - - static int getMaximumInitialCapacity() { - return maximumInitialCapacity; - } - - static void setMaximumInitialCapacity(int maximumInitialCapacity) { - NativeArray.maximumInitialCapacity = maximumInitialCapacity; - } - - public NativeArray(long lengthArg) { - denseOnly = lengthArg <= maximumInitialCapacity; - if (denseOnly) { - int intLength = (int) lengthArg; - if (intLength < DEFAULT_INITIAL_CAPACITY) intLength = DEFAULT_INITIAL_CAPACITY; - dense = new Object[intLength]; - Arrays.fill(dense, Scriptable.NOT_FOUND); - } - length = lengthArg; - } - - public NativeArray(Object[] array) { - denseOnly = true; - dense = array; - length = array.length; - } - - @Override - public String getClassName() { - return "Array"; - } - - private static final int Id_length = 1, MAX_INSTANCE_ID = 1; - - @Override - protected int getMaxInstanceId() { - return MAX_INSTANCE_ID; - } - - @Override - protected void setInstanceIdAttributes(int id, int attr) { - if (id == Id_length) { - lengthAttr = attr; - } - } - - @Override - protected int findInstanceIdInfo(String s) { - if (s.equals("length")) { - return instanceIdInfo(lengthAttr, Id_length); - } - return super.findInstanceIdInfo(s); - } - - @Override - protected String getInstanceIdName(int id) { - if (id == Id_length) { - return "length"; - } - return super.getInstanceIdName(id); - } - - @Override - protected Object getInstanceIdValue(int id) { - if (id == Id_length) { - return ScriptRuntime.wrapNumber(length); - } - return super.getInstanceIdValue(id); - } - - @Override - protected void setInstanceIdValue(int id, Object value) { - if (id == Id_length) { - setLength(value); - return; - } - super.setInstanceIdValue(id, value); - } - - @Override - protected void fillConstructorProperties(IdFunctionObject ctor) { - addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_join, "join", 1); - addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_reverse, "reverse", 0); - addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_sort, "sort", 1); - addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_push, "push", 1); - addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_pop, "pop", 0); - addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_shift, "shift", 0); - addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_unshift, "unshift", 1); - addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_splice, "splice", 2); - addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_concat, "concat", 1); - addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_slice, "slice", 2); - addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_indexOf, "indexOf", 1); - addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_lastIndexOf, "lastIndexOf", 1); - addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_every, "every", 1); - addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_filter, "filter", 1); - addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_forEach, "forEach", 1); - addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_map, "map", 1); - addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_some, "some", 1); - addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_find, "find", 1); - addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_findIndex, "findIndex", 1); - addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_reduce, "reduce", 1); - addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_reduceRight, "reduceRight", 1); - addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_isArray, "isArray", 1); - addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_of, "of", 0); - addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_from, "from", 1); - super.fillConstructorProperties(ctor); - } - - @Override - protected void initPrototypeId(int id) { - if (id == SymbolId_iterator) { - initPrototypeMethod(ARRAY_TAG, id, SymbolKey.ITERATOR, "[Symbol.iterator]", 0); - return; - } - - String s, fnName = null; - int arity; - switch (id) { - case Id_constructor: - arity = 1; - s = "constructor"; - break; - case Id_toString: - arity = 0; - s = "toString"; - break; - case Id_toLocaleString: - arity = 0; - s = "toLocaleString"; - break; - case Id_toSource: - arity = 0; - s = "toSource"; - break; - case Id_join: - arity = 1; - s = "join"; - break; - case Id_reverse: - arity = 0; - s = "reverse"; - break; - case Id_sort: - arity = 1; - s = "sort"; - break; - case Id_push: - arity = 1; - s = "push"; - break; - case Id_pop: - arity = 0; - s = "pop"; - break; - case Id_shift: - arity = 0; - s = "shift"; - break; - case Id_unshift: - arity = 1; - s = "unshift"; - break; - case Id_splice: - arity = 2; - s = "splice"; - break; - case Id_concat: - arity = 1; - s = "concat"; - break; - case Id_slice: - arity = 2; - s = "slice"; - break; - case Id_indexOf: - arity = 1; - s = "indexOf"; - break; - case Id_lastIndexOf: - arity = 1; - s = "lastIndexOf"; - break; - case Id_every: - arity = 1; - s = "every"; - break; - case Id_filter: - arity = 1; - s = "filter"; - break; - case Id_forEach: - arity = 1; - s = "forEach"; - break; - case Id_map: - arity = 1; - s = "map"; - break; - case Id_some: - arity = 1; - s = "some"; - break; - case Id_find: - arity = 1; - s = "find"; - break; - case Id_findIndex: - arity = 1; - s = "findIndex"; - break; - case Id_reduce: - arity = 1; - s = "reduce"; - break; - case Id_reduceRight: - arity = 1; - s = "reduceRight"; - break; - case Id_fill: - arity = 1; - s = "fill"; - break; - case Id_keys: - arity = 0; - s = "keys"; - break; - case Id_values: - arity = 0; - s = "values"; - break; - case Id_entries: - arity = 0; - s = "entries"; - break; - case Id_includes: - arity = 1; - s = "includes"; - break; - case Id_copyWithin: - arity = 2; - s = "copyWithin"; - break; - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - - initPrototypeMethod(ARRAY_TAG, id, s, fnName, arity); - } - - @Override - public Object execIdCall( - IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!f.hasTag(ARRAY_TAG)) { - return super.execIdCall(f, cx, scope, thisObj, args); - } - int id = f.methodId(); - again: - for (; ; ) { - switch (id) { - case ConstructorId_join: - case ConstructorId_reverse: - case ConstructorId_sort: - case ConstructorId_push: - case ConstructorId_pop: - case ConstructorId_shift: - case ConstructorId_unshift: - case ConstructorId_splice: - case ConstructorId_concat: - case ConstructorId_slice: - case ConstructorId_indexOf: - case ConstructorId_lastIndexOf: - case ConstructorId_every: - case ConstructorId_filter: - case ConstructorId_forEach: - case ConstructorId_map: - case ConstructorId_some: - case ConstructorId_find: - case ConstructorId_findIndex: - case ConstructorId_reduce: - case ConstructorId_reduceRight: - { - // this is a small trick; we will handle all the ConstructorId_xxx calls - // the same way the object calls are processed - // so we adjust the args, inverting the id and - // restarting the method selection - // Attention: the implementations have to be aware of this - if (args.length > 0) { - thisObj = ScriptRuntime.toObject(cx, scope, args[0]); - Object[] newArgs = new Object[args.length - 1]; - for (int i = 0; i < newArgs.length; i++) newArgs[i] = args[i + 1]; - args = newArgs; - } - id = -id; - continue again; - } - - case ConstructorId_isArray: - return Boolean.valueOf(args.length > 0 && js_isArray(args[0])); - - case ConstructorId_of: - { - return js_of(cx, scope, thisObj, args); - } - - case ConstructorId_from: - { - return js_from(cx, scope, thisObj, args); - } - - case Id_constructor: - { - boolean inNewExpr = (thisObj == null); - if (!inNewExpr) { - // IdFunctionObject.construct will set up parent, proto - return f.construct(cx, scope, args); - } - return jsConstructor(cx, scope, args); - } - - case Id_toString: - return toStringHelper( - cx, - scope, - thisObj, - cx.hasFeature(Context.FEATURE_TO_STRING_AS_SOURCE), - false); - - case Id_toLocaleString: - return toStringHelper(cx, scope, thisObj, false, true); - - case Id_toSource: - return toStringHelper(cx, scope, thisObj, true, false); - - case Id_join: - return js_join(cx, scope, thisObj, args); - - case Id_reverse: - return js_reverse(cx, scope, thisObj, args); - - case Id_sort: - return js_sort(cx, scope, thisObj, args); - - case Id_push: - return js_push(cx, scope, thisObj, args); - - case Id_pop: - return js_pop(cx, scope, thisObj, args); - - case Id_shift: - return js_shift(cx, scope, thisObj, args); - - case Id_unshift: - return js_unshift(cx, scope, thisObj, args); - - case Id_splice: - return js_splice(cx, scope, thisObj, args); - - case Id_concat: - return js_concat(cx, scope, thisObj, args); - - case Id_slice: - return js_slice(cx, scope, thisObj, args); - - case Id_indexOf: - return js_indexOf(cx, scope, thisObj, args); - - case Id_lastIndexOf: - return js_lastIndexOf(cx, scope, thisObj, args); - - case Id_includes: - return js_includes(cx, scope, thisObj, args); - - case Id_fill: - return js_fill(cx, scope, thisObj, args); - - case Id_copyWithin: - return js_copyWithin(cx, scope, thisObj, args); - - case Id_every: - case Id_filter: - case Id_forEach: - case Id_map: - case Id_some: - case Id_find: - case Id_findIndex: - return iterativeMethod(cx, f, scope, thisObj, args); - case Id_reduce: - case Id_reduceRight: - return reduceMethod(cx, id, scope, thisObj, args); - - case Id_keys: - thisObj = ScriptRuntime.toObject(cx, scope, thisObj); - return new NativeArrayIterator( - scope, thisObj, NativeArrayIterator.ARRAY_ITERATOR_TYPE.KEYS); - - case Id_entries: - thisObj = ScriptRuntime.toObject(cx, scope, thisObj); - return new NativeArrayIterator( - scope, thisObj, NativeArrayIterator.ARRAY_ITERATOR_TYPE.ENTRIES); - - case Id_values: - case SymbolId_iterator: - thisObj = ScriptRuntime.toObject(cx, scope, thisObj); - return new NativeArrayIterator( - scope, thisObj, NativeArrayIterator.ARRAY_ITERATOR_TYPE.VALUES); - } - throw new IllegalArgumentException( - "Array.prototype has no method: " + f.getFunctionName()); - } - } - - @Override - public Object get(int index, Scriptable start) { - if (!denseOnly && isGetterOrSetter(null, index, false)) return super.get(index, start); - if (dense != null && 0 <= index && index < dense.length) return dense[index]; - return super.get(index, start); - } - - @Override - public boolean has(int index, Scriptable start) { - if (!denseOnly && isGetterOrSetter(null, index, false)) return super.has(index, start); - if (dense != null && 0 <= index && index < dense.length) return dense[index] != NOT_FOUND; - return super.has(index, start); - } - - private static long toArrayIndex(Object id) { - if (id instanceof String) { - return toArrayIndex((String) id); - } else if (id instanceof Number) { - return toArrayIndex(((Number) id).doubleValue()); - } - return -1; - } - - // if id is an array index (ECMA 15.4.0), return the number, - // otherwise return -1L - private static long toArrayIndex(String id) { - long index = toArrayIndex(ScriptRuntime.toNumber(id)); - // Assume that ScriptRuntime.toString(index) is the same - // as java.lang.Long.toString(index) for long - if (Long.toString(index).equals(id)) { - return index; - } - return -1; - } - - private static long toArrayIndex(double d) { - if (!Double.isNaN(d)) { - long index = ScriptRuntime.toUint32(d); - if (index == d && index != 4294967295L) { - return index; - } - } - return -1; - } - - private static int toDenseIndex(Object id) { - long index = toArrayIndex(id); - return 0 <= index && index < Integer.MAX_VALUE ? (int) index : -1; - } - - @Override - public void put(String id, Scriptable start, Object value) { - super.put(id, start, value); - if (start == this) { - // If the object is sealed, super will throw exception - long index = toArrayIndex(id); - if (index >= length) { - length = index + 1; - modCount++; - denseOnly = false; - } - } - } - - private boolean ensureCapacity(int capacity) { - if (capacity > dense.length) { - if (capacity > MAX_PRE_GROW_SIZE) { - denseOnly = false; - return false; - } - capacity = Math.max(capacity, (int) (dense.length * GROW_FACTOR)); - Object[] newDense = new Object[capacity]; - System.arraycopy(dense, 0, newDense, 0, dense.length); - Arrays.fill(newDense, dense.length, newDense.length, Scriptable.NOT_FOUND); - dense = newDense; - } - return true; - } - - @Override - public void put(int index, Scriptable start, Object value) { - if (start == this - && !isSealed() - && dense != null - && 0 <= index - && (denseOnly || !isGetterOrSetter(null, index, true))) { - if (!isExtensible() && this.length <= index) { - return; - } else if (index < dense.length) { - dense[index] = value; - if (this.length <= index) { - this.length = (long) index + 1; - this.modCount++; - } - return; - } else if (denseOnly - && index < dense.length * GROW_FACTOR - && ensureCapacity(index + 1)) { - dense[index] = value; - this.length = (long) index + 1; - this.modCount++; - return; - } else { - denseOnly = false; - } - } - super.put(index, start, value); - if (start == this && (lengthAttr & READONLY) == 0) { - // only set the array length if given an array index (ECMA 15.4.0) - if (this.length <= index) { - // avoid overflowing index! - this.length = (long) index + 1; - this.modCount++; - } - } - } - - @Override - public void delete(int index) { - if (dense != null - && 0 <= index - && index < dense.length - && !isSealed() - && (denseOnly || !isGetterOrSetter(null, index, true))) { - dense[index] = NOT_FOUND; - } else { - super.delete(index); - } - } - - @Override - public Object[] getIds(boolean nonEnumerable, boolean getSymbols) { - Object[] superIds = super.getIds(nonEnumerable, getSymbols); - if (dense == null) { - return superIds; - } - int N = dense.length; - long currentLength = length; - if (N > currentLength) { - N = (int) currentLength; - } - if (N == 0) { - return superIds; - } - int superLength = superIds.length; - Object[] ids = new Object[N + superLength]; - - int presentCount = 0; - for (int i = 0; i != N; ++i) { - // Replace existing elements by their indexes - if (dense[i] != NOT_FOUND) { - ids[presentCount] = Integer.valueOf(i); - ++presentCount; - } - } - if (presentCount != N) { - // dense contains deleted elems, need to shrink the result - Object[] tmp = new Object[presentCount + superLength]; - System.arraycopy(ids, 0, tmp, 0, presentCount); - ids = tmp; - } - System.arraycopy(superIds, 0, ids, presentCount, superLength); - return ids; - } - - public List getIndexIds() { - Object[] ids = getIds(); - List indices = new ArrayList(ids.length); - for (Object id : ids) { - int int32Id = ScriptRuntime.toInt32(id); - if (int32Id >= 0 - && ScriptRuntime.toString(int32Id).equals(ScriptRuntime.toString(id))) { - indices.add(Integer.valueOf(int32Id)); - } - } - return indices; - } - - @Override - public Object getDefaultValue(Class hint) { - if (hint == ScriptRuntime.NumberClass) { - Context cx = Context.getContext(); - if (cx.getLanguageVersion() == Context.VERSION_1_2) return Long.valueOf(length); - } - return super.getDefaultValue(hint); - } - - private ScriptableObject defaultIndexPropertyDescriptor(Object value) { - Scriptable scope = getParentScope(); - if (scope == null) scope = this; - ScriptableObject desc = new NativeObject(); - ScriptRuntime.setBuiltinProtoAndParent(desc, scope, TopLevel.Builtins.Object); - desc.defineProperty("value", value, EMPTY); - desc.defineProperty("writable", Boolean.TRUE, EMPTY); - desc.defineProperty("enumerable", Boolean.TRUE, EMPTY); - desc.defineProperty("configurable", Boolean.TRUE, EMPTY); - return desc; - } - - @Override - public int getAttributes(int index) { - if (dense != null && index >= 0 && index < dense.length && dense[index] != NOT_FOUND) { - return EMPTY; - } - return super.getAttributes(index); - } - - @Override - protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { - if (dense != null) { - int index = toDenseIndex(id); - if (0 <= index && index < dense.length && dense[index] != NOT_FOUND) { - Object value = dense[index]; - return defaultIndexPropertyDescriptor(value); - } - } - return super.getOwnPropertyDescriptor(cx, id); - } - - @Override - protected void defineOwnProperty( - Context cx, Object id, ScriptableObject desc, boolean checkValid) { - long index = toArrayIndex(id); - if (index >= length) { - length = index + 1; - modCount++; - } - - if (index != -1 && dense != null) { - Object[] values = dense; - dense = null; - denseOnly = false; - for (int i = 0; i < values.length; i++) { - if (values[i] != NOT_FOUND) { - if (!isExtensible()) { - // Force creating a slot, before calling .put(...) on the next line, which - // would otherwise fail on a array on which preventExtensions() has been - // called - setAttributes(i, 0); - } - put(i, this, values[i]); - } - } - } - - super.defineOwnProperty(cx, id, desc, checkValid); - - if (id instanceof String && ((String) id).equals("length")) { - lengthAttr = - getAttributes("length"); // Update cached attributes value for length property - } - } - - /** See ECMA 15.4.1,2 */ - private static Object jsConstructor(Context cx, Scriptable scope, Object[] args) { - if (args.length == 0) return new NativeArray(0); - - // Only use 1 arg as first element for version 1.2; for - // any other version (including 1.3) follow ECMA and use it as - // a length. - if (cx.getLanguageVersion() == Context.VERSION_1_2) { - return new NativeArray(args); - } - Object arg0 = args[0]; - if (args.length > 1 || !(arg0 instanceof Number)) { - return new NativeArray(args); - } - long len = ScriptRuntime.toUint32(arg0); - if (len != ((Number) arg0).doubleValue()) { - String msg = ScriptRuntime.getMessageById("msg.arraylength.bad"); - throw ScriptRuntime.rangeError(msg); - } - return new NativeArray(len); - } - - private static Scriptable callConstructorOrCreateArray( - Context cx, Scriptable scope, Scriptable arg, long length, boolean lengthAlways) { - Scriptable result = null; - - if (arg instanceof Function) { - try { - final Object[] args = - (lengthAlways || (length > 0)) - ? new Object[] {Long.valueOf(length)} - : ScriptRuntime.emptyArgs; - result = ((Function) arg).construct(cx, scope, args); - } catch (EcmaError ee) { - if (!"TypeError".equals(ee.getName())) { - throw ee; - } - // If we get here then it is likely that the function we called is not really - // a constructor. Unfortunately there's no better way to tell in Rhino right now. - } - } - - if (result == null) { - // "length" below is really a hint so don't worry if it's really large - result = cx.newArray(scope, (length > Integer.MAX_VALUE) ? 0 : (int) length); - } - - return result; - } - - private static Object js_from(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - final Scriptable items = - ScriptRuntime.toObject(scope, (args.length >= 1) ? args[0] : Undefined.instance); - Object mapArg = (args.length >= 2) ? args[1] : Undefined.instance; - Scriptable thisArg = Undefined.SCRIPTABLE_UNDEFINED; - final boolean mapping = !Undefined.isUndefined(mapArg); - Function mapFn = null; - - if (mapping) { - if (!(mapArg instanceof Function)) { - throw ScriptRuntime.typeErrorById("msg.map.function.not"); - } - mapFn = (Function) mapArg; - if (args.length >= 3) { - thisArg = ensureScriptable(args[2]); - } - } - - Object iteratorProp = ScriptableObject.getProperty(items, SymbolKey.ITERATOR); - if (!(items instanceof NativeArray) - && (iteratorProp != Scriptable.NOT_FOUND) - && !Undefined.isUndefined(iteratorProp)) { - final Object iterator = ScriptRuntime.callIterator(items, cx, scope); - if (!Undefined.isUndefined(iterator)) { - final Scriptable result = - callConstructorOrCreateArray(cx, scope, thisObj, 0, false); - long k = 0; - try (IteratorLikeIterable it = new IteratorLikeIterable(cx, scope, iterator)) { - for (Object temp : it) { - if (mapping) { - temp = - mapFn.call( - cx, - scope, - thisArg, - new Object[] {temp, Long.valueOf(k)}); - } - defineElem(cx, result, k, temp); - k++; - } - } - setLengthProperty(cx, result, k); - return result; - } - } - - final long length = getLengthProperty(cx, items); - final Scriptable result = callConstructorOrCreateArray(cx, scope, thisObj, length, true); - for (long k = 0; k < length; k++) { - Object temp = getElem(cx, items, k); - if (mapping) { - temp = mapFn.call(cx, scope, thisArg, new Object[] {temp, Long.valueOf(k)}); - } - defineElem(cx, result, k, temp); - } - - setLengthProperty(cx, result, length); - return result; - } - - private static Object js_of(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - final Scriptable result = - callConstructorOrCreateArray(cx, scope, thisObj, args.length, true); - - for (int i = 0; i < args.length; i++) { - defineElem(cx, result, i, args[i]); - } - setLengthProperty(cx, result, args.length); - - return result; - } - - public long getLength() { - return length; - } - - /** @deprecated Use {@link #getLength()} instead. */ - @Deprecated - public long jsGet_length() { - return getLength(); - } - - /** - * Change the value of the internal flag that determines whether all storage is handed by a - * dense backing array rather than an associative store. - * - * @param denseOnly new value for denseOnly flag - * @throws IllegalArgumentException if an attempt is made to enable denseOnly after it was - * disabled; NativeArray code is not written to handle switching back to a dense - * representation - */ - void setDenseOnly(boolean denseOnly) { - if (denseOnly && !this.denseOnly) throw new IllegalArgumentException(); - this.denseOnly = denseOnly; - } - - private void setLength(Object val) { - /* XXX do we satisfy this? - * 15.4.5.1 [[Put]](P, V): - * 1. Call the [[CanPut]] method of A with name P. - * 2. If Result(1) is false, return. - * ? - */ - if ((lengthAttr & READONLY) != 0) { - return; - } - - double d = ScriptRuntime.toNumber(val); - long longVal = ScriptRuntime.toUint32(d); - if (longVal != d) { - String msg = ScriptRuntime.getMessageById("msg.arraylength.bad"); - throw ScriptRuntime.rangeError(msg); - } - - if (denseOnly) { - if (longVal < length) { - // downcast okay because denseOnly - Arrays.fill(dense, (int) longVal, dense.length, NOT_FOUND); - length = longVal; - modCount++; - return; - } else if (longVal < MAX_PRE_GROW_SIZE - && longVal < (length * GROW_FACTOR) - && ensureCapacity((int) longVal)) { - length = longVal; - modCount++; - return; - } else { - denseOnly = false; - } - } - if (longVal < length) { - // remove all properties between longVal and length - if (length - longVal > 0x1000) { - // assume that the representation is sparse - Object[] e = getIds(); // will only find in object itself - for (int i = 0; i < e.length; i++) { - Object id = e[i]; - if (id instanceof String) { - // > MAXINT will appear as string - String strId = (String) id; - long index = toArrayIndex(strId); - if (index >= longVal) delete(strId); - } else { - int index = ((Integer) id).intValue(); - if (index >= longVal) delete(index); - } - } - } else { - // assume a dense representation - for (long i = longVal; i < length; i++) { - deleteElem(this, i); - } - } - } - length = longVal; - modCount++; - } - - /* Support for generic Array-ish objects. Most of the Array - * functions try to be generic; anything that has a length - * property is assumed to be an array. - * getLengthProperty returns 0 if obj does not have the length property - * or its value is not convertible to a number. - */ - static long getLengthProperty(Context cx, Scriptable obj) { - // These will give numeric lengths within Uint32 range. - if (obj instanceof NativeString) { - return ((NativeString) obj).getLength(); - } - if (obj instanceof NativeArray) { - return ((NativeArray) obj).getLength(); - } - if (obj instanceof XMLObject) { - Callable lengthFunc = (Callable) ((XMLObject) obj).get("length", obj); - return ((Number) lengthFunc.call(cx, obj, obj, ScriptRuntime.emptyArgs)).longValue(); - } - - Object len = ScriptableObject.getProperty(obj, "length"); - if (len == Scriptable.NOT_FOUND) { - // toUint32(undefined) == 0 - return 0; - } - - double doubleLen = ScriptRuntime.toNumber(len); - - // ToLength - if (doubleLen > NativeNumber.MAX_SAFE_INTEGER) { - return (long) NativeNumber.MAX_SAFE_INTEGER; - } - if (doubleLen < 0) { - return 0; - } - return (long) doubleLen; - } - - private static Object setLengthProperty(Context cx, Scriptable target, long length) { - Object len = ScriptRuntime.wrapNumber(length); - ScriptableObject.putProperty(target, "length", len); - return len; - } - - /* Utility functions to encapsulate index > Integer.MAX_VALUE - * handling. Also avoids unnecessary object creation that would - * be necessary to use the general ScriptRuntime.get/setElem - * functions... though this is probably premature optimization. - */ - private static void deleteElem(Scriptable target, long index) { - int i = (int) index; - if (i == index) { - target.delete(i); - } else { - target.delete(Long.toString(index)); - } - } - - private static Object getElem(Context cx, Scriptable target, long index) { - Object elem = getRawElem(target, index); - return (elem != Scriptable.NOT_FOUND ? elem : Undefined.instance); - } - - // same as getElem, but without converting NOT_FOUND to undefined - private static Object getRawElem(Scriptable target, long index) { - if (index > Integer.MAX_VALUE) { - return ScriptableObject.getProperty(target, Long.toString(index)); - } - return ScriptableObject.getProperty(target, (int) index); - } - - private static void defineElem(Context cx, Scriptable target, long index, Object value) { - if (index > Integer.MAX_VALUE) { - String id = Long.toString(index); - target.put(id, target, value); - } else { - target.put((int) index, target, value); - } - } - - private static void setElem(Context cx, Scriptable target, long index, Object value) { - if (index > Integer.MAX_VALUE) { - String id = Long.toString(index); - ScriptableObject.putProperty(target, id, value); - } else { - ScriptableObject.putProperty(target, (int) index, value); - } - } - - // Similar as setElem(), but triggers deleteElem() if value is NOT_FOUND - private static void setRawElem(Context cx, Scriptable target, long index, Object value) { - if (value == NOT_FOUND) { - deleteElem(target, index); - } else { - setElem(cx, target, index, value); - } - } - - private static String toStringHelper( - Context cx, Scriptable scope, Scriptable thisObj, boolean toSource, boolean toLocale) { - Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); - - /* It's probably redundant to handle long lengths in this - * function; StringBuilders are limited to 2^31 in java. - */ - long length = getLengthProperty(cx, o); - - StringBuilder result = new StringBuilder(256); - - // whether to return '4,unquoted,5' or '[4, "quoted", 5]' - String separator; - - if (toSource) { - result.append('['); - separator = ", "; - } else { - separator = ","; - } - - boolean haslast = false; - long i = 0; - - boolean toplevel, iterating; - if (cx.iterating == null) { - toplevel = true; - iterating = false; - cx.iterating = new ObjToIntMap(31); - } else { - toplevel = false; - iterating = cx.iterating.has(o); - } - - // Make sure cx.iterating is set to null when done - // so we don't leak memory - try { - if (!iterating) { - // stop recursion - cx.iterating.put(o, 0); - - // make toSource print null and undefined values in recent versions - boolean skipUndefinedAndNull = - !toSource || cx.getLanguageVersion() < Context.VERSION_1_5; - for (i = 0; i < length; i++) { - if (i > 0) result.append(separator); - Object elem = getRawElem(o, i); - if (elem == NOT_FOUND - || (skipUndefinedAndNull - && (elem == null || elem == Undefined.instance))) { - haslast = false; - continue; - } - haslast = true; - - if (toSource) { - result.append(ScriptRuntime.uneval(cx, scope, elem)); - - } else if (elem instanceof String) { - result.append((String) elem); - - } else { - if (toLocale) { - Callable fun; - Scriptable funThis; - fun = - ScriptRuntime.getPropFunctionAndThis( - elem, "toLocaleString", cx, scope); - funThis = ScriptRuntime.lastStoredScriptable(cx); - elem = fun.call(cx, scope, funThis, ScriptRuntime.emptyArgs); - } - result.append(ScriptRuntime.toString(elem)); - } - } - - // processing of thisObj done, remove it from the recursion detector - // to allow thisObj to be again in the array later on - cx.iterating.remove(o); - } - } finally { - if (toplevel) { - cx.iterating = null; - } - } - - if (toSource) { - // for [,,].length behavior; we want toString to be symmetric. - if (!haslast && i > 0) result.append(", ]"); - else result.append(']'); - } - return result.toString(); - } - - /** See ECMA 15.4.4.3 */ - private static String js_join(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); - - long llength = getLengthProperty(cx, o); - int length = (int) llength; - if (llength != length) { - throw Context.reportRuntimeErrorById( - "msg.arraylength.too.big", String.valueOf(llength)); - } - // if no args, use "," as separator - String separator = - (args.length < 1 || args[0] == Undefined.instance) - ? "," - : ScriptRuntime.toString(args[0]); - if (o instanceof NativeArray) { - NativeArray na = (NativeArray) o; - if (na.denseOnly) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < length; i++) { - if (i != 0) { - sb.append(separator); - } - if (i < na.dense.length) { - Object temp = na.dense[i]; - if (temp != null - && temp != Undefined.instance - && temp != Scriptable.NOT_FOUND) { - sb.append(ScriptRuntime.toString(temp)); - } - } - } - return sb.toString(); - } - } - if (length == 0) { - return ""; - } - String[] buf = new String[length]; - int total_size = 0; - for (int i = 0; i != length; i++) { - Object temp = getElem(cx, o, i); - if (temp != null && temp != Undefined.instance) { - String str = ScriptRuntime.toString(temp); - total_size += str.length(); - buf[i] = str; - } - } - total_size += (length - 1) * separator.length(); - StringBuilder sb = new StringBuilder(total_size); - for (int i = 0; i != length; i++) { - if (i != 0) { - sb.append(separator); - } - String str = buf[i]; - if (str != null) { - // str == null for undefined or null - sb.append(str); - } - } - return sb.toString(); - } - - /** See ECMA 15.4.4.4 */ - private static Scriptable js_reverse( - Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); - - if (o instanceof NativeArray) { - NativeArray na = (NativeArray) o; - if (na.denseOnly) { - for (int i = 0, j = ((int) na.length) - 1; i < j; i++, j--) { - Object temp = na.dense[i]; - na.dense[i] = na.dense[j]; - na.dense[j] = temp; - } - return o; - } - } - long len = getLengthProperty(cx, o); - - long half = len / 2; - for (long i = 0; i < half; i++) { - long j = len - i - 1; - Object temp1 = getRawElem(o, i); - Object temp2 = getRawElem(o, j); - setRawElem(cx, o, i, temp2); - setRawElem(cx, o, j, temp1); - } - return o; - } - - /** See ECMA 15.4.4.5 */ - private static Scriptable js_sort( - final Context cx, - final Scriptable scope, - final Scriptable thisObj, - final Object[] args) { - Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); - - final Comparator comparator; - if (args.length > 0 && Undefined.instance != args[0]) { - final Callable jsCompareFunction = ScriptRuntime.getValueFunctionAndThis(args[0], cx); - final Scriptable funThis = ScriptRuntime.lastStoredScriptable(cx); - final Object[] cmpBuf = new Object[2]; // Buffer for cmp arguments - comparator = - new ElementComparator( - new Comparator() { - @Override - public int compare(final Object x, final Object y) { - // This comparator is invoked only for non-undefined objects - cmpBuf[0] = x; - cmpBuf[1] = y; - Object ret = jsCompareFunction.call(cx, scope, funThis, cmpBuf); - double d = ScriptRuntime.toNumber(ret); - int cmp = Double.compare(d, 0); - if (cmp < 0) { - return -1; - } else if (cmp > 0) { - return +1; - } - return 0; - } - }); - } else { - comparator = DEFAULT_COMPARATOR; - } - - long llength = getLengthProperty(cx, o); - final int length = (int) llength; - if (llength != length) { - throw Context.reportRuntimeErrorById( - "msg.arraylength.too.big", String.valueOf(llength)); - } - // copy the JS array into a working array, so it can be - // sorted cheaply. - final Object[] working = new Object[length]; - for (int i = 0; i != length; ++i) { - working[i] = getRawElem(o, i); - } - - Sorting.get().hybridSort(working, comparator); - - // copy the working array back into thisObj - for (int i = 0; i < length; ++i) { - setRawElem(cx, o, i, working[i]); - } - - return o; - } - - private static Object js_push(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); - - if (o instanceof NativeArray) { - NativeArray na = (NativeArray) o; - if (na.denseOnly && na.ensureCapacity((int) na.length + args.length)) { - for (int i = 0; i < args.length; i++) { - na.dense[(int) na.length++] = args[i]; - na.modCount++; - } - return ScriptRuntime.wrapNumber(na.length); - } - } - long length = getLengthProperty(cx, o); - for (int i = 0; i < args.length; i++) { - setElem(cx, o, length + i, args[i]); - } - - length += args.length; - Object lengthObj = setLengthProperty(cx, o, length); - - /* - * If JS1.2, follow Perl4 by returning the last thing pushed. - * Otherwise, return the new array length. - */ - if (cx.getLanguageVersion() == Context.VERSION_1_2) - // if JS1.2 && no arguments, return undefined. - return args.length == 0 ? Undefined.instance : args[args.length - 1]; - - return lengthObj; - } - - private static Object js_pop(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); - - Object result; - if (o instanceof NativeArray) { - NativeArray na = (NativeArray) o; - if (na.denseOnly && na.length > 0) { - na.length--; - na.modCount++; - result = na.dense[(int) na.length]; - na.dense[(int) na.length] = NOT_FOUND; - return result; - } - } - long length = getLengthProperty(cx, o); - if (length > 0) { - length--; - - // Get the to-be-deleted property's value. - result = getElem(cx, o, length); - - // We need to delete the last property, because 'thisObj' may not - // have setLength which does that for us. - deleteElem(o, length); - } else { - result = Undefined.instance; - } - // necessary to match js even when length < 0; js pop will give a - // length property to any target it is called on. - setLengthProperty(cx, o, length); - - return result; - } - - private static Object js_shift( - Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); - - if (o instanceof NativeArray) { - NativeArray na = (NativeArray) o; - if (na.denseOnly && na.length > 0) { - na.length--; - na.modCount++; - Object result = na.dense[0]; - System.arraycopy(na.dense, 1, na.dense, 0, (int) na.length); - na.dense[(int) na.length] = NOT_FOUND; - return result == NOT_FOUND ? Undefined.instance : result; - } - } - Object result; - long length = getLengthProperty(cx, o); - if (length > 0) { - long i = 0; - length--; - - // Get the to-be-deleted property's value. - result = getElem(cx, o, i); - - /* - * Slide down the array above the first element. Leave i - * set to point to the last element. - */ - if (length > 0) { - for (i = 1; i <= length; i++) { - Object temp = getRawElem(o, i); - setRawElem(cx, o, i - 1, temp); - } - } - // We need to delete the last property, because 'thisObj' may not - // have setLength which does that for us. - deleteElem(o, length); - } else { - result = Undefined.instance; - } - setLengthProperty(cx, o, length); - return result; - } - - private static Object js_unshift( - Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); - - if (o instanceof NativeArray) { - NativeArray na = (NativeArray) o; - if (na.denseOnly && na.ensureCapacity((int) na.length + args.length)) { - System.arraycopy(na.dense, 0, na.dense, args.length, (int) na.length); - for (int i = 0; i < args.length; i++) { - na.dense[i] = args[i]; - } - na.length += args.length; - na.modCount++; - return ScriptRuntime.wrapNumber(na.length); - } - } - long length = getLengthProperty(cx, o); - int argc = args.length; - - if (argc > 0) { - if (length + argc > NativeNumber.MAX_SAFE_INTEGER) { - throw ScriptRuntime.typeErrorById("msg.arraylength.too.big", length + argc); - } - - /* Slide up the array to make room for args at the bottom */ - if (length > 0) { - for (long last = length - 1; last >= 0; last--) { - Object temp = getRawElem(o, last); - setRawElem(cx, o, last + argc, temp); - } - } - - /* Copy from argv to the bottom of the array. */ - for (int i = 0; i < args.length; i++) { - setElem(cx, o, i, args[i]); - } - } - /* Follow Perl by returning the new array length. */ - length += argc; - return setLengthProperty(cx, o, length); - } - - private static Object js_splice( - Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); - - NativeArray na = null; - boolean denseMode = false; - if (o instanceof NativeArray) { - na = (NativeArray) o; - denseMode = na.denseOnly; - } - - /* create an empty Array to return. */ - scope = getTopLevelScope(scope); - int argc = args.length; - if (argc == 0) return cx.newArray(scope, 0); - long length = getLengthProperty(cx, o); - - /* Convert the first argument into a starting index. */ - long begin = toSliceIndex(ScriptRuntime.toInteger(args[0]), length); - argc--; - - /* Convert the second argument into count */ - long actualDeleteCount; - if (args.length == 1) { - actualDeleteCount = length - begin; - } else { - double dcount = ScriptRuntime.toInteger(args[1]); - if (dcount < 0) { - actualDeleteCount = 0; - } else if (dcount > (length - begin)) { - actualDeleteCount = length - begin; - } else { - actualDeleteCount = (long) dcount; - } - argc--; - } - - long end = begin + actualDeleteCount; - long delta = argc - actualDeleteCount; - - if (length + delta > NativeNumber.MAX_SAFE_INTEGER) { - throw ScriptRuntime.typeErrorById("msg.arraylength.too.big", length + delta); - } - if (actualDeleteCount > Integer.MAX_VALUE) { - String msg = ScriptRuntime.getMessageById("msg.arraylength.bad"); - throw ScriptRuntime.rangeError(msg); - } - - /* If there are elements to remove, put them into the return value. */ - Object result; - if (actualDeleteCount != 0) { - if (actualDeleteCount == 1 && (cx.getLanguageVersion() == Context.VERSION_1_2)) { - /* - * JS lacks "list context", whereby in Perl one turns the - * single scalar that's spliced out into an array just by - * assigning it to @single instead of $single, or by using it - * as Perl push's first argument, for instance. - * - * JS1.2 emulated Perl too closely and returned a non-Array for - * the single-splice-out case, requiring callers to test and - * wrap in [] if necessary. So JS1.3, default, and other - * versions all return an array of length 1 for uniformity. - */ - result = getElem(cx, o, begin); - } else { - if (denseMode) { - int intLen = (int) (end - begin); - Object[] copy = new Object[intLen]; - System.arraycopy(na.dense, (int) begin, copy, 0, intLen); - result = cx.newArray(scope, copy); - } else { - Scriptable resultArray = cx.newArray(scope, 0); - for (long last = begin; last != end; last++) { - Object temp = getRawElem(o, last); - if (temp != NOT_FOUND) { - setElem(cx, resultArray, last - begin, temp); - } - } - // Need to set length for sparse result array - setLengthProperty(cx, resultArray, end - begin); - result = resultArray; - } - } - } else { // (actualDeleteCount == 0) - if (cx.getLanguageVersion() == Context.VERSION_1_2) { - /* Emulate C JS1.2; if no elements are removed, return undefined. */ - result = Undefined.instance; - } else { - result = cx.newArray(scope, 0); - } - } - - /* Find the direction (up or down) to copy and make way for argv. */ - if (denseMode - && length + delta < Integer.MAX_VALUE - && na.ensureCapacity((int) (length + delta))) { - System.arraycopy( - na.dense, (int) end, na.dense, (int) (begin + argc), (int) (length - end)); - if (argc > 0) { - System.arraycopy(args, 2, na.dense, (int) begin, argc); - } - if (delta < 0) { - Arrays.fill(na.dense, (int) (length + delta), (int) length, NOT_FOUND); - } - na.length = length + delta; - na.modCount++; - return result; - } - - if (delta > 0) { - for (long last = length - 1; last >= end; last--) { - Object temp = getRawElem(o, last); - setRawElem(cx, o, last + delta, temp); - } - } else if (delta < 0) { - for (long last = end; last < length; last++) { - Object temp = getRawElem(o, last); - setRawElem(cx, o, last + delta, temp); - } - // Do this backwards because some implementations might use a - // non-sparse array and therefore might not be able to handle - // deleting elements "in the middle". This makes us compatible - // with older Rhino releases. - for (long k = length - 1; k >= length + delta; --k) { - deleteElem(o, k); - } - } - - /* Copy from argv into the hole to complete the splice. */ - int argoffset = args.length - argc; - for (int i = 0; i < argc; i++) { - setElem(cx, o, begin + i, args[i + argoffset]); - } - - /* Update length in case we deleted elements from the end. */ - setLengthProperty(cx, o, length + delta); - return result; - } - - private static boolean isConcatSpreadable(Context cx, Scriptable scope, Object val) { - // First, look for the new @@isConcatSpreadable test as per ECMAScript 6 and up - if (val instanceof Scriptable) { - final Object spreadable = - ScriptableObject.getProperty((Scriptable) val, SymbolKey.IS_CONCAT_SPREADABLE); - if ((spreadable != Scriptable.NOT_FOUND) && !Undefined.isUndefined(spreadable)) { - // If @@isConcatSpreadable was undefined, we have to fall back to testing for an - // array. - // Otherwise, we found some value - return ScriptRuntime.toBoolean(spreadable); - } - } - - if (cx.getLanguageVersion() < Context.VERSION_ES6) { - // Otherwise, for older Rhino versions, fall back to the old algorithm, which treats - // things with - // the Array constructor as arrays. However, this is contrary to ES6! - final Function ctor = ScriptRuntime.getExistingCtor(cx, scope, "Array"); - if (ScriptRuntime.instanceOf(val, ctor, cx)) { - return true; - } - } - - // Otherwise, it's only spreadable if it's a native array - return js_isArray(val); - } - - // Concat elements of "arg" into the destination, with optimizations for native, - // dense arrays. - private static long concatSpreadArg( - Context cx, Scriptable result, Scriptable arg, long offset) { - long srclen = getLengthProperty(cx, arg); - long newlen = srclen + offset; - - // First, optimize for a pair of native, dense arrays - if ((newlen <= Integer.MAX_VALUE) && (result instanceof NativeArray)) { - final NativeArray denseResult = (NativeArray) result; - if (denseResult.denseOnly && (arg instanceof NativeArray)) { - final NativeArray denseArg = (NativeArray) arg; - if (denseArg.denseOnly) { - // Now we can optimize - denseResult.ensureCapacity((int) newlen); - System.arraycopy( - denseArg.dense, 0, denseResult.dense, (int) offset, (int) srclen); - return newlen; - } - // We could also optimize here if we are copying to a dense target from a non-dense - // native array. However, if the source array is very sparse then the result will be - // very bad -- so don't. - } - } - - // If we get here then we have to do things the generic way - long dstpos = offset; - for (long srcpos = 0; srcpos < srclen; srcpos++, dstpos++) { - final Object temp = getRawElem(arg, srcpos); - if (temp != Scriptable.NOT_FOUND) { - defineElem(cx, result, dstpos, temp); - } - } - return newlen; - } - - private static long doConcat( - Context cx, Scriptable scope, Scriptable result, Object arg, long offset) { - if (isConcatSpreadable(cx, scope, arg)) { - return concatSpreadArg(cx, result, (Scriptable) arg, offset); - } - defineElem(cx, result, offset, arg); - return offset + 1; - } - - /* - * See Ecma 262v3 15.4.4.4 - */ - private static Scriptable js_concat( - Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); - - // create an empty Array to return. - scope = getTopLevelScope(scope); - final Scriptable result = cx.newArray(scope, 0); - - long length = doConcat(cx, scope, result, o, 0); - for (Object arg : args) { - length = doConcat(cx, scope, result, arg, length); - } - - setLengthProperty(cx, result, length); - return result; - } - - private static Scriptable js_slice( - Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); - - long len = getLengthProperty(cx, o); - - long begin, end; - if (args.length == 0) { - begin = 0; - end = len; - } else { - begin = toSliceIndex(ScriptRuntime.toInteger(args[0]), len); - if (args.length == 1 || args[1] == Undefined.instance) { - end = len; - } else { - end = toSliceIndex(ScriptRuntime.toInteger(args[1]), len); - } - } - - if (end - begin > Integer.MAX_VALUE) { - String msg = ScriptRuntime.getMessageById("msg.arraylength.bad"); - throw ScriptRuntime.rangeError(msg); - } - - Scriptable result = cx.newArray(scope, 0); - for (long slot = begin; slot < end; slot++) { - Object temp = getRawElem(o, slot); - if (temp != NOT_FOUND) { - defineElem(cx, result, slot - begin, temp); - } - } - setLengthProperty(cx, result, Math.max(0, end - begin)); - - return result; - } - - private static long toSliceIndex(double value, long length) { - long result; - if (value < 0.0) { - if (value + length < 0.0) { - result = 0; - } else { - result = (long) (value + length); - } - } else if (value > length) { - result = length; - } else { - result = (long) value; - } - return result; - } - - private static Object js_indexOf( - Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - Object compareTo = args.length > 0 ? args[0] : Undefined.instance; - - Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); - long length = getLengthProperty(cx, o); - /* - * From http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:indexOf - * The index at which to begin the search. Defaults to 0, i.e. the - * whole array will be searched. If the index is greater than or - * equal to the length of the array, -1 is returned, i.e. the array - * will not be searched. If negative, it is taken as the offset from - * the end of the array. Note that even when the index is negative, - * the array is still searched from front to back. If the calculated - * index is less than 0, the whole array will be searched. - */ - long start; - if (args.length < 2) { - // default - start = 0; - } else { - start = (long) ScriptRuntime.toInteger(args[1]); - if (start < 0) { - start += length; - if (start < 0) start = 0; - } - if (start > length - 1) return NEGATIVE_ONE; - } - if (o instanceof NativeArray) { - NativeArray na = (NativeArray) o; - if (na.denseOnly) { - Scriptable proto = na.getPrototype(); - for (int i = (int) start; i < length; i++) { - Object val = na.dense[i]; - if (val == NOT_FOUND && proto != null) { - val = ScriptableObject.getProperty(proto, i); - } - if (val != NOT_FOUND && ScriptRuntime.shallowEq(val, compareTo)) { - return Long.valueOf(i); - } - } - return NEGATIVE_ONE; - } - } - for (long i = start; i < length; i++) { - Object val = getRawElem(o, i); - if (val != NOT_FOUND && ScriptRuntime.shallowEq(val, compareTo)) { - return Long.valueOf(i); - } - } - return NEGATIVE_ONE; - } - - private static Object js_lastIndexOf( - Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - Object compareTo = args.length > 0 ? args[0] : Undefined.instance; - - Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); - long length = getLengthProperty(cx, o); - /* - * From http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:lastIndexOf - * The index at which to start searching backwards. Defaults to the - * array's length, i.e. the whole array will be searched. If the - * index is greater than or equal to the length of the array, the - * whole array will be searched. If negative, it is taken as the - * offset from the end of the array. Note that even when the index - * is negative, the array is still searched from back to front. If - * the calculated index is less than 0, -1 is returned, i.e. the - * array will not be searched. - */ - long start; - if (args.length < 2) { - // default - start = length - 1; - } else { - start = (long) ScriptRuntime.toInteger(args[1]); - if (start >= length) start = length - 1; - else if (start < 0) start += length; - if (start < 0) return NEGATIVE_ONE; - } - if (o instanceof NativeArray) { - NativeArray na = (NativeArray) o; - if (na.denseOnly) { - Scriptable proto = na.getPrototype(); - for (int i = (int) start; i >= 0; i--) { - Object val = na.dense[i]; - if (val == NOT_FOUND && proto != null) { - val = ScriptableObject.getProperty(proto, i); - } - if (val != NOT_FOUND && ScriptRuntime.shallowEq(val, compareTo)) { - return Long.valueOf(i); - } - } - return NEGATIVE_ONE; - } - } - for (long i = start; i >= 0; i--) { - Object val = getRawElem(o, i); - if (val != NOT_FOUND && ScriptRuntime.shallowEq(val, compareTo)) { - return Long.valueOf(i); - } - } - return NEGATIVE_ONE; - } - - /* - See ECMA-262 22.1.3.13 - */ - private static Boolean js_includes( - Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - Object compareTo = args.length > 0 ? args[0] : Undefined.instance; - - Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); - long len = ScriptRuntime.toLength(new Object[] {getProperty(thisObj, "length")}, 0); - if (len == 0) return Boolean.FALSE; - - long k; - if (args.length < 2) { - k = 0; - } else { - k = (long) ScriptRuntime.toInteger(args[1]); - if (k < 0) { - k += len; - if (k < 0) k = 0; - } - if (k > len - 1) return Boolean.FALSE; - } - if (o instanceof NativeArray) { - NativeArray na = (NativeArray) o; - if (na.denseOnly) { - Scriptable proto = na.getPrototype(); - for (int i = (int) k; i < len; i++) { - Object elementK = na.dense[i]; - if (elementK == NOT_FOUND && proto != null) { - elementK = ScriptableObject.getProperty(proto, i); - } - if (elementK == NOT_FOUND) { - elementK = Undefined.instance; - } - if (ScriptRuntime.sameZero(elementK, compareTo)) { - return Boolean.TRUE; - } - } - return Boolean.FALSE; - } - } - for (; k < len; k++) { - Object elementK = getRawElem(o, k); - if (elementK == NOT_FOUND) { - elementK = Undefined.instance; - } - if (ScriptRuntime.sameZero(elementK, compareTo)) { - return Boolean.TRUE; - } - } - return Boolean.FALSE; - } - - private static Object js_fill(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); - long len = getLengthProperty(cx, o); - - long relativeStart = 0; - if (args.length >= 2) { - relativeStart = (long) ScriptRuntime.toInteger(args[1]); - } - final long k; - if (relativeStart < 0) { - k = Math.max((len + relativeStart), 0); - } else { - k = Math.min(relativeStart, len); - } - - long relativeEnd = len; - if (args.length >= 3 && !Undefined.isUndefined(args[2])) { - relativeEnd = (long) ScriptRuntime.toInteger(args[2]); - } - final long fin; - if (relativeEnd < 0) { - fin = Math.max((len + relativeEnd), 0); - } else { - fin = Math.min(relativeEnd, len); - } - - Object value = args.length > 0 ? args[0] : Undefined.instance; - for (long i = k; i < fin; i++) { - setRawElem(cx, thisObj, i, value); - } - - return thisObj; - } - - private static Object js_copyWithin( - Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); - long len = getLengthProperty(cx, o); - - Object targetArg = (args.length >= 1) ? args[0] : Undefined.instance; - long relativeTarget = (long) ScriptRuntime.toInteger(targetArg); - long to; - if (relativeTarget < 0) { - to = Math.max((len + relativeTarget), 0); - } else { - to = Math.min(relativeTarget, len); - } - - Object startArg = (args.length >= 2) ? args[1] : Undefined.instance; - long relativeStart = (long) ScriptRuntime.toInteger(startArg); - long from; - if (relativeStart < 0) { - from = Math.max((len + relativeStart), 0); - } else { - from = Math.min(relativeStart, len); - } - - long relativeEnd = len; - if (args.length >= 3 && !Undefined.isUndefined(args[2])) { - relativeEnd = (long) ScriptRuntime.toInteger(args[2]); - } - final long fin; - if (relativeEnd < 0) { - fin = Math.max((len + relativeEnd), 0); - } else { - fin = Math.min(relativeEnd, len); - } - - long count = Math.min(fin - from, len - to); - int direction = 1; - if (from < to && to < from + count) { - direction = -1; - from = from + count - 1; - to = to + count - 1; - } - - // Optimize for a native array. If properties were overridden with setters - // and other non-default options then we won't get here. - if ((o instanceof NativeArray) && (count <= Integer.MAX_VALUE)) { - NativeArray na = (NativeArray) o; - if (na.denseOnly) { - for (; count > 0; count--) { - na.dense[(int) to] = na.dense[(int) from]; - from += direction; - to += direction; - } - - return thisObj; - } - } - - for (; count > 0; count--) { - final Object temp = getRawElem(o, from); - if ((temp == Scriptable.NOT_FOUND) || Undefined.isUndefined(temp)) { - deleteElem(o, to); - } else { - setElem(cx, o, to, temp); - } - - from += direction; - to += direction; - } - - return thisObj; - } - - /** Implements the methods "every", "filter", "forEach", "map", and "some". */ - private static Object iterativeMethod( - Context cx, - IdFunctionObject idFunctionObject, - Scriptable scope, - Scriptable thisObj, - Object[] args) { - Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); - - // execIdCall(..) uses a trick for all the ConstructorId_xxx calls - // they are handled like object calls by adjusting the args list - // as a result we have to handle ConstructorId_xxx calls (negative id) - // the same way and always us the abs value of the id for method selection - int id = Math.abs(idFunctionObject.methodId()); - if (Id_find == id || Id_findIndex == id) { - requireObjectCoercible(cx, o, idFunctionObject); - } - - long length = getLengthProperty(cx, o); - if (id == Id_map && length > Integer.MAX_VALUE) { - String msg = ScriptRuntime.getMessageById("msg.arraylength.bad"); - throw ScriptRuntime.rangeError(msg); - } - - Object callbackArg = args.length > 0 ? args[0] : Undefined.instance; - if (callbackArg == null || !(callbackArg instanceof Function)) { - throw ScriptRuntime.notFunctionError(callbackArg); - } - if (cx.getLanguageVersion() >= Context.VERSION_ES6 - && (callbackArg instanceof NativeRegExp)) { - // Previously, it was allowed to pass RegExp instance as a callback (it implements - // Function) - // But according to ES2015 21.2.6 Properties of RegExp Instances: - // > RegExp instances are ordinary objects that inherit properties from the RegExp - // prototype object. - // > RegExp instances have internal slots [[RegExpMatcher]], [[OriginalSource]], and - // [[OriginalFlags]]. - // so, no [[Call]] for RegExp-s - throw ScriptRuntime.notFunctionError(callbackArg); - } - - Function f = (Function) callbackArg; - Scriptable parent = ScriptableObject.getTopLevelScope(f); - Scriptable thisArg; - if (args.length < 2 || args[1] == null || args[1] == Undefined.instance) { - thisArg = parent; - } else { - thisArg = ScriptRuntime.toObject(cx, scope, args[1]); - } - - Scriptable array = null; - if (id == Id_filter || id == Id_map) { - int resultLength = id == Id_map ? (int) length : 0; - array = cx.newArray(scope, resultLength); - } - long j = 0; - for (long i = 0; i < length; i++) { - Object[] innerArgs = new Object[3]; - Object elem = getRawElem(o, i); - if (elem == Scriptable.NOT_FOUND) { - if (id == Id_find || id == Id_findIndex) { - elem = Undefined.instance; - } else { - continue; - } - } - innerArgs[0] = elem; - innerArgs[1] = Long.valueOf(i); - innerArgs[2] = o; - Object result = f.call(cx, parent, thisArg, innerArgs); - switch (id) { - case Id_every: - if (!ScriptRuntime.toBoolean(result)) return Boolean.FALSE; - break; - case Id_filter: - if (ScriptRuntime.toBoolean(result)) defineElem(cx, array, j++, innerArgs[0]); - break; - case Id_forEach: - break; - case Id_map: - defineElem(cx, array, i, result); - break; - case Id_some: - if (ScriptRuntime.toBoolean(result)) return Boolean.TRUE; - break; - case Id_find: - if (ScriptRuntime.toBoolean(result)) return elem; - break; - case Id_findIndex: - if (ScriptRuntime.toBoolean(result)) return ScriptRuntime.wrapNumber(i); - break; - } - } - switch (id) { - case Id_every: - return Boolean.TRUE; - case Id_filter: - case Id_map: - return array; - case Id_some: - return Boolean.FALSE; - case Id_findIndex: - return ScriptRuntime.wrapNumber(-1); - case Id_forEach: - default: - return Undefined.instance; - } - } - - /** Implements the methods "reduce" and "reduceRight". */ - private static Object reduceMethod( - Context cx, int id, Scriptable scope, Scriptable thisObj, Object[] args) { - Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); - - long length = getLengthProperty(cx, o); - Object callbackArg = args.length > 0 ? args[0] : Undefined.instance; - if (callbackArg == null || !(callbackArg instanceof Function)) { - throw ScriptRuntime.notFunctionError(callbackArg); - } - Function f = (Function) callbackArg; - Scriptable parent = ScriptableObject.getTopLevelScope(f); - // hack to serve both reduce and reduceRight with the same loop - boolean movingLeft = id == Id_reduce; - Object value = args.length > 1 ? args[1] : Scriptable.NOT_FOUND; - for (long i = 0; i < length; i++) { - long index = movingLeft ? i : (length - 1 - i); - Object elem = getRawElem(o, index); - if (elem == Scriptable.NOT_FOUND) { - continue; - } - if (value == Scriptable.NOT_FOUND) { - // no initial value passed, use first element found as inital value - value = elem; - } else { - Object[] innerArgs = {value, elem, Long.valueOf(index), o}; - value = f.call(cx, parent, parent, innerArgs); - } - } - if (value == Scriptable.NOT_FOUND) { - // reproduce spidermonkey error message - throw ScriptRuntime.typeErrorById("msg.empty.array.reduce"); - } - return value; - } - - private static boolean js_isArray(Object o) { - if (!(o instanceof Scriptable)) { - return false; - } - return "Array".equals(((Scriptable) o).getClassName()); - } - - // methods to implement java.util.List - - @Override - public boolean contains(Object o) { - return indexOf(o) > -1; - } - - @Override - public Object[] toArray() { - return toArray(ScriptRuntime.emptyArgs); - } - - @Override - public Object[] toArray(Object[] a) { - int len = size(); - Object[] array = - a.length >= len - ? a - : (Object[]) - java.lang.reflect.Array.newInstance( - a.getClass().getComponentType(), len); - for (int i = 0; i < len; i++) { - array[i] = get(i); - } - return array; - } - - @Override - public boolean containsAll(Collection c) { - for (Object aC : c) if (!contains(aC)) return false; - return true; - } - - @Override - public int size() { - long longLen = length; - if (longLen > Integer.MAX_VALUE) { - throw new IllegalStateException( - "list.length (" + length + ") exceeds Integer.MAX_VALUE"); - } - return (int) longLen; - } - - @Override - public boolean isEmpty() { - return length == 0; - } - - public Object get(long index) { - if (index < 0 || index >= length) { - throw new IndexOutOfBoundsException(); - } - Object value = getRawElem(this, index); - if (value == Scriptable.NOT_FOUND || value == Undefined.instance) { - return null; - } else if (value instanceof Wrapper) { - return ((Wrapper) value).unwrap(); - } else { - return value; - } - } - - @Override - public Object get(int index) { - return get((long) index); - } - - @Override - public int indexOf(Object o) { - int len = size(); - if (o == null) { - for (int i = 0; i < len; i++) { - if (get(i) == null) { - return i; - } - } - } else { - for (int i = 0; i < len; i++) { - if (o.equals(get(i))) { - return i; - } - } - } - return -1; - } - - @Override - public int lastIndexOf(Object o) { - int len = size(); - if (o == null) { - for (int i = len - 1; i >= 0; i--) { - if (get(i) == null) { - return i; - } - } - } else { - for (int i = len - 1; i >= 0; i--) { - if (o.equals(get(i))) { - return i; - } - } - } - return -1; - } - - @Override - public Iterator iterator() { - return listIterator(0); - } - - @Override - public ListIterator listIterator() { - return listIterator(0); - } - - @Override - public ListIterator listIterator(final int start) { - final int len = size(); - - if (start < 0 || start > len) { - throw new IndexOutOfBoundsException("Index: " + start); - } - - return new ListIterator() { - - int cursor = start; - int modCount = NativeArray.this.modCount; - - @Override - public boolean hasNext() { - return cursor < len; - } - - @Override - public Object next() { - checkModCount(modCount); - if (cursor == len) { - throw new NoSuchElementException(); - } - return get(cursor++); - } - - @Override - public boolean hasPrevious() { - return cursor > 0; - } - - @Override - public Object previous() { - checkModCount(modCount); - if (cursor == 0) { - throw new NoSuchElementException(); - } - return get(--cursor); - } - - @Override - public int nextIndex() { - return cursor; - } - - @Override - public int previousIndex() { - return cursor - 1; - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - - @Override - public void add(Object o) { - throw new UnsupportedOperationException(); - } - - @Override - public void set(Object o) { - throw new UnsupportedOperationException(); - } - }; - } - - @Override - public boolean add(Object o) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean remove(Object o) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean addAll(Collection c) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean removeAll(Collection c) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean retainAll(Collection c) { - throw new UnsupportedOperationException(); - } - - @Override - public void clear() { - throw new UnsupportedOperationException(); - } - - @Override - public void add(int index, Object element) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean addAll(int index, Collection c) { - throw new UnsupportedOperationException(); - } - - @Override - public Object set(int index, Object element) { - throw new UnsupportedOperationException(); - } - - @Override - public Object remove(int index) { - throw new UnsupportedOperationException(); - } - - @Override - public List subList(int fromIndex, int toIndex) { - if (fromIndex < 0) throw new IndexOutOfBoundsException("fromIndex = " + fromIndex); - if (toIndex > size()) throw new IndexOutOfBoundsException("toIndex = " + toIndex); - if (fromIndex > toIndex) - throw new IllegalArgumentException( - "fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")"); - - return new AbstractList() { - private int modCount = NativeArray.this.modCount; - - @Override - public Object get(int index) { - checkModCount(modCount); - return NativeArray.this.get(index + fromIndex); - } - - @Override - public int size() { - checkModCount(modCount); - return toIndex - fromIndex; - } - }; - } - - private void checkModCount(int modCount) { - if (this.modCount != modCount) { - throw new ConcurrentModificationException(); - } - } - - @Override - protected int findPrototypeId(Symbol k) { - if (SymbolKey.ITERATOR.equals(k)) { - return SymbolId_iterator; - } - return 0; - } - - // Comparators for the js_sort method. Putting them here lets us unit-test them better. - - private static final Comparator STRING_COMPARATOR = new StringLikeComparator(); - private static final Comparator DEFAULT_COMPARATOR = new ElementComparator(); - - public static final class StringLikeComparator implements Comparator, Serializable { - - private static final long serialVersionUID = 5299017659728190979L; - - @Override - public int compare(final Object x, final Object y) { - final String a = ScriptRuntime.toString(x); - final String b = ScriptRuntime.toString(y); - return a.compareTo(b); - } - } - - public static final class ElementComparator implements Comparator, Serializable { - - private static final long serialVersionUID = -1189948017688708858L; - - private final Comparator child; - - public ElementComparator() { - child = STRING_COMPARATOR; - } - - public ElementComparator(Comparator c) { - child = c; - } - - @Override - public int compare(final Object x, final Object y) { - // Sort NOT_FOUND to very end, Undefined before that, exclusively, as per - // ECMA 22.1.3.25.1. - if (x == Undefined.instance) { - if (y == Undefined.instance) { - return 0; - } - if (y == NOT_FOUND) { - return -1; - } - return 1; - } else if (x == NOT_FOUND) { - return y == NOT_FOUND ? 0 : 1; - } - - if (y == NOT_FOUND) { - return -1; - } - if (y == Undefined.instance) { - return -1; - } - - return child.compare(x, y); - } - } - - @Override - protected int findPrototypeId(String s) { - int id; - switch (s) { - case "constructor": - id = Id_constructor; - break; - case "toString": - id = Id_toString; - break; - case "toLocaleString": - id = Id_toLocaleString; - break; - case "toSource": - id = Id_toSource; - break; - case "join": - id = Id_join; - break; - case "reverse": - id = Id_reverse; - break; - case "sort": - id = Id_sort; - break; - case "push": - id = Id_push; - break; - case "pop": - id = Id_pop; - break; - case "shift": - id = Id_shift; - break; - case "unshift": - id = Id_unshift; - break; - case "splice": - id = Id_splice; - break; - case "concat": - id = Id_concat; - break; - case "slice": - id = Id_slice; - break; - case "indexOf": - id = Id_indexOf; - break; - case "lastIndexOf": - id = Id_lastIndexOf; - break; - case "every": - id = Id_every; - break; - case "filter": - id = Id_filter; - break; - case "forEach": - id = Id_forEach; - break; - case "map": - id = Id_map; - break; - case "some": - id = Id_some; - break; - case "find": - id = Id_find; - break; - case "findIndex": - id = Id_findIndex; - break; - case "reduce": - id = Id_reduce; - break; - case "reduceRight": - id = Id_reduceRight; - break; - case "fill": - id = Id_fill; - break; - case "keys": - id = Id_keys; - break; - case "values": - id = Id_values; - break; - case "entries": - id = Id_entries; - break; - case "includes": - id = Id_includes; - break; - case "copyWithin": - id = Id_copyWithin; - break; - default: - id = 0; - break; - } - return id; - } - - private static final int Id_constructor = 1, - Id_toString = 2, - Id_toLocaleString = 3, - Id_toSource = 4, - Id_join = 5, - Id_reverse = 6, - Id_sort = 7, - Id_push = 8, - Id_pop = 9, - Id_shift = 10, - Id_unshift = 11, - Id_splice = 12, - Id_concat = 13, - Id_slice = 14, - Id_indexOf = 15, - Id_lastIndexOf = 16, - Id_every = 17, - Id_filter = 18, - Id_forEach = 19, - Id_map = 20, - Id_some = 21, - Id_find = 22, - Id_findIndex = 23, - Id_reduce = 24, - Id_reduceRight = 25, - Id_fill = 26, - Id_keys = 27, - Id_values = 28, - Id_entries = 29, - Id_includes = 30, - Id_copyWithin = 31, - SymbolId_iterator = 32, - MAX_PROTOTYPE_ID = SymbolId_iterator; - private static final int ConstructorId_join = -Id_join, - ConstructorId_reverse = -Id_reverse, - ConstructorId_sort = -Id_sort, - ConstructorId_push = -Id_push, - ConstructorId_pop = -Id_pop, - ConstructorId_shift = -Id_shift, - ConstructorId_unshift = -Id_unshift, - ConstructorId_splice = -Id_splice, - ConstructorId_concat = -Id_concat, - ConstructorId_slice = -Id_slice, - ConstructorId_indexOf = -Id_indexOf, - ConstructorId_lastIndexOf = -Id_lastIndexOf, - ConstructorId_every = -Id_every, - ConstructorId_filter = -Id_filter, - ConstructorId_forEach = -Id_forEach, - ConstructorId_map = -Id_map, - ConstructorId_some = -Id_some, - ConstructorId_find = -Id_find, - ConstructorId_findIndex = -Id_findIndex, - ConstructorId_reduce = -Id_reduce, - ConstructorId_reduceRight = -Id_reduceRight, - ConstructorId_isArray = -26, - ConstructorId_of = -27, - ConstructorId_from = -28; - - /** Internal representation of the JavaScript array's length property. */ - private long length; - - /** Attributes of the array's length property */ - private int lengthAttr = DONTENUM | PERMANENT; - - /** modCount required for subList/iterators */ - private transient int modCount; - - /** - * Fast storage for dense arrays. Sparse arrays will use the superclass's hashtable storage - * scheme. - */ - private Object[] dense; - - /** True if all numeric properties are stored in dense. */ - private boolean denseOnly; - - /** The maximum size of dense that will be allocated initially. */ - private static int maximumInitialCapacity = 10000; - - /** The default capacity for dense. */ - private static final int DEFAULT_INITIAL_CAPACITY = 10; - - /** The factor to grow dense by. */ - private static final double GROW_FACTOR = 1.5; - - private static final int MAX_PRE_GROW_SIZE = (int) (Integer.MAX_VALUE / GROW_FACTOR); -} diff --git a/src/main/java/org/mozilla/javascript/NativeArrayIterator.java b/src/main/java/org/mozilla/javascript/NativeArrayIterator.java deleted file mode 100644 index 08e6645b..00000000 --- a/src/main/java/org/mozilla/javascript/NativeArrayIterator.java +++ /dev/null @@ -1,76 +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; - -public final class NativeArrayIterator extends ES6Iterator { - public enum ARRAY_ITERATOR_TYPE { - ENTRIES, - KEYS, - VALUES - } - - private static final long serialVersionUID = 1L; - private static final String ITERATOR_TAG = "ArrayIterator"; - - private ARRAY_ITERATOR_TYPE type; - - static void init(ScriptableObject scope, boolean sealed) { - ES6Iterator.init(scope, sealed, new NativeArrayIterator(), ITERATOR_TAG); - } - - /** - * Only for constructing the prototype object. - */ - private NativeArrayIterator() { - super(); - } - - public NativeArrayIterator(Scriptable scope, Scriptable arrayLike, ARRAY_ITERATOR_TYPE type) { - super(scope, ITERATOR_TAG); - this.index = 0; - this.arrayLike = arrayLike; - this.type = type; - } - - @Override - public String getClassName() { - return "Array Iterator"; - } - - @Override - protected boolean isDone(Context cx, Scriptable scope) { - return index >= NativeArray.getLengthProperty(cx, arrayLike); - } - - @Override - protected Object nextValue(Context cx, Scriptable scope) { - if (type == ARRAY_ITERATOR_TYPE.KEYS) { - return Integer.valueOf(index++); - } - - Object value = arrayLike.get(index, arrayLike); - if (value == Scriptable.NOT_FOUND) { - value = Undefined.instance; - } - - if (type == ARRAY_ITERATOR_TYPE.ENTRIES) { - value = cx.newArray(scope, new Object[] {Integer.valueOf(index), value}); - } - - index++; - return value; - } - - @Override - protected String getTag() { - return ITERATOR_TAG; - } - - private Scriptable arrayLike; - private int index; -} - diff --git a/src/main/java/org/mozilla/javascript/NativeBigInt.java b/src/main/java/org/mozilla/javascript/NativeBigInt.java deleted file mode 100644 index 139ba26c..00000000 --- a/src/main/java/org/mozilla/javascript/NativeBigInt.java +++ /dev/null @@ -1,223 +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.Arrays; - -/** This class implements the BigInt native object. */ -final class NativeBigInt extends IdScriptableObject { - private static final long serialVersionUID = 1335609231306775449L; - - private static final Object BIG_INT_TAG = "BigInt"; - - static void init(Scriptable scope, boolean sealed) { - NativeBigInt obj = new NativeBigInt(BigInteger.ZERO); - obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); - } - - NativeBigInt(BigInteger bigInt) { - bigIntValue = bigInt; - } - - @Override - public String getClassName() { - return "BigInt"; - } - - @Override - protected void fillConstructorProperties(IdFunctionObject ctor) { - addIdFunctionProperty(ctor, BIG_INT_TAG, ConstructorId_asIntN, "asIntN", 2); - addIdFunctionProperty(ctor, BIG_INT_TAG, ConstructorId_asUintN, "asUintN", 2); - - super.fillConstructorProperties(ctor); - } - - @Override - protected void initPrototypeId(int id) { - if (id == SymbolId_toStringTag) { - initPrototypeValue( - SymbolId_toStringTag, - SymbolKey.TO_STRING_TAG, - getClassName(), - DONTENUM | READONLY); - return; - } - - String s; - int arity; - switch (id) { - case Id_constructor: - arity = 1; - s = "constructor"; - break; - case Id_toString: - arity = 0; - s = "toString"; - break; - case Id_toLocaleString: - arity = 0; - s = "toLocaleString"; - break; - case Id_toSource: - arity = 0; - s = "toSource"; - break; - case Id_valueOf: - arity = 0; - s = "valueOf"; - break; - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - initPrototypeMethod(BIG_INT_TAG, id, s, arity); - } - - @Override - public Object execIdCall( - IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!f.hasTag(BIG_INT_TAG)) { - return super.execIdCall(f, cx, scope, thisObj, args); - } - int id = f.methodId(); - if (id == Id_constructor) { - if (thisObj == null) { - // new BigInt(val) throws TypeError. - throw ScriptRuntime.typeErrorById("msg.not.ctor", BIG_INT_TAG); - } - BigInteger val = (args.length >= 1) ? ScriptRuntime.toBigInt(args[0]) : BigInteger.ZERO; - // BigInt(val) converts val to a BigInteger value. - return val; - - } else if (id < Id_constructor) { - return execConstructorCall(id, args); - } - - // The rest of BigInt.prototype methods require thisObj to be BigInt - BigInteger value = ensureType(thisObj, NativeBigInt.class, f).bigIntValue; - - switch (id) { - case Id_toString: - case Id_toLocaleString: - { - // toLocaleString is just an alias for toString for now - int base = - (args.length == 0 || args[0] == Undefined.instance) - ? 10 - : ScriptRuntime.toInt32(args[0]); - return ScriptRuntime.bigIntToString(value, base); - } - - case Id_toSource: - return "(new BigInt(" + ScriptRuntime.toString(value) + "))"; - - case Id_valueOf: - return value; - - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - } - - private static Object execConstructorCall(int id, Object[] args) { - switch (id) { - case ConstructorId_asIntN: - case ConstructorId_asUintN: - { - int bits = - ScriptRuntime.toIndex(args.length < 1 ? Undefined.instance : args[0]); - BigInteger bigInt = - ScriptRuntime.toBigInt(args.length < 2 ? Undefined.instance : args[1]); - - if (bits == 0) { - return BigInteger.ZERO; - } - - byte[] bytes = bigInt.toByteArray(); - - int newBytesLen = (bits / Byte.SIZE) + 1; - if (newBytesLen > bytes.length) { - return bigInt; - } - - byte[] newBytes = - Arrays.copyOfRange(bytes, bytes.length - newBytesLen, bytes.length); - - int mod = bits % Byte.SIZE; - switch (id) { - case ConstructorId_asIntN: - if (mod == 0) { - newBytes[0] = newBytes[1] < 0 ? (byte) -1 : 0; - } else if ((newBytes[0] & (1 << (mod - 1))) != 0) { - newBytes[0] |= -1 << mod; - } else { - newBytes[0] &= (1 << mod) - 1; - } - break; - case ConstructorId_asUintN: - newBytes[0] &= (1 << mod) - 1; - break; - } - return new BigInteger(newBytes); - } - - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - } - - @Override - public String toString() { - return ScriptRuntime.bigIntToString(bigIntValue, 10); - } - - @Override - protected int findPrototypeId(Symbol k) { - if (SymbolKey.TO_STRING_TAG.equals(k)) { - return SymbolId_toStringTag; - } - return 0; - } - - @Override - protected int findPrototypeId(String s) { - int id; - switch (s) { - case "constructor": - id = Id_constructor; - break; - case "toString": - id = Id_toString; - break; - case "toLocaleString": - id = Id_toLocaleString; - break; - case "toSource": - id = Id_toSource; - break; - case "valueOf": - id = Id_valueOf; - break; - default: - id = 0; - break; - } - return id; - } - - private static final int ConstructorId_asIntN = -1, - ConstructorId_asUintN = -2, - Id_constructor = 1, - Id_toString = 2, - Id_toLocaleString = 3, - Id_toSource = 4, - Id_valueOf = 5, - SymbolId_toStringTag = 6, - MAX_PROTOTYPE_ID = SymbolId_toStringTag; - - private BigInteger bigIntValue; -} diff --git a/src/main/java/org/mozilla/javascript/NativeBoolean.java b/src/main/java/org/mozilla/javascript/NativeBoolean.java deleted file mode 100644 index 1f11751c..00000000 --- a/src/main/java/org/mozilla/javascript/NativeBoolean.java +++ /dev/null @@ -1,146 +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; - -/** - * This class implements the Boolean native object. See ECMA 15.6. - * - * @author Norris Boyd - */ -final class NativeBoolean extends IdScriptableObject { - private static final long serialVersionUID = -3716996899943880933L; - - private static final Object BOOLEAN_TAG = "Boolean"; - - static void init(Scriptable scope, boolean sealed) { - NativeBoolean obj = new NativeBoolean(false); - obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); - } - - NativeBoolean(boolean b) { - booleanValue = b; - } - - @Override - public String getClassName() { - return "Boolean"; - } - - @Override - public Object getDefaultValue(Class typeHint) { - // This is actually non-ECMA, but will be proposed - // as a change in round 2. - if (typeHint == ScriptRuntime.BooleanClass) return ScriptRuntime.wrapBoolean(booleanValue); - return super.getDefaultValue(typeHint); - } - - @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 = 0; - s = "toSource"; - break; - case Id_valueOf: - arity = 0; - s = "valueOf"; - break; - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - initPrototypeMethod(BOOLEAN_TAG, id, s, arity); - } - - @Override - public Object execIdCall( - IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!f.hasTag(BOOLEAN_TAG)) { - return super.execIdCall(f, cx, scope, thisObj, args); - } - int id = f.methodId(); - - if (id == Id_constructor) { - boolean b; - if (args.length == 0) { - b = false; - } else { - // see special handling in ScriptRuntime.toBoolean(Object) - // avoidObjectDetection() is used to implement document.all - // see Note on page - // - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean - b = - args[0] instanceof ScriptableObject - && ((ScriptableObject) args[0]).avoidObjectDetection() - ? false - : ScriptRuntime.toBoolean(args[0]); - } - if (thisObj == null) { - // new Boolean(val) creates a new boolean object. - return new NativeBoolean(b); - } - // Boolean(val) converts val to a boolean. - return ScriptRuntime.wrapBoolean(b); - } - - // The rest of Boolean.prototype methods require thisObj to be Boolean - boolean value = ensureType(thisObj, NativeBoolean.class, f).booleanValue; - - switch (id) { - case Id_toString: - return value ? "true" : "false"; - - case Id_toSource: - return value ? "(new Boolean(true))" : "(new Boolean(false))"; - - case Id_valueOf: - return ScriptRuntime.wrapBoolean(value); - } - throw new IllegalArgumentException(String.valueOf(id)); - } - - @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 "valueOf": - id = Id_valueOf; - break; - default: - id = 0; - break; - } - return id; - } - - private static final int Id_constructor = 1, - Id_toString = 2, - Id_toSource = 3, - Id_valueOf = 4, - MAX_PROTOTYPE_ID = 4; - - private boolean booleanValue; -} diff --git a/src/main/java/org/mozilla/javascript/NativeCall.java b/src/main/java/org/mozilla/javascript/NativeCall.java deleted file mode 100644 index fb237e4e..00000000 --- a/src/main/java/org/mozilla/javascript/NativeCall.java +++ /dev/null @@ -1,137 +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; - -/** - * This class implements the activation object. - * - * See ECMA 10.1.6 - * - * @see org.mozilla.javascript.Arguments - * @author Norris Boyd - */ -public final class NativeCall extends IdScriptableObject -{ - private static final long serialVersionUID = -7471457301304454454L; - - private static final Object CALL_TAG = "Call"; - - static void init(Scriptable scope, boolean sealed) - { - NativeCall obj = new NativeCall(); - obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); - } - - NativeCall() { } - - NativeCall(NativeFunction function, Scriptable scope, Object[] args, boolean isArrow, boolean isStrict) - { - this.function = function; - - setParentScope(scope); - // leave prototype null - - this.originalArgs = (args == null) ? ScriptRuntime.emptyArgs : args; - this.isStrict = isStrict; - - // initialize values of arguments - int paramAndVarCount = function.getParamAndVarCount(); - int paramCount = function.getParamCount(); - if (paramAndVarCount != 0) { - for (int i = 0; i < paramCount; ++i) { - String name = function.getParamOrVarName(i); - Object val = i < args.length ? args[i] - : Undefined.instance; - defineProperty(name, val, PERMANENT); - } - } - - // initialize "arguments" property but only if it was not overridden by - // the parameter with the same name - if (!super.has("arguments", this) && !isArrow) { - arguments = new Arguments(this); - defineProperty("arguments", arguments, PERMANENT); - } - - if (paramAndVarCount != 0) { - for (int i = paramCount; i < paramAndVarCount; ++i) { - String name = function.getParamOrVarName(i); - if (!super.has(name, this)) { - if (function.getParamOrVarConst(i)) { - defineProperty(name, Undefined.instance, CONST); - } else if (!(function instanceof InterpretedFunction) - || ((InterpretedFunction) function).hasFunctionNamed(name)) { - defineProperty(name, Undefined.instance, PERMANENT); - } - } - } - } - } - - @Override - public String getClassName() - { - return "Call"; - } - - @Override - protected int findPrototypeId(String s) - { - return s.equals("constructor") ? Id_constructor : 0; - } - - @Override - protected void initPrototypeId(int id) - { - String s; - int arity; - if (id == Id_constructor) { - arity=1; s="constructor"; - } else { - throw new IllegalArgumentException(String.valueOf(id)); - } - initPrototypeMethod(CALL_TAG, id, s, arity); - } - - @Override - public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) - { - if (!f.hasTag(CALL_TAG)) { - return super.execIdCall(f, cx, scope, thisObj, args); - } - int id = f.methodId(); - if (id == Id_constructor) { - if (thisObj != null) { - throw Context.reportRuntimeErrorById("msg.only.from.new", "Call"); - } - ScriptRuntime.checkDeprecated(cx, "Call"); - NativeCall result = new NativeCall(); - result.setPrototype(getObjectPrototype(scope)); - return result; - } - throw new IllegalArgumentException(String.valueOf(id)); - } - - public void defineAttributesForArguments() { - if (arguments != null) { - arguments.defineAttributesForStrictMode(); - } - } - - private static final int - Id_constructor = 1, - MAX_PROTOTYPE_ID = 1; - - NativeFunction function; - Object[] originalArgs; - boolean isStrict; - private Arguments arguments; - - transient NativeCall parentActivationCall; -} - diff --git a/src/main/java/org/mozilla/javascript/NativeCallSite.java b/src/main/java/org/mozilla/javascript/NativeCallSite.java deleted file mode 100644 index 1647e13d..00000000 --- a/src/main/java/org/mozilla/javascript/NativeCallSite.java +++ /dev/null @@ -1,278 +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; - -/** - * This class is used by the V8 extension "Error.prepareStackTrace." It is passed to that function, - * which may then use it to format the stack as it sees fit. - */ -public class NativeCallSite extends IdScriptableObject { - private static final long serialVersionUID = 2688372752566593594L; - private static final String CALLSITE_TAG = "CallSite"; - private ScriptStackElement element; - - static void init(Scriptable scope, boolean sealed) { - NativeCallSite cs = new NativeCallSite(); - cs.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); - } - - static NativeCallSite make(Scriptable scope, Scriptable ctorObj) { - NativeCallSite cs = new NativeCallSite(); - Scriptable proto = (Scriptable) (ctorObj.get("prototype", ctorObj)); - cs.setParentScope(scope); - cs.setPrototype(proto); - return cs; - } - - private NativeCallSite() {} - - void setElement(ScriptStackElement elt) { - this.element = elt; - } - - @Override - public String getClassName() { - return "CallSite"; - } - - @Override - protected void initPrototypeId(int id) { - String s; - int arity; - switch (id) { - case Id_constructor: - arity = 0; - s = "constructor"; - break; - case Id_getThis: - arity = 0; - s = "getThis"; - break; - case Id_getTypeName: - arity = 0; - s = "getTypeName"; - break; - case Id_getFunction: - arity = 0; - s = "getFunction"; - break; - case Id_getFunctionName: - arity = 0; - s = "getFunctionName"; - break; - case Id_getMethodName: - arity = 0; - s = "getMethodName"; - break; - case Id_getFileName: - arity = 0; - s = "getFileName"; - break; - case Id_getLineNumber: - arity = 0; - s = "getLineNumber"; - break; - case Id_getColumnNumber: - arity = 0; - s = "getColumnNumber"; - break; - case Id_getEvalOrigin: - arity = 0; - s = "getEvalOrigin"; - break; - case Id_isToplevel: - arity = 0; - s = "isToplevel"; - break; - case Id_isEval: - arity = 0; - s = "isEval"; - break; - case Id_isNative: - arity = 0; - s = "isNative"; - break; - case Id_isConstructor: - arity = 0; - s = "isConstructor"; - break; - case Id_toString: - arity = 0; - s = "toString"; - break; - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - initPrototypeMethod(CALLSITE_TAG, id, s, arity); - } - - @Override - public Object execIdCall( - IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!f.hasTag(CALLSITE_TAG)) { - return super.execIdCall(f, cx, scope, thisObj, args); - } - int id = f.methodId(); - switch (id) { - case Id_constructor: - return make(scope, f); - case Id_getFunctionName: - return getFunctionName(thisObj); - case Id_getFileName: - return getFileName(thisObj); - case Id_getLineNumber: - return getLineNumber(thisObj); - case Id_getThis: - case Id_getTypeName: - case Id_getFunction: - case Id_getColumnNumber: - return Undefined.instance; - case Id_getMethodName: - return null; - case Id_getEvalOrigin: - case Id_isEval: - case Id_isConstructor: - case Id_isNative: - case Id_isToplevel: - return Boolean.FALSE; - case Id_toString: - return js_toString(thisObj); - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - } - - @Override - public String toString() { - if (element == null) { - return ""; - } - return element.toString(); - } - - private static Object js_toString(Scriptable obj) { - while (obj != null && !(obj instanceof NativeCallSite)) { - obj = obj.getPrototype(); - } - if (obj == null) { - return NOT_FOUND; - } - NativeCallSite cs = (NativeCallSite) obj; - StringBuilder sb = new StringBuilder(); - cs.element.renderJavaStyle(sb); - return sb.toString(); - } - - private static Object getFunctionName(Scriptable obj) { - while (obj != null && !(obj instanceof NativeCallSite)) { - obj = obj.getPrototype(); - } - if (obj == null) { - return NOT_FOUND; - } - NativeCallSite cs = (NativeCallSite) obj; - return (cs.element == null ? null : cs.element.functionName); - } - - private static Object getFileName(Scriptable obj) { - while (obj != null && !(obj instanceof NativeCallSite)) { - obj = obj.getPrototype(); - } - if (obj == null) { - return NOT_FOUND; - } - NativeCallSite cs = (NativeCallSite) obj; - return (cs.element == null ? null : cs.element.fileName); - } - - private static Object getLineNumber(Scriptable obj) { - while (obj != null && !(obj instanceof NativeCallSite)) { - obj = obj.getPrototype(); - } - if (obj == null) { - return NOT_FOUND; - } - NativeCallSite cs = (NativeCallSite) obj; - if ((cs.element == null) || (cs.element.lineNumber < 0)) { - return Undefined.instance; - } - return Integer.valueOf(cs.element.lineNumber); - } - - @Override - protected int findPrototypeId(String s) { - int id; - switch (s) { - case "constructor": - id = Id_constructor; - break; - case "getThis": - id = Id_getThis; - break; - case "getTypeName": - id = Id_getTypeName; - break; - case "getFunction": - id = Id_getFunction; - break; - case "getFunctionName": - id = Id_getFunctionName; - break; - case "getMethodName": - id = Id_getMethodName; - break; - case "getFileName": - id = Id_getFileName; - break; - case "getLineNumber": - id = Id_getLineNumber; - break; - case "getColumnNumber": - id = Id_getColumnNumber; - break; - case "getEvalOrigin": - id = Id_getEvalOrigin; - break; - case "isToplevel": - id = Id_isToplevel; - break; - case "isEval": - id = Id_isEval; - break; - case "isNative": - id = Id_isNative; - break; - case "isConstructor": - id = Id_isConstructor; - break; - case "toString": - id = Id_toString; - break; - default: - id = 0; - break; - } - return id; - } - - private static final int Id_constructor = 1, - Id_getThis = 2, - Id_getTypeName = 3, - Id_getFunction = 4, - Id_getFunctionName = 5, - Id_getMethodName = 6, - Id_getFileName = 7, - Id_getLineNumber = 8, - Id_getColumnNumber = 9, - Id_getEvalOrigin = 10, - Id_isToplevel = 11, - Id_isEval = 12, - Id_isNative = 13, - Id_isConstructor = 14, - Id_toString = 15, - MAX_PROTOTYPE_ID = 15; -} diff --git a/src/main/java/org/mozilla/javascript/NativeCollectionIterator.java b/src/main/java/org/mozilla/javascript/NativeCollectionIterator.java deleted file mode 100644 index 96724bd2..00000000 --- a/src/main/java/org/mozilla/javascript/NativeCollectionIterator.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.mozilla.javascript; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.Collections; -import java.util.Iterator; - -public class NativeCollectionIterator extends ES6Iterator { - private static final long serialVersionUID = 7094840979404373443L; - private String className; - private Type type; - private transient Iterator iterator = Collections.emptyIterator(); - enum Type { KEYS, VALUES, BOTH } - - static void init(ScriptableObject scope, String tag, boolean sealed) { - ES6Iterator.init(scope, sealed, new NativeCollectionIterator(tag), tag); - } - - public NativeCollectionIterator(String tag) - { - this.className = tag; - this.iterator = Collections.emptyIterator(); - this.type = Type.BOTH; - } - - public NativeCollectionIterator( - Scriptable scope, String className, - Type type, Iterator iterator) - { - super(scope, className); - this.className = className; - this.iterator = iterator; - this.type = type; - } - - @Override - public String getClassName() { - return className; - } - - @Override - protected boolean isDone(Context cx, Scriptable scope) { - return !iterator.hasNext(); - } - - @Override - protected Object nextValue(Context cx, Scriptable scope) { - final Hashtable.Entry e = iterator.next(); - switch (type) { - case KEYS: - return e.key; - case VALUES: - return e.value; - case BOTH: - return cx.newArray(scope, new Object[] { e.key, e.value }); - default: - throw new AssertionError(); - } - } - - private void readObject(ObjectInputStream stream) - throws IOException, ClassNotFoundException - { - stream.defaultReadObject(); - className = (String) stream.readObject(); - type = (Type) stream.readObject(); - iterator = Collections.emptyIterator(); - } - - private void writeObject(ObjectOutputStream stream) - throws IOException - { - stream.defaultWriteObject(); - stream.writeObject(className); - stream.writeObject(type); - } -} diff --git a/src/main/java/org/mozilla/javascript/NativeContinuation.java b/src/main/java/org/mozilla/javascript/NativeContinuation.java deleted file mode 100644 index 0a640043..00000000 --- a/src/main/java/org/mozilla/javascript/NativeContinuation.java +++ /dev/null @@ -1,109 +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.Objects; - -public final class NativeContinuation extends IdScriptableObject implements Function { - private static final long serialVersionUID = 1794167133757605367L; - - private static final Object FTAG = "Continuation"; - - private Object implementation; - - public static void init(Context cx, Scriptable scope, boolean sealed) { - NativeContinuation obj = new NativeContinuation(); - obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); - } - - public Object getImplementation() { - return implementation; - } - - public void initImplementation(Object implementation) { - this.implementation = implementation; - } - - @Override - public String getClassName() { - return "Continuation"; - } - - @Override - public Scriptable construct(Context cx, Scriptable scope, Object[] args) { - throw Context.reportRuntimeError("Direct call is not supported"); - } - - @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - return Interpreter.restartContinuation(this, cx, scope, args); - } - - public static boolean isContinuationConstructor(IdFunctionObject f) { - if (f.hasTag(FTAG) && f.methodId() == Id_constructor) { - return true; - } - return false; - } - - /** - * Returns true if both continuations have equal implementations. - * - * @param c1 one continuation - * @param c2 another continuation - * @return true if the implementations of both continuations are equal, or they are both null. - * @throws NullPointerException if either continuation is null - */ - public static boolean equalImplementations(NativeContinuation c1, NativeContinuation c2) { - return Objects.equals(c1.implementation, c2.implementation); - } - - @Override - protected void initPrototypeId(int id) { - String s; - int arity; - switch (id) { - case Id_constructor: - arity = 0; - s = "constructor"; - break; - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - initPrototypeMethod(FTAG, id, s, arity); - } - - @Override - public Object execIdCall( - IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!f.hasTag(FTAG)) { - return super.execIdCall(f, cx, scope, thisObj, args); - } - int id = f.methodId(); - switch (id) { - case Id_constructor: - throw Context.reportRuntimeError("Direct call is not supported"); - } - throw new IllegalArgumentException(String.valueOf(id)); - } - - @Override - protected int findPrototypeId(String s) { - int id; - switch (s) { - case "constructor": - id = Id_constructor; - break; - default: - id = 0; - break; - } - return id; - } - - private static final int Id_constructor = 1, MAX_PROTOTYPE_ID = 1; -} diff --git a/src/main/java/org/mozilla/javascript/NativeDate.java b/src/main/java/org/mozilla/javascript/NativeDate.java deleted file mode 100644 index bde9a615..00000000 --- a/src/main/java/org/mozilla/javascript/NativeDate.java +++ /dev/null @@ -1,1920 +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.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.TimeZone; - -/** - * This class implements the Date native object. See ECMA 15.9. - * - * @author Mike McCabe - *

Significant parts of this code are adapted from the venerable jsdate.cpp (also Mozilla): - * https://dxr.mozilla.org/mozilla-central/source/js/src/jsdate.cpp - */ -final class NativeDate extends IdScriptableObject { - private static final long serialVersionUID = -8307438915861678966L; - - private static final Object DATE_TAG = "Date"; - - private static final String js_NaN_date_str = "Invalid Date"; - - static void init(Scriptable scope, boolean sealed) { - NativeDate obj = new NativeDate(); - // Set the value of the prototype Date to NaN ('invalid date'); - obj.date = ScriptRuntime.NaN; - obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); - } - - private NativeDate() {} - - @Override - public String getClassName() { - return "Date"; - } - - @Override - public Object getDefaultValue(Class typeHint) { - if (typeHint == null) typeHint = ScriptRuntime.StringClass; - return super.getDefaultValue(typeHint); - } - - double getJSTimeValue() { - return date; - } - - @Override - protected void fillConstructorProperties(IdFunctionObject ctor) { - addIdFunctionProperty(ctor, DATE_TAG, ConstructorId_now, "now", 0); - addIdFunctionProperty(ctor, DATE_TAG, ConstructorId_parse, "parse", 1); - addIdFunctionProperty(ctor, DATE_TAG, ConstructorId_UTC, "UTC", 7); - super.fillConstructorProperties(ctor); - } - - @Override - protected void initPrototypeId(int id) { - String s; - int arity; - switch (id) { - case Id_constructor: - arity = 7; - s = "constructor"; - break; - case Id_toString: - arity = 0; - s = "toString"; - break; - case Id_toTimeString: - arity = 0; - s = "toTimeString"; - break; - case Id_toDateString: - arity = 0; - s = "toDateString"; - break; - case Id_toLocaleString: - arity = 0; - s = "toLocaleString"; - break; - case Id_toLocaleTimeString: - arity = 0; - s = "toLocaleTimeString"; - break; - case Id_toLocaleDateString: - arity = 0; - s = "toLocaleDateString"; - break; - case Id_toUTCString: - arity = 0; - s = "toUTCString"; - break; - case Id_toSource: - arity = 0; - s = "toSource"; - break; - case Id_valueOf: - arity = 0; - s = "valueOf"; - break; - case Id_getTime: - arity = 0; - s = "getTime"; - break; - case Id_getYear: - arity = 0; - s = "getYear"; - break; - case Id_getFullYear: - arity = 0; - s = "getFullYear"; - break; - case Id_getUTCFullYear: - arity = 0; - s = "getUTCFullYear"; - break; - case Id_getMonth: - arity = 0; - s = "getMonth"; - break; - case Id_getUTCMonth: - arity = 0; - s = "getUTCMonth"; - break; - case Id_getDate: - arity = 0; - s = "getDate"; - break; - case Id_getUTCDate: - arity = 0; - s = "getUTCDate"; - break; - case Id_getDay: - arity = 0; - s = "getDay"; - break; - case Id_getUTCDay: - arity = 0; - s = "getUTCDay"; - break; - case Id_getHours: - arity = 0; - s = "getHours"; - break; - case Id_getUTCHours: - arity = 0; - s = "getUTCHours"; - break; - case Id_getMinutes: - arity = 0; - s = "getMinutes"; - break; - case Id_getUTCMinutes: - arity = 0; - s = "getUTCMinutes"; - break; - case Id_getSeconds: - arity = 0; - s = "getSeconds"; - break; - case Id_getUTCSeconds: - arity = 0; - s = "getUTCSeconds"; - break; - case Id_getMilliseconds: - arity = 0; - s = "getMilliseconds"; - break; - case Id_getUTCMilliseconds: - arity = 0; - s = "getUTCMilliseconds"; - break; - case Id_getTimezoneOffset: - arity = 0; - s = "getTimezoneOffset"; - break; - case Id_setTime: - arity = 1; - s = "setTime"; - break; - case Id_setMilliseconds: - arity = 1; - s = "setMilliseconds"; - break; - case Id_setUTCMilliseconds: - arity = 1; - s = "setUTCMilliseconds"; - break; - case Id_setSeconds: - arity = 2; - s = "setSeconds"; - break; - case Id_setUTCSeconds: - arity = 2; - s = "setUTCSeconds"; - break; - case Id_setMinutes: - arity = 3; - s = "setMinutes"; - break; - case Id_setUTCMinutes: - arity = 3; - s = "setUTCMinutes"; - break; - case Id_setHours: - arity = 4; - s = "setHours"; - break; - case Id_setUTCHours: - arity = 4; - s = "setUTCHours"; - break; - case Id_setDate: - arity = 1; - s = "setDate"; - break; - case Id_setUTCDate: - arity = 1; - s = "setUTCDate"; - break; - case Id_setMonth: - arity = 2; - s = "setMonth"; - break; - case Id_setUTCMonth: - arity = 2; - s = "setUTCMonth"; - break; - case Id_setFullYear: - arity = 3; - s = "setFullYear"; - break; - case Id_setUTCFullYear: - arity = 3; - s = "setUTCFullYear"; - break; - case Id_setYear: - arity = 1; - s = "setYear"; - break; - case Id_toISOString: - arity = 0; - s = "toISOString"; - break; - case Id_toJSON: - arity = 1; - s = "toJSON"; - break; - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - initPrototypeMethod(DATE_TAG, id, s, arity); - } - - @Override - public Object execIdCall( - IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!f.hasTag(DATE_TAG)) { - return super.execIdCall(f, cx, scope, thisObj, args); - } - int id = f.methodId(); - switch (id) { - case ConstructorId_now: - return ScriptRuntime.wrapNumber(now()); - - case ConstructorId_parse: - { - String dataStr = ScriptRuntime.toString(args, 0); - return ScriptRuntime.wrapNumber(date_parseString(dataStr)); - } - - case ConstructorId_UTC: - return ScriptRuntime.wrapNumber(jsStaticFunction_UTC(args)); - - case Id_constructor: - { - // if called as a function, just return a string - // representing the current time. - if (thisObj != null) return date_format(now(), Id_toString); - return jsConstructor(args); - } - - case Id_toJSON: - { - final String toISOString = "toISOString"; - - Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); - Object tv = ScriptRuntime.toPrimitive(o, ScriptRuntime.NumberClass); - if (tv instanceof Number) { - double d = ((Number) tv).doubleValue(); - if (Double.isNaN(d) || Double.isInfinite(d)) { - return null; - } - } - Object toISO = ScriptableObject.getProperty(o, toISOString); - if (toISO == NOT_FOUND) { - throw ScriptRuntime.typeErrorById( - "msg.function.not.found.in", - toISOString, - ScriptRuntime.toString(o)); - } - if (!(toISO instanceof Callable)) { - throw ScriptRuntime.typeErrorById( - "msg.isnt.function.in", - toISOString, - ScriptRuntime.toString(o), - ScriptRuntime.toString(toISO)); - } - Object result = ((Callable) toISO).call(cx, scope, o, ScriptRuntime.emptyArgs); - if (!ScriptRuntime.isPrimitive(result)) { - throw ScriptRuntime.typeErrorById( - "msg.toisostring.must.return.primitive", - ScriptRuntime.toString(result)); - } - return result; - } - } - - // The rest of Date.prototype methods require thisObj to be Date - NativeDate realThis = ensureType(thisObj, NativeDate.class, f); - double t = realThis.date; - - switch (id) { - case Id_toString: - case Id_toTimeString: - case Id_toDateString: - if (!Double.isNaN(t)) { - return date_format(t, id); - } - return js_NaN_date_str; - - case Id_toLocaleString: - case Id_toLocaleTimeString: - case Id_toLocaleDateString: - if (!Double.isNaN(t)) { - return toLocale_helper(t, id); - } - return js_NaN_date_str; - - case Id_toUTCString: - if (!Double.isNaN(t)) { - return js_toUTCString(t); - } - return js_NaN_date_str; - - case Id_toSource: - return "(new Date(" + ScriptRuntime.toString(t) + "))"; - - case Id_valueOf: - case Id_getTime: - return ScriptRuntime.wrapNumber(t); - - case Id_getYear: - case Id_getFullYear: - case Id_getUTCFullYear: - if (!Double.isNaN(t)) { - if (id != Id_getUTCFullYear) t = LocalTime(t); - t = YearFromTime(t); - if (id == Id_getYear) { - if (cx.hasFeature(Context.FEATURE_NON_ECMA_GET_YEAR)) { - if (1900 <= t && t < 2000) { - t -= 1900; - } - } else { - t -= 1900; - } - } - } - return ScriptRuntime.wrapNumber(t); - - case Id_getMonth: - case Id_getUTCMonth: - if (!Double.isNaN(t)) { - if (id == Id_getMonth) t = LocalTime(t); - t = MonthFromTime(t); - } - return ScriptRuntime.wrapNumber(t); - - case Id_getDate: - case Id_getUTCDate: - if (!Double.isNaN(t)) { - if (id == Id_getDate) t = LocalTime(t); - t = DateFromTime(t); - } - return ScriptRuntime.wrapNumber(t); - - case Id_getDay: - case Id_getUTCDay: - if (!Double.isNaN(t)) { - if (id == Id_getDay) t = LocalTime(t); - t = WeekDay(t); - } - return ScriptRuntime.wrapNumber(t); - - case Id_getHours: - case Id_getUTCHours: - if (!Double.isNaN(t)) { - if (id == Id_getHours) t = LocalTime(t); - t = HourFromTime(t); - } - return ScriptRuntime.wrapNumber(t); - - case Id_getMinutes: - case Id_getUTCMinutes: - if (!Double.isNaN(t)) { - if (id == Id_getMinutes) t = LocalTime(t); - t = MinFromTime(t); - } - return ScriptRuntime.wrapNumber(t); - - case Id_getSeconds: - case Id_getUTCSeconds: - if (!Double.isNaN(t)) { - if (id == Id_getSeconds) t = LocalTime(t); - t = SecFromTime(t); - } - return ScriptRuntime.wrapNumber(t); - - case Id_getMilliseconds: - case Id_getUTCMilliseconds: - if (!Double.isNaN(t)) { - if (id == Id_getMilliseconds) t = LocalTime(t); - t = msFromTime(t); - } - return ScriptRuntime.wrapNumber(t); - - case Id_getTimezoneOffset: - if (!Double.isNaN(t)) { - t = (t - LocalTime(t)) / msPerMinute; - } - return ScriptRuntime.wrapNumber(t); - - case Id_setTime: - t = TimeClip(ScriptRuntime.toNumber(args, 0)); - realThis.date = t; - return ScriptRuntime.wrapNumber(t); - - case Id_setMilliseconds: - case Id_setUTCMilliseconds: - case Id_setSeconds: - case Id_setUTCSeconds: - case Id_setMinutes: - case Id_setUTCMinutes: - case Id_setHours: - case Id_setUTCHours: - t = makeTime(t, args, id); - realThis.date = t; - return ScriptRuntime.wrapNumber(t); - - case Id_setDate: - case Id_setUTCDate: - case Id_setMonth: - case Id_setUTCMonth: - case Id_setFullYear: - case Id_setUTCFullYear: - t = makeDate(t, args, id); - realThis.date = t; - return ScriptRuntime.wrapNumber(t); - - case Id_setYear: - { - double year = ScriptRuntime.toNumber(args, 0); - - if (Double.isNaN(year) || Double.isInfinite(year)) { - t = ScriptRuntime.NaN; - } else { - if (Double.isNaN(t)) { - t = 0; - } else { - t = LocalTime(t); - } - - if (year >= 0 && year <= 99) year += 1900; - - double day = MakeDay(year, MonthFromTime(t), DateFromTime(t)); - t = MakeDate(day, TimeWithinDay(t)); - t = internalUTC(t); - t = TimeClip(t); - } - } - realThis.date = t; - return ScriptRuntime.wrapNumber(t); - - case Id_toISOString: - if (!Double.isNaN(t)) { - return js_toISOString(t); - } - String msg = ScriptRuntime.getMessageById("msg.invalid.date"); - throw ScriptRuntime.rangeError(msg); - - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - } - - /* ECMA helper functions */ - - private static final double HalfTimeDomain = 8.64e15; - private static final double HoursPerDay = 24.0; - private static final double MinutesPerHour = 60.0; - private static final double SecondsPerMinute = 60.0; - private static final double msPerSecond = 1000.0; - private static final double MinutesPerDay = (HoursPerDay * MinutesPerHour); - private static final double SecondsPerDay = (MinutesPerDay * SecondsPerMinute); - private static final double SecondsPerHour = (MinutesPerHour * SecondsPerMinute); - private static final double msPerDay = (SecondsPerDay * msPerSecond); - private static final double msPerHour = (SecondsPerHour * msPerSecond); - private static final double msPerMinute = (SecondsPerMinute * msPerSecond); - - private static double Day(double t) { - return Math.floor(t / msPerDay); - } - - private static double TimeWithinDay(double t) { - double result; - result = t % msPerDay; - if (result < 0) result += msPerDay; - return result; - } - - private static boolean IsLeapYear(int year) { - return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); - } - - /* math here has to be f.p, because we need - * floor((1968 - 1969) / 4) == -1 - */ - private static double DayFromYear(double y) { - return ((365 * ((y) - 1970) - + Math.floor(((y) - 1969) / 4.0) - - Math.floor(((y) - 1901) / 100.0) - + Math.floor(((y) - 1601) / 400.0))); - } - - private static double TimeFromYear(double y) { - return DayFromYear(y) * msPerDay; - } - - private static int YearFromTime(double t) { - if (Double.isInfinite(t) || Double.isNaN(t)) { - return 0; - } - - double y = Math.floor(t / (msPerDay * 365.2425)) + 1970; - double t2 = TimeFromYear(y); - - /* - * Adjust the year if the approximation was wrong. Since the year was - * computed using the average number of ms per year, it will usually - * be wrong for dates within several hours of a year transition. - */ - if (t2 > t) { - y--; - } else { - if (t2 + msPerDay * DaysInYear(y) <= t) y++; - } - return (int) y; - } - - private static double DayFromMonth(int m, int year) { - int day = m * 30; - - if (m >= 7) { - day += m / 2 - 1; - } else if (m >= 2) { - day += (m - 1) / 2 - 1; - } else { - day += m; - } - - if (m >= 2 && IsLeapYear(year)) { - ++day; - } - - return day; - } - - private static double DaysInYear(double year) { - if (Double.isInfinite(year) || Double.isNaN(year)) { - return ScriptRuntime.NaN; - } - return IsLeapYear((int) year) ? 366.0 : 365.0; - } - - private static int DaysInMonth(int year, int month) { - // month is 1-based for DaysInMonth! - if (month == 2) return IsLeapYear(year) ? 29 : 28; - return month >= 8 ? 31 - (month & 1) : 30 + (month & 1); - } - - private static int MonthFromTime(double t) { - int year = YearFromTime(t); - int d = (int) (Day(t) - DayFromYear(year)); - - d -= 31 + 28; - if (d < 0) { - return (d < -28) ? 0 : 1; - } - - if (IsLeapYear(year)) { - if (d == 0) return 1; // 29 February - --d; - } - - // d: date count from 1 March - int estimate = d / 30; // approx number of month since March - int mstart; - switch (estimate) { - case 0: - return 2; - case 1: - mstart = 31; - break; - case 2: - mstart = 31 + 30; - break; - case 3: - mstart = 31 + 30 + 31; - break; - case 4: - mstart = 31 + 30 + 31 + 30; - break; - case 5: - mstart = 31 + 30 + 31 + 30 + 31; - break; - case 6: - mstart = 31 + 30 + 31 + 30 + 31 + 31; - break; - case 7: - mstart = 31 + 30 + 31 + 30 + 31 + 31 + 30; - break; - case 8: - mstart = 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31; - break; - case 9: - mstart = 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30; - break; - case 10: - return 11; // Late december - default: - throw Kit.codeBug(); - } - // if d < mstart then real month since March == estimate - 1 - return (d >= mstart) ? estimate + 2 : estimate + 1; - } - - private static int DateFromTime(double t) { - int year = YearFromTime(t); - int d = (int) (Day(t) - DayFromYear(year)); - - d -= 31 + 28; - if (d < 0) { - return (d < -28) ? d + 31 + 28 + 1 : d + 28 + 1; - } - - if (IsLeapYear(year)) { - if (d == 0) return 29; // 29 February - --d; - } - - // d: date count from 1 March - int mdays, mstart; - switch (d / 30) { // approx number of month since March - case 0: - return d + 1; - case 1: - mdays = 31; - mstart = 31; - break; - case 2: - mdays = 30; - mstart = 31 + 30; - break; - case 3: - mdays = 31; - mstart = 31 + 30 + 31; - break; - case 4: - mdays = 30; - mstart = 31 + 30 + 31 + 30; - break; - case 5: - mdays = 31; - mstart = 31 + 30 + 31 + 30 + 31; - break; - case 6: - mdays = 31; - mstart = 31 + 30 + 31 + 30 + 31 + 31; - break; - case 7: - mdays = 30; - mstart = 31 + 30 + 31 + 30 + 31 + 31 + 30; - break; - case 8: - mdays = 31; - mstart = 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31; - break; - case 9: - mdays = 30; - mstart = 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30; - break; - case 10: - return d - (31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30) + 1; // Late december - default: - throw Kit.codeBug(); - } - d -= mstart; - if (d < 0) { - // wrong estimate: sfhift to previous month - d += mdays; - } - return d + 1; - } - - private static int WeekDay(double t) { - double result; - result = Day(t) + 4; - result = result % 7; - if (result < 0) result += 7; - return (int) result; - } - - private static double now() { - return System.currentTimeMillis(); - } - - private static double DaylightSavingTA(double t) { - // Another workaround! The JRE doesn't seem to know about DST - // before year 1 AD, so we map to equivalent dates for the - // purposes of finding DST. To be safe, we do this for years - // before 1970. - if (t < 0.0) { - int year = EquivalentYear(YearFromTime(t)); - double day = MakeDay(year, MonthFromTime(t), DateFromTime(t)); - t = MakeDate(day, TimeWithinDay(t)); - } - Date date = new Date((long) t); - if (thisTimeZone.inDaylightTime(date)) return msPerHour; - return 0; - } - - /* - * Find a year for which any given date will fall on the same weekday. - * - * This function should be used with caution when used other than - * for determining DST; it hasn't been proven not to produce an - * incorrect year for times near year boundaries. - */ - private static int EquivalentYear(int year) { - int day = (int) DayFromYear(year) + 4; - day = day % 7; - if (day < 0) day += 7; - // Years and leap years on which Jan 1 is a Sunday, Monday, etc. - if (IsLeapYear(year)) { - switch (day) { - case 0: - return 1984; - case 1: - return 1996; - case 2: - return 1980; - case 3: - return 1992; - case 4: - return 1976; - case 5: - return 1988; - case 6: - return 1972; - } - } else { - switch (day) { - case 0: - return 1978; - case 1: - return 1973; - case 2: - return 1985; - case 3: - return 1986; - case 4: - return 1981; - case 5: - return 1971; - case 6: - return 1977; - } - } - // Unreachable - throw Kit.codeBug(); - } - - private static double LocalTime(double t) { - return t + LocalTZA + DaylightSavingTA(t); - } - - private static double internalUTC(double t) { - return t - LocalTZA - DaylightSavingTA(t - LocalTZA); - } - - private static int HourFromTime(double t) { - double result; - result = Math.floor(t / msPerHour) % HoursPerDay; - if (result < 0) result += HoursPerDay; - return (int) result; - } - - private static int MinFromTime(double t) { - double result; - result = Math.floor(t / msPerMinute) % MinutesPerHour; - if (result < 0) result += MinutesPerHour; - return (int) result; - } - - private static int SecFromTime(double t) { - double result; - result = Math.floor(t / msPerSecond) % SecondsPerMinute; - if (result < 0) result += SecondsPerMinute; - return (int) result; - } - - private static int msFromTime(double t) { - double result; - result = t % msPerSecond; - if (result < 0) result += msPerSecond; - return (int) result; - } - - private static double MakeTime(double hour, double min, double sec, double ms) { - return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms; - } - - private static double MakeDay(double year, double month, double date) { - year += Math.floor(month / 12); - - month = month % 12; - if (month < 0) month += 12; - - double yearday = Math.floor(TimeFromYear(year) / msPerDay); - double monthday = DayFromMonth((int) month, (int) year); - - return yearday + monthday + date - 1; - } - - private static double MakeDate(double day, double time) { - return day * msPerDay + time; - } - - private static double TimeClip(double d) { - if (Double.isNaN(d) - || d == Double.POSITIVE_INFINITY - || d == Double.NEGATIVE_INFINITY - || Math.abs(d) > HalfTimeDomain) { - return ScriptRuntime.NaN; - } - if (d > 0.0) return Math.floor(d + 0.); - return Math.ceil(d + 0.); - } - - /* end of ECMA helper functions */ - - /* find UTC time from given date... no 1900 correction! */ - private static double date_msecFromDate( - double year, - double mon, - double mday, - double hour, - double min, - double sec, - double msec) { - double day; - double time; - double result; - - day = MakeDay(year, mon, mday); - time = MakeTime(hour, min, sec, msec); - result = MakeDate(day, time); - return result; - } - - /* compute the time in msec (unclipped) from the given args */ - private static final int MAXARGS = 7; - - private static double date_msecFromArgs(Object[] args) { - double array[] = new double[MAXARGS]; - int loop; - double d; - - for (loop = 0; loop < MAXARGS; loop++) { - if (loop < args.length) { - d = ScriptRuntime.toNumber(args[loop]); - if (Double.isNaN(d) || Double.isInfinite(d)) { - return ScriptRuntime.NaN; - } - array[loop] = ScriptRuntime.toInteger(args[loop]); - } else { - if (loop == 2) { - array[loop] = 1; /* Default the date argument to 1. */ - } else { - array[loop] = 0; - } - } - } - - /* adjust 2-digit years into the 20th century */ - if (array[0] >= 0 && array[0] <= 99) array[0] += 1900; - - return date_msecFromDate( - array[0], array[1], array[2], array[3], array[4], array[5], array[6]); - } - - private static double jsStaticFunction_UTC(Object[] args) { - if (args.length == 0) { - return ScriptRuntime.NaN; - } - return TimeClip(date_msecFromArgs(args)); - } - - /** - * 15.9.1.15 Date Time String Format
- * Parse input string according to simplified ISO-8601 Extended Format: - * - *

    - *
  • YYYY-MM-DD'T'HH:mm:ss.sss'Z' - *
  • or YYYY-MM-DD'T'HH:mm:ss.sss[+-]hh:mm - *
- */ - private static double parseISOString(String s) { - // we use a simple state machine to parse the input string - final int ERROR = -1; - final int YEAR = 0, MONTH = 1, DAY = 2; - final int HOUR = 3, MIN = 4, SEC = 5, MSEC = 6; - final int TZHOUR = 7, TZMIN = 8; - int state = YEAR; - // default values per [15.9.1.15 Date Time String Format] - int[] values = {1970, 1, 1, 0, 0, 0, 0, -1, -1}; - int yearlen = 4, yearmod = 1, tzmod = 1; - int i = 0, len = s.length(); - if (len != 0) { - char c = s.charAt(0); - if (c == '+' || c == '-') { - // 15.9.1.15.1 Extended years - i += 1; - yearlen = 6; - yearmod = (c == '-') ? -1 : 1; - } else if (c == 'T') { - // time-only forms no longer in spec, but follow spidermonkey here - i += 1; - state = HOUR; - } - } - loop: - while (state != ERROR) { - int m = i + (state == YEAR ? yearlen : state == MSEC ? 3 : 2); - if (m > len) { - state = ERROR; - break; - } - - int value = 0; - for (; i < m; ++i) { - char c = s.charAt(i); - if (c < '0' || c > '9') { - state = ERROR; - break loop; - } - value = 10 * value + (c - '0'); - } - values[state] = value; - - if (i == len) { - // reached EOF, check for end state - switch (state) { - case HOUR: - case TZHOUR: - state = ERROR; - } - break; - } - - char c = s.charAt(i++); - if (c == 'Z') { - // handle abbrevation for UTC timezone - values[TZHOUR] = 0; - values[TZMIN] = 0; - switch (state) { - case MIN: - case SEC: - case MSEC: - break; - default: - state = ERROR; - } - break; - } - - // state transition - switch (state) { - case YEAR: - case MONTH: - state = (c == '-' ? state + 1 : c == 'T' ? HOUR : ERROR); - break; - case DAY: - state = (c == 'T' ? HOUR : ERROR); - break; - case HOUR: - state = (c == ':' ? MIN : ERROR); - break; - case TZHOUR: - // state = (c == ':' ? state + 1 : ERROR); - // Non-standard extension, https://bugzilla.mozilla.org/show_bug.cgi?id=682754 - if (c != ':') { - // back off by one and try to read without ':' separator - i -= 1; - } - state = TZMIN; - break; - case MIN: - state = (c == ':' ? SEC : c == '+' || c == '-' ? TZHOUR : ERROR); - break; - case SEC: - state = (c == '.' ? MSEC : c == '+' || c == '-' ? TZHOUR : ERROR); - break; - case MSEC: - state = (c == '+' || c == '-' ? TZHOUR : ERROR); - break; - case TZMIN: - state = ERROR; - break; - } - if (state == TZHOUR) { - // save timezone modificator - tzmod = (c == '-') ? -1 : 1; - } - } - - syntax: - { - // error or unparsed characters - if (state == ERROR || i != len) break syntax; - - // check values - int year = values[YEAR], month = values[MONTH], day = values[DAY]; - int hour = values[HOUR], min = values[MIN], sec = values[SEC], msec = values[MSEC]; - int tzhour = values[TZHOUR], tzmin = values[TZMIN]; - if (year > 275943 // ceil(1e8/365) + 1970 = 275943 - || (month < 1 || month > 12) - || (day < 1 || day > DaysInMonth(year, month)) - || hour > 24 - || (hour == 24 && (min > 0 || sec > 0 || msec > 0)) - || min > 59 - || sec > 59 - || tzhour > 23 - || tzmin > 59) { - break syntax; - } - // valid ISO-8601 format, compute date in milliseconds - double date = date_msecFromDate(year * yearmod, month - 1, day, hour, min, sec, msec); - if (tzhour == -1) { - // Spec says to use UTC timezone, the following bug report says - // that local timezone was meant to be used. Stick with spec for now. - // https://bugs.ecmascript.org/show_bug.cgi?id=112 - // date = internalUTC(date); - } else { - date -= (tzhour * 60 + tzmin) * msPerMinute * tzmod; - } - - if (date < -HalfTimeDomain || date > HalfTimeDomain) break syntax; - return date; - } - - // invalid ISO-8601 format, return NaN - return ScriptRuntime.NaN; - } - - private static double date_parseString(String s) { - double d = parseISOString(s); - if (!Double.isNaN(d)) { - return d; - } - - int year = -1; - int mon = -1; - int mday = -1; - int hour = -1; - int min = -1; - int sec = -1; - char c = 0; - char si = 0; - int i = 0; - int n = -1; - double tzoffset = -1; - char prevc = 0; - int limit = 0; - boolean seenplusminus = false; - - limit = s.length(); - while (i < limit) { - c = s.charAt(i); - i++; - if (c <= ' ' || c == ',' || c == '-') { - if (i < limit) { - si = s.charAt(i); - if (c == '-' && '0' <= si && si <= '9') { - prevc = c; - } - } - continue; - } - if (c == '(') { - /* comments) */ - int depth = 1; - while (i < limit) { - c = s.charAt(i); - i++; - if (c == '(') depth++; - else if (c == ')') if (--depth <= 0) break; - } - continue; - } - if ('0' <= c && c <= '9') { - n = c - '0'; - while (i < limit && '0' <= (c = s.charAt(i)) && c <= '9') { - n = n * 10 + c - '0'; - i++; - } - - /* allow TZA before the year, so - * 'Wed Nov 05 21:49:11 GMT-0800 1997' - * works */ - - /* uses of seenplusminus allow : in TZA, so Java - * no-timezone style of GMT+4:30 works - */ - if ((prevc == '+' || prevc == '-') /* && year>=0 */) { - /* make ':' case below change tzoffset */ - seenplusminus = true; - - /* offset */ - if (n < 24) n = n * 60; /* EG. "GMT-3" */ - else n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */ - if (prevc == '+') /* plus means east of GMT */ n = -n; - if (tzoffset != 0 && tzoffset != -1) return ScriptRuntime.NaN; - tzoffset = n; - } else if (n >= 70 || (prevc == '/' && mon >= 0 && mday >= 0 && year < 0)) { - if (year >= 0) return ScriptRuntime.NaN; - else if (c <= ' ' || c == ',' || c == '/' || i >= limit) - year = n < 100 ? n + 1900 : n; - else return ScriptRuntime.NaN; - } else if (c == ':') { - if (hour < 0) hour = /*byte*/ n; - else if (min < 0) min = /*byte*/ n; - else return ScriptRuntime.NaN; - } else if (c == '/') { - if (mon < 0) mon = /*byte*/ n - 1; - else if (mday < 0) mday = /*byte*/ n; - else return ScriptRuntime.NaN; - } else if (i < limit && c != ',' && c > ' ' && c != '-') { - return ScriptRuntime.NaN; - } else if (seenplusminus && n < 60) { - /* handle GMT-3:30 */ - if (tzoffset < 0) tzoffset -= n; - else tzoffset += n; - } else if (hour >= 0 && min < 0) { - min = /*byte*/ n; - } else if (min >= 0 && sec < 0) { - sec = /*byte*/ n; - } else if (mday < 0) { - mday = /*byte*/ n; - } else { - return ScriptRuntime.NaN; - } - prevc = 0; - } else if (c == '/' || c == ':' || c == '+' || c == '-') { - prevc = c; - } else { - int st = i - 1; - while (i < limit) { - c = s.charAt(i); - if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'))) break; - i++; - } - int letterCount = i - st; - if (letterCount < 2) return ScriptRuntime.NaN; - /* - * Use ported code from jsdate.c rather than the locale-specific - * date-parsing code from Java, to keep js and rhino consistent. - * Is this the right strategy? - */ - String wtb = - "am;pm;" - + "monday;tuesday;wednesday;thursday;friday;" - + "saturday;sunday;" - + "january;february;march;april;may;june;" - + "july;august;september;october;november;december;" - + "gmt;ut;utc;est;edt;cst;cdt;mst;mdt;pst;pdt;"; - int index = 0; - for (int wtbOffset = 0; ; ) { - int wtbNext = wtb.indexOf(';', wtbOffset); - if (wtbNext < 0) return ScriptRuntime.NaN; - if (wtb.regionMatches(true, wtbOffset, s, st, letterCount)) break; - wtbOffset = wtbNext + 1; - ++index; - } - if (index < 2) { - /* - * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as - * 12:30, instead of blindly adding 12 if PM. - */ - if (hour > 12 || hour < 0) { - return ScriptRuntime.NaN; - } else if (index == 0) { - // AM - if (hour == 12) hour = 0; - } else { - // PM - if (hour != 12) hour += 12; - } - } else if ((index -= 2) < 7) { - // ignore week days - } else if ((index -= 7) < 12) { - // month - if (mon < 0) { - mon = index; - } else { - return ScriptRuntime.NaN; - } - } else { - index -= 12; - // timezones - switch (index) { - case 0 /* gmt */: - tzoffset = 0; - break; - case 1 /* ut */: - tzoffset = 0; - break; - case 2 /* utc */: - tzoffset = 0; - break; - case 3 /* est */: - tzoffset = 5 * 60; - break; - case 4 /* edt */: - tzoffset = 4 * 60; - break; - case 5 /* cst */: - tzoffset = 6 * 60; - break; - case 6 /* cdt */: - tzoffset = 5 * 60; - break; - case 7 /* mst */: - tzoffset = 7 * 60; - break; - case 8 /* mdt */: - tzoffset = 6 * 60; - break; - case 9 /* pst */: - tzoffset = 8 * 60; - break; - case 10 /* pdt */: - tzoffset = 7 * 60; - break; - default: - Kit.codeBug(); - } - } - } - } - if (year < 0 || mon < 0 || mday < 0) return ScriptRuntime.NaN; - if (sec < 0) sec = 0; - if (min < 0) min = 0; - if (hour < 0) hour = 0; - - double msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0); - if (tzoffset == -1) { - /* no time zone specified, have to use local */ - return internalUTC(msec); - } - return msec + tzoffset * msPerMinute; - } - - private static String date_format(double t, int methodId) { - StringBuilder result = new StringBuilder(60); - double local = LocalTime(t); - - /* Tue Oct 31 09:41:40 GMT-0800 (PST) 2000 */ - /* Tue Oct 31 2000 */ - /* 09:41:40 GMT-0800 (PST) */ - - if (methodId != Id_toTimeString) { - appendWeekDayName(result, WeekDay(local)); - result.append(' '); - appendMonthName(result, MonthFromTime(local)); - result.append(' '); - append0PaddedUint(result, DateFromTime(local), 2); - result.append(' '); - int year = YearFromTime(local); - if (year < 0) { - result.append('-'); - year = -year; - } - append0PaddedUint(result, year, 4); - if (methodId != Id_toDateString) result.append(' '); - } - - if (methodId != Id_toDateString) { - append0PaddedUint(result, HourFromTime(local), 2); - result.append(':'); - append0PaddedUint(result, MinFromTime(local), 2); - result.append(':'); - append0PaddedUint(result, SecFromTime(local), 2); - - // offset from GMT in minutes. The offset includes daylight - // savings, if it applies. - int minutes = (int) Math.floor((LocalTZA + DaylightSavingTA(t)) / msPerMinute); - // map 510 minutes to 0830 hours - int offset = (minutes / 60) * 100 + minutes % 60; - if (offset > 0) { - result.append(" GMT+"); - } else { - result.append(" GMT-"); - offset = -offset; - } - append0PaddedUint(result, offset, 4); - - // Find an equivalent year before getting the timezone - // comment. See DaylightSavingTA. - if (t < 0.0) { - int equiv = EquivalentYear(YearFromTime(local)); - double day = MakeDay(equiv, MonthFromTime(t), DateFromTime(t)); - t = MakeDate(day, TimeWithinDay(t)); - } - result.append(" ("); - Date date = new Date((long) t); - synchronized (timeZoneFormatter) { - result.append(timeZoneFormatter.format(date)); - } - result.append(')'); - } - return result.toString(); - } - - /* the javascript constructor */ - private static Object jsConstructor(Object[] args) { - NativeDate obj = new NativeDate(); - - // if called as a constructor with no args, - // return a new Date with the current time. - if (args.length == 0) { - obj.date = now(); - return obj; - } - - // if called with just one arg - - if (args.length == 1) { - Object arg0 = args[0]; - if (arg0 instanceof NativeDate) { - obj.date = ((NativeDate) arg0).date; - return obj; - } - if (arg0 instanceof Scriptable) { - arg0 = ((Scriptable) arg0).getDefaultValue(null); - } - double date; - if (arg0 instanceof CharSequence) { - // it's a string; parse it. - date = date_parseString(arg0.toString()); - } else { - // if it's not a string, use it as a millisecond date - date = ScriptRuntime.toNumber(arg0); - } - obj.date = TimeClip(date); - return obj; - } - - double time = date_msecFromArgs(args); - - if (!Double.isNaN(time) && !Double.isInfinite(time)) time = TimeClip(internalUTC(time)); - - obj.date = time; - - return obj; - } - - private static String toLocale_helper(double t, int methodId) { - DateFormat formatter; - switch (methodId) { - case Id_toLocaleString: - formatter = localeDateTimeFormatter; - break; - case Id_toLocaleTimeString: - formatter = localeTimeFormatter; - break; - case Id_toLocaleDateString: - formatter = localeDateFormatter; - break; - default: - throw new AssertionError(); // unreachable - } - - synchronized (formatter) { - return formatter.format(new Date((long) t)); - } - } - - private static String js_toUTCString(double date) { - StringBuilder result = new StringBuilder(60); - - appendWeekDayName(result, WeekDay(date)); - result.append(", "); - append0PaddedUint(result, DateFromTime(date), 2); - result.append(' '); - appendMonthName(result, MonthFromTime(date)); - result.append(' '); - int year = YearFromTime(date); - if (year < 0) { - result.append('-'); - year = -year; - } - append0PaddedUint(result, year, 4); - result.append(' '); - append0PaddedUint(result, HourFromTime(date), 2); - result.append(':'); - append0PaddedUint(result, MinFromTime(date), 2); - result.append(':'); - append0PaddedUint(result, SecFromTime(date), 2); - result.append(" GMT"); - return result.toString(); - } - - private static String js_toISOString(double t) { - StringBuilder result = new StringBuilder(27); - - int year = YearFromTime(t); - if (year < 0) { - result.append('-'); - append0PaddedUint(result, -year, 6); - } else if (year > 9999) { - result.append('+'); - append0PaddedUint(result, year, 6); - } else { - append0PaddedUint(result, year, 4); - } - result.append('-'); - append0PaddedUint(result, MonthFromTime(t) + 1, 2); - result.append('-'); - append0PaddedUint(result, DateFromTime(t), 2); - result.append('T'); - append0PaddedUint(result, HourFromTime(t), 2); - result.append(':'); - append0PaddedUint(result, MinFromTime(t), 2); - result.append(':'); - append0PaddedUint(result, SecFromTime(t), 2); - result.append('.'); - append0PaddedUint(result, msFromTime(t), 3); - result.append('Z'); - return result.toString(); - } - - private static void append0PaddedUint(StringBuilder sb, int i, int minWidth) { - if (i < 0) Kit.codeBug(); - int scale = 1; - --minWidth; - if (i >= 10) { - if (i < 1000 * 1000 * 1000) { - for (; ; ) { - int newScale = scale * 10; - if (i < newScale) { - break; - } - --minWidth; - scale = newScale; - } - } else { - // Separated case not to check against 10 * 10^9 overflow - minWidth -= 9; - scale = 1000 * 1000 * 1000; - } - } - while (minWidth > 0) { - sb.append('0'); - --minWidth; - } - while (scale != 1) { - sb.append((char) ('0' + (i / scale))); - i %= scale; - scale /= 10; - } - sb.append((char) ('0' + i)); - } - - private static void appendMonthName(StringBuilder sb, int index) { - // Take advantage of the fact that all month abbreviations - // have the same length to minimize amount of strings runtime has - // to keep in memory - String months = - "Jan" + "Feb" + "Mar" + "Apr" + "May" + "Jun" + "Jul" + "Aug" + "Sep" + "Oct" - + "Nov" + "Dec"; - index *= 3; - for (int i = 0; i != 3; ++i) { - sb.append(months.charAt(index + i)); - } - } - - private static void appendWeekDayName(StringBuilder sb, int index) { - String days = "Sun" + "Mon" + "Tue" + "Wed" + "Thu" + "Fri" + "Sat"; - index *= 3; - for (int i = 0; i != 3; ++i) { - sb.append(days.charAt(index + i)); - } - } - - private static double makeTime(double date, Object[] args, int methodId) { - if (args.length == 0) { - /* - * Satisfy the ECMA rule that if a function is called with - * fewer arguments than the specified formal arguments, the - * remaining arguments are set to undefined. Seems like all - * the Date.setWhatever functions in ECMA are only varargs - * beyond the first argument; this should be set to undefined - * if it's not given. This means that "d = new Date(); - * d.setMilliseconds()" returns NaN. Blech. - */ - return ScriptRuntime.NaN; - } - - int maxargs; - boolean local = true; - switch (methodId) { - case Id_setUTCMilliseconds: - local = false; - // fallthrough - case Id_setMilliseconds: - maxargs = 1; - break; - - case Id_setUTCSeconds: - local = false; - // fallthrough - case Id_setSeconds: - maxargs = 2; - break; - - case Id_setUTCMinutes: - local = false; - // fallthrough - case Id_setMinutes: - maxargs = 3; - break; - - case Id_setUTCHours: - local = false; - // fallthrough - case Id_setHours: - maxargs = 4; - break; - - default: - throw Kit.codeBug(); - } - - boolean hasNaN = false; - int numNums = args.length < maxargs ? args.length : maxargs; - assert numNums <= 4; - double[] nums = new double[4]; - for (int i = 0; i < numNums; i++) { - double d = ScriptRuntime.toNumber(args[i]); - if (Double.isNaN(d) || Double.isInfinite(d)) { - hasNaN = true; - } else { - nums[i] = ScriptRuntime.toInteger(d); - } - } - - // just return NaN if the date is already NaN, - // limit checks that happen in MakeTime in ECMA. - if (hasNaN || Double.isNaN(date)) { - return ScriptRuntime.NaN; - } - - int i = 0, stop = numNums; - double hour, min, sec, msec; - double lorutime; /* Local or UTC version of date */ - - if (local) lorutime = LocalTime(date); - else lorutime = date; - - if (maxargs >= 4 && i < stop) hour = nums[i++]; - else hour = HourFromTime(lorutime); - - if (maxargs >= 3 && i < stop) min = nums[i++]; - else min = MinFromTime(lorutime); - - if (maxargs >= 2 && i < stop) sec = nums[i++]; - else sec = SecFromTime(lorutime); - - if (maxargs >= 1 && i < stop) msec = nums[i++]; - else msec = msFromTime(lorutime); - - double time = MakeTime(hour, min, sec, msec); - double result = MakeDate(Day(lorutime), time); - - if (local) result = internalUTC(result); - - return TimeClip(result); - } - - private static double makeDate(double date, Object[] args, int methodId) { - /* see complaint about ECMA in date_MakeTime */ - if (args.length == 0) { - return ScriptRuntime.NaN; - } - - int maxargs; - boolean local = true; - switch (methodId) { - case Id_setUTCDate: - local = false; - // fallthrough - case Id_setDate: - maxargs = 1; - break; - - case Id_setUTCMonth: - local = false; - // fallthrough - case Id_setMonth: - maxargs = 2; - break; - - case Id_setUTCFullYear: - local = false; - // fallthrough - case Id_setFullYear: - maxargs = 3; - break; - - default: - throw Kit.codeBug(); - } - - boolean hasNaN = false; - int numNums = args.length < maxargs ? args.length : maxargs; - assert 1 <= numNums && numNums <= 3; - double[] nums = new double[3]; - for (int i = 0; i < numNums; i++) { - double d = ScriptRuntime.toNumber(args[i]); - if (Double.isNaN(d) || Double.isInfinite(d)) { - hasNaN = true; - } else { - nums[i] = ScriptRuntime.toInteger(d); - } - } - - // limit checks that happen in MakeTime in ECMA. - if (hasNaN) { - return ScriptRuntime.NaN; - } - - int i = 0, stop = numNums; - double year, month, day; - double lorutime; /* Local or UTC version of date */ - - /* return NaN if date is NaN and we're not setting the year, - * If we are, use 0 as the time. */ - if (Double.isNaN(date)) { - if (maxargs < 3) { - return ScriptRuntime.NaN; - } - lorutime = 0; - } else { - if (local) lorutime = LocalTime(date); - else lorutime = date; - } - - if (maxargs >= 3 && i < stop) year = nums[i++]; - else year = YearFromTime(lorutime); - - if (maxargs >= 2 && i < stop) month = nums[i++]; - else month = MonthFromTime(lorutime); - - if (maxargs >= 1 && i < stop) day = nums[i++]; - else day = DateFromTime(lorutime); - - day = MakeDay(year, month, day); /* day within year */ - double result = MakeDate(day, TimeWithinDay(lorutime)); - - if (local) result = internalUTC(result); - - return TimeClip(result); - } - - @Override - protected int findPrototypeId(String s) { - int id; - switch (s) { - case "constructor": - id = Id_constructor; - break; - case "toString": - id = Id_toString; - break; - case "toTimeString": - id = Id_toTimeString; - break; - case "toDateString": - id = Id_toDateString; - break; - case "toLocaleString": - id = Id_toLocaleString; - break; - case "toLocaleTimeString": - id = Id_toLocaleTimeString; - break; - case "toLocaleDateString": - id = Id_toLocaleDateString; - break; - case "toUTCString": - id = Id_toUTCString; - break; - case "toSource": - id = Id_toSource; - break; - case "valueOf": - id = Id_valueOf; - break; - case "getTime": - id = Id_getTime; - break; - case "getYear": - id = Id_getYear; - break; - case "getFullYear": - id = Id_getFullYear; - break; - case "getUTCFullYear": - id = Id_getUTCFullYear; - break; - case "getMonth": - id = Id_getMonth; - break; - case "getUTCMonth": - id = Id_getUTCMonth; - break; - case "getDate": - id = Id_getDate; - break; - case "getUTCDate": - id = Id_getUTCDate; - break; - case "getDay": - id = Id_getDay; - break; - case "getUTCDay": - id = Id_getUTCDay; - break; - case "getHours": - id = Id_getHours; - break; - case "getUTCHours": - id = Id_getUTCHours; - break; - case "getMinutes": - id = Id_getMinutes; - break; - case "getUTCMinutes": - id = Id_getUTCMinutes; - break; - case "getSeconds": - id = Id_getSeconds; - break; - case "getUTCSeconds": - id = Id_getUTCSeconds; - break; - case "getMilliseconds": - id = Id_getMilliseconds; - break; - case "getUTCMilliseconds": - id = Id_getUTCMilliseconds; - break; - case "getTimezoneOffset": - id = Id_getTimezoneOffset; - break; - case "setTime": - id = Id_setTime; - break; - case "setMilliseconds": - id = Id_setMilliseconds; - break; - case "setUTCMilliseconds": - id = Id_setUTCMilliseconds; - break; - case "setSeconds": - id = Id_setSeconds; - break; - case "setUTCSeconds": - id = Id_setUTCSeconds; - break; - case "setMinutes": - id = Id_setMinutes; - break; - case "setUTCMinutes": - id = Id_setUTCMinutes; - break; - case "setHours": - id = Id_setHours; - break; - case "setUTCHours": - id = Id_setUTCHours; - break; - case "setDate": - id = Id_setDate; - break; - case "setUTCDate": - id = Id_setUTCDate; - break; - case "setMonth": - id = Id_setMonth; - break; - case "setUTCMonth": - id = Id_setUTCMonth; - break; - case "setFullYear": - id = Id_setFullYear; - break; - case "setUTCFullYear": - id = Id_setUTCFullYear; - break; - case "setYear": - id = Id_setYear; - break; - case "toISOString": - id = Id_toISOString; - break; - case "toJSON": - id = Id_toJSON; - break; - case "toGMTString": - id = Id_toGMTString; - break; - default: - id = 0; - break; - } - return id; - } - - private static final int ConstructorId_now = -3, - ConstructorId_parse = -2, - ConstructorId_UTC = -1, - Id_constructor = 1, - Id_toString = 2, - Id_toTimeString = 3, - Id_toDateString = 4, - Id_toLocaleString = 5, - Id_toLocaleTimeString = 6, - Id_toLocaleDateString = 7, - Id_toUTCString = 8, - Id_toSource = 9, - Id_valueOf = 10, - Id_getTime = 11, - Id_getYear = 12, - Id_getFullYear = 13, - Id_getUTCFullYear = 14, - Id_getMonth = 15, - Id_getUTCMonth = 16, - Id_getDate = 17, - Id_getUTCDate = 18, - Id_getDay = 19, - Id_getUTCDay = 20, - Id_getHours = 21, - Id_getUTCHours = 22, - Id_getMinutes = 23, - Id_getUTCMinutes = 24, - Id_getSeconds = 25, - Id_getUTCSeconds = 26, - Id_getMilliseconds = 27, - Id_getUTCMilliseconds = 28, - Id_getTimezoneOffset = 29, - Id_setTime = 30, - Id_setMilliseconds = 31, - Id_setUTCMilliseconds = 32, - Id_setSeconds = 33, - Id_setUTCSeconds = 34, - Id_setMinutes = 35, - Id_setUTCMinutes = 36, - Id_setHours = 37, - Id_setUTCHours = 38, - Id_setDate = 39, - Id_setUTCDate = 40, - Id_setMonth = 41, - Id_setUTCMonth = 42, - Id_setFullYear = 43, - Id_setUTCFullYear = 44, - Id_setYear = 45, - Id_toISOString = 46, - Id_toJSON = 47, - MAX_PROTOTYPE_ID = Id_toJSON; - - private static final int Id_toGMTString = Id_toUTCString; // Alias, see Ecma B.2.6 - - /* cached values */ - private static final TimeZone thisTimeZone = TimeZone.getDefault(); - private static final double LocalTZA = thisTimeZone.getRawOffset(); - - // not thread safe - private static final DateFormat timeZoneFormatter = new SimpleDateFormat("zzz"); - private static final DateFormat localeDateTimeFormatter = - new SimpleDateFormat("MMMM d, yyyy h:mm:ss a z"); - private static final DateFormat localeDateFormatter = new SimpleDateFormat("MMMM d, yyyy"); - private static final DateFormat localeTimeFormatter = new SimpleDateFormat("h:mm:ss a z"); - - private double date; -} diff --git a/src/main/java/org/mozilla/javascript/NativeError.java b/src/main/java/org/mozilla/javascript/NativeError.java deleted file mode 100644 index 2af3d7fb..00000000 --- a/src/main/java/org/mozilla/javascript/NativeError.java +++ /dev/null @@ -1,383 +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; - -/** - * The class of error objects - * - *

ECMA 15.11 - */ -final class NativeError extends IdScriptableObject { - private static final long serialVersionUID = -5338413581437645187L; - - private static final Object ERROR_TAG = "Error"; - - /** Default stack limit is set to "Infinity", here represented as a negative int */ - public static final int DEFAULT_STACK_LIMIT = -1; - - // This is used by "captureStackTrace" - private static final String STACK_HIDE_KEY = "_stackHide"; - - private RhinoException stackProvider; - private Object stack; - - static void init(Scriptable scope, boolean sealed) { - NativeError obj = new NativeError(); - ScriptableObject.putProperty(obj, "name", "Error"); - ScriptableObject.putProperty(obj, "message", ""); - ScriptableObject.putProperty(obj, "fileName", ""); - ScriptableObject.putProperty(obj, "lineNumber", 0); - obj.setAttributes("name", ScriptableObject.DONTENUM); - obj.setAttributes("message", ScriptableObject.DONTENUM); - obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); - NativeCallSite.init(obj, sealed); - } - - static NativeError make(Context cx, Scriptable scope, IdFunctionObject ctorObj, Object[] args) { - Scriptable proto = (Scriptable) (ctorObj.get("prototype", ctorObj)); - - NativeError obj = new NativeError(); - obj.setPrototype(proto); - obj.setParentScope(scope); - - int arglen = args.length; - if (arglen >= 1) { - if (!Undefined.isUndefined(args[0])) { - ScriptableObject.putProperty(obj, "message", ScriptRuntime.toString(args[0])); - obj.setAttributes("message", ScriptableObject.DONTENUM); - } - if (arglen >= 2) { - ScriptableObject.putProperty(obj, "fileName", args[1]); - if (arglen >= 3) { - int line = ScriptRuntime.toInt32(args[2]); - ScriptableObject.putProperty(obj, "lineNumber", line); - } - } - } - return obj; - } - - @Override - protected void fillConstructorProperties(IdFunctionObject ctor) { - addIdFunctionProperty( - ctor, ERROR_TAG, ConstructorId_captureStackTrace, "captureStackTrace", 2); - - // Define a stack to be used even if this Error is never thrown. - // The big cost is in turning this into an actual stack trace, which is set up to - // be done lazily below. - stackProvider = new EvaluatorException(""); - - // This is running on the global "Error" object. Associate an object there that can store - // default stack trace, etc. - // This prevents us from having to add two additional fields to every Error object. - ProtoProps protoProps = new ProtoProps(); - associateValue(ProtoProps.KEY, protoProps); - - ctor.defineProperty( - "stackTraceLimit", - protoProps::getStackTraceLimit, - protoProps::setStackTraceLimit, - 0); - ctor.defineProperty( - "prepareStackTrace", - protoProps::getPrepareStackTrace, - protoProps::setPrepareStackTrace, - 0); - - super.fillConstructorProperties(ctor); - } - - @Override - public String getClassName() { - return "Error"; - } - - @Override - public String toString() { - // According to spec, Error.prototype.toString() may return undefined. - Object toString = js_toString(this); - return toString instanceof String ? (String) toString : super.toString(); - } - - @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 = 0; - s = "toSource"; - break; - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - initPrototypeMethod(ERROR_TAG, id, s, arity); - } - - @Override - public Object execIdCall( - IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!f.hasTag(ERROR_TAG)) { - return super.execIdCall(f, cx, scope, thisObj, args); - } - int id = f.methodId(); - switch (id) { - case Id_constructor: - NativeError err = make(cx, scope, f, args); - // All new Errors have a default exception installed so that - // there is a stack trace captured even if they are never thrown. - err.setStackProvider(new EvaluatorException("")); - return err; - - case Id_toString: - return js_toString(thisObj); - - case Id_toSource: - return js_toSource(cx, scope, thisObj); - - case ConstructorId_captureStackTrace: - js_captureStackTrace(cx, scope, thisObj, args); - return Undefined.instance; - } - throw new IllegalArgumentException(String.valueOf(id)); - } - - public void setStackProvider(RhinoException re) { - // We go some extra miles to make sure the stack property is only - // generated on demand, is cached after the first access, and is - // overwritable like an ordinary property. Hence this setup with - // the getter and setter below. - if (stackProvider == null) { - defineProperty("stack", this::getStackDelegated, this::setStackDelegated, DONTENUM); - } - stackProvider = re; - } - - public Object getStackDelegated() { - if (stack != null) { - return stack; - } - if (stackProvider == null) { - return NOT_FOUND; - } - - // Get the object where prototype stuff is stored. - int limit = DEFAULT_STACK_LIMIT; - Function prepare = null; - NativeError cons = (NativeError) getPrototype(); - ProtoProps pp = (ProtoProps) cons.getAssociatedValue(ProtoProps.KEY); - - if (pp != null) { - limit = pp.stackTraceLimit; - prepare = pp.prepareStackTrace; - } - - // This key is only set by captureStackTrace - String hideFunc = (String) getAssociatedValue(STACK_HIDE_KEY); - ScriptStackElement[] stackTrace = stackProvider.getScriptStack(limit, hideFunc); - - // Determine whether to format the stack trace ourselves, or call the user's code to do it - Object value; - if (prepare == null) { - value = RhinoException.formatStackTrace(stackTrace, stackProvider.details()); - } else { - value = callPrepareStack(prepare, stackTrace); - } - stack = value; - return value; - } - - public void setStackDelegated(Object value) { - stackProvider = null; - stack = value; - } - - private Object callPrepareStack(Function prepare, ScriptStackElement[] stack) { - Context cx = Context.getCurrentContext(); - Object[] elts = new Object[stack.length]; - - // The "prepareStackTrace" function takes an array of CallSite objects. - for (int i = 0; i < stack.length; i++) { - NativeCallSite site = (NativeCallSite) cx.newObject(this, "CallSite"); - site.setElement(stack[i]); - elts[i] = site; - } - - Scriptable eltArray = cx.newArray(this, elts); - return prepare.call(cx, prepare, this, new Object[] {this, eltArray}); - } - - private static Object js_toString(Scriptable thisObj) { - Object nameObj = ScriptableObject.getProperty(thisObj, "name"); - String name; - if (nameObj == NOT_FOUND || Undefined.isUndefined(nameObj)) { - name = "Error"; - } else { - name = ScriptRuntime.toString(nameObj); - } - Object msgObj = ScriptableObject.getProperty(thisObj, "message"); - String msg; - if (msgObj == NOT_FOUND || Undefined.isUndefined(msgObj)) { - msg = ""; - } else { - msg = ScriptRuntime.toString(msgObj); - } - if (name.isEmpty()) { - return msg; - } else if (msg.isEmpty()) { - return name; - } else { - return name + ": " + msg; - } - } - - private static String js_toSource(Context cx, Scriptable scope, Scriptable thisObj) { - // Emulation of SpiderMonkey behavior - Object name = ScriptableObject.getProperty(thisObj, "name"); - Object message = ScriptableObject.getProperty(thisObj, "message"); - Object fileName = ScriptableObject.getProperty(thisObj, "fileName"); - Object lineNumber = ScriptableObject.getProperty(thisObj, "lineNumber"); - - StringBuilder sb = new StringBuilder(); - sb.append("(new "); - if (name == NOT_FOUND) { - name = Undefined.instance; - } - sb.append(ScriptRuntime.toString(name)); - sb.append("("); - if (message != NOT_FOUND || fileName != NOT_FOUND || lineNumber != NOT_FOUND) { - if (message == NOT_FOUND) { - message = ""; - } - sb.append(ScriptRuntime.uneval(cx, scope, message)); - if (fileName != NOT_FOUND || lineNumber != NOT_FOUND) { - sb.append(", "); - if (fileName == NOT_FOUND) { - fileName = ""; - } - sb.append(ScriptRuntime.uneval(cx, scope, fileName)); - if (lineNumber != NOT_FOUND) { - int line = ScriptRuntime.toInt32(lineNumber); - if (line != 0) { - sb.append(", "); - sb.append(ScriptRuntime.toString(line)); - } - } - } - } - sb.append("))"); - return sb.toString(); - } - - private static void js_captureStackTrace( - Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - ScriptableObject obj = (ScriptableObject) ScriptRuntime.toObject(cx, scope, args[0]); - Function func = null; - if (args.length > 1) { - func = (Function) ScriptRuntime.toObjectOrNull(cx, args[1], scope); - } - - // Create a new error that will have the correct prototype so we can re-use "getStackTrace" - NativeError err = (NativeError) cx.newObject(thisObj, "Error"); - // Wire it up so that it will have an actual exception with a stack trace - err.setStackProvider(new EvaluatorException("[object Object]")); - - // Figure out if they passed a function used to hide part of the stack - if (func != null) { - Object funcName = func.get("name", func); - if ((funcName != null) && !Undefined.isUndefined(funcName)) { - err.associateValue(STACK_HIDE_KEY, Context.toString(funcName)); - } - } - - // from https://v8.dev/docs/stack-trace-api - // Error.captureStackTrace(error, constructorOpt) - // adds a stack property to the given error object that yields the stack trace - // at the time captureStackTrace was called. Stack traces collected through - // Error.captureStackTrace are immediately collected, formatted, - // and attached to the given error object. - obj.defineProperty("stack", err.get("stack"), ScriptableObject.DONTENUM); - } - - @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; - default: - id = 0; - break; - } - return id; - } - - private static final int Id_constructor = 1, - Id_toString = 2, - Id_toSource = 3, - ConstructorId_captureStackTrace = -1, - MAX_PROTOTYPE_ID = 3; - - /** - * We will attch this object to the constructor and use it solely to store the constructor - * properties that are "global." We can't make them static because there can be many contexts in - * the same JVM. - */ - private static final class ProtoProps implements Serializable { - static final String KEY = "_ErrorPrototypeProps"; - - private static final long serialVersionUID = 1907180507775337939L; - - int stackTraceLimit = DEFAULT_STACK_LIMIT; - Function prepareStackTrace; - - public Object getStackTraceLimit() { - if (stackTraceLimit >= 0) { - return stackTraceLimit; - } - return Double.POSITIVE_INFINITY; - } - - public void setStackTraceLimit(Object value) { - double limit = Context.toNumber(value); - if (Double.isNaN(limit) || Double.isInfinite(limit)) { - stackTraceLimit = -1; - } else { - stackTraceLimit = (int) limit; - } - } - - public Object getPrepareStackTrace() { - return (prepareStackTrace == null ? Undefined.instance : prepareStackTrace); - } - - public void setPrepareStackTrace(Object value) { - if ((value == null) || Undefined.isUndefined(value)) { - prepareStackTrace = null; - } else if (value instanceof Function) { - prepareStackTrace = (Function) value; - } - } - } -} diff --git a/src/main/java/org/mozilla/javascript/NativeFunction.java b/src/main/java/org/mozilla/javascript/NativeFunction.java deleted file mode 100644 index 891d95c4..00000000 --- a/src/main/java/org/mozilla/javascript/NativeFunction.java +++ /dev/null @@ -1,143 +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.debug.DebuggableScript; - -/** - * This class implements the Function native object. - * See ECMA 15.3. - * @author Norris Boyd - */ -public abstract class NativeFunction extends BaseFunction -{ - - private static final long serialVersionUID = 8713897114082216401L; - - public final void initScriptFunction(Context cx, Scriptable scope) - { - initScriptFunction(cx, scope, isGeneratorFunction()); - } - - public final void initScriptFunction(Context cx, Scriptable scope, boolean es6GeneratorFunction) - { - ScriptRuntime.setFunctionProtoAndParent(this, scope, es6GeneratorFunction); - } - - /** - * @param indent How much to indent the decompiled result - * - * @param flags Flags specifying format of decompilation output - */ - @Override - final String decompile(int indent, int flags) - { - String encodedSource = getEncodedSource(); - if (encodedSource == null) { - return super.decompile(indent, flags); - } - UintMap properties = new UintMap(1); - properties.put(Decompiler.INITIAL_INDENT_PROP, indent); - return Decompiler.decompile(encodedSource, flags, properties); - } - - @Override - public int getLength() - { - int paramCount = getParamCount(); - if (getLanguageVersion() != Context.VERSION_1_2) { - return paramCount; - } - Context cx = Context.getContext(); - NativeCall activation = ScriptRuntime.findFunctionActivation(cx, this); - if (activation == null) { - return paramCount; - } - return activation.originalArgs.length; - } - - @Override - public int getArity() - { - return getParamCount(); - } - - /** - * @deprecated Use {@link BaseFunction#getFunctionName()} instead. - * For backwards compatibility keep an old method name used by - * Batik and possibly others. - */ - @Deprecated - public String jsGet_name() - { - return getFunctionName(); - } - - /** - * Get encoded source string. - */ - public String getEncodedSource() - { - return null; - } - - public DebuggableScript getDebuggableView() - { - return null; - } - - /** - * Resume execution of a suspended generator. - * @param cx The current context - * @param scope Scope for the parent generator function - * @param operation The resumption operation (next, send, etc.. ) - * @param state The generator state (has locals, stack, etc.) - * @param value The return value of yield (if required). - * @return The next yielded value (if any) - */ - public Object resumeGenerator(Context cx, Scriptable scope, - int operation, Object state, Object value) - { - throw new EvaluatorException("resumeGenerator() not implemented"); - } - - - protected abstract int getLanguageVersion(); - - /** - * Get number of declared parameters. It should be 0 for scripts. - */ - protected abstract int getParamCount(); - - /** - * Get number of declared parameters and variables defined through var - * statements. - */ - protected abstract int getParamAndVarCount(); - - /** - * Get parameter or variable name. - * If index < {@link #getParamCount()}, then return the name of the - * corresponding parameter. Otherwise return the name of variable. - */ - protected abstract String getParamOrVarName(int index); - - /** - * Get parameter or variable const-ness. - * If index < {@link #getParamCount()}, then return the const-ness - * of the corresponding parameter. Otherwise return whether the variable is - * const. - */ - protected boolean getParamOrVarConst(int index) - { - // By default return false to preserve compatibility with existing - // classes subclassing this class, which are mostly generated by jsc - // from earlier Rhino versions. See Bugzilla #396117. - return false; - } -} - diff --git a/src/main/java/org/mozilla/javascript/NativeGenerator.java b/src/main/java/org/mozilla/javascript/NativeGenerator.java deleted file mode 100644 index b1081621..00000000 --- a/src/main/java/org/mozilla/javascript/NativeGenerator.java +++ /dev/null @@ -1,222 +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; - -/** - * This class implements generator objects. See - * http://developer.mozilla.org/en/docs/New_in_JavaScript_1.7#Generators - * - * @author Norris Boyd - */ -public final class NativeGenerator extends IdScriptableObject { - private static final long serialVersionUID = 1645892441041347273L; - - private static final Object GENERATOR_TAG = "Generator"; - - static NativeGenerator init(ScriptableObject scope, boolean sealed) { - // Generator - // Can't use "NativeGenerator().exportAsJSClass" since we don't want - // to define "Generator" as a constructor in the top-level scope. - - NativeGenerator prototype = new NativeGenerator(); - if (scope != null) { - prototype.setParentScope(scope); - prototype.setPrototype(getObjectPrototype(scope)); - } - prototype.activatePrototypeMap(MAX_PROTOTYPE_ID); - if (sealed) { - prototype.sealObject(); - } - - // Need to access Generator prototype when constructing - // Generator instances, but don't have a generator constructor - // to use to find the prototype. Use the "associateValue" - // approach instead. - if (scope != null) { - scope.associateValue(GENERATOR_TAG, prototype); - } - - return prototype; - } - - /** Only for constructing the prototype object. */ - private NativeGenerator() {} - - public NativeGenerator(Scriptable scope, NativeFunction function, Object savedState) { - this.function = function; - this.savedState = savedState; - // Set parent and prototype properties. Since we don't have a - // "Generator" constructor in the top scope, we stash the - // prototype in the top scope's associated value. - Scriptable top = ScriptableObject.getTopLevelScope(scope); - this.setParentScope(top); - NativeGenerator prototype = - (NativeGenerator) ScriptableObject.getTopScopeValue(top, GENERATOR_TAG); - this.setPrototype(prototype); - } - - public static final int GENERATOR_SEND = 0, GENERATOR_THROW = 1, GENERATOR_CLOSE = 2; - - @Override - public String getClassName() { - return "Generator"; - } - - @Override - protected void initPrototypeId(int id) { - String s; - int arity; - switch (id) { - case Id_close: - arity = 1; - s = "close"; - break; - case Id_next: - arity = 1; - s = "next"; - break; - case Id_send: - arity = 0; - s = "send"; - break; - case Id_throw: - arity = 0; - s = "throw"; - break; - case Id___iterator__: - arity = 1; - s = "__iterator__"; - break; - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - initPrototypeMethod(GENERATOR_TAG, id, s, arity); - } - - @Override - public Object execIdCall( - IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!f.hasTag(GENERATOR_TAG)) { - return super.execIdCall(f, cx, scope, thisObj, args); - } - int id = f.methodId(); - - NativeGenerator generator = ensureType(thisObj, NativeGenerator.class, f); - - switch (id) { - case Id_close: - // need to run any pending finally clauses - return generator.resume(cx, scope, GENERATOR_CLOSE, new GeneratorClosedException()); - - case Id_next: - // arguments to next() are ignored - generator.firstTime = false; - return generator.resume(cx, scope, GENERATOR_SEND, Undefined.instance); - - case Id_send: - { - Object arg = args.length > 0 ? args[0] : Undefined.instance; - if (generator.firstTime && !arg.equals(Undefined.instance)) { - throw ScriptRuntime.typeErrorById("msg.send.newborn"); - } - return generator.resume(cx, scope, GENERATOR_SEND, arg); - } - - case Id_throw: - return generator.resume( - cx, scope, GENERATOR_THROW, args.length > 0 ? args[0] : Undefined.instance); - - case Id___iterator__: - return thisObj; - - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - } - - private Object resume(Context cx, Scriptable scope, int operation, Object value) { - if (savedState == null) { - if (operation == GENERATOR_CLOSE) return Undefined.instance; - Object thrown; - if (operation == GENERATOR_THROW) { - thrown = value; - } else { - thrown = NativeIterator.getStopIterationObject(scope); - } - throw new JavaScriptException(thrown, lineSource, lineNumber); - } - try { - synchronized (this) { - // generator execution is necessarily single-threaded and - // non-reentrant. - // See https://bugzilla.mozilla.org/show_bug.cgi?id=349263 - if (locked) throw ScriptRuntime.typeErrorById("msg.already.exec.gen"); - locked = true; - } - return function.resumeGenerator(cx, scope, operation, savedState, value); - } catch (GeneratorClosedException e) { - // On closing a generator in the compile path, the generator - // throws a special exception. This ensures execution of all pending - // finalizers and will not get caught by user code. - return Undefined.instance; - } catch (RhinoException e) { - lineNumber = e.lineNumber(); - lineSource = e.lineSource(); - savedState = null; - throw e; - } finally { - synchronized (this) { - locked = false; - } - if (operation == GENERATOR_CLOSE) savedState = null; - } - } - - @Override - protected int findPrototypeId(String s) { - int id; - switch (s) { - case "close": - id = Id_close; - break; - case "next": - id = Id_next; - break; - case "send": - id = Id_send; - break; - case "throw": - id = Id_throw; - break; - case "__iterator__": - id = Id___iterator__; - break; - default: - id = 0; - break; - } - return id; - } - - private static final int Id_close = 1, - Id_next = 2, - Id_send = 3, - Id_throw = 4, - Id___iterator__ = 5, - MAX_PROTOTYPE_ID = 5; - - private NativeFunction function; - private Object savedState; - private String lineSource; - private int lineNumber; - private boolean firstTime = true; - private boolean locked; - - public static class GeneratorClosedException extends RuntimeException { - private static final long serialVersionUID = 2561315658662379681L; - } -} diff --git a/src/main/java/org/mozilla/javascript/NativeGlobal.java b/src/main/java/org/mozilla/javascript/NativeGlobal.java deleted file mode 100644 index c3d6e5eb..00000000 --- a/src/main/java/org/mozilla/javascript/NativeGlobal.java +++ /dev/null @@ -1,765 +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 static org.mozilla.javascript.ScriptableObject.DONTENUM; -import static org.mozilla.javascript.ScriptableObject.PERMANENT; -import static org.mozilla.javascript.ScriptableObject.READONLY; - -import java.io.Serializable; -import org.mozilla.javascript.xml.XMLLib; - -/** - * This class implements the global native object (function and value properties only). - * - *

See ECMA 15.1.[12]. - * - * @author Mike Shaver - */ -public class NativeGlobal implements Serializable, IdFunctionCall { - static final long serialVersionUID = 6080442165748707530L; - - public static void init(Context cx, Scriptable scope, boolean sealed) { - NativeGlobal obj = new NativeGlobal(); - - for (int id = 1; id <= LAST_SCOPE_FUNCTION_ID; ++id) { - String name; - int arity = 1; - switch (id) { - case Id_decodeURI: - name = "decodeURI"; - break; - case Id_decodeURIComponent: - name = "decodeURIComponent"; - break; - case Id_encodeURI: - name = "encodeURI"; - break; - case Id_encodeURIComponent: - name = "encodeURIComponent"; - break; - case Id_escape: - name = "escape"; - break; - case Id_eval: - name = "eval"; - break; - case Id_isFinite: - name = "isFinite"; - break; - case Id_isNaN: - name = "isNaN"; - break; - case Id_isXMLName: - name = "isXMLName"; - break; - case Id_parseFloat: - name = "parseFloat"; - break; - case Id_parseInt: - name = "parseInt"; - arity = 2; - break; - case Id_unescape: - name = "unescape"; - break; - case Id_uneval: - name = "uneval"; - break; - default: - throw Kit.codeBug(); - } - IdFunctionObject f = new IdFunctionObject(obj, FTAG, id, name, arity, scope); - if (sealed) { - f.sealObject(); - } - f.exportAsScopeProperty(); - } - - ScriptableObject.defineProperty( - scope, "NaN", ScriptRuntime.NaNobj, READONLY | DONTENUM | PERMANENT); - ScriptableObject.defineProperty( - scope, - "Infinity", - ScriptRuntime.wrapNumber(Double.POSITIVE_INFINITY), - READONLY | DONTENUM | PERMANENT); - ScriptableObject.defineProperty( - scope, "undefined", Undefined.instance, READONLY | DONTENUM | PERMANENT); - ScriptableObject.defineProperty(scope, "globalThis", scope, DONTENUM); - - /* - Each error constructor gets its own Error object as a prototype, - with the 'name' property set to the name of the error. - */ - Scriptable nativeError = - ScriptableObject.ensureScriptable(ScriptableObject.getProperty(scope, "Error")); - Scriptable nativeErrorProto = - ScriptableObject.ensureScriptable( - ScriptableObject.getProperty(nativeError, "prototype")); - - for (TopLevel.NativeErrors error : TopLevel.NativeErrors.values()) { - if (error == TopLevel.NativeErrors.Error) { - // Error is initialized elsewhere and we should not overwrite it. - continue; - } - String name = error.name(); - ScriptableObject errorProto = - (ScriptableObject) - ScriptRuntime.newBuiltinObject( - cx, scope, TopLevel.Builtins.Error, ScriptRuntime.emptyArgs); - errorProto.defineProperty("name", name, DONTENUM); - errorProto.defineProperty("message", "", DONTENUM); - IdFunctionObject ctor = - new IdFunctionObject(obj, FTAG, Id_new_CommonError, name, 1, scope); - ctor.markAsConstructor(errorProto); - ctor.setPrototype(nativeError); - errorProto.put("constructor", errorProto, ctor); - errorProto.setAttributes("constructor", ScriptableObject.DONTENUM); - errorProto.setPrototype(nativeErrorProto); - if (sealed) { - errorProto.sealObject(); - ctor.sealObject(); - } - ctor.setAttributes("name", DONTENUM | READONLY); - ctor.setAttributes("length", DONTENUM | READONLY); - ctor.exportAsScopeProperty(); - } - } - - @Override - public Object execIdCall( - IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (f.hasTag(FTAG)) { - int methodId = f.methodId(); - switch (methodId) { - case Id_decodeURI: - case Id_decodeURIComponent: - { - String str = ScriptRuntime.toString(args, 0); - return decode(str, methodId == Id_decodeURI); - } - - case Id_encodeURI: - case Id_encodeURIComponent: - { - String str = ScriptRuntime.toString(args, 0); - return encode(str, methodId == Id_encodeURI); - } - - case Id_escape: - return js_escape(args); - - case Id_eval: - return js_eval(cx, scope, args); - - case Id_isFinite: - { - if (args.length < 1) { - return Boolean.FALSE; - } - return NativeNumber.isFinite(args[0]); - } - - case Id_isNaN: - { - // The global method isNaN, as per ECMA-262 15.1.2.6. - boolean result; - if (args.length < 1) { - result = true; - } else { - double d = ScriptRuntime.toNumber(args[0]); - result = Double.isNaN(d); - } - return ScriptRuntime.wrapBoolean(result); - } - - case Id_isXMLName: - { - Object name = (args.length == 0) ? Undefined.instance : args[0]; - XMLLib xmlLib = XMLLib.extractFromScope(scope); - return ScriptRuntime.wrapBoolean(xmlLib.isXMLName(cx, name)); - } - - case Id_parseFloat: - return js_parseFloat(args); - - case Id_parseInt: - return js_parseInt(args); - - case Id_unescape: - return js_unescape(args); - - case Id_uneval: - { - Object value = (args.length != 0) ? args[0] : Undefined.instance; - return ScriptRuntime.uneval(cx, scope, value); - } - - case Id_new_CommonError: - // The implementation of all the ECMA error constructors - // (SyntaxError, TypeError, etc.) - return NativeError.make(cx, scope, f, args); - } - } - throw f.unknown(); - } - - /** The global method parseInt, as per ECMA-262 15.1.2.2. */ - static Object js_parseInt(Object[] args) { - String s = ScriptRuntime.toString(args, 0); - int radix = ScriptRuntime.toInt32(args, 1); - - int len = s.length(); - if (len == 0) return ScriptRuntime.NaNobj; - - boolean negative = false; - int start = 0; - char c; - do { - c = s.charAt(start); - if (!ScriptRuntime.isStrWhiteSpaceChar(c)) break; - start++; - } while (start < len); - - if (c == '+' || (negative = (c == '-'))) start++; - - final int NO_RADIX = -1; - if (radix == 0) { - radix = NO_RADIX; - } else if (radix < 2 || radix > 36) { - return ScriptRuntime.NaNobj; - } else if (radix == 16 && len - start > 1 && s.charAt(start) == '0') { - c = s.charAt(start + 1); - if (c == 'x' || c == 'X') start += 2; - } - - if (radix == NO_RADIX) { - radix = 10; - if (len - start > 1 && s.charAt(start) == '0') { - c = s.charAt(start + 1); - if (c == 'x' || c == 'X') { - radix = 16; - start += 2; - } else if ('0' <= c && c <= '9') { - Context cx = Context.getCurrentContext(); - if (cx == null || cx.getLanguageVersion() < Context.VERSION_1_5) { - radix = 8; - start++; - } - } - } - } - - double d = ScriptRuntime.stringPrefixToNumber(s, start, radix); - return ScriptRuntime.wrapNumber(negative ? -d : d); - } - - /** - * The global method parseFloat, as per ECMA-262 15.1.2.3. - * - * @param args the arguments to parseFloat, ignoring args[>=1] - */ - static Object js_parseFloat(Object[] args) { - if (args.length < 1) return ScriptRuntime.NaNobj; - - String s = ScriptRuntime.toString(args[0]); - int len = s.length(); - int start = 0; - // Scan forward to skip whitespace - char c; - for (; ; ) { - if (start == len) { - return ScriptRuntime.NaNobj; - } - c = s.charAt(start); - if (!ScriptRuntime.isStrWhiteSpaceChar(c)) { - break; - } - ++start; - } - - int i = start; - if (c == '+' || c == '-') { - ++i; - if (i == len) { - return ScriptRuntime.NaNobj; - } - c = s.charAt(i); - } - - if (c == 'I') { - // check for "Infinity" - if (i + 8 <= len && s.regionMatches(i, "Infinity", 0, 8)) { - double d; - if (s.charAt(start) == '-') { - d = Double.NEGATIVE_INFINITY; - } else { - d = Double.POSITIVE_INFINITY; - } - return ScriptRuntime.wrapNumber(d); - } - return ScriptRuntime.NaNobj; - } - - // Find the end of the legal bit - int decimal = -1; - int exponent = -1; - boolean exponentValid = false; - for (; i < len; i++) { - switch (s.charAt(i)) { - case '.': - if (decimal != -1) // Only allow a single decimal point. - break; - decimal = i; - continue; - - case 'e': - case 'E': - if (exponent != -1) { - break; - } else if (i == len - 1) { - break; - } - exponent = i; - continue; - - case '+': - case '-': - // Only allow '+' or '-' after 'e' or 'E' - if (exponent != i - 1) { - break; - } else if (i == len - 1) { - --i; - break; - } - continue; - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (exponent != -1) { - exponentValid = true; - } - continue; - - default: - break; - } - break; - } - if (exponent != -1 && !exponentValid) { - i = exponent; - } - s = s.substring(start, i); - try { - return Double.valueOf(s); - } catch (NumberFormatException ex) { - return ScriptRuntime.NaNobj; - } - } - - /** - * The global method escape, as per ECMA-262 15.1.2.4. - * - *

Includes code for the 'mask' argument supported by the C escape method, which used to be - * part of the browser embedding. Blame for the strange constant names should be directed there. - */ - private static Object js_escape(Object[] args) { - final int URL_XALPHAS = 1, URL_XPALPHAS = 2, URL_PATH = 4; - - String s = ScriptRuntime.toString(args, 0); - - int mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH; - if (args.length > 1) { // the 'mask' argument. Non-ECMA. - double d = ScriptRuntime.toNumber(args[1]); - if (Double.isNaN(d) - || ((mask = (int) d) != d) - || 0 != (mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH))) { - throw Context.reportRuntimeErrorById("msg.bad.esc.mask"); - } - } - - StringBuilder sb = null; - for (int k = 0, L = s.length(); k != L; ++k) { - int c = s.charAt(k); - if (mask != 0 - && ((c >= '0' && c <= '9') - || (c >= 'A' && c <= 'Z') - || (c >= 'a' && c <= 'z') - || c == '@' - || c == '*' - || c == '_' - || c == '-' - || c == '.' - || (0 != (mask & URL_PATH) && (c == '/' || c == '+')))) { - if (sb != null) { - sb.append((char) c); - } - } else { - if (sb == null) { - sb = new StringBuilder(L + 3); - sb.append(s); - sb.setLength(k); - } - - int hexSize; - if (c < 256) { - if (c == ' ' && mask == URL_XPALPHAS) { - sb.append('+'); - continue; - } - sb.append('%'); - hexSize = 2; - } else { - sb.append('%'); - sb.append('u'); - hexSize = 4; - } - - // append hexadecimal form of c left-padded with 0 - for (int shift = (hexSize - 1) * 4; shift >= 0; shift -= 4) { - int digit = 0xf & (c >> shift); - int hc = (digit < 10) ? '0' + digit : 'A' - 10 + digit; - sb.append((char) hc); - } - } - } - - return (sb == null) ? s : sb.toString(); - } - - /** The global unescape method, as per ECMA-262 15.1.2.5. */ - private static Object js_unescape(Object[] args) { - String s = ScriptRuntime.toString(args, 0); - int firstEscapePos = s.indexOf('%'); - if (firstEscapePos >= 0) { - int L = s.length(); - char[] buf = s.toCharArray(); - int destination = firstEscapePos; - for (int k = firstEscapePos; k != L; ) { - char c = buf[k]; - ++k; - if (c == '%' && k != L) { - int end, start; - if (buf[k] == 'u') { - start = k + 1; - end = k + 5; - } else { - start = k; - end = k + 2; - } - if (end <= L) { - int x = 0; - for (int i = start; i != end; ++i) { - x = Kit.xDigitToInt(buf[i], x); - } - if (x >= 0) { - c = (char) x; - k = end; - } - } - } - buf[destination] = c; - ++destination; - } - s = new String(buf, 0, destination); - } - return s; - } - - /** - * This is an indirect call to eval, and thus uses the global environment. Direct calls are - * executed via ScriptRuntime.callSpecial(). - */ - private static Object js_eval(Context cx, Scriptable scope, Object[] args) { - Scriptable global = ScriptableObject.getTopLevelScope(scope); - return ScriptRuntime.evalSpecial(cx, global, global, args, "eval code", 1); - } - - static boolean isEvalFunction(Object functionObj) { - if (functionObj instanceof IdFunctionObject) { - IdFunctionObject function = (IdFunctionObject) functionObj; - if (function.hasTag(FTAG) && function.methodId() == Id_eval) { - return true; - } - } - return false; - } - - /** @deprecated Use {@link ScriptRuntime#constructError(String,String)} instead. */ - @Deprecated - public static EcmaError constructError( - Context cx, String error, String message, Scriptable scope) { - return ScriptRuntime.constructError(error, message); - } - - /** - * @deprecated Use {@link ScriptRuntime#constructError(String,String,String,int,String,int)} - * instead. - */ - @Deprecated - public static EcmaError constructError( - Context cx, - String error, - String message, - Scriptable scope, - String sourceName, - int lineNumber, - int columnNumber, - String lineSource) { - return ScriptRuntime.constructError( - error, message, - sourceName, lineNumber, - lineSource, columnNumber); - } - - /* - * ECMA 3, 15.1.3 URI Handling Function Properties - * - * The following are implementations of the algorithms - * given in the ECMA specification for the hidden functions - * 'Encode' and 'Decode'. - */ - private static String encode(String str, boolean fullUri) { - byte[] utf8buf = null; - StringBuilder sb = null; - - for (int k = 0, length = str.length(); k != length; ++k) { - char C = str.charAt(k); - if (encodeUnescaped(C, fullUri)) { - if (sb != null) { - sb.append(C); - } - } else { - if (sb == null) { - sb = new StringBuilder(length + 3); - sb.append(str); - sb.setLength(k); - utf8buf = new byte[6]; - } - if (0xDC00 <= C && C <= 0xDFFF) { - throw uriError(); - } - int V; - if (C < 0xD800 || 0xDBFF < C) { - V = C; - } else { - k++; - if (k == length) { - throw uriError(); - } - char C2 = str.charAt(k); - if (!(0xDC00 <= C2 && C2 <= 0xDFFF)) { - throw uriError(); - } - V = ((C - 0xD800) << 10) + (C2 - 0xDC00) + 0x10000; - } - int L = oneUcs4ToUtf8Char(utf8buf, V); - for (int j = 0; j < L; j++) { - int d = 0xff & utf8buf[j]; - sb.append('%'); - sb.append(toHexChar(d >>> 4)); - sb.append(toHexChar(d & 0xf)); - } - } - } - return (sb == null) ? str : sb.toString(); - } - - private static char toHexChar(int i) { - if (i >> 4 != 0) Kit.codeBug(); - return (char) ((i < 10) ? i + '0' : i - 10 + 'A'); - } - - private static int unHex(char c) { - if ('A' <= c && c <= 'F') { - return c - 'A' + 10; - } else if ('a' <= c && c <= 'f') { - return c - 'a' + 10; - } else if ('0' <= c && c <= '9') { - return c - '0'; - } else { - return -1; - } - } - - private static int unHex(char c1, char c2) { - int i1 = unHex(c1); - int i2 = unHex(c2); - if (i1 >= 0 && i2 >= 0) { - return (i1 << 4) | i2; - } - return -1; - } - - private static String decode(String str, boolean fullUri) { - char[] buf = null; - int bufTop = 0; - - for (int k = 0, length = str.length(); k != length; ) { - char C = str.charAt(k); - if (C != '%') { - if (buf != null) { - buf[bufTop++] = C; - } - ++k; - } else { - if (buf == null) { - // decode always compress so result can not be bigger then - // str.length() - buf = new char[length]; - str.getChars(0, k, buf, 0); - bufTop = k; - } - int start = k; - if (k + 3 > length) throw uriError(); - int B = unHex(str.charAt(k + 1), str.charAt(k + 2)); - if (B < 0) throw uriError(); - k += 3; - if ((B & 0x80) == 0) { - C = (char) B; - } else { - // Decode UTF-8 sequence into ucs4Char and encode it into - // UTF-16 - int utf8Tail, ucs4Char, minUcs4Char; - if ((B & 0xC0) == 0x80) { - // First UTF-8 should be ouside 0x80..0xBF - throw uriError(); - } else if ((B & 0x20) == 0) { - utf8Tail = 1; - ucs4Char = B & 0x1F; - minUcs4Char = 0x80; - } else if ((B & 0x10) == 0) { - utf8Tail = 2; - ucs4Char = B & 0x0F; - minUcs4Char = 0x800; - } else if ((B & 0x08) == 0) { - utf8Tail = 3; - ucs4Char = B & 0x07; - minUcs4Char = 0x10000; - } else if ((B & 0x04) == 0) { - utf8Tail = 4; - ucs4Char = B & 0x03; - minUcs4Char = 0x200000; - } else if ((B & 0x02) == 0) { - utf8Tail = 5; - ucs4Char = B & 0x01; - minUcs4Char = 0x4000000; - } else { - // First UTF-8 can not be 0xFF or 0xFE - throw uriError(); - } - if (k + 3 * utf8Tail > length) throw uriError(); - for (int j = 0; j != utf8Tail; j++) { - if (str.charAt(k) != '%') throw uriError(); - B = unHex(str.charAt(k + 1), str.charAt(k + 2)); - if (B < 0 || (B & 0xC0) != 0x80) throw uriError(); - ucs4Char = (ucs4Char << 6) | (B & 0x3F); - k += 3; - } - // Check for overlongs and other should-not-present codes - if (ucs4Char < minUcs4Char || (ucs4Char >= 0xD800 && ucs4Char <= 0xDFFF)) { - ucs4Char = INVALID_UTF8; - } else if (ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) { - ucs4Char = 0xFFFD; - } - if (ucs4Char >= 0x10000) { - ucs4Char -= 0x10000; - if (ucs4Char > 0xFFFFF) { - throw uriError(); - } - char H = (char) ((ucs4Char >>> 10) + 0xD800); - C = (char) ((ucs4Char & 0x3FF) + 0xDC00); - buf[bufTop++] = H; - } else { - C = (char) ucs4Char; - } - } - if (fullUri && URI_DECODE_RESERVED.indexOf(C) >= 0) { - for (int x = start; x != k; x++) { - buf[bufTop++] = str.charAt(x); - } - } else { - buf[bufTop++] = C; - } - } - } - return (buf == null) ? str : new String(buf, 0, bufTop); - } - - private static boolean encodeUnescaped(char c, boolean fullUri) { - if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || ('0' <= c && c <= '9')) { - return true; - } - if ("-_.!~*'()".indexOf(c) >= 0) { - return true; - } - if (fullUri) { - return URI_DECODE_RESERVED.indexOf(c) >= 0; - } - return false; - } - - private static EcmaError uriError() { - return ScriptRuntime.constructError( - "URIError", ScriptRuntime.getMessageById("msg.bad.uri")); - } - - private static final String URI_DECODE_RESERVED = ";/?:@&=+$,#"; - private static final int INVALID_UTF8 = Integer.MAX_VALUE; - - /* Convert one UCS-4 char and write it into a UTF-8 buffer, which must be - * at least 6 bytes long. Return the number of UTF-8 bytes of data written. - */ - private static int oneUcs4ToUtf8Char(byte[] utf8Buffer, int ucs4Char) { - int utf8Length = 1; - - // JS_ASSERT(ucs4Char <= 0x7FFFFFFF); - if ((ucs4Char & ~0x7F) == 0) utf8Buffer[0] = (byte) ucs4Char; - else { - int i; - int a = ucs4Char >>> 11; - utf8Length = 2; - while (a != 0) { - a >>>= 5; - utf8Length++; - } - i = utf8Length; - while (--i > 0) { - utf8Buffer[i] = (byte) ((ucs4Char & 0x3F) | 0x80); - ucs4Char >>>= 6; - } - utf8Buffer[0] = (byte) (0x100 - (1 << (8 - utf8Length)) + ucs4Char); - } - return utf8Length; - } - - private static final Object FTAG = "Global"; - - private static final int Id_decodeURI = 1, - Id_decodeURIComponent = 2, - Id_encodeURI = 3, - Id_encodeURIComponent = 4, - Id_escape = 5, - Id_eval = 6, - Id_isFinite = 7, - Id_isNaN = 8, - Id_isXMLName = 9, - Id_parseFloat = 10, - Id_parseInt = 11, - Id_unescape = 12, - Id_uneval = 13, - LAST_SCOPE_FUNCTION_ID = 13, - Id_new_CommonError = 14; -} diff --git a/src/main/java/org/mozilla/javascript/NativeIterator.java b/src/main/java/org/mozilla/javascript/NativeIterator.java deleted file mode 100644 index 4f5747c9..00000000 --- a/src/main/java/org/mozilla/javascript/NativeIterator.java +++ /dev/null @@ -1,279 +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.Iterator; - -/** - * This class implements iterator objects. See - * http://developer.mozilla.org/en/docs/New_in_JavaScript_1.7#Iterators - * - * @author Norris Boyd - */ -public final class NativeIterator extends IdScriptableObject { - private static final long serialVersionUID = -4136968203581667681L; - private static final Object ITERATOR_TAG = "Iterator"; - - static void init(Context cx, ScriptableObject scope, boolean sealed) { - // Iterator - NativeIterator iterator = new NativeIterator(); - iterator.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); - - // Generator - if (cx.getLanguageVersion() >= Context.VERSION_ES6) { - ES6Generator.init(scope, sealed); - } else { - NativeGenerator.init(scope, sealed); - } - - // StopIteration - NativeObject obj = new StopIteration(); - obj.setPrototype(getObjectPrototype(scope)); - obj.setParentScope(scope); - if (sealed) { - obj.sealObject(); - } - ScriptableObject.defineProperty(scope, STOP_ITERATION, obj, ScriptableObject.DONTENUM); - // Use "associateValue" so that generators can continue to - // throw StopIteration even if the property of the global - // scope is replaced or deleted. - scope.associateValue(ITERATOR_TAG, obj); - } - - /** Only for constructing the prototype object. */ - private NativeIterator() {} - - private NativeIterator(Object objectIterator) { - this.objectIterator = objectIterator; - } - - /** - * Get the value of the "StopIteration" object. Note that this value is stored in the top-level - * scope using "associateValue" so the value can still be found even if a script overwrites or - * deletes the global "StopIteration" property. - * - * @param scope a scope whose parent chain reaches a top-level scope - * @return the StopIteration object - */ - public static Object getStopIterationObject(Scriptable scope) { - Scriptable top = ScriptableObject.getTopLevelScope(scope); - return ScriptableObject.getTopScopeValue(top, ITERATOR_TAG); - } - - private static final String STOP_ITERATION = "StopIteration"; - public static final String ITERATOR_PROPERTY_NAME = "__iterator__"; - - public static class StopIteration extends NativeObject { - private static final long serialVersionUID = 2485151085722377663L; - - private Object value = Undefined.instance; - - public StopIteration() {} - - public StopIteration(Object val) { - this.value = val; - } - - public Object getValue() { - return value; - } - - @Override - public String getClassName() { - return STOP_ITERATION; - } - - /* StopIteration has custom instanceof behavior since it - * doesn't have a constructor. - */ - @Override - public boolean hasInstance(Scriptable instance) { - return instance instanceof StopIteration; - } - } - - @Override - public String getClassName() { - return "Iterator"; - } - - @Override - protected void initPrototypeId(int id) { - String s; - int arity; - switch (id) { - case Id_constructor: - arity = 2; - s = "constructor"; - break; - case Id_next: - arity = 0; - s = "next"; - break; - case Id___iterator__: - arity = 1; - s = ITERATOR_PROPERTY_NAME; - break; - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - initPrototypeMethod(ITERATOR_TAG, id, s, arity); - } - - @Override - public Object execIdCall( - IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!f.hasTag(ITERATOR_TAG)) { - return super.execIdCall(f, cx, scope, thisObj, args); - } - int id = f.methodId(); - - if (id == Id_constructor) { - return jsConstructor(cx, scope, thisObj, args); - } - - NativeIterator iterator = ensureType(thisObj, NativeIterator.class, f); - - switch (id) { - case Id_next: - return iterator.next(cx, scope); - - case Id___iterator__: - /// XXX: what about argument? SpiderMonkey apparently ignores it - return thisObj; - - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - } - - /* The JavaScript constructor */ - private static Object jsConstructor( - Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (args.length == 0 || args[0] == null || args[0] == Undefined.instance) { - Object argument = args.length == 0 ? Undefined.instance : args[0]; - throw ScriptRuntime.typeErrorById( - "msg.no.properties", ScriptRuntime.toString(argument)); - } - Scriptable obj = ScriptRuntime.toObject(cx, scope, args[0]); - boolean keyOnly = args.length > 1 && ScriptRuntime.toBoolean(args[1]); - if (thisObj != null) { - // Called as a function. Convert to iterator if possible. - - // For objects that implement java.lang.Iterable or - // java.util.Iterator, have JavaScript Iterator call the underlying - // iteration methods - Iterator iterator = getJavaIterator(obj); - if (iterator != null) { - scope = ScriptableObject.getTopLevelScope(scope); - return cx.getWrapFactory() - .wrap( - cx, - scope, - new WrappedJavaIterator(iterator, scope), - WrappedJavaIterator.class); - } - - // Otherwise, just call the runtime routine - Scriptable jsIterator = ScriptRuntime.toIterator(cx, scope, obj, keyOnly); - if (jsIterator != null) { - return jsIterator; - } - } - - // Otherwise, just set up to iterate over the properties of the object. - // Do not call __iterator__ method. - Object objectIterator = - ScriptRuntime.enumInit( - obj, - cx, - scope, - keyOnly - ? ScriptRuntime.ENUMERATE_KEYS_NO_ITERATOR - : ScriptRuntime.ENUMERATE_ARRAY_NO_ITERATOR); - ScriptRuntime.setEnumNumbers(objectIterator, true); - NativeIterator result = new NativeIterator(objectIterator); - result.setPrototype(ScriptableObject.getClassPrototype(scope, result.getClassName())); - result.setParentScope(scope); - return result; - } - - private Object next(Context cx, Scriptable scope) { - Boolean b = ScriptRuntime.enumNext(this.objectIterator); - if (!b.booleanValue()) { - // Out of values. Throw StopIteration. - throw new JavaScriptException(NativeIterator.getStopIterationObject(scope), null, 0); - } - return ScriptRuntime.enumId(this.objectIterator, cx); - } - - /** - * If "obj" is a java.util.Iterator or a java.lang.Iterable, return a wrapping as a JavaScript - * Iterator. Otherwise, return null. This method is in VMBridge since Iterable is a JDK 1.5 - * addition. - */ - private static Iterator getJavaIterator(Object obj) { - if (obj instanceof Wrapper) { - Object unwrapped = ((Wrapper) obj).unwrap(); - Iterator iterator = null; - if (unwrapped instanceof Iterator) iterator = (Iterator) unwrapped; - if (unwrapped instanceof Iterable) iterator = ((Iterable) unwrapped).iterator(); - return iterator; - } - return null; - } - - public static class WrappedJavaIterator { - WrappedJavaIterator(Iterator iterator, Scriptable scope) { - this.iterator = iterator; - this.scope = scope; - } - - public Object next() { - if (!iterator.hasNext()) { - // Out of values. Throw StopIteration. - throw new JavaScriptException( - NativeIterator.getStopIterationObject(scope), null, 0); - } - return iterator.next(); - } - - public Object __iterator__(boolean b) { - return this; - } - - private Iterator iterator; - private Scriptable scope; - } - - @Override - protected int findPrototypeId(String s) { - int id; - switch (s) { - case "constructor": - id = Id_constructor; - break; - case "next": - id = Id_next; - break; - case "__iterator__": - id = Id___iterator__; - break; - default: - id = 0; - break; - } - return id; - } - - private static final int Id_constructor = 1, - Id_next = 2, - Id___iterator__ = 3, - MAX_PROTOTYPE_ID = 3; - - private Object objectIterator; -} diff --git a/src/main/java/org/mozilla/javascript/NativeJSON.java b/src/main/java/org/mozilla/javascript/NativeJSON.java deleted file mode 100644 index 53e7f343..00000000 --- a/src/main/java/org/mozilla/javascript/NativeJSON.java +++ /dev/null @@ -1,629 +0,0 @@ -/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; 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.lang.reflect.Array; -import java.math.BigInteger; -import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.Map; -import java.util.Stack; -import org.mozilla.javascript.json.JsonParser; -import org.mozilla.javascript.xml.XMLObject; - -/** - * This class implements the JSON native object. See ECMA 15.12. - * - * @author Matthew Crumley, Raphael Speyer - */ -public final class NativeJSON extends IdScriptableObject { - private static final long serialVersionUID = -4567599697595654984L; - - private static final Object JSON_TAG = "JSON"; - - private static final int MAX_STRINGIFY_GAP_LENGTH = 10; - - static void init(Scriptable scope, boolean sealed) { - NativeJSON obj = new NativeJSON(); - obj.activatePrototypeMap(MAX_ID); - obj.setPrototype(getObjectPrototype(scope)); - obj.setParentScope(scope); - if (sealed) { - obj.sealObject(); - } - ScriptableObject.defineProperty(scope, "JSON", obj, ScriptableObject.DONTENUM); - } - - private NativeJSON() {} - - @Override - public String getClassName() { - return "JSON"; - } - - @Override - protected void initPrototypeId(int id) { - if (id <= LAST_METHOD_ID) { - String name; - int arity; - switch (id) { - case Id_toSource: - arity = 0; - name = "toSource"; - break; - case Id_parse: - arity = 2; - name = "parse"; - break; - case Id_stringify: - arity = 3; - name = "stringify"; - break; - default: - throw new IllegalStateException(String.valueOf(id)); - } - initPrototypeMethod(JSON_TAG, id, name, arity); - } else { - throw new IllegalStateException(String.valueOf(id)); - } - } - - @Override - public Object execIdCall( - IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!f.hasTag(JSON_TAG)) { - return super.execIdCall(f, cx, scope, thisObj, args); - } - int methodId = f.methodId(); - switch (methodId) { - case Id_toSource: - return "JSON"; - - case Id_parse: - { - String jtext = ScriptRuntime.toString(args, 0); - Object reviver = null; - if (args.length > 1) { - reviver = args[1]; - } - if (reviver instanceof Callable) { - return parse(cx, scope, jtext, (Callable) reviver); - } - return parse(cx, scope, jtext); - } - - case Id_stringify: - { - Object value = Undefined.instance, replacer = null, space = null; - - if (args.length > 0) { - value = args[0]; - if (args.length > 1) { - replacer = args[1]; - if (args.length > 2) { - space = args[2]; - } - } - } - - return stringify(cx, scope, value, replacer, space); - } - - default: - throw new IllegalStateException(String.valueOf(methodId)); - } - } - - private static Object parse(Context cx, Scriptable scope, String jtext) { - try { - return new JsonParser(cx, scope).parseValue(jtext); - } catch (JsonParser.ParseException ex) { - throw ScriptRuntime.constructError("SyntaxError", ex.getMessage()); - } - } - - public static Object parse(Context cx, Scriptable scope, String jtext, Callable reviver) { - Object unfiltered = parse(cx, scope, jtext); - Scriptable root = cx.newObject(scope); - root.put("", root, unfiltered); - return walk(cx, scope, reviver, root, ""); - } - - private static Object walk( - Context cx, Scriptable scope, Callable reviver, Scriptable holder, Object name) { - final Object property; - if (name instanceof Number) { - property = holder.get(((Number) name).intValue(), holder); - } else { - property = holder.get(((String) name), holder); - } - - if (property instanceof Scriptable) { - Scriptable val = ((Scriptable) property); - if (val instanceof NativeArray) { - long len = ((NativeArray) val).getLength(); - for (long i = 0; i < len; i++) { - // indices greater than MAX_INT are represented as strings - if (i > Integer.MAX_VALUE) { - String id = Long.toString(i); - Object newElement = walk(cx, scope, reviver, val, id); - if (newElement == Undefined.instance) { - val.delete(id); - } else { - val.put(id, val, newElement); - } - } else { - int idx = (int) i; - Object newElement = walk(cx, scope, reviver, val, Integer.valueOf(idx)); - if (newElement == Undefined.instance) { - val.delete(idx); - } else { - val.put(idx, val, newElement); - } - } - } - } else { - Object[] keys = val.getIds(); - for (Object p : keys) { - Object newElement = walk(cx, scope, reviver, val, p); - if (newElement == Undefined.instance) { - if (p instanceof Number) val.delete(((Number) p).intValue()); - else val.delete((String) p); - } else { - if (p instanceof Number) val.put(((Number) p).intValue(), val, newElement); - else val.put((String) p, val, newElement); - } - } - } - } - - return reviver.call(cx, scope, holder, new Object[] {name, property}); - } - - private static String repeat(char c, int count) { - char chars[] = new char[count]; - Arrays.fill(chars, c); - return new String(chars); - } - - private static class StringifyState { - StringifyState( - Context cx, - Scriptable scope, - String indent, - String gap, - Callable replacer, - Object[] propertyList) { - this.cx = cx; - this.scope = scope; - - this.indent = indent; - this.gap = gap; - this.replacer = replacer; - this.propertyList = propertyList; - } - - Stack stack = new Stack(); - String indent; - String gap; - Callable replacer; - Object[] propertyList; - - Context cx; - Scriptable scope; - } - - public static Object stringify( - Context cx, Scriptable scope, Object value, Object replacer, Object space) { - String indent = ""; - String gap = ""; - - Object[] propertyList = null; - Callable replacerFunction = null; - - if (replacer instanceof Callable) { - replacerFunction = (Callable) replacer; - } else if (replacer instanceof NativeArray) { - LinkedHashSet propertySet = new LinkedHashSet(); - NativeArray replacerArray = (NativeArray) replacer; - for (int i : replacerArray.getIndexIds()) { - Object v = replacerArray.get(i, replacerArray); - if (v instanceof String) { - propertySet.add(v); - } else if (v instanceof Number - || v instanceof NativeString - || v instanceof NativeNumber) { - // TODO: This should also apply to subclasses of NativeString and NativeNumber - // once the class, extends, and super keywords are implemented - propertySet.add(ScriptRuntime.toString(v)); - } - } - // After items have been converted to strings and duplicates removed, transform to an - // array and convert indexed keys back to Integers as required for later processing - propertyList = new Object[propertySet.size()]; - int i = 0; - for (Object prop : propertySet) { - ScriptRuntime.StringIdOrIndex idOrIndex = ScriptRuntime.toStringIdOrIndex(cx, prop); - // This will always be a String or Integer - propertyList[i++] = - (idOrIndex.stringId == null) ? idOrIndex.index : idOrIndex.stringId; - } - } - - if (space instanceof NativeNumber) { - space = Double.valueOf(ScriptRuntime.toNumber(space)); - } else if (space instanceof NativeString) { - space = ScriptRuntime.toString(space); - } - - if (space instanceof Number) { - int gapLength = (int) ScriptRuntime.toInteger(space); - gapLength = Math.min(MAX_STRINGIFY_GAP_LENGTH, gapLength); - gap = (gapLength > 0) ? repeat(' ', gapLength) : ""; - } else if (space instanceof String) { - gap = (String) space; - if (gap.length() > MAX_STRINGIFY_GAP_LENGTH) { - gap = gap.substring(0, MAX_STRINGIFY_GAP_LENGTH); - } - } - - StringifyState state = - new StringifyState(cx, scope, indent, gap, replacerFunction, propertyList); - - ScriptableObject wrapper = new NativeObject(); - wrapper.setParentScope(scope); - wrapper.setPrototype(ScriptableObject.getObjectPrototype(scope)); - wrapper.defineProperty("", value, 0); - return str("", wrapper, state); - } - - private static Object str(Object key, Scriptable holder, StringifyState state) { - Object value = null; - Object unwrappedJavaValue = null; - - if (key instanceof String) { - value = getProperty(holder, (String) key); - } else { - value = getProperty(holder, ((Number) key).intValue()); - } - - if (value instanceof Scriptable && hasProperty((Scriptable) value, "toJSON")) { - Object toJSON = getProperty((Scriptable) value, "toJSON"); - if (toJSON instanceof Callable) { - value = callMethod(state.cx, (Scriptable) value, "toJSON", new Object[] {key}); - } - } else if (value instanceof BigInteger) { - Scriptable bigInt = ScriptRuntime.toObject(state.cx, state.scope, value); - if (hasProperty(bigInt, "toJSON")) { - Object toJSON = getProperty(bigInt, "toJSON"); - if (toJSON instanceof Callable) { - value = callMethod(state.cx, bigInt, "toJSON", new Object[] {key}); - } - } - } - - if (state.replacer != null) { - value = state.replacer.call(state.cx, state.scope, holder, new Object[] {key, value}); - } - - if (value instanceof NativeNumber) { - value = Double.valueOf(ScriptRuntime.toNumber(value)); - } else if (value instanceof NativeString) { - value = ScriptRuntime.toString(value); - } else if (value instanceof NativeBoolean) { - value = ((NativeBoolean) value).getDefaultValue(ScriptRuntime.BooleanClass); - } else if (state.cx.getLanguageVersion() >= Context.VERSION_ES6 - && value instanceof NativeBigInt) { - value = ((NativeBigInt) value).getDefaultValue(ScriptRuntime.BigIntegerClass); - } else if (value instanceof NativeJavaObject) { - unwrappedJavaValue = ((NativeJavaObject) value).unwrap(); - if (!(unwrappedJavaValue instanceof Map - || unwrappedJavaValue instanceof Collection - || unwrappedJavaValue.getClass().isArray())) { - value = unwrappedJavaValue; - } else { - // Don't unwrap Java objects to be processed by jo() or ja() - unwrappedJavaValue = null; - } - } else if (value instanceof XMLObject) { - value = ((XMLObject) value).toString(); - } - - if (value == null) return "null"; - if (value.equals(Boolean.TRUE)) return "true"; - if (value.equals(Boolean.FALSE)) return "false"; - - if (value instanceof CharSequence) { - return quote(value.toString()); - } - - if (value instanceof Number) { - if (value instanceof BigInteger) { - throw ScriptRuntime.typeErrorById("msg.json.cant.serialize", "BigInt"); - } - double d = ((Number) value).doubleValue(); - if (!Double.isNaN(d) - && d != Double.POSITIVE_INFINITY - && d != Double.NEGATIVE_INFINITY) { - return ScriptRuntime.toString(value); - } - return "null"; - } - - if (unwrappedJavaValue != null) { - return javaToJSON(value, state); - } - - if ((value instanceof Scriptable) && !(value instanceof Callable)) { - if (isObjectArrayLike(value)) { - return ja((Scriptable) value, state); - } - return jo((Scriptable) value, state); - } - return Undefined.instance; - } - - private static String join(Collection objs, String delimiter) { - if (objs == null || objs.isEmpty()) { - return ""; - } - Iterator iter = objs.iterator(); - if (!iter.hasNext()) return ""; - StringBuilder builder = new StringBuilder(iter.next().toString()); - while (iter.hasNext()) { - builder.append(delimiter).append(iter.next()); - } - return builder.toString(); - } - - private static String jo(Scriptable value, StringifyState state) { - Object trackValue = value, unwrapped = null; - if (value instanceof Wrapper) { - trackValue = unwrapped = ((Wrapper) value).unwrap(); - } - - if (state.stack.search(trackValue) != -1) { - throw ScriptRuntime.typeErrorById("msg.cyclic.value", trackValue.getClass().getName()); - } - state.stack.push(trackValue); - - if (unwrapped instanceof Map) { - Map map = (Map) unwrapped; - Scriptable nObj = state.cx.newObject(state.scope); - for (Map.Entry entry : map.entrySet()) { - if (entry.getKey() instanceof Symbol) continue; - Object wrappedValue = Context.javaToJS(entry.getValue(), state.scope, state.cx); - int attributes; - String key; - if (entry.getKey() instanceof String) { - // Keys that are actually Strings are permanent and will not be - // overridden by other objects having the same toString value. - key = (String) entry.getKey(); - attributes = ScriptableObject.READONLY | ScriptableObject.PERMANENT; - } else { - // To avoid duplicate keys in JSON, replace previous key having the same - // toString value as the current object. - key = entry.getKey().toString(); - attributes = ScriptableObject.EMPTY; - } - try { - ScriptableObject.defineProperty(nObj, key, wrappedValue, attributes); - } catch (EcmaError error) { - // ignore TypeErrors if we tried to rewrite the property for a - // String key. - } - } - value = nObj; - } - - String stepback = state.indent; - state.indent = state.indent + state.gap; - Object[] k = null; - if (state.propertyList != null) { - k = state.propertyList; - } else { - k = value.getIds(); - } - - Collection partial = new LinkedList(); - - for (Object p : k) { - Object strP = str(p, value, state); - if (strP != Undefined.instance) { - String member = quote(p.toString()) + ":"; - if (state.gap.length() > 0) { - member = member + " "; - } - member = member + strP; - partial.add(member); - } - } - - final String finalValue; - - if (partial.isEmpty()) { - finalValue = "{}"; - } else { - if (state.gap.length() == 0) { - finalValue = '{' + join(partial, ",") + '}'; - } else { - String separator = ",\n" + state.indent; - String properties = join(partial, separator); - finalValue = "{\n" + state.indent + properties + '\n' + stepback + '}'; - } - } - - state.stack.pop(); - state.indent = stepback; - return finalValue; - } - - private static String ja(Scriptable value, StringifyState state) { - Object trackValue = value, unwrapped = null; - if (value instanceof Wrapper) { - trackValue = unwrapped = ((Wrapper) value).unwrap(); - } - if (state.stack.search(trackValue) != -1) { - throw ScriptRuntime.typeErrorById("msg.cyclic.value", trackValue.getClass().getName()); - } - state.stack.push(trackValue); - - String stepback = state.indent; - state.indent = state.indent + state.gap; - Collection partial = new LinkedList(); - - if (unwrapped != null) { - Object[] elements = null; - if (unwrapped.getClass().isArray()) { - int length = Array.getLength(unwrapped); - elements = new Object[length]; - for (int i = 0; i < length; i++) { - elements[i] = Context.javaToJS(Array.get(unwrapped, i), state.scope, state.cx); - } - } else if (unwrapped instanceof Collection) { - Collection collection = (Collection) unwrapped; - elements = new Object[collection.size()]; - int i = 0; - for (Object o : collection) { - elements[i++] = Context.javaToJS(o, state.scope, state.cx); - } - } - if (elements != null) { - value = state.cx.newArray(state.scope, elements); - } - } - - long len = ((NativeArray) value).getLength(); - - for (long index = 0; index < len; index++) { - Object strP; - if (index > Integer.MAX_VALUE) { - strP = str(Long.toString(index), value, state); - } else { - strP = str(Integer.valueOf((int) index), value, state); - } - if (strP == Undefined.instance) { - partial.add("null"); - } else { - partial.add(strP); - } - } - - final String finalValue; - - if (partial.isEmpty()) { - finalValue = "[]"; - } else { - if (state.gap.length() == 0) { - finalValue = '[' + join(partial, ",") + ']'; - } else { - String separator = ",\n" + state.indent; - String properties = join(partial, separator); - finalValue = "[\n" + state.indent + properties + '\n' + stepback + ']'; - } - } - - state.stack.pop(); - state.indent = stepback; - return finalValue; - } - - private static String quote(String string) { - StringBuilder product = - new StringBuilder(string.length() + 2); // two extra chars for " on either side - product.append('"'); - int length = string.length(); - for (int i = 0; i < length; i++) { - char c = string.charAt(i); - switch (c) { - case '"': - product.append("\\\""); - break; - case '\\': - product.append("\\\\"); - break; - case '\b': - product.append("\\b"); - break; - case '\f': - product.append("\\f"); - break; - case '\n': - product.append("\\n"); - break; - case '\r': - product.append("\\r"); - break; - case '\t': - product.append("\\t"); - break; - default: - if (c < ' ') { - product.append("\\u"); - String hex = String.format("%04x", Integer.valueOf(c)); - product.append(hex); - } else { - product.append(c); - } - break; - } - } - product.append('"'); - return product.toString(); - } - - private static Object javaToJSON(Object value, StringifyState state) { - value = state.cx.getJavaToJSONConverter().apply(value); - value = Context.javaToJS(value, state.scope, state.cx); - - ScriptableObject wrapper = new NativeObject(); - wrapper.setParentScope(state.scope); - wrapper.setPrototype(ScriptableObject.getObjectPrototype(state.scope)); - wrapper.defineProperty("", value, 0); - return str("", wrapper, state); - } - - private static boolean isObjectArrayLike(Object o) { - if (o instanceof NativeArray) { - return true; - } - if (o instanceof NativeJavaObject) { - Object unwrapped = ((NativeJavaObject) o).unwrap(); - return (unwrapped instanceof Collection) || (unwrapped.getClass().isArray()); - } - return false; - } - - @Override - protected int findPrototypeId(String s) { - int id; - switch (s) { - case "toSource": - id = Id_toSource; - break; - case "parse": - id = Id_parse; - break; - case "stringify": - id = Id_stringify; - break; - default: - id = 0; - break; - } - return id; - } - - private static final int Id_toSource = 1, - Id_parse = 2, - Id_stringify = 3, - LAST_METHOD_ID = 3, - MAX_ID = 3; -} diff --git a/src/main/java/org/mozilla/javascript/NativeJavaArray.java b/src/main/java/org/mozilla/javascript/NativeJavaArray.java deleted file mode 100644 index c7c36264..00000000 --- a/src/main/java/org/mozilla/javascript/NativeJavaArray.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.javascript; - -import java.lang.reflect.Array; - -/** - * This class reflects Java arrays into the JavaScript environment. - * - * @author Mike Shaver - * @see NativeJavaClass - * @see NativeJavaObject - * @see NativeJavaPackage - */ - -public class NativeJavaArray - extends NativeJavaObject - implements SymbolScriptable -{ - private static final long serialVersionUID = -924022554283675333L; - - @Override - public String getClassName() { - return "JavaArray"; - } - - public static NativeJavaArray wrap(Scriptable scope, Object array) { - return new NativeJavaArray(scope, array); - } - - @Override - public Object unwrap() { - return array; - } - - public NativeJavaArray(Scriptable scope, Object array) { - super(scope, null, ScriptRuntime.ObjectClass); - Class cl = array.getClass(); - if (!cl.isArray()) { - throw new RuntimeException("Array expected"); - } - this.array = array; - this.length = Array.getLength(array); - this.cls = cl.getComponentType(); - } - - @Override - public boolean has(String id, Scriptable start) { - return id.equals("length") || super.has(id, start); - } - - @Override - public boolean has(int index, Scriptable start) { - return 0 <= index && index < length; - } - - @Override - public boolean has(Symbol key, Scriptable start) { - return SymbolKey.IS_CONCAT_SPREADABLE.equals(key); - } - - @Override - public Object get(String id, Scriptable start) { - if (id.equals("length")) - return Integer.valueOf(length); - Object result = super.get(id, start); - if (result == NOT_FOUND && - !ScriptableObject.hasProperty(getPrototype(), id)) - { - throw Context.reportRuntimeErrorById( - "msg.java.member.not.found", array.getClass().getName(), id); - } - return result; - } - - @Override - public Object get(int index, Scriptable start) { - if (0 <= index && index < length) { - Context cx = Context.getContext(); - Object obj = Array.get(array, index); - return cx.getWrapFactory().wrap(cx, this, obj, cls); - } - return Undefined.instance; - } - - @Override - public Object get(Symbol key, Scriptable start) { - if (SymbolKey.IS_CONCAT_SPREADABLE.equals(key)) { - return Boolean.TRUE; - } - return Scriptable.NOT_FOUND; - } - - @Override - public void put(String id, Scriptable start, Object value) { - // Ignore assignments to "length"--it's readonly. - if (!id.equals("length")) - throw Context.reportRuntimeErrorById( - "msg.java.array.member.not.found", id); - } - - @Override - public void put(int index, Scriptable start, Object value) { - if (0 <= index && index < length) { - Array.set(array, index, Context.jsToJava(value, cls)); - } - else { - throw Context.reportRuntimeErrorById( - "msg.java.array.index.out.of.bounds", String.valueOf(index), - String.valueOf(length - 1)); - } - } - - @Override - public void delete(Symbol key) { - // All symbols are read-only - } - - @Override - public Object getDefaultValue(Class hint) { - if (hint == null || hint == ScriptRuntime.StringClass) - return array.toString(); - if (hint == ScriptRuntime.BooleanClass) - return Boolean.TRUE; - if (hint == ScriptRuntime.NumberClass) - return ScriptRuntime.NaNobj; - return this; - } - - @Override - public Object[] getIds() { - Object[] result = new Object[length]; - int i = length; - while (--i >= 0) - result[i] = Integer.valueOf(i); - return result; - } - - @Override - public boolean hasInstance(Scriptable value) { - if (!(value instanceof Wrapper)) - return false; - Object instance = ((Wrapper)value).unwrap(); - return cls.isInstance(instance); - } - - @Override - public Scriptable getPrototype() { - if (prototype == null) { - prototype = - ScriptableObject.getArrayPrototype(this.getParentScope()); - } - return prototype; - } - - Object array; - int length; - Class cls; -} diff --git a/src/main/java/org/mozilla/javascript/NativeJavaClass.java b/src/main/java/org/mozilla/javascript/NativeJavaClass.java deleted file mode 100644 index e9601a93..00000000 --- a/src/main/java/org/mozilla/javascript/NativeJavaClass.java +++ /dev/null @@ -1,312 +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.lang.reflect.Array; -import java.lang.reflect.Modifier; -import java.util.Map; - -/** - * This class reflects Java classes into the JavaScript environment, mainly - * for constructors and static members. We lazily reflect properties, - * and currently do not guarantee that a single j.l.Class is only - * reflected once into the JS environment, although we should. - * The only known case where multiple reflections - * are possible occurs when a j.l.Class is wrapped as part of a - * method return or property access, rather than by walking the - * Packages/java tree. - * - * @author Mike Shaver - * @see NativeJavaArray - * @see NativeJavaObject - * @see NativeJavaPackage - */ - -public class NativeJavaClass extends NativeJavaObject implements Function -{ - private static final long serialVersionUID = -6460763940409461664L; - - // Special property for getting the underlying Java class object. - static final String javaClassPropertyName = "__javaObject__"; - - public NativeJavaClass() { - } - - public NativeJavaClass(Scriptable scope, Class cl) { - this(scope, cl, false); - } - - public NativeJavaClass(Scriptable scope, Class cl, boolean isAdapter) { - super(scope, cl, null, isAdapter); - } - - @Override - protected void initMembers() { - Class cl = (Class)javaObject; - members = JavaMembers.lookupClass(parent, cl, cl, isAdapter); - staticFieldAndMethods = members.getFieldAndMethodsObjects(this, cl, true); - } - - @Override - public String getClassName() { - return "JavaClass"; - } - - @Override - public boolean has(String name, Scriptable start) { - return members.has(name, true) || javaClassPropertyName.equals(name); - } - - @Override - public Object get(String name, Scriptable start) { - // When used as a constructor, ScriptRuntime.newObject() asks - // for our prototype to create an object of the correct type. - // We don't really care what the object is, since we're returning - // one constructed out of whole cloth, so we return null. - if (name.equals("prototype")) - return null; - - if (staticFieldAndMethods != null) { - Object result = staticFieldAndMethods.get(name); - if (result != null) - return result; - } - - if (members.has(name, true)) { - return members.get(this, name, javaObject, true); - } - - Context cx = Context.getContext(); - Scriptable scope = ScriptableObject.getTopLevelScope(start); - WrapFactory wrapFactory = cx.getWrapFactory(); - - if (javaClassPropertyName.equals(name)) { - return wrapFactory.wrap(cx, scope, javaObject, - ScriptRuntime.ClassClass); - } - - // experimental: look for nested classes by appending $name to - // current class' name. - Class nestedClass = findNestedClass(getClassObject(), name); - if (nestedClass != null) { - Scriptable nestedValue = wrapFactory.wrapJavaClass(cx, scope, - nestedClass); - nestedValue.setParentScope(this); - return nestedValue; - } - - throw members.reportMemberNotFound(name); - } - - @Override - public void put(String name, Scriptable start, Object value) { - members.put(this, name, javaObject, value, true); - } - - @Override - public Object[] getIds() { - return members.getIds(true); - } - - public Class getClassObject() { - return (Class) super.unwrap(); - } - - @Override - public Object getDefaultValue(Class hint) { - if (hint == null || hint == ScriptRuntime.StringClass) - return this.toString(); - if (hint == ScriptRuntime.BooleanClass) - return Boolean.TRUE; - if (hint == ScriptRuntime.NumberClass) - return ScriptRuntime.NaNobj; - return this; - } - - @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, - Object[] args) - { - // If it looks like a "cast" of an object to this class type, - // walk the prototype chain to see if there's a wrapper of a - // object that's an instanceof this class. - if (args.length == 1 && args[0] instanceof Scriptable) { - Class c = getClassObject(); - Scriptable p = (Scriptable) args[0]; - do { - if (p instanceof Wrapper) { - Object o = ((Wrapper) p).unwrap(); - if (c.isInstance(o)) - return p; - } - p = p.getPrototype(); - } while (p != null); - } - return construct(cx, scope, args); - } - - @Override - public Scriptable construct(Context cx, Scriptable scope, Object[] args) - { - Class classObject = getClassObject(); - int modifiers = classObject.getModifiers(); - if (! (Modifier.isInterface(modifiers) || - Modifier.isAbstract(modifiers))) - { - NativeJavaMethod ctors = members.ctors; - int index = ctors.findCachedFunction(cx, args); - if (index < 0) { - String sig = NativeJavaMethod.scriptSignature(args); - throw Context.reportRuntimeErrorById( - "msg.no.java.ctor", classObject.getName(), sig); - } - - // Found the constructor, so try invoking it. - return constructSpecific(cx, scope, args, ctors.methods[index]); - } - if (args.length == 0) { - throw Context.reportRuntimeErrorById("msg.adapter.zero.args"); - } - Scriptable topLevel = ScriptableObject.getTopLevelScope(this); - String msg = ""; - try { - // When running on Android create an InterfaceAdapter since our - // bytecode generation won't work on Dalvik VM. - if ("Dalvik".equals(System.getProperty("java.vm.name")) - && classObject.isInterface()) { - Object obj = createInterfaceAdapter(classObject, - ScriptableObject.ensureScriptableObject(args[0])); - return cx.getWrapFactory().wrapAsJavaObject(cx, scope, obj, null); - } - // use JavaAdapter to construct a new class on the fly that - // implements/extends this interface/abstract class. - Object v = topLevel.get("JavaAdapter", topLevel); - if (v != NOT_FOUND) { - Function f = (Function) v; - // Args are (interface, js object) - Object[] adapterArgs = { this, args[0] }; - return f.construct(cx, topLevel, adapterArgs); - } - } catch (Exception ex) { - // fall through to error - String m = ex.getMessage(); - if (m != null) - msg = m; - } - throw Context.reportRuntimeErrorById( - "msg.cant.instantiate", msg, classObject.getName()); - } - - static Scriptable constructSpecific(Context cx, Scriptable scope, - Object[] args, MemberBox ctor) - { - Object instance = constructInternal(args, ctor); - // we need to force this to be wrapped, because construct _has_ - // to return a scriptable - Scriptable topLevel = ScriptableObject.getTopLevelScope(scope); - return cx.getWrapFactory().wrapNewObject(cx, topLevel, instance); - } - - static Object constructInternal(Object[] args, MemberBox ctor) - { - Class[] argTypes = ctor.argTypes; - - if (ctor.vararg) { - // marshall the explicit parameter - Object[] newArgs = new Object[argTypes.length]; - for (int i = 0; i < argTypes.length-1; i++) { - newArgs[i] = Context.jsToJava(args[i], argTypes[i]); - } - - Object varArgs; - - // Handle special situation where a single variable parameter - // is given and it is a Java or ECMA array. - if (args.length == argTypes.length && - (args[args.length-1] == null || - args[args.length-1] instanceof NativeArray || - args[args.length-1] instanceof NativeJavaArray)) - { - // convert the ECMA array into a native array - varArgs = Context.jsToJava(args[args.length-1], - argTypes[argTypes.length - 1]); - } else { - // marshall the variable parameter - Class componentType = argTypes[argTypes.length - 1]. - getComponentType(); - varArgs = Array.newInstance(componentType, - args.length - argTypes.length + 1); - for (int i=0; i < Array.getLength(varArgs); i++) { - Object value = Context.jsToJava(args[argTypes.length-1 + i], - componentType); - Array.set(varArgs, i, value); - } - } - - // add varargs - newArgs[argTypes.length-1] = varArgs; - // replace the original args with the new one - args = newArgs; - } else { - Object[] origArgs = args; - for (int i = 0; i < args.length; i++) { - Object arg = args[i]; - Object x = Context.jsToJava(arg, argTypes[i]); - if (x != arg) { - if (args == origArgs) { - args = origArgs.clone(); - } - args[i] = x; - } - } - } - - return ctor.newInstance(args); - } - - @Override - public String toString() { - return "[JavaClass " + getClassObject().getName() + "]"; - } - - /** - * Determines if prototype is a wrapped Java object and performs - * a Java "instanceof". - * Exception: if value is an instance of NativeJavaClass, it isn't - * considered an instance of the Java class; this forestalls any - * name conflicts between java.lang.Class's methods and the - * static methods exposed by a JavaNativeClass. - */ - @Override - public boolean hasInstance(Scriptable value) { - - if (value instanceof Wrapper && - !(value instanceof NativeJavaClass)) { - Object instance = ((Wrapper)value).unwrap(); - - return getClassObject().isInstance(instance); - } - - // value wasn't something we understand - return false; - } - - private static Class findNestedClass(Class parentClass, String name) { - String nestedClassName = parentClass.getName() + '$' + name; - ClassLoader loader = parentClass.getClassLoader(); - if (loader == null) { - // ALERT: if loader is null, nested class should be loaded - // via system class loader which can be different from the - // loader that brought Rhino classes that Class.forName() would - // use, but ClassLoader.getSystemClassLoader() is Java 2 only - return Kit.classOrNull(nestedClassName); - } - return Kit.classOrNull(loader, nestedClassName); - } - - private Map staticFieldAndMethods; -} diff --git a/src/main/java/org/mozilla/javascript/NativeJavaConstructor.java b/src/main/java/org/mozilla/javascript/NativeJavaConstructor.java deleted file mode 100644 index 08359bbf..00000000 --- a/src/main/java/org/mozilla/javascript/NativeJavaConstructor.java +++ /dev/null @@ -1,54 +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; - -/** - * This class reflects a single Java constructor into the JavaScript - * environment. It satisfies a request for an overloaded constructor, - * as introduced in LiveConnect 3. - * All NativeJavaConstructors behave as JSRef `bound' methods, in that they - * always construct the same NativeJavaClass regardless of any reparenting - * that may occur. - * - * @author Frank Mitchell - * @see NativeJavaMethod - * @see NativeJavaPackage - * @see NativeJavaClass - */ - -public class NativeJavaConstructor extends BaseFunction -{ - private static final long serialVersionUID = -8149253217482668463L; - - MemberBox ctor; - - public NativeJavaConstructor(MemberBox ctor) - { - this.ctor = ctor; - } - - @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, - Object[] args) - { - return NativeJavaClass.constructSpecific(cx, scope, args, ctor); - } - - @Override - public String getFunctionName() - { - String sig = JavaMembers.liveConnectSignature(ctor.argTypes); - return "".concat(sig); - } - - @Override - public String toString() - { - return "[JavaConstructor " + ctor.getName() + "]"; - } -} - diff --git a/src/main/java/org/mozilla/javascript/NativeJavaList.java b/src/main/java/org/mozilla/javascript/NativeJavaList.java deleted file mode 100644 index 464dbfc7..00000000 --- a/src/main/java/org/mozilla/javascript/NativeJavaList.java +++ /dev/null @@ -1,186 +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.ArrayList; -import java.util.List; - -/** - * NativeJavaList is a wrapper for java objects implementing java.util.List - * interface. This wrapper delegates index based access in javascript (like - * value[x] = 3) to the according {@link List#get(int)}, {@link List#set(int, Object)} and - * {@link List#add(Object)} methods. This allows you to use java lists in many places like a - * javascript Array. - * - *

Supported functions: - * - *

    - *
  • index based access is delegated to List.get/set/add. If index >= length, - * the skipped elements will be filled with null values - *
  • iterator support with for...of (provided by NativeJavaObject for all - * iterables) - *
  • when iterating with for .. in (or for each .. in) then - * getIds - * + index based access is used. - *
  • reading and setting length property. When modifying the length property, the - * list is either truncated or will be filled with null values up to length - * - *
  • deleting entries: delete value[index] will be equivalent with - * value[index] = null and is implemented to provide array compatibility. - *
- * - * Important: JavaList does not support sparse arrays. So setting the length property to a - * high value or writing to a high index may allocate a lot of memory. - * - *

Note: Although JavaList looks like a javascript-Array, it is - * not an - * Array. Some methods behave very similar like Array.indexOf and - * java.util.List.indexOf, others are named differently like Array.includes vs. - * java.util.List.contains. Especially forEach is different in Array - * and java.util.List. Also deleting entries will set entries to null - * instead to Undefined - */ -public class NativeJavaList extends NativeJavaObject { - - private static final long serialVersionUID = 660285467829047519L; - - private List list; - - @SuppressWarnings("unchecked") - public NativeJavaList(Scriptable scope, Object list) { - super(scope, list, list.getClass()); - assert list instanceof List; - this.list = (List) list; - } - - @Override - public String getClassName() { - return "JavaList"; - } - - @Override - public boolean has(String name, Scriptable start) { - if (name.equals("length")) { - return true; - } - return super.has(name, start); - } - - @Override - public boolean has(int index, Scriptable start) { - if (isWithValidIndex(index)) { - return true; - } - return super.has(index, start); - } - - public void delete(int index) { - if (isWithValidIndex(index)) { - list.set(index, null); - } - } - - @Override - public boolean has(Symbol key, Scriptable start) { - if (SymbolKey.IS_CONCAT_SPREADABLE.equals(key)) { - return true; - } - return super.has(key, start); - } - - @Override - public Object get(String name, Scriptable start) { - if ("length".equals(name)) { - return Integer.valueOf(list.size()); - } - return super.get(name, start); - } - - @Override - public Object get(int index, Scriptable start) { - if (isWithValidIndex(index)) { - Context cx = Context.getCurrentContext(); - Object obj = list.get(index); - if (cx != null) { - return cx.getWrapFactory().wrap(cx, this, obj, obj == null ? null : obj.getClass()); - } - return obj; - } - return Undefined.instance; - } - - @Override - public Object get(Symbol key, Scriptable start) { - if (SymbolKey.IS_CONCAT_SPREADABLE.equals(key)) { - return Boolean.TRUE; - } - return super.get(key, start); - } - - @Override - public void put(int index, Scriptable start, Object value) { - if (index >= 0) { - Object javaValue = Context.jsToJava(value, Object.class); - if (index == list.size()) { - list.add(javaValue); // use "add" at the end of list. - } else { - ensureCapacity(index + 1); - list.set(index, javaValue); - } - return; - } - super.put(index, start, value); - } - - @Override - public void put(String name, Scriptable start, Object value) { - if (list != null && "length".equals(name)) { - setLength(value); - return; - } - super.put(name, start, value); - } - - private void ensureCapacity(int minCapacity) { - if (minCapacity > list.size()) { - if (list instanceof ArrayList) { - ((ArrayList) list).ensureCapacity(minCapacity); - } - while (minCapacity > list.size()) { - list.add(null); - } - } - } - - private void setLength(Object val) { - double d = ScriptRuntime.toNumber(val); - long longVal = ScriptRuntime.toUint32(d); - if (longVal != d || longVal > Integer.MAX_VALUE) { - String msg = ScriptRuntime.getMessageById("msg.arraylength.bad"); - throw ScriptRuntime.rangeError(msg); - } - if (longVal < list.size()) { - list.subList((int) longVal, list.size()).clear(); - } else { - ensureCapacity((int) longVal); - } - } - - @Override - public Object[] getIds() { - List list = (List) javaObject; - Object[] result = new Object[list.size()]; - int i = list.size(); - while (--i >= 0) { - result[i] = Integer.valueOf(i); - } - return result; - } - - private boolean isWithValidIndex(int index) { - return index >= 0 && index < list.size(); - } -} diff --git a/src/main/java/org/mozilla/javascript/NativeJavaMap.java b/src/main/java/org/mozilla/javascript/NativeJavaMap.java deleted file mode 100644 index 9f2de67b..00000000 --- a/src/main/java/org/mozilla/javascript/NativeJavaMap.java +++ /dev/null @@ -1,197 +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.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -/** - * NativeJavaMap is a wrapper for java objects implementing java.util.Map - * interface. When {@link Context#FEATURE_ENABLE_JAVA_MAP_ACCESS} is enabled, property based - * access like map[key] is delegated to {@link Map#get(Object)} or {@link - * Map#put(Object, Object)} operations so that a JavaMap acts very similar to a - * javascript Object There is also an iterator to iterate over entries with - * for .. of. - * - *

Limitations: The wrapped map should have String or Integer as - * key. Otherwise, property based access may not work properly. - */ -public class NativeJavaMap extends NativeJavaObject { - - private static final long serialVersionUID = -3786257752907047381L; - - private Map map; - - static void init(ScriptableObject scope, boolean sealed) { - NativeJavaMapIterator.init(scope, sealed); - } - - @SuppressWarnings("unchecked") - public NativeJavaMap(Scriptable scope, Object map) { - super(scope, map, map.getClass()); - assert map instanceof Map; - this.map = (Map) map; - } - - @Override - public String getClassName() { - return "JavaMap"; - } - - @Override - public boolean has(String name, Scriptable start) { - Context cx = Context.getCurrentContext(); - if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - if (map.containsKey(name)) { - return true; - } - } - return super.has(name, start); - } - - @Override - public boolean has(int index, Scriptable start) { - Context cx = Context.getCurrentContext(); - if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - if (map.containsKey(Integer.valueOf(index))) { - return true; - } - } - return super.has(index, start); - } - - @Override - public boolean has(Symbol key, Scriptable start) { - if (SymbolKey.ITERATOR.equals(key)) { - return true; - } - return false; - } - - @Override - public Object get(String name, Scriptable start) { - Context cx = Context.getCurrentContext(); - if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - if (map.containsKey(name)) { - Object obj = map.get(name); - return cx.getWrapFactory().wrap(cx, this, obj, obj == null ? null : obj.getClass()); - } - } - return super.get(name, start); - } - - @Override - public Object get(int index, Scriptable start) { - Context cx = Context.getCurrentContext(); - if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - if (map.containsKey(Integer.valueOf(index))) { - Object obj = map.get(Integer.valueOf(index)); - return cx.getWrapFactory().wrap(cx, this, obj, obj == null ? null : obj.getClass()); - } - } - return super.get(index, start); - } - - @Override - public Object get(Symbol key, Scriptable start) { - if (SymbolKey.ITERATOR.equals(key)) { - return symbol_iterator; - } - return super.get(key, start); - } - - @Override - public void put(String name, Scriptable start, Object value) { - Context cx = Context.getCurrentContext(); - if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - map.put(name, Context.jsToJava(value, Object.class)); - } else { - super.put(name, start, value); - } - } - - @Override - public void put(int index, Scriptable start, Object value) { - Context cx = Context.getContext(); - if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - map.put(Integer.valueOf(index), Context.jsToJava(value, Object.class)); - } else { - super.put(index, start, value); - } - } - - @Override - public Object[] getIds() { - Context cx = Context.getCurrentContext(); - if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { - List ids = new ArrayList<>(map.size()); - for (Object key : map.keySet()) { - if (key instanceof Integer) { - ids.add(key); - } else { - ids.add(ScriptRuntime.toString(key)); - } - } - return ids.toArray(); - } - return super.getIds(); - } - - private static Callable symbol_iterator = - (Context cx, Scriptable scope, Scriptable thisObj, Object[] args) -> { - if (!(thisObj instanceof NativeJavaMap)) { - throw ScriptRuntime.typeErrorById("msg.incompat.call", SymbolKey.ITERATOR); - } - return new NativeJavaMapIterator(scope, ((NativeJavaMap) thisObj).map); - }; - - private static final class NativeJavaMapIterator extends ES6Iterator { - private static final long serialVersionUID = 1L; - private static final String ITERATOR_TAG = "JavaMapIterator"; - - static void init(ScriptableObject scope, boolean sealed) { - ES6Iterator.init(scope, sealed, new NativeJavaMapIterator(), ITERATOR_TAG); - } - - /** Only for constructing the prototype object. */ - private NativeJavaMapIterator() { - super(); - } - - NativeJavaMapIterator(Scriptable scope, Map map) { - super(scope, ITERATOR_TAG); - this.iterator = map.entrySet().iterator(); - } - - @Override - public String getClassName() { - return "Java Map Iterator"; - } - - @Override - protected boolean isDone(Context cx, Scriptable scope) { - return !iterator.hasNext(); - } - - @Override - protected Object nextValue(Context cx, Scriptable scope) { - if (!iterator.hasNext()) { - return cx.newArray(scope, new Object[] {Undefined.instance, Undefined.instance}); - } - Map.Entry e = iterator.next(); - return cx.newArray(scope, new Object[] {e.getKey(), e.getValue()}); - } - - @Override - protected String getTag() { - return ITERATOR_TAG; - } - - private Iterator> iterator; - } -} diff --git a/src/main/java/org/mozilla/javascript/NativeJavaMethod.java b/src/main/java/org/mozilla/javascript/NativeJavaMethod.java deleted file mode 100644 index 0a4bf619..00000000 --- a/src/main/java/org/mozilla/javascript/NativeJavaMethod.java +++ /dev/null @@ -1,571 +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.lang.reflect.Array; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * This class reflects Java methods into the JavaScript environment and handles overloading of - * methods. - * - * @author Mike Shaver - * @see NativeJavaArray - * @see NativeJavaPackage - * @see NativeJavaClass - */ -public class NativeJavaMethod extends BaseFunction { - - private static final long serialVersionUID = -3440381785576412928L; - - NativeJavaMethod(MemberBox[] methods) { - this.functionName = methods[0].getName(); - this.methods = methods; - } - - NativeJavaMethod(MemberBox[] methods, String name) { - this.functionName = name; - this.methods = methods; - } - - NativeJavaMethod(MemberBox method, String name) { - this.functionName = name; - this.methods = new MemberBox[] {method}; - } - - public NativeJavaMethod(Method method, String name) { - this(new MemberBox(method), name); - } - - @Override - public String getFunctionName() { - return functionName; - } - - static String scriptSignature(Object[] values) { - StringBuilder sig = new StringBuilder(); - for (int i = 0; i != values.length; ++i) { - Object value = values[i]; - - String s; - if (value == null) { - s = "null"; - } else if (value instanceof Boolean) { - s = "boolean"; - } else if (value instanceof String) { - s = "string"; - } else if (value instanceof Number) { - s = "number"; - } else if (value instanceof Scriptable) { - if (value instanceof Undefined) { - s = "undefined"; - } else if (value instanceof Wrapper) { - Object wrapped = ((Wrapper) value).unwrap(); - s = wrapped.getClass().getName(); - } else if (value instanceof Function) { - s = "function"; - } else { - s = "object"; - } - } else { - s = JavaMembers.javaSignature(value.getClass()); - } - - if (i != 0) { - sig.append(','); - } - sig.append(s); - } - return sig.toString(); - } - - @Override - 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("() {"); - } - sb.append("/*\n"); - sb.append(toString()); - sb.append(justbody ? "*/\n" : "*/}\n"); - return sb.toString(); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - for (int i = 0, N = methods.length; i != N; ++i) { - // Check member type, we also use this for overloaded constructors - if (methods[i].isMethod()) { - Method method = methods[i].method(); - sb.append(JavaMembers.javaSignature(method.getReturnType())); - sb.append(' '); - sb.append(method.getName()); - } else { - sb.append(methods[i].getName()); - } - sb.append(JavaMembers.liveConnectSignature(methods[i].argTypes)); - sb.append('\n'); - } - return sb.toString(); - } - - @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - // Find a method that matches the types given. - if (methods.length == 0) { - throw new RuntimeException("No methods defined for call"); - } - - int index = findCachedFunction(cx, args); - if (index < 0) { - Class c = methods[0].method().getDeclaringClass(); - String sig = c.getName() + '.' + getFunctionName() + '(' + scriptSignature(args) + ')'; - throw Context.reportRuntimeErrorById("msg.java.no_such_method", sig); - } - - MemberBox meth = methods[index]; - Class[] argTypes = meth.argTypes; - - if (meth.vararg) { - // marshall the explicit parameters - Object[] newArgs = new Object[argTypes.length]; - for (int i = 0; i < argTypes.length - 1; i++) { - newArgs[i] = Context.jsToJava(args[i], argTypes[i]); - } - - Object varArgs; - - // Handle special situation where a single variable parameter - // is given and it is a Java or ECMA array or is null. - if (args.length == argTypes.length - && (args[args.length - 1] == null - || args[args.length - 1] instanceof NativeArray - || args[args.length - 1] instanceof NativeJavaArray)) { - // convert the ECMA array into a native array - varArgs = Context.jsToJava(args[args.length - 1], argTypes[argTypes.length - 1]); - } else { - // marshall the variable parameters - Class componentType = argTypes[argTypes.length - 1].getComponentType(); - varArgs = Array.newInstance(componentType, args.length - argTypes.length + 1); - for (int i = 0; i < Array.getLength(varArgs); i++) { - Object value = Context.jsToJava(args[argTypes.length - 1 + i], componentType); - Array.set(varArgs, i, value); - } - } - - // add varargs - newArgs[argTypes.length - 1] = varArgs; - // replace the original args with the new one - args = newArgs; - } else { - // First, we marshall the args. - Object[] origArgs = args; - for (int i = 0; i < args.length; i++) { - Object arg = args[i]; - Object coerced = Context.jsToJava(arg, argTypes[i]); - if (coerced != arg) { - if (origArgs == args) { - args = args.clone(); - } - args[i] = coerced; - } - } - } - Object javaObject; - if (meth.isStatic()) { - javaObject = null; // don't need an object - } else { - Scriptable o = thisObj; - Class c = meth.getDeclaringClass(); - for (; ; ) { - if (o == null) { - throw Context.reportRuntimeErrorById( - "msg.nonjava.method", - getFunctionName(), - ScriptRuntime.toString(thisObj), - c.getName()); - } - if (o instanceof Wrapper) { - javaObject = ((Wrapper) o).unwrap(); - if (c.isInstance(javaObject)) { - break; - } - } - o = o.getPrototype(); - } - } - if (debug) { - printDebug("Calling ", meth, args); - } - - Object retval = meth.invoke(javaObject, args); - Class staticType = meth.method().getReturnType(); - - if (debug) { - Class actualType = (retval == null) ? null : retval.getClass(); - System.err.println( - " ----- Returned " - + retval - + " actual = " - + actualType - + " expect = " - + staticType); - } - - Object wrapped = - cx.getWrapFactory() - .wrap( - cx, scope, - retval, staticType); - if (debug) { - Class actualType = (wrapped == null) ? null : wrapped.getClass(); - System.err.println(" ----- Wrapped as " + wrapped + " class = " + actualType); - } - - if (wrapped == null && staticType == Void.TYPE) { - wrapped = Undefined.instance; - } - return wrapped; - } - - int findCachedFunction(Context cx, Object[] args) { - if (methods.length > 1) { - for (ResolvedOverload ovl : overloadCache) { - if (ovl.matches(args)) { - return ovl.index; - } - } - int index = findFunction(cx, methods, args); - // As a sanity measure, don't let the lookup cache grow longer - // than twice the number of overloaded methods - if (overloadCache.size() < methods.length * 2) { - ResolvedOverload ovl = new ResolvedOverload(args, index); - overloadCache.addIfAbsent(ovl); - } - return index; - } - return findFunction(cx, methods, args); - } - - /** - * Find the index of the correct function to call given the set of methods or constructors and - * the arguments. If no function can be found to call, return -1. - */ - static int findFunction(Context cx, MemberBox[] methodsOrCtors, Object[] args) { - if (methodsOrCtors.length == 0) { - return -1; - } else if (methodsOrCtors.length == 1) { - MemberBox member = methodsOrCtors[0]; - Class[] argTypes = member.argTypes; - int alength = argTypes.length; - - if (member.vararg) { - alength--; - if (alength > args.length) { - return -1; - } - } else { - if (alength != args.length) { - return -1; - } - } - for (int j = 0; j != alength; ++j) { - if (!NativeJavaObject.canConvert(args[j], argTypes[j])) { - if (debug) printDebug("Rejecting (args can't convert) ", member, args); - return -1; - } - } - if (debug) printDebug("Found ", member, args); - return 0; - } - - int firstBestFit = -1; - int[] extraBestFits = null; - int extraBestFitsCount = 0; - - search: - for (int i = 0; i < methodsOrCtors.length; i++) { - MemberBox member = methodsOrCtors[i]; - Class[] argTypes = member.argTypes; - int alength = argTypes.length; - if (member.vararg) { - alength--; - if (alength > args.length) { - continue search; - } - } else { - if (alength != args.length) { - continue search; - } - } - for (int j = 0; j < alength; j++) { - if (!NativeJavaObject.canConvert(args[j], argTypes[j])) { - if (debug) printDebug("Rejecting (args can't convert) ", member, args); - continue search; - } - } - if (firstBestFit < 0) { - if (debug) printDebug("Found first applicable ", member, args); - firstBestFit = i; - } else { - // Compare with all currently fit methods. - // The loop starts from -1 denoting firstBestFit and proceed - // until extraBestFitsCount to avoid extraBestFits allocation - // in the most common case of no ambiguity - int betterCount = 0; // number of times member was prefered over - // best fits - int worseCount = 0; // number of times best fits were prefered - // over member - for (int j = -1; j != extraBestFitsCount; ++j) { - int bestFitIndex; - if (j == -1) { - bestFitIndex = firstBestFit; - } else { - bestFitIndex = extraBestFits[j]; - } - MemberBox bestFit = methodsOrCtors[bestFitIndex]; - if (cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS) - && bestFit.isPublic() != member.isPublic()) { - // When FEATURE_ENHANCED_JAVA_ACCESS gives us access - // to non-public members, continue to prefer public - // methods in overloading - if (!bestFit.isPublic()) ++betterCount; - else ++worseCount; - } else { - int preference = - preferSignature( - args, - argTypes, - member.vararg, - bestFit.argTypes, - bestFit.vararg); - if (preference == PREFERENCE_AMBIGUOUS) { - break; - } else if (preference == PREFERENCE_FIRST_ARG) { - ++betterCount; - } else if (preference == PREFERENCE_SECOND_ARG) { - ++worseCount; - } else { - if (preference != PREFERENCE_EQUAL) Kit.codeBug(); - // This should not happen in theory - // but on some JVMs, Class.getMethods will return all - // static methods of the class hierarchy, even if - // a derived class's parameters match exactly. - // We want to call the derived class's method. - if (bestFit.isStatic() - && bestFit.getDeclaringClass() - .isAssignableFrom(member.getDeclaringClass())) { - // On some JVMs, Class.getMethods will return all - // static methods of the class hierarchy, even if - // a derived class's parameters match exactly. - // We want to call the derived class's method. - if (debug) - printDebug("Substituting (overridden static)", member, args); - if (j == -1) { - firstBestFit = i; - } else { - extraBestFits[j] = i; - } - } else { - if (debug) - printDebug("Ignoring same signature member ", member, args); - } - continue search; - } - } - } - if (betterCount == 1 + extraBestFitsCount) { - // member was prefered over all best fits - if (debug) printDebug("New first applicable ", member, args); - firstBestFit = i; - extraBestFitsCount = 0; - } else if (worseCount == 1 + extraBestFitsCount) { - // all best fits were prefered over member, ignore it - if (debug) printDebug("Rejecting (all current bests better) ", member, args); - } else { - // some ambiguity was present, add member to best fit set - if (debug) printDebug("Added to best fit set ", member, args); - if (extraBestFits == null) { - // Allocate maximum possible array - extraBestFits = new int[methodsOrCtors.length - 1]; - } - extraBestFits[extraBestFitsCount] = i; - ++extraBestFitsCount; - } - } - } - - if (firstBestFit < 0) { - // Nothing was found - return -1; - } else if (extraBestFitsCount == 0) { - // single best fit - return firstBestFit; - } - - // report remaining ambiguity - StringBuilder buf = new StringBuilder(); - for (int j = -1; j != extraBestFitsCount; ++j) { - int bestFitIndex; - if (j == -1) { - bestFitIndex = firstBestFit; - } else { - bestFitIndex = extraBestFits[j]; - } - buf.append("\n "); - buf.append(methodsOrCtors[bestFitIndex].toJavaDeclaration()); - } - - MemberBox firstFitMember = methodsOrCtors[firstBestFit]; - String memberName = firstFitMember.getName(); - String memberClass = firstFitMember.getDeclaringClass().getName(); - - if (methodsOrCtors[0].isCtor()) { - throw Context.reportRuntimeErrorById( - "msg.constructor.ambiguous", memberName, scriptSignature(args), buf.toString()); - } - throw Context.reportRuntimeErrorById( - "msg.method.ambiguous", - memberClass, - memberName, - scriptSignature(args), - buf.toString()); - } - - /** Types are equal */ - private static final int PREFERENCE_EQUAL = 0; - - private static final int PREFERENCE_FIRST_ARG = 1; - private static final int PREFERENCE_SECOND_ARG = 2; - /** No clear "easy" conversion */ - private static final int PREFERENCE_AMBIGUOUS = 3; - - /** - * Determine which of two signatures is the closer fit. Returns one of PREFERENCE_EQUAL, - * PREFERENCE_FIRST_ARG, PREFERENCE_SECOND_ARG, or PREFERENCE_AMBIGUOUS. - */ - private static int preferSignature( - Object[] args, Class[] sig1, boolean vararg1, Class[] sig2, boolean vararg2) { - int totalPreference = 0; - for (int j = 0; j < args.length; j++) { - Class type1 = vararg1 && j >= sig1.length ? sig1[sig1.length - 1] : sig1[j]; - Class type2 = vararg2 && j >= sig2.length ? sig2[sig2.length - 1] : sig2[j]; - if (type1 == type2) { - continue; - } - Object arg = args[j]; - - // Determine which of type1, type2 is easier to convert from arg. - - int rank1 = NativeJavaObject.getConversionWeight(arg, type1); - int rank2 = NativeJavaObject.getConversionWeight(arg, type2); - - int preference; - if (rank1 < rank2) { - preference = PREFERENCE_FIRST_ARG; - } else if (rank1 > rank2) { - preference = PREFERENCE_SECOND_ARG; - } else { - // Equal ranks - if (rank1 == NativeJavaObject.CONVERSION_NONTRIVIAL) { - if (type1.isAssignableFrom(type2)) { - preference = PREFERENCE_SECOND_ARG; - } else if (type2.isAssignableFrom(type1)) { - preference = PREFERENCE_FIRST_ARG; - } else { - preference = PREFERENCE_AMBIGUOUS; - } - } else { - preference = PREFERENCE_AMBIGUOUS; - } - } - - totalPreference |= preference; - - if (totalPreference == PREFERENCE_AMBIGUOUS) { - break; - } - } - return totalPreference; - } - - private static final boolean debug = false; - - private static void printDebug(String msg, MemberBox member, Object[] args) { - if (debug) { - StringBuilder sb = new StringBuilder(); - sb.append(" ----- "); - sb.append(msg); - sb.append(member.getDeclaringClass().getName()); - sb.append('.'); - if (member.isMethod()) { - sb.append(member.getName()); - } - sb.append(JavaMembers.liveConnectSignature(member.argTypes)); - sb.append(" for arguments ("); - sb.append(scriptSignature(args)); - sb.append(')'); - System.out.println(sb); - } - } - - MemberBox[] methods; - private String functionName; - private final transient CopyOnWriteArrayList overloadCache = - new CopyOnWriteArrayList<>(); -} - -class ResolvedOverload { - final Class[] types; - final int index; - - ResolvedOverload(Object[] args, int index) { - this.index = index; - types = new Class[args.length]; - for (int i = 0, l = args.length; i < l; i++) { - Object arg = args[i]; - if (arg instanceof Wrapper) arg = ((Wrapper) arg).unwrap(); - types[i] = arg == null ? null : arg.getClass(); - } - } - - boolean matches(Object[] args) { - if (args.length != types.length) { - return false; - } - for (int i = 0, l = args.length; i < l; i++) { - Object arg = args[i]; - if (arg instanceof Wrapper) arg = ((Wrapper) arg).unwrap(); - if (arg == null) { - if (types[i] != null) return false; - } else if (arg.getClass() != types[i]) { - return false; - } - } - return true; - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof ResolvedOverload)) { - return false; - } - ResolvedOverload ovl = (ResolvedOverload) other; - return Arrays.equals(types, ovl.types) && index == ovl.index; - } - - @Override - public int hashCode() { - return Arrays.hashCode(types); - } -} diff --git a/src/main/java/org/mozilla/javascript/NativeJavaObject.java b/src/main/java/org/mozilla/javascript/NativeJavaObject.java deleted file mode 100644 index ab3e20ef..00000000 --- a/src/main/java/org/mozilla/javascript/NativeJavaObject.java +++ /dev/null @@ -1,988 +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.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.lang.reflect.Array; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.math.BigInteger; -import java.util.Date; -import java.util.Iterator; -import java.util.Map; - -/** - * This class reflects non-Array Java objects into the JavaScript environment. It reflect fields - * directly, and uses NativeJavaMethod objects to reflect (possibly overloaded) methods. It also - * provides iterator support for all iterable objects. - * - *

- * - * @author Mike Shaver - * @see NativeJavaArray - * @see NativeJavaPackage - * @see NativeJavaClass - */ -public class NativeJavaObject implements Scriptable, SymbolScriptable, Wrapper, Serializable { - - private static final long serialVersionUID = -6948590651130498591L; - - static void init(ScriptableObject scope, boolean sealed) { - JavaIterableIterator.init(scope, sealed); - } - - public NativeJavaObject() {} - - public NativeJavaObject(Scriptable scope, Object javaObject, Class staticType) { - this(scope, javaObject, staticType, false); - } - - public NativeJavaObject( - Scriptable scope, Object javaObject, Class staticType, boolean isAdapter) { - this.parent = scope; - this.javaObject = javaObject; - this.staticType = staticType; - this.isAdapter = isAdapter; - initMembers(); - } - - protected void initMembers() { - Class dynamicType; - if (javaObject != null) { - dynamicType = javaObject.getClass(); - } else { - dynamicType = staticType; - } - members = JavaMembers.lookupClass(parent, dynamicType, staticType, isAdapter); - fieldAndMethods = members.getFieldAndMethodsObjects(this, javaObject, false); - } - - @Override - public boolean has(String name, Scriptable start) { - return members.has(name, false); - } - - @Override - public boolean has(int index, Scriptable start) { - return false; - } - - @Override - public boolean has(Symbol key, Scriptable start) { - if (SymbolKey.ITERATOR.equals(key) && javaObject instanceof Iterable) { - return true; - } - return false; - } - - @Override - public Object get(String name, Scriptable start) { - if (fieldAndMethods != null) { - Object result = fieldAndMethods.get(name); - if (result != null) { - return result; - } - } - // TODO: passing 'this' as the scope is bogus since it has - // no parent scope - return members.get(this, name, javaObject, false); - } - - @Override - public Object get(Symbol key, Scriptable start) { - if (SymbolKey.ITERATOR.equals(key) && javaObject instanceof Iterable) { - return symbol_iterator; - } - // Native Java objects have no Symbol members - return Scriptable.NOT_FOUND; - } - - @Override - public Object get(int index, Scriptable start) { - throw members.reportMemberNotFound(Integer.toString(index)); - } - - @Override - public void put(String name, Scriptable start, Object value) { - // We could be asked to modify the value of a property in the - // prototype. Since we can't add a property to a Java object, - // we modify it in the prototype rather than copy it down. - if (prototype == null || members.has(name, false)) - members.put(this, name, javaObject, value, false); - else prototype.put(name, prototype, value); - } - - @Override - public void put(Symbol symbol, Scriptable start, Object value) { - // We could be asked to modify the value of a property in the - // prototype. Since we can't add a property to a Java object, - // we modify it in the prototype rather than copy it down. - String name = symbol.toString(); - if (prototype == null || members.has(name, false)) { - members.put(this, name, javaObject, value, false); - } else if (prototype instanceof SymbolScriptable) { - ((SymbolScriptable) prototype).put(symbol, prototype, value); - } - } - - @Override - public void put(int index, Scriptable start, Object value) { - throw members.reportMemberNotFound(Integer.toString(index)); - } - - @Override - public boolean hasInstance(Scriptable value) { - // This is an instance of a Java class, so always return false - return false; - } - - @Override - public void delete(String name) {} - - @Override - public void delete(Symbol key) {} - - @Override - public void delete(int index) {} - - @Override - public Scriptable getPrototype() { - if (prototype == null && javaObject instanceof String) { - return TopLevel.getBuiltinPrototype( - ScriptableObject.getTopLevelScope(parent), TopLevel.Builtins.String); - } - return prototype; - } - - /** Sets the prototype of the object. */ - @Override - public void setPrototype(Scriptable m) { - prototype = m; - } - - /** Returns the parent (enclosing) scope of the object. */ - @Override - public Scriptable getParentScope() { - return parent; - } - - /** Sets the parent (enclosing) scope of the object. */ - @Override - public void setParentScope(Scriptable m) { - parent = m; - } - - @Override - public Object[] getIds() { - return members.getIds(false); - } - - /** - * @deprecated Use {@link Context#getWrapFactory()} together with calling {@link - * WrapFactory#wrap(Context, Scriptable, Object, Class)} - */ - @Deprecated - public static Object wrap(Scriptable scope, Object obj, Class staticType) { - - Context cx = Context.getContext(); - return cx.getWrapFactory().wrap(cx, scope, obj, staticType); - } - - @Override - public Object unwrap() { - return javaObject; - } - - @Override - public String getClassName() { - return "JavaObject"; - } - - @Override - public Object getDefaultValue(Class hint) { - Object value; - if (hint == null) { - if (javaObject instanceof Boolean) { - hint = ScriptRuntime.BooleanClass; - } - if (javaObject instanceof Number) { - hint = ScriptRuntime.NumberClass; - } - } - if (hint == null || hint == ScriptRuntime.StringClass) { - value = javaObject.toString(); - } else { - String converterName; - if (hint == ScriptRuntime.BooleanClass) { - converterName = "booleanValue"; - } else if (hint == ScriptRuntime.NumberClass) { - converterName = "doubleValue"; - } else { - throw Context.reportRuntimeErrorById("msg.default.value"); - } - Object converterObject = get(converterName, this); - if (converterObject instanceof Function) { - Function f = (Function) converterObject; - value = - f.call( - Context.getContext(), - f.getParentScope(), - this, - ScriptRuntime.emptyArgs); - } else { - if (hint == ScriptRuntime.NumberClass && javaObject instanceof Boolean) { - boolean b = ((Boolean) javaObject).booleanValue(); - value = b ? ScriptRuntime.wrapNumber(1.0) : ScriptRuntime.zeroObj; - } else { - value = javaObject.toString(); - } - } - } - return value; - } - - /** - * Determine whether we can/should convert between the given type and the desired one. This - * should be superceded by a conversion-cost calculation function, but for now I'll hide behind - * precedent. - */ - public static boolean canConvert(Object fromObj, Class to) { - int weight = getConversionWeight(fromObj, to); - - return (weight < CONVERSION_NONE); - } - - private static final int JSTYPE_UNDEFINED = 0; // undefined type - private static final int JSTYPE_NULL = 1; // null - private static final int JSTYPE_BOOLEAN = 2; // boolean - private static final int JSTYPE_NUMBER = 3; // number - private static final int JSTYPE_STRING = 4; // string - private static final int JSTYPE_JAVA_CLASS = 5; // JavaClass - private static final int JSTYPE_JAVA_OBJECT = 6; // JavaObject - private static final int JSTYPE_JAVA_ARRAY = 7; // JavaArray - private static final int JSTYPE_OBJECT = 8; // Scriptable - private static final int JSTYPE_BIGINT = 9; // BigInt - - static final byte CONVERSION_TRIVIAL = 1; - static final byte CONVERSION_NONTRIVIAL = 0; - static final byte CONVERSION_NONE = 99; - - /** - * Derive a ranking based on how "natural" the conversion is. The special value CONVERSION_NONE - * means no conversion is possible, and CONVERSION_NONTRIVIAL signals that more type conformance - * testing is required. Based on "preferred method - * conversions" from Live Connect 3 - */ - static int getConversionWeight(Object fromObj, Class to) { - int fromCode = getJSTypeCode(fromObj); - - switch (fromCode) { - case JSTYPE_UNDEFINED: - if (to == ScriptRuntime.StringClass || to == ScriptRuntime.ObjectClass) { - return 1; - } - break; - - case JSTYPE_NULL: - if (!to.isPrimitive()) { - return 1; - } - break; - - case JSTYPE_BOOLEAN: - // "boolean" is #1 - if (to == Boolean.TYPE) { - return 1; - } else if (to == ScriptRuntime.BooleanClass) { - return 2; - } else if (to == ScriptRuntime.ObjectClass) { - return 3; - } else if (to == ScriptRuntime.StringClass) { - return 4; - } - break; - - case JSTYPE_NUMBER: - case JSTYPE_BIGINT: - if (to.isPrimitive()) { - if (to == Double.TYPE) { - return 1; - } else if (to != Boolean.TYPE) { - return 1 + getSizeRank(to); - } - } else { - if (to == ScriptRuntime.StringClass) { - // native numbers are #1-8 - return 9; - } else if (to == ScriptRuntime.BigIntegerClass) { - return 10; - } else if (to == ScriptRuntime.ObjectClass) { - return 11; - } else if (ScriptRuntime.NumberClass.isAssignableFrom(to)) { - // "double" is #1 - return 2; - } - } - break; - - case JSTYPE_STRING: - if (to == ScriptRuntime.StringClass) { - return 1; - } else if (to.isInstance(fromObj)) { - return 2; - } else if (to.isPrimitive()) { - if (to == Character.TYPE) { - return 3; - } else if (to != Boolean.TYPE) { - return 4; - } - } - break; - - case JSTYPE_JAVA_CLASS: - if (to == ScriptRuntime.ClassClass) { - return 1; - } else if (to == ScriptRuntime.ObjectClass) { - return 3; - } else if (to == ScriptRuntime.StringClass) { - return 4; - } - break; - - case JSTYPE_JAVA_OBJECT: - case JSTYPE_JAVA_ARRAY: - Object javaObj = fromObj; - if (javaObj instanceof Wrapper) { - javaObj = ((Wrapper) javaObj).unwrap(); - } - if (to.isInstance(javaObj)) { - return CONVERSION_NONTRIVIAL; - } - if (to == ScriptRuntime.StringClass) { - return 2; - } else if (to.isPrimitive() && to != Boolean.TYPE) { - return (fromCode == JSTYPE_JAVA_ARRAY) ? CONVERSION_NONE : 2 + getSizeRank(to); - } - break; - - case JSTYPE_OBJECT: - // Other objects takes #1-#3 spots - if (to != ScriptRuntime.ObjectClass && to.isInstance(fromObj)) { - // No conversion required, but don't apply for java.lang.Object - return 1; - } - if (to.isArray()) { - if (fromObj instanceof NativeArray) { - // This is a native array conversion to a java array - // Array conversions are all equal, and preferable to object - // and string conversion, per LC3. - return 2; - } - } else if (to == ScriptRuntime.ObjectClass) { - return 3; - } else if (to == ScriptRuntime.StringClass) { - return 4; - } else if (to == ScriptRuntime.DateClass) { - if (fromObj instanceof NativeDate) { - // This is a native date to java date conversion - return 1; - } - } else if (to.isInterface()) { - - if (fromObj instanceof NativeFunction) { - // See comments in createInterfaceAdapter - return 1; - } - if (fromObj instanceof NativeObject) { - return 2; - } - return 12; - } else if (to.isPrimitive() && to != Boolean.TYPE) { - return 4 + getSizeRank(to); - } - break; - } - - return CONVERSION_NONE; - } - - static int getSizeRank(Class aType) { - if (aType == Double.TYPE) { - return 1; - } else if (aType == Float.TYPE) { - return 2; - } else if (aType == Long.TYPE) { - return 3; - } else if (aType == Integer.TYPE) { - return 4; - } else if (aType == Short.TYPE) { - return 5; - } else if (aType == Character.TYPE) { - return 6; - } else if (aType == Byte.TYPE) { - return 7; - } else if (aType == Boolean.TYPE) { - return CONVERSION_NONE; - } else { - return 8; - } - } - - private static int getJSTypeCode(Object value) { - if (value == null) { - return JSTYPE_NULL; - } else if (value == Undefined.instance) { - return JSTYPE_UNDEFINED; - } else if (value instanceof CharSequence) { - return JSTYPE_STRING; - } else if (value instanceof BigInteger) { - return JSTYPE_BIGINT; - } else if (value instanceof Number) { - return JSTYPE_NUMBER; - } else if (value instanceof Boolean) { - return JSTYPE_BOOLEAN; - } else if (value instanceof Scriptable) { - if (value instanceof NativeJavaClass) { - return JSTYPE_JAVA_CLASS; - } else if (value instanceof NativeJavaArray) { - return JSTYPE_JAVA_ARRAY; - } else if (value instanceof Wrapper) { - return JSTYPE_JAVA_OBJECT; - } else { - return JSTYPE_OBJECT; - } - } else if (value instanceof Class) { - return JSTYPE_JAVA_CLASS; - } else { - Class valueClass = value.getClass(); - if (valueClass.isArray()) { - return JSTYPE_JAVA_ARRAY; - } - return JSTYPE_JAVA_OBJECT; - } - } - - /** - * Not intended for public use. Callers should use the public API Context.toType. - * - * @deprecated as of 1.5 Release 4 - * @see org.mozilla.javascript.Context#jsToJava(Object, Class) - */ - @Deprecated - public static Object coerceType(Class type, Object value) { - return coerceTypeImpl(type, value); - } - - /** Type-munging for field setting and method invocation. Conforms to LC3 specification */ - static Object coerceTypeImpl(Class type, Object value) { - if (value != null && value.getClass() == type) { - return value; - } - - int jsTypeCode = getJSTypeCode(value); - switch (jsTypeCode) { - case JSTYPE_NULL: - // raise error if type.isPrimitive() - if (type.isPrimitive()) { - reportConversionError(value, type); - } - return null; - - case JSTYPE_UNDEFINED: - if (type == ScriptRuntime.StringClass || type == ScriptRuntime.ObjectClass) { - return "undefined"; - } - reportConversionError("undefined", type); - break; - - case JSTYPE_BOOLEAN: - // Under LC3, only JS Booleans can be coerced into a Boolean value - if (type == Boolean.TYPE - || type == ScriptRuntime.BooleanClass - || type == ScriptRuntime.ObjectClass) { - return value; - } else if (type == ScriptRuntime.StringClass) { - return value.toString(); - } else { - reportConversionError(value, type); - } - break; - - case JSTYPE_NUMBER: - case JSTYPE_BIGINT: - if (type == ScriptRuntime.StringClass) { - return ScriptRuntime.toString(value); - } else if (type == ScriptRuntime.ObjectClass) { - Context context = Context.getCurrentContext(); - if ((context != null) - && context.hasFeature(Context.FEATURE_INTEGER_WITHOUT_DECIMAL_PLACE)) { - // to process numbers like 2.0 as 2 without decimal place - long roundedValue = Math.round(toDouble(value)); - if (roundedValue == toDouble(value)) { - return coerceToNumber(Long.TYPE, value); - } - } - return coerceToNumber( - jsTypeCode == JSTYPE_BIGINT ? BigInteger.class : Double.TYPE, value); - } else if ((type.isPrimitive() && type != Boolean.TYPE) - || ScriptRuntime.NumberClass.isAssignableFrom(type)) { - return coerceToNumber(type, value); - } else { - reportConversionError(value, type); - } - break; - - case JSTYPE_STRING: - if (type == ScriptRuntime.StringClass || type.isInstance(value)) { - return value.toString(); - } else if (type == Character.TYPE || type == ScriptRuntime.CharacterClass) { - // Special case for converting a single char string to a - // character - // Placed here because it applies *only* to JS strings, - // not other JS objects converted to strings - if (((CharSequence) value).length() == 1) { - return Character.valueOf(((CharSequence) value).charAt(0)); - } - return coerceToNumber(type, value); - } else if ((type.isPrimitive() && type != Boolean.TYPE) - || ScriptRuntime.NumberClass.isAssignableFrom(type)) { - return coerceToNumber(type, value); - } else { - reportConversionError(value, type); - } - break; - - case JSTYPE_JAVA_CLASS: - if (value instanceof Wrapper) { - value = ((Wrapper) value).unwrap(); - } - - if (type == ScriptRuntime.ClassClass || type == ScriptRuntime.ObjectClass) { - return value; - } else if (type == ScriptRuntime.StringClass) { - return value.toString(); - } else { - reportConversionError(value, type); - } - break; - - case JSTYPE_JAVA_OBJECT: - case JSTYPE_JAVA_ARRAY: - if (value instanceof Wrapper) { - value = ((Wrapper) value).unwrap(); - } - if (type.isPrimitive()) { - if (type == Boolean.TYPE) { - reportConversionError(value, type); - } - return coerceToNumber(type, value); - } - if (type == ScriptRuntime.StringClass) { - return value.toString(); - } - if (type.isInstance(value)) { - return value; - } - reportConversionError(value, type); - break; - - case JSTYPE_OBJECT: - if (type == ScriptRuntime.StringClass) { - return ScriptRuntime.toString(value); - } else if (type.isPrimitive()) { - if (type == Boolean.TYPE) { - reportConversionError(value, type); - } - return coerceToNumber(type, value); - } else if (type.isInstance(value)) { - return value; - } else if (type == ScriptRuntime.DateClass && value instanceof NativeDate) { - double time = ((NativeDate) value).getJSTimeValue(); - // XXX: This will replace NaN by 0 - return new Date((long) time); - } else if (type.isArray() && value instanceof NativeArray) { - // Make a new java array, and coerce the JS array components - // to the target (component) type. - NativeArray array = (NativeArray) value; - long length = array.getLength(); - Class arrayType = type.getComponentType(); - Object Result = Array.newInstance(arrayType, (int) length); - for (int i = 0; i < length; ++i) { - try { - Array.set(Result, i, coerceTypeImpl(arrayType, array.get(i, array))); - } catch (EvaluatorException ee) { - reportConversionError(value, type); - } - } - - return Result; - } else if (value instanceof Wrapper) { - value = ((Wrapper) value).unwrap(); - if (type.isInstance(value)) return value; - reportConversionError(value, type); - } else if (type.isInterface() - && (value instanceof NativeObject - || (value instanceof Callable - && value instanceof ScriptableObject))) { - // Try to use function/object as implementation of Java interface. - return createInterfaceAdapter(type, (ScriptableObject) value); - } else { - reportConversionError(value, type); - } - break; - } - - return value; - } - - protected static Object createInterfaceAdapter(Class type, ScriptableObject so) { - // XXX: Currently only instances of ScriptableObject are - // supported since the resulting interface proxies should - // be reused next time conversion is made and generic - // Callable has no storage for it. Weak references can - // address it but for now use this restriction. - - Object key = Kit.makeHashKeyFromPair(COERCED_INTERFACE_KEY, type); - Object old = so.getAssociatedValue(key); - if (old != null) { - // Function was already wrapped - return old; - } - Context cx = Context.getContext(); - Object glue = InterfaceAdapter.create(cx, type, so); - // Store for later retrieval - glue = so.associateValue(key, glue); - return glue; - } - - private static Object coerceToNumber(Class type, Object value) { - Class valueClass = value.getClass(); - - // Character - if (type == Character.TYPE || type == ScriptRuntime.CharacterClass) { - if (valueClass == ScriptRuntime.CharacterClass) { - return value; - } - return Character.valueOf( - (char) - toInteger( - value, - ScriptRuntime.CharacterClass, - Character.MIN_VALUE, - Character.MAX_VALUE)); - } - - // Double, Float - if (type == ScriptRuntime.ObjectClass - || type == ScriptRuntime.DoubleClass - || type == Double.TYPE) { - if (valueClass == ScriptRuntime.DoubleClass) { - return value; - } - return Double.valueOf(toDouble(value)); - } - - if (type == ScriptRuntime.BigIntegerClass) { - if (valueClass == ScriptRuntime.BigIntegerClass) { - return value; - } - return ScriptRuntime.toBigInt(value); - } - - if (type == ScriptRuntime.FloatClass || type == Float.TYPE) { - if (valueClass == ScriptRuntime.FloatClass) { - return value; - } - double number = toDouble(value); - if (Double.isInfinite(number) || Double.isNaN(number) || number == 0.0) { - return Float.valueOf((float) number); - } - - double absNumber = Math.abs(number); - if (absNumber < Float.MIN_VALUE) { - return Float.valueOf((number > 0.0) ? +0.0f : -0.0f); - } else if (absNumber > Float.MAX_VALUE) { - return Float.valueOf( - (number > 0.0) ? Float.POSITIVE_INFINITY : Float.NEGATIVE_INFINITY); - } else { - return Float.valueOf((float) number); - } - } - - // Integer, Long, Short, Byte - if (type == ScriptRuntime.IntegerClass || type == Integer.TYPE) { - if (valueClass == ScriptRuntime.IntegerClass) { - return value; - } - return Integer.valueOf( - (int) - toInteger( - value, - ScriptRuntime.IntegerClass, - Integer.MIN_VALUE, - Integer.MAX_VALUE)); - } - - if (type == ScriptRuntime.LongClass || type == Long.TYPE) { - if (valueClass == ScriptRuntime.LongClass) { - return value; - } - /* Long values cannot be expressed exactly in doubles. - * We thus use the largest and smallest double value that - * has a value expressible as a long value. We build these - * numerical values from their hexidecimal representations - * to avoid any problems caused by attempting to parse a - * decimal representation. - */ - final double max = Double.longBitsToDouble(0x43dfffffffffffffL); - final double min = Double.longBitsToDouble(0xc3e0000000000000L); - return Long.valueOf(toInteger(value, ScriptRuntime.LongClass, min, max)); - } - - if (type == ScriptRuntime.ShortClass || type == Short.TYPE) { - if (valueClass == ScriptRuntime.ShortClass) { - return value; - } - return Short.valueOf( - (short) - toInteger( - value, - ScriptRuntime.ShortClass, - Short.MIN_VALUE, - Short.MAX_VALUE)); - } - - if (type == ScriptRuntime.ByteClass || type == Byte.TYPE) { - if (valueClass == ScriptRuntime.ByteClass) { - return value; - } - return Byte.valueOf( - (byte) - toInteger( - value, - ScriptRuntime.ByteClass, - Byte.MIN_VALUE, - Byte.MAX_VALUE)); - } - - return Double.valueOf(toDouble(value)); - } - - private static double toDouble(Object value) { - if (value instanceof Number) { - return ((Number) value).doubleValue(); - } else if (value instanceof String) { - return ScriptRuntime.toNumber((String) value); - } else if (value instanceof Scriptable) { - if (value instanceof Wrapper) { - // XXX: optimize tail-recursion? - return toDouble(((Wrapper) value).unwrap()); - } - return ScriptRuntime.toNumber(value); - } else { - Method meth; - try { - meth = value.getClass().getMethod("doubleValue", (Class[]) null); - } catch (NoSuchMethodException e) { - meth = null; - } catch (SecurityException e) { - meth = null; - } - if (meth != null) { - try { - return ((Number) meth.invoke(value, (Object[]) null)).doubleValue(); - } catch (IllegalAccessException e) { - // XXX: ignore, or error message? - reportConversionError(value, Double.TYPE); - } catch (InvocationTargetException e) { - // XXX: ignore, or error message? - reportConversionError(value, Double.TYPE); - } - } - return ScriptRuntime.toNumber(value.toString()); - } - } - - private static long toInteger(Object value, Class type, double min, double max) { - double d = toDouble(value); - - if (Double.isInfinite(d) || Double.isNaN(d)) { - // Convert to string first, for more readable message - reportConversionError(ScriptRuntime.toString(value), type); - } - - if (d > 0.0) { - d = Math.floor(d); - } else { - d = Math.ceil(d); - } - - if (d < min || d > max) { - // Convert to string first, for more readable message - reportConversionError(ScriptRuntime.toString(value), type); - } - return (long) d; - } - - static void reportConversionError(Object value, Class type) { - // It uses String.valueOf(value), not value.toString() since - // value can be null, bug 282447. - throw Context.reportRuntimeErrorById( - "msg.conversion.not.allowed", - String.valueOf(value), - JavaMembers.javaSignature(type)); - } - - private void writeObject(ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - - out.writeBoolean(isAdapter); - if (isAdapter) { - if (adapter_writeAdapterObject == null) { - throw new IOException(); - } - Object[] args = {javaObject, out}; - try { - adapter_writeAdapterObject.invoke(null, args); - } catch (Exception ex) { - throw new IOException(); - } - } else { - out.writeObject(javaObject); - } - - if (staticType != null) { - out.writeObject(staticType.getName()); - } else { - out.writeObject(null); - } - } - - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - - isAdapter = in.readBoolean(); - if (isAdapter) { - if (adapter_readAdapterObject == null) throw new ClassNotFoundException(); - Object[] args = {this, in}; - try { - javaObject = adapter_readAdapterObject.invoke(null, args); - } catch (Exception ex) { - throw new IOException(); - } - } else { - javaObject = in.readObject(); - } - - String className = (String) in.readObject(); - if (className != null) { - staticType = Class.forName(className); - } else { - staticType = null; - } - - initMembers(); - } - - private static Callable symbol_iterator = - (Context cx, Scriptable scope, Scriptable thisObj, Object[] args) -> { - if (!(thisObj instanceof NativeJavaObject)) { - throw ScriptRuntime.typeErrorById("msg.incompat.call", SymbolKey.ITERATOR); - } - Object javaObject = ((NativeJavaObject) thisObj).javaObject; - if (!(javaObject instanceof Iterable)) { - throw ScriptRuntime.typeErrorById("msg.incompat.call", SymbolKey.ITERATOR); - } - return new JavaIterableIterator(scope, (Iterable) javaObject); - }; - - private static final class JavaIterableIterator extends ES6Iterator { - private static final long serialVersionUID = 1L; - private static final String ITERATOR_TAG = "JavaIterableIterator"; - - static void init(ScriptableObject scope, boolean sealed) { - ES6Iterator.init(scope, sealed, new JavaIterableIterator(), ITERATOR_TAG); - } - - /** Only for constructing the prototype object. */ - private JavaIterableIterator() { - super(); - } - - JavaIterableIterator(Scriptable scope, Iterable iterable) { - super(scope, ITERATOR_TAG); - this.iterator = iterable.iterator(); - } - - @Override - public String getClassName() { - return "Java Iterable Iterator"; - } - - @Override - protected boolean isDone(Context cx, Scriptable scope) { - return !iterator.hasNext(); - } - - @Override - protected Object nextValue(Context cx, Scriptable scope) { - if (!iterator.hasNext()) { - return Undefined.instance; - } - return iterator.next(); - } - - @Override - protected String getTag() { - return ITERATOR_TAG; - } - - private Iterator iterator; - } - - /** The prototype of this object. */ - protected Scriptable prototype; - - /** The parent scope of this object. */ - protected Scriptable parent; - - protected transient Object javaObject; - - protected transient Class staticType; - protected transient JavaMembers members; - private transient Map fieldAndMethods; - protected transient boolean isAdapter; - - private static final Object COERCED_INTERFACE_KEY = "Coerced Interface"; - private static Method adapter_writeAdapterObject; - private static Method adapter_readAdapterObject; - - static { - // Reflection in java is verbose - Class[] sig2 = new Class[2]; - Class cl = Kit.classOrNull("org.mozilla.javascript.JavaAdapter"); - if (cl != null) { - try { - sig2[0] = ScriptRuntime.ObjectClass; - sig2[1] = Kit.classOrNull("java.io.ObjectOutputStream"); - adapter_writeAdapterObject = cl.getMethod("writeAdapterObject", sig2); - - sig2[0] = ScriptRuntime.ScriptableClass; - sig2[1] = Kit.classOrNull("java.io.ObjectInputStream"); - adapter_readAdapterObject = cl.getMethod("readAdapterObject", sig2); - - } catch (NoSuchMethodException e) { - adapter_writeAdapterObject = null; - adapter_readAdapterObject = null; - } - } - } -} diff --git a/src/main/java/org/mozilla/javascript/NativeJavaPackage.java b/src/main/java/org/mozilla/javascript/NativeJavaPackage.java deleted file mode 100644 index 7b4cbb8d..00000000 --- a/src/main/java/org/mozilla/javascript/NativeJavaPackage.java +++ /dev/null @@ -1,198 +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.IOException; -import java.io.ObjectInputStream; -import java.util.HashSet; -import java.util.Set; - -/** - * This class reflects Java packages into the JavaScript environment. We - * lazily reflect classes and subpackages, and use a caching/sharing - * system to ensure that members reflected into one JavaPackage appear - * in all other references to the same package (as with Packages.java.lang - * and java.lang). - * - * @author Mike Shaver - * @see NativeJavaArray - * @see NativeJavaObject - * @see NativeJavaClass - */ - -public class NativeJavaPackage extends ScriptableObject -{ - private static final long serialVersionUID = 7445054382212031523L; - - NativeJavaPackage(boolean internalUsage, String packageName, - ClassLoader classLoader) - { - this.packageName = packageName; - this.classLoader = classLoader; - } - - /** - * @deprecated NativeJavaPackage is an internal class, do not use - * it directly. - */ - @Deprecated - public NativeJavaPackage(String packageName, ClassLoader classLoader) { - this(false, packageName, classLoader); - } - - /** - * @deprecated NativeJavaPackage is an internal class, do not use - * it directly. - */ - @Deprecated - public NativeJavaPackage(String packageName) { - this(false, packageName, - Context.getCurrentContext().getApplicationClassLoader()); - } - - @Override - public String getClassName() { - return "JavaPackage"; - } - - @Override - public boolean has(String id, Scriptable start) { - return true; - } - - @Override - public boolean has(int index, Scriptable start) { - return false; - } - - @Override - public void put(String id, Scriptable start, Object value) { - // Can't add properties to Java packages. Sorry. - } - - @Override - public void put(int index, Scriptable start, Object value) { - throw Context.reportRuntimeErrorById("msg.pkg.int"); - } - - @Override - public Object get(String id, Scriptable start) { - // # ZOTE CORE MODIFIED PART - try { - return getPkgProperty(id, start, true); - } catch (NullPointerException ignore) {} - return NOT_FOUND; - // # END - } - - @Override - public Object get(int index, Scriptable start) { - return NOT_FOUND; - } - - // set up a name which is known to be a package so we don't - // need to look for a class by that name - NativeJavaPackage forcePackage(String name, Scriptable scope) - { - Object cached = super.get(name, this); - if (cached != null && cached instanceof NativeJavaPackage) { - return (NativeJavaPackage) cached; - } - String newPackage = packageName.length() == 0 - ? name - : packageName + "." + name; - NativeJavaPackage pkg = new NativeJavaPackage(true, newPackage, classLoader); - ScriptRuntime.setObjectProtoAndParent(pkg, scope); - super.put(name, this, pkg); - return pkg; - } - - synchronized Object getPkgProperty(String name, Scriptable start, - boolean createPkg) - { - Object cached = super.get(name, start); - if (cached != NOT_FOUND) - return cached; - if (negativeCache != null && negativeCache.contains(name)) { - // Performance optimization: see bug 421071 - return null; - } - - String className = (packageName.length() == 0) - ? name : packageName + '.' + name; - Context cx = Context.getContext(); - ClassShutter shutter = cx.getClassShutter(); - Scriptable newValue = null; - if (shutter == null || shutter.visibleToScripts(className)) { - Class cl = null; - if (classLoader != null) { - cl = Kit.classOrNull(classLoader, className); - } else { - cl = Kit.classOrNull(className); - } - if (cl != null) { - WrapFactory wrapFactory = cx.getWrapFactory(); - newValue = wrapFactory.wrapJavaClass(cx, getTopLevelScope(this), cl); - newValue.setPrototype(getPrototype()); - } - } - if (newValue == null) { - if (createPkg) { - NativeJavaPackage pkg; - pkg = new NativeJavaPackage(true, className, classLoader); - ScriptRuntime.setObjectProtoAndParent(pkg, getParentScope()); - newValue = pkg; - } else { - // add to negative cache - if (negativeCache == null) - negativeCache = new HashSet(); - negativeCache.add(name); - } - } - if (newValue != null) { - // Make it available for fast lookup and sharing of - // lazily-reflected constructors and static members. - super.put(name, start, newValue); - } - return newValue; - } - - @Override - public Object getDefaultValue(Class ignored) { - return toString(); - } - - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - this.classLoader = Context.getCurrentContext().getApplicationClassLoader(); - } - - @Override - public String toString() { - return "[JavaPackage " + packageName + "]"; - } - - @Override - public boolean equals(Object obj) { - if(obj instanceof NativeJavaPackage) { - NativeJavaPackage njp = (NativeJavaPackage)obj; - return packageName.equals(njp.packageName) && - classLoader == njp.classLoader; - } - return false; - } - - @Override - public int hashCode() { - return packageName.hashCode() ^ - (classLoader == null ? 0 : classLoader.hashCode()); - } - - private String packageName; - private transient ClassLoader classLoader; - private Set negativeCache = null; -} diff --git a/src/main/java/org/mozilla/javascript/NativeJavaTopPackage.java b/src/main/java/org/mozilla/javascript/NativeJavaTopPackage.java deleted file mode 100644 index c6879157..00000000 --- a/src/main/java/org/mozilla/javascript/NativeJavaTopPackage.java +++ /dev/null @@ -1,157 +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; - -/** - * This class reflects Java packages into the JavaScript environment. We - * lazily reflect classes and subpackages, and use a caching/sharing - * system to ensure that members reflected into one JavaPackage appear - * in all other references to the same package (as with Packages.java.lang - * and java.lang). - * - * @author Mike Shaver - * @see NativeJavaArray - * @see NativeJavaObject - * @see NativeJavaClass - */ - -public class NativeJavaTopPackage - extends NativeJavaPackage implements Function, IdFunctionCall -{ - private static final long serialVersionUID = -1455787259477709999L; - - // we know these are packages so we can skip the class check - // note that this is ok even if the package isn't present. - private static final String[][] commonPackages = { - {"java", "lang", "reflect"}, - {"java", "io"}, - {"java", "math"}, - {"java", "net"}, - {"java", "util", "zip"}, - {"java", "text", "resources"}, - {"java", "applet"}, - {"javax", "swing"} - }; - - NativeJavaTopPackage(ClassLoader loader) - { - super(true, "", loader); - } - - @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, - Object[] args) - { - return construct(cx, scope, args); - } - - @Override - public Scriptable construct(Context cx, Scriptable scope, Object[] args) - { - ClassLoader loader = null; - if (args.length != 0) { - Object arg = args[0]; - if (arg instanceof Wrapper) { - arg = ((Wrapper)arg).unwrap(); - } - if (arg instanceof ClassLoader) { - loader = (ClassLoader)arg; - } - } - if (loader == null) { - Context.reportRuntimeErrorById("msg.not.classloader"); - return null; - } - NativeJavaPackage pkg = new NativeJavaPackage(true, "", loader); - ScriptRuntime.setObjectProtoAndParent(pkg, scope); - return pkg; - } - - public static void init(Context cx, Scriptable scope, boolean sealed) - { - ClassLoader loader = cx.getApplicationClassLoader(); - final NativeJavaTopPackage top = new NativeJavaTopPackage(loader); - top.setPrototype(getObjectPrototype(scope)); - top.setParentScope(scope); - - for (int i = 0; i != commonPackages.length; i++) { - NativeJavaPackage parent = top; - for (int j = 0; j != commonPackages[i].length; j++) { - parent = parent.forcePackage(commonPackages[i][j], scope); - } - } - - // getClass implementation - IdFunctionObject getClass = new IdFunctionObject(top, FTAG, Id_getClass, - "getClass", 1, scope); - - // We want to get a real alias, and not a distinct JavaPackage - // with the same packageName, so that we share classes and top - // that are underneath. - String[] topNames = ScriptRuntime.getTopPackageNames(); - NativeJavaPackage[] topPackages = new NativeJavaPackage[topNames.length]; - for (int i=0; i < topNames.length; i++) { - topPackages[i] = (NativeJavaPackage)top.get(topNames[i], top); - } - - // It's safe to downcast here since initStandardObjects takes - // a ScriptableObject. - ScriptableObject global = (ScriptableObject) scope; - - if (sealed) { - getClass.sealObject(); - } - getClass.exportAsScopeProperty(); - global.defineProperty("Packages", top, ScriptableObject.DONTENUM); - for (int i=0; i < topNames.length; i++) { - global.defineProperty(topNames[i], topPackages[i], - ScriptableObject.DONTENUM); - } - } - - @Override - public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) - { - if (f.hasTag(FTAG)) { - if (f.methodId() == Id_getClass) { - return js_getClass(cx, scope, args); - } - } - throw f.unknown(); - } - - private Scriptable js_getClass(Context cx, Scriptable scope, Object[] args) - { - if (args.length > 0 && args[0] instanceof Wrapper) { - Scriptable result = this; - Class cl = ((Wrapper) args[0]).unwrap().getClass(); - // Evaluate the class name by getting successive properties of - // the string to find the appropriate NativeJavaClass object - String name = cl.getName(); - int offset = 0; - for (;;) { - int index = name.indexOf('.', offset); - String propName = index == -1 - ? name.substring(offset) - : name.substring(offset, index); - Object prop = result.get(propName, result); - if (!(prop instanceof Scriptable)) - break; // fall through to error - result = (Scriptable) prop; - if (index == -1) - return result; - offset = index+1; - } - } - throw Context.reportRuntimeErrorById("msg.not.java.obj"); - } - - private static final Object FTAG = "JavaTopPackage"; - private static final int Id_getClass = 1; -} - diff --git a/src/main/java/org/mozilla/javascript/NativeMap.java b/src/main/java/org/mozilla/javascript/NativeMap.java deleted file mode 100644 index 0e254d76..00000000 --- a/src/main/java/org/mozilla/javascript/NativeMap.java +++ /dev/null @@ -1,352 +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.Iterator; - -public class NativeMap extends IdScriptableObject { - private static final long serialVersionUID = 1171922614280016891L; - private static final Object MAP_TAG = "Map"; - static final String ITERATOR_TAG = "Map Iterator"; - - private final Hashtable entries = new Hashtable(); - - private boolean instanceOfMap = false; - - static void init(Context cx, Scriptable scope, boolean sealed) { - NativeMap obj = new NativeMap(); - obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, false); - - ScriptableObject desc = (ScriptableObject) cx.newObject(scope); - desc.put("enumerable", desc, Boolean.FALSE); - desc.put("configurable", desc, Boolean.TRUE); - desc.put("get", desc, obj.get(NativeSet.GETSIZE, obj)); - obj.defineOwnProperty(cx, "size", desc); - - if (sealed) { - obj.sealObject(); - } - } - - @Override - public String getClassName() { - return "Map"; - } - - @Override - public Object execIdCall( - IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!f.hasTag(MAP_TAG)) { - return super.execIdCall(f, cx, scope, thisObj, args); - } - int id = f.methodId(); - switch (id) { - case Id_constructor: - if (thisObj == null) { - NativeMap nm = new NativeMap(); - nm.instanceOfMap = true; - if (args.length > 0) { - loadFromIterable(cx, scope, nm, key(args)); - } - return nm; - } - throw ScriptRuntime.typeErrorById("msg.no.new", "Map"); - case Id_set: - return realThis(thisObj, f) - .js_set(key(args), args.length > 1 ? args[1] : Undefined.instance); - case Id_delete: - return realThis(thisObj, f).js_delete(key(args)); - case Id_get: - return realThis(thisObj, f).js_get(key(args)); - case Id_has: - return realThis(thisObj, f).js_has(key(args)); - case Id_clear: - return realThis(thisObj, f).js_clear(); - case Id_keys: - return realThis(thisObj, f).js_iterator(scope, NativeCollectionIterator.Type.KEYS); - case Id_values: - return realThis(thisObj, f) - .js_iterator(scope, NativeCollectionIterator.Type.VALUES); - case Id_entries: - return realThis(thisObj, f).js_iterator(scope, NativeCollectionIterator.Type.BOTH); - case Id_forEach: - return realThis(thisObj, f) - .js_forEach( - cx, - scope, - args.length > 0 ? args[0] : Undefined.instance, - args.length > 1 ? args[1] : Undefined.instance); - case SymbolId_getSize: - return realThis(thisObj, f).js_getSize(); - } - throw new IllegalArgumentException("Map.prototype has no method: " + f.getFunctionName()); - } - - private Object js_set(Object k, Object v) { - // Special handling of "negative zero" from the spec. - Object key = k; - if ((key instanceof Number) && ((Number) key).doubleValue() == ScriptRuntime.negativeZero) { - key = ScriptRuntime.zeroObj; - } - entries.put(key, v); - return this; - } - - private Object js_delete(Object arg) { - return Boolean.valueOf(entries.deleteEntry(arg)); - } - - private Object js_get(Object arg) { - final Hashtable.Entry entry = entries.getEntry(arg); - if (entry == null) { - return Undefined.instance; - } - return entry.value; - } - - private Object js_has(Object arg) { - return Boolean.valueOf(entries.has(arg)); - } - - private Object js_getSize() { - return Integer.valueOf(entries.size()); - } - - private Object js_iterator(Scriptable scope, NativeCollectionIterator.Type type) { - return new NativeCollectionIterator(scope, ITERATOR_TAG, type, entries.iterator()); - } - - private Object js_clear() { - entries.clear(); - return Undefined.instance; - } - - private Object js_forEach(Context cx, Scriptable scope, Object arg1, Object arg2) { - if (!(arg1 instanceof Callable)) { - throw ScriptRuntime.typeErrorById( - "msg.isnt.function", arg1, ScriptRuntime.typeof(arg1)); - } - final Callable f = (Callable) arg1; - - boolean isStrict = cx.isStrictMode(); - Iterator i = entries.iterator(); - while (i.hasNext()) { - // Per spec must convert every time so that primitives are always regenerated... - Scriptable thisObj = ScriptRuntime.toObjectOrNull(cx, arg2, scope); - - if (thisObj == null && !isStrict) { - thisObj = scope; - } - if (thisObj == null) { - thisObj = Undefined.SCRIPTABLE_UNDEFINED; - } - - final Hashtable.Entry e = i.next(); - f.call(cx, scope, thisObj, new Object[] {e.value, e.key, this}); - } - return Undefined.instance; - } - - /** - * If an "iterable" object was passed to the constructor, there are many many things to do... - * Make this static because NativeWeakMap has the exact same requirement. - */ - static void loadFromIterable(Context cx, Scriptable scope, ScriptableObject map, Object arg1) { - if ((arg1 == null) || Undefined.instance.equals(arg1)) { - return; - } - - // Call the "[Symbol.iterator]" property as a function. - final Object ito = ScriptRuntime.callIterator(arg1, cx, scope); - if (Undefined.instance.equals(ito)) { - // Per spec, ignore if the iterator is undefined - return; - } - - // Find the "add" function of our own prototype, since it might have - // been replaced. Since we're not fully constructed yet, create a dummy instance - // so that we can get our own prototype. - Scriptable proto = ScriptableObject.getClassPrototype(scope, map.getClassName()); - final Callable set = ScriptRuntime.getPropFunctionAndThis(proto, "set", cx, scope); - ScriptRuntime.lastStoredScriptable(cx); - - ScriptRuntime.loadFromIterable( - cx, - scope, - arg1, - (key, value) -> { - set.call(cx, scope, map, new Object[] {key, value}); - }); - } - - private static NativeMap realThis(Scriptable thisObj, IdFunctionObject f) { - final NativeMap nm = ensureType(thisObj, NativeMap.class, f); - if (!nm.instanceOfMap) { - // Check for "Map internal data tag" - throw ScriptRuntime.typeErrorById("msg.incompat.call", f.getFunctionName()); - } - - return nm; - } - - @Override - protected void initPrototypeId(int id) { - switch (id) { - case SymbolId_getSize: - initPrototypeMethod(MAP_TAG, id, NativeSet.GETSIZE, "get size", 0); - return; - case SymbolId_toStringTag: - initPrototypeValue( - SymbolId_toStringTag, - SymbolKey.TO_STRING_TAG, - getClassName(), - DONTENUM | READONLY); - return; - // fallthrough - } - - String s, fnName = null; - int arity; - switch (id) { - case Id_constructor: - arity = 0; - s = "constructor"; - break; - case Id_set: - arity = 2; - s = "set"; - break; - case Id_get: - arity = 1; - s = "get"; - break; - case Id_delete: - arity = 1; - s = "delete"; - break; - case Id_has: - arity = 1; - s = "has"; - break; - case Id_clear: - arity = 0; - s = "clear"; - break; - case Id_keys: - arity = 0; - s = "keys"; - break; - case Id_values: - arity = 0; - s = "values"; - break; - case Id_entries: - arity = 0; - s = "entries"; - break; - case Id_forEach: - arity = 1; - s = "forEach"; - break; - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - initPrototypeMethod(MAP_TAG, id, s, fnName, arity); - } - - @Override - protected int findPrototypeId(Symbol k) { - if (NativeSet.GETSIZE.equals(k)) { - return SymbolId_getSize; - } - if (SymbolKey.ITERATOR.equals(k)) { - // ECMA spec says that the "Symbol.iterator" property of the prototype has the - // "same value" as the "entries" property. We implement this by returning the - // ID of "entries" when the iterator symbol is accessed. - return Id_entries; - } - if (SymbolKey.TO_STRING_TAG.equals(k)) { - return SymbolId_toStringTag; - } - return 0; - } - - @Override - protected int findPrototypeId(String s) { - int id; - switch (s) { - case "constructor": - id = Id_constructor; - break; - case "set": - id = Id_set; - break; - case "get": - id = Id_get; - break; - case "delete": - id = Id_delete; - break; - case "has": - id = Id_has; - break; - case "clear": - id = Id_clear; - break; - case "keys": - id = Id_keys; - break; - case "values": - id = Id_values; - break; - case "entries": - id = Id_entries; - break; - case "forEach": - id = Id_forEach; - break; - default: - id = 0; - break; - } - return id; - } - - // Note that "SymbolId_iterator" is not present here. That's because the spec - // requires that it be the same value as the "entries" prototype property. - private static final int Id_constructor = 1, - Id_set = 2, - Id_get = 3, - Id_delete = 4, - Id_has = 5, - Id_clear = 6, - Id_keys = 7, - Id_values = 8, - Id_entries = 9, - Id_forEach = 10, - SymbolId_getSize = 11, - SymbolId_toStringTag = 12, - MAX_PROTOTYPE_ID = SymbolId_toStringTag; - - /** - * Extracts the key from the first args entry if any and takes care of the Delegator. This is - * used by {@code NativeSet}, {@code NativeWeakMap}, and {@code NativeWekSet} also. - * - * @param args the args - * @return the first argument (de-delegated) or undefined if there is no element in args - */ - static Object key(Object[] args) { - if (args.length > 0) { - Object key = args[0]; - if (key instanceof Delegator) { - return ((Delegator) key).getDelegee(); - } - return key; - } - return Undefined.instance; - } -} diff --git a/src/main/java/org/mozilla/javascript/NativeMath.java b/src/main/java/org/mozilla/javascript/NativeMath.java deleted file mode 100644 index c811228c..00000000 --- a/src/main/java/org/mozilla/javascript/NativeMath.java +++ /dev/null @@ -1,784 +0,0 @@ -/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; 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; - -/** - * This class implements the Math native object. See ECMA 15.8. - * - * @author Norris Boyd - */ -final class NativeMath extends IdScriptableObject { - private static final long serialVersionUID = -8838847185801131569L; - - private static final Object MATH_TAG = "Math"; - private static final double LOG2E = 1.4426950408889634; - private static final Double Double32 = Double.valueOf(32d); - - static void init(Scriptable scope, boolean sealed) { - NativeMath obj = new NativeMath(); - obj.activatePrototypeMap(MAX_ID); - obj.setPrototype(getObjectPrototype(scope)); - obj.setParentScope(scope); - if (sealed) { - obj.sealObject(); - } - ScriptableObject.defineProperty(scope, "Math", obj, ScriptableObject.DONTENUM); - } - - private NativeMath() {} - - @Override - public String getClassName() { - return "Math"; - } - - @Override - protected void initPrototypeId(int id) { - if (id <= LAST_METHOD_ID) { - String name; - int arity; - switch (id) { - case Id_toSource: - arity = 0; - name = "toSource"; - break; - case Id_abs: - arity = 1; - name = "abs"; - break; - case Id_acos: - arity = 1; - name = "acos"; - break; - case Id_acosh: - arity = 1; - name = "acosh"; - break; - case Id_asin: - arity = 1; - name = "asin"; - break; - case Id_asinh: - arity = 1; - name = "asinh"; - break; - case Id_atan: - arity = 1; - name = "atan"; - break; - case Id_atanh: - arity = 1; - name = "atanh"; - break; - case Id_atan2: - arity = 2; - name = "atan2"; - break; - case Id_cbrt: - arity = 1; - name = "cbrt"; - break; - case Id_ceil: - arity = 1; - name = "ceil"; - break; - case Id_clz32: - arity = 1; - name = "clz32"; - break; - case Id_cos: - arity = 1; - name = "cos"; - break; - case Id_cosh: - arity = 1; - name = "cosh"; - break; - case Id_exp: - arity = 1; - name = "exp"; - break; - case Id_expm1: - arity = 1; - name = "expm1"; - break; - case Id_floor: - arity = 1; - name = "floor"; - break; - case Id_fround: - arity = 1; - name = "fround"; - break; - case Id_hypot: - arity = 2; - name = "hypot"; - break; - case Id_imul: - arity = 2; - name = "imul"; - break; - case Id_log: - arity = 1; - name = "log"; - break; - case Id_log1p: - arity = 1; - name = "log1p"; - break; - case Id_log10: - arity = 1; - name = "log10"; - break; - case Id_log2: - arity = 1; - name = "log2"; - break; - case Id_max: - arity = 2; - name = "max"; - break; - case Id_min: - arity = 2; - name = "min"; - break; - case Id_pow: - arity = 2; - name = "pow"; - break; - case Id_random: - arity = 0; - name = "random"; - break; - case Id_round: - arity = 1; - name = "round"; - break; - case Id_sign: - arity = 1; - name = "sign"; - break; - case Id_sin: - arity = 1; - name = "sin"; - break; - case Id_sinh: - arity = 1; - name = "sinh"; - break; - case Id_sqrt: - arity = 1; - name = "sqrt"; - break; - case Id_tan: - arity = 1; - name = "tan"; - break; - case Id_tanh: - arity = 1; - name = "tanh"; - break; - case Id_trunc: - arity = 1; - name = "trunc"; - break; - default: - throw new IllegalStateException(String.valueOf(id)); - } - initPrototypeMethod(MATH_TAG, id, name, arity); - } else { - String name; - double x; - switch (id) { - case Id_E: - x = Math.E; - name = "E"; - break; - case Id_PI: - x = Math.PI; - name = "PI"; - break; - case Id_LN10: - x = 2.302585092994046; - name = "LN10"; - break; - case Id_LN2: - x = 0.6931471805599453; - name = "LN2"; - break; - case Id_LOG2E: - x = LOG2E; - name = "LOG2E"; - break; - case Id_LOG10E: - x = 0.4342944819032518; - name = "LOG10E"; - break; - case Id_SQRT1_2: - x = 0.7071067811865476; - name = "SQRT1_2"; - break; - case Id_SQRT2: - x = 1.4142135623730951; - name = "SQRT2"; - break; - default: - throw new IllegalStateException(String.valueOf(id)); - } - initPrototypeValue( - id, name, ScriptRuntime.wrapNumber(x), DONTENUM | READONLY | PERMANENT); - } - } - - @Override - public Object execIdCall( - IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!f.hasTag(MATH_TAG)) { - return super.execIdCall(f, cx, scope, thisObj, args); - } - double x; - int methodId = f.methodId(); - switch (methodId) { - case Id_toSource: - return "Math"; - - case Id_abs: - x = ScriptRuntime.toNumber(args, 0); - // abs(-0.0) should be 0.0, but -0.0 < 0.0 == false - x = (x == 0.0) ? 0.0 : (x < 0.0) ? -x : x; - break; - - case Id_acos: - case Id_asin: - x = ScriptRuntime.toNumber(args, 0); - if (!Double.isNaN(x) && -1.0 <= x && x <= 1.0) { - x = (methodId == Id_acos) ? Math.acos(x) : Math.asin(x); - } else { - x = Double.NaN; - } - break; - - case Id_acosh: - x = ScriptRuntime.toNumber(args, 0); - if (!Double.isNaN(x)) { - return Double.valueOf(Math.log(x + Math.sqrt(x * x - 1.0))); - } - return ScriptRuntime.NaNobj; - - case Id_asinh: - x = ScriptRuntime.toNumber(args, 0); - if (Double.isInfinite(x)) { - return Double.valueOf(x); - } - if (!Double.isNaN(x)) { - if (x == 0) { - if (1 / x > 0) { - return ScriptRuntime.zeroObj; - } - return ScriptRuntime.negativeZeroObj; - } - return Double.valueOf(Math.log(x + Math.sqrt(x * x + 1.0))); - } - return ScriptRuntime.NaNobj; - - case Id_atan: - x = ScriptRuntime.toNumber(args, 0); - x = Math.atan(x); - break; - - case Id_atanh: - x = ScriptRuntime.toNumber(args, 0); - if (!Double.isNaN(x) && -1.0 <= x && x <= 1.0) { - if (x == 0) { - if (1 / x > 0) { - return ScriptRuntime.zeroObj; - } - return ScriptRuntime.negativeZeroObj; - } - return Double.valueOf(0.5 * Math.log((x + 1.0) / (x - 1.0))); - } - return ScriptRuntime.NaNobj; - - case Id_atan2: - x = ScriptRuntime.toNumber(args, 0); - x = Math.atan2(x, ScriptRuntime.toNumber(args, 1)); - break; - - case Id_cbrt: - x = ScriptRuntime.toNumber(args, 0); - x = Math.cbrt(x); - break; - - case Id_ceil: - x = ScriptRuntime.toNumber(args, 0); - x = Math.ceil(x); - break; - - case Id_clz32: - x = ScriptRuntime.toNumber(args, 0); - if (x == 0 || Double.isNaN(x) || Double.isInfinite(x)) { - return Double32; - } - long n = ScriptRuntime.toUint32(x); - if (n == 0) { - return Double32; - } - return Double.valueOf(31 - Math.floor(Math.log(n >>> 0) * LOG2E)); - - case Id_cos: - x = ScriptRuntime.toNumber(args, 0); - x = Double.isInfinite(x) ? Double.NaN : Math.cos(x); - break; - - case Id_cosh: - x = ScriptRuntime.toNumber(args, 0); - x = Math.cosh(x); - break; - - case Id_hypot: - x = js_hypot(args); - break; - - case Id_exp: - x = ScriptRuntime.toNumber(args, 0); - x = - (x == Double.POSITIVE_INFINITY) - ? x - : (x == Double.NEGATIVE_INFINITY) ? 0.0 : Math.exp(x); - break; - - case Id_expm1: - x = ScriptRuntime.toNumber(args, 0); - x = Math.expm1(x); - break; - - case Id_floor: - x = ScriptRuntime.toNumber(args, 0); - x = Math.floor(x); - break; - - case Id_fround: - x = ScriptRuntime.toNumber(args, 0); - // Rely on Java to truncate down to a "float" here" - x = (float) x; - break; - - case Id_imul: - x = js_imul(args); - break; - - case Id_log: - x = ScriptRuntime.toNumber(args, 0); - // Java's log(<0) = -Infinity; we need NaN - x = (x < 0) ? Double.NaN : Math.log(x); - break; - - case Id_log1p: - x = ScriptRuntime.toNumber(args, 0); - x = Math.log1p(x); - break; - - case Id_log10: - x = ScriptRuntime.toNumber(args, 0); - x = Math.log10(x); - break; - - case Id_log2: - x = ScriptRuntime.toNumber(args, 0); - // Java's log(<0) = -Infinity; we need NaN - x = (x < 0) ? Double.NaN : Math.log(x) * LOG2E; - break; - - case Id_max: - case Id_min: - x = (methodId == Id_max) ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; - for (int i = 0; i != args.length; ++i) { - double d = ScriptRuntime.toNumber(args[i]); - if (methodId == Id_max) { - // if (x < d) x = d; does not work due to -0.0 >= +0.0 - x = Math.max(x, d); - } else { - x = Math.min(x, d); - } - } - break; - - case Id_pow: - x = ScriptRuntime.toNumber(args, 0); - x = js_pow(x, ScriptRuntime.toNumber(args, 1)); - break; - - case Id_random: - x = Math.random(); - break; - - case Id_round: - x = ScriptRuntime.toNumber(args, 0); - if (!Double.isNaN(x) && !Double.isInfinite(x)) { - // Round only finite x - long l = Math.round(x); - if (l != 0) { - x = l; - } else { - // We must propagate the sign of d into the result - if (x < 0.0) { - x = ScriptRuntime.negativeZero; - } else if (x != 0.0) { - x = 0.0; - } - } - } - break; - - case Id_sign: - x = ScriptRuntime.toNumber(args, 0); - if (!Double.isNaN(x)) { - if (x == 0) { - if (1 / x > 0) { - return ScriptRuntime.zeroObj; - } - return ScriptRuntime.negativeZeroObj; - } - return Double.valueOf(Math.signum(x)); - } - return ScriptRuntime.NaNobj; - - case Id_sin: - x = ScriptRuntime.toNumber(args, 0); - x = Double.isInfinite(x) ? Double.NaN : Math.sin(x); - break; - - case Id_sinh: - x = ScriptRuntime.toNumber(args, 0); - x = Math.sinh(x); - break; - - case Id_sqrt: - x = ScriptRuntime.toNumber(args, 0); - x = Math.sqrt(x); - break; - - case Id_tan: - x = ScriptRuntime.toNumber(args, 0); - x = Math.tan(x); - break; - - case Id_tanh: - x = ScriptRuntime.toNumber(args, 0); - x = Math.tanh(x); - break; - - case Id_trunc: - x = ScriptRuntime.toNumber(args, 0); - x = js_trunc(x); - break; - - default: - throw new IllegalStateException(String.valueOf(methodId)); - } - return ScriptRuntime.wrapNumber(x); - } - - // See Ecma 15.8.2.13 - private static double js_pow(double x, double y) { - double result; - if (Double.isNaN(y)) { - // y is NaN, result is always NaN - result = y; - } else if (y == 0) { - // Java's pow(NaN, 0) = NaN; we need 1 - result = 1.0; - } else if (x == 0) { - // Many differences from Java's Math.pow - if (1 / x > 0) { - result = (y > 0) ? 0 : Double.POSITIVE_INFINITY; - } else { - // x is -0, need to check if y is an odd integer - long y_long = (long) y; - if (y_long == y && (y_long & 0x1) != 0) { - result = (y > 0) ? -0.0 : Double.NEGATIVE_INFINITY; - } else { - result = (y > 0) ? 0.0 : Double.POSITIVE_INFINITY; - } - } - } else { - result = Math.pow(x, y); - if (Double.isNaN(result)) { - // Check for broken Java implementations that gives NaN - // when they should return something else - if (y == Double.POSITIVE_INFINITY) { - if (x < -1.0 || 1.0 < x) { - result = Double.POSITIVE_INFINITY; - } else if (-1.0 < x && x < 1.0) { - result = 0; - } - } else if (y == Double.NEGATIVE_INFINITY) { - if (x < -1.0 || 1.0 < x) { - result = 0; - } else if (-1.0 < x && x < 1.0) { - result = Double.POSITIVE_INFINITY; - } - } else if (x == Double.POSITIVE_INFINITY) { - result = (y > 0) ? Double.POSITIVE_INFINITY : 0.0; - } else if (x == Double.NEGATIVE_INFINITY) { - long y_long = (long) y; - if (y_long == y && (y_long & 0x1) != 0) { - // y is odd integer - result = (y > 0) ? Double.NEGATIVE_INFINITY : -0.0; - } else { - result = (y > 0) ? Double.POSITIVE_INFINITY : 0.0; - } - } - } - } - return result; - } - - // Based on code from - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/hypot - private static double js_hypot(Object[] args) { - if (args == null) { - return 0.0; - } - double y = 0.0; - - // Spec and tests say that any "Infinity" result takes precedence. - boolean hasNaN = false; - boolean hasInfinity = false; - - for (Object o : args) { - double d = ScriptRuntime.toNumber(o); - if (Double.isNaN(d)) { - hasNaN = true; - } else if (Double.isInfinite(d)) { - hasInfinity = true; - } else { - y += d * d; - } - } - - if (hasInfinity) { - return Double.POSITIVE_INFINITY; - } - if (hasNaN) { - return Double.NaN; - } - return Math.sqrt(y); - } - - private static double js_trunc(double d) { - return ((d < 0.0) ? Math.ceil(d) : Math.floor(d)); - } - - // From EcmaScript 6 section 20.2.2.19 - private static int js_imul(Object[] args) { - if (args == null) { - return 0; - } - - int x = ScriptRuntime.toInt32(args, 0); - int y = ScriptRuntime.toInt32(args, 1); - return x * y; - } - - @Override - protected int findPrototypeId(String s) { - int id; - switch (s) { - case "toSource": - id = Id_toSource; - break; - case "abs": - id = Id_abs; - break; - case "acos": - id = Id_acos; - break; - case "asin": - id = Id_asin; - break; - case "atan": - id = Id_atan; - break; - case "atan2": - id = Id_atan2; - break; - case "ceil": - id = Id_ceil; - break; - case "cos": - id = Id_cos; - break; - case "exp": - id = Id_exp; - break; - case "floor": - id = Id_floor; - break; - case "log": - id = Id_log; - break; - case "max": - id = Id_max; - break; - case "min": - id = Id_min; - break; - case "pow": - id = Id_pow; - break; - case "random": - id = Id_random; - break; - case "round": - id = Id_round; - break; - case "sin": - id = Id_sin; - break; - case "sqrt": - id = Id_sqrt; - break; - case "tan": - id = Id_tan; - break; - case "cbrt": - id = Id_cbrt; - break; - case "cosh": - id = Id_cosh; - break; - case "expm1": - id = Id_expm1; - break; - case "hypot": - id = Id_hypot; - break; - case "log1p": - id = Id_log1p; - break; - case "log10": - id = Id_log10; - break; - case "sinh": - id = Id_sinh; - break; - case "tanh": - id = Id_tanh; - break; - case "imul": - id = Id_imul; - break; - case "trunc": - id = Id_trunc; - break; - case "acosh": - id = Id_acosh; - break; - case "asinh": - id = Id_asinh; - break; - case "atanh": - id = Id_atanh; - break; - case "sign": - id = Id_sign; - break; - case "log2": - id = Id_log2; - break; - case "fround": - id = Id_fround; - break; - case "clz32": - id = Id_clz32; - break; - case "E": - id = Id_E; - break; - case "PI": - id = Id_PI; - break; - case "LN10": - id = Id_LN10; - break; - case "LN2": - id = Id_LN2; - break; - case "LOG2E": - id = Id_LOG2E; - break; - case "LOG10E": - id = Id_LOG10E; - break; - case "SQRT1_2": - id = Id_SQRT1_2; - break; - case "SQRT2": - id = Id_SQRT2; - break; - default: - id = 0; - break; - } - return id; - } - - private static final int Id_toSource = 1, - Id_abs = 2, - Id_acos = 3, - Id_asin = 4, - Id_atan = 5, - Id_atan2 = 6, - Id_ceil = 7, - Id_cos = 8, - Id_exp = 9, - Id_floor = 10, - Id_log = 11, - Id_max = 12, - Id_min = 13, - Id_pow = 14, - Id_random = 15, - Id_round = 16, - Id_sin = 17, - Id_sqrt = 18, - Id_tan = 19, - Id_cbrt = 20, - Id_cosh = 21, - Id_expm1 = 22, - Id_hypot = 23, - Id_log1p = 24, - Id_log10 = 25, - Id_sinh = 26, - Id_tanh = 27, - Id_imul = 28, - Id_trunc = 29, - Id_acosh = 30, - Id_asinh = 31, - Id_atanh = 32, - Id_sign = 33, - Id_log2 = 34, - Id_fround = 35, - Id_clz32 = 36, - LAST_METHOD_ID = Id_clz32; - - /* Missing from ES6: - clz32 - fround - log2 - */ - - private static final int Id_E = LAST_METHOD_ID + 1, - Id_PI = LAST_METHOD_ID + 2, - Id_LN10 = LAST_METHOD_ID + 3, - Id_LN2 = LAST_METHOD_ID + 4, - Id_LOG2E = LAST_METHOD_ID + 5, - Id_LOG10E = LAST_METHOD_ID + 6, - Id_SQRT1_2 = LAST_METHOD_ID + 7, - Id_SQRT2 = LAST_METHOD_ID + 8, - MAX_ID = LAST_METHOD_ID + 8; -} diff --git a/src/main/java/org/mozilla/javascript/NativeNumber.java b/src/main/java/org/mozilla/javascript/NativeNumber.java deleted file mode 100644 index 63f730d6..00000000 --- a/src/main/java/org/mozilla/javascript/NativeNumber.java +++ /dev/null @@ -1,382 +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; - -/** - * This class implements the Number native object. - * - *

See ECMA 15.7. - * - * @author Norris Boyd - */ -final class NativeNumber extends IdScriptableObject { - private static final long serialVersionUID = 3504516769741512101L; - - /** @see https://www.ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer */ - public static final double MAX_SAFE_INTEGER = 9007199254740991.0; // Math.pow(2, 53) - 1 - - private static final Object NUMBER_TAG = "Number"; - - private static final int MAX_PRECISION = 100; - private static final double MIN_SAFE_INTEGER = -MAX_SAFE_INTEGER; - private static final double EPSILON = 2.220446049250313e-16; // Math.pow(2, -52) - - static void init(Scriptable scope, boolean sealed) { - NativeNumber obj = new NativeNumber(0.0); - obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); - } - - NativeNumber(double number) { - doubleValue = number; - } - - @Override - public String getClassName() { - return "Number"; - } - - @Override - protected void fillConstructorProperties(IdFunctionObject ctor) { - final int attr = - ScriptableObject.DONTENUM | ScriptableObject.PERMANENT | ScriptableObject.READONLY; - - ctor.defineProperty("NaN", ScriptRuntime.NaNobj, attr); - ctor.defineProperty( - "POSITIVE_INFINITY", ScriptRuntime.wrapNumber(Double.POSITIVE_INFINITY), attr); - ctor.defineProperty( - "NEGATIVE_INFINITY", ScriptRuntime.wrapNumber(Double.NEGATIVE_INFINITY), attr); - ctor.defineProperty("MAX_VALUE", ScriptRuntime.wrapNumber(Double.MAX_VALUE), attr); - ctor.defineProperty("MIN_VALUE", ScriptRuntime.wrapNumber(Double.MIN_VALUE), attr); - ctor.defineProperty("MAX_SAFE_INTEGER", ScriptRuntime.wrapNumber(MAX_SAFE_INTEGER), attr); - ctor.defineProperty("MIN_SAFE_INTEGER", ScriptRuntime.wrapNumber(MIN_SAFE_INTEGER), attr); - ctor.defineProperty("EPSILON", ScriptRuntime.wrapNumber(EPSILON), attr); - - addIdFunctionProperty(ctor, NUMBER_TAG, ConstructorId_isFinite, "isFinite", 1); - addIdFunctionProperty(ctor, NUMBER_TAG, ConstructorId_isNaN, "isNaN", 1); - addIdFunctionProperty(ctor, NUMBER_TAG, ConstructorId_isInteger, "isInteger", 1); - addIdFunctionProperty(ctor, NUMBER_TAG, ConstructorId_isSafeInteger, "isSafeInteger", 1); - Object parseFloat = ScriptRuntime.getTopLevelProp(ctor, "parseFloat"); - if (parseFloat instanceof IdFunctionObject) { - ((IdFunctionObject) parseFloat).addAsProperty(ctor); - } - Object parseInt = ScriptRuntime.getTopLevelProp(ctor, "parseInt"); - if (parseInt instanceof IdFunctionObject) { - ((IdFunctionObject) parseInt).addAsProperty(ctor); - } - - 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 = 1; - s = "toString"; - break; - case Id_toLocaleString: - arity = 1; - s = "toLocaleString"; - break; - case Id_toSource: - arity = 0; - s = "toSource"; - break; - case Id_valueOf: - arity = 0; - s = "valueOf"; - break; - case Id_toFixed: - arity = 1; - s = "toFixed"; - break; - case Id_toExponential: - arity = 1; - s = "toExponential"; - break; - case Id_toPrecision: - arity = 1; - s = "toPrecision"; - break; - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - initPrototypeMethod(NUMBER_TAG, id, s, arity); - } - - @Override - public Object execIdCall( - IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!f.hasTag(NUMBER_TAG)) { - return super.execIdCall(f, cx, scope, thisObj, args); - } - int id = f.methodId(); - if (id == Id_constructor) { - double val = (args.length >= 1) ? ScriptRuntime.toNumeric(args[0]).doubleValue() : 0.0; - if (thisObj == null) { - // new Number(val) creates a new Number object. - return new NativeNumber(val); - } - // Number(val) converts val to a number value. - return ScriptRuntime.wrapNumber(val); - - } else if (id < Id_constructor) { - return execConstructorCall(id, args); - } - - // The rest of Number.prototype methods require thisObj to be Number - double value = ensureType(thisObj, NativeNumber.class, f).doubleValue; - - switch (id) { - case Id_toString: - case Id_toLocaleString: - { - // toLocaleString is just an alias for toString for now - int base = - (args.length == 0 || Undefined.isUndefined(args[0])) - ? 10 - : ScriptRuntime.toInt32(args[0]); - return ScriptRuntime.numberToString(value, base); - } - - case Id_toSource: - return "(new Number(" + ScriptRuntime.toString(value) + "))"; - - case Id_valueOf: - return ScriptRuntime.wrapNumber(value); - - case Id_toFixed: - int precisionMin = cx.version < Context.VERSION_ES6 ? -20 : 0; - return num_to(value, args, DToA.DTOSTR_FIXED, DToA.DTOSTR_FIXED, precisionMin, 0); - - case Id_toExponential: - { - // Handle special values before range check - if (Double.isNaN(value)) { - return "NaN"; - } - if (Double.isInfinite(value)) { - if (value >= 0) { - return "Infinity"; - } - return "-Infinity"; - } - // General case - return num_to( - value, - args, - DToA.DTOSTR_STANDARD_EXPONENTIAL, - DToA.DTOSTR_EXPONENTIAL, - 0, - 1); - } - - case Id_toPrecision: - { - // Undefined precision, fall back to ToString() - if (args.length == 0 || Undefined.isUndefined(args[0])) { - return ScriptRuntime.numberToString(value, 10); - } - // Handle special values before range check - if (Double.isNaN(value)) { - return "NaN"; - } - if (Double.isInfinite(value)) { - if (value >= 0) { - return "Infinity"; - } - return "-Infinity"; - } - return num_to(value, args, DToA.DTOSTR_STANDARD, DToA.DTOSTR_PRECISION, 1, 0); - } - - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - } - - private static Object execConstructorCall(int id, Object[] args) { - switch (id) { - case ConstructorId_isFinite: - if ((args.length == 0) || Undefined.isUndefined(args[0])) { - return Boolean.FALSE; - } - if (args[0] instanceof Number) { - // Match ES6 polyfill, which only works for "number" types - return isFinite(args[0]); - } - return Boolean.FALSE; - - case ConstructorId_isNaN: - if ((args.length == 0) || Undefined.isUndefined(args[0])) { - return Boolean.FALSE; - } - if (args[0] instanceof Number) { - return isNaN((Number) args[0]); - } - return Boolean.FALSE; - - case ConstructorId_isInteger: - if ((args.length == 0) || Undefined.isUndefined(args[0])) { - return Boolean.FALSE; - } - if (args[0] instanceof Number) { - return Boolean.valueOf(isInteger((Number) args[0])); - } - return Boolean.FALSE; - - case ConstructorId_isSafeInteger: - if ((args.length == 0) || (Undefined.instance == args[0])) { - return Boolean.FALSE; - } - if (args[0] instanceof Number) { - return Boolean.valueOf(isSafeInteger((Number) args[0])); - } - return Boolean.FALSE; - - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - } - - @Override - public String toString() { - return ScriptRuntime.numberToString(doubleValue, 10); - } - - private static String num_to( - double val, - Object[] args, - int zeroArgMode, - int oneArgMode, - int precisionMin, - int precisionOffset) { - int precision; - if (args.length == 0) { - precision = 0; - oneArgMode = zeroArgMode; - } else { - /* We allow a larger range of precision than - ECMA requires; this is permitted by ECMA. */ - double p = ScriptRuntime.toInteger(args[0]); - if (p < precisionMin || p > MAX_PRECISION) { - String msg = - ScriptRuntime.getMessageById( - "msg.bad.precision", ScriptRuntime.toString(args[0])); - throw ScriptRuntime.rangeError(msg); - } - precision = ScriptRuntime.toInt32(p); - } - StringBuilder sb = new StringBuilder(); - DToA.JS_dtostr(sb, oneArgMode, precision + precisionOffset, val); - return sb.toString(); - } - - static Object isFinite(Object val) { - double d = ScriptRuntime.toNumber(val); - Double nd = Double.valueOf(d); - return ScriptRuntime.wrapBoolean(!nd.isInfinite() && !nd.isNaN()); - } - - private static Boolean isNaN(Number val) { - if (val instanceof Double) { - return Boolean.valueOf(((Double) val).isNaN()); - } - - double d = val.doubleValue(); - return Boolean.valueOf(Double.isNaN(d)); - } - - private static boolean isInteger(Number val) { - if (val instanceof Double) { - return isDoubleInteger((Double) val); - } - return isDoubleInteger(val.doubleValue()); - } - - private static boolean isDoubleInteger(Double d) { - return !d.isInfinite() && !d.isNaN() && (Math.floor(d.doubleValue()) == d.doubleValue()); - } - - private static boolean isDoubleInteger(double d) { - return !Double.isInfinite(d) && !Double.isNaN(d) && (Math.floor(d) == d); - } - - private static boolean isSafeInteger(Number val) { - if (val instanceof Double) { - return isDoubleSafeInteger((Double) val); - } - return isDoubleSafeInteger(val.doubleValue()); - } - - private static boolean isDoubleSafeInteger(Double d) { - return isDoubleInteger(d) - && (d.doubleValue() <= MAX_SAFE_INTEGER) - && (d.doubleValue() >= MIN_SAFE_INTEGER); - } - - private static boolean isDoubleSafeInteger(double d) { - return isDoubleInteger(d) && (d <= MAX_SAFE_INTEGER) && (d >= MIN_SAFE_INTEGER); - } - - @Override - protected int findPrototypeId(String s) { - int id; - switch (s) { - case "constructor": - id = Id_constructor; - break; - case "toString": - id = Id_toString; - break; - case "toLocaleString": - id = Id_toLocaleString; - break; - case "toSource": - id = Id_toSource; - break; - case "valueOf": - id = Id_valueOf; - break; - case "toFixed": - id = Id_toFixed; - break; - case "toExponential": - id = Id_toExponential; - break; - case "toPrecision": - id = Id_toPrecision; - break; - default: - id = 0; - break; - } - return id; - } - - private static final int ConstructorId_isFinite = -1, - ConstructorId_isNaN = -2, - ConstructorId_isInteger = -3, - ConstructorId_isSafeInteger = -4, - Id_constructor = 1, - Id_toString = 2, - Id_toLocaleString = 3, - Id_toSource = 4, - Id_valueOf = 5, - Id_toFixed = 6, - Id_toExponential = 7, - Id_toPrecision = 8, - MAX_PROTOTYPE_ID = 8; - - private double doubleValue; -} diff --git a/src/main/java/org/mozilla/javascript/NativeObject.java b/src/main/java/org/mozilla/javascript/NativeObject.java deleted file mode 100644 index bc85d578..00000000 --- a/src/main/java/org/mozilla/javascript/NativeObject.java +++ /dev/null @@ -1,1014 +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.AbstractCollection; -import java.util.AbstractSet; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; -import org.mozilla.javascript.ScriptRuntime.StringIdOrIndex; - -/** - * This class implements the Object native object. See ECMA 15.2. - * - * @author Norris Boyd - */ -public class NativeObject extends IdScriptableObject implements Map { - private static final long serialVersionUID = -6345305608474346996L; - - private static final Object OBJECT_TAG = "Object"; - - static void init(Scriptable scope, boolean sealed) { - NativeObject obj = new NativeObject(); - obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); - } - - @Override - public String getClassName() { - return "Object"; - } - - @Override - public String toString() { - return ScriptRuntime.defaultObjectToString(this); - } - - @Override - protected void fillConstructorProperties(IdFunctionObject ctor) { - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_getPrototypeOf, "getPrototypeOf", 1); - if (Context.getCurrentContext().version >= Context.VERSION_ES6) { - addIdFunctionProperty( - ctor, OBJECT_TAG, ConstructorId_setPrototypeOf, "setPrototypeOf", 2); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_entries, "entries", 1); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_fromEntries, "fromEntries", 1); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_values, "values", 1); - } - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_keys, "keys", 1); - addIdFunctionProperty( - ctor, OBJECT_TAG, ConstructorId_getOwnPropertyNames, "getOwnPropertyNames", 1); - addIdFunctionProperty( - ctor, OBJECT_TAG, ConstructorId_getOwnPropertySymbols, "getOwnPropertySymbols", 1); - addIdFunctionProperty( - ctor, - OBJECT_TAG, - ConstructorId_getOwnPropertyDescriptor, - "getOwnPropertyDescriptor", - 2); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_defineProperty, "defineProperty", 3); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_isExtensible, "isExtensible", 1); - addIdFunctionProperty( - ctor, OBJECT_TAG, ConstructorId_preventExtensions, "preventExtensions", 1); - addIdFunctionProperty( - ctor, OBJECT_TAG, ConstructorId_defineProperties, "defineProperties", 2); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_create, "create", 2); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_isSealed, "isSealed", 1); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_isFrozen, "isFrozen", 1); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_seal, "seal", 1); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_freeze, "freeze", 1); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_assign, "assign", 2); - addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_is, "is", 2); - 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_toLocaleString: - arity = 0; - s = "toLocaleString"; - break; - case Id_valueOf: - arity = 0; - s = "valueOf"; - break; - case Id_hasOwnProperty: - arity = 1; - s = "hasOwnProperty"; - break; - case Id_propertyIsEnumerable: - arity = 1; - s = "propertyIsEnumerable"; - break; - case Id_isPrototypeOf: - arity = 1; - s = "isPrototypeOf"; - break; - case Id_toSource: - arity = 0; - s = "toSource"; - break; - case Id___defineGetter__: - arity = 2; - s = "__defineGetter__"; - break; - case Id___defineSetter__: - arity = 2; - s = "__defineSetter__"; - break; - case Id___lookupGetter__: - arity = 1; - s = "__lookupGetter__"; - break; - case Id___lookupSetter__: - arity = 1; - s = "__lookupSetter__"; - break; - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - initPrototypeMethod(OBJECT_TAG, id, s, arity); - } - - @Override - public Object execIdCall( - IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!f.hasTag(OBJECT_TAG)) { - return super.execIdCall(f, cx, scope, thisObj, args); - } - int id = f.methodId(); - switch (id) { - case Id_constructor: - { - if (thisObj != null) { - // BaseFunction.construct will set up parent, proto - return f.construct(cx, scope, args); - } - if (args.length == 0 || args[0] == null || Undefined.isUndefined(args[0])) { - return new NativeObject(); - } - return ScriptRuntime.toObject(cx, scope, args[0]); - } - - case Id_toLocaleString: - { - if (thisObj == null) { - throw ScriptRuntime.notFunctionError(null); - } - Object toString = ScriptableObject.getProperty(thisObj, "toString"); - if (!(toString instanceof Callable)) { - throw ScriptRuntime.notFunctionError(toString); - } - Callable fun = (Callable) toString; - return fun.call(cx, scope, thisObj, ScriptRuntime.emptyArgs); - } - - case Id_toString: - { - if (cx.hasFeature(Context.FEATURE_TO_STRING_AS_SOURCE)) { - String s = - ScriptRuntime.defaultObjectToSource( - cx, scope, - thisObj, args); - int L = s.length(); - if (L != 0 && s.charAt(0) == '(' && s.charAt(L - 1) == ')') { - // Strip () that surrounds toSource - s = s.substring(1, L - 1); - } - return s; - } - return ScriptRuntime.defaultObjectToString(thisObj); - } - - case Id_valueOf: - if (cx.getLanguageVersion() >= Context.VERSION_1_8 - && (thisObj == null || Undefined.isUndefined(thisObj))) { - throw ScriptRuntime.typeErrorById( - "msg." + (thisObj == null ? "null" : "undef") + ".to.object"); - } - return thisObj; - - case Id_hasOwnProperty: - { - if (cx.getLanguageVersion() >= Context.VERSION_1_8 - && (thisObj == null || Undefined.isUndefined(thisObj))) { - throw ScriptRuntime.typeErrorById( - "msg." + (thisObj == null ? "null" : "undef") + ".to.object"); - } - boolean result; - Object arg = args.length < 1 ? Undefined.instance : args[0]; - if (arg instanceof Symbol) { - result = ensureSymbolScriptable(thisObj).has((Symbol) arg, thisObj); - } else { - StringIdOrIndex s = ScriptRuntime.toStringIdOrIndex(cx, arg); - if (s.stringId == null) { - result = thisObj.has(s.index, thisObj); - } else { - result = thisObj.has(s.stringId, thisObj); - } - } - return ScriptRuntime.wrapBoolean(result); - } - - case Id_propertyIsEnumerable: - { - if (cx.getLanguageVersion() >= Context.VERSION_1_8 - && (thisObj == null || Undefined.isUndefined(thisObj))) { - throw ScriptRuntime.typeErrorById( - "msg." + (thisObj == null ? "null" : "undef") + ".to.object"); - } - - boolean result; - Object arg = args.length < 1 ? Undefined.instance : args[0]; - - if (arg instanceof Symbol) { - result = ((SymbolScriptable) thisObj).has((Symbol) arg, thisObj); - result = result && isEnumerable((Symbol) arg, thisObj); - } else { - StringIdOrIndex s = ScriptRuntime.toStringIdOrIndex(cx, arg); - // When checking if a property is enumerable, a missing property should - // return "false" instead of - // throwing an exception. See: https://github.com/mozilla/rhino/issues/415 - try { - if (s.stringId == null) { - result = thisObj.has(s.index, thisObj); - result = result && isEnumerable(s.index, thisObj); - } else { - result = thisObj.has(s.stringId, thisObj); - result = result && isEnumerable(s.stringId, thisObj); - } - } catch (EvaluatorException ee) { - if (ee.getMessage() - .startsWith( - ScriptRuntime.getMessageById( - "msg.prop.not.found", - s.stringId == null - ? Integer.toString(s.index) - : s.stringId))) { - result = false; - } else { - throw ee; - } - } - } - return ScriptRuntime.wrapBoolean(result); - } - - case Id_isPrototypeOf: - { - if (cx.getLanguageVersion() >= Context.VERSION_1_8 - && (thisObj == null || Undefined.isUndefined(thisObj))) { - throw ScriptRuntime.typeErrorById( - "msg." + (thisObj == null ? "null" : "undef") + ".to.object"); - } - - boolean result = false; - if (args.length != 0 && args[0] instanceof Scriptable) { - Scriptable v = (Scriptable) args[0]; - do { - v = v.getPrototype(); - if (v == thisObj) { - result = true; - break; - } - } while (v != null); - } - return ScriptRuntime.wrapBoolean(result); - } - - case Id_toSource: - return ScriptRuntime.defaultObjectToSource(cx, scope, thisObj, args); - case Id___defineGetter__: - case Id___defineSetter__: - { - if (args.length < 2 || !(args[1] instanceof Callable)) { - Object badArg = (args.length >= 2 ? args[1] : Undefined.instance); - throw ScriptRuntime.notFunctionError(badArg); - } - if (!(thisObj instanceof ScriptableObject)) { - throw Context.reportRuntimeErrorById( - "msg.extend.scriptable", - thisObj == null ? "null" : thisObj.getClass().getName(), - String.valueOf(args[0])); - } - ScriptableObject so = (ScriptableObject) thisObj; - StringIdOrIndex s = ScriptRuntime.toStringIdOrIndex(cx, args[0]); - int index = s.stringId != null ? 0 : s.index; - Callable getterOrSetter = (Callable) args[1]; - boolean isSetter = (id == Id___defineSetter__); - so.setGetterOrSetter(s.stringId, index, getterOrSetter, isSetter); - if (so instanceof NativeArray) ((NativeArray) so).setDenseOnly(false); - } - return Undefined.instance; - - case Id___lookupGetter__: - case Id___lookupSetter__: - { - if (args.length < 1 || !(thisObj instanceof ScriptableObject)) - return Undefined.instance; - - ScriptableObject so = (ScriptableObject) thisObj; - StringIdOrIndex s = ScriptRuntime.toStringIdOrIndex(cx, args[0]); - int index = s.stringId != null ? 0 : s.index; - boolean isSetter = (id == Id___lookupSetter__); - Object gs; - for (; ; ) { - gs = so.getGetterOrSetter(s.stringId, index, this, isSetter); - if (gs != null) { - break; - } - // If there is no getter or setter for the object itself, - // how about the prototype? - Scriptable v = so.getPrototype(); - if (v == null) { - break; - } - if (v instanceof ScriptableObject) { - so = (ScriptableObject) v; - } else { - break; - } - } - if (gs != null) { - return gs; - } - } - return Undefined.instance; - - case ConstructorId_getPrototypeOf: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - Scriptable obj = getCompatibleObject(cx, scope, arg); - return obj.getPrototype(); - } - case ConstructorId_setPrototypeOf: - { - if (args.length < 2) { - throw ScriptRuntime.typeErrorById( - "msg.method.missing.parameter", - "Object.setPrototypeOf", - "2", - Integer.toString(args.length)); - } - Scriptable proto = (args[1] == null) ? null : ensureScriptable(args[1]); - if (proto instanceof Symbol) { - throw ScriptRuntime.typeErrorById( - "msg.arg.not.object", ScriptRuntime.typeof(proto)); - } - - final Object arg0 = args[0]; - if (cx.getLanguageVersion() >= Context.VERSION_ES6) { - ScriptRuntimeES6.requireObjectCoercible(cx, arg0, f); - } - if (!(arg0 instanceof ScriptableObject)) { - return arg0; - } - ScriptableObject obj = (ScriptableObject) arg0; - if (!obj.isExtensible()) { - throw ScriptRuntime.typeErrorById("msg.not.extensible"); - } - - // cycle detection - Scriptable prototypeProto = proto; - while (prototypeProto != null) { - if (prototypeProto == obj) { - throw ScriptRuntime.typeErrorById( - "msg.object.cyclic.prototype", obj.getClass().getSimpleName()); - } - prototypeProto = prototypeProto.getPrototype(); - } - obj.setPrototype(proto); - return obj; - } - case ConstructorId_keys: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - Scriptable obj = getCompatibleObject(cx, scope, arg); - Object[] ids = obj.getIds(); - for (int i = 0; i < ids.length; i++) { - ids[i] = ScriptRuntime.toString(ids[i]); - } - return cx.newArray(scope, ids); - } - - case ConstructorId_entries: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - Scriptable obj = getCompatibleObject(cx, scope, arg); - Object[] ids = obj.getIds(); - int j = 0; - for (int i = 0; i < ids.length; i++) { - if (ids[i] instanceof Integer) { - int intId = (Integer) ids[i]; - if (obj.has(intId, obj) && isEnumerable(intId, obj)) { - String stringId = ScriptRuntime.toString(ids[i]); - Object[] entry = new Object[] {stringId, obj.get(intId, obj)}; - ids[j++] = cx.newArray(scope, entry); - } - } else { - String stringId = ScriptRuntime.toString(ids[i]); - if (obj.has(stringId, obj) && isEnumerable(stringId, obj)) { - Object[] entry = new Object[] {stringId, obj.get(stringId, obj)}; - ids[j++] = cx.newArray(scope, entry); - } - } - } - if (j != ids.length) { - ids = Arrays.copyOf(ids, j); - } - return cx.newArray(scope, ids); - } - case ConstructorId_fromEntries: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - arg = getCompatibleObject(cx, scope, arg); - Scriptable obj = cx.newObject(scope); - ScriptRuntime.loadFromIterable( - cx, - scope, - arg, - (key, value) -> { - if (key instanceof Integer) { - obj.put((Integer) key, obj, value); - } else if (key instanceof Symbol - && obj instanceof SymbolScriptable) { - ((SymbolScriptable) obj).put((Symbol) key, obj, value); - } else { - obj.put(ScriptRuntime.toString(key), obj, value); - } - }); - return obj; - } - case ConstructorId_values: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - Scriptable obj = getCompatibleObject(cx, scope, arg); - Object[] ids = obj.getIds(); - int j = 0; - for (int i = 0; i < ids.length; i++) { - if (ids[i] instanceof Integer) { - int intId = (Integer) ids[i]; - if (obj.has(intId, obj) && isEnumerable(intId, obj)) { - ids[j++] = obj.get(intId, obj); - } - } else { - String stringId = ScriptRuntime.toString(ids[i]); - // getter may remove keys - if (obj.has(stringId, obj) && isEnumerable(stringId, obj)) { - ids[j++] = obj.get(stringId, obj); - } - } - } - if (j != ids.length) { - ids = Arrays.copyOf(ids, j); - } - return cx.newArray(scope, ids); - } - case ConstructorId_getOwnPropertyNames: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - Scriptable s = getCompatibleObject(cx, scope, arg); - ScriptableObject obj = ensureScriptableObject(s); - Object[] ids = obj.getIds(true, false); - for (int i = 0; i < ids.length; i++) { - ids[i] = ScriptRuntime.toString(ids[i]); - } - return cx.newArray(scope, ids); - } - case ConstructorId_getOwnPropertySymbols: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - Scriptable s = getCompatibleObject(cx, scope, arg); - ScriptableObject obj = ensureScriptableObject(s); - Object[] ids = obj.getIds(true, true); - ArrayList syms = new ArrayList(); - for (int i = 0; i < ids.length; i++) { - if (ids[i] instanceof Symbol) { - syms.add(ids[i]); - } - } - return cx.newArray(scope, syms.toArray()); - } - case ConstructorId_getOwnPropertyDescriptor: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - // TODO(norris): There's a deeper issue here if - // arg instanceof Scriptable. Should we create a new - // interface to admit the new ECMAScript 5 operations? - Scriptable s = getCompatibleObject(cx, scope, arg); - ScriptableObject obj = ensureScriptableObject(s); - Object nameArg = args.length < 2 ? Undefined.instance : args[1]; - Scriptable desc = obj.getOwnPropertyDescriptor(cx, nameArg); - return desc == null ? Undefined.instance : desc; - } - case ConstructorId_defineProperty: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - ScriptableObject obj = ensureScriptableObject(arg); - Object name = args.length < 2 ? Undefined.instance : args[1]; - Object descArg = args.length < 3 ? Undefined.instance : args[2]; - ScriptableObject desc = ensureScriptableObject(descArg); - obj.defineOwnProperty(cx, name, desc); - return obj; - } - case ConstructorId_isExtensible: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - if (cx.getLanguageVersion() >= Context.VERSION_ES6 - && !(arg instanceof ScriptableObject)) { - return Boolean.FALSE; - } - - ScriptableObject obj = ensureScriptableObject(arg); - return Boolean.valueOf(obj.isExtensible()); - } - case ConstructorId_preventExtensions: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - if (cx.getLanguageVersion() >= Context.VERSION_ES6 - && !(arg instanceof ScriptableObject)) { - return arg; - } - - ScriptableObject obj = ensureScriptableObject(arg); - obj.preventExtensions(); - return obj; - } - case ConstructorId_defineProperties: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - ScriptableObject obj = ensureScriptableObject(arg); - Object propsObj = args.length < 2 ? Undefined.instance : args[1]; - Scriptable props = Context.toObject(propsObj, scope); - obj.defineOwnProperties(cx, ensureScriptableObject(props)); - return obj; - } - case ConstructorId_create: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - Scriptable obj = (arg == null) ? null : ensureScriptable(arg); - - ScriptableObject newObject = new NativeObject(); - newObject.setParentScope(scope); - newObject.setPrototype(obj); - - if (args.length > 1 && !Undefined.isUndefined(args[1])) { - Scriptable props = Context.toObject(args[1], scope); - newObject.defineOwnProperties(cx, ensureScriptableObject(props)); - } - - return newObject; - } - case ConstructorId_isSealed: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - if (cx.getLanguageVersion() >= Context.VERSION_ES6 - && !(arg instanceof ScriptableObject)) { - return Boolean.TRUE; - } - - return AbstractEcmaObjectOperations.testIntegrityLevel( - cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.SEALED); - } - case ConstructorId_isFrozen: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - if (cx.getLanguageVersion() >= Context.VERSION_ES6 - && !(arg instanceof ScriptableObject)) { - return Boolean.TRUE; - } - - return AbstractEcmaObjectOperations.testIntegrityLevel( - cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.FROZEN); - } - case ConstructorId_seal: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - if (cx.getLanguageVersion() >= Context.VERSION_ES6 - && !(arg instanceof ScriptableObject)) { - return arg; - } - - AbstractEcmaObjectOperations.setIntegrityLevel( - cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.SEALED); - - return arg; - } - case ConstructorId_freeze: - { - Object arg = args.length < 1 ? Undefined.instance : args[0]; - if (cx.getLanguageVersion() >= Context.VERSION_ES6 - && !(arg instanceof ScriptableObject)) { - return arg; - } - - AbstractEcmaObjectOperations.setIntegrityLevel( - cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.FROZEN); - - return arg; - } - - case ConstructorId_assign: - { - Scriptable targetObj; - if (args.length > 0) { - targetObj = ScriptRuntime.toObject(cx, scope, args[0]); - } else { - targetObj = ScriptRuntime.toObject(cx, scope, Undefined.instance); - } - for (int i = 1; i < args.length; i++) { - if ((args[i] == null) || Undefined.isUndefined(args[i])) { - continue; - } - Scriptable sourceObj = ScriptRuntime.toObject(cx, scope, args[i]); - Object[] ids = sourceObj.getIds(); - for (Object key : ids) { - if (targetObj instanceof ScriptableObject) { - ScriptableObject desc = - ((ScriptableObject) targetObj) - .getOwnPropertyDescriptor(cx, key); - if (desc != null - && isDataDescriptor(desc) - && isFalse(desc.get("writable"))) { - throw ScriptRuntime.typeErrorById( - "msg.change.value.with.writable.false", key); - } - } - if (key instanceof String) { - Object val = sourceObj.get((String) key, sourceObj); - if ((val != Scriptable.NOT_FOUND) && !Undefined.isUndefined(val)) { - targetObj.put((String) key, targetObj, val); - } - } else if (key instanceof Number) { - int ii = ScriptRuntime.toInt32(key); - Object val = sourceObj.get(ii, sourceObj); - if ((val != Scriptable.NOT_FOUND) && !Undefined.isUndefined(val)) { - targetObj.put(ii, targetObj, val); - } - } - } - } - return targetObj; - } - - case ConstructorId_is: - { - Object a1 = args.length < 1 ? Undefined.instance : args[0]; - Object a2 = args.length < 2 ? Undefined.instance : args[1]; - return ScriptRuntime.wrapBoolean(ScriptRuntime.same(a1, a2)); - } - - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - } - - private boolean isEnumerable(int index, Object obj) { - if (obj instanceof ScriptableObject) { - ScriptableObject so = (ScriptableObject) obj; - int attrs = so.getAttributes(index); - return (attrs & ScriptableObject.DONTENUM) == 0; - } else { - return true; - } - } - - private boolean isEnumerable(String key, Object obj) { - if (obj instanceof ScriptableObject) { - ScriptableObject so = (ScriptableObject) obj; - int attrs = so.getAttributes(key); - return (attrs & ScriptableObject.DONTENUM) == 0; - } else { - return true; - } - } - - private boolean isEnumerable(Symbol sym, Object obj) { - if (obj instanceof ScriptableObject) { - ScriptableObject so = (ScriptableObject) obj; - int attrs = so.getAttributes(sym); - return (attrs & ScriptableObject.DONTENUM) == 0; - } else { - return true; - } - } - - private static Scriptable getCompatibleObject(Context cx, Scriptable scope, Object arg) { - if (cx.getLanguageVersion() >= Context.VERSION_ES6) { - Scriptable s = ScriptRuntime.toObject(cx, scope, arg); - return ensureScriptable(s); - } - return ensureScriptable(arg); - } - - // methods implementing java.util.Map - - @Override - public boolean containsKey(Object key) { - if (key instanceof String) { - return has((String) key, this); - } else if (key instanceof Number) { - return has(((Number) key).intValue(), this); - } - return false; - } - - @Override - public boolean containsValue(Object value) { - for (Object obj : values()) { - if (value == obj || value != null && value.equals(obj)) { - return true; - } - } - return false; - } - - @Override - public Object remove(Object key) { - Object value = get(key); - if (key instanceof String) { - delete((String) key); - } else if (key instanceof Number) { - delete(((Number) key).intValue()); - } - return value; - } - - @Override - public Set keySet() { - return new KeySet(); - } - - @Override - public Collection values() { - return new ValueCollection(); - } - - @Override - public Set> entrySet() { - return new EntrySet(); - } - - @Override - public Object put(Object key, Object value) { - throw new UnsupportedOperationException(); - } - - @Override - public void putAll(Map m) { - throw new UnsupportedOperationException(); - } - - @Override - public void clear() { - throw new UnsupportedOperationException(); - } - - class EntrySet extends AbstractSet> { - @Override - public Iterator> iterator() { - return new Iterator>() { - Object[] ids = getIds(); - Object key = null; - int index = 0; - - @Override - public boolean hasNext() { - return index < ids.length; - } - - @Override - public Map.Entry next() { - final Object ekey = key = ids[index++]; - final Object value = get(key); - return new Map.Entry() { - @Override - public Object getKey() { - return ekey; - } - - @Override - public Object getValue() { - return value; - } - - @Override - public Object setValue(Object value) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof Map.Entry)) { - return false; - } - Map.Entry e = (Map.Entry) other; - return (ekey == null ? e.getKey() == null : ekey.equals(e.getKey())) - && (value == null - ? e.getValue() == null - : value.equals(e.getValue())); - } - - @Override - public int hashCode() { - return (ekey == null ? 0 : ekey.hashCode()) - ^ (value == null ? 0 : value.hashCode()); - } - - @Override - public String toString() { - return ekey + "=" + value; - } - }; - } - - @Override - public void remove() { - if (key == null) { - throw new IllegalStateException(); - } - NativeObject.this.remove(key); - key = null; - } - }; - } - - @Override - public int size() { - return NativeObject.this.size(); - } - } - - class KeySet extends AbstractSet { - - @Override - public boolean contains(Object key) { - return containsKey(key); - } - - @Override - public Iterator iterator() { - return new Iterator() { - Object[] ids = getIds(); - Object key; - int index = 0; - - @Override - public boolean hasNext() { - return index < ids.length; - } - - @Override - public Object next() { - try { - return (key = ids[index++]); - } catch (ArrayIndexOutOfBoundsException e) { - key = null; - throw new NoSuchElementException(); - } - } - - @Override - public void remove() { - if (key == null) { - throw new IllegalStateException(); - } - NativeObject.this.remove(key); - key = null; - } - }; - } - - @Override - public int size() { - return NativeObject.this.size(); - } - } - - class ValueCollection extends AbstractCollection { - - @Override - public Iterator iterator() { - return new Iterator() { - Object[] ids = getIds(); - Object key; - int index = 0; - - @Override - public boolean hasNext() { - return index < ids.length; - } - - @Override - public Object next() { - return get((key = ids[index++])); - } - - @Override - public void remove() { - if (key == null) { - throw new IllegalStateException(); - } - NativeObject.this.remove(key); - key = null; - } - }; - } - - @Override - public int size() { - return NativeObject.this.size(); - } - } - - @Override - protected int findPrototypeId(String s) { - int id; - switch (s) { - case "constructor": - id = Id_constructor; - break; - case "toString": - id = Id_toString; - break; - case "toLocaleString": - id = Id_toLocaleString; - break; - case "valueOf": - id = Id_valueOf; - break; - case "hasOwnProperty": - id = Id_hasOwnProperty; - break; - case "propertyIsEnumerable": - id = Id_propertyIsEnumerable; - break; - case "isPrototypeOf": - id = Id_isPrototypeOf; - break; - case "toSource": - id = Id_toSource; - break; - case "__defineGetter__": - id = Id___defineGetter__; - break; - case "__defineSetter__": - id = Id___defineSetter__; - break; - case "__lookupGetter__": - id = Id___lookupGetter__; - break; - case "__lookupSetter__": - id = Id___lookupSetter__; - break; - default: - id = 0; - break; - } - return id; - } - - private static final int ConstructorId_getPrototypeOf = -1, - ConstructorId_keys = -2, - ConstructorId_getOwnPropertyNames = -3, - ConstructorId_getOwnPropertyDescriptor = -4, - ConstructorId_defineProperty = -5, - ConstructorId_isExtensible = -6, - ConstructorId_preventExtensions = -7, - ConstructorId_defineProperties = -8, - ConstructorId_create = -9, - ConstructorId_isSealed = -10, - ConstructorId_isFrozen = -11, - ConstructorId_seal = -12, - ConstructorId_freeze = -13, - ConstructorId_getOwnPropertySymbols = -14, - ConstructorId_assign = -15, - ConstructorId_is = -16, - - // ES6 - ConstructorId_setPrototypeOf = -17, - ConstructorId_entries = -18, - ConstructorId_fromEntries = -19, - ConstructorId_values = -20, - Id_constructor = 1, - Id_toString = 2, - Id_toLocaleString = 3, - Id_valueOf = 4, - Id_hasOwnProperty = 5, - Id_propertyIsEnumerable = 6, - Id_isPrototypeOf = 7, - Id_toSource = 8, - Id___defineGetter__ = 9, - Id___defineSetter__ = 10, - Id___lookupGetter__ = 11, - Id___lookupSetter__ = 12, - MAX_PROTOTYPE_ID = 12; -} diff --git a/src/main/java/org/mozilla/javascript/NativePromise.java b/src/main/java/org/mozilla/javascript/NativePromise.java deleted file mode 100644 index 05d79efe..00000000 --- a/src/main/java/org/mozilla/javascript/NativePromise.java +++ /dev/null @@ -1,798 +0,0 @@ -/* 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.ArrayList; -import org.mozilla.javascript.TopLevel.NativeErrors; - -public class NativePromise extends ScriptableObject { - - enum State { - PENDING, - FULFILLED, - REJECTED - } - - enum ReactionType { - FULFILL, - REJECT - } - - private State state = State.PENDING; - private Object result = null; - private boolean handled = false; - - private ArrayList fulfillReactions = new ArrayList<>(); - private ArrayList rejectReactions = new ArrayList<>(); - - public static void init(Context cx, Scriptable scope, boolean sealed) { - LambdaConstructor constructor = - new LambdaConstructor( - scope, - "Promise", - 1, - LambdaConstructor.CONSTRUCTOR_NEW, - NativePromise::constructor); - constructor.setStandardPropertyAttributes(DONTENUM | READONLY); - constructor.setPrototypePropertyAttributes(DONTENUM | READONLY | PERMANENT); - - constructor.defineConstructorMethod( - scope, "resolve", 1, NativePromise::resolve, DONTENUM, DONTENUM | READONLY); - constructor.defineConstructorMethod( - scope, "reject", 1, NativePromise::reject, DONTENUM, DONTENUM | READONLY); - constructor.defineConstructorMethod( - scope, "all", 1, NativePromise::all, DONTENUM, DONTENUM | READONLY); - constructor.defineConstructorMethod( - scope, "race", 1, NativePromise::race, DONTENUM, DONTENUM | READONLY); - - ScriptableObject speciesDescriptor = (ScriptableObject) cx.newObject(scope); - ScriptableObject.putProperty(speciesDescriptor, "enumerable", false); - ScriptableObject.putProperty(speciesDescriptor, "configurable", true); - ScriptableObject.putProperty( - speciesDescriptor, - "get", - new LambdaFunction( - scope, - "get [Symbol.species]", - 0, - (Context lcx, Scriptable lscope, Scriptable thisObj, Object[] args) -> - constructor)); - constructor.defineOwnProperty(cx, SymbolKey.SPECIES, speciesDescriptor, false); - - constructor.definePrototypeMethod( - scope, - "then", - 2, - (Context lcx, Scriptable lscope, Scriptable thisObj, Object[] args) -> { - NativePromise self = - LambdaConstructor.convertThisObject(thisObj, NativePromise.class); - return self.then(lcx, lscope, constructor, args); - }, - DONTENUM, - DONTENUM | READONLY); - constructor.definePrototypeMethod( - scope, "catch", 1, NativePromise::doCatch, DONTENUM, DONTENUM | READONLY); - constructor.definePrototypeMethod( - scope, - "finally", - 1, - (Context lcx, Scriptable lscope, Scriptable thisObj, Object[] args) -> - doFinally(lcx, lscope, thisObj, constructor, args), - DONTENUM, - DONTENUM | READONLY); - - constructor.definePrototypeProperty( - SymbolKey.TO_STRING_TAG, "Promise", DONTENUM | READONLY); - - ScriptableObject.defineProperty(scope, "Promise", constructor, DONTENUM); - if (sealed) { - constructor.sealObject(); - } - } - - private static Scriptable constructor(Context cx, Scriptable scope, Object[] args) { - if (args.length < 1 || !(args[0] instanceof Callable)) { - throw ScriptRuntime.typeErrorById("msg.function.expected"); - } - Callable executor = (Callable) args[0]; - NativePromise promise = new NativePromise(); - ResolvingFunctions resolving = new ResolvingFunctions(scope, promise); - - Scriptable thisObj = Undefined.SCRIPTABLE_UNDEFINED; - if (!cx.isStrictMode()) { - Scriptable tcs = cx.topCallScope; - if (tcs != null) { - thisObj = tcs; - } - } - - try { - executor.call(cx, scope, thisObj, new Object[] {resolving.resolve, resolving.reject}); - } catch (RhinoException re) { - resolving.reject.call(cx, scope, thisObj, new Object[] {getErrorObject(cx, scope, re)}); - } - - return promise; - } - - @Override - public String getClassName() { - return "Promise"; - } - - Object getResult() { - return result; - } - - // Promise.resolve - private static Object resolve(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!ScriptRuntime.isObject(thisObj)) { - throw ScriptRuntime.typeErrorById("msg.arg.not.object", ScriptRuntime.typeof(thisObj)); - } - Object arg = (args.length > 0 ? args[0] : Undefined.instance); - return resolveInternal(cx, scope, thisObj, arg); - } - - // PromiseResolve abstract operation - private static Object resolveInternal( - Context cx, Scriptable scope, Object constructor, Object arg) { - if (arg instanceof NativePromise) { - Object argConstructor = ScriptRuntime.getObjectProp(arg, "constructor", cx, scope); - if (argConstructor == constructor) { - return arg; - } - } - Capability cap = new Capability(cx, scope, constructor); - cap.resolve.call(cx, scope, Undefined.SCRIPTABLE_UNDEFINED, new Object[] {arg}); - return cap.promise; - } - - // Promise.reject - private static Object reject(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!ScriptRuntime.isObject(thisObj)) { - throw ScriptRuntime.typeErrorById("msg.arg.not.object", ScriptRuntime.typeof(thisObj)); - } - Object arg = (args.length > 0 ? args[0] : Undefined.instance); - Capability cap = new Capability(cx, scope, thisObj); - cap.reject.call(cx, scope, Undefined.SCRIPTABLE_UNDEFINED, new Object[] {arg}); - return cap.promise; - } - - // Promise.all - private static Object all(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - Capability cap = new Capability(cx, scope, thisObj); - Object arg = (args.length > 0 ? args[0] : Undefined.instance); - - IteratorLikeIterable iterable; - try { - Object maybeIterable = ScriptRuntime.callIterator(arg, cx, scope); - iterable = new IteratorLikeIterable(cx, scope, maybeIterable); - } catch (RhinoException re) { - cap.reject.call( - cx, - scope, - Undefined.SCRIPTABLE_UNDEFINED, - new Object[] {getErrorObject(cx, scope, re)}); - return cap.promise; - } - - IteratorLikeIterable.Itr iterator = iterable.iterator(); - try { - PromiseAllResolver resolver = new PromiseAllResolver(iterator, thisObj, cap); - try { - return resolver.resolve(cx, scope); - } finally { - if (!iterator.isDone()) { - iterable.close(); - } - } - } catch (RhinoException re) { - cap.reject.call( - cx, - scope, - Undefined.SCRIPTABLE_UNDEFINED, - new Object[] {getErrorObject(cx, scope, re)}); - return cap.promise; - } - } - - // Promise.race - private static Object race(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - Capability cap = new Capability(cx, scope, thisObj); - Object arg = (args.length > 0 ? args[0] : Undefined.instance); - - IteratorLikeIterable iterable; - try { - Object maybeIterable = ScriptRuntime.callIterator(arg, cx, scope); - iterable = new IteratorLikeIterable(cx, scope, maybeIterable); - } catch (RhinoException re) { - cap.reject.call( - cx, - scope, - Undefined.SCRIPTABLE_UNDEFINED, - new Object[] {getErrorObject(cx, scope, re)}); - return cap.promise; - } - - IteratorLikeIterable.Itr iterator = iterable.iterator(); - try { - try { - return performRace(cx, scope, iterator, thisObj, cap); - } finally { - if (!iterator.isDone()) { - iterable.close(); - } - } - } catch (RhinoException re) { - cap.reject.call( - cx, - scope, - Undefined.SCRIPTABLE_UNDEFINED, - new Object[] {getErrorObject(cx, scope, re)}); - return cap.promise; - } - } - - private static Object performRace( - Context cx, - Scriptable scope, - IteratorLikeIterable.Itr iterator, - Scriptable thisObj, - Capability cap) { - Callable resolve = ScriptRuntime.getPropFunctionAndThis(thisObj, "resolve", cx, scope); - Scriptable localThis = ScriptRuntime.lastStoredScriptable(cx); - - // Manually iterate for exception handling purposes - while (true) { - boolean hasNext; - Object nextVal = Undefined.instance; - boolean nextOk = false; - try { - hasNext = iterator.hasNext(); - if (hasNext) { - nextVal = iterator.next(); - } - nextOk = true; - } finally { - if (!nextOk) { - iterator.setDone(true); - } - } - - if (!hasNext) { - return cap.promise; - } - - // Call "resolve" to get the next promise in the chain - Object nextPromise = resolve.call(cx, scope, localThis, new Object[] {nextVal}); - - // And then call "then" on it. - // Logic in the resolution function ensures we don't deliver duplicate results - Callable thenFunc = - ScriptRuntime.getPropFunctionAndThis(nextPromise, "then", cx, scope); - thenFunc.call( - cx, - scope, - ScriptRuntime.lastStoredScriptable(cx), - new Object[] {cap.resolve, cap.reject}); - } - } - - // Promise.prototype.then - private Object then( - Context cx, Scriptable scope, LambdaConstructor defaultConstructor, Object[] args) { - Constructable constructable = - AbstractEcmaObjectOperations.speciesConstructor(cx, this, defaultConstructor); - Capability capability = new Capability(cx, scope, constructable); - - Callable onFulfilled = null; - if (args.length >= 1 && args[0] instanceof Callable) { - onFulfilled = (Callable) args[0]; - } - Callable onRejected = null; - if (args.length >= 2 && args[1] instanceof Callable) { - onRejected = (Callable) args[1]; - } - - Reaction fulfillReaction = new Reaction(capability, ReactionType.FULFILL, onFulfilled); - Reaction rejectReaction = new Reaction(capability, ReactionType.REJECT, onRejected); - - if (state == State.PENDING) { - fulfillReactions.add(fulfillReaction); - rejectReactions.add(rejectReaction); - } else if (state == State.FULFILLED) { - cx.enqueueMicrotask(() -> fulfillReaction.invoke(cx, scope, result)); - } else { - assert (state == State.REJECTED); - if (!handled) { - cx.getUnhandledPromiseTracker().promiseHandled(this); - } - cx.enqueueMicrotask(() -> rejectReaction.invoke(cx, scope, result)); - } - handled = true; - return capability.promise; - } - - // Promise.prototype.catch - private static Object doCatch(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - Object arg = (args.length > 0 ? args[0] : Undefined.instance); - Scriptable coercedThis = ScriptRuntime.toObject(cx, scope, thisObj); - // No guarantee that the caller didn't change the prototype of "then"! - Callable thenFunc = ScriptRuntime.getPropFunctionAndThis(coercedThis, "then", cx, scope); - return thenFunc.call( - cx, - scope, - ScriptRuntime.lastStoredScriptable(cx), - new Object[] {Undefined.instance, arg}); - } - - // Promise.prototype.finally - private static Object doFinally( - Context cx, - Scriptable scope, - Scriptable thisObj, - LambdaConstructor defaultConstructor, - Object[] args) { - if (!ScriptRuntime.isObject(thisObj)) { - throw ScriptRuntime.typeErrorById("msg.arg.not.object", ScriptRuntime.typeof(thisObj)); - } - Object onFinally = args.length > 0 ? args[0] : Undefined.SCRIPTABLE_UNDEFINED; - Object thenFinally = onFinally; - Object catchFinally = onFinally; - Constructable constructor = - AbstractEcmaObjectOperations.speciesConstructor(cx, thisObj, defaultConstructor); - if (onFinally instanceof Callable) { - Callable callableOnFinally = (Callable) thenFinally; - thenFinally = makeThenFinally(scope, constructor, callableOnFinally); - catchFinally = makeCatchFinally(scope, constructor, callableOnFinally); - } - Callable thenFunc = ScriptRuntime.getPropFunctionAndThis(thisObj, "then", cx, scope); - Scriptable to = ScriptRuntime.lastStoredScriptable(cx); - return thenFunc.call(cx, scope, to, new Object[] {thenFinally, catchFinally}); - } - - // Abstract "Then Finally Function" - private static Callable makeThenFinally( - Scriptable scope, Object constructor, Callable onFinally) { - return new LambdaFunction( - scope, - 1, - (Context cx, Scriptable ls, Scriptable thisObj, Object[] args) -> { - Object value = args.length > 0 ? args[0] : Undefined.instance; - LambdaFunction valueThunk = - new LambdaFunction( - scope, - 0, - (Context vc, Scriptable vs, Scriptable vt, Object[] va) -> - value); - Object result = - onFinally.call( - cx, - ls, - Undefined.SCRIPTABLE_UNDEFINED, - ScriptRuntime.emptyArgs); - Object promise = resolveInternal(cx, scope, constructor, result); - Callable thenFunc = - ScriptRuntime.getPropFunctionAndThis(promise, "then", cx, scope); - return thenFunc.call( - cx, - scope, - ScriptRuntime.lastStoredScriptable(cx), - new Object[] {valueThunk}); - }); - } - - // Abstract "Catch Finally Thrower" - private static Callable makeCatchFinally( - Scriptable scope, Object constructor, Callable onFinally) { - return new LambdaFunction( - scope, - 1, - (Context cx, Scriptable ls, Scriptable thisObj, Object[] args) -> { - Object reason = args.length > 0 ? args[0] : Undefined.instance; - LambdaFunction reasonThrower = - new LambdaFunction( - scope, - 0, - (Context vc, Scriptable vs, Scriptable vt, Object[] va) -> { - throw new JavaScriptException(reason, null, 0); - }); - Object result = - onFinally.call( - cx, - ls, - Undefined.SCRIPTABLE_UNDEFINED, - ScriptRuntime.emptyArgs); - Object promise = resolveInternal(cx, scope, constructor, result); - Callable thenFunc = - ScriptRuntime.getPropFunctionAndThis(promise, "then", cx, scope); - return thenFunc.call( - cx, - scope, - ScriptRuntime.lastStoredScriptable(cx), - new Object[] {reasonThrower}); - }); - } - - // Abstract operation to fulfill a promise - private Object fulfillPromise(Context cx, Scriptable scope, Object value) { - assert (state == State.PENDING); - result = value; - ArrayList reactions = fulfillReactions; - fulfillReactions = new ArrayList<>(); - if (!rejectReactions.isEmpty()) { - rejectReactions = new ArrayList<>(); - } - state = State.FULFILLED; - for (Reaction r : reactions) { - cx.enqueueMicrotask(() -> r.invoke(cx, scope, value)); - } - return Undefined.instance; - } - - // Abstract operation to reject a promise. - private Object rejectPromise(Context cx, Scriptable scope, Object reason) { - assert (state == State.PENDING); - result = reason; - ArrayList reactions = rejectReactions; - rejectReactions = new ArrayList<>(); - if (!fulfillReactions.isEmpty()) { - fulfillReactions = new ArrayList<>(); - } - state = State.REJECTED; - cx.getUnhandledPromiseTracker().promiseRejected(this); - for (Reaction r : reactions) { - cx.enqueueMicrotask(() -> r.invoke(cx, scope, reason)); - } - return Undefined.instance; - } - - // Promise Resolve Thenable Job. - // This gets called by the "resolving func" as a microtask. - private void callThenable(Context cx, Scriptable scope, Object resolution, Callable thenFunc) { - ResolvingFunctions resolving = new ResolvingFunctions(scope, this); - Scriptable thisObj = - (resolution instanceof Scriptable - ? (Scriptable) resolution - : Undefined.SCRIPTABLE_UNDEFINED); - try { - thenFunc.call(cx, scope, thisObj, new Object[] {resolving.resolve, resolving.reject}); - } catch (RhinoException re) { - resolving.reject.call( - cx, - scope, - Undefined.SCRIPTABLE_UNDEFINED, - new Object[] {getErrorObject(cx, scope, re)}); - } - } - - private static Object getErrorObject(Context cx, Scriptable scope, RhinoException re) { - if (re instanceof JavaScriptException) { - return ((JavaScriptException) re).getValue(); - } - - TopLevel.NativeErrors constructor = NativeErrors.Error; - if (re instanceof EcmaError) { - EcmaError ee = (EcmaError) re; - switch (ee.getName()) { - case "EvalError": - constructor = NativeErrors.EvalError; - break; - case "RangeError": - constructor = NativeErrors.RangeError; - break; - case "ReferenceError": - constructor = NativeErrors.ReferenceError; - break; - case "SyntaxError": - constructor = NativeErrors.SyntaxError; - break; - case "TypeError": - constructor = NativeErrors.TypeError; - break; - case "URIError": - constructor = NativeErrors.URIError; - break; - case "InternalError": - constructor = NativeErrors.InternalError; - break; - case "JavaException": - constructor = NativeErrors.JavaException; - break; - default: - break; - } - } - return ScriptRuntime.newNativeError(cx, scope, constructor, new Object[] {re.getMessage()}); - } - - // Output of "CreateResolvingFunctions." Carries with it an "alreadyResolved" state, - // so we make it a separate object. This actually fires resolution functions on - // the passed callbacks. - private static class ResolvingFunctions { - - private boolean alreadyResolved = false; - LambdaFunction resolve; - LambdaFunction reject; - - ResolvingFunctions(Scriptable topScope, NativePromise promise) { - resolve = - new LambdaFunction( - topScope, - 1, - (Context cx, Scriptable scope, Scriptable thisObj, Object[] args) -> - resolve( - cx, - scope, - promise, - (args.length > 0 ? args[0] : Undefined.instance))); - resolve.setStandardPropertyAttributes(DONTENUM | READONLY); - reject = - new LambdaFunction( - topScope, - 1, - (Context cx, Scriptable scope, Scriptable thisObj, Object[] args) -> - reject( - cx, - scope, - promise, - (args.length > 0 ? args[0] : Undefined.instance))); - reject.setStandardPropertyAttributes(DONTENUM | READONLY); - } - - private Object reject(Context cx, Scriptable scope, NativePromise promise, Object reason) { - if (alreadyResolved) { - return Undefined.instance; - } - alreadyResolved = true; - return promise.rejectPromise(cx, scope, reason); - } - - private Object resolve( - Context cx, Scriptable scope, NativePromise promise, Object resolution) { - if (alreadyResolved) { - return Undefined.instance; - } - alreadyResolved = true; - - if (resolution == promise) { - Object err = - ScriptRuntime.newNativeError( - cx, - scope, - NativeErrors.TypeError, - new Object[] {"No promise self-resolution"}); - return promise.rejectPromise(cx, scope, err); - } - - if (!ScriptRuntime.isObject(resolution)) { - return promise.fulfillPromise(cx, scope, resolution); - } - - Scriptable sresolution = ScriptableObject.ensureScriptable(resolution); - Object thenObj = ScriptableObject.getProperty(sresolution, "then"); - if (!(thenObj instanceof Callable)) { - return promise.fulfillPromise(cx, scope, resolution); - } - - cx.enqueueMicrotask( - () -> promise.callThenable(cx, scope, resolution, (Callable) thenObj)); - return Undefined.instance; - } - } - - // "Promise Reaction" record. This is an input to the microtask. - private static class Reaction { - Capability capability; - ReactionType reaction = ReactionType.REJECT; - Callable handler; - - Reaction(Capability cap, ReactionType type, Callable handler) { - this.capability = cap; - this.reaction = type; - this.handler = handler; - } - - // Implementation of NewPromiseReactionJob - void invoke(Context cx, Scriptable scope, Object arg) { - try { - Object result = null; - if (handler == null) { - switch (reaction) { - case FULFILL: - result = arg; - break; - case REJECT: - capability.reject.call( - cx, scope, Undefined.SCRIPTABLE_UNDEFINED, new Object[] {arg}); - return; - } - } else { - result = - handler.call( - cx, scope, Undefined.SCRIPTABLE_UNDEFINED, new Object[] {arg}); - } - capability.resolve.call( - cx, scope, Undefined.SCRIPTABLE_UNDEFINED, new Object[] {result}); - - } catch (RhinoException re) { - capability.reject.call( - cx, - scope, - Undefined.SCRIPTABLE_UNDEFINED, - new Object[] {getErrorObject(cx, scope, re)}); - } - } - } - - // "Promise Capability Record" - // This abstracts a promise from the specific native implementation by keeping track - // of the "resolve" and "reject" functions. - private static class Capability { - Object promise; - private Object rawResolve = Undefined.instance; - Callable resolve; - private Object rawReject = Undefined.instance; - Callable reject; - - // Given an object that represents a constructor function, execute it as if it - // meets the "Promise" constructor pattern, which takes a function that will - // be called with "resolve" and "reject" functions. - Capability(Context topCx, Scriptable topScope, Object pc) { - if (!(pc instanceof Constructable)) { - throw ScriptRuntime.typeErrorById("msg.constructor.expected"); - } - Constructable promiseConstructor = (Constructable) pc; - LambdaFunction executorFunc = - new LambdaFunction( - topScope, - 2, - (Context cx, Scriptable scope, Scriptable thisObj, Object[] args) -> - executor(args)); - executorFunc.setStandardPropertyAttributes(DONTENUM | READONLY); - - promise = promiseConstructor.construct(topCx, topScope, new Object[] {executorFunc}); - - if (!(rawResolve instanceof Callable)) { - throw ScriptRuntime.typeErrorById("msg.function.expected"); - } - resolve = (Callable) rawResolve; - - if (!(rawReject instanceof Callable)) { - throw ScriptRuntime.typeErrorById("msg.function.expected"); - } - reject = (Callable) rawReject; - } - - private Object executor(Object[] args) { - if (!Undefined.isUndefined(rawResolve) || !Undefined.isUndefined(rawReject)) { - throw ScriptRuntime.typeErrorById("msg.promise.capability.state"); - } - if (args.length > 0) { - rawResolve = args[0]; - } - if (args.length > 1) { - rawReject = args[1]; - } - return Undefined.instance; - } - } - - // This object keeps track of the state necessary to execute Promise.all - private static class PromiseAllResolver { - // Limit the number of promises in Promise.all the same as it is in V8. - private static final int MAX_PROMISES = 1 << 21; - - final ArrayList values = new ArrayList<>(); - int remainingElements = 1; - - IteratorLikeIterable.Itr iterator; - Scriptable thisObj; - Capability capability; - - PromiseAllResolver(IteratorLikeIterable.Itr iter, Scriptable thisObj, Capability cap) { - this.iterator = iter; - this.thisObj = thisObj; - this.capability = cap; - } - - Object resolve(Context topCx, Scriptable topScope) { - int index = 0; - // Do this first because we should catch any exception before - // invoking the iterator. - Callable resolve = - ScriptRuntime.getPropFunctionAndThis(thisObj, "resolve", topCx, topScope); - Scriptable storedThis = ScriptRuntime.lastStoredScriptable(topCx); - - // Iterate manually because we need to catch exceptions in a special way. - while (true) { - if (index == MAX_PROMISES) { - throw ScriptRuntime.rangeErrorById("msg.promise.all.toobig"); - } - boolean hasNext; - Object nextVal = Undefined.instance; - boolean nextOk = false; - try { - hasNext = iterator.hasNext(); - if (hasNext) { - nextVal = iterator.next(); - } - nextOk = true; - } finally { - if (!nextOk) { - iterator.setDone(true); - } - } - - if (!hasNext) { - if (--remainingElements == 0) { - finalResolution(topCx, topScope); - } - return capability.promise; - } - - values.add(Undefined.instance); - - // Call "resolve" to get the next promise in the chain - Object nextPromise = - resolve.call(topCx, topScope, storedThis, new Object[] {nextVal}); - - // Create a resolution func that will stash its result in the right place - PromiseElementResolver eltResolver = new PromiseElementResolver(index); - LambdaFunction resolveFunc = - new LambdaFunction( - topScope, - 1, - (Context cx, Scriptable scope, Scriptable thisObj, Object[] args) -> - eltResolver.resolve( - cx, - scope, - (args.length > 0 ? args[0] : Undefined.instance), - this)); - resolveFunc.setStandardPropertyAttributes(DONTENUM | READONLY); - remainingElements++; - - // Call "then" on the promise with the resolution func - Callable thenFunc = - ScriptRuntime.getPropFunctionAndThis(nextPromise, "then", topCx, topScope); - thenFunc.call( - topCx, - topScope, - ScriptRuntime.lastStoredScriptable(topCx), - new Object[] {resolveFunc, capability.reject}); - index++; - } - } - - void finalResolution(Context cx, Scriptable scope) { - Scriptable newArray = cx.newArray(scope, values.toArray()); - capability.resolve.call( - cx, scope, Undefined.SCRIPTABLE_UNDEFINED, new Object[] {newArray}); - } - } - - // This object keeps track of the state necessary to resolve one element in Promise.all - private static class PromiseElementResolver { - - private boolean alreadyCalled = false; - private final int index; - - PromiseElementResolver(int ix) { - this.index = ix; - } - - Object resolve(Context cx, Scriptable scope, Object result, PromiseAllResolver resolver) { - if (alreadyCalled) { - return Undefined.instance; - } - alreadyCalled = true; - resolver.values.set(index, result); - if (--resolver.remainingElements == 0) { - resolver.finalResolution(cx, scope); - } - return Undefined.instance; - } - } -} diff --git a/src/main/java/org/mozilla/javascript/NativeScript.java b/src/main/java/org/mozilla/javascript/NativeScript.java deleted file mode 100644 index 15821349..00000000 --- a/src/main/java/org/mozilla/javascript/NativeScript.java +++ /dev/null @@ -1,188 +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 JavaScript Script object. - * - *

Note that the C version of the engine uses XDR as the format used by freeze and thaw. Since - * this depends on the internal format of structures in the C runtime, we cannot duplicate it. - * - *

Since we cannot replace 'this' as a result of the compile method, will forward requests to - * execute to the nonnull 'script' field. - * - * @since 1.3 - * @author Norris Boyd - */ -class NativeScript extends BaseFunction { - private static final long serialVersionUID = -6795101161980121700L; - - private static final Object SCRIPT_TAG = "Script"; - - static void init(Scriptable scope, boolean sealed) { - NativeScript obj = new NativeScript(null); - obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); - } - - private NativeScript(Script script) { - this.script = script; - } - - /** Returns the name of this JavaScript class, "Script". */ - @Override - public String getClassName() { - return "Script"; - } - - @Override - public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (script != null) { - return script.exec(cx, scope); - } - return Undefined.instance; - } - - @Override - public Scriptable construct(Context cx, Scriptable scope, Object[] args) { - throw Context.reportRuntimeErrorById("msg.script.is.not.constructor"); - } - - @Override - public int getLength() { - return 0; - } - - @Override - public int getArity() { - return 0; - } - - @Override - String decompile(int indent, int flags) { - if (script instanceof NativeFunction) { - return ((NativeFunction) script).decompile(indent, flags); - } - return super.decompile(indent, flags); - } - - @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_exec: - arity = 0; - s = "exec"; - break; - case Id_compile: - arity = 1; - s = "compile"; - break; - default: - throw new IllegalArgumentException(String.valueOf(id)); - } - initPrototypeMethod(SCRIPT_TAG, id, s, arity); - } - - @Override - public Object execIdCall( - IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - if (!f.hasTag(SCRIPT_TAG)) { - return super.execIdCall(f, cx, scope, thisObj, args); - } - int id = f.methodId(); - switch (id) { - case Id_constructor: - { - String source = (args.length == 0) ? "" : ScriptRuntime.toString(args[0]); - Script script = compile(cx, source); - NativeScript nscript = new NativeScript(script); - ScriptRuntime.setObjectProtoAndParent(nscript, scope); - return nscript; - } - - case Id_toString: - { - NativeScript real = realThis(thisObj, f); - Script realScript = real.script; - if (realScript == null) { - return ""; - } - return cx.decompileScript(realScript, 0); - } - - case Id_exec: - { - throw Context.reportRuntimeErrorById("msg.cant.call.indirect", "exec"); - } - - case Id_compile: - { - NativeScript real = realThis(thisObj, f); - String source = ScriptRuntime.toString(args, 0); - real.script = compile(cx, source); - return real; - } - } - throw new IllegalArgumentException(String.valueOf(id)); - } - - private static NativeScript realThis(Scriptable thisObj, IdFunctionObject f) { - return ensureType(thisObj, NativeScript.class, f); - } - - private static Script compile(Context cx, String source) { - int[] linep = {0}; - String filename = Context.getSourcePositionFromStack(linep); - if (filename == null) { - filename = "