aboutsummaryrefslogtreecommitdiffstats
path: root/trunk/infrastructure/rhino1_7R1/src
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/infrastructure/rhino1_7R1/src')
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/build.xml98
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/manifest3
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/classfile/ByteCode.java274
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/classfile/ClassFileWriter.java3038
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Arguments.java311
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/BaseFunction.java553
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Callable.java59
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ClassCache.java220
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ClassShutter.java89
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/CompilerEnvirons.java233
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ConstProperties.java109
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Context.java2526
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextAction.java59
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextFactory.java594
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextListener.java60
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DToA.java1271
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Decompiler.java918
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DefaultErrorReporter.java113
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DefiningClassLoader.java88
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Delegator.java266
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/EcmaError.java160
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ErrorReporter.java106
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Evaluator.java118
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/EvaluatorException.java123
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Function.java84
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/FunctionNode.java117
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/FunctionObject.java569
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/GeneratedClassLoader.java66
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IRFactory.java1607
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdFunctionCall.java55
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdFunctionObject.java189
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdScriptableObject.java734
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ImporterTopLevel.java318
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InformativeParser.java225
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterfaceAdapter.java156
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterpretedFunction.java221
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Interpreter.java4643
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterpreterData.java192
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaAdapter.java1129
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaMembers.java935
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaScriptException.java117
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Kit.java486
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/LazilyLoadedCtor.java136
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/MemberBox.java362
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeArray.java1727
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeBoolean.java170
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeCall.java154
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeDate.java1604
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeError.java227
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeFunction.java169
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGenerator.java281
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGlobal.java790
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeIterator.java260
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaArray.java168
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaClass.java320
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaConstructor.java85
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaMethod.java576
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaObject.java1002
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaPackage.java199
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaTopPackage.java187
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeMath.java399
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeNumber.java244
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeObject.java316
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeScript.java221
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeString.java983
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeWith.java207
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Node.java1394
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NodeTransformer.java565
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ObjArray.java388
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ObjToIntMap.java697
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Parser.java2554
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/PolicySecurityController.java223
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Ref.java64
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/RefCallable.java59
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/RegExpProxy.java71
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/RhinoException.java306
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Script.java73
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptOrFnNode.java241
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptRuntime.java3830
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Scriptable.java342
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptableObject.java2428
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/SecureCaller.java198
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/SecurityController.java211
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/SecurityUtilities.java80
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/SpecialRef.java151
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Synchronizer.java81
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Token.java436
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/TokenStream.java1500
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/UintMap.java659
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Undefined.java60
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/UniqueTag.java120
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/VMBridge.java183
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/WrapFactory.java183
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/WrappedException.java93
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Wrapper.java58
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/continuations/Continuation.java136
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/debug/DebugFrame.java91
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/debug/DebuggableObject.java61
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/debug/DebuggableScript.java119
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/debug/Debugger.java69
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/jdk11/VMBridge_jdk11.java84
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/jdk13/VMBridge_jdk13.java157
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/jdk15/VMBridge_jdk15.java87
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Block.java615
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/ClassCompiler.java214
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Codegen.java5031
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/DataFlowBitSet.java134
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/OptFunctionNode.java149
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/OptRuntime.java311
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/OptTransformer.java133
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Optimizer.java510
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/regexp/NativeRegExp.java2782
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java289
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/regexp/RegExpImpl.java541
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/regexp/SubString.java75
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/resources/Messages.properties778
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/resources/Messages_fr.properties329
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/serialize/ScriptableInputStream.java112
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/serialize/ScriptableOutputStream.java207
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/xml/XMLLib.java132
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/xml/XMLObject.java128
121 files changed, 64541 insertions, 0 deletions
diff --git a/trunk/infrastructure/rhino1_7R1/src/build.xml b/trunk/infrastructure/rhino1_7R1/src/build.xml
new file mode 100644
index 0000000..a0a1e13
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/build.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- ***** BEGIN LICENSE BLOCK *****
+ - Version: MPL 1.1/GPL 2.0
+ -
+ - The contents of this file are subject to the Mozilla Public License Version
+ - 1.1 (the "License"); you may not use this file except in compliance with
+ - the License. You may obtain a copy of the License at
+ - http://www.mozilla.org/MPL/
+ -
+ - Software distributed under the License is distributed on an "AS IS" basis,
+ - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ - for the specific language governing rights and limitations under the
+ - License.
+ -
+ - The Original Code is Rhino code, released May 6, 1999.
+ -
+ - The Initial Developer of the Original Code is
+ - Netscape Communications Corporation.
+ - Portions created by the Initial Developer are Copyright (C) 1997-1999
+ - the Initial Developer. All Rights Reserved.
+ -
+ - Contributor(s):
+ -
+ - Alternatively, the contents of this file may be used under the terms of
+ - the GNU General Public License Version 2 or later (the "GPL"), in which
+ - case the provisions of the GPL are applicable instead of those above. If
+ - you wish to allow use of your version of this file only under the terms of
+ - the GPL and not to allow others to use your version of this file under the
+ - MPL, indicate your decision by deleting the provisions above and replacing
+ - them with the notice and other provisions required by the GPL. If you do
+ - not delete the provisions above, a recipient may use your version of this
+ - file under either the MPL or the GPL.
+ -
+ - ***** END LICENSE BLOCK ***** -->
+
+
+<!--
+Build file for Rhino using Ant (see http://jakarta.apache.org/ant/index.html)
+Requires Ant version 1.2
+-->
+<project name="src" default="compile" basedir="..">
+
+ <property file="build.properties"/>
+
+ <available property="jdk15"
+ classname="java.lang.reflect.ParameterizedType" />
+
+ <target name="compile" depends="compile-most,compile-jdk15">
+ </target>
+
+ <target name="compile-most">
+ <javac srcdir="src"
+ destdir="${classes}"
+ includes="org/**/*.java"
+ excludes="org/**/jdk15/*.java"
+ deprecation="on"
+ debug="${debug}"
+ target="${target-jvm}"
+ source="${source-level}"
+ >
+ </javac>
+ <copy todir="${classes}">
+ <fileset dir="src" includes="org/**/*.properties" />
+ <filterset>
+ <filter token="IMPLEMENTATION.VERSION"
+ value="${implementation.version}"/>
+ </filterset>
+ </copy>
+ </target>
+
+ <target name="compile-jdk15" if="jdk15">
+ <javac srcdir="src"
+ destdir="${classes}"
+ includes="org/**/jdk15/*.java"
+ deprecation="on"
+ debug="${debug}"
+ target="${target-jvm}"
+ source="${source-level}"
+ >
+ </javac>
+ </target>
+
+ <target name="copy-source">
+ <mkdir dir="${dist.dir}/src"/>
+ <copy todir="${dist.dir}/src">
+ <fileset dir="src"
+ includes="**/*.java,**/*.properties,**/*.xml,manifest"/>
+ </copy>
+ </target>
+
+ <target name="clean">
+ <delete includeEmptyDirs="true">
+ <fileset dir="${classes}"
+ excludes="org/mozilla/javascript/tools/**"/>
+ </delete>
+ </target>
+
+</project>
diff --git a/trunk/infrastructure/rhino1_7R1/src/manifest b/trunk/infrastructure/rhino1_7R1/src/manifest
new file mode 100644
index 0000000..b7d0c06
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/manifest
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: org.mozilla.javascript.tools.shell.Main
+Class-Path: xbean.jar
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/classfile/ByteCode.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/classfile/ByteCode.java
new file mode 100644
index 0000000..fa4713e
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/classfile/ByteCode.java
@@ -0,0 +1,274 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Roger Lawrence
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+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,
+ 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;
+
+
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/classfile/ClassFileWriter.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/classfile/ClassFileWriter.java
new file mode 100644
index 0000000..b9c6c96
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/classfile/ClassFileWriter.java
@@ -0,0 +1,3038 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Roger Lawrence
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.classfile;
+
+import org.mozilla.javascript.ObjToIntMap;
+import org.mozilla.javascript.ObjArray;
+import org.mozilla.javascript.UintMap;
+
+import java.io.*;
+
+/**
+ * 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 {
+ 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);
+ itsFlags = ACC_PUBLIC;
+ }
+
+ 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(new Short(interfaceIndex));
+ }
+
+ public static final short
+ ACC_PUBLIC = 0x0001,
+ ACC_PRIVATE = 0x0002,
+ ACC_PROTECTED = 0x0004,
+ ACC_STATIC = 0x0008,
+ ACC_FINAL = 0x0010,
+ 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(methodNameIndex, typeIndex,
+ flags);
+ itsMethods.add(itsCurrentMethod);
+ }
+
+ /**
+ * Complete generation of the method.
+ *
+ * After this method is called, no more code can be added to the
+ * method begun with <code>startMethod</code>.
+ *
+ * @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;
+
+ 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 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;
+
+ if (attrLength > 65536) {
+ // See http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html,
+ // section 4.10, "The amount of code per non-native, non-abstract
+ // method is limited to 65536 bytes...
+ throw new ClassFileFormatException(
+ "generated bytecode for method exceeds 64K limit.");
+ }
+ byte[] codeAttribute = new byte[attrLength];
+ int index = 0;
+ int codeAttrIndex = itsConstantPool.addUtf8("Code");
+ index = putInt16(codeAttrIndex, codeAttribute, index);
+ attrLength -= 6; // discount the attribute header
+ index = putInt32(attrLength, codeAttribute, index);
+ index = putInt16(itsMaxStack, codeAttribute, index);
+ index = putInt16(itsMaxLocals, codeAttribute, index);
+ index = putInt32(itsCodeBufferTop, codeAttribute, index);
+ System.arraycopy(itsCodeBuffer, 0, codeAttribute, index,
+ itsCodeBufferTop);
+ index += itsCodeBufferTop;
+
+ if (itsExceptionTableTop > 0) {
+ index = putInt16(itsExceptionTableTop, codeAttribute, index);
+ for (int i = 0; i < itsExceptionTableTop; i++) {
+ ExceptionTableEntry ete = itsExceptionTable[i];
+ short startPC = (short)getLabelPC(ete.itsStartLabel);
+ short endPC = (short)getLabelPC(ete.itsEndLabel);
+ short handlerPC = (short)getLabelPC(ete.itsHandlerLabel);
+ short catchType = ete.itsCatchType;
+ if (startPC == -1)
+ throw new IllegalStateException("start label not defined");
+ if (endPC == -1)
+ throw new IllegalStateException("end label not defined");
+ if (handlerPC == -1)
+ throw new IllegalStateException(
+ "handler label not defined");
+
+ index = putInt16(startPC, codeAttribute, index);
+ index = putInt16(endPC, codeAttribute, index);
+ index = putInt16(handlerPC, codeAttribute, index);
+ index = putInt16(catchType, codeAttribute, index);
+ }
+ }
+ else {
+ // write 0 as exception table length
+ index = putInt16(0, codeAttribute, index);
+ }
+
+ int attributeCount = 0;
+ if (itsLineNumberTable != null)
+ attributeCount++;
+ if (itsVarDescriptors != null)
+ attributeCount++;
+ 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);
+ }
+ }
+
+ itsCurrentMethod.setCodeAttribute(codeAttribute);
+
+ itsExceptionTable = null;
+ itsExceptionTableTop = 0;
+ itsLineNumberTableTop = 0;
+ itsCodeBufferTop = 0;
+ itsCurrentMethod = null;
+ itsMaxStack = 0;
+ itsStackTop = 0;
+ itsLabelTableTop = 0;
+ itsFixupTableTop = 0;
+ itsVarDescriptors = 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);
+ }
+ }
+
+ /**
+ * 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 :
+ // fallthru...
+ case ByteCode.IFEQ :
+ case ByteCode.IFNE :
+ case ByteCode.IFLT :
+ case ByteCode.IFGE :
+ case ByteCode.IFGT :
+ case ByteCode.IFLE :
+ case ByteCode.IF_ICMPEQ :
+ case ByteCode.IF_ICMPNE :
+ case ByteCode.IF_ICMPLT :
+ case ByteCode.IF_ICMPGE :
+ case ByteCode.IF_ICMPGT :
+ case ByteCode.IF_ICMPLE :
+ case ByteCode.IF_ACMPEQ :
+ case ByteCode.IF_ACMPNE :
+ case ByteCode.JSR :
+ case ByteCode.IFNULL :
+ case ByteCode.IFNONNULL : {
+ if ((theOperand & 0x80000000) != 0x80000000) {
+ if ((theOperand < 0) || (theOperand > 65535))
+ throw new IllegalArgumentException(
+ "Bad label for branch");
+ }
+ int branchPC = itsCodeBufferTop;
+ addToCodeBuffer(theOpCode);
+ if ((theOperand & 0x80000000) != 0x80000000) {
+ // hard displacement
+ addToCodeInt16(theOperand);
+ }
+ 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);
+ }
+ else {
+ addLabelFixup(theOperand, branchPC + 1);
+ addToCodeInt16(0);
+ }
+ }
+ }
+ break;
+
+ case ByteCode.BIPUSH :
+ if ((byte)theOperand != theOperand)
+ throw new IllegalArgumentException("out of range byte");
+ addToCodeBuffer(theOpCode);
+ addToCodeBuffer((byte)theOperand);
+ break;
+
+ case ByteCode.SIPUSH :
+ if ((short)theOperand != theOperand)
+ throw new IllegalArgumentException("out of range short");
+ addToCodeBuffer(theOpCode);
+ addToCodeInt16(theOperand);
+ break;
+
+ case ByteCode.NEWARRAY :
+ if (!(0 <= theOperand && theOperand < 256))
+ throw new IllegalArgumentException("out of range index");
+ addToCodeBuffer(theOpCode);
+ addToCodeBuffer(theOperand);
+ break;
+
+ case ByteCode.GETFIELD :
+ case ByteCode.PUTFIELD :
+ if (!(0 <= theOperand && theOperand < 65536))
+ throw new IllegalArgumentException("out of range field");
+ addToCodeBuffer(theOpCode);
+ addToCodeInt16(theOperand);
+ break;
+
+ case ByteCode.LDC :
+ case ByteCode.LDC_W :
+ case ByteCode.LDC2_W :
+ if (!(0 <= theOperand && theOperand < 65536))
+ throw new IllegalArgumentException("out of range index");
+ if (theOperand >= 256
+ || theOpCode == ByteCode.LDC_W
+ || theOpCode == ByteCode.LDC2_W)
+ {
+ if (theOpCode == ByteCode.LDC) {
+ addToCodeBuffer(ByteCode.LDC_W);
+ } else {
+ addToCodeBuffer(theOpCode);
+ }
+ addToCodeInt16(theOperand);
+ } else {
+ addToCodeBuffer(theOpCode);
+ addToCodeBuffer(theOperand);
+ }
+ break;
+
+ case ByteCode.RET :
+ case ByteCode.ILOAD :
+ case ByteCode.LLOAD :
+ case ByteCode.FLOAD :
+ case ByteCode.DLOAD :
+ case ByteCode.ALOAD :
+ case ByteCode.ISTORE :
+ case ByteCode.LSTORE :
+ case ByteCode.FSTORE :
+ case ByteCode.DSTORE :
+ case ByteCode.ASTORE :
+ if (!(0 <= theOperand && theOperand < 65536))
+ throw new ClassFileFormatException("out of range variable");
+ if (theOperand >= 256) {
+ addToCodeBuffer(ByteCode.WIDE);
+ addToCodeBuffer(theOpCode);
+ addToCodeInt16(theOperand);
+ }
+ else {
+ addToCodeBuffer(theOpCode);
+ addToCodeBuffer(theOperand);
+ }
+ break;
+
+ default :
+ throw new IllegalArgumentException(
+ "Unexpected opcode for 1 operand");
+ }
+
+ itsStackTop = (short)newStack;
+ if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
+ if (DEBUGSTACK) {
+ System.out.println("After "+bytecodeStr(theOpCode)
+ +" stack = "+itsStackTop);
+ }
+ }
+
+ /**
+ * Generate the load constant bytecode for the given integer.
+ *
+ * @param k the constant
+ */
+ public void addLoadConstant(int k) {
+ switch (k) {
+ case 0: add(ByteCode.ICONST_0); break;
+ case 1: add(ByteCode.ICONST_1); break;
+ case 2: add(ByteCode.ICONST_2); break;
+ case 3: add(ByteCode.ICONST_3); break;
+ case 4: add(ByteCode.ICONST_4); break;
+ case 5: add(ByteCode.ICONST_5); break;
+ default:
+ add(ByteCode.LDC, itsConstantPool.addConstant(k));
+ break;
+ }
+ }
+
+ /**
+ * Generate the load constant bytecode for the given long.
+ *
+ * @param k the constant
+ */
+ public void addLoadConstant(long k) {
+ add(ByteCode.LDC2_W, itsConstantPool.addConstant(k));
+ }
+
+ /**
+ * Generate the load constant bytecode for the given float.
+ *
+ * @param k the constant
+ */
+ public void addLoadConstant(float k) {
+ add(ByteCode.LDC, itsConstantPool.addConstant(k));
+ }
+
+ /**
+ * Generate the load constant bytecode for the given double.
+ *
+ * @param k the constant
+ */
+ public void addLoadConstant(double k) {
+ add(ByteCode.LDC2_W, itsConstantPool.addConstant(k));
+ }
+
+ /**
+ * Generate the load constant bytecode for the given string.
+ *
+ * @param k the constant
+ */
+ public void addLoadConstant(String k) {
+ add(ByteCode.LDC, itsConstantPool.addConstant(k));
+ }
+
+ /**
+ * Add the given two-operand bytecode to the current method.
+ *
+ * @param theOpCode the opcode of the bytecode
+ * @param theOperand1 the first operand of the bytecode
+ * @param theOperand2 the second operand of the bytecode
+ */
+ public void add(int theOpCode, int theOperand1, int theOperand2) {
+ if (DEBUGCODE) {
+ System.out.println("Add "+bytecodeStr(theOpCode)
+ +", "+Integer.toHexString(theOperand1)
+ +", "+Integer.toHexString(theOperand2));
+ }
+ int newStack = itsStackTop + stackChange(theOpCode);
+ if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
+
+ if (theOpCode == ByteCode.IINC) {
+ if (!(0 <= theOperand1 && theOperand1 < 65536))
+ throw new ClassFileFormatException("out of range variable");
+ if (!(0 <= theOperand2 && theOperand2 < 65536))
+ throw new ClassFileFormatException("out of range increment");
+
+ if (theOperand1 > 255 || theOperand2 < -128 || theOperand2 > 127) {
+ addToCodeBuffer(ByteCode.WIDE);
+ addToCodeBuffer(ByteCode.IINC);
+ addToCodeInt16(theOperand1);
+ addToCodeInt16(theOperand2);
+ }
+ else {
+ addToCodeBuffer(ByteCode.WIDE);
+ addToCodeBuffer(ByteCode.IINC);
+ addToCodeBuffer(theOperand1);
+ addToCodeBuffer(theOperand2);
+ }
+ }
+ else if (theOpCode == ByteCode.MULTIANEWARRAY) {
+ if (!(0 <= theOperand1 && theOperand1 < 65536))
+ throw new IllegalArgumentException("out of range index");
+ if (!(0 <= theOperand2 && theOperand2 < 256))
+ throw new IllegalArgumentException("out of range dimensions");
+
+ addToCodeBuffer(ByteCode.MULTIANEWARRAY);
+ addToCodeInt16(theOperand1);
+ addToCodeBuffer(theOperand2);
+ }
+ else {
+ throw new IllegalArgumentException(
+ "Unexpected opcode for 2 operands");
+ }
+ itsStackTop = (short)newStack;
+ if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
+ if (DEBUGSTACK) {
+ System.out.println("After "+bytecodeStr(theOpCode)
+ +" stack = "+itsStackTop);
+ }
+
+ }
+
+ public void add(int theOpCode, String className) {
+ if (DEBUGCODE) {
+ System.out.println("Add "+bytecodeStr(theOpCode)
+ +", "+className);
+ }
+ int newStack = itsStackTop + stackChange(theOpCode);
+ if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
+ switch (theOpCode) {
+ case ByteCode.NEW :
+ case ByteCode.ANEWARRAY :
+ case ByteCode.CHECKCAST :
+ case ByteCode.INSTANCEOF : {
+ short classIndex = itsConstantPool.addClass(className);
+ addToCodeBuffer(theOpCode);
+ addToCodeInt16(classIndex);
+ }
+ break;
+
+ default :
+ throw new IllegalArgumentException(
+ "bad opcode for class reference");
+ }
+ itsStackTop = (short)newStack;
+ if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
+ if (DEBUGSTACK) {
+ System.out.println("After "+bytecodeStr(theOpCode)
+ +" stack = "+itsStackTop);
+ }
+ }
+
+
+ public void add(int theOpCode, String className, String fieldName,
+ String fieldType)
+ {
+ if (DEBUGCODE) {
+ System.out.println("Add "+bytecodeStr(theOpCode)
+ +", "+className+", "+fieldName+", "+fieldType);
+ }
+ int newStack = itsStackTop + stackChange(theOpCode);
+ char fieldTypeChar = fieldType.charAt(0);
+ int fieldSize = (fieldTypeChar == 'J' || fieldTypeChar == 'D')
+ ? 2 : 1;
+ switch (theOpCode) {
+ case ByteCode.GETFIELD :
+ case ByteCode.GETSTATIC :
+ newStack += fieldSize;
+ break;
+ case ByteCode.PUTSTATIC :
+ case ByteCode.PUTFIELD :
+ newStack -= fieldSize;
+ break;
+ default :
+ throw new IllegalArgumentException(
+ "bad opcode for field reference");
+ }
+ if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
+ short fieldRefIndex = itsConstantPool.addFieldRef(className,
+ fieldName, fieldType);
+ addToCodeBuffer(theOpCode);
+ addToCodeInt16(fieldRefIndex);
+
+ itsStackTop = (short)newStack;
+ if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
+ if (DEBUGSTACK) {
+ System.out.println("After "+bytecodeStr(theOpCode)
+ +" stack = "+itsStackTop);
+ }
+ }
+
+ public void addInvoke(int theOpCode, String className, String methodName,
+ String methodType)
+ {
+ if (DEBUGCODE) {
+ System.out.println("Add "+bytecodeStr(theOpCode)
+ +", "+className+", "+methodName+", "
+ +methodType);
+ }
+ int parameterInfo = sizeOfParameters(methodType);
+ int parameterCount = parameterInfo >>> 16;
+ int stackDiff = (short)parameterInfo;
+
+ int newStack = itsStackTop + stackDiff;
+ newStack += stackChange(theOpCode); // adjusts for 'this'
+ if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
+
+ switch (theOpCode) {
+ case ByteCode.INVOKEVIRTUAL :
+ case ByteCode.INVOKESPECIAL :
+ case ByteCode.INVOKESTATIC :
+ case ByteCode.INVOKEINTERFACE : {
+ addToCodeBuffer(theOpCode);
+ if (theOpCode == ByteCode.INVOKEINTERFACE) {
+ short ifMethodRefIndex
+ = itsConstantPool.addInterfaceMethodRef(
+ className, methodName,
+ methodType);
+ addToCodeInt16(ifMethodRefIndex);
+ addToCodeBuffer(parameterCount + 1);
+ addToCodeBuffer(0);
+ }
+ else {
+ short methodRefIndex = itsConstantPool.addMethodRef(
+ className, methodName,
+ methodType);
+ addToCodeInt16(methodRefIndex);
+ }
+ }
+ break;
+
+ default :
+ throw new IllegalArgumentException(
+ "bad opcode for method reference");
+ }
+ itsStackTop = (short)newStack;
+ if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
+ if (DEBUGSTACK) {
+ System.out.println("After "+bytecodeStr(theOpCode)
+ +" stack = "+itsStackTop);
+ }
+ }
+
+ /**
+ * Generate code to load the given integer on stack.
+ *
+ * @param k the constant
+ */
+ public void addPush(int k)
+ {
+ if ((byte)k == k) {
+ if (k == -1) {
+ add(ByteCode.ICONST_M1);
+ } else if (0 <= k && k <= 5) {
+ add((byte)(ByteCode.ICONST_0 + k));
+ } else {
+ add(ByteCode.BIPUSH, (byte)k);
+ }
+ } else if ((short)k == k) {
+ add(ByteCode.SIPUSH, (short)k);
+ } else {
+ addLoadConstant(k);
+ }
+ }
+
+ public void addPush(boolean k)
+ {
+ add(k ? ByteCode.ICONST_1 : ByteCode.ICONST_0);
+ }
+
+ /**
+ * Generate code to load the given long on stack.
+ *
+ * @param k the constant
+ */
+ public void addPush(long k)
+ {
+ int ik = (int)k;
+ if (ik == k) {
+ addPush(ik);
+ add(ByteCode.I2L);
+ } else {
+ addLoadConstant(k);
+ }
+ }
+
+ /**
+ * Generate code to load the given double on stack.
+ *
+ * @param k the constant
+ */
+ public void addPush(double k)
+ {
+ if (k == 0.0) {
+ // zero
+ add(ByteCode.DCONST_0);
+ if (1.0 / k < 0) {
+ // Negative zero
+ add(ByteCode.DNEG);
+ }
+ } else if (k == 1.0 || k == -1.0) {
+ add(ByteCode.DCONST_1);
+ if (k < 0) {
+ add(ByteCode.DNEG);
+ }
+ } else {
+ addLoadConstant(k);
+ }
+ }
+
+ /**
+ * Generate the code to leave on stack the given string even if the
+ * string encoding exeeds the class file limit for single string constant
+ *
+ * @param k the constant
+ */
+ public void addPush(String k) {
+ int length = k.length();
+ int limit = itsConstantPool.getUtfEncodingLimit(k, 0, length);
+ if (limit == length) {
+ addLoadConstant(k);
+ return;
+ }
+ // Split string into picies fitting the UTF limit and generate code for
+ // StringBuffer sb = new StringBuffer(length);
+ // sb.append(loadConstant(piece_1));
+ // ...
+ // sb.append(loadConstant(piece_N));
+ // sb.toString();
+ final String SB = "java/lang/StringBuffer";
+ add(ByteCode.NEW, SB);
+ add(ByteCode.DUP);
+ addPush(length);
+ addInvoke(ByteCode.INVOKESPECIAL, SB, "<init>", "(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/StringBuffer;");
+ 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)
+ {
+ setTableSwitchJump(switchStart, -1, itsCodeBufferTop);
+ }
+
+ public final void markTableSwitchCase(int switchStart, int caseIndex)
+ {
+ 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;
+ setTableSwitchJump(switchStart, caseIndex, itsCodeBufferTop);
+ }
+
+ public void setTableSwitchJump(int switchStart, int caseIndex,
+ int jumpTarget)
+ {
+ if (!(0 <= jumpTarget && jumpTarget <= itsCodeBufferTop))
+ throw new IllegalArgumentException("Bad jump target: "+jumpTarget);
+ if (!(caseIndex >= -1))
+ throw new IllegalArgumentException("Bad case index: "+caseIndex);
+
+ int padSize = 3 & ~switchStart; // == 3 - switchStart % 4
+ int caseOffset;
+ if (caseIndex < 0) {
+ // default label
+ caseOffset = switchStart + 1 + padSize;
+ } else {
+ caseOffset = switchStart + 1 + padSize + 4 * (3 + caseIndex);
+ }
+ if (!(0 <= switchStart
+ && switchStart <= itsCodeBufferTop - 4 * 4 - padSize - 1))
+ {
+ throw new IllegalArgumentException(
+ switchStart+" is outside a possible range of tableswitch"
+ +" in already generated code");
+ }
+ if ((0xFF & itsCodeBuffer[switchStart]) != ByteCode.TABLESWITCH) {
+ throw new IllegalArgumentException(
+ switchStart+" is not offset of tableswitch statement");
+ }
+ if (!(0 <= caseOffset && caseOffset + 4 <= itsCodeBufferTop)) {
+ // caseIndex >= -1 does not guarantee that caseOffset >= 0 due
+ // to a possible overflow.
+ throw new ClassFileFormatException(
+ "Too big case index: "+caseIndex);
+ }
+ // ALERT: perhaps check against case bounds?
+ putInt32(jumpTarget - switchStart, itsCodeBuffer, caseOffset);
+ }
+
+ public int acquireLabel()
+ {
+ int top = itsLabelTableTop;
+ if (itsLabelTable == null || top == itsLabelTable.length) {
+ if (itsLabelTable == null) {
+ itsLabelTable = new int[MIN_LABEL_TABLE_SIZE];
+ }else {
+ int[] tmp = new int[itsLabelTable.length * 2];
+ System.arraycopy(itsLabelTable, 0, tmp, 0, top);
+ itsLabelTable = tmp;
+ }
+ }
+ itsLabelTableTop = top + 1;
+ itsLabelTable[top] = -1;
+ return top | 0x80000000;
+ }
+
+ public void markLabel(int label)
+ {
+ if (!(label < 0))
+ throw new IllegalArgumentException("Bad label, no biscuit");
+
+ label &= 0x7FFFFFFF;
+ if (label > itsLabelTableTop)
+ throw new IllegalArgumentException("Bad label");
+
+ if (itsLabelTable[label] != -1) {
+ throw new IllegalStateException("Can only mark label once");
+ }
+
+ itsLabelTable[label] = itsCodeBufferTop;
+ }
+
+ public void markLabel(int label, short stackTop)
+ {
+ markLabel(label);
+ itsStackTop = stackTop;
+ }
+
+ public void markHandler(int theLabel) {
+ itsStackTop = 1;
+ markLabel(theLabel);
+ }
+
+ private int getLabelPC(int label)
+ {
+ if (!(label < 0))
+ throw new IllegalArgumentException("Bad label, no biscuit");
+ label &= 0x7FFFFFFF;
+ if (!(label < itsLabelTableTop))
+ throw new IllegalArgumentException("Bad label");
+ return itsLabelTable[label];
+ }
+
+ private void addLabelFixup(int label, int fixupSite)
+ {
+ if (!(label < 0))
+ throw new IllegalArgumentException("Bad label, no biscuit");
+ label &= 0x7FFFFFFF;
+ if (!(label < itsLabelTableTop))
+ throw new IllegalArgumentException("Bad label");
+ int top = itsFixupTableTop;
+ if (itsFixupTable == null || top == itsFixupTable.length) {
+ if (itsFixupTable == null) {
+ itsFixupTable = new long[MIN_FIXUP_TABLE_SIZE];
+ }else {
+ long[] tmp = new long[itsFixupTable.length * 2];
+ System.arraycopy(itsFixupTable, 0, tmp, 0, top);
+ itsFixupTable = tmp;
+ }
+ }
+ itsFixupTableTop = top + 1;
+ itsFixupTable[top] = ((long)label << 32) | fixupSite;
+ }
+
+ private void fixLabelGotos()
+ {
+ byte[] codeBuffer = itsCodeBuffer;
+ for (int i = 0; i < itsFixupTableTop; i++) {
+ long fixup = itsFixupTable[i];
+ int label = (int)(fixup >> 32);
+ int fixupSite = (int)fixup;
+ int pc = itsLabelTable[label];
+ if (pc == -1) {
+ // Unlocated label
+ throw new RuntimeException();
+ }
+ // -1 to get delta from instruction start
+ 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;
+ }
+
+ /**
+ * Write the class file to the OutputStream.
+ *
+ * @param oStream the stream to write to
+ * @throws IOException if writing to the stream produces an exception
+ */
+ public void write(OutputStream oStream)
+ throws IOException
+ {
+ byte[] array = toByteArray();
+ oStream.write(array);
+ }
+
+ private int getWriteSize()
+ {
+ int size = 0;
+
+ if (itsSourceFileNameIndex != 0) {
+ itsConstantPool.addUtf8("SourceFile");
+ }
+
+ size += 8; //writeLong(FileHeaderConstant);
+ size += itsConstantPool.getWriteSize();
+ size += 2; //writeShort(itsFlags);
+ size += 2; //writeShort(itsThisClassIndex);
+ size += 2; //writeShort(itsSuperClassIndex);
+ size += 2; //writeShort(itsInterfaces.size());
+ size += 2 * itsInterfaces.size();
+
+ size += 2; //writeShort(itsFields.size());
+ for (int i = 0; i < itsFields.size(); i++) {
+ size += ((ClassFileField)(itsFields.get(i))).getWriteSize();
+ }
+
+ size += 2; //writeShort(itsMethods.size());
+ for (int i = 0; i < itsMethods.size(); i++) {
+ size += ((ClassFileMethod)(itsMethods.get(i))).getWriteSize();
+ }
+
+ if (itsSourceFileNameIndex != 0) {
+ size += 2; //writeShort(1); attributes count
+ size += 2; //writeShort(sourceFileAttributeNameIndex);
+ size += 4; //writeInt(2);
+ size += 2; //writeShort(itsSourceFileNameIndex);
+ }else {
+ size += 2; //out.writeShort(0); no attributes
+ }
+
+ return size;
+ }
+
+ /**
+ * Get the class file as array of bytesto the OutputStream.
+ */
+ public byte[] toByteArray()
+ {
+ int dataSize = getWriteSize();
+ byte[] data = new byte[dataSize];
+ int offset = 0;
+
+ short sourceFileAttributeNameIndex = 0;
+ if (itsSourceFileNameIndex != 0) {
+ sourceFileAttributeNameIndex = itsConstantPool.addUtf8(
+ "SourceFile");
+ }
+
+ offset = putInt64(FileHeaderConstant, data, offset);
+ offset = itsConstantPool.write(data, offset);
+ offset = putInt16(itsFlags, data, offset);
+ offset = putInt16(itsThisClassIndex, data, offset);
+ offset = putInt16(itsSuperClassIndex, data, offset);
+ offset = putInt16(itsInterfaces.size(), data, offset);
+ for (int i = 0; i < itsInterfaces.size(); i++) {
+ int interfaceIndex = ((Short)(itsInterfaces.get(i))).shortValue();
+ offset = putInt16(interfaceIndex, data, offset);
+ }
+ offset = putInt16(itsFields.size(), data, offset);
+ for (int i = 0; i < itsFields.size(); i++) {
+ ClassFileField field = (ClassFileField)itsFields.get(i);
+ offset = field.write(data, offset);
+ }
+ offset = putInt16(itsMethods.size(), data, offset);
+ for (int i = 0; i < itsMethods.size(); i++) {
+ ClassFileMethod method = (ClassFileMethod)itsMethods.get(i);
+ offset = method.write(data, offset);
+ }
+ if (itsSourceFileNameIndex != 0) {
+ offset = putInt16(1, data, offset); // attributes count
+ offset = putInt16(sourceFileAttributeNameIndex, data, offset);
+ offset = putInt32(2, data, offset);
+ offset = putInt16(itsSourceFileNameIndex, data, offset);
+ } else {
+ offset = putInt16(0, data, offset); // no attributes
+ }
+
+ if (offset != dataSize) {
+ // Check getWriteSize is consistent with write!
+ throw new RuntimeException();
+ }
+
+ return data;
+ }
+
+ static int putInt64(long value, byte[] array, int offset)
+ {
+ offset = putInt32((int)(value >>> 32), array, offset);
+ return putInt32((int)value, array, offset);
+ }
+
+ private static void badStack(int value)
+ {
+ String s;
+ if (value < 0) { s = "Stack underflow: "+value; }
+ else { s = "Too big stack: "+value; }
+ throw new IllegalStateException(s);
+ }
+
+ /*
+ Really weird. Returns an int with # parameters in hi 16 bits, and
+ stack difference removal of parameters from stack and pushing the
+ result (it does not take into account removal of this in case of
+ non-static methods).
+ If Java really supported references we wouldn't have to be this
+ perverted.
+ */
+ private static int sizeOfParameters(String pString)
+ {
+ int length = pString.length();
+ int rightParenthesis = pString.lastIndexOf(')');
+ if (3 <= length /* minimal signature takes at least 3 chars: ()V */
+ && pString.charAt(0) == '('
+ && 1 <= rightParenthesis && rightParenthesis + 1 < length)
+ {
+ boolean ok = true;
+ int index = 1;
+ int stackDiff = 0;
+ int count = 0;
+ stringLoop:
+ while (index != rightParenthesis) {
+ switch (pString.charAt(index)) {
+ default:
+ ok = false;
+ break stringLoop;
+ case 'J' :
+ case 'D' :
+ --stackDiff;
+ // fall thru
+ case 'B' :
+ case 'S' :
+ case 'C' :
+ case 'I' :
+ case 'Z' :
+ case 'F' :
+ --stackDiff;
+ ++count;
+ ++index;
+ continue;
+ case '[' :
+ ++index;
+ int c = pString.charAt(index);
+ while (c == '[') {
+ ++index;
+ c = pString.charAt(index);
+ }
+ switch (c) {
+ default:
+ ok = false;
+ break stringLoop;
+ case 'J' :
+ case 'D' :
+ case 'B' :
+ case 'S' :
+ case 'C' :
+ case 'I' :
+ case 'Z' :
+ case 'F' :
+ --stackDiff;
+ ++count;
+ ++index;
+ continue;
+ case 'L':
+ // fall thru
+ }
+ // fall thru
+ case 'L' : {
+ --stackDiff;
+ ++count;
+ ++index;
+ int semicolon = pString.indexOf(';', index);
+ if (!(index + 1 <= semicolon
+ && semicolon < rightParenthesis))
+ {
+ ok = false;
+ break stringLoop;
+ }
+ index = semicolon + 1;
+ continue;
+ }
+ }
+ }
+ if (ok) {
+ switch (pString.charAt(rightParenthesis + 1)) {
+ default:
+ ok = false;
+ break;
+ case 'J' :
+ case 'D' :
+ ++stackDiff;
+ // fall thru
+ case 'B' :
+ case 'S' :
+ case 'C' :
+ case 'I' :
+ case 'Z' :
+ case 'F' :
+ case 'L' :
+ case '[' :
+ ++stackDiff;
+ // fall thru
+ case 'V' :
+ break;
+ }
+ if (ok) {
+ return ((count << 16) | (0xFFFF & stackDiff));
+ }
+ }
+ }
+ throw new IllegalArgumentException(
+ "Bad parameter signature: "+pString);
+ }
+
+ static int putInt16(int value, byte[] array, int offset)
+ {
+ array[offset + 0] = (byte)(value >>> 8);
+ array[offset + 1] = (byte)value;
+ return offset + 2;
+ }
+
+ static int putInt32(int value, byte[] array, int offset)
+ {
+ array[offset + 0] = (byte)(value >>> 24);
+ array[offset + 1] = (byte)(value >>> 16);
+ array[offset + 2] = (byte)(value >>> 8);
+ array[offset + 3] = (byte)value;
+ return offset + 4;
+ }
+
+ /**
+ * Number of operands accompanying the opcode.
+ */
+ static int opcodeCount(int opcode)
+ {
+ switch (opcode) {
+ case ByteCode.AALOAD:
+ case ByteCode.AASTORE:
+ case ByteCode.ACONST_NULL:
+ case ByteCode.ALOAD_0:
+ case ByteCode.ALOAD_1:
+ case ByteCode.ALOAD_2:
+ case ByteCode.ALOAD_3:
+ case ByteCode.ARETURN:
+ case ByteCode.ARRAYLENGTH:
+ case ByteCode.ASTORE_0:
+ case ByteCode.ASTORE_1:
+ case ByteCode.ASTORE_2:
+ case ByteCode.ASTORE_3:
+ case ByteCode.ATHROW:
+ case ByteCode.BALOAD:
+ case ByteCode.BASTORE:
+ case ByteCode.BREAKPOINT:
+ case ByteCode.CALOAD:
+ case ByteCode.CASTORE:
+ case ByteCode.D2F:
+ case ByteCode.D2I:
+ case ByteCode.D2L:
+ case ByteCode.DADD:
+ case ByteCode.DALOAD:
+ case ByteCode.DASTORE:
+ case ByteCode.DCMPG:
+ case ByteCode.DCMPL:
+ case ByteCode.DCONST_0:
+ case ByteCode.DCONST_1:
+ case ByteCode.DDIV:
+ case ByteCode.DLOAD_0:
+ case ByteCode.DLOAD_1:
+ case ByteCode.DLOAD_2:
+ case ByteCode.DLOAD_3:
+ case ByteCode.DMUL:
+ case ByteCode.DNEG:
+ case ByteCode.DREM:
+ case ByteCode.DRETURN:
+ case ByteCode.DSTORE_0:
+ case ByteCode.DSTORE_1:
+ case ByteCode.DSTORE_2:
+ case ByteCode.DSTORE_3:
+ case ByteCode.DSUB:
+ case ByteCode.DUP:
+ case ByteCode.DUP2:
+ case ByteCode.DUP2_X1:
+ case ByteCode.DUP2_X2:
+ case ByteCode.DUP_X1:
+ case ByteCode.DUP_X2:
+ case ByteCode.F2D:
+ case ByteCode.F2I:
+ case ByteCode.F2L:
+ case ByteCode.FADD:
+ case ByteCode.FALOAD:
+ case ByteCode.FASTORE:
+ case ByteCode.FCMPG:
+ case ByteCode.FCMPL:
+ case ByteCode.FCONST_0:
+ case ByteCode.FCONST_1:
+ case ByteCode.FCONST_2:
+ case ByteCode.FDIV:
+ case ByteCode.FLOAD_0:
+ case ByteCode.FLOAD_1:
+ case ByteCode.FLOAD_2:
+ case ByteCode.FLOAD_3:
+ case ByteCode.FMUL:
+ case ByteCode.FNEG:
+ case ByteCode.FREM:
+ case ByteCode.FRETURN:
+ case ByteCode.FSTORE_0:
+ case ByteCode.FSTORE_1:
+ case ByteCode.FSTORE_2:
+ case ByteCode.FSTORE_3:
+ case ByteCode.FSUB:
+ case ByteCode.I2B:
+ case ByteCode.I2C:
+ case ByteCode.I2D:
+ case ByteCode.I2F:
+ case ByteCode.I2L:
+ case ByteCode.I2S:
+ case ByteCode.IADD:
+ case ByteCode.IALOAD:
+ case ByteCode.IAND:
+ case ByteCode.IASTORE:
+ case ByteCode.ICONST_0:
+ case ByteCode.ICONST_1:
+ case ByteCode.ICONST_2:
+ case ByteCode.ICONST_3:
+ case ByteCode.ICONST_4:
+ case ByteCode.ICONST_5:
+ case ByteCode.ICONST_M1:
+ case ByteCode.IDIV:
+ case ByteCode.ILOAD_0:
+ case ByteCode.ILOAD_1:
+ case ByteCode.ILOAD_2:
+ case ByteCode.ILOAD_3:
+ case ByteCode.IMPDEP1:
+ case ByteCode.IMPDEP2:
+ case ByteCode.IMUL:
+ case ByteCode.INEG:
+ case ByteCode.IOR:
+ case ByteCode.IREM:
+ case ByteCode.IRETURN:
+ case ByteCode.ISHL:
+ case ByteCode.ISHR:
+ case ByteCode.ISTORE_0:
+ case ByteCode.ISTORE_1:
+ case ByteCode.ISTORE_2:
+ case ByteCode.ISTORE_3:
+ case ByteCode.ISUB:
+ case ByteCode.IUSHR:
+ case ByteCode.IXOR:
+ case ByteCode.L2D:
+ case ByteCode.L2F:
+ case ByteCode.L2I:
+ case ByteCode.LADD:
+ case ByteCode.LALOAD:
+ case ByteCode.LAND:
+ case ByteCode.LASTORE:
+ case ByteCode.LCMP:
+ case ByteCode.LCONST_0:
+ case ByteCode.LCONST_1:
+ case ByteCode.LDIV:
+ case ByteCode.LLOAD_0:
+ case ByteCode.LLOAD_1:
+ case ByteCode.LLOAD_2:
+ case ByteCode.LLOAD_3:
+ case ByteCode.LMUL:
+ case ByteCode.LNEG:
+ case ByteCode.LOR:
+ case ByteCode.LREM:
+ case ByteCode.LRETURN:
+ case ByteCode.LSHL:
+ case ByteCode.LSHR:
+ case ByteCode.LSTORE_0:
+ case ByteCode.LSTORE_1:
+ case ByteCode.LSTORE_2:
+ case ByteCode.LSTORE_3:
+ case ByteCode.LSUB:
+ case ByteCode.LUSHR:
+ case ByteCode.LXOR:
+ case ByteCode.MONITORENTER:
+ case ByteCode.MONITOREXIT:
+ case ByteCode.NOP:
+ case ByteCode.POP:
+ case ByteCode.POP2:
+ case ByteCode.RETURN:
+ case ByteCode.SALOAD:
+ case ByteCode.SASTORE:
+ case ByteCode.SWAP:
+ case ByteCode.WIDE:
+ return 0;
+ case ByteCode.ALOAD:
+ case ByteCode.ANEWARRAY:
+ case ByteCode.ASTORE:
+ case ByteCode.BIPUSH:
+ case ByteCode.CHECKCAST:
+ case ByteCode.DLOAD:
+ case ByteCode.DSTORE:
+ case ByteCode.FLOAD:
+ case ByteCode.FSTORE:
+ case ByteCode.GETFIELD:
+ case ByteCode.GETSTATIC:
+ case ByteCode.GOTO:
+ case ByteCode.GOTO_W:
+ case ByteCode.IFEQ:
+ case ByteCode.IFGE:
+ case ByteCode.IFGT:
+ case ByteCode.IFLE:
+ case ByteCode.IFLT:
+ case ByteCode.IFNE:
+ case ByteCode.IFNONNULL:
+ case ByteCode.IFNULL:
+ case ByteCode.IF_ACMPEQ:
+ case ByteCode.IF_ACMPNE:
+ case ByteCode.IF_ICMPEQ:
+ case ByteCode.IF_ICMPGE:
+ case ByteCode.IF_ICMPGT:
+ case ByteCode.IF_ICMPLE:
+ case ByteCode.IF_ICMPLT:
+ case ByteCode.IF_ICMPNE:
+ case ByteCode.ILOAD:
+ case ByteCode.INSTANCEOF:
+ case ByteCode.INVOKEINTERFACE:
+ case ByteCode.INVOKESPECIAL:
+ case ByteCode.INVOKESTATIC:
+ case ByteCode.INVOKEVIRTUAL:
+ case ByteCode.ISTORE:
+ case ByteCode.JSR:
+ case ByteCode.JSR_W:
+ case ByteCode.LDC:
+ case ByteCode.LDC2_W:
+ case ByteCode.LDC_W:
+ case ByteCode.LLOAD:
+ case ByteCode.LSTORE:
+ case ByteCode.NEW:
+ case ByteCode.NEWARRAY:
+ case ByteCode.PUTFIELD:
+ case ByteCode.PUTSTATIC:
+ case ByteCode.RET:
+ case ByteCode.SIPUSH:
+ return 1;
+
+ case ByteCode.IINC:
+ case ByteCode.MULTIANEWARRAY:
+ return 2;
+
+ case ByteCode.LOOKUPSWITCH:
+ case ByteCode.TABLESWITCH:
+ return -1;
+ }
+ throw new IllegalArgumentException("Bad opcode: "+opcode);
+ }
+
+ /**
+ * The effect on the operand stack of a given opcode.
+ */
+ static int stackChange(int opcode)
+ {
+ // For INVOKE... accounts only for popping this (unless static),
+ // ignoring parameters and return type
+ switch (opcode) {
+ case ByteCode.DASTORE:
+ case ByteCode.LASTORE:
+ return -4;
+
+ case ByteCode.AASTORE:
+ case ByteCode.BASTORE:
+ case ByteCode.CASTORE:
+ case ByteCode.DCMPG:
+ case ByteCode.DCMPL:
+ case ByteCode.FASTORE:
+ case ByteCode.IASTORE:
+ case ByteCode.LCMP:
+ case ByteCode.SASTORE:
+ return -3;
+
+ case ByteCode.DADD:
+ case ByteCode.DDIV:
+ case ByteCode.DMUL:
+ case ByteCode.DREM:
+ case ByteCode.DRETURN:
+ case ByteCode.DSTORE:
+ case ByteCode.DSTORE_0:
+ case ByteCode.DSTORE_1:
+ case ByteCode.DSTORE_2:
+ case ByteCode.DSTORE_3:
+ case ByteCode.DSUB:
+ case ByteCode.IF_ACMPEQ:
+ case ByteCode.IF_ACMPNE:
+ case ByteCode.IF_ICMPEQ:
+ case ByteCode.IF_ICMPGE:
+ case ByteCode.IF_ICMPGT:
+ case ByteCode.IF_ICMPLE:
+ case ByteCode.IF_ICMPLT:
+ case ByteCode.IF_ICMPNE:
+ case ByteCode.LADD:
+ case ByteCode.LAND:
+ case ByteCode.LDIV:
+ case ByteCode.LMUL:
+ case ByteCode.LOR:
+ case ByteCode.LREM:
+ case ByteCode.LRETURN:
+ case ByteCode.LSTORE:
+ case ByteCode.LSTORE_0:
+ case ByteCode.LSTORE_1:
+ case ByteCode.LSTORE_2:
+ case ByteCode.LSTORE_3:
+ case ByteCode.LSUB:
+ case ByteCode.LXOR:
+ case ByteCode.POP2:
+ return -2;
+
+ case ByteCode.AALOAD:
+ case ByteCode.ARETURN:
+ case ByteCode.ASTORE:
+ case ByteCode.ASTORE_0:
+ case ByteCode.ASTORE_1:
+ case ByteCode.ASTORE_2:
+ case ByteCode.ASTORE_3:
+ case ByteCode.ATHROW:
+ case ByteCode.BALOAD:
+ case ByteCode.CALOAD:
+ case ByteCode.D2F:
+ case ByteCode.D2I:
+ case ByteCode.FADD:
+ case ByteCode.FALOAD:
+ case ByteCode.FCMPG:
+ case ByteCode.FCMPL:
+ case ByteCode.FDIV:
+ case ByteCode.FMUL:
+ case ByteCode.FREM:
+ case ByteCode.FRETURN:
+ case ByteCode.FSTORE:
+ case ByteCode.FSTORE_0:
+ case ByteCode.FSTORE_1:
+ case ByteCode.FSTORE_2:
+ case ByteCode.FSTORE_3:
+ case ByteCode.FSUB:
+ case ByteCode.GETFIELD:
+ case ByteCode.IADD:
+ case ByteCode.IALOAD:
+ case ByteCode.IAND:
+ case ByteCode.IDIV:
+ case ByteCode.IFEQ:
+ case ByteCode.IFGE:
+ case ByteCode.IFGT:
+ case ByteCode.IFLE:
+ case ByteCode.IFLT:
+ case ByteCode.IFNE:
+ case ByteCode.IFNONNULL:
+ case ByteCode.IFNULL:
+ case ByteCode.IMUL:
+ case ByteCode.INVOKEINTERFACE: //
+ case ByteCode.INVOKESPECIAL: // but needs to account for
+ case ByteCode.INVOKEVIRTUAL: // pops 'this' (unless static)
+ case ByteCode.IOR:
+ case ByteCode.IREM:
+ case ByteCode.IRETURN:
+ case ByteCode.ISHL:
+ case ByteCode.ISHR:
+ case ByteCode.ISTORE:
+ case ByteCode.ISTORE_0:
+ case ByteCode.ISTORE_1:
+ case ByteCode.ISTORE_2:
+ case ByteCode.ISTORE_3:
+ case ByteCode.ISUB:
+ case ByteCode.IUSHR:
+ case ByteCode.IXOR:
+ case ByteCode.L2F:
+ case ByteCode.L2I:
+ case ByteCode.LOOKUPSWITCH:
+ case ByteCode.LSHL:
+ case ByteCode.LSHR:
+ case ByteCode.LUSHR:
+ case ByteCode.MONITORENTER:
+ case ByteCode.MONITOREXIT:
+ case ByteCode.POP:
+ case ByteCode.PUTFIELD:
+ case ByteCode.SALOAD:
+ case ByteCode.TABLESWITCH:
+ return -1;
+
+ case ByteCode.ANEWARRAY:
+ case ByteCode.ARRAYLENGTH:
+ case ByteCode.BREAKPOINT:
+ case ByteCode.CHECKCAST:
+ case ByteCode.D2L:
+ case ByteCode.DALOAD:
+ case ByteCode.DNEG:
+ case ByteCode.F2I:
+ case ByteCode.FNEG:
+ case ByteCode.GETSTATIC:
+ case ByteCode.GOTO:
+ case ByteCode.GOTO_W:
+ case ByteCode.I2B:
+ case ByteCode.I2C:
+ case ByteCode.I2F:
+ case ByteCode.I2S:
+ case ByteCode.IINC:
+ case ByteCode.IMPDEP1:
+ case ByteCode.IMPDEP2:
+ case ByteCode.INEG:
+ case ByteCode.INSTANCEOF:
+ case ByteCode.INVOKESTATIC:
+ case ByteCode.L2D:
+ case ByteCode.LALOAD:
+ case ByteCode.LNEG:
+ case ByteCode.NEWARRAY:
+ case ByteCode.NOP:
+ case ByteCode.PUTSTATIC:
+ case ByteCode.RET:
+ case ByteCode.RETURN:
+ case ByteCode.SWAP:
+ case ByteCode.WIDE:
+ return 0;
+
+ case ByteCode.ACONST_NULL:
+ case ByteCode.ALOAD:
+ case ByteCode.ALOAD_0:
+ case ByteCode.ALOAD_1:
+ case ByteCode.ALOAD_2:
+ case ByteCode.ALOAD_3:
+ case ByteCode.BIPUSH:
+ case ByteCode.DUP:
+ case ByteCode.DUP_X1:
+ case ByteCode.DUP_X2:
+ case ByteCode.F2D:
+ case ByteCode.F2L:
+ case ByteCode.FCONST_0:
+ case ByteCode.FCONST_1:
+ case ByteCode.FCONST_2:
+ case ByteCode.FLOAD:
+ case ByteCode.FLOAD_0:
+ case ByteCode.FLOAD_1:
+ case ByteCode.FLOAD_2:
+ case ByteCode.FLOAD_3:
+ case ByteCode.I2D:
+ case ByteCode.I2L:
+ case ByteCode.ICONST_0:
+ case ByteCode.ICONST_1:
+ case ByteCode.ICONST_2:
+ case ByteCode.ICONST_3:
+ case ByteCode.ICONST_4:
+ case ByteCode.ICONST_5:
+ case ByteCode.ICONST_M1:
+ case ByteCode.ILOAD:
+ case ByteCode.ILOAD_0:
+ case ByteCode.ILOAD_1:
+ case ByteCode.ILOAD_2:
+ case ByteCode.ILOAD_3:
+ case ByteCode.JSR:
+ case ByteCode.JSR_W:
+ case ByteCode.LDC:
+ case ByteCode.LDC_W:
+ case ByteCode.MULTIANEWARRAY:
+ case ByteCode.NEW:
+ case ByteCode.SIPUSH:
+ return 1;
+
+ case ByteCode.DCONST_0:
+ case ByteCode.DCONST_1:
+ case ByteCode.DLOAD:
+ case ByteCode.DLOAD_0:
+ case ByteCode.DLOAD_1:
+ case ByteCode.DLOAD_2:
+ case ByteCode.DLOAD_3:
+ case ByteCode.DUP2:
+ case ByteCode.DUP2_X1:
+ case ByteCode.DUP2_X2:
+ case ByteCode.LCONST_0:
+ case ByteCode.LCONST_1:
+ case ByteCode.LDC2_W:
+ case ByteCode.LLOAD:
+ case ByteCode.LLOAD_0:
+ case ByteCode.LLOAD_1:
+ case ByteCode.LLOAD_2:
+ case ByteCode.LLOAD_3:
+ return 2;
+ }
+ throw new IllegalArgumentException("Bad opcode: "+opcode);
+ }
+
+ /*
+ * Number of bytes of operands generated after the opcode.
+ * Not in use currently.
+ */
+/*
+ int extra(int opcode)
+ {
+ switch (opcode) {
+ case ByteCode.AALOAD:
+ case ByteCode.AASTORE:
+ case ByteCode.ACONST_NULL:
+ case ByteCode.ALOAD_0:
+ case ByteCode.ALOAD_1:
+ case ByteCode.ALOAD_2:
+ case ByteCode.ALOAD_3:
+ case ByteCode.ARETURN:
+ case ByteCode.ARRAYLENGTH:
+ case ByteCode.ASTORE_0:
+ case ByteCode.ASTORE_1:
+ case ByteCode.ASTORE_2:
+ case ByteCode.ASTORE_3:
+ case ByteCode.ATHROW:
+ case ByteCode.BALOAD:
+ case ByteCode.BASTORE:
+ case ByteCode.BREAKPOINT:
+ case ByteCode.CALOAD:
+ case ByteCode.CASTORE:
+ case ByteCode.D2F:
+ case ByteCode.D2I:
+ case ByteCode.D2L:
+ case ByteCode.DADD:
+ case ByteCode.DALOAD:
+ case ByteCode.DASTORE:
+ case ByteCode.DCMPG:
+ case ByteCode.DCMPL:
+ case ByteCode.DCONST_0:
+ case ByteCode.DCONST_1:
+ case ByteCode.DDIV:
+ case ByteCode.DLOAD_0:
+ case ByteCode.DLOAD_1:
+ case ByteCode.DLOAD_2:
+ case ByteCode.DLOAD_3:
+ case ByteCode.DMUL:
+ case ByteCode.DNEG:
+ case ByteCode.DREM:
+ case ByteCode.DRETURN:
+ case ByteCode.DSTORE_0:
+ case ByteCode.DSTORE_1:
+ case ByteCode.DSTORE_2:
+ case ByteCode.DSTORE_3:
+ case ByteCode.DSUB:
+ case ByteCode.DUP2:
+ case ByteCode.DUP2_X1:
+ case ByteCode.DUP2_X2:
+ case ByteCode.DUP:
+ case ByteCode.DUP_X1:
+ case ByteCode.DUP_X2:
+ case ByteCode.F2D:
+ case ByteCode.F2I:
+ case ByteCode.F2L:
+ case ByteCode.FADD:
+ case ByteCode.FALOAD:
+ case ByteCode.FASTORE:
+ case ByteCode.FCMPG:
+ case ByteCode.FCMPL:
+ case ByteCode.FCONST_0:
+ case ByteCode.FCONST_1:
+ case ByteCode.FCONST_2:
+ case ByteCode.FDIV:
+ case ByteCode.FLOAD_0:
+ case ByteCode.FLOAD_1:
+ case ByteCode.FLOAD_2:
+ case ByteCode.FLOAD_3:
+ case ByteCode.FMUL:
+ case ByteCode.FNEG:
+ case ByteCode.FREM:
+ case ByteCode.FRETURN:
+ case ByteCode.FSTORE_0:
+ case ByteCode.FSTORE_1:
+ case ByteCode.FSTORE_2:
+ case ByteCode.FSTORE_3:
+ case ByteCode.FSUB:
+ case ByteCode.I2B:
+ case ByteCode.I2C:
+ case ByteCode.I2D:
+ case ByteCode.I2F:
+ case ByteCode.I2L:
+ case ByteCode.I2S:
+ case ByteCode.IADD:
+ case ByteCode.IALOAD:
+ case ByteCode.IAND:
+ case ByteCode.IASTORE:
+ case ByteCode.ICONST_0:
+ case ByteCode.ICONST_1:
+ case ByteCode.ICONST_2:
+ case ByteCode.ICONST_3:
+ case ByteCode.ICONST_4:
+ case ByteCode.ICONST_5:
+ case ByteCode.ICONST_M1:
+ case ByteCode.IDIV:
+ case ByteCode.ILOAD_0:
+ case ByteCode.ILOAD_1:
+ case ByteCode.ILOAD_2:
+ case ByteCode.ILOAD_3:
+ case ByteCode.IMPDEP1:
+ case ByteCode.IMPDEP2:
+ case ByteCode.IMUL:
+ case ByteCode.INEG:
+ case ByteCode.IOR:
+ case ByteCode.IREM:
+ case ByteCode.IRETURN:
+ case ByteCode.ISHL:
+ case ByteCode.ISHR:
+ case ByteCode.ISTORE_0:
+ case ByteCode.ISTORE_1:
+ case ByteCode.ISTORE_2:
+ case ByteCode.ISTORE_3:
+ case ByteCode.ISUB:
+ case ByteCode.IUSHR:
+ case ByteCode.IXOR:
+ case ByteCode.L2D:
+ case ByteCode.L2F:
+ case ByteCode.L2I:
+ case ByteCode.LADD:
+ case ByteCode.LALOAD:
+ case ByteCode.LAND:
+ case ByteCode.LASTORE:
+ case ByteCode.LCMP:
+ case ByteCode.LCONST_0:
+ case ByteCode.LCONST_1:
+ case ByteCode.LDIV:
+ case ByteCode.LLOAD_0:
+ case ByteCode.LLOAD_1:
+ case ByteCode.LLOAD_2:
+ case ByteCode.LLOAD_3:
+ case ByteCode.LMUL:
+ case ByteCode.LNEG:
+ case ByteCode.LOR:
+ case ByteCode.LREM:
+ case ByteCode.LRETURN:
+ case ByteCode.LSHL:
+ case ByteCode.LSHR:
+ case ByteCode.LSTORE_0:
+ case ByteCode.LSTORE_1:
+ case ByteCode.LSTORE_2:
+ case ByteCode.LSTORE_3:
+ case ByteCode.LSUB:
+ case ByteCode.LUSHR:
+ case ByteCode.LXOR:
+ case ByteCode.MONITORENTER:
+ case ByteCode.MONITOREXIT:
+ case ByteCode.NOP:
+ case ByteCode.POP2:
+ case ByteCode.POP:
+ case ByteCode.RETURN:
+ case ByteCode.SALOAD:
+ case ByteCode.SASTORE:
+ case ByteCode.SWAP:
+ case ByteCode.WIDE:
+ return 0;
+
+ case ByteCode.ALOAD:
+ case ByteCode.ASTORE:
+ case ByteCode.BIPUSH:
+ case ByteCode.DLOAD:
+ case ByteCode.DSTORE:
+ case ByteCode.FLOAD:
+ case ByteCode.FSTORE:
+ case ByteCode.ILOAD:
+ case ByteCode.ISTORE:
+ case ByteCode.LDC:
+ case ByteCode.LLOAD:
+ case ByteCode.LSTORE:
+ case ByteCode.NEWARRAY:
+ case ByteCode.RET:
+ return 1;
+
+ case ByteCode.ANEWARRAY:
+ case ByteCode.CHECKCAST:
+ case ByteCode.GETFIELD:
+ case ByteCode.GETSTATIC:
+ case ByteCode.GOTO:
+ case ByteCode.IFEQ:
+ case ByteCode.IFGE:
+ case ByteCode.IFGT:
+ case ByteCode.IFLE:
+ case ByteCode.IFLT:
+ case ByteCode.IFNE:
+ case ByteCode.IFNONNULL:
+ case ByteCode.IFNULL:
+ case ByteCode.IF_ACMPEQ:
+ case ByteCode.IF_ACMPNE:
+ case ByteCode.IF_ICMPEQ:
+ case ByteCode.IF_ICMPGE:
+ case ByteCode.IF_ICMPGT:
+ case ByteCode.IF_ICMPLE:
+ case ByteCode.IF_ICMPLT:
+ case ByteCode.IF_ICMPNE:
+ case ByteCode.IINC:
+ case ByteCode.INSTANCEOF:
+ case ByteCode.INVOKEINTERFACE:
+ case ByteCode.INVOKESPECIAL:
+ case ByteCode.INVOKESTATIC:
+ case ByteCode.INVOKEVIRTUAL:
+ case ByteCode.JSR:
+ case ByteCode.LDC2_W:
+ case ByteCode.LDC_W:
+ case ByteCode.NEW:
+ case ByteCode.PUTFIELD:
+ case ByteCode.PUTSTATIC:
+ case ByteCode.SIPUSH:
+ return 2;
+
+ case ByteCode.MULTIANEWARRAY:
+ return 3;
+
+ case ByteCode.GOTO_W:
+ case ByteCode.JSR_W:
+ return 4;
+
+ case ByteCode.LOOKUPSWITCH: // depends on alignment
+ case ByteCode.TABLESWITCH: // depends on alignment
+ return -1;
+ }
+ throw new IllegalArgumentException("Bad opcode: "+opcode);
+ }
+*/
+ private static String bytecodeStr(int code)
+ {
+ if (DEBUGSTACK || DEBUGCODE) {
+ switch (code) {
+ case ByteCode.NOP: return "nop";
+ case ByteCode.ACONST_NULL: return "aconst_null";
+ case ByteCode.ICONST_M1: return "iconst_m1";
+ case ByteCode.ICONST_0: return "iconst_0";
+ case ByteCode.ICONST_1: return "iconst_1";
+ case ByteCode.ICONST_2: return "iconst_2";
+ case ByteCode.ICONST_3: return "iconst_3";
+ case ByteCode.ICONST_4: return "iconst_4";
+ case ByteCode.ICONST_5: return "iconst_5";
+ case ByteCode.LCONST_0: return "lconst_0";
+ case ByteCode.LCONST_1: return "lconst_1";
+ case ByteCode.FCONST_0: return "fconst_0";
+ case ByteCode.FCONST_1: return "fconst_1";
+ case ByteCode.FCONST_2: return "fconst_2";
+ case ByteCode.DCONST_0: return "dconst_0";
+ case ByteCode.DCONST_1: return "dconst_1";
+ case ByteCode.BIPUSH: return "bipush";
+ case ByteCode.SIPUSH: return "sipush";
+ case ByteCode.LDC: return "ldc";
+ case ByteCode.LDC_W: return "ldc_w";
+ case ByteCode.LDC2_W: return "ldc2_w";
+ case ByteCode.ILOAD: return "iload";
+ case ByteCode.LLOAD: return "lload";
+ case ByteCode.FLOAD: return "fload";
+ case ByteCode.DLOAD: return "dload";
+ case ByteCode.ALOAD: return "aload";
+ case ByteCode.ILOAD_0: return "iload_0";
+ case ByteCode.ILOAD_1: return "iload_1";
+ case ByteCode.ILOAD_2: return "iload_2";
+ case ByteCode.ILOAD_3: return "iload_3";
+ case ByteCode.LLOAD_0: return "lload_0";
+ case ByteCode.LLOAD_1: return "lload_1";
+ case ByteCode.LLOAD_2: return "lload_2";
+ case ByteCode.LLOAD_3: return "lload_3";
+ case ByteCode.FLOAD_0: return "fload_0";
+ case ByteCode.FLOAD_1: return "fload_1";
+ case ByteCode.FLOAD_2: return "fload_2";
+ case ByteCode.FLOAD_3: return "fload_3";
+ case ByteCode.DLOAD_0: return "dload_0";
+ case ByteCode.DLOAD_1: return "dload_1";
+ case ByteCode.DLOAD_2: return "dload_2";
+ case ByteCode.DLOAD_3: return "dload_3";
+ case ByteCode.ALOAD_0: return "aload_0";
+ case ByteCode.ALOAD_1: return "aload_1";
+ case ByteCode.ALOAD_2: return "aload_2";
+ case ByteCode.ALOAD_3: return "aload_3";
+ case ByteCode.IALOAD: return "iaload";
+ case ByteCode.LALOAD: return "laload";
+ case ByteCode.FALOAD: return "faload";
+ case ByteCode.DALOAD: return "daload";
+ case ByteCode.AALOAD: return "aaload";
+ case ByteCode.BALOAD: return "baload";
+ case ByteCode.CALOAD: return "caload";
+ case ByteCode.SALOAD: return "saload";
+ case ByteCode.ISTORE: return "istore";
+ case ByteCode.LSTORE: return "lstore";
+ case ByteCode.FSTORE: return "fstore";
+ case ByteCode.DSTORE: return "dstore";
+ case ByteCode.ASTORE: return "astore";
+ case ByteCode.ISTORE_0: return "istore_0";
+ case ByteCode.ISTORE_1: return "istore_1";
+ case ByteCode.ISTORE_2: return "istore_2";
+ case ByteCode.ISTORE_3: return "istore_3";
+ case ByteCode.LSTORE_0: return "lstore_0";
+ case ByteCode.LSTORE_1: return "lstore_1";
+ case ByteCode.LSTORE_2: return "lstore_2";
+ case ByteCode.LSTORE_3: return "lstore_3";
+ case ByteCode.FSTORE_0: return "fstore_0";
+ case ByteCode.FSTORE_1: return "fstore_1";
+ case ByteCode.FSTORE_2: return "fstore_2";
+ case ByteCode.FSTORE_3: return "fstore_3";
+ case ByteCode.DSTORE_0: return "dstore_0";
+ case ByteCode.DSTORE_1: return "dstore_1";
+ case ByteCode.DSTORE_2: return "dstore_2";
+ case ByteCode.DSTORE_3: return "dstore_3";
+ case ByteCode.ASTORE_0: return "astore_0";
+ case ByteCode.ASTORE_1: return "astore_1";
+ case ByteCode.ASTORE_2: return "astore_2";
+ case ByteCode.ASTORE_3: return "astore_3";
+ case ByteCode.IASTORE: return "iastore";
+ case ByteCode.LASTORE: return "lastore";
+ case ByteCode.FASTORE: return "fastore";
+ case ByteCode.DASTORE: return "dastore";
+ case ByteCode.AASTORE: return "aastore";
+ case ByteCode.BASTORE: return "bastore";
+ case ByteCode.CASTORE: return "castore";
+ case ByteCode.SASTORE: return "sastore";
+ case ByteCode.POP: return "pop";
+ case ByteCode.POP2: return "pop2";
+ case ByteCode.DUP: return "dup";
+ case ByteCode.DUP_X1: return "dup_x1";
+ case ByteCode.DUP_X2: return "dup_x2";
+ case ByteCode.DUP2: return "dup2";
+ case ByteCode.DUP2_X1: return "dup2_x1";
+ case ByteCode.DUP2_X2: return "dup2_x2";
+ case ByteCode.SWAP: return "swap";
+ case ByteCode.IADD: return "iadd";
+ case ByteCode.LADD: return "ladd";
+ case ByteCode.FADD: return "fadd";
+ case ByteCode.DADD: return "dadd";
+ case ByteCode.ISUB: return "isub";
+ case ByteCode.LSUB: return "lsub";
+ case ByteCode.FSUB: return "fsub";
+ case ByteCode.DSUB: return "dsub";
+ case ByteCode.IMUL: return "imul";
+ case ByteCode.LMUL: return "lmul";
+ case ByteCode.FMUL: return "fmul";
+ case ByteCode.DMUL: return "dmul";
+ case ByteCode.IDIV: return "idiv";
+ case ByteCode.LDIV: return "ldiv";
+ case ByteCode.FDIV: return "fdiv";
+ case ByteCode.DDIV: return "ddiv";
+ case ByteCode.IREM: return "irem";
+ case ByteCode.LREM: return "lrem";
+ case ByteCode.FREM: return "frem";
+ case ByteCode.DREM: return "drem";
+ case ByteCode.INEG: return "ineg";
+ case ByteCode.LNEG: return "lneg";
+ case ByteCode.FNEG: return "fneg";
+ case ByteCode.DNEG: return "dneg";
+ case ByteCode.ISHL: return "ishl";
+ case ByteCode.LSHL: return "lshl";
+ case ByteCode.ISHR: return "ishr";
+ case ByteCode.LSHR: return "lshr";
+ case ByteCode.IUSHR: return "iushr";
+ case ByteCode.LUSHR: return "lushr";
+ case ByteCode.IAND: return "iand";
+ case ByteCode.LAND: return "land";
+ case ByteCode.IOR: return "ior";
+ case ByteCode.LOR: return "lor";
+ case ByteCode.IXOR: return "ixor";
+ case ByteCode.LXOR: return "lxor";
+ case ByteCode.IINC: return "iinc";
+ case ByteCode.I2L: return "i2l";
+ case ByteCode.I2F: return "i2f";
+ case ByteCode.I2D: return "i2d";
+ case ByteCode.L2I: return "l2i";
+ case ByteCode.L2F: return "l2f";
+ case ByteCode.L2D: return "l2d";
+ case ByteCode.F2I: return "f2i";
+ case ByteCode.F2L: return "f2l";
+ case ByteCode.F2D: return "f2d";
+ case ByteCode.D2I: return "d2i";
+ case ByteCode.D2L: return "d2l";
+ case ByteCode.D2F: return "d2f";
+ case ByteCode.I2B: return "i2b";
+ case ByteCode.I2C: return "i2c";
+ case ByteCode.I2S: return "i2s";
+ case ByteCode.LCMP: return "lcmp";
+ case ByteCode.FCMPL: return "fcmpl";
+ case ByteCode.FCMPG: return "fcmpg";
+ case ByteCode.DCMPL: return "dcmpl";
+ case ByteCode.DCMPG: return "dcmpg";
+ case ByteCode.IFEQ: return "ifeq";
+ case ByteCode.IFNE: return "ifne";
+ case ByteCode.IFLT: return "iflt";
+ case ByteCode.IFGE: return "ifge";
+ case ByteCode.IFGT: return "ifgt";
+ case ByteCode.IFLE: return "ifle";
+ case ByteCode.IF_ICMPEQ: return "if_icmpeq";
+ case ByteCode.IF_ICMPNE: return "if_icmpne";
+ case ByteCode.IF_ICMPLT: return "if_icmplt";
+ case ByteCode.IF_ICMPGE: return "if_icmpge";
+ case ByteCode.IF_ICMPGT: return "if_icmpgt";
+ case ByteCode.IF_ICMPLE: return "if_icmple";
+ case ByteCode.IF_ACMPEQ: return "if_acmpeq";
+ case ByteCode.IF_ACMPNE: return "if_acmpne";
+ case ByteCode.GOTO: return "goto";
+ case ByteCode.JSR: return "jsr";
+ case ByteCode.RET: return "ret";
+ case ByteCode.TABLESWITCH: return "tableswitch";
+ case ByteCode.LOOKUPSWITCH: return "lookupswitch";
+ case ByteCode.IRETURN: return "ireturn";
+ case ByteCode.LRETURN: return "lreturn";
+ case ByteCode.FRETURN: return "freturn";
+ case ByteCode.DRETURN: return "dreturn";
+ case ByteCode.ARETURN: return "areturn";
+ case ByteCode.RETURN: return "return";
+ case ByteCode.GETSTATIC: return "getstatic";
+ case ByteCode.PUTSTATIC: return "putstatic";
+ case ByteCode.GETFIELD: return "getfield";
+ case ByteCode.PUTFIELD: return "putfield";
+ case ByteCode.INVOKEVIRTUAL: return "invokevirtual";
+ case ByteCode.INVOKESPECIAL: return "invokespecial";
+ case ByteCode.INVOKESTATIC: return "invokestatic";
+ case ByteCode.INVOKEINTERFACE: return "invokeinterface";
+ case ByteCode.NEW: return "new";
+ case ByteCode.NEWARRAY: return "newarray";
+ case ByteCode.ANEWARRAY: return "anewarray";
+ case ByteCode.ARRAYLENGTH: return "arraylength";
+ case ByteCode.ATHROW: return "athrow";
+ case ByteCode.CHECKCAST: return "checkcast";
+ case ByteCode.INSTANCEOF: return "instanceof";
+ case ByteCode.MONITORENTER: return "monitorenter";
+ case ByteCode.MONITOREXIT: return "monitorexit";
+ case ByteCode.WIDE: return "wide";
+ case ByteCode.MULTIANEWARRAY: return "multianewarray";
+ case ByteCode.IFNULL: return "ifnull";
+ case ByteCode.IFNONNULL: return "ifnonnull";
+ case ByteCode.GOTO_W: return "goto_w";
+ case ByteCode.JSR_W: return "jsr_w";
+ case ByteCode.BREAKPOINT: return "breakpoint";
+
+ case ByteCode.IMPDEP1: return "impdep1";
+ case ByteCode.IMPDEP2: return "impdep2";
+ }
+ }
+ return "";
+ }
+
+ final char[] getCharBuffer(int minimalSize)
+ {
+ if (minimalSize > tmpCharBuffer.length) {
+ int newSize = tmpCharBuffer.length * 2;
+ if (minimalSize > newSize) { newSize = minimalSize; }
+ tmpCharBuffer = new char[newSize];
+ }
+ return tmpCharBuffer;
+ }
+
+ private static final int LineNumberTableSize = 16;
+ private static final int ExceptionTableSize = 4;
+
+ private final static long FileHeaderConstant = 0xCAFEBABE0003002DL;
+ // Set DEBUG flags to true to get better checking and progress info.
+ private static final boolean DEBUGSTACK = false;
+ private static final boolean DEBUGLABELS = false;
+ private static final boolean DEBUGCODE = false;
+
+ private String generatedClassName;
+
+ private ExceptionTableEntry itsExceptionTable[];
+ private int itsExceptionTableTop;
+
+ private int itsLineNumberTable[]; // pack start_pc & line_number together
+ private int itsLineNumberTableTop;
+
+ private byte[] itsCodeBuffer = new byte[256];
+ private int itsCodeBufferTop;
+
+ private ConstantPool itsConstantPool;
+
+ private ClassFileMethod itsCurrentMethod;
+ private short itsStackTop;
+
+ private short itsMaxStack;
+ private short itsMaxLocals;
+
+ private ObjArray itsMethods = new ObjArray();
+ private ObjArray itsFields = new ObjArray();
+ private ObjArray itsInterfaces = new ObjArray();
+
+ private short itsFlags;
+ private short itsThisClassIndex;
+ private short itsSuperClassIndex;
+ private short itsSourceFileNameIndex;
+
+ private static final int MIN_LABEL_TABLE_SIZE = 32;
+ private int[] itsLabelTable;
+ private int itsLabelTableTop;
+
+// itsFixupTable[i] = (label_index << 32) | fixup_site
+ private static final int MIN_FIXUP_TABLE_SIZE = 40;
+ private long[] itsFixupTable;
+ private int itsFixupTableTop;
+ private ObjArray itsVarDescriptors;
+
+ private char[] tmpCharBuffer = new char[64];
+}
+
+final class ExceptionTableEntry
+{
+
+ ExceptionTableEntry(int startLabel, int endLabel,
+ int handlerLabel, short catchType)
+ {
+ itsStartLabel = startLabel;
+ itsEndLabel = endLabel;
+ itsHandlerLabel = handlerLabel;
+ itsCatchType = catchType;
+ }
+
+ int itsStartLabel;
+ int itsEndLabel;
+ int itsHandlerLabel;
+ short itsCatchType;
+}
+
+final class ClassFileField
+{
+
+ ClassFileField(short nameIndex, short typeIndex, short flags)
+ {
+ itsNameIndex = nameIndex;
+ itsTypeIndex = typeIndex;
+ itsFlags = flags;
+ itsHasAttributes = false;
+ }
+
+ void setAttributes(short attr1, short attr2, short attr3, int index)
+ {
+ itsHasAttributes = true;
+ itsAttr1 = attr1;
+ itsAttr2 = attr2;
+ itsAttr3 = attr3;
+ itsIndex = index;
+ }
+
+ int write(byte[] data, int offset)
+ {
+ offset = ClassFileWriter.putInt16(itsFlags, data, offset);
+ offset = ClassFileWriter.putInt16(itsNameIndex, data, offset);
+ offset = ClassFileWriter.putInt16(itsTypeIndex, data, offset);
+ if (!itsHasAttributes) {
+ // write 0 attributes
+ offset = ClassFileWriter.putInt16(0, data, offset);
+ } else {
+ offset = ClassFileWriter.putInt16(1, data, offset);
+ offset = ClassFileWriter.putInt16(itsAttr1, data, offset);
+ offset = ClassFileWriter.putInt16(itsAttr2, data, offset);
+ offset = ClassFileWriter.putInt16(itsAttr3, data, offset);
+ offset = ClassFileWriter.putInt16(itsIndex, data, offset);
+ }
+ return offset;
+ }
+
+ int getWriteSize()
+ {
+ int size = 2 * 3;
+ if (!itsHasAttributes) {
+ size += 2;
+ } else {
+ size += 2 + 2 * 4;
+ }
+ return size;
+ }
+
+ private short itsNameIndex;
+ private short itsTypeIndex;
+ private short itsFlags;
+ private boolean itsHasAttributes;
+ private short itsAttr1, itsAttr2, itsAttr3;
+ private int itsIndex;
+}
+
+final class ClassFileMethod
+{
+
+ ClassFileMethod(short nameIndex, short typeIndex, short flags)
+ {
+ itsNameIndex = nameIndex;
+ 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;
+ }
+
+ private short itsNameIndex;
+ private short itsTypeIndex;
+ private short itsFlags;
+ private byte[] itsCodeAttribute;
+
+}
+
+final class ConstantPool
+{
+
+ ConstantPool(ClassFileWriter cfw)
+ {
+ this.cfw = cfw;
+ itsTopIndex = 1; // the zero'th entry is reserved
+ itsPool = new byte[ConstantPoolSize];
+ itsTop = 0;
+ }
+
+ private static final int ConstantPoolSize = 256;
+ private static final byte
+ CONSTANT_Class = 7,
+ CONSTANT_Fieldref = 9,
+ CONSTANT_Methodref = 10,
+ CONSTANT_InterfaceMethodref = 11,
+ CONSTANT_String = 8,
+ CONSTANT_Integer = 3,
+ CONSTANT_Float = 4,
+ CONSTANT_Long = 5,
+ CONSTANT_Double = 6,
+ CONSTANT_NameAndType = 12,
+ CONSTANT_Utf8 = 1;
+
+ int write(byte[] data, int offset)
+ {
+ offset = ClassFileWriter.putInt16((short)itsTopIndex, data, offset);
+ System.arraycopy(itsPool, 0, data, offset, itsTop);
+ offset += itsTop;
+ return offset;
+ }
+
+ int getWriteSize()
+ {
+ return 2 + itsTop;
+ }
+
+ int addConstant(int k)
+ {
+ ensure(5);
+ itsPool[itsTop++] = CONSTANT_Integer;
+ itsTop = ClassFileWriter.putInt32(k, itsPool, itsTop);
+ return (short)(itsTopIndex++);
+ }
+
+ int addConstant(long k)
+ {
+ ensure(9);
+ itsPool[itsTop++] = CONSTANT_Long;
+ itsTop = ClassFileWriter.putInt64(k, itsPool, itsTop);
+ int index = itsTopIndex;
+ itsTopIndex += 2;
+ return index;
+ }
+
+ int addConstant(float k)
+ {
+ ensure(5);
+ itsPool[itsTop++] = CONSTANT_Float;
+ int bits = Float.floatToIntBits(k);
+ itsTop = ClassFileWriter.putInt32(bits, itsPool, itsTop);
+ 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;
+ 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);
+ }
+ return theIndex;
+ }
+
+ boolean isUnderUtfEncodingLimit(String s)
+ {
+ int strLen = s.length();
+ if (strLen * 3 <= MAX_UTF_ENCODING_SIZE) {
+ return true;
+ } else if (strLen > MAX_UTF_ENCODING_SIZE) {
+ return false;
+ }
+ return strLen == getUtfEncodingLimit(s, 0, strLen);
+ }
+
+ /**
+ * Get maximum i such that <tt>start <= i <= end</tt> and
+ * <tt>s.substring(start, i)</tt> 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");
+ }
+ }
+ 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);
+ return (short)(itsTopIndex++);
+ }
+
+ short addClass(String className)
+ {
+ int theIndex = itsClassHash.get(className, -1);
+ if (theIndex == -1) {
+ String slashed = className;
+ if (className.indexOf('.') > 0) {
+ slashed = ClassFileWriter.getSlashedForm(className);
+ theIndex = itsClassHash.get(slashed, -1);
+ if (theIndex != -1) {
+ itsClassHash.put(className, theIndex);
+ }
+ }
+ if (theIndex == -1) {
+ int utf8Index = addUtf8(slashed);
+ ensure(3);
+ itsPool[itsTop++] = CONSTANT_Class;
+ itsTop = ClassFileWriter.putInt16(utf8Index, itsPool, itsTop);
+ theIndex = itsTopIndex++;
+ itsClassHash.put(slashed, theIndex);
+ if (className != slashed) {
+ itsClassHash.put(className, theIndex);
+ }
+ }
+ }
+ 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);
+ }
+ 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);
+ }
+ 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);
+ return (short)(itsTopIndex++);
+ }
+
+ void ensure(int howMuch)
+ {
+ if (itsTop + howMuch > itsPool.length) {
+ int newCapacity = itsPool.length * 2;
+ if (itsTop + howMuch > newCapacity) {
+ newCapacity = itsTop + howMuch;
+ }
+ byte[] tmp = new byte[newCapacity];
+ System.arraycopy(itsPool, 0, tmp, 0, itsTop);
+ itsPool = tmp;
+ }
+ }
+
+ private ClassFileWriter cfw;
+
+ private static final int MAX_UTF_ENCODING_SIZE = 65535;
+
+ private UintMap itsStringConstHash = new UintMap();
+ private ObjToIntMap itsUtf8Hash = new ObjToIntMap();
+ private ObjToIntMap itsFieldRefHash = new ObjToIntMap();
+ private ObjToIntMap itsMethodRefHash = new ObjToIntMap();
+ private ObjToIntMap itsClassHash = new ObjToIntMap();
+
+ private int itsTop;
+ private int itsTopIndex;
+ private byte itsPool[];
+}
+
+final class FieldOrMethodRef
+{
+ FieldOrMethodRef(String className, String name, String type)
+ {
+ this.className = className;
+ this.name = name;
+ this.type = type;
+ }
+
+ 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);
+ }
+
+ 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/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Arguments.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Arguments.java
new file mode 100644
index 0000000..954b078
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Arguments.java
@@ -0,0 +1,311 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+/**
+ * This class implements the "arguments" object.
+ *
+ * See ECMA 10.1.8
+ *
+ * @see org.mozilla.javascript.NativeCall
+ * @author Norris Boyd
+ */
+final class Arguments extends IdScriptableObject
+{
+ static final long serialVersionUID = 4275508002492040609L;
+
+ public Arguments(NativeCall activation)
+ {
+ this.activation = activation;
+
+ Scriptable parent = activation.getParentScope();
+ setParentScope(parent);
+ setPrototype(ScriptableObject.getObjectPrototype(parent));
+
+ args = activation.originalArgs;
+ lengthObj = new Integer(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;
+ }
+ }
+
+ public String getClassName()
+ {
+ return "Object";
+ }
+
+ public boolean has(int index, Scriptable start)
+ {
+ if (0 <= index && index < args.length) {
+ if (args[index] != NOT_FOUND) {
+ return true;
+ }
+ }
+ return super.has(index, start);
+ }
+
+ public Object get(int index, Scriptable start)
+ {
+ if (0 <= index && index < args.length) {
+ Object value = args[index];
+ if (value != NOT_FOUND) {
+ if (sharedWithActivation(index)) {
+ NativeFunction f = activation.function;
+ String argName = f.getParamOrVarName(index);
+ value = activation.get(argName, activation);
+ if (value == NOT_FOUND) Kit.codeBug();
+ }
+ return value;
+ }
+ }
+ return super.get(index, start);
+ }
+
+ private boolean sharedWithActivation(int index)
+ {
+ 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;
+ }
+
+ public void put(int index, Scriptable start, Object value)
+ {
+ if (0 <= index && index < args.length) {
+ if (args[index] != NOT_FOUND) {
+ if (sharedWithActivation(index)) {
+ String argName;
+ argName = activation.function.getParamOrVarName(index);
+ activation.put(argName, activation, value);
+ return;
+ }
+ synchronized (this) {
+ if (args[index] != NOT_FOUND) {
+ if (args == activation.originalArgs) {
+ args = args.clone();
+ }
+ args[index] = value;
+ return;
+ }
+ }
+ }
+ }
+ super.put(index, start, value);
+ }
+
+ public void delete(int index)
+ {
+ if (0 <= index && index < args.length) {
+ synchronized (this) {
+ if (args[index] != NOT_FOUND) {
+ if (args == activation.originalArgs) {
+ args = args.clone();
+ }
+ args[index] = NOT_FOUND;
+ return;
+ }
+ }
+ }
+ super.delete(index);
+ }
+
+// #string_id_map#
+
+ private static final int
+ Id_callee = 1,
+ Id_length = 2,
+ Id_caller = 3,
+
+ MAX_INSTANCE_ID = 3;
+
+ protected int getMaxInstanceId()
+ {
+ return MAX_INSTANCE_ID;
+ }
+
+ protected int findInstanceIdInfo(String s)
+ {
+ int id;
+// #generated# Last update: 2007-05-09 08:15:04 EDT
+ L0: { id = 0; String X = null; int c;
+ if (s.length()==6) {
+ c=s.charAt(5);
+ if (c=='e') { X="callee";id=Id_callee; }
+ else if (c=='h') { X="length";id=Id_length; }
+ else if (c=='r') { X="caller";id=Id_caller; }
+ }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ break L0;
+ }
+// #/generated#
+
+ if (id == 0) return super.findInstanceIdInfo(s);
+
+ int attr;
+ switch (id) {
+ case Id_callee:
+ case Id_caller:
+ case Id_length:
+ attr = DONTENUM;
+ break;
+ default: throw new IllegalStateException();
+ }
+ return instanceIdInfo(attr, id);
+ }
+
+// #/string_id_map#
+
+ protected String getInstanceIdName(int id)
+ {
+ switch (id) {
+ case Id_callee: return "callee";
+ case Id_length: return "length";
+ case Id_caller: return "caller";
+ }
+ return null;
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ Object[] getIds(boolean getAll)
+ {
+ Object[] ids = super.getIds(getAll);
+ if (getAll && args.length != 0) {
+ boolean[] present = null;
+ 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 == null) {
+ present = new boolean[args.length];
+ }
+ if (!present[index]) {
+ present[index] = 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 == null || !present[i]) {
+ ids[offset] = new Integer(i);
+ ++offset;
+ }
+ }
+ if (offset != extraCount) Kit.codeBug();
+ }
+ }
+ return ids;
+ }
+
+// 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 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/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/BaseFunction.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/BaseFunction.java
new file mode 100644
index 0000000..d7d8992
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/BaseFunction.java
@@ -0,0 +1,553 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ * Roger Lawrence
+ * Mike McCabe
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+/**
+ * The base class for Function objects
+ * See ECMA 15.3.
+ * @author Norris Boyd
+ */
+public class BaseFunction extends IdScriptableObject implements Function
+{
+
+ static final long serialVersionUID = 5311394446546053859L;
+
+ private static final Object FUNCTION_TAG = new Object();
+
+ 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);
+ }
+
+ public BaseFunction()
+ {
+ }
+
+ public BaseFunction(Scriptable scope, Scriptable prototype)
+ {
+ super(scope, prototype);
+ }
+
+ public String getClassName() {
+ return "Function";
+ }
+
+ /**
+ * Implements the instanceof operator for JavaScript Function objects.
+ * <p>
+ * <code>
+ * foo = new Foo();<br>
+ * foo instanceof Foo; // true<br>
+ * </code>
+ *
+ * @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
+ *
+ */
+ public boolean hasInstance(Scriptable instance)
+ {
+ Object protoProp = ScriptableObject.getProperty(this, "prototype");
+ if (protoProp instanceof Scriptable) {
+ return ScriptRuntime.jsDelegatesTo(instance, (Scriptable)protoProp);
+ }
+ throw ScriptRuntime.typeError1("msg.instanceof.bad.prototype",
+ getFunctionName());
+ }
+
+// #string_id_map#
+
+ private static final int
+ Id_length = 1,
+ Id_arity = 2,
+ Id_name = 3,
+ Id_prototype = 4,
+ Id_arguments = 5,
+
+ MAX_INSTANCE_ID = 5;
+
+ protected int getMaxInstanceId()
+ {
+ return MAX_INSTANCE_ID;
+ }
+
+ protected int findInstanceIdInfo(String s)
+ {
+ int id;
+// #generated# Last update: 2007-05-09 08:15:15 EDT
+ L0: { id = 0; String X = null; int c;
+ L: switch (s.length()) {
+ case 4: X="name";id=Id_name; break L;
+ case 5: X="arity";id=Id_arity; break L;
+ case 6: X="length";id=Id_length; break L;
+ case 9: c=s.charAt(0);
+ if (c=='a') { X="arguments";id=Id_arguments; }
+ else if (c=='p') { X="prototype";id=Id_prototype; }
+ break L;
+ }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ break L0;
+ }
+// #/generated#
+// #/string_id_map#
+
+ if (id == 0) return super.findInstanceIdInfo(s);
+
+ int attr;
+ switch (id) {
+ case Id_length:
+ case Id_arity:
+ case Id_name:
+ attr = DONTENUM | READONLY | PERMANENT;
+ break;
+ case Id_prototype:
+ attr = prototypePropertyAttributes;
+ break;
+ case Id_arguments:
+ attr = DONTENUM | PERMANENT;
+ break;
+ default: throw new IllegalStateException();
+ }
+ return instanceIdInfo(attr, id);
+ }
+
+ 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);
+ }
+
+ protected Object getInstanceIdValue(int id)
+ {
+ switch (id) {
+ case Id_length: return ScriptRuntime.wrapInt(getLength());
+ case Id_arity: return ScriptRuntime.wrapInt(getArity());
+ case Id_name: return getFunctionName();
+ case Id_prototype: return getPrototypeProperty();
+ case Id_arguments: return getArguments();
+ }
+ return super.getInstanceIdValue(id);
+ }
+
+ protected void setInstanceIdValue(int id, Object value)
+ {
+ if (id == Id_prototype) {
+ if ((prototypePropertyAttributes & READONLY) == 0) {
+ prototypeProperty = (value != null)
+ ? value : UniqueTag.NULL_VALUE;
+ }
+ return;
+ } else if (id == Id_arguments) {
+ if (value == NOT_FOUND) {
+ // This should not be called since "arguments" is PERMANENT
+ Kit.codeBug();
+ }
+ defaultPut("arguments", value);
+ }
+ super.setInstanceIdValue(id, value);
+ }
+
+ 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);
+ }
+
+ 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_toSource: arity=1; s="toSource"; break;
+ case Id_apply: arity=2; s="apply"; break;
+ case Id_call: arity=1; s="call"; 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;
+ }
+
+ 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);
+ }
+ throw new IllegalArgumentException(String.valueOf(id));
+ }
+
+ private BaseFunction realFunction(Scriptable thisObj, IdFunctionObject f)
+ {
+ Object x = thisObj.getDefaultValue(ScriptRuntime.FunctionClass);
+ if (x instanceof BaseFunction) {
+ return (BaseFunction)x;
+ }
+ throw ScriptRuntime.typeError1("msg.incompat.call",
+ f.getFunctionName());
+ }
+
+ /**
+ * 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 getClassPrototype(this, "Object");
+ }
+
+ /**
+ * Should be overridden.
+ */
+ public Object call(Context cx, Scriptable scope, Scriptable thisObj,
+ Object[] args)
+ {
+ return Undefined.instance;
+ }
+
+ 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 implementaion of call as constructor, name="
+ +getFunctionName()+" in "+getClass().getName());
+ }
+ result = (Scriptable)val;
+ if (result.getPrototype() == null) {
+ result.setPrototype(getClassPrototype());
+ }
+ 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 <tt>thisObj</tt> argument when invoking
+ * {@link #call}.
+ * The methos is allowed to return <tt>null</tt> 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)
+ {
+ StringBuffer sb = new StringBuffer();
+ 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 "";
+ }
+
+ final Object getPrototypeProperty() {
+ Object result = prototypeProperty;
+ if (result == null) {
+ synchronized (this) {
+ result = prototypeProperty;
+ if (result == null) {
+ setupDefaultPrototype();
+ result = prototypeProperty;
+ }
+ }
+ }
+ else if (result == UniqueTag.NULL_VALUE) { result = null; }
+ return result;
+ }
+
+ private void setupDefaultPrototype()
+ {
+ NativeObject obj = new NativeObject();
+ final int attr = ScriptableObject.DONTENUM;
+ obj.defineProperty("constructor", this, attr);
+ // 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);
+ }
+ }
+
+ private Object getArguments()
+ {
+ // <Function name>.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 = defaultGet("arguments");
+ if (value != NOT_FOUND) {
+ // Should after changing <Function name>.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 static Object jsConstructor(Context cx, Scriptable scope,
+ Object[] args)
+ {
+ int arglen = args.length;
+ StringBuffer sourceBuf = new StringBuffer();
+
+ sourceBuf.append("function ");
+ /* 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('}');
+ String source = sourceBuf.toString();
+
+ int[] linep = new int[1];
+ String filename = Context.getSourcePositionFromStack(linep);
+ if (filename == null) {
+ filename = "<eval'ed string>";
+ 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);
+ }
+
+ protected int findPrototypeId(String s)
+ {
+ int id;
+// #string_id_map#
+// #generated# Last update: 2007-05-09 08:15:15 EDT
+ L0: { id = 0; String X = null; int c;
+ L: switch (s.length()) {
+ case 4: X="call";id=Id_call; break L;
+ case 5: X="apply";id=Id_apply; break L;
+ case 8: c=s.charAt(3);
+ if (c=='o') { X="toSource";id=Id_toSource; }
+ else if (c=='t') { X="toString";id=Id_toString; }
+ break L;
+ case 11: X="constructor";id=Id_constructor; break L;
+ }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ break L0;
+ }
+// #/generated#
+ return id;
+ }
+
+ private static final int
+ Id_constructor = 1,
+ Id_toString = 2,
+ Id_toSource = 3,
+ Id_apply = 4,
+ Id_call = 5,
+
+ MAX_PROTOTYPE_ID = 5;
+
+// #/string_id_map#
+
+ private Object prototypeProperty;
+ // For function object instances, attribute is PERMANENT; see ECMA 15.3.5.2
+ private int prototypePropertyAttributes = PERMANENT;
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Callable.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Callable.java
new file mode 100644
index 0000000..03e0fce
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Callable.java
@@ -0,0 +1,59 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov, igor@fastmail.fm
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+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 <code>this</code> 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/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ClassCache.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ClassCache.java
new file mode 100644
index 0000000..9047278
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ClassCache.java
@@ -0,0 +1,220 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov, igor@fastmail.fm
+ * Norris Boyd
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * 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
+{
+ private static final Object AKEY = new Object();
+ private volatile boolean cachingIsEnabled = true;
+ private HashMap<Class<?>,JavaMembers> classTable
+ = new HashMap<Class<?>,JavaMembers>();
+ private HashMap<Class<?>,JavaMembers> javaAdapterGeneratedClasses
+ = new HashMap<Class<?>,JavaMembers>();
+ private HashMap<JavaAdapter.JavaAdapterSignature,Class<?>> classAdapterCache
+ = new HashMap<JavaAdapter.JavaAdapterSignature,Class<?>>();
+ private HashMap<Class<?>,Object> interfaceAdapterCache;
+ private int generatedClassSerial;
+
+ /**
+ * 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)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Empty caches of generated Java classes and Java reflection information.
+ */
+ public synchronized void clearCaches()
+ {
+ classTable.clear();
+ javaAdapterGeneratedClasses.clear();
+ classAdapterCache.clear();
+ 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.
+ * <p>
+ * By default, the engine will cache the results of
+ * <tt>Class.getMethods()</tt> 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.
+ * <p>
+ * If caching is enabled and this method is called with a
+ * <code>false</code> argument, the caches will be emptied.
+ * <p>
+ * 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<Class<?>,JavaMembers> getClassCacheMap() {
+ return classTable;
+ }
+
+ Map<JavaAdapter.JavaAdapterSignature,Class<?>> getInterfaceAdapterCacheMap()
+ {
+ return classAdapterCache;
+ }
+
+ /**
+ * @deprecated
+ * The method always returns false.
+ * @see #setInvokerOptimizationEnabled(boolean enabled)
+ */
+ 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.
+ */
+ 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 HashMap<Class<?>,Object>();
+ }
+ interfaceAdapterCache.put(cl, iadapter);
+ }
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ClassShutter.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ClassShutter.java
new file mode 100644
index 0000000..d5f4cd6
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ClassShutter.java
@@ -0,0 +1,89 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// 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.
+ * <p>
+ * An embedding may filter which Java classes are exposed through
+ * LiveConnect to JavaScript scripts.
+ * <p>
+ * 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.
+ * <p>
+ * 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.
+ * <p>
+ * @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/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/CompilerEnvirons.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/CompilerEnvirons.java
new file mode 100644
index 0000000..645d098
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/CompilerEnvirons.java
@@ -0,0 +1,233 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov, igor@fastmail.fm
+ * Bob Jervis
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.util.Hashtable;
+
+public class CompilerEnvirons
+{
+ public CompilerEnvirons()
+ {
+ errorReporter = DefaultErrorReporter.instance;
+ languageVersion = Context.VERSION_DEFAULT;
+ generateDebugInfo = true;
+ useDynamicScope = false;
+ reservedKeywordAsIdentifier = false;
+ allowMemberExprAsFunctionName = false;
+ xmlAvailable = true;
+ optimizationLevel = 0;
+ generatingSource = true;
+ strictMode = false;
+ warningAsError = false;
+ generateObserverCount = false;
+ }
+
+ public void initFromContext(Context cx)
+ {
+ setErrorReporter(cx.getErrorReporter());
+ this.languageVersion = cx.getLanguageVersion();
+ useDynamicScope = cx.compileFunctionsWithDynamicScopeFlag;
+ 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 isUseDynamicScope()
+ {
+ return useDynamicScope;
+ }
+
+ public final boolean isReservedKeywordAsIdentifier()
+ {
+ return reservedKeywordAsIdentifier;
+ }
+
+ public void setReservedKeywordAsIdentifier(boolean flag)
+ {
+ reservedKeywordAsIdentifier = flag;
+ }
+
+ 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 final boolean isStrictMode()
+ {
+ return strictMode;
+ }
+
+ public final boolean reportWarningAsError()
+ {
+ return warningAsError;
+ }
+
+ /**
+ * Specify whether or not source information should be generated.
+ * <p>
+ * 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;
+ }
+
+ private ErrorReporter errorReporter;
+
+ private int languageVersion;
+ private boolean generateDebugInfo;
+ private boolean useDynamicScope;
+ private boolean reservedKeywordAsIdentifier;
+ private boolean allowMemberExprAsFunctionName;
+ private boolean xmlAvailable;
+ private int optimizationLevel;
+ private boolean generatingSource;
+ private boolean strictMode;
+ private boolean warningAsError;
+ private boolean generateObserverCount;
+ Hashtable activationNames;
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ConstProperties.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ConstProperties.java
new file mode 100644
index 0000000..860db79
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ConstProperties.java
@@ -0,0 +1,109 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Bob Jervis
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// API class
+
+package org.mozilla.javascript;
+
+public interface ConstProperties {
+ /**
+ * Sets a named const property in this object.
+ * <p>
+ * The property is specified by a string name
+ * as defined for <code>Scriptable.get</code>.
+ * <p>
+ * The possible values that may be passed in are as defined for
+ * <code>Scriptable.get</code>. A class that implements this method may choose
+ * to ignore calls to set certain properties, in which case those
+ * properties are effectively read-only.<p>
+ * For properties defined in a prototype chain,
+ * use <code>putProperty</code> in ScriptableObject. <p>
+ * Note that if a property <i>a</i> is defined in the prototype <i>p</i>
+ * of an object <i>o</i>, then evaluating <code>o.a = 23</code> will cause
+ * <code>set</code> to be called on the prototype <i>p</i> with
+ * <i>o</i> as the <i>start</i> parameter.
+ * To preserve JavaScript semantics, it is the Scriptable
+ * object's responsibility to modify <i>o</i>. <p>
+ * 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.<p>
+ * <p>
+ * The values that may be set are limited to the following:
+ * <UL>
+ * <LI>java.lang.Boolean objects</LI>
+ * <LI>java.lang.String objects</LI>
+ * <LI>java.lang.Number objects</LI>
+ * <LI>org.mozilla.javascript.Scriptable objects</LI>
+ * <LI>null</LI>
+ * <LI>The value returned by Context.getUndefinedValue()</LI>
+ * </UL><p>
+ * Arbitrary Java objects may be wrapped in a Scriptable by first calling
+ * <code>Context.toObject</code>. This allows the property of a JavaScript
+ * object to contain an arbitrary Java object as a value.<p>
+ * Note that <code>has</code> will be called by the runtime first before
+ * <code>set</code> 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/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Context.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Context.java
new file mode 100644
index 0000000..0833883
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Context.java
@@ -0,0 +1,2526 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Bob Jervis
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// API class
+
+package org.mozilla.javascript;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.CharArrayWriter;
+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.Hashtable;
+import java.util.Locale;
+
+import org.mozilla.javascript.debug.DebuggableScript;
+import org.mozilla.javascript.debug.Debugger;
+import org.mozilla.javascript.xml.XMLLib;
+
+/**
+ * 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.<p>
+ *
+ * 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.<p>
+ *
+ * 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
+{
+ /**
+ * Language versions.
+ *
+ * All integral values are reserved for future version numbers.
+ */
+
+ /**
+ * The unknown version.
+ */
+ 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;
+
+ /**
+ * Controls behaviour of <tt>Date.prototype.getYear()</tt>.
+ * If <tt>hasFeature(FEATURE_NON_ECMA_GET_YEAR)</tt> 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 <tt>hasFeature(FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME)</tt> returns
+ * true, allow <tt>function memberExpression(args) { body }</tt> to be
+ * syntax sugar for <tt>memberExpression = function(args) { body }</tt>,
+ * 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 <tt>hasFeature(RESERVED_KEYWORD_AS_IDENTIFIER)</tt> 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 <tt>toString()</tt> should returns the same result
+ * as <tt>toSource()</tt> when applied to objects and arrays.
+ * If <tt>hasFeature(FEATURE_TO_STRING_AS_SOURCE)</tt> returns true,
+ * calling <tt>toString()</tt> on JS objects gives the same result as
+ * calling <tt>toSource()</tt>. That is it returns JS source with code
+ * to create an object with all enumeratable fields of the original object
+ * instead of printing <tt>[object <i>result of
+ * {@link Scriptable#getClassName()}</i>]</tt>.
+ * <p>
+ * 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 <tt>__proto__</tt> and <tt>__parent__</tt>
+ * are treated specially.
+ * If <tt>hasFeature(FEATURE_PARENT_PROTO_PROPERTIES)</tt> returns true,
+ * treat <tt>__parent__</tt> and <tt>__proto__</tt> as special properties.
+ * <p>
+ * 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 <tt>x.__proto__ = y</tt> changes the prototype
+ * chain of the object <tt>x</tt> to point to <tt>y</tt>,
+ * <tt>x["__proto__"] = y</tt> simply assigns a new value to the property
+ * <tt>__proto__</tt> in <tt>x</tt> 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.
+ */
+ 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.
+ * <p>
+ * 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.
+ * <p>
+ * This is useful to define shared scope containing functions that can
+ * be called from scripts and functions using private scopes.
+ * <p>
+ * 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 creates new variable in the global scope as
+ * required by ECMA 262.
+ * <p>
+ * 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.
+ * <p>
+ * 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 behaviour 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.
+ * <p>
+ * 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.
+ * <p>
+ * 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.
+ * <p>
+ * Note that this feature should only be enabled for trusted scripts.
+ * <p>
+ * By default {@link #hasFeature(int)} returns false.
+ * @since 1.7 Release 1
+ */
+ public static final int FEATURE_ENHANCED_JAVA_ACCESS = 13;
+
+
+ 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;
+
+ /**
+ * Create a new Context.
+ *
+ * Note that the Context must be associated with a thread before
+ * it can be used to execute a script.
+ * @deprecated use {@link ContextFactory#enter()} or
+ * {@link ContextFactory#call(ContextAction)} instead.
+ */
+ public Context()
+ {
+ this(ContextFactory.getGlobal());
+ }
+
+ Context(ContextFactory factory)
+ {
+ assert factory != null;
+ this.factory = factory;
+ setLanguageVersion(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. <p>
+ *
+ * @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.
+ * @deprecated use {@link ContextFactory#enter()} or
+ * {@link ContextFactory#call(ContextAction)} instead as this method relies
+ * on usage of a static singleton "global" ContextFactory.
+ * @return a Context associated with the current thread
+ * @see #getCurrentContext()
+ * @see #exit()
+ * @see #call(ContextAction)
+ */
+ public static Context enter()
+ {
+ return enter(null);
+ }
+
+ /**
+ * Get a Context associated with the current thread, using
+ * the given Context if need be.
+ * <p>
+ * The same as <code>enter()</code> except that <code>cx</code>
+ * is associated with the current thread and returned if
+ * the current thread has no associated context and <code>cx</code>
+ * 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)
+ */
+ 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 <code>exit()</code> 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);
+ }
+ }
+
+ /**
+ * Call {@link ContextAction#run(Context cx)}
+ * using the Context instance associated with the current thread.
+ * If no Context is associated with the thread, then
+ * <tt>ContextFactory.getGlobal().makeContext()</tt> 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)}.
+ */
+ public static Object 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)}.
+ * <p>
+ * It is allowed but not advisable to use null for <tt>factory</tt>
+ * 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, new ContextAction() {
+ public Object run(Context cx) {
+ return callable.call(cx, scope, thisObj, args);
+ }
+ });
+ }
+
+ /**
+ * The method implements {@links ContextFactory#call(ContextAction)} logic.
+ */
+ static Object call(ContextFactory factory, ContextAction action) {
+ Context cx = enter(null, factory);
+ try {
+ return action.run(cx);
+ }
+ finally {
+ exit();
+ }
+ }
+
+ /**
+ * @deprecated
+ * @see ContextFactory#addListener(ContextFactory.Listener)
+ * @see ContextFactory#getGlobal()
+ */
+ 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) {
+ RuntimeException rex = new RuntimeException();
+ Kit.initCause(rex, ex);
+ throw rex;
+ }
+ return;
+ }
+
+ ContextFactory.getGlobal().addListener(listener);
+ }
+
+ /**
+ * @deprecated
+ * @see ContextFactory#removeListener(ContextFactory.Listener)
+ * @see ContextFactory#getGlobal()
+ */
+ 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.
+ * <p>
+ * If <tt>sealKey</tt> is not null, calling
+ * {@link #unseal(Object sealKey)} with the same key unseals
+ * the object. If <tt>sealKey</tt> 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 <tt>sealKey</tt> argument should not be null and should match
+ * <tt>sealKey</tt> 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.
+ * <p>
+ * 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.
+ *
+ * <p>
+ * 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,
+ new Integer(this.version),
+ new Integer(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:
+ 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.
+ *
+ * <p>
+ * The implementation version is of the form
+ * <pre>
+ * "<i>name langVer</i> <code>release</code> <i>relNum date</i>"
+ * </pre>
+ * where <i>name</i> is the name of the product, <i>langVer</i> is
+ * the language version, <i>relNum</i> is the release number, and
+ * <i>date</i> 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()
+ {
+ // XXX Probably it would be better to embed this directly into source
+ // with special build preprocessing but that would require some ant
+ // tweaking and then replacing token in resource files was simpler
+ if (implementationVersion == null) {
+ implementationVersion
+ = ScriptRuntime.getMessage0("implementation.version");
+ }
+ return implementationVersion;
+ }
+
+ /**
+ * 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);
+ } else {
+ throw new EvaluatorException(message, sourceName, lineno,
+ lineSource, lineOffset);
+ }
+ }
+
+ static EvaluatorException reportRuntimeError0(String messageId)
+ {
+ String msg = ScriptRuntime.getMessage0(messageId);
+ return reportRuntimeError(msg);
+ }
+
+ static EvaluatorException reportRuntimeError1(String messageId,
+ Object arg1)
+ {
+ String msg = ScriptRuntime.getMessage1(messageId, arg1);
+ return reportRuntimeError(msg);
+ }
+
+ static EvaluatorException reportRuntimeError2(String messageId,
+ Object arg1, Object arg2)
+ {
+ String msg = ScriptRuntime.getMessage2(messageId, arg1, arg2);
+ return reportRuntimeError(msg);
+ }
+
+ static EvaluatorException reportRuntimeError3(String messageId,
+ Object arg1, Object arg2,
+ Object arg3)
+ {
+ String msg = ScriptRuntime.getMessage3(messageId, arg1, arg2, arg3);
+ return reportRuntimeError(msg);
+ }
+
+ static EvaluatorException reportRuntimeError4(String messageId,
+ Object arg1, Object arg2,
+ Object arg3, Object arg4)
+ {
+ String msg
+ = ScriptRuntime.getMessage4(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.<p>
+ *
+ * This method must be called to initialize a scope before scripts
+ * can be evaluated in that scope.<p>
+ *
+ * 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.
+ *
+ * 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.<p>
+ *
+ * This method must be called to initialize a scope before scripts
+ * can be evaluated in that scope.<p>
+ *
+ * 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.
+ *
+ * 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.<p>
+ *
+ * This method must be called to initialize a scope before scripts
+ * can be evaluated in that scope.<p>
+ *
+ * This method does not affect the Context it is called upon.<p>
+ *
+ * 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);
+ }
+
+ /**
+ * 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);
+ } else {
+ 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);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Check whether a string is ready to be compiled.
+ * <p>
+ * 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.
+ * <p>
+ * 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);
+ /*APPJET*/
+ Parser p = InformativeParser.makeParser(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.
+ if (errorseen && p.eof())
+ return false;
+ else
+ return true;
+ }
+
+ /**
+ * @deprecated
+ * @see #compileReader(Reader in, String sourceName, int lineno,
+ * Object securityDomain)
+ */
+ 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.
+ * <p>
+ * 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, in, null, sourceName, lineno,
+ securityDomain, false, null, null);
+ }
+
+ /**
+ * Compiles the source in the given string.
+ * <p>
+ * 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
+ * @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, null, source, sourceName, lineno,
+ securityDomain, false,
+ compiler, compilationErrorReporter);
+ } catch (IOException ex) {
+ // Should not happen when dealing with source as string
+ throw new RuntimeException();
+ }
+ }
+
+ /**
+ * Compile a JavaScript function.
+ * <p>
+ * 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, null, 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();
+ }
+ }
+
+ /**
+ * Decompile the script.
+ * <p>
+ * 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.
+ * <p>
+ * Decompiles a previously compiled JavaScript function object to
+ * canonical source.
+ * <p>
+ * 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);
+ else
+ return "function " + fun.getClassName() +
+ "() {\n\t[native code]\n}\n";
+ }
+
+ /**
+ * Decompile the body of a JavaScript Function.
+ * <p>
+ * 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 final Scriptable newObject(Scriptable scope)
+ {
+ return newObject(scope, "Object", ScriptRuntime.emptyArgs);
+ }
+
+ /**
+ * Create a new JavaScript object by executing the named constructor.
+ *
+ * The call <code>newObject(scope, "Foo")</code> 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 final Scriptable newObject(Scriptable scope, String constructorName)
+ {
+ return newObject(scope, constructorName, ScriptRuntime.emptyArgs);
+ }
+
+ /**
+ * Creates a new JavaScript object by executing the named constructor.
+ *
+ * Searches <code>scope</code> for the named constructor, calls it with
+ * the given arguments, and returns the result.<p>
+ *
+ * The code
+ * <pre>
+ * Object[] args = { "a", "b" };
+ * newObject(scope, "Foo", args)</pre>
+ * is equivalent to evaluating "new Foo('a', 'b')", assuming that the Foo
+ * constructor has been defined in <code>scope</code>.
+ *
+ * @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 final Scriptable newObject(Scriptable scope, String constructorName,
+ Object[] args)
+ {
+ scope = ScriptableObject.getTopLevelScope(scope);
+ Function ctor = ScriptRuntime.getExistingCtor(this, scope,
+ constructorName);
+ if (args == null) { args = ScriptRuntime.emptyArgs; }
+ return ctor.construct(this, scope, args);
+ }
+
+ /**
+ * Create an array with a specified initial length.
+ * <p>
+ * @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 final Scriptable newArray(Scriptable scope, int length)
+ {
+ NativeArray result = new NativeArray(length);
+ ScriptRuntime.setObjectProtoAndParent(result, scope);
+ 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 final Scriptable newArray(Scriptable scope, Object[] elements)
+ {
+ if (elements.getClass().getComponentType() != ScriptRuntime.ObjectClass)
+ throw new IllegalArgumentException();
+ NativeArray result = new NativeArray(elements);
+ ScriptRuntime.setObjectProtoAndParent(result, scope);
+ return result;
+ }
+
+ /**
+ * Get the elements of a JavaScript array.
+ * <p>
+ * 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.
+ * <p>
+ * 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.
+ * <p>
+ * Returns a Java double for the JavaScript Number.
+ * <p>
+ * 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.
+ * <p>
+ * See ECMA 9.8.
+ * <p>
+ * @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.
+ * <p>
+ * Note that a scope must be provided to look up the constructors
+ * for Number, Boolean, and String.
+ * <p>
+ * See ECMA 9.9.
+ * <p>
+ * 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)
+ */
+ 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.
+ * <p>
+ * 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.
+ * <p>
+ * Note that for Number instances during any arithmetic operation in
+ * JavaScript the engine will always use the result of
+ * <tt>Number.doubleValue()</tt> resulting in a precision loss if
+ * the number can not fit into double.
+ * <p>
+ * If value is an instance of Character, it will be converted to string of
+ * length 1 and its JavaScript type will be string.
+ * <p>
+ * 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:
+ * <pre>
+ * Context cx = Context.getCurrentContext();
+ * return cx.getWrapFactory().wrap(cx, scope, value, null);
+ * </pre>
+ *
+ * @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)
+ {
+ 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 {
+ Context 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.
+ */
+ public static Object toType(Object value, Class desiredType)
+ throws IllegalArgumentException
+ {
+ try {
+ return jsToJava(value, desiredType);
+ } catch (EvaluatorException ex) {
+ IllegalArgumentException
+ ex2 = new IllegalArgumentException(ex.getMessage());
+ Kit.initCause(ex2, ex);
+ throw ex2;
+ }
+ }
+
+ /**
+ * 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.
+ * <p>
+ * This method always throws an exception, its return value is provided
+ * only for convenience to allow a usage like:
+ * <pre>
+ * throw Context.throwAsScriptRuntimeEx(ex);
+ * </pre>
+ * 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.
+ * <p>
+ * 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.
+ * <p>
+ * 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.
+ * <p>
+ * 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.
+ * <p>
+ * 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.
+ * <p> SecurityController may only be set if it is currently null
+ * and {@link SecurityController#hasGlobal()} is <tt>false</tt>.
+ * 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.
+ * <p> {@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 void setClassShutter(ClassShutter shutter)
+ {
+ if (sealed) onSealedMutation();
+ if (shutter == null) throw new IllegalArgumentException();
+ if (classShutter != null) {
+ throw new SecurityException("Cannot overwrite existing " +
+ "ClassShutter object");
+ }
+ classShutter = shutter;
+ }
+
+ final ClassShutter getClassShutter()
+ {
+ return classShutter;
+ }
+
+ /**
+ * Get a value corresponding to a key.
+ * <p>
+ * Since the Context is associated with a thread it can be
+ * used to maintain values that can be later retrieved using
+ * the current thread.
+ * <p>
+ * 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 (hashtable == null)
+ return null;
+ return hashtable.get(key);
+ }
+
+ /**
+ * Put a value that can later be retrieved using a given key.
+ * <p>
+ * @param key the key used to index the value
+ * @param value the value to save
+ */
+ public final void putThreadLocal(Object key, Object value)
+ {
+ if (sealed) onSealedMutation();
+ if (hashtable == null)
+ hashtable = new Hashtable();
+ hashtable.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 (hashtable == null)
+ return;
+ hashtable.remove(key);
+ }
+
+ /**
+ * @deprecated
+ * @see #FEATURE_DYNAMIC_SCOPE
+ * @see #hasFeature(int)
+ */
+ public final boolean hasCompileFunctionsWithDynamicScope()
+ {
+ return compileFunctionsWithDynamicScopeFlag;
+ }
+
+ /**
+ * @deprecated
+ * @see #FEATURE_DYNAMIC_SCOPE
+ * @see #hasFeature(int)
+ */
+ public final void setCompileFunctionsWithDynamicScope(boolean flag)
+ {
+ if (sealed) onSealedMutation();
+ compileFunctionsWithDynamicScopeFlag = flag;
+ }
+
+ /**
+ * @deprecated
+ * @see ClassCache#get(Scriptable)
+ * @see ClassCache#setCachingEnabled(boolean)
+ */
+ public static void setCachingEnabled(boolean cachingEnabled)
+ {
+ }
+
+ /**
+ * Set a WrapFactory for this Context.
+ * <p>
+ * 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.
+ * <p>
+ * 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 <code>featureIndex</code> 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 <code>Context</code>. Note
+ that the XMLLib.Factory interface should be considered experimental.
+
+ The default implementation uses the implementation provided by this
+ <code>Context</code>'s {@link ContextFactory}.
+
+ @return An XMLLib.Factory. Should not return <code>null</code> 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
+ * <code>observeInstructionCount()</code>.
+ * When the threshold is zero, instruction counting is disabled,
+ * otherwise each time the run-time executes at least the threshold value
+ * of script instructions, <code>observeInstructionCount()</code> will
+ * be called.
+ */
+ public final int getInstructionObserverThreshold()
+ {
+ return instructionThreshold;
+ }
+
+ /**
+ * Set threshold of executed instructions counter that triggers call to
+ * <code>observeInstructionCount()</code>.
+ * When the threshold is zero, instruction counting is disabled,
+ * otherwise each time the run-time executes at least the threshold value
+ * of script instructions, <code>observeInstructionCount()</code> will
+ * be called.<p/>
+ * 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
+ * <code>threshold</code> 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 <code>setInstructionObserverThreshold()</code>.
+ * The method is useful to observe long running scripts and if necessary
+ * to terminate them.
+ * <p>
+ * The instruction counting support is available only for interpreted
+ * scripts generated when the optimization level is set to -1.
+ * <p>
+ * 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 <code>observeInstructionCount</code>
+ * @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
+ = VMBridge.instance.getCurrentThreadClassLoader();
+ 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
+ return threadLoader;
+ }
+ // 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();
+ }
+ }
+ applicationClassLoader = loader;
+ }
+ 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;
+ }
+
+ /********** 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;
+ }
+
+ private Object compileImpl(Scriptable scope,
+ Reader sourceReader, 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");
+ }
+
+ // One of sourceReader or sourceString has to be null
+ if (!(sourceReader == null ^ sourceString == null)) Kit.codeBug();
+ // 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();
+ }
+
+ if (debugger != null) {
+ if (sourceReader != null) {
+ sourceString = Kit.readReader(sourceReader);
+ sourceReader = null;
+ }
+ }
+
+ /*APPJET*/
+ Parser p = InformativeParser.makeParser(compilerEnv,
+ compilationErrorReporter);
+ if (returnFunction) {
+ p.calledByCompileFunction = true;
+ }
+ ScriptOrFnNode tree;
+ if (sourceString != null) {
+ tree = p.parse(sourceString, sourceName, lineno);
+ } else {
+ tree = p.parse(sourceReader, sourceName, lineno);
+ }
+ if (returnFunction) {
+ if (!(tree.getFunctionCount() == 1
+ && tree.getFirstChild() != null
+ && tree.getFirstChild().getType() == Token.FUNCTION))
+ {
+ // XXX: the check just look 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);
+ }
+ }
+
+ if (compiler == null) {
+ compiler = createCompiler();
+ }
+
+ String encodedSource = p.getEncodedSource();
+
+ Object bytecode = compiler.compile(compilerEnv,
+ tree, encodedSource,
+ 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 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.
+ */
+ CharArrayWriter writer = new CharArrayWriter();
+ RuntimeException re = new RuntimeException();
+ re.printStackTrace(new PrintWriter(writer));
+ String s = writer.toString();
+ int open = -1;
+ int close = -1;
+ int colon = -1;
+ for (int i=0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if (c == ':')
+ colon = i;
+ else if (c == '(')
+ open = i;
+ else if (c == ')')
+ close = i;
+ else if (c == '\n' && open != -1 && close != -1 && colon != -1 &&
+ open < colon && colon < close)
+ {
+ String fileStr = s.substring(open + 1, colon);
+ if (!fileStr.endsWith(".java")) {
+ String lineStr = s.substring(colon + 1, close);
+ try {
+ linep[0] = Integer.parseInt(lineStr);
+ if (linep[0] < 0) {
+ linep[0] = 0;
+ }
+ return fileStr;
+ }
+ catch (NumberFormatException e) {
+ // fall through
+ }
+ }
+ open = close = colon = -1;
+ }
+ }
+
+ 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 Hashtable(5);
+ activationNames.put(name, 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.containsKey(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);
+ }
+
+ private static String implementationVersion;
+
+ private final ContextFactory factory;
+ private boolean sealed;
+ private Object sealKey;
+
+ Scriptable topCallScope;
+ NativeCall currentActivationCall;
+ XMLLib cachedXMLLib;
+
+ // 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 ClassShutter classShutter;
+ private ErrorReporter errorReporter;
+ RegExpProxy regExpProxy;
+ private Locale locale;
+ private boolean generatingDebug;
+ private boolean generatingDebugChanged;
+ private boolean generatingSource=true;
+ boolean compileFunctionsWithDynamicScopeFlag;
+ boolean useDynamicScope;
+ private int optimizationLevel;
+ private int maximumInterpreterStackDepth;
+ private WrapFactory wrapFactory;
+ Debugger debugger;
+ private Object debuggerData;
+ private int enterCount;
+ private Object propertyListeners;
+ private Hashtable hashtable;
+ private ClassLoader applicationClassLoader;
+
+ /**
+ * This is the list of names of objects forcing the creation of
+ * function activation records.
+ */
+ Hashtable 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 index-like result from function
+ int scratchIndex;
+
+ // 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;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextAction.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextAction.java
new file mode 100644
index 0000000..1c584a9
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextAction.java
@@ -0,0 +1,59 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov, igor@fastmail.fm
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// 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.
+ */
+public interface ContextAction
+{
+ /**
+ * Execute action using the supplied Context instance.
+ * When Rhino runtime calls the method, <tt>cx</tt> will be associated
+ * with the current thread as active context.
+ *
+ * @see Context#call(ContextAction)
+ * @see ContextFactory#call(ContextAction)
+ */
+ public Object run(Context cx);
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextFactory.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextFactory.java
new file mode 100644
index 0000000..4f9fde2
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextFactory.java
@@ -0,0 +1,594 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov, igor@fastmail.fm
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// API class
+
+package org.mozilla.javascript;
+
+/**
+ * Factory class that Rhino runtime uses to create new {@link Context}
+ * instances. A <code>ContextFactory</code> can also notify listeners
+ * about context creation and release.
+ * <p>
+ * 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)}.
+ * <p>
+ * 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)}.
+ * <p>
+ * 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.
+ * <pre>
+ * 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();
+ * // Use pure interpreter mode to allow for
+ * // {@link #observeInstructionCount(Context, int)} to work
+ * cx.setOptimizationLevel(-1);
+ * // 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);
+ * }
+ *
+ * }
+ *
+ * </pre>
+ */
+
+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 synchronized static void initGlobal(ContextFactory factory)
+ {
+ if (factory == null) {
+ throw new IllegalArgumentException();
+ }
+ if (hasCustomGlobal) {
+ throw new IllegalStateException();
+ }
+ hasCustomGlobal = true;
+ global = factory;
+ }
+
+ /**
+ * 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. <tt>makeContext()</tt> 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 false;
+
+ 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;
+ }
+ // It is a bug to call the method with unknown featureIndex
+ throw new IllegalArgumentException(String.valueOf(featureIndex));
+ }
+
+ private 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 <code>Context</code> 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"
+ );
+ } else if (Kit.classOrNull("org.apache.xmlbeans.XmlCursor") != null) {
+ return org.mozilla.javascript.xml.XMLLib.Factory.create(
+ "org.mozilla.javascript.xml.impl.xmlbeans.XMLLibImpl"
+ );
+ } else {
+ 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(ClassLoader parent)
+ {
+ 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)
+ {
+ return callable.call(cx, scope, thisObj, args);
+ }
+
+ /**
+ * 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 Object 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.
+ * <p>
+ * Calling <code>enterContext()</code> 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
+ * <code>enterContext()</code> must have a matching call to
+ * {@link Context#exit()}.
+ * <pre>
+ * Context cx = contextFactory.enterContext();
+ * try {
+ * ...
+ * cx.evaluateString(...);
+ * } finally {
+ * Context.exit();
+ * }
+ * </pre>
+ * Instead of using <tt>enterContext()</tt>, <tt>exit()</tt> pair consider
+ * using {@link #call(ContextAction)} which guarantees proper association
+ * of Context instances with the current thread.
+ * With this method the above example becomes:
+ * <pre>
+ * ContextFactory.call(new ContextAction() {
+ * public Object run(Context cx) {
+ * ...
+ * cx.evaluateString(...);
+ * return null;
+ * }
+ * });
+ * </pre>
+ * @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
+ */
+ public final Context enter()
+ {
+ return enterContext(null);
+ }
+
+ /**
+ * @deprecated Use {@link Context#exit()} instead.
+ */
+ public final void exit()
+ {
+ Context.exit();
+ }
+
+ /**
+ * Get a Context associated with the current thread, using the given
+ * Context if need be.
+ * <p>
+ * The same as <code>enterContext()</code> except that <code>cx</code>
+ * is associated with the current thread and returned if the current thread
+ * has no associated context and <code>cx</code> 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 <code>cx</code> is already associated
+ * with a different thread
+ */
+ public final Context enterContext(Context cx)
+ {
+ return Context.enter(cx, this);
+ }
+} \ No newline at end of file
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextListener.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextListener.java
new file mode 100644
index 0000000..5e17145
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextListener.java
@@ -0,0 +1,60 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// API class
+
+package org.mozilla.javascript;
+
+/**
+ * @deprecated Embeddings that wish to customize newly created
+ * {@link Context} instances should implement
+ * {@link ContextFactory.Listener}.
+ */
+public interface ContextListener extends ContextFactory.Listener
+{
+
+ /**
+ * @deprecated Rhino runtime never calls the method.
+ */
+ public void contextEntered(Context cx);
+
+ /**
+ * @deprecated Rhino runtime never calls the method.
+ */
+ public void contextExited(Context cx);
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DToA.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DToA.java
new file mode 100644
index 0000000..ad2a68a
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DToA.java
@@ -0,0 +1,1271 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Waldemar Horwat
+ * Roger Lawrence
+ * Attila Szegedi
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/****************************************************************
+ *
+ * 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 {
+
+
+/* "-0.0000...(1073 zeros after decimal point)...0001\0" is the longest string that we could produce,
+ * which occurs when printing -5e-324 in binary. We could compute a better estimate of the size of
+ * the output string and malloc fewer bytes depending on d and base, but why bother? */
+
+ private static final int DTOBASESTR_BUFFER_SIZE = 1078;
+
+ 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 <precision> digits after the decimal point; exponential if number is large */
+ DTOSTR_EXPONENTIAL = 3, /* Always exponential format; <precision> significant digits */
+ DTOSTR_PRECISION = 4; /* Either fixed or exponential format; <precision> 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;
+ } else {
+ /* We have a fraction. */
+
+ char[] buffer; /* The output string */
+ int p; /* index to current position in the buffer */
+ int digit;
+ double df; /* The fractional part of d */
+ BigInteger b;
+
+ buffer = new char[DTOBASESTR_BUFFER_SIZE];
+ p = 0;
+ 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<<Log2P);
+ }
+
+ b = b.shiftLeft(e[0] + s2);
+ BigInteger s = BigInteger.valueOf(1);
+ s = s.shiftLeft(s2);
+ /* At this point we have the following:
+ * s = 2^s2;
+ * 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[p++] = BASEDIGIT(digit);
+ } while (!done);
+
+ StringBuffer sb = new StringBuffer(intDigits.length() + 1 + p);
+ sb.append(intDigits);
+ sb.append('.');
+ sb.append(buffer, 0, p);
+ return sb.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(StringBuffer 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, StringBuffer 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) ? word0(d) << (64 - i) | word1(d) >>> (i - 32) : 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;
+ /* no break */
+ case 4:
+ if (ndigits <= 0)
+ ndigits = 1;
+ ilim = ilim1 = i = ndigits;
+ break;
+ case 3:
+ leftright = false;
+ /* no break */
+ 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(StringBuffer 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(StringBuffer 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/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Decompiler.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Decompiler.java
new file mode 100644
index 0000000..8547d37
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Decompiler.java
@@ -0,0 +1,918 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Mike Ang
+ * Igor Bukanov
+ * Bob Jervis
+ * Mike McCabe
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+/**
+ * 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();
+ 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 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);
+ }
+ }
+ }
+
+ 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();
+
+ StringBuffer result = new StringBuffer();
+ 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:
+ result.append(source.charAt(i) == Token.GET ? "get " : "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.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.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.COLONCOLON:
+ result.append("::");
+ break;
+
+ case Token.DOTDOT:
+ result.append("..");
+ break;
+
+ case Token.DOTQUERY:
+ result.append(".(");
+ break;
+
+ case Token.XMLATTR:
+ result.append('@');
+ break;
+
+ 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,
+ StringBuffer 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,
+ StringBuffer 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;
+ }
+
+ 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/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DefaultErrorReporter.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DefaultErrorReporter.java
new file mode 100644
index 0000000..c7d93d4
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DefaultErrorReporter.java
@@ -0,0 +1,113 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+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;
+ }
+
+ 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
+ }
+ }
+
+ 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);
+ }
+ }
+
+ public EvaluatorException runtimeError(String message, String sourceURI,
+ int line, String lineText,
+ int lineOffset)
+ {
+ if (chainedReporter != null) {
+ return chainedReporter.runtimeError(
+ message, sourceURI, line, lineText, lineOffset);
+ } else {
+ return new EvaluatorException(
+ message, sourceURI, line, lineText, lineOffset);
+ }
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DefiningClassLoader.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DefiningClassLoader.java
new file mode 100644
index 0000000..5864b5d
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DefiningClassLoader.java
@@ -0,0 +1,88 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Roger Lawrence
+ * Patrick Beard
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+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;
+ }
+
+ 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()));
+ }
+
+ public void linkClass(Class cl) {
+ resolveClass(cl);
+ }
+
+ 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/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Delegator.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Delegator.java
new file mode 100644
index 0000000..e044863
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Delegator.java
@@ -0,0 +1,266 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Delegator.java, released
+ * Sep 27, 2000.
+ *
+ * The Initial Developer of the Original Code is
+ * Matthias Radestock. <matthias@sorted.org>.
+ * Portions created by the Initial Developer are Copyright (C) 2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// 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 {
+
+ 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
+ */
+ public String getClassName() {
+ return obj.getClassName();
+ }
+ /**
+ * @see org.mozilla.javascript.Scriptable#get(String, Scriptable)
+ */
+ public Object get(String name, Scriptable start) {
+ return obj.get(name,start);
+ }
+ /**
+ * @see org.mozilla.javascript.Scriptable#get(int, Scriptable)
+ */
+ public Object get(int index, Scriptable start) {
+ return obj.get(index,start);
+ }
+ /**
+ * @see org.mozilla.javascript.Scriptable#has(String, Scriptable)
+ */
+ public boolean has(String name, Scriptable start) {
+ return obj.has(name,start);
+ }
+ /**
+ * @see org.mozilla.javascript.Scriptable#has(int, Scriptable)
+ */
+ public boolean has(int index, Scriptable start) {
+ return obj.has(index,start);
+ }
+ /**
+ * @see org.mozilla.javascript.Scriptable#put(String, Scriptable, Object)
+ */
+ public void put(String name, Scriptable start, Object value) {
+ obj.put(name,start,value);
+ }
+ /**
+ * @see org.mozilla.javascript.Scriptable#put(int, Scriptable, Object)
+ */
+ public void put(int index, Scriptable start, Object value) {
+ obj.put(index,start,value);
+ }
+ /**
+ * @see org.mozilla.javascript.Scriptable#delete(String)
+ */
+ public void delete(String name) {
+ obj.delete(name);
+ }
+ /**
+ * @see org.mozilla.javascript.Scriptable#delete(int)
+ */
+ public void delete(int index) {
+ obj.delete(index);
+ }
+ /**
+ * @see org.mozilla.javascript.Scriptable#getPrototype
+ */
+ public Scriptable getPrototype() {
+ return obj.getPrototype();
+ }
+ /**
+ * @see org.mozilla.javascript.Scriptable#setPrototype
+ */
+ public void setPrototype(Scriptable prototype) {
+ obj.setPrototype(prototype);
+ }
+ /**
+ * @see org.mozilla.javascript.Scriptable#getParentScope
+ */
+ public Scriptable getParentScope() {
+ return obj.getParentScope();
+ }
+ /**
+ * @see org.mozilla.javascript.Scriptable#setParentScope
+ */
+ public void setParentScope(Scriptable parent) {
+ obj.setParentScope(parent);
+ }
+ /**
+ * @see org.mozilla.javascript.Scriptable#getIds
+ */
+ public Object[] getIds() {
+ return obj.getIds();
+ }
+ /**
+ * Note that this method does not get forwarded to the delegee if
+ * the <code>hint</code> parameter is null,
+ * <code>ScriptRuntime.ScriptableClass</code> or
+ * <code>ScriptRuntime.FunctionClass</code>. Instead the object
+ * itself is returned.
+ *
+ * @param hint the type hint
+ * @return the default value
+ *
+ * @see org.mozilla.javascript.Scriptable#getDefaultValue
+ */
+ public Object getDefaultValue(Class hint) {
+ return (hint == null ||
+ hint == ScriptRuntime.ScriptableClass ||
+ hint == ScriptRuntime.FunctionClass) ?
+ this : obj.getDefaultValue(hint);
+ }
+ /**
+ * @see org.mozilla.javascript.Scriptable#hasInstance
+ */
+ public boolean hasInstance(Scriptable instance) {
+ return obj.hasInstance(instance);
+ }
+ /**
+ * @see org.mozilla.javascript.Function#call
+ */
+ public Object call(Context cx, Scriptable scope, Scriptable thisObj,
+ Object[] args)
+ {
+ return ((Function)obj).call(cx,scope,thisObj,args);
+ }
+
+ /**
+ * Note that if the <code>delegee</code> is <code>null</code>,
+ * this method creates a new instance of the Delegator itself
+ * rathert than forwarding the call to the
+ * <code>delegee</code>. 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[])
+ */
+ public Scriptable construct(Context cx, Scriptable scope, Object[] args)
+ {
+ if (obj == 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;
+ }
+ else {
+ return ((Function)obj).construct(cx,scope,args);
+ }
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/EcmaError.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/EcmaError.java
new file mode 100644
index 0000000..1fd8f03
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/EcmaError.java
@@ -0,0 +1,160 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Roger Lawrence
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// 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
+{
+ 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 reponsible 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.
+ */
+ public EcmaError(Scriptable nativeError, String sourceName,
+ int lineNumber, int columnNumber, String lineSource)
+ {
+ this("InternalError", ScriptRuntime.toString(nativeError),
+ sourceName, lineNumber, lineSource, columnNumber);
+ }
+
+ 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 implemenation-defined string describing the error.
+ */
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+ /**
+ * @deprecated Use {@link RhinoException#sourceName()} from the super class.
+ */
+ public String getSourceName()
+ {
+ return sourceName();
+ }
+
+ /**
+ * @deprecated Use {@link RhinoException#lineNumber()} from the super class.
+ */
+ public int getLineNumber()
+ {
+ return lineNumber();
+ }
+
+ /**
+ * @deprecated
+ * Use {@link RhinoException#columnNumber()} from the super class.
+ */
+ public int getColumnNumber() {
+ return columnNumber();
+ }
+
+ /**
+ * @deprecated Use {@link RhinoException#lineSource()} from the super class.
+ */
+ public String getLineSource() {
+ return lineSource();
+ }
+
+ /**
+ * @deprecated
+ * Always returns <b>null</b>.
+ */
+ public Scriptable getErrorObject()
+ {
+ return null;
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ErrorReporter.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ErrorReporter.java
new file mode 100644
index 0000000..4649370
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ErrorReporter.java
@@ -0,0 +1,106 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// 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/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Evaluator.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Evaluator.java
new file mode 100644
index 0000000..e222af3
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Evaluator.java
@@ -0,0 +1,118 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.util.List;
+
+/**
+ * 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 intermediate representation
+ * @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,
+ ScriptOrFnNode 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/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/EvaluatorException.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/EvaluatorException.java
new file mode 100644
index 0000000..7b4e7cc
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/EvaluatorException.java
@@ -0,0 +1,123 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+package org.mozilla.javascript;
+
+/**
+ * The class of exceptions thrown by the JavaScript engine.
+ */
+public class EvaluatorException extends RhinoException
+{
+ 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.
+ */
+ public String getSourceName()
+ {
+ return sourceName();
+ }
+
+ /**
+ * @deprecated Use {@link RhinoException#lineNumber()} from the super class.
+ */
+ public int getLineNumber()
+ {
+ return lineNumber();
+ }
+
+ /**
+ * @deprecated Use {@link RhinoException#columnNumber()} from the super class.
+ */
+ public int getColumnNumber()
+ {
+ return columnNumber();
+ }
+
+ /**
+ * @deprecated Use {@link RhinoException#lineSource()} from the super class.
+ */
+ public String getLineSource()
+ {
+ return lineSource();
+ }
+
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Function.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Function.java
new file mode 100644
index 0000000..a4377e6
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Function.java
@@ -0,0 +1,84 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// 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
+{
+ /**
+ * 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 <code>this</code> object
+ * @param args the array of arguments
+ * @return the result of the call
+ */
+ public 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 <code>new</code> 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
+ */
+ public Scriptable construct(Context cx, Scriptable scope, Object[] args);
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/FunctionNode.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/FunctionNode.java
new file mode 100644
index 0000000..484167e
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/FunctionNode.java
@@ -0,0 +1,117 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Roger Lawrence
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class FunctionNode extends ScriptOrFnNode {
+
+ public FunctionNode(String name) {
+ super(Token.FUNCTION);
+ functionName = name;
+ }
+
+ public String getFunctionName() {
+ return functionName;
+ }
+
+ public boolean requiresActivation() {
+ return itsNeedsActivation;
+ }
+
+ public boolean getIgnoreDynamicScope() {
+ return itsIgnoreDynamicScope;
+ }
+
+ public boolean isGenerator() {
+ return itsIsGenerator;
+ }
+
+ public void addResumptionPoint(Node target) {
+ if (generatorResumePoints == null)
+ generatorResumePoints = new ArrayList();
+ generatorResumePoints.add(target);
+ }
+
+ public ArrayList getResumptionPoints() {
+ return generatorResumePoints;
+ }
+
+ public HashMap getLiveLocals() {
+ return liveLocals;
+ }
+
+ public void addLiveLocals(Node node, int[] locals) {
+ if (liveLocals == null)
+ liveLocals = new HashMap();
+ liveLocals.put(node, locals);
+ }
+
+ /**
+ * There are three types of functions that can be defined. The first
+ * is a function statement. This is a function appearing as a top-level
+ * statement (i.e., not nested inside some other statement) in either a
+ * script or a function.
+ *
+ * The second is a function expression, which is a function appearing in
+ * an expression except for the third type, which is...
+ *
+ * The third type is a function expression where the expression is the
+ * top-level expression in an expression statement.
+ *
+ * The three types of functions have different treatment and must be
+ * distinguished.
+ */
+ public static final int FUNCTION_STATEMENT = 1;
+ public static final int FUNCTION_EXPRESSION = 2;
+ public static final int FUNCTION_EXPRESSION_STATEMENT = 3;
+
+ public int getFunctionType() {
+ return itsFunctionType;
+ }
+
+ String functionName;
+ int itsFunctionType;
+ boolean itsNeedsActivation;
+ boolean itsIgnoreDynamicScope;
+ boolean itsIsGenerator;
+ ArrayList generatorResumePoints;
+ HashMap liveLocals;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/FunctionObject.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/FunctionObject.java
new file mode 100644
index 0000000..8fa4e68
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/FunctionObject.java
@@ -0,0 +1,569 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ * David C. Navas
+ * Ted Neward
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// API class
+
+package org.mozilla.javascript;
+
+import java.lang.reflect.*;
+import java.io.*;
+
+public class FunctionObject extends BaseFunction
+{
+ static final long serialVersionUID = -5332312783643935019L;
+
+ /**
+ * Create a JavaScript function object from a Java method.
+ *
+ * <p>The <code>member</code> argument must be either a java.lang.reflect.Method
+ * or a java.lang.reflect.Constructor and must match one of two forms.<p>
+ *
+ * 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.<p>
+ *
+ * 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.<p>
+ *
+ * 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.<p>
+ *
+ * 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
+ * <pre>
+ * (Context cx, Object[] args, Function ctorObj,
+ * boolean inNewExpr)</pre>
+ * and if it is a Method, be static and return an Object result.<p>
+ *
+ * Otherwise, if the FunctionObject will <i>not</i> be used to define a
+ * constructor, the member must be a static Method with parameters
+ * (Context cx, Scriptable thisObj, Object[] args,
+ * Function funObj) </pre>
+ * <pre>
+ * and an Object result.<p>
+ *
+ * When the function varargs form is called as part of a function call,
+ * the <code>args</code> parameter contains the
+ * arguments, with <code>thisObj</code>
+ * set to the JavaScript 'this' value. <code>funObj</code>
+ * is the function object for the invoked function.<p>
+ *
+ * When the constructor varargs form is called or invoked while evaluating
+ * a <code>new</code> expression, <code>args</code> contains the
+ * arguments, <code>ctorObj</code> refers to this FunctionObject, and
+ * <code>inNewExpr</code> is true if and only if a <code>new</code>
+ * 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.)<p>
+ *
+ * @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.reportRuntimeError1(
+ "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.reportRuntimeError1(
+ "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.reportRuntimeError2(
+ "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.reportRuntimeError1(
+ "msg.bad.ctor.return", ctorType.getName());
+ }
+ }
+
+ ScriptRuntime.setFunctionProtoAndParent(this, scope);
+ }
+
+ /**
+ * @return One of <tt>JAVA_*_TYPE</tt> 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 new Integer(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 new Double(ScriptRuntime.toNumber(arg));
+ case JAVA_SCRIPTABLE_TYPE:
+ if (arg instanceof Scriptable)
+ return arg;
+ return ScriptRuntime.toObject(cx, scope, arg);
+ case JAVA_OBJECT_TYPE:
+ 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).
+ */
+ public int getArity() {
+ return parmsLength < 0 ? 1 : parmsLength;
+ }
+
+ /**
+ * Return the same value as {@link #getArity()}.
+ */
+ public int getLength() {
+ return getArity();
+ }
+
+ 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.reportRuntimeError2(
+ "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.
+ * <p>
+ * 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
+ * <code>prototype.getClassName()</code>
+ * 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 convertion.
+ */
+ public static Object convertArg(Context cx, Scriptable scope,
+ Object arg, Class desired)
+ {
+ int tag = getTypeTag(desired);
+ if (tag == JAVA_UNSUPPORTED_TYPE) {
+ throw Context.reportRuntimeError1
+ ("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.
+ * <p>
+ * Implements Function.call.
+ *
+ * @see org.mozilla.javascript.Function#call(
+ * Context, Scriptable, Scriptable, Object[])
+ */
+ public Object call(Context cx, Scriptable scope, Scriptable thisObj,
+ Object[] args)
+ {
+ Object result;
+ boolean checkMethodResult = false;
+
+ if (parmsLength < 0) {
+ 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 (!clazz.isInstance(thisObj)) {
+ boolean compatible = false;
+ if (thisObj == scope) {
+ 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.typeError1("msg.incompat.call",
+ functionName);
+ }
+ }
+ }
+
+ Object[] invokeArgs;
+ if (parmsLength == args.length) {
+ // 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 < args.length)
+ ? 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.
+ */
+ 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/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/GeneratedClassLoader.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/GeneratedClassLoader.java
new file mode 100644
index 0000000..0f73615
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/GeneratedClassLoader.java
@@ -0,0 +1,66 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// 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/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IRFactory.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IRFactory.java
new file mode 100644
index 0000000..1f51cb1
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IRFactory.java
@@ -0,0 +1,1607 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ * Ethan Hugg
+ * Bob Jervis
+ * Terry Lucas
+ * Milen Nankov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * This class allows the creation of nodes, and follows the Factory pattern.
+ *
+ * @see Node
+ * @author Mike McCabe
+ * @author Norris Boyd
+ */
+final class IRFactory
+{
+ IRFactory(Parser parser)
+ {
+ this.parser = parser;
+ }
+
+ ScriptOrFnNode createScript()
+ {
+ return new ScriptOrFnNode(Token.SCRIPT);
+ }
+
+ /**
+ * Script (for associating file/url names with toplevel scripts.)
+ */
+ void initScript(ScriptOrFnNode scriptNode, Node body)
+ {
+ Node children = body.getFirstChild();
+ if (children != null) { scriptNode.addChildrenToBack(children); }
+ }
+
+ /**
+ * Leaf
+ */
+ Node createLeaf(int nodeType)
+ {
+ return new Node(nodeType);
+ }
+
+ /**
+ * Statement leaf nodes.
+ */
+
+ Node createSwitch(Node expr, int lineno)
+ {
+ //
+ // The switch will be rewritten from:
+ //
+ // switch (expr) {
+ // case test1: statements1;
+ // ...
+ // default: statementsDefault;
+ // ...
+ // case testN: statementsN;
+ // }
+ //
+ // to:
+ //
+ // {
+ // switch (expr) {
+ // case test1: goto label1;
+ // ...
+ // case testN: goto labelN;
+ // }
+ // goto labelDefault;
+ // label1:
+ // statements1;
+ // ...
+ // labelDefault:
+ // statementsDefault;
+ // ...
+ // labelN:
+ // statementsN;
+ // breakLabel:
+ // }
+ //
+ // where inside switch each "break;" without label will be replaced
+ // by "goto breakLabel".
+ //
+ // If the original switch does not have the default label, then
+ // the transformed code would contain after the switch instead of
+ // goto labelDefault;
+ // the following goto:
+ // goto breakLabel;
+ //
+
+ Node.Jump switchNode = new Node.Jump(Token.SWITCH, expr, lineno);
+ Node block = new Node(Token.BLOCK, switchNode);
+ return block;
+ }
+
+ /**
+ * If caseExpression argument is null it indicate default label.
+ */
+ void addSwitchCase(Node switchBlock, Node caseExpression, Node statements)
+ {
+ if (switchBlock.getType() != Token.BLOCK) throw Kit.codeBug();
+ Node.Jump switchNode = (Node.Jump)switchBlock.getFirstChild();
+ if (switchNode.getType() != Token.SWITCH) throw Kit.codeBug();
+
+ Node gotoTarget = Node.newTarget();
+ if (caseExpression != null) {
+ Node.Jump caseNode = new Node.Jump(Token.CASE, caseExpression);
+ caseNode.target = gotoTarget;
+ switchNode.addChildToBack(caseNode);
+ } else {
+ switchNode.setDefault(gotoTarget);
+ }
+ switchBlock.addChildToBack(gotoTarget);
+ switchBlock.addChildToBack(statements);
+ }
+
+ void closeSwitch(Node switchBlock)
+ {
+ if (switchBlock.getType() != Token.BLOCK) throw Kit.codeBug();
+ Node.Jump switchNode = (Node.Jump)switchBlock.getFirstChild();
+ if (switchNode.getType() != Token.SWITCH) throw Kit.codeBug();
+
+ Node switchBreakTarget = Node.newTarget();
+ // switchNode.target is only used by NodeTransformer
+ // to detect switch end
+ switchNode.target = switchBreakTarget;
+
+ Node defaultTarget = switchNode.getDefault();
+ if (defaultTarget == null) {
+ defaultTarget = switchBreakTarget;
+ }
+
+ switchBlock.addChildAfter(makeJump(Token.GOTO, defaultTarget),
+ switchNode);
+ switchBlock.addChildToBack(switchBreakTarget);
+ }
+
+ Node createVariables(int token, int lineno)
+ {
+ return new Node(token, lineno);
+ }
+
+ Node createExprStatement(Node expr, int lineno)
+ {
+ int type;
+ if (parser.insideFunction()) {
+ type = Token.EXPR_VOID;
+ } else {
+ type = Token.EXPR_RESULT;
+ }
+ return new Node(type, expr, lineno);
+ }
+
+ Node createExprStatementNoReturn(Node expr, int lineno)
+ {
+ return new Node(Token.EXPR_VOID, expr, lineno);
+ }
+
+ Node createDefaultNamespace(Node expr, int lineno)
+ {
+ // default xml namespace requires activation
+ setRequiresActivation();
+ Node n = createUnary(Token.DEFAULTNAMESPACE, expr);
+ Node result = createExprStatement(n, lineno);
+ return result;
+ }
+
+ /**
+ * Name
+ */
+ Node createName(String name)
+ {
+ checkActivationName(name, Token.NAME);
+ return Node.newString(Token.NAME, name);
+ }
+
+ private Node createName(int type, String name, Node child)
+ {
+ Node result = createName(name);
+ result.setType(type);
+ if (child != null)
+ result.addChildToBack(child);
+ return result;
+ }
+
+ /**
+ * String (for literals)
+ */
+ Node createString(String string)
+ {
+ return Node.newString(string);
+ }
+
+ /**
+ * Number (for literals)
+ */
+ Node createNumber(double number)
+ {
+ return Node.newNumber(number);
+ }
+
+ /**
+ * Catch clause of try/catch/finally
+ * @param varName the name of the variable to bind to the exception
+ * @param catchCond the condition under which to catch the exception.
+ * May be null if no condition is given.
+ * @param stmts the statements in the catch clause
+ * @param lineno the starting line number of the catch clause
+ */
+ Node createCatch(String varName, Node catchCond, Node stmts, int lineno)
+ {
+ if (catchCond == null) {
+ catchCond = new Node(Token.EMPTY);
+ }
+ return new Node(Token.CATCH, createName(varName),
+ catchCond, stmts, lineno);
+ }
+
+ /**
+ * Throw
+ */
+ Node createThrow(Node expr, int lineno)
+ {
+ return new Node(Token.THROW, expr, lineno);
+ }
+
+ /**
+ * Return
+ */
+ Node createReturn(Node expr, int lineno)
+ {
+ return expr == null
+ ? new Node(Token.RETURN, lineno)
+ : new Node(Token.RETURN, expr, lineno);
+ }
+
+ /**
+ * Debugger
+ */
+ Node createDebugger(int lineno)
+ {
+ return new Node(Token.DEBUGGER, lineno);
+ }
+
+ /**
+ * Label
+ */
+ Node createLabel(int lineno)
+ {
+ return new Node.Jump(Token.LABEL, lineno);
+ }
+
+ Node getLabelLoop(Node label)
+ {
+ return ((Node.Jump)label).getLoop();
+ }
+
+ /**
+ * Label
+ */
+ Node createLabeledStatement(Node labelArg, Node statement)
+ {
+ Node.Jump label = (Node.Jump)labelArg;
+
+ // Make a target and put it _after_ the statement
+ // node. And in the LABEL node, so breaks get the
+ // right target.
+
+ Node breakTarget = Node.newTarget();
+ Node block = new Node(Token.BLOCK, label, statement, breakTarget);
+ label.target = breakTarget;
+
+ return block;
+ }
+
+ /**
+ * Break (possibly labeled)
+ */
+ Node createBreak(Node breakStatement, int lineno)
+ {
+ Node.Jump n = new Node.Jump(Token.BREAK, lineno);
+ Node.Jump jumpStatement;
+ int t = breakStatement.getType();
+ if (t == Token.LOOP || t == Token.LABEL) {
+ jumpStatement = (Node.Jump)breakStatement;
+ } else if (t == Token.BLOCK
+ && breakStatement.getFirstChild().getType() == Token.SWITCH)
+ {
+ jumpStatement = (Node.Jump)breakStatement.getFirstChild();
+ } else {
+ throw Kit.codeBug();
+ }
+ n.setJumpStatement(jumpStatement);
+ return n;
+ }
+
+ /**
+ * Continue (possibly labeled)
+ */
+ Node createContinue(Node loop, int lineno)
+ {
+ if (loop.getType() != Token.LOOP) Kit.codeBug();
+ Node.Jump n = new Node.Jump(Token.CONTINUE, lineno);
+ n.setJumpStatement((Node.Jump)loop);
+ return n;
+ }
+
+ /**
+ * Statement block
+ * Creates the empty statement block
+ * Must make subsequent calls to add statements to the node
+ */
+ Node createBlock(int lineno)
+ {
+ return new Node(Token.BLOCK, lineno);
+ }
+
+ FunctionNode createFunction(String name)
+ {
+ return new FunctionNode(name);
+ }
+
+ Node initFunction(FunctionNode fnNode, int functionIndex,
+ Node statements, int functionType)
+ {
+ fnNode.itsFunctionType = functionType;
+ fnNode.addChildToBack(statements);
+
+ int functionCount = fnNode.getFunctionCount();
+ if (functionCount != 0) {
+ // Functions containing other functions require activation objects
+ fnNode.itsNeedsActivation = true;
+ }
+
+ if (functionType == FunctionNode.FUNCTION_EXPRESSION) {
+ String name = fnNode.getFunctionName();
+ if (name != null && name.length() != 0) {
+ // A function expression needs to have its name as a
+ // variable (if it isn't already allocated as a variable).
+ // See ECMA Ch. 13. We add code to the beginning of the
+ // function to initialize a local variable of the
+ // function's name to the function value.
+ Node setFn = new Node(Token.EXPR_VOID,
+ new Node(Token.SETNAME,
+ Node.newString(Token.BINDNAME, name),
+ new Node(Token.THISFN)));
+ statements.addChildrenToFront(setFn);
+ }
+ }
+
+ // Add return to end if needed.
+ Node lastStmt = statements.getLastChild();
+ if (lastStmt == null || lastStmt.getType() != Token.RETURN) {
+ statements.addChildToBack(new Node(Token.RETURN));
+ }
+
+ Node result = Node.newString(Token.FUNCTION,
+ fnNode.getFunctionName());
+ result.putIntProp(Node.FUNCTION_PROP, functionIndex);
+ return result;
+ }
+
+ /**
+ * Add a child to the back of the given node. This function
+ * breaks the Factory abstraction, but it removes a requirement
+ * from implementors of Node.
+ */
+ void addChildToBack(Node parent, Node child)
+ {
+ parent.addChildToBack(child);
+ }
+
+ /**
+ * Create a node that can be used to hold lexically scoped variable
+ * definitions (via let declarations).
+ *
+ * @param token the token of the node to create
+ * @param lineno line number of source
+ * @return the created node
+ */
+ Node createScopeNode(int token, int lineno) {
+ return new Node.Scope(token, lineno);
+ }
+
+ /**
+ * Create loop node. The parser will later call
+ * createWhile|createDoWhile|createFor|createForIn
+ * to finish loop generation.
+ */
+ Node createLoopNode(Node loopLabel, int lineno)
+ {
+ Node.Jump result = new Node.Scope(Token.LOOP, lineno);
+ if (loopLabel != null) {
+ ((Node.Jump)loopLabel).setLoop(result);
+ }
+ return result;
+ }
+
+ /**
+ * While
+ */
+ Node createWhile(Node loop, Node cond, Node body)
+ {
+ return createLoop((Node.Jump)loop, LOOP_WHILE, body, cond,
+ null, null);
+ }
+
+ /**
+ * DoWhile
+ */
+ Node createDoWhile(Node loop, Node body, Node cond)
+ {
+ return createLoop((Node.Jump)loop, LOOP_DO_WHILE, body, cond,
+ null, null);
+ }
+
+ /**
+ * For
+ */
+ Node createFor(Node loop, Node init, Node test, Node incr, Node body)
+ {
+ if (init.getType() == Token.LET) {
+ // rewrite "for (let i=s; i < N; i++)..." as
+ // "let (i=s) { for (; i < N; i++)..." so that "s" is evaluated
+ // outside the scope of the for.
+ Node.Scope let = Node.Scope.splitScope((Node.Scope)loop);
+ let.setType(Token.LET);
+ let.addChildrenToBack(init);
+ let.addChildToBack(createLoop((Node.Jump)loop, LOOP_FOR, body, test,
+ new Node(Token.EMPTY), incr));
+ return let;
+ }
+ return createLoop((Node.Jump)loop, LOOP_FOR, body, test, init, incr);
+ }
+
+ private Node createLoop(Node.Jump loop, int loopType, Node body, Node cond,
+ Node init, Node incr)
+ {
+ Node bodyTarget = Node.newTarget();
+ Node condTarget = Node.newTarget();
+ if (loopType == LOOP_FOR && cond.getType() == Token.EMPTY) {
+ cond = new Node(Token.TRUE);
+ }
+ Node.Jump IFEQ = new Node.Jump(Token.IFEQ, cond);
+ IFEQ.target = bodyTarget;
+ Node breakTarget = Node.newTarget();
+
+ loop.addChildToBack(bodyTarget);
+ loop.addChildrenToBack(body);
+ if (loopType == LOOP_WHILE || loopType == LOOP_FOR) {
+ // propagate lineno to condition
+ loop.addChildrenToBack(new Node(Token.EMPTY, loop.getLineno()));
+ }
+ loop.addChildToBack(condTarget);
+ loop.addChildToBack(IFEQ);
+ loop.addChildToBack(breakTarget);
+
+ loop.target = breakTarget;
+ Node continueTarget = condTarget;
+
+ if (loopType == LOOP_WHILE || loopType == LOOP_FOR) {
+ // Just add a GOTO to the condition in the do..while
+ loop.addChildToFront(makeJump(Token.GOTO, condTarget));
+
+ if (loopType == LOOP_FOR) {
+ int initType = init.getType();
+ if (initType != Token.EMPTY) {
+ if (initType != Token.VAR && initType != Token.LET) {
+ init = new Node(Token.EXPR_VOID, init);
+ }
+ loop.addChildToFront(init);
+ }
+ Node incrTarget = Node.newTarget();
+ loop.addChildAfter(incrTarget, body);
+ if (incr.getType() != Token.EMPTY) {
+ incr = new Node(Token.EXPR_VOID, incr);
+ loop.addChildAfter(incr, incrTarget);
+ }
+ continueTarget = incrTarget;
+ }
+ }
+
+ loop.setContinue(continueTarget);
+
+ return loop;
+ }
+
+ /**
+ * For .. In
+ *
+ */
+ Node createForIn(int declType, Node loop, Node lhs, Node obj, Node body,
+ boolean isForEach)
+ {
+ int destructuring = -1;
+ int destructuringLen = 0;
+ Node lvalue;
+ int type = lhs.getType();
+ if (type == Token.VAR || type == Token.LET) {
+ Node lastChild = lhs.getLastChild();
+ if (lhs.getFirstChild() != lastChild) {
+ /*
+ * check that there was only one variable given.
+ * we can't do this in the parser, because then the
+ * parser would have to know something about the
+ * 'init' node of the for-in loop.
+ */
+ parser.reportError("msg.mult.index");
+ }
+ if (lastChild.getType() == Token.ARRAYLIT ||
+ lastChild.getType() == Token.OBJECTLIT)
+ {
+ type = destructuring = lastChild.getType();
+ lvalue = lastChild;
+ destructuringLen = lastChild.getIntProp(
+ Node.DESTRUCTURING_ARRAY_LENGTH, 0);
+ } else if (lastChild.getType() == Token.NAME) {
+ lvalue = Node.newString(Token.NAME, lastChild.getString());
+ } else {
+ parser.reportError("msg.bad.for.in.lhs");
+ return obj;
+ }
+ } else if (type == Token.ARRAYLIT || type == Token.OBJECTLIT) {
+ destructuring = type;
+ lvalue = lhs;
+ destructuringLen = lhs.getIntProp(Node.DESTRUCTURING_ARRAY_LENGTH, 0);
+ } else {
+ lvalue = makeReference(lhs);
+ if (lvalue == null) {
+ parser.reportError("msg.bad.for.in.lhs");
+ return obj;
+ }
+ }
+
+ Node localBlock = new Node(Token.LOCAL_BLOCK);
+ int initType = (isForEach) ? Token.ENUM_INIT_VALUES :
+ (destructuring != -1) ? Token.ENUM_INIT_ARRAY :
+ Token.ENUM_INIT_KEYS;
+ Node init = new Node(initType, obj);
+ init.putProp(Node.LOCAL_BLOCK_PROP, localBlock);
+ Node cond = new Node(Token.ENUM_NEXT);
+ cond.putProp(Node.LOCAL_BLOCK_PROP, localBlock);
+ Node id = new Node(Token.ENUM_ID);
+ id.putProp(Node.LOCAL_BLOCK_PROP, localBlock);
+
+ Node newBody = new Node(Token.BLOCK);
+ Node assign;
+ if (destructuring != -1) {
+ assign = createDestructuringAssignment(declType, lvalue, id);
+ if (!isForEach && (destructuring == Token.OBJECTLIT ||
+ destructuringLen != 2))
+ {
+ // destructuring assignment is only allowed in for..each or
+ // with an array type of length 2 (to hold key and value)
+ parser.reportError("msg.bad.for.in.destruct");
+ }
+ } else {
+ assign = simpleAssignment(lvalue, id);
+ }
+ newBody.addChildToBack(new Node(Token.EXPR_VOID, assign));
+ newBody.addChildToBack(body);
+
+ loop = createWhile(loop, cond, newBody);
+ loop.addChildToFront(init);
+ if (type == Token.VAR || type == Token.LET)
+ loop.addChildToFront(lhs);
+ localBlock.addChildToBack(loop);
+
+ return localBlock;
+ }
+
+ /**
+ * Try/Catch/Finally
+ *
+ * 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.
+ */
+ 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);
+ Node.Jump pn = new Node.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;
+ }
+
+ /**
+ * Throw, Return, Label, Break and Continue are defined in ASTFactory.
+ */
+
+ /**
+ * With
+ */
+ 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;
+ }
+
+ /**
+ * DOTQUERY
+ */
+ public Node createDotQuery (Node obj, Node body, int lineno)
+ {
+ setRequiresActivation();
+ Node result = new Node(Token.DOTQUERY, obj, body, lineno);
+ return result;
+ }
+
+ Node createArrayLiteral(ObjArray elems, int skipCount, int destructuringLen)
+ {
+ int length = elems.size();
+ int[] skipIndexes = null;
+ if (skipCount != 0) {
+ skipIndexes = new int[skipCount];
+ }
+ Node array = new Node(Token.ARRAYLIT);
+ for (int i = 0, j = 0; i != length; ++i) {
+ Node elem = (Node)elems.get(i);
+ if (elem != null) {
+ array.addChildToBack(elem);
+ } else {
+ skipIndexes[j] = i;
+ ++j;
+ }
+ }
+ if (skipCount != 0) {
+ array.putProp(Node.SKIP_INDEXES_PROP, skipIndexes);
+ }
+ array.putIntProp(Node.DESTRUCTURING_ARRAY_LENGTH, destructuringLen);
+ return array;
+ }
+
+ /**
+ * Object Literals
+ * <BR> createObjectLiteral rewrites its argument as object
+ * creation plus object property entries, so later compiler
+ * stages don't need to know about object literals.
+ */
+ Node createObjectLiteral(ObjArray elems)
+ {
+ int size = elems.size() / 2;
+ Node object = new Node(Token.OBJECTLIT);
+ Object[] properties;
+ if (size == 0) {
+ properties = ScriptRuntime.emptyArgs;
+ } else {
+ properties = new Object[size];
+ for (int i = 0; i != size; ++i) {
+ properties[i] = elems.get(2 * i);
+ Node value = (Node)elems.get(2 * i + 1);
+ object.addChildToBack(value);
+ }
+ }
+ object.putProp(Node.OBJECT_IDS_PROP, properties);
+ return object;
+ }
+
+ /**
+ * Regular expressions
+ */
+ Node createRegExp(int regexpIndex)
+ {
+ Node n = new Node(Token.REGEXP);
+ n.putIntProp(Node.REGEXP_PROP, regexpIndex);
+ return n;
+ }
+
+ /**
+ * If statement
+ */
+ 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();
+ Node.Jump IFNE = new Node.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;
+ }
+
+ 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);
+ }
+
+ /**
+ * Unary
+ */
+ 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 {
+ n = new Node(Token.TRUE);
+ }
+ 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);
+ }
+
+ Node createYield(Node child, int lineno)
+ {
+ if (!parser.insideFunction()) {
+ parser.reportError("msg.bad.yield");
+ }
+ setRequiresActivation();
+ setIsGenerator();
+ if (child != null)
+ return new Node(Token.YIELD, child, lineno);
+ else
+ return new Node(Token.YIELD, lineno);
+ }
+
+ 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;
+ }
+
+ Node createIncDec(int nodeType, boolean post, Node child)
+ {
+ child = makeReference(child);
+ if (child == null) {
+ String msg;
+ if (nodeType == Token.DEC) {
+ msg = "msg.bad.decr";
+ } else {
+ msg = "msg.bad.incr";
+ }
+ parser.reportError(msg);
+ return null;
+ }
+
+ 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();
+ }
+
+ 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, createString(name));
+ }
+ Node elem = createString(name);
+ memberTypeFlags |= Node.PROPERTY_FLAG;
+ return createMemberRefGet(target, namespace, elem, memberTypeFlags);
+ }
+
+ 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);
+ }
+
+ /**
+ * Binary
+ */
+ 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 simpleAssignment(Node left, Node right)
+ {
+ int nodeType = left.getType();
+ switch (nodeType) {
+ case Token.NAME:
+ left.setType(Token.BINDNAME);
+ return new Node(Token.SETNAME, left, right);
+
+ case Token.GETPROP:
+ case Token.GETELEM: {
+ Node obj = left.getFirstChild();
+ Node id = left.getLastChild();
+ int type;
+ if (nodeType == Token.GETPROP) {
+ type = Token.SETPROP;
+ } else {
+ type = Token.SETELEM;
+ }
+ return new Node(type, obj, id, right);
+ }
+ case Token.GET_REF: {
+ Node ref = left.getFirstChild();
+ checkMutableReference(ref);
+ return new Node(Token.SET_REF, ref, right);
+ }
+ }
+
+ throw Kit.codeBug();
+ }
+
+ private void checkMutableReference(Node n)
+ {
+ int memberTypeFlags = n.getIntProp(Node.MEMBER_TYPE_PROP, 0);
+ if ((memberTypeFlags & Node.DESCENDANTS_FLAG) != 0) {
+ parser.reportError("msg.bad.assign.left");
+ }
+ }
+
+ 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) {
+ parser.reportError("msg.bad.destruct.op");
+ return right;
+ }
+ return createDestructuringAssignment(-1, left, right);
+ }
+ parser.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;
+ 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();
+ }
+
+ /**
+ * Given a destructuring assignment with a left hand side parsed
+ * as an array or object literal and a right hand side expression,
+ * rewrite as a series of assignments to the variables defined in
+ * left from property accesses to the expression on the right.
+ * @param type declaration type: Token.VAR or Token.LET or -1
+ * @param left array or object literal containing NAME nodes for
+ * variables to assign
+ * @param right expression to assign from
+ * @return expression that performs a series of assignments to
+ * the variables defined in left
+ */
+ Node createDestructuringAssignment(int type, Node left, Node right)
+ {
+ String tempName = parser.currentScriptOrFn.getNextTempName();
+ Node result = destructuringAssignmentHelper(type, left, right,
+ tempName);
+ Node comma = result.getLastChild();
+ comma.addChildToBack(createName(tempName));
+ return result;
+ }
+
+ private Node destructuringAssignmentHelper(int variableType, Node left,
+ Node right, String tempName)
+ {
+ Node result = createScopeNode(Token.LETEXPR,
+ parser.getCurrentLineNumber());
+ result.addChildToFront(new Node(Token.LET,
+ createName(Token.NAME, tempName, right)));
+ try {
+ parser.pushScope(result);
+ parser.defineSymbol(Token.LET, tempName);
+ } finally {
+ parser.popScope();
+ }
+ Node comma = new Node(Token.COMMA);
+ result.addChildToBack(comma);
+ final int setOp = variableType == Token.CONST ? Token.SETCONST
+ : Token.SETNAME;
+ List<String> destructuringNames = new ArrayList<String>();
+ boolean empty = true;
+ int type = left.getType();
+ if (type == Token.ARRAYLIT) {
+ int index = 0;
+ int[] skipIndices = (int[])left.getProp(Node.SKIP_INDEXES_PROP);
+ int skip = 0;
+ Node n = left.getFirstChild();
+ for (;;) {
+ if (skipIndices != null) {
+ while (skip < skipIndices.length &&
+ skipIndices[skip] == index) {
+ skip++;
+ index++;
+ }
+ }
+ if (n == null)
+ break;
+ Node rightElem = new Node(Token.GETELEM,
+ createName(tempName),
+ createNumber(index));
+ if (n.getType() == Token.NAME) {
+ String name = n.getString();
+ comma.addChildToBack(new Node(setOp,
+ createName(Token.BINDNAME, name, null),
+ rightElem));
+ if (variableType != -1) {
+ parser.defineSymbol(variableType, name);
+ destructuringNames.add(name);
+ }
+ } else {
+ comma.addChildToBack(
+ destructuringAssignmentHelper(variableType, n,
+ rightElem,
+ parser.currentScriptOrFn.getNextTempName()));
+ }
+ index++;
+ empty = false;
+ n = n.getNext();
+ }
+ } else if (type == Token.OBJECTLIT) {
+ int index = 0;
+ Object[] propertyIds = (Object[])
+ left.getProp(Node.OBJECT_IDS_PROP);
+ for (Node n = left.getFirstChild(); n != null; n = n.getNext())
+ {
+ Object id = propertyIds[index];
+ Node rightElem = id instanceof String
+ ? new Node(Token.GETPROP,
+ createName(tempName),
+ createString((String)id))
+ : new Node(Token.GETELEM,
+ createName(tempName),
+ createNumber(((Number)id).intValue()));
+ if (n.getType() == Token.NAME) {
+ String name = n.getString();
+ comma.addChildToBack(new Node(setOp,
+ createName(Token.BINDNAME, name, null),
+ rightElem));
+ if (variableType != -1) {
+ parser.defineSymbol(variableType, name);
+ destructuringNames.add(name);
+ }
+ } else {
+ comma.addChildToBack(
+ destructuringAssignmentHelper(variableType, n,
+ rightElem,
+ parser.currentScriptOrFn.getNextTempName()));
+ }
+ index++;
+ empty = false;
+ }
+ } else if (type == Token.GETPROP || type == Token.GETELEM) {
+ comma.addChildToBack(simpleAssignment(left, createName(tempName)));
+ } else {
+ parser.reportError("msg.bad.assign.left");
+ }
+ if (empty) {
+ // Don't want a COMMA node with no children. Just add a zero.
+ comma.addChildToBack(createNumber(0));
+ }
+ result.putProp(Node.DESTRUCTURING_NAMES, destructuringNames);
+ return result;
+ }
+
+ 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 Node.Jump makeJump(int type, Node target)
+ {
+ Node.Jump n = new Node.Jump(type);
+ n.target = target;
+ return n;
+ }
+
+ private 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 (num == num && num != 0.0) {
+ return ALWAYS_TRUE_BOOLEAN;
+ } else {
+ return ALWAYS_FALSE_BOOLEAN;
+ }
+ }
+ }
+ return 0;
+ }
+
+ private void checkActivationName(String name, int token)
+ {
+ if (parser.insideFunction()) {
+ boolean activation = false;
+ if ("arguments".equals(name)
+ || (parser.compilerEnv.activationNames != null
+ && parser.compilerEnv.activationNames.containsKey(name)))
+ {
+ activation = true;
+ } else if ("length".equals(name)) {
+ if (token == Token.GETPROP
+ && parser.compilerEnv.getLanguageVersion()
+ == Context.VERSION_1_2)
+ {
+ // Use of "length" in 1.2 requires an activation object.
+ activation = true;
+ }
+ }
+ if (activation) {
+ setRequiresActivation();
+ }
+ }
+ }
+
+ private void setRequiresActivation()
+ {
+ if (parser.insideFunction()) {
+ ((FunctionNode)parser.currentScriptOrFn).itsNeedsActivation = true;
+ }
+ }
+
+ private void setIsGenerator()
+ {
+ if (parser.insideFunction()) {
+ ((FunctionNode)parser.currentScriptOrFn).itsIsGenerator = true;
+ }
+ }
+
+ private Parser 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;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdFunctionCall.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdFunctionCall.java
new file mode 100644
index 0000000..713fabf
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdFunctionCall.java
@@ -0,0 +1,55 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+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/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdFunctionObject.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdFunctionObject.java
new file mode 100644
index 0000000..c578dfa
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdFunctionObject.java
@@ -0,0 +1,189 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// API class
+
+package org.mozilla.javascript;
+
+public class IdFunctionObject extends BaseFunction
+{
+
+ 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;
+ if (arity < 0) throw new IllegalArgumentException();
+ }
+
+ 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 this.tag == 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());
+ }
+
+ 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;
+ }
+
+ public Object call(Context cx, Scriptable scope, Scriptable thisObj,
+ Object[] args)
+ {
+ return idcall.execIdCall(this, cx, scope, thisObj, args);
+ }
+
+ 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.typeError1("msg.not.ctor", functionName);
+ }
+
+ String decompile(int indent, int flags)
+ {
+ StringBuffer sb = new StringBuffer();
+ 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();
+ }
+
+ public int getArity()
+ {
+ return arity;
+ }
+
+ public int getLength() { return getArity(); }
+
+ 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);
+ }
+
+ private final IdFunctionCall idcall;
+ private final Object tag;
+ private final int methodId;
+ private int arity;
+ private boolean useCallAsConstructor;
+ private String functionName;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdScriptableObject.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdScriptableObject.java
new file mode 100644
index 0000000..2b3ecf3
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdScriptableObject.java
@@ -0,0 +1,734 @@
+/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.io.*;
+
+/**
+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 initializition of constructor and protype objects, descendant
+may override scopeInit or fillConstructorProperties methods.
+
+*/
+public abstract class IdScriptableObject extends ScriptableObject
+ implements IdFunctionCall
+{
+ private transient volatile PrototypeValues prototypeValues;
+
+ private static final class PrototypeValues implements Serializable
+ {
+ static final long serialVersionUID = 3038645279153854371L;
+
+ private static final int VALUE_SLOT = 0;
+ private static final int NAME_SLOT = 1;
+ private static final int SLOT_SPAN = 2;
+
+ private IdScriptableObject obj;
+ private int maxId;
+ private volatile Object[] valueArray;
+ private volatile short[] attributeArray;
+ private volatile int lastFoundId = 1;
+
+ // 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);
+ }
+
+ private void initSlot(int id, String 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 + VALUE_SLOT];
+ if (value2 == null) {
+ array[index + VALUE_SLOT] = 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)
+ {
+ Object[] array = valueArray;
+ if (array == null) {
+ return obj.findPrototypeId(name);
+ }
+ int id = lastFoundId;
+ if (name == array[(id - 1) * SLOT_SPAN + NAME_SLOT]) {
+ return id;
+ }
+ id = obj.findPrototypeId(name);
+ if (id != 0) {
+ int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT;
+ // Make cache to work!
+ array[nameSlot] = name;
+ lastFoundId = id;
+ }
+ return id;
+ }
+
+ 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 + VALUE_SLOT;
+ 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 + VALUE_SLOT;
+ synchronized (this) {
+ valueArray[valueSlot] = value;
+ }
+ }
+ else {
+ int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT;
+ String name = (String)valueArray[nameSlot];
+ start.put(name, start, value);
+ }
+ }
+ }
+
+ final void delete(int id)
+ {
+ ensureId(id);
+ int attr = attributeArray[id - 1];
+ if ((attr & PERMANENT) == 0) {
+ int valueSlot = (id - 1) * SLOT_SPAN + VALUE_SLOT;
+ 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, 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;
+ String name = (String)valueArray[nameSlot];
+ if (names == null) {
+ names = new Object[maxId];
+ }
+ names[count++] = name;
+ }
+ }
+ }
+ 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 + VALUE_SLOT;
+ 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 Object defaultGet(String name)
+ {
+ return super.get(name, this);
+ }
+
+ protected final void defaultPut(String name, Object value)
+ {
+ super.put(name, this, value);
+ }
+
+ 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);
+ }
+
+ public Object get(String name, Scriptable start)
+ {
+ int info = findInstanceIdInfo(name);
+ if (info != 0) {
+ int id = (info & 0xFFFF);
+ return getInstanceIdValue(id);
+ }
+ if (prototypeValues != null) {
+ int id = prototypeValues.findId(name);
+ if (id != 0) {
+ return prototypeValues.get(id);
+ }
+ }
+ return super.get(name, start);
+ }
+
+ public void put(String name, Scriptable start, Object value)
+ {
+ int info = findInstanceIdInfo(name);
+ if (info != 0) {
+ if (start == this && isSealed()) {
+ throw Context.reportRuntimeError1("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.reportRuntimeError1("msg.modify.sealed",
+ name);
+ }
+ prototypeValues.set(id, start, value);
+ return;
+ }
+ }
+ super.put(name, start, value);
+ }
+
+ 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);
+ if ((attr & PERMANENT) == 0) {
+ 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);
+ }
+
+ 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);
+ }
+
+ public void setAttributes(String name, int attributes)
+ {
+ ScriptableObject.checkValidAttributes(attributes);
+ int info = findInstanceIdInfo(name);
+ if (info != 0) {
+ int currentAttributes = (info >>> 16);
+ if (attributes != currentAttributes) {
+ throw new RuntimeException(
+ "Change of attributes for this id is not supported");
+ }
+ return;
+ }
+ if (prototypeValues != null) {
+ int id = prototypeValues.findId(name);
+ if (id != 0) {
+ prototypeValues.setAttributes(id, attributes);
+ return;
+ }
+ }
+ super.setAttributes(name, attributes);
+ }
+
+ Object[] getIds(boolean getAll)
+ {
+ Object[] result = super.getIds(getAll);
+
+ if (prototypeValues != null) {
+ result = prototypeValues.getNames(getAll, 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 (getAll || (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 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));
+ }
+
+ /** '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)
+ {
+ 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 void initPrototypeMethod(Object tag, int id, String name,
+ int arity)
+ {
+ Scriptable scope = ScriptableObject.getTopLevelScope(this);
+ IdFunctionObject f = newIdFunction(tag, id, name, arity, scope);
+ prototypeValues.initValue(id, name, f, DONTENUM);
+ }
+
+ 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);
+ }
+
+ protected void initPrototypeId(int id)
+ {
+ throw new IllegalStateException(String.valueOf(id));
+ }
+
+ protected int findPrototypeId(String name)
+ {
+ throw new IllegalStateException(name);
+ }
+
+ 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 construct type error to indicate incompatible call
+ * when converting script thisObj to a particular type is not possible.
+ * Possible usage would be to have a private function like realThis:
+ * <pre>
+ * private static NativeSomething realThis(Scriptable thisObj,
+ * IdFunctionObject f)
+ * {
+ * if (!(thisObj instanceof NativeSomething))
+ * throw incompatibleCallError(f);
+ * return (NativeSomething)thisObj;
+ * }
+ * </pre>
+ * Note that although such function can be implemented universally via
+ * java.lang.Class.isInstance(), it would be much more slower.
+ * @param f function that is attempting to convert 'this'
+ * object.
+ * @return Scriptable object suitable for a check by the instanceof
+ * operator.
+ * @throws RuntimeException if no more instanceof target can be found
+ */
+ protected static EcmaError incompatibleCallError(IdFunctionObject f)
+ {
+ throw ScriptRuntime.typeError1("msg.incompat.call",
+ f.getFunctionName());
+ }
+
+ private IdFunctionObject newIdFunction(Object tag, int id, String name,
+ int arity, Scriptable scope)
+ {
+ IdFunctionObject f = new IdFunctionObject(this, tag, id, name, arity,
+ scope);
+ if (isSealed()) { f.sealObject(); }
+ return f;
+ }
+
+ 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/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ImporterTopLevel.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ImporterTopLevel.java
new file mode 100644
index 0000000..294deab
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ImporterTopLevel.java
@@ -0,0 +1,318 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ * Matthias Radestock
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// 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.
+ * <p>
+ * This class can be used to create a top-level scope using the following code:
+ * <pre>
+ * Scriptable scope = new ImporterTopLevel(cx);
+ * </pre>
+ * Then JavaScript code will have access to the following methods:
+ * <ul>
+ * <li>importClass - will "import" a class by making its unqualified name
+ * available as a property of the top-level scope
+ * <li>importPackage - will "import" all the classes of the package by
+ * searching for unqualified names as classes qualified
+ * by the given package.
+ * </ul>
+ * The following code from the shell illustrates this use:
+ * <pre>
+ * 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 IdScriptableObject
+{
+ static final long serialVersionUID = -9095380847465315412L;
+
+ private static final Object IMPORTER_TAG = new Object();
+
+ public ImporterTopLevel() { }
+
+ public ImporterTopLevel(Context cx) {
+ this(cx, false);
+ }
+
+ public ImporterTopLevel(Context cx, boolean sealed)
+ {
+ initStandardObjects(cx, sealed);
+ }
+
+ 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");
+ }
+
+ public boolean has(String name, Scriptable start) {
+ return super.has(name, start)
+ || getPackageProperty(name, start) != NOT_FOUND;
+ }
+
+ 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;
+ Object[] elements;
+ synchronized (importedPackages) {
+ elements = importedPackages.toArray();
+ }
+ 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.reportRuntimeError2(
+ "msg.ambig.import", result.toString(), v.toString());
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @deprecated Kept only for compatibility.
+ */
+ public void importPackage(Context cx, Scriptable thisObj, Object[] args,
+ Function funObj)
+ {
+ js_importPackage(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) {
+ result.importClass((NativeJavaClass)arg);
+ } else if (arg instanceof NativeJavaPackage) {
+ result.importPackage((NativeJavaPackage)arg);
+ } else {
+ throw Context.reportRuntimeError1(
+ "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 Object js_importClass(Object[] args)
+ {
+ for (int i = 0; i != args.length; i++) {
+ Object arg = args[i];
+ if (!(arg instanceof NativeJavaClass)) {
+ throw Context.reportRuntimeError1(
+ "msg.not.class", Context.toString(arg));
+ }
+ importClass((NativeJavaClass)arg);
+ }
+ return Undefined.instance;
+ }
+
+ private Object js_importPackage(Object[] args)
+ {
+ for (int i = 0; i != args.length; i++) {
+ Object arg = args[i];
+ if (!(arg instanceof NativeJavaPackage)) {
+ throw Context.reportRuntimeError1(
+ "msg.not.pkg", Context.toString(arg));
+ }
+ importPackage((NativeJavaPackage)arg);
+ }
+ return Undefined.instance;
+ }
+
+ private void importPackage(NativeJavaPackage pkg)
+ {
+ if(pkg == null) {
+ return;
+ }
+ synchronized (importedPackages) {
+ for (int j = 0; j != importedPackages.size(); j++) {
+ if (pkg.equals(importedPackages.get(j))) {
+ return;
+ }
+ }
+ importedPackages.add(pkg);
+ }
+ }
+
+ private void importClass(NativeJavaClass cl)
+ {
+ String s = cl.getClassObject().getName();
+ String n = s.substring(s.lastIndexOf('.')+1);
+ Object val = get(n, this);
+ if (val != NOT_FOUND && val != cl) {
+ throw Context.reportRuntimeError1("msg.prop.defined", n);
+ }
+ //defineProperty(n, cl, DONTENUM);
+ put(n, this, cl);
+ }
+
+ 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);
+ }
+
+ 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 realThis(thisObj, f).js_importClass(args);
+
+ case Id_importPackage:
+ return realThis(thisObj, f).js_importPackage(args);
+ }
+ throw new IllegalArgumentException(String.valueOf(id));
+ }
+
+ private ImporterTopLevel realThis(Scriptable thisObj, IdFunctionObject f)
+ {
+ if (topScopeFlag) {
+ // when used as top scope importPackage and importClass are global
+ // function that ignore thisObj
+ return this;
+ }
+ if (!(thisObj instanceof ImporterTopLevel))
+ throw incompatibleCallError(f);
+ return (ImporterTopLevel)thisObj;
+ }
+
+// #string_id_map#
+
+ protected int findPrototypeId(String s)
+ {
+ int id;
+// #generated# Last update: 2007-05-09 08:15:24 EDT
+ L0: { id = 0; String X = null; int c;
+ int s_length = s.length();
+ if (s_length==11) {
+ c=s.charAt(0);
+ if (c=='c') { X="constructor";id=Id_constructor; }
+ else if (c=='i') { X="importClass";id=Id_importClass; }
+ }
+ else if (s_length==13) { X="importPackage";id=Id_importPackage; }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ break L0;
+ }
+// #/generated#
+ return id;
+ }
+
+ private static final int
+ Id_constructor = 1,
+ Id_importClass = 2,
+ Id_importPackage = 3,
+ MAX_PROTOTYPE_ID = 3;
+
+// #/string_id_map#
+
+ private ObjArray importedPackages = new ObjArray();
+ private boolean topScopeFlag;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InformativeParser.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InformativeParser.java
new file mode 100644
index 0000000..c73db34
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InformativeParser.java
@@ -0,0 +1,225 @@
+package org.mozilla.javascript;
+
+import java.io.IOException;
+
+/**
+ * Subclass of Rhino's Parser that saves information about the token stream
+ * and error message to allow more helpful error messages.
+ *
+ * @author David Greenspan for AppJet
+ */
+
+/*
+ This class is written with speed in mind, to some extent. Rhino's tokenizer
+ is pretty efficient, and we wouldn't want to slow it down by, for example,
+ creating a TokenInfo object on the heap for every token seen.
+ */
+
+/*APPJET*/
+public class InformativeParser extends Parser {
+
+ public static class InformativeEvaluatorException extends EvaluatorException {
+ final ParseErrorInfo pei;
+
+ InformativeEvaluatorException(String errorMessage,
+ String sourceName, int lineNumber,
+ String lineSource, int columnNumber,
+ ParseErrorInfo peInfo) {
+ super(errorMessage, sourceName,
+ lineNumber, lineSource, columnNumber);
+ pei = peInfo;
+ }
+
+ public ParseErrorInfo getParseErrorInfo() {
+ return pei;
+ }
+ }
+
+ public static class ParseErrorInfo {
+ ParseErrorInfo() {}
+
+ String messageId = null;
+ String messageArg = null;
+
+ final int tokenMaxHistory = 10;
+ // ring buffers
+ final int[] tokenTypes = new int[tokenMaxHistory];
+ final String[] tokenStrings = new String[tokenMaxHistory];
+ final double[] tokenNumbers = new double[tokenMaxHistory];
+ final int[] tokenLineNumbers = new int[tokenMaxHistory];
+ final int[] tokenLineOffsets = new int[tokenMaxHistory];
+ int nextBufPos = 0;
+ int historyLength = 0;
+ boolean tokenPeeking = false;
+ int peekSlot;
+
+ void reportPeekToken(int type, String str, double num, int lineno,
+ int lineOffset) {
+ if (! tokenPeeking) {
+ peekSlot = nextBufPos;
+ tokenTypes[nextBufPos] = type;
+ tokenStrings[nextBufPos] = str;
+ tokenNumbers[nextBufPos] = num;
+ tokenLineNumbers[nextBufPos] = lineno;
+ tokenLineOffsets[nextBufPos] = lineOffset;
+
+ nextBufPos++;
+ if (nextBufPos == tokenMaxHistory) nextBufPos = 0;
+ if (historyLength < tokenMaxHistory) historyLength++;
+ tokenPeeking = true;
+ }
+ }
+
+ void reportConsumeToken() {
+ tokenPeeking = false;
+ }
+
+ private TokenInfo backToken(int n) {
+ // 0 is most recent token added to history
+ if (n >= historyLength) return null;
+ int i = (nextBufPos - 1 - n);
+ while (i < 0) i += tokenMaxHistory;
+ return new TokenInfo(tokenTypes[i], tokenStrings[i],
+ tokenNumbers[i], tokenLineNumbers[i],
+ tokenLineOffsets[i]);
+ }
+
+ public String getMessageId() { return messageId; }
+ public String getMessageArg() { return messageArg; }
+ public TokenInfo getPeekToken() {
+ if (tokenPeeking) return backToken(0);
+ return null;
+ }
+ public TokenInfo getPrevToken(int n) {
+ // 1 = last non-peek token seen, 2 = before that, etc.
+ if (! tokenPeeking) n--;
+ return backToken(n);
+ }
+ public TokenInfo getPrevToken() {
+ return getPrevToken(1);
+ }
+ }
+
+ public static class TokenInfo {
+ private int type, lineno, lineOffset;
+ private String str;
+ private double num;
+ TokenInfo(int type, String str, double num, int lineno,
+ int lineOffset) {
+ this.type = type; this.str = str; this.num = num;
+ this.lineno = lineno; this.lineOffset = lineOffset;
+ }
+ public int getType() { return type; }
+ public int getLineNumber() { return lineno; }
+ public int getLineOffset() { return lineOffset; }
+ public double getNumber() { return num; }
+ public String getString() { return str; }
+ }
+
+ ParseErrorInfo info = new ParseErrorInfo();
+
+ void doErrorReporterError(String message, String sourceURI, int line,
+ String lineText, int lineOffset) {
+
+ throw new InformativeEvaluatorException(message, sourceURI, line,
+ lineText, lineOffset, info);
+
+ }
+
+ public InformativeParser(CompilerEnvirons compilerEnv) {
+ // we override most calls to the parent's ErrorReporter anyway
+ super(compilerEnv, DefaultErrorReporter.instance);
+ }
+
+ @Override int peekToken() throws IOException {
+ int tt = super.peekToken();
+ info.reportPeekToken(tt, ts.getString(), ts.getNumber(),
+ ts.getLineno(), ts.getOffset());
+ return tt;
+ }
+ @Override void consumeToken() {
+ super.consumeToken();
+ info.reportConsumeToken();
+ }
+
+ @Override void addWarning(String messageId, String messageArg)
+ {
+ info.messageId = messageId;
+ info.messageArg = messageArg;
+
+ String message = ScriptRuntime.getMessage1(messageId, messageArg);
+ if (compilerEnv.reportWarningAsError()) {
+ ++syntaxErrorCount;
+ doErrorReporterError(message, sourceURI, ts.getLineno(),
+ ts.getLine(), ts.getOffset());
+ }
+ else { /* don't report */ }
+ }
+
+ @Override void addError(String messageId)
+ {
+ info.messageId = messageId;
+
+ ++syntaxErrorCount;
+ String message = ScriptRuntime.getMessage0(messageId);
+ doErrorReporterError(message, sourceURI, ts.getLineno(),
+ ts.getLine(), ts.getOffset());
+ }
+
+ @Override void addError(String messageId, String messageArg)
+ {
+ info.messageId = messageId;
+ info.messageArg = messageArg;
+
+ ++syntaxErrorCount;
+ String message = ScriptRuntime.getMessage1(messageId, messageArg);
+ doErrorReporterError(message, sourceURI, ts.getLineno(),
+ ts.getLine(), ts.getOffset());
+ }
+
+ @Override protected Decompiler createDecompiler(CompilerEnvirons env) {
+ return new MyDecompiler();
+ }
+
+ public static final ErrorReporter THROW_INFORMATIVE_ERRORS
+ = new ErrorReporter() {
+ public void warning(String message, String sourceURI, int line,
+ String lineText, int lineOffset) {
+ DefaultErrorReporter.instance.warning
+ (message, sourceURI, line, lineText, lineOffset);
+ }
+ public void error(String message, String sourceURI, int line,
+ String lineText, int lineOffset) {
+ DefaultErrorReporter.instance.error
+ (message, sourceURI, line, lineText, lineOffset);
+ }
+ public EvaluatorException runtimeError(String message,
+ String sourceURI,
+ int line, String lineText,
+ int lineOffset) {
+ return DefaultErrorReporter.instance.runtimeError
+ (message, sourceURI, line, lineText, lineOffset);
+ }
+
+ };
+
+ public static Parser makeParser(CompilerEnvirons compilerEnv,
+ ErrorReporter errorReporter) {
+ if (errorReporter == THROW_INFORMATIVE_ERRORS) {
+ return new InformativeParser(compilerEnv);
+ }
+ else {
+ return new Parser(compilerEnv, errorReporter);
+ }
+ }
+
+ private class MyDecompiler extends Decompiler {
+ @Override void addRegexp(String regexp, String flags) {
+ super.addRegexp(regexp, flags);
+ String str = '/'+regexp+'/'+flags;
+ info.reportPeekToken(Token.REGEXP, str, ts.getNumber(),
+ ts.getLineno(), ts.getOffset());
+ info.reportConsumeToken();
+ }
+ }
+} \ No newline at end of file
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterfaceAdapter.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterfaceAdapter.java
new file mode 100644
index 0000000..877e6a2
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterfaceAdapter.java
@@ -0,0 +1,156 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.lang.reflect.Method;
+
+/**
+ * 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 <tt>cl</tt> is not interface or
+ * has methods with different signatures.
+ */
+ static Object create(Context cx, Class cl, Callable function)
+ {
+ 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) {
+ Method[] methods = cl.getMethods();
+ if (methods.length == 0) {
+ throw Context.reportRuntimeError2(
+ "msg.no.empty.interface.conversion",
+ String.valueOf(function),
+ cl.getClass().getName());
+ }
+ boolean canCallFunction = false;
+ canCallFunctionChecks: {
+ Class[] argTypes = methods[0].getParameterTypes();
+ // check that the rest of methods has the same signature
+ for (int i = 1; i != methods.length; ++i) {
+ Class[] types2 = methods[i].getParameterTypes();
+ if (types2.length != argTypes.length) {
+ break canCallFunctionChecks;
+ }
+ for (int j = 0; j != argTypes.length; ++j) {
+ if (types2[j] != argTypes[j]) {
+ break canCallFunctionChecks;
+ }
+ }
+ }
+ canCallFunction= true;
+ }
+ if (!canCallFunction) {
+ throw Context.reportRuntimeError2(
+ "msg.no.function.interface.conversion",
+ String.valueOf(function),
+ cl.getClass().getName());
+ }
+ adapter = new InterfaceAdapter(cf, cl);
+ cache.cacheInterfaceAdapter(cl, adapter);
+ }
+ return VMBridge.instance.newInterfaceProxy(
+ adapter.proxyHelper, cf, adapter, function, topScope);
+ }
+
+ 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 Method method,
+ final Object[] args)
+ {
+ ContextAction action = new ContextAction() {
+ public Object run(Context cx)
+ {
+ return invokeImpl(cx, target, topScope, method, args);
+ }
+ };
+ return cf.call(action);
+ }
+
+ Object invokeImpl(Context cx,
+ Object target,
+ Scriptable topScope,
+ Method method,
+ Object[] args)
+ {
+ int N = (args == null) ? 0 : args.length;
+
+ Callable function = (Callable)target;
+ Scriptable thisObj = topScope;
+ Object[] jsargs = new Object[N + 1];
+ jsargs[N] = method.getName();
+ if (N != 0) {
+ WrapFactory wf = cx.getWrapFactory();
+ for (int i = 0; i != N; ++i) {
+ jsargs[i] = wf.wrap(cx, topScope, args[i], null);
+ }
+ }
+
+ Object result = function.call(cx, topScope, thisObj, jsargs);
+ Class javaResultType = method.getReturnType();
+ if (javaResultType == Void.TYPE) {
+ result = null;
+ } else {
+ result = Context.jsToJava(result, javaResultType);
+ }
+ return result;
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterpretedFunction.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterpretedFunction.java
new file mode 100644
index 0000000..db84299
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterpretedFunction.java
@@ -0,0 +1,221 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ * Bob Jervis
+ * Roger Lawrence
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import org.mozilla.javascript.debug.DebuggableScript;
+
+final class InterpretedFunction extends NativeFunction implements Script
+{
+ static final long serialVersionUID = 541475680333911468L;
+
+ InterpreterData idata;
+ SecurityController securityController;
+ Object securityDomain;
+ Scriptable[] functionRegExps;
+
+ 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.initInterpretedFunction(cx, scope);
+ 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.initInterpretedFunction(cx, scope);
+ return f;
+ }
+
+ Scriptable[] createRegExpWraps(Context cx, Scriptable scope)
+ {
+ if (idata.itsRegExpLiterals == null) Kit.codeBug();
+
+ RegExpProxy rep = ScriptRuntime.checkRegExpProxy(cx);
+ int N = idata.itsRegExpLiterals.length;
+ Scriptable[] array = new Scriptable[N];
+ for (int i = 0; i != N; ++i) {
+ array[i] = rep.wrapRegExp(cx, scope, idata.itsRegExpLiterals[i]);
+ }
+ return array;
+ }
+
+ private void initInterpretedFunction(Context cx, Scriptable scope)
+ {
+ initScriptFunction(cx, scope);
+ if (idata.itsRegExpLiterals != null) {
+ functionRegExps = createRegExpWraps(cx, scope);
+ }
+ }
+
+ 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.
+ */
+ public Object call(Context cx, Scriptable scope, Scriptable thisObj,
+ Object[] args)
+ {
+ if (!ScriptRuntime.hasTopCall(cx)) {
+ return ScriptRuntime.doTopCall(this, cx, scope, thisObj, args);
+ }
+ return Interpreter.interpret(this, cx, scope, thisObj, args);
+ }
+
+ public Object exec(Context cx, Scriptable scope)
+ {
+ if (idata.itsFunctionType != 0) {
+ // Can only be applied to scripts
+ throw new IllegalStateException();
+ }
+ if (!ScriptRuntime.hasTopCall(cx)) {
+ // It will go through "call" path. but they are equivalent
+ return ScriptRuntime.doTopCall(
+ this, cx, scope, scope, ScriptRuntime.emptyArgs);
+ }
+ return Interpreter.interpret(
+ this, cx, scope, scope, ScriptRuntime.emptyArgs);
+ }
+
+ public String getEncodedSource()
+ {
+ return Interpreter.getEncodedSource(idata);
+ }
+
+ public DebuggableScript getDebuggableView()
+ {
+ return idata;
+ }
+
+ public Object resumeGenerator(Context cx, Scriptable scope, int operation,
+ Object state, Object value)
+ {
+ return Interpreter.resumeGenerator(cx, scope, operation, state, value);
+ }
+
+ protected int getLanguageVersion()
+ {
+ return idata.languageVersion;
+ }
+
+ protected int getParamCount()
+ {
+ return idata.argCount;
+ }
+
+ protected int getParamAndVarCount()
+ {
+ return idata.argNames.length;
+ }
+
+ protected String getParamOrVarName(int index)
+ {
+ return idata.argNames[index];
+ }
+
+ protected boolean getParamOrVarConst(int index)
+ {
+ return idata.argIsConst[index];
+ }
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Interpreter.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Interpreter.java
new file mode 100644
index 0000000..a68c025
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Interpreter.java
@@ -0,0 +1,4643 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Patrick Beard
+ * Norris Boyd
+ * Igor Bukanov
+ * Ethan Hugg
+ * Bob Jervis
+ * Terry Lucas
+ * Roger Lawrence
+ * Milen Nankov
+ * Hannes Wallnoefer
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.io.PrintStream;
+import java.io.Serializable;
+import java.util.List;
+import java.util.ArrayList;
+
+import org.mozilla.javascript.continuations.Continuation;
+import org.mozilla.javascript.debug.DebugFrame;
+
+public class Interpreter implements Evaluator
+{
+
+// Additional interpreter-specific codes
+
+ private static final int
+
+ // 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 unefined
+ 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,
+
+ // Last icode
+ MIN_ICODE = -64;
+
+ // data for parsing
+
+ private CompilerEnvirons compilerEnv;
+
+ private boolean itsInFunctionFlag;
+ private boolean itsInTryFlag;
+
+ private InterpreterData itsData;
+ private ScriptOrFnNode scriptOrFn;
+ private int itsICodeTop;
+ private int itsStackDepth;
+ private int itsLineNumber;
+ private int itsDoubleTableTop;
+ private ObjToIntMap itsStrings = new ObjToIntMap(20);
+ private int itsLocalTop;
+
+ private static final int MIN_LABEL_TABLE_SIZE = 32;
+ private static final int MIN_FIXUP_TABLE_SIZE = 40;
+ private int[] itsLabelTable;
+ private int itsLabelTableTop;
+// itsFixupTable[i] = (label_index << 32) | fixup_site
+ private long[] itsFixupTable;
+ private int itsFixupTableTop;
+ private ObjArray itsLiteralIds = new ObjArray();
+
+ private int itsExceptionTableTop;
+ private static final int EXCEPTION_TRY_START_SLOT = 0;
+ private static final int EXCEPTION_TRY_END_SLOT = 1;
+ private static final int EXCEPTION_HANDLER_SLOT = 2;
+ private static final int EXCEPTION_TYPE_SLOT = 3;
+ private static final int EXCEPTION_LOCAL_SLOT = 4;
+ private static final int EXCEPTION_SCOPE_SLOT = 5;
+ // SLOT_SIZE: space for try start/end, handler, start, handler type,
+ // exception local and scope local
+ private static final int EXCEPTION_SLOT_SIZE = 6;
+
+// ECF_ or Expression Context Flags constants: for now only TAIL is available
+ private static final int ECF_TAIL = 1 << 0;
+
+ /**
+ * Class to hold data corresponding to one interpreted call stack frame.
+ */
+ private static class CallFrame implements Cloneable, Serializable
+ {
+ static final long serialVersionUID = -2843792508994958978L;
+
+ CallFrame parentFrame;
+ // amount of stack frames before this one on the interpretation stack
+ int frameIndex;
+ // If true indicates read-only frame that is a part of continuation
+ boolean frozen;
+
+ InterpretedFunction fnOrScript;
+ 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
+
+ Object[] stack;
+ int[] stackAttributes;
+ double[] sDbl;
+ CallFrame varSource; // defaults to this unless continuation frame
+ int localShift;
+ int emptyStackTop;
+
+ DebugFrame debuggerFrame;
+ boolean useActivation;
+
+ Scriptable thisObj;
+ Scriptable[] scriptRegExps;
+
+// 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 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;
+ }
+ }
+
+ private static final class ContinuationJump implements Serializable
+ {
+ static final long serialVersionUID = 7687739156004308247L;
+
+ CallFrame capturedFrame;
+ CallFrame branchFrame;
+ Object result;
+ double resultDbl;
+
+ ContinuationJump(Continuation 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);
+ }
+ }
+
+ private 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";
+ }
+
+ // icode without name
+ throw new IllegalStateException(String.valueOf(bytecode));
+ }
+
+ private static boolean validIcode(int icode)
+ {
+ return MIN_ICODE <= icode && icode <= -1;
+ }
+
+ private static boolean validTokenCode(int token)
+ {
+ return Token.FIRST_BYTECODE_TOKEN <= token
+ && token <= Token.LAST_BYTECODE_TOKEN;
+ }
+
+ private static boolean validBytecode(int bytecode)
+ {
+ return validIcode(bytecode) || validTokenCode(bytecode);
+ }
+
+ public Object compile(CompilerEnvirons compilerEnv,
+ ScriptOrFnNode tree,
+ String encodedSource,
+ boolean returnFunction)
+ {
+ this.compilerEnv = compilerEnv;
+ new NodeTransformer().transform(tree);
+
+ if (Token.printTrees) {
+ /*APPJET*///System.out.println(tree.toStringTree(tree));
+ }
+
+ if (returnFunction) {
+ tree = tree.getFunctionNode(0);
+ }
+
+ scriptOrFn = tree;
+ itsData = new InterpreterData(compilerEnv.getLanguageVersion(),
+ scriptOrFn.getSourceName(),
+ encodedSource);
+ itsData.topLevel = true;
+
+ if (returnFunction) {
+ generateFunctionICode();
+ } else {
+ generateICodeFromTree(scriptOrFn);
+ }
+
+ return itsData;
+ }
+
+ public Script createScriptObject(Object bytecode, Object staticSecurityDomain)
+ {
+ if(bytecode != itsData)
+ {
+ Kit.codeBug();
+ }
+ return InterpretedFunction.createScript(itsData,
+ staticSecurityDomain);
+ }
+
+ public void setEvalScriptFlag(Script script) {
+ ((InterpretedFunction)script).idata.evalScriptFlag = true;
+ }
+
+
+ public Function createFunctionObject(Context cx, Scriptable scope,
+ Object bytecode, Object staticSecurityDomain)
+ {
+ if(bytecode != itsData)
+ {
+ Kit.codeBug();
+ }
+ return InterpretedFunction.createFunction(cx, scope, itsData,
+ staticSecurityDomain);
+ }
+
+ private void generateFunctionICode()
+ {
+ itsInFunctionFlag = true;
+
+ FunctionNode theFunction = (FunctionNode)scriptOrFn;
+
+ itsData.itsFunctionType = theFunction.getFunctionType();
+ itsData.itsNeedsActivation = theFunction.requiresActivation();
+ itsData.itsName = theFunction.getFunctionName();
+ if (!theFunction.getIgnoreDynamicScope()) {
+ if (compilerEnv.isUseDynamicScope()) {
+ itsData.useDynamicScope = true;
+ }
+ }
+ if (theFunction.isGenerator()) {
+ addIcode(Icode_GENERATOR);
+ addUint16(theFunction.getBaseLineno() & 0xFFFF);
+ }
+
+ generateICodeFromTree(theFunction.getLastChild());
+ }
+
+ private void generateICodeFromTree(Node tree)
+ {
+ generateNestedFunctions();
+
+ generateRegExpLiterals();
+
+ 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 != itsICodeTop) {
+ // Make itsData.itsICode length exactly itsICodeTop to save memory
+ // and catch bugs with jumps beyond icode as early as possible
+ byte[] tmp = new byte[itsICodeTop];
+ System.arraycopy(itsData.itsICode, 0, tmp, 0, itsICodeTop);
+ itsData.itsICode = tmp;
+ }
+ if (itsStrings.size() == 0) {
+ itsData.itsStringTable = null;
+ } else {
+ itsData.itsStringTable = new String[itsStrings.size()];
+ ObjToIntMap.Iterator iter = itsStrings.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 (itsDoubleTableTop == 0) {
+ itsData.itsDoubleTable = null;
+ } else if (itsData.itsDoubleTable.length != itsDoubleTableTop) {
+ double[] tmp = new double[itsDoubleTableTop];
+ System.arraycopy(itsData.itsDoubleTable, 0, tmp, 0,
+ itsDoubleTableTop);
+ itsData.itsDoubleTable = tmp;
+ }
+ if (itsExceptionTableTop != 0
+ && itsData.itsExceptionTable.length != itsExceptionTableTop)
+ {
+ int[] tmp = new int[itsExceptionTableTop];
+ System.arraycopy(itsData.itsExceptionTable, 0, tmp, 0,
+ itsExceptionTableTop);
+ 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 (itsLiteralIds.size() != 0) {
+ itsData.literalIds = itsLiteralIds.toArray();
+ }
+
+ if (Token.printICode) 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 def = scriptOrFn.getFunctionNode(i);
+ Interpreter jsi = new Interpreter();
+ jsi.compilerEnv = compilerEnv;
+ jsi.scriptOrFn = def;
+ jsi.itsData = new InterpreterData(itsData);
+ jsi.generateFunctionICode();
+ array[i] = jsi.itsData;
+ }
+ 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 updateLineNumber(Node node)
+ {
+ int lineno = node.getLineno();
+ if (lineno != itsLineNumber && lineno >= 0) {
+ if (itsData.firstLinePC < 0) {
+ itsData.firstLinePC = lineno;
+ }
+ itsLineNumber = lineno;
+ addIcode(Icode_LINE);
+ addUint16(lineno & 0xFFFF);
+ }
+ }
+
+ private 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);
+ case Token.SCRIPT:
+ // fall through
+ 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 (Node.Jump caseNode = (Node.Jump)child.getNext();
+ caseNode != null;
+ caseNode = (Node.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 = ((Node.Jump)node).target;
+ visitExpression(child, 0);
+ addGoto(target, type);
+ stackChange(-1);
+ }
+ break;
+
+ case Token.GOTO:
+ {
+ Node target = ((Node.Jump)node).target;
+ addGoto(target, type);
+ }
+ break;
+
+ case Token.JSR:
+ {
+ Node target = ((Node.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:
+ {
+ Node.Jump tryNode = (Node.Jump)node;
+ int exceptionObjectLocal = getLocalBlockRef(tryNode);
+ int scopeLocal = allocLocal();
+
+ addIndexOp(Icode_SCOPE_SAVE, scopeLocal);
+
+ int tryStart = itsICodeTop;
+ 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
+ = itsLabelTable[getTargetLabel(catchTarget)];
+ addExceptionHandler(
+ tryStart, catchStartPC, catchStartPC,
+ false, exceptionObjectLocal, scopeLocal);
+ }
+ Node finallyTarget = tryNode.getFinally();
+ if (finallyTarget != null) {
+ int finallyStartPC
+ = itsLabelTable[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(itsLineNumber & 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) {
+ // We're in a generator, so change RETURN to GENERATOR_END
+ addIcode(Icode_GENERATOR_END);
+ addUint16(itsLineNumber & 0xFFFF);
+ } else if (child != null) {
+ visitExpression(child, ECF_TAIL);
+ addToken(Token.RETURN);
+ stackChange(-1);
+ } else {
+ addIcode(Icode_RETUNDEF);
+ }
+ 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:
+ visitExpression(child, 0);
+ addIndexOp(type, getLocalBlockRef(node));
+ stackChange(-1);
+ break;
+
+ case Icode_GENERATOR:
+ break;
+
+ default:
+ throw badTree(node);
+ }
+
+ if (itsStackDepth != initialStackDepth) {
+ throw Kit.codeBug();
+ }
+ }
+
+ private void visitExpression(Node node, int contextFlags)
+ {
+ int type = node.getType();
+ Node child = node.getFirstChild();
+ int savedStackDepth = itsStackDepth;
+ 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) {
+ 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 (callType != Node.NON_SPECIALCALL) {
+ // embed line number and source filename
+ addIndexOp(Icode_CALLSPECIAL, argCount);
+ addUint8(callType);
+ addUint8(type == Token.NEW ? 1 : 0);
+ addUint16(itsLineNumber & 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 = itsICodeTop;
+ 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 = itsICodeTop;
+ addGotoOp(Token.IFNE);
+ stackChange(-1);
+ // Preserve tail context flag if any
+ visitExpression(ifThen, contextFlags & ECF_TAIL);
+ int afterElseJumpStart = itsICodeTop;
+ addGotoOp(Token.GOTO);
+ resolveForwardGoto(elseJumpStart);
+ itsStackDepth = 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.GETELEM:
+ case Token.DELPROP:
+ 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.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.SETNAME:
+ {
+ String name = child.getString();
+ visitExpression(child, 0);
+ child = child.getNext();
+ visitExpression(child, 0);
+ addStringOp(Token.SETNAME, 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.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 = itsICodeTop;
+ 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:
+ if (child != null) {
+ visitExpression(child, 0);
+ } else {
+ addIcode(Icode_UNDEF);
+ stackChange(1);
+ }
+ addToken(Token.YIELD);
+ 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;
+ }
+
+ default:
+ throw badTree(node);
+ }
+ if (savedStackDepth + 1 != itsStackDepth) {
+ 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.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 {
+ 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 = itsLiteralIds.size();
+ itsLiteralIds.add(skipIndexes);
+ addIndexOp(Icode_SPARE_ARRAYLIT, index);
+ }
+ } else {
+ int index = itsLiteralIds.size();
+ itsLiteralIds.add(propertyIds);
+ addIndexOp(Token.OBJECTLIT, 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, itsStackDepth);
+ visitExpression(expr, 0);
+ }
+
+ private 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 = itsLabelTableTop;
+ if (itsLabelTable == null || label == 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, label);
+ itsLabelTable = tmp;
+ }
+ }
+ itsLabelTableTop = label + 1;
+ itsLabelTable[label] = -1;
+
+ target.labelId(label);
+ return label;
+ }
+
+ private void markTargetLabel(Node target)
+ {
+ int label = getTargetLabel(target);
+ if (itsLabelTable[label] != -1) {
+ // Can mark label only once
+ Kit.codeBug();
+ }
+ itsLabelTable[label] = itsICodeTop;
+ }
+
+ private void addGoto(Node target, int gotoOp)
+ {
+ int label = getTargetLabel(target);
+ if (!(label < itsLabelTableTop)) Kit.codeBug();
+ int targetPC = itsLabelTable[label];
+
+ if (targetPC != -1) {
+ addBackwardGoto(gotoOp, targetPC);
+ } else {
+ int gotoPC = itsICodeTop;
+ addGotoOp(gotoOp);
+ 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) | gotoPC;
+ }
+ }
+
+ private void fixLabelGotos()
+ {
+ for (int i = 0; i < itsFixupTableTop; i++) {
+ long fixup = itsFixupTable[i];
+ int label = (int)(fixup >> 32);
+ int jumpSource = (int)fixup;
+ int pc = itsLabelTable[label];
+ if (pc == -1) {
+ // Unlocated label
+ throw Kit.codeBug();
+ }
+ resolveGoto(jumpSource, pc);
+ }
+ itsFixupTableTop = 0;
+ }
+
+ private void addBackwardGoto(int gotoOp, int jumpPC)
+ {
+ int fromPC = itsICodeTop;
+ // 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 (itsICodeTop < fromPC + 3) throw Kit.codeBug();
+ resolveGoto(fromPC, itsICodeTop);
+ }
+
+ 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 (!validTokenCode(token)) throw Kit.codeBug();
+ addUint8(token);
+ }
+
+ private void addIcode(int icode)
+ {
+ if (!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 = itsICodeTop;
+ if (top == array.length) {
+ array = increaseICodeCapacity(1);
+ }
+ array[top] = (byte)value;
+ itsICodeTop = top + 1;
+ }
+
+ private void addUint16(int value)
+ {
+ if ((value & ~0xFFFF) != 0) throw Kit.codeBug();
+ byte[] array = itsData.itsICode;
+ int top = itsICodeTop;
+ if (top + 2 > array.length) {
+ array = increaseICodeCapacity(2);
+ }
+ array[top] = (byte)(value >>> 8);
+ array[top + 1] = (byte)value;
+ itsICodeTop = top + 2;
+ }
+
+ private void addInt(int i)
+ {
+ byte[] array = itsData.itsICode;
+ int top = itsICodeTop;
+ 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;
+ itsICodeTop = top + 4;
+ }
+
+ private int getDoubleIndex(double num)
+ {
+ int index = itsDoubleTableTop;
+ 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;
+ itsDoubleTableTop = index + 1;
+ return index;
+ }
+
+ private void addGotoOp(int gotoOp)
+ {
+ byte[] array = itsData.itsICode;
+ int top = itsICodeTop;
+ if (top + 3 > array.length) {
+ array = increaseICodeCapacity(3);
+ }
+ array[top] = (byte)gotoOp;
+ // Offset would written later
+ itsICodeTop = 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 (validIcode(op)) {
+ addIcode(op);
+ } else {
+ addToken(op);
+ }
+ }
+
+ private void addIndexOp(int op, int index)
+ {
+ addIndexPrefix(index);
+ if (validIcode(op)) {
+ addIcode(op);
+ } else {
+ addToken(op);
+ }
+ }
+
+ private void addStringPrefix(String str)
+ {
+ int index = itsStrings.get(str, -1);
+ if (index == -1) {
+ index = itsStrings.size();
+ itsStrings.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 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 = itsExceptionTableTop;
+ int[] table = itsData.itsExceptionTable;
+ if (table == null) {
+ if (top != 0) Kit.codeBug();
+ table = new int[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 + EXCEPTION_TRY_START_SLOT] = icodeStart;
+ table[top + EXCEPTION_TRY_END_SLOT] = icodeEnd;
+ table[top + EXCEPTION_HANDLER_SLOT] = handlerStart;
+ table[top + EXCEPTION_TYPE_SLOT] = isFinally ? 1 : 0;
+ table[top + EXCEPTION_LOCAL_SLOT] = exceptionObjectLocal;
+ table[top + EXCEPTION_SCOPE_SLOT] = scopeLocal;
+
+ itsExceptionTableTop = top + EXCEPTION_SLOT_SIZE;
+ }
+
+ private byte[] increaseICodeCapacity(int extraSize)
+ {
+ int capacity = itsData.itsICode.length;
+ int top = itsICodeTop;
+ 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) {
+ itsStackDepth += change;
+ } else {
+ int newDepth = itsStackDepth + change;
+ if (newDepth > itsData.itsMaxStack) {
+ itsData.itsMaxStack = newDepth;
+ }
+ itsStackDepth = newDepth;
+ }
+ }
+
+ private int allocLocal()
+ {
+ int localSlot = itsLocalTop;
+ ++itsLocalTop;
+ if (itsLocalTop > itsData.itsMaxLocals) {
+ itsData.itsMaxLocals = itsLocalTop;
+ }
+ return localSlot;
+ }
+
+ private void releaseLocal(int localSlot)
+ {
+ --itsLocalTop;
+ if (localSlot != itsLocalTop) Kit.codeBug();
+ }
+
+ 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;
+ }
+
+ private static void dumpICode(InterpreterData idata)
+ {
+ if (!Token.printICode) {
+ return;
+ }
+
+ byte iCode[] = idata.itsICode;
+ int iCodeLength = iCode.length;
+ String[] strings = idata.itsStringTable;
+ 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 = 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_GENERATOR :
+ case Icode_GENERATOR_END :
+ {
+ 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;
+ }
+ 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];
+ int scopeLocal = table[i + EXCEPTION_SCOPE_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_GENERATOR:
+ case Icode_GENERATOR_END:
+ // 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();
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ public String getPatchedStack(RhinoException ex,
+ String nativeStackTrace)
+ {
+ String tag = "org.mozilla.javascript.Interpreter.interpretLoop";
+ StringBuffer sb = new StringBuffer(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();
+ }
+
+ public List getScriptStack(RhinoException ex)
+ {
+ if (ex.interpreterStackInfo == null) {
+ return null;
+ }
+
+ List list = new ArrayList();
+ String lineSeparator =
+ SecurityUtilities.getSystemProperty("line.separator");
+
+ CallFrame[] array = (CallFrame[])ex.interpreterStackInfo;
+ int[] linePC = ex.interpreterLineData;
+ int arrayIndex = array.length;
+ int linePCIndex = linePC.length;
+ while (arrayIndex != 0) {
+ --arrayIndex;
+ StringBuffer sb = new StringBuffer();
+ CallFrame frame = array[arrayIndex];
+ while (frame != null) {
+ if (linePCIndex == 0) Kit.codeBug();
+ --linePCIndex;
+ InterpreterData idata = frame.idata;
+ sb.append("\tat ");
+ 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));
+ }
+ if (idata.itsName != null && idata.itsName.length() != 0) {
+ sb.append(" (");
+ sb.append(idata.itsName);
+ sb.append(')');
+ }
+ sb.append(lineSeparator);
+ frame = frame.parentFrame;
+ }
+ list.add(sb.toString());
+ }
+ return list;
+ }
+
+ 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 = new CallFrame();
+ initFrame(cx, scope, thisObj, args, null, 0, args.length,
+ ifun, null, frame);
+
+ 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(Continuation c, Context cx,
+ Scriptable scope, Object[] args)
+ {
+ if (!ScriptRuntime.hasTopCall(cx)) {
+ return ScriptRuntime.doTopCall(c, cx, scope, null, args);
+ }
+
+ 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 = UniqueTag.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;
+ 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 throable == null and indexReg == -1 allows to
+ // catch bugs with using indeReg to access array eleemnts 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;
+
+ // 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;
+ NativeGenerator generator = new NativeGenerator(frame.scope,
+ generatorFrame.fnOrScript, generatorFrame);
+ frame.result = generator;
+ break Loop;
+ } else {
+ // We are now resuming execution. Fall through to YIELD case.
+ }
+ }
+ // fall through...
+ case Token.YIELD: {
+ if (!frame.frozen) {
+ return freezeGenerator(cx, frame, stackTop, generatorState);
+ } else {
+ 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 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;
+ Object rhs = stack[stackTop + 1];
+ Object lhs = stack[stackTop];
+ boolean valBln;
+ object_compare:
+ {
+ number_compare:
+ {
+ double rDbl, lDbl;
+ if (rhs == DBL_MRK) {
+ rDbl = sDbl[stackTop + 1];
+ lDbl = stack_double(frame, stackTop);
+ } else if (lhs == DBL_MRK) {
+ rDbl = ScriptRuntime.toNumber(rhs);
+ lDbl = sDbl[stackTop];
+ } else {
+ break number_compare;
+ }
+ switch (op) {
+ case Token.GE:
+ valBln = (lDbl >= rDbl);
+ break object_compare;
+ case Token.LE:
+ valBln = (lDbl <= rDbl);
+ break object_compare;
+ case Token.GT:
+ valBln = (lDbl > rDbl);
+ break object_compare;
+ case Token.LT:
+ valBln = (lDbl < rDbl);
+ break object_compare;
+ default:
+ throw Kit.codeBug();
+ }
+ }
+ switch (op) {
+ case Token.GE:
+ valBln = ScriptRuntime.cmp_LE(rhs, lhs);
+ break;
+ case Token.LE:
+ valBln = ScriptRuntime.cmp_LE(lhs, rhs);
+ break;
+ case Token.GT:
+ valBln = ScriptRuntime.cmp_LT(rhs, lhs);
+ break;
+ case Token.LT:
+ valBln = ScriptRuntime.cmp_LT(lhs, rhs);
+ break;
+ default:
+ throw Kit.codeBug();
+ }
+ }
+ stack[stackTop] = ScriptRuntime.wrapBoolean(valBln);
+ continue Loop;
+ }
+ case Token.IN :
+ case Token.INSTANCEOF : {
+ 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]);
+ boolean valBln;
+ if (op == Token.IN) {
+ valBln = ScriptRuntime.in(lhs, rhs, cx);
+ } else {
+ valBln = ScriptRuntime.instanceOf(lhs, rhs, cx);
+ }
+ stack[stackTop] = ScriptRuntime.wrapBoolean(valBln);
+ continue Loop;
+ }
+ case Token.EQ :
+ case Token.NE : {
+ --stackTop;
+ boolean valBln;
+ Object rhs = stack[stackTop + 1];
+ Object lhs = stack[stackTop];
+ if (rhs == DBL_MRK) {
+ if (lhs == DBL_MRK) {
+ valBln = (sDbl[stackTop] == sDbl[stackTop + 1]);
+ } else {
+ valBln = ScriptRuntime.eqNumber(sDbl[stackTop + 1], lhs);
+ }
+ } else {
+ if (lhs == DBL_MRK) {
+ valBln = ScriptRuntime.eqNumber(sDbl[stackTop], rhs);
+ } else {
+ valBln = ScriptRuntime.eq(lhs, rhs);
+ }
+ }
+ valBln ^= (op == Token.NE);
+ stack[stackTop] = ScriptRuntime.wrapBoolean(valBln);
+ continue Loop;
+ }
+ case Token.SHEQ :
+ case Token.SHNE : {
+ --stackTop;
+ Object rhs = stack[stackTop + 1];
+ Object lhs = stack[stackTop];
+ boolean valBln;
+ shallow_compare: {
+ double rdbl, ldbl;
+ if (rhs == DBL_MRK) {
+ rdbl = sDbl[stackTop + 1];
+ if (lhs == DBL_MRK) {
+ ldbl = sDbl[stackTop];
+ } else if (lhs instanceof Number) {
+ ldbl = ((Number)lhs).doubleValue();
+ } else {
+ valBln = false;
+ break shallow_compare;
+ }
+ } else if (lhs == DBL_MRK) {
+ ldbl = sDbl[stackTop];
+ if (rhs == DBL_MRK) {
+ rdbl = sDbl[stackTop + 1];
+ } else if (rhs instanceof Number) {
+ rdbl = ((Number)rhs).doubleValue();
+ } else {
+ valBln = false;
+ break shallow_compare;
+ }
+ } else {
+ valBln = ScriptRuntime.shallowEq(lhs, rhs);
+ break shallow_compare;
+ }
+ valBln = (ldbl == rdbl);
+ }
+ 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 : {
+ int rIntValue = stack_int32(frame, stackTop);
+ stack[stackTop] = DBL_MRK;
+ sDbl[stackTop] = ~rIntValue;
+ continue Loop;
+ }
+ case Token.BITAND :
+ case Token.BITOR :
+ case Token.BITXOR :
+ case Token.LSH :
+ case Token.RSH : {
+ int lIntValue = stack_int32(frame, stackTop-1);
+ int rIntValue = stack_int32(frame, stackTop);
+ stack[--stackTop] = DBL_MRK;
+ switch (op) {
+ case Token.BITAND:
+ lIntValue &= rIntValue;
+ break;
+ case Token.BITOR:
+ lIntValue |= rIntValue;
+ break;
+ case Token.BITXOR:
+ lIntValue ^= rIntValue;
+ break;
+ case Token.LSH:
+ lIntValue <<= rIntValue;
+ break;
+ case Token.RSH:
+ lIntValue >>= rIntValue;
+ break;
+ }
+ sDbl[stackTop] = lIntValue;
+ 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.NEG :
+ case Token.POS : {
+ double rDbl = stack_double(frame, stackTop);
+ stack[stackTop] = DBL_MRK;
+ if (op == Token.NEG) {
+ rDbl = -rDbl;
+ }
+ sDbl[stackTop] = rDbl;
+ continue Loop;
+ }
+ case Token.ADD :
+ --stackTop;
+ do_add(stack, sDbl, stackTop, cx);
+ continue Loop;
+ case Token.SUB :
+ case Token.MUL :
+ case Token.DIV :
+ case Token.MOD : {
+ double rDbl = stack_double(frame, stackTop);
+ --stackTop;
+ double lDbl = stack_double(frame, stackTop);
+ stack[stackTop] = DBL_MRK;
+ switch (op) {
+ case Token.SUB:
+ lDbl -= rDbl;
+ break;
+ case Token.MUL:
+ lDbl *= rDbl;
+ break;
+ case Token.DIV:
+ lDbl /= rDbl;
+ break;
+ case Token.MOD:
+ lDbl %= rDbl;
+ break;
+ }
+ sDbl[stackTop] = lDbl;
+ 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.SETNAME : {
+ Object rhs = stack[stackTop];
+ if (rhs == DBL_MRK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+ --stackTop;
+ Scriptable lhs = (Scriptable)stack[stackTop];
+ stack[stackTop] = ScriptRuntime.setName(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 : {
+ 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.delete(lhs, rhs, cx);
+ 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);
+ 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);
+ 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);
+ 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, iCode[frame.pc]);
+ ++frame.pc;
+ continue Loop;
+ }
+ case Token.GETELEM : {
+ --stackTop;
+ Object lhs = stack[stackTop];
+ if (lhs == DBL_MRK) {
+ lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+ }
+ Object value;
+ Object id = stack[stackTop + 1];
+ if (id != DBL_MRK) {
+ value = ScriptRuntime.getObjectElem(lhs, id, cx);
+ } else {
+ double d = sDbl[stackTop + 1];
+ value = ScriptRuntime.getObjectIndex(lhs, d, cx);
+ }
+ stack[stackTop] = value;
+ continue Loop;
+ }
+ case Token.SETELEM : {
+ stackTop -= 2;
+ Object rhs = stack[stackTop + 2];
+ if (rhs == DBL_MRK) {
+ rhs = ScriptRuntime.wrapNumber(sDbl[stackTop + 2]);
+ }
+ Object lhs = stack[stackTop];
+ if (lhs == DBL_MRK) {
+ lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+ }
+ Object value;
+ Object id = stack[stackTop + 1];
+ if (id != DBL_MRK) {
+ value = ScriptRuntime.setObjectElem(lhs, id, rhs, cx);
+ } else {
+ double d = sDbl[stackTop + 1];
+ value = ScriptRuntime.setObjectIndex(lhs, d, rhs, cx);
+ }
+ stack[stackTop] = value;
+ continue Loop;
+ }
+ case Icode_ELEM_INC_DEC: {
+ 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.elemIncrDecr(lhs, rhs, cx,
+ iCode[frame.pc]);
+ ++frame.pc;
+ 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);
+ 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, 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);
+ ++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);
+ 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;
+ }
+ 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 == DBL_MRK)
+ 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;
+ 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;
+ CallFrame calleeFrame = new CallFrame();
+ 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);
+ }
+ initFrame(cx, calleeScope, funThisObj, stack, sDbl,
+ stackTop + 2, indexReg, ifun, callParentFrame,
+ calleeFrame);
+ if (op != Icode_TAIL_CALL) {
+ frame.savedStackTop = stackTop;
+ frame.savedCallOp = op;
+ }
+ frame = calleeFrame;
+ continue StateLoop;
+ }
+ }
+
+ if (fun instanceof Continuation) {
+ // Jump to the captured continuation
+ ContinuationJump cjump;
+ cjump = new ContinuationJump((Continuation)fun, frame);
+
+ // continuation result is the first argument if any
+ // of contination 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 (Continuation.isContinuationConstructor(ifun)) {
+ captureContinuation(cx, frame, stackTop);
+ 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;
+ }
+ }
+ }
+ }
+
+ 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 = new CallFrame();
+ initFrame(cx, frame.scope, newInstance, stack, sDbl,
+ stackTop + 1, indexReg, f, frame,
+ calleeFrame);
+
+ 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 (Continuation.isContinuationConstructor(ifun)) {
+ captureContinuation(cx, frame, stackTop);
+ 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.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 :
+ if (!frame.useActivation) {
+ if ((varAttributes[indexReg] & ScriptableObject.READONLY) == 0) {
+ throw Context.reportRuntimeError1("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 == DBL_MRK) val = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+ 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();
+ }
+ continue Loop;
+ case Icode_SETVAR1:
+ indexReg = iCode[frame.pc++];
+ // fallthrough
+ case Token.SETVAR :
+ if (!frame.useActivation) {
+ if ((varAttributes[indexReg] & ScriptableObject.READONLY) == 0) {
+ vars[indexReg] = stack[stackTop];
+ varDbls[indexReg] = sDbl[stackTop];
+ }
+ } else {
+ Object val = stack[stackTop];
+ if (val == DBL_MRK) val = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+ stringReg = frame.idata.argNames[indexReg];
+ frame.scope.put(stringReg, frame.scope, val);
+ }
+ continue Loop;
+ case Icode_GETVAR1:
+ indexReg = iCode[frame.pc++];
+ // fallthrough
+ case Token.GETVAR :
+ ++stackTop;
+ if (!frame.useActivation) {
+ stack[stackTop] = vars[indexReg];
+ sDbl[stackTop] = varDbls[indexReg];
+ } else {
+ stringReg = frame.idata.argNames[indexReg];
+ stack[stackTop] = frame.scope.get(stringReg, frame.scope);
+ }
+ continue Loop;
+ case Icode_VAR_INC_DEC : {
+ // indexReg : varindex
+ ++stackTop;
+ int incrDecrMask = iCode[frame.pc];
+ if (!frame.useActivation) {
+ stack[stackTop] = DBL_MRK;
+ Object varValue = vars[indexReg];
+ double d;
+ if (varValue == DBL_MRK) {
+ d = varDbls[indexReg];
+ } else {
+ d = ScriptRuntime.toNumber(varValue);
+ vars[indexReg] = DBL_MRK;
+ }
+ double d2 = ((incrDecrMask & Node.DECR_FLAG) == 0)
+ ? d + 1.0 : d - 1.0;
+ varDbls[indexReg] = d2;
+ sDbl[stackTop] = ((incrDecrMask & Node.POST_FLAG) == 0) ? d2 : d;
+ } else {
+ String varName = frame.idata.argNames[indexReg];
+ stack[stackTop] = ScriptRuntime.nameIncrDecr(frame.scope, varName,
+ cx, incrDecrMask);
+ }
+ ++frame.pc;
+ 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 : {
+ 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 :
+ ScriptRuntime.ENUMERATE_ARRAY;
+ stack[indexReg] = ScriptRuntime.enumInit(lhs, cx, 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);
+ continue Loop;
+ }
+ case Token.REF_MEMBER: {
+ //indexReg: flags
+ Object elem = stack[stackTop];
+ if (elem == DBL_MRK) elem = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+ --stackTop;
+ Object obj = stack[stackTop];
+ if (obj == DBL_MRK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+ stack[stackTop] = ScriptRuntime.memberRef(obj, elem, cx, indexReg);
+ continue Loop;
+ }
+ case Token.REF_NS_MEMBER: {
+ //indexReg: flags
+ Object elem = stack[stackTop];
+ if (elem == DBL_MRK) elem = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+ --stackTop;
+ Object ns = stack[stackTop];
+ if (ns == DBL_MRK) ns = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+ --stackTop;
+ Object obj = stack[stackTop];
+ if (obj == DBL_MRK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+ stack[stackTop] = ScriptRuntime.memberRef(obj, ns, elem, cx, 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
+ Object name = stack[stackTop];
+ if (name == DBL_MRK) name = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+ --stackTop;
+ Object ns = stack[stackTop];
+ if (ns == DBL_MRK) ns = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+ stack[stackTop] = ScriptRuntime.nameRef(ns, name, cx, frame.scope,
+ 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 :
+ stack[++stackTop] = InterpretedFunction.createFunction(cx, frame.scope,
+ frame.fnOrScript,
+ indexReg);
+ continue Loop;
+ case Icode_CLOSURE_STMT :
+ initFunction(cx, frame.scope, frame.fnOrScript, indexReg);
+ continue Loop;
+ case Token.REGEXP :
+ stack[++stackTop] = frame.scriptRegExps[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);
+ }
+ break 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;
+ 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 RuntimeException) {
+ exState = cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)
+ ? EX_CATCH_STATE
+ : EX_FINALLY_STATE;
+ } 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 {
+ 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;
+ } else {
+ // Must be instance of Error or code bug
+ throw (Error)throwable;
+ }
+ }
+
+ return (interpreterResult != DBL_MRK)
+ ? interpreterResult
+ : ScriptRuntime.wrapNumber(interpreterResultDbl);
+ }
+
+ 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 (isFrameEnterExitRequired(x)) {
+ 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)
+ {
+ if (generatorState.operation == NativeGenerator.GENERATOR_CLOSE) {
+ // Error: no yields when generator is closing
+ throw ScriptRuntime.typeError0("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);
+ return (frame.result != UniqueTag.DOUBLE_MARK)
+ ? frame.result
+ : ScriptRuntime.wrapNumber(frame.resultDbl);
+ }
+
+ 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 <generator>.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)
+ 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) {
+ applyThis = ScriptRuntime.toObjectOrNull(cx, stack[stackTop + 2]);
+ }
+ 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;
+ }
+ CallFrame calleeFrame = new CallFrame();
+ if(BaseFunction.isApply(ifun)) {
+ Object[] callArgs = indexReg < 2 ? ScriptRuntime.emptyArgs :
+ ScriptRuntime.getApplyArguments(cx, stack[stackTop + 3]);
+ initFrame(cx, calleeScope, applyThis, callArgs, null, 0,
+ callArgs.length, iApplyCallable, frame, calleeFrame);
+ }
+ 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;
+ initFrame(cx, calleeScope, applyThis, stack, sDbl, stackTop + 2,
+ argCount, iApplyCallable, frame, calleeFrame);
+ }
+
+ frame = calleeFrame;
+ return frame;
+ }
+
+ private static void initFrame(Context cx, Scriptable callerScope,
+ Scriptable thisObj,
+ Object[] args, double[] argsDbl,
+ int argShift, int argCount,
+ InterpretedFunction fnOrScript,
+ CallFrame parentFrame, CallFrame frame)
+ {
+ InterpreterData idata = fnOrScript.idata;
+
+ boolean useActivation = idata.itsNeedsActivation;
+ DebugFrame debuggerFrame = null;
+ if (cx.debugger != null) {
+ debuggerFrame = cx.debugger.getFrame(cx, idata);
+ if (debuggerFrame != null) {
+ useActivation = true;
+ }
+ }
+
+ 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;
+ }
+
+ Scriptable scope;
+ if (idata.itsFunctionType != 0) {
+ if (!idata.useDynamicScope) {
+ scope = fnOrScript.getParentScope();
+ } else {
+ scope = callerScope;
+ }
+
+ if (useActivation) {
+ scope = ScriptRuntime.createFunctionActivation(
+ fnOrScript, scope, args);
+ }
+ } 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);
+ }
+ }
+ }
+
+ Scriptable[] scriptRegExps = null;
+ if (idata.itsRegExpLiterals != null) {
+ // Wrapped regexps for functions are stored in
+ // InterpretedFunction
+ // but for script which should not contain references to scope
+ // the regexps re-wrapped during each script execution
+ if (idata.itsFunctionType != 0) {
+ scriptRegExps = fnOrScript.functionRegExps;
+ } else {
+ scriptRegExps = fnOrScript.createRegExpWraps(cx, scope);
+ }
+ }
+
+ // Initialize args, vars, locals and stack
+
+ int emptyStackTop = idata.itsMaxVars + idata.itsMaxLocals - 1;
+ int maxFrameArray = idata.itsMaxFrameArray;
+ if (maxFrameArray != emptyStackTop + idata.itsMaxStack + 1)
+ Kit.codeBug();
+
+ Object[] stack;
+ int[] stackAttributes;
+ double[] sDbl;
+ boolean stackReuse;
+ if (frame.stack != null && maxFrameArray <= frame.stack.length) {
+ // Reuse stacks from old frame
+ stackReuse = true;
+ stack = frame.stack;
+ stackAttributes = frame.stackAttributes;
+ sDbl = frame.sDbl;
+ } else {
+ stackReuse = false;
+ 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
+
+ frame.parentFrame = parentFrame;
+ frame.frameIndex = (parentFrame == null)
+ ? 0 : parentFrame.frameIndex + 1;
+ if(frame.frameIndex > cx.getMaximumInterpreterStackDepth())
+ {
+ throw Context.reportRuntimeError("Exceeded maximum stack depth");
+ }
+ frame.frozen = false;
+
+ frame.fnOrScript = fnOrScript;
+ frame.idata = idata;
+
+ frame.stack = stack;
+ frame.stackAttributes = stackAttributes;
+ frame.sDbl = sDbl;
+ frame.varSource = frame;
+ frame.localShift = idata.itsMaxVars;
+ frame.emptyStackTop = emptyStackTop;
+
+ frame.debuggerFrame = debuggerFrame;
+ frame.useActivation = useActivation;
+
+ frame.thisObj = thisObj;
+ frame.scriptRegExps = scriptRegExps;
+
+ // Initialize initial values of variables that change during
+ // interpretation.
+ frame.result = Undefined.instance;
+ frame.pc = 0;
+ frame.pcPrevBranch = 0;
+ frame.pcSourceLineStart = idata.firstLinePC;
+ frame.scope = scope;
+
+ frame.savedStackTop = emptyStackTop;
+ frame.savedCallOp = 0;
+
+ 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;
+ }
+ if (stackReuse) {
+ // Clean the stack part and space beyond stack if any
+ // of the old array to allow to GC objects there
+ for (int i = emptyStackTop + 1; i != stack.length; ++i) {
+ stack[i] = null;
+ }
+ }
+
+ enterFrame(cx, frame, args, false);
+ }
+
+ private static boolean isFrameEnterExitRequired(CallFrame frame)
+ {
+ return frame.debuggerFrame != null || frame.idata.itsNeedsActivation;
+ }
+
+ 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 == UniqueTag.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;
+ }
+
+ private static void captureContinuation(Context cx, CallFrame frame,
+ int stackTop)
+ {
+ Continuation c = new Continuation();
+ ScriptRuntime.setObjectProtoAndParent(
+ c, ScriptRuntime.getTopCallScope(cx));
+
+ // Make sure that all frames upstack frames are frozen
+ CallFrame x = frame.parentFrame;
+ 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
+ }
+ x = x.parentFrame;
+ }
+
+ c.initImplementation(frame.parentFrame);
+ frame.stack[stackTop] = c;
+ }
+
+ private static int stack_int32(CallFrame frame, int i)
+ {
+ Object x = frame.stack[i];
+ double value;
+ if (x == UniqueTag.DOUBLE_MARK) {
+ value = frame.sDbl[i];
+ } else {
+ value = ScriptRuntime.toNumber(x);
+ }
+ return ScriptRuntime.toInt32(value);
+ }
+
+ private static double stack_double(CallFrame frame, int i)
+ {
+ Object x = frame.stack[i];
+ if (x != UniqueTag.DOUBLE_MARK) {
+ return ScriptRuntime.toNumber(x);
+ } else {
+ return frame.sDbl[i];
+ }
+ }
+
+ private static boolean stack_boolean(CallFrame frame, int i)
+ {
+ Object x = frame.stack[i];
+ if (x == Boolean.TRUE) {
+ return true;
+ } else if (x == Boolean.FALSE) {
+ return false;
+ } else if (x == UniqueTag.DOUBLE_MARK) {
+ double d = frame.sDbl[i];
+ return d == d && d != 0.0;
+ } else if (x == null || x == Undefined.instance) {
+ return false;
+ } else if (x instanceof Number) {
+ double d = ((Number)x).doubleValue();
+ return (d == d && d != 0.0);
+ } else if (x instanceof Boolean) {
+ return ((Boolean)x).booleanValue();
+ } else {
+ return ScriptRuntime.toBoolean(x);
+ }
+ }
+
+ private static void do_add(Object[] stack, double[] sDbl, int stackTop,
+ Context cx)
+ {
+ Object rhs = stack[stackTop + 1];
+ Object lhs = stack[stackTop];
+ double d;
+ boolean leftRightOrder;
+ if (rhs == UniqueTag.DOUBLE_MARK) {
+ d = sDbl[stackTop + 1];
+ if (lhs == UniqueTag.DOUBLE_MARK) {
+ sDbl[stackTop] += d;
+ return;
+ }
+ leftRightOrder = true;
+ // fallthrough to object + number code
+ } else if (lhs == UniqueTag.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);
+ } else if (lhs instanceof String) {
+ String lstr = (String)lhs;
+ String rstr = ScriptRuntime.toString(rhs);
+ stack[stackTop] = lstr.concat(rstr);
+ } else if (rhs instanceof String) {
+ String lstr = ScriptRuntime.toString(lhs);
+ String rstr = (String)rhs;
+ stack[stackTop] = lstr.concat(rstr);
+ } else {
+ double lDbl = (lhs instanceof Number)
+ ? ((Number)lhs).doubleValue() : ScriptRuntime.toNumber(lhs);
+ double rDbl = (rhs instanceof Number)
+ ? ((Number)rhs).doubleValue() : ScriptRuntime.toNumber(rhs);
+ stack[stackTop] = UniqueTag.DOUBLE_MARK;
+ sDbl[stackTop] = lDbl + rDbl;
+ }
+ 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 String) {
+ String lstr = (String)lhs;
+ String rstr = ScriptRuntime.toString(d);
+ if (leftRightOrder) {
+ stack[stackTop] = lstr.concat(rstr);
+ } else {
+ stack[stackTop] = rstr.concat(lstr);
+ }
+ } else {
+ double lDbl = (lhs instanceof Number)
+ ? ((Number)lhs).doubleValue() : ScriptRuntime.toNumber(lhs);
+ stack[stackTop] = UniqueTag.DOUBLE_MARK;
+ sDbl[stackTop] = lDbl + d;
+ }
+ }
+
+ 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/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterpreterData.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterpreterData.java
new file mode 100644
index 0000000..7435b10
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterpreterData.java
@@ -0,0 +1,192 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Bob Jervis
+ * Roger Lawrence
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.io.Serializable;
+
+import org.mozilla.javascript.debug.DebuggableScript;
+
+final class InterpreterData implements Serializable, DebuggableScript
+{
+ 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;
+
+ InterpreterData(int languageVersion,
+ String sourceFile, String encodedSource)
+ {
+ this.languageVersion = languageVersion;
+ this.itsSourceFile = sourceFile;
+ this.encodedSource = encodedSource;
+
+ init();
+ }
+
+ InterpreterData(InterpreterData parent)
+ {
+ this.parentData = parent;
+ this.languageVersion = parent.languageVersion;
+ this.itsSourceFile = parent.itsSourceFile;
+ this.encodedSource = parent.encodedSource;
+
+ init();
+ }
+
+ private void init()
+ {
+ itsICode = new byte[INITIAL_MAX_ICODE_LENGTH];
+ itsStringTable = new String[INITIAL_STRINGTABLE_SIZE];
+ }
+
+ String itsName;
+ String itsSourceFile;
+ boolean itsNeedsActivation;
+ int itsFunctionType;
+
+ String[] itsStringTable;
+ double[] itsDoubleTable;
+ InterpreterData[] itsNestedFunctions;
+ Object[] itsRegExpLiterals;
+
+ 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 useDynamicScope;
+
+ boolean topLevel;
+
+ Object[] literalIds;
+
+ UintMap longJumps;
+
+ int firstLinePC = -1; // PC for the first LINE icode
+
+ InterpreterData parentData;
+
+ boolean evalScriptFlag; // true if script corresponds to eval() code
+
+ public boolean isTopLevel()
+ {
+ return topLevel;
+ }
+
+ public boolean isFunction()
+ {
+ return itsFunctionType != 0;
+ }
+
+ public String getFunctionName()
+ {
+ return itsName;
+ }
+
+ public int getParamCount()
+ {
+ return argCount;
+ }
+
+ public int getParamAndVarCount()
+ {
+ return argNames.length;
+ }
+
+ public String getParamOrVarName(int index)
+ {
+ return argNames[index];
+ }
+
+ public boolean getParamOrVarConst(int index)
+ {
+ return argIsConst[index];
+ }
+
+ public String getSourceName()
+ {
+ return itsSourceFile;
+ }
+
+ public boolean isGeneratedScript()
+ {
+ return ScriptRuntime.isGeneratedScript(itsSourceFile);
+ }
+
+ public int[] getLineNumbers()
+ {
+ return Interpreter.getLineNumbers(this);
+ }
+
+ public int getFunctionCount()
+ {
+ return (itsNestedFunctions == null) ? 0 : itsNestedFunctions.length;
+ }
+
+ public DebuggableScript getFunction(int index)
+ {
+ return itsNestedFunctions[index];
+ }
+
+ public DebuggableScript getParent()
+ {
+ return parentData;
+ }
+
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaAdapter.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaAdapter.java
new file mode 100644
index 0000000..6e0a827
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaAdapter.java
@@ -0,0 +1,1129 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Patrick Beard
+ * Norris Boyd
+ * Igor Bukanov
+ * Mike McCabe
+ * Matthias Radestock
+ * Andi Vajda
+ * Andrew Wason
+ * Kemal Bayram
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import org.mozilla.classfile.*;
+import java.lang.reflect.*;
+import java.io.*;
+import java.security.*;
+import java.util.*;
+
+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;
+ }
+
+ 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 != names.get(name, arity + 1))
+ return false;
+ }
+ return true;
+ }
+
+ public int hashCode()
+ {
+ return superClass.hashCode()
+ | (0x9e3779b9 * (names.size() | (interfaces.length << 16)));
+ }
+ }
+
+ 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();
+ }
+
+ 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.typeError0("msg.adapter.zero.args");
+ }
+
+ Class superClass = null;
+ Class[] intfs = new Class[N - 1];
+ int interfaceCount = 0;
+ for (int i = 0; i != N - 1; ++i) {
+ Object arg = args[i];
+ if (!(arg instanceof NativeJavaClass)) {
+ throw ScriptRuntime.typeError2("msg.not.java.class.arg",
+ String.valueOf(i),
+ ScriptRuntime.toString(arg));
+ }
+ Class c = ((NativeJavaClass) arg).getClassObject();
+ if (!c.isInterface()) {
+ if (superClass != null) {
+ throw ScriptRuntime.typeError2("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);
+ Scriptable obj = ScriptRuntime.toObject(cx, scope, args[N - 1]);
+
+ Class adapterClass = getAdapterClass(scope, superClass, interfaces,
+ obj);
+
+ Class[] ctorParms = {
+ ScriptRuntime.ContextFactoryClass,
+ ScriptRuntime.ScriptableClass
+ };
+ Object[] ctorArgs = { cx.getFactory(), obj };
+ try {
+ Object adapter = adapterClass.getConstructor(ctorParms).
+ newInstance(ctorArgs);
+ return getAdapterSelf(adapterClass, adapter);
+ } 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<JavaAdapterSignature,Class<?>> 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(),
+ "<adapter>");
+ 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('.', '/');
+ generateCtor(cfw, adapterName, superName);
+ 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)) {
+ 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());
+ 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());
+ 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);
+ }
+ return cfw.toByteArray();
+ }
+
+ static Method[] getOverridableMethods(Class c)
+ {
+ ArrayList<Method> list = new ArrayList<Method>();
+ HashSet<String> skip = new HashSet<String>();
+ while (c != null) {
+ Method[] methods = 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);
+ }
+ }
+ c = c.getSuperclass();
+ }
+ return list.toArray(new Method[list.size()]);
+ }
+
+ static Class loadAdapterClass(String className, byte[] classBytes)
+ {
+ Object staticDomain;
+ Class domainClass = SecurityController.getStaticSecurityDomainClass();
+ if(domainClass == CodeSource.class || domainClass == ProtectionDomain.class) {
+ ProtectionDomain 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 Undefined.instance;
+ }
+ 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);
+ } else {
+ return factory.call(new ContextAction() {
+ public Object run(Context cx)
+ {
+ return doCall(cx, 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 (Scriptable)Context.call(new ContextAction() {
+ public Object run(Context cx)
+ {
+ ScriptableObject global = ScriptRuntime.getGlobal(cx);
+ script.exec(cx, global);
+ return global;
+ }
+ });
+ }
+
+ private static void generateCtor(ClassFileWriter cfw, String adapterName,
+ String superName)
+ {
+ cfw.startMethod("<init>",
+ "(Lorg/mozilla/javascript/ContextFactory;"
+ +"Lorg/mozilla/javascript/Scriptable;)V",
+ ClassFileWriter.ACC_PUBLIC);
+
+ // Invoke base class constructor
+ cfw.add(ByteCode.ALOAD_0); // this
+ cfw.addInvoke(ByteCode.INVOKESPECIAL, superName, "<init>", "()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;");
+
+ 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_2); // 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((short)3); // 3: this + factory + delegee
+ }
+
+ private static void generateSerialCtor(ClassFileWriter cfw,
+ String adapterName,
+ String superName)
+ {
+ cfw.startMethod("<init>",
+ "(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, "<init>", "()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); // second 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("<init>", "()V", ClassFileWriter.ACC_PUBLIC);
+
+ // Invoke base class constructor
+ cfw.add(ByteCode.ALOAD_0); // this
+ cfw.addInvoke(ByteCode.INVOKESPECIAL, superName, "<init>", "()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, "<init>", "()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",
+ "<init>", "(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",
+ "<init>", "(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.toString());
+ }
+
+ } 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)
+ {
+ StringBuffer sb = new StringBuffer();
+ 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.reportRuntimeError0(
+ "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, true);
+
+ 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 (int i = 0; i < parms.length; i++) {
+ paramOffset += generatePushParam(cfw, paramOffset, parms[i]);
+ }
+
+ // 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)
+ {
+ StringBuffer sb = new StringBuffer();
+ appendMethodSignature(argTypes, method.getReturnType(), sb);
+ return sb.toString();
+ }
+
+ static int appendMethodSignature(Class[] argTypes,
+ Class returnType,
+ StringBuffer sb)
+ {
+ sb.append('(');
+ int firstLocal = 1 + argTypes.length; // includes this.
+ for (int i = 0; i < argTypes.length; i++) {
+ Class type = argTypes[i];
+ appendTypeString(sb, type);
+ if (type == Long.TYPE || type == Double.TYPE) {
+ // adjust for duble slot
+ ++firstLocal;
+ }
+ }
+ sb.append(')');
+ appendTypeString(sb, returnType);
+ return firstLocal;
+ }
+
+ private static StringBuffer appendTypeString(StringBuffer 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 = new Object();
+ private static final int Id_JavaAdapter = 1;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaMembers.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaMembers.java
new file mode 100644
index 0000000..84ef2d4
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaMembers.java
@@ -0,0 +1,935 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Cameron McCormack
+ * Frank Mitchell
+ * Mike Shaver
+ * Kurt Westerfeld
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+/**
+ *
+ * @author Mike Shaver
+ * @author Norris Boyd
+ * @see NativeJavaObject
+ * @see NativeJavaClass
+ */
+class JavaMembers
+{
+ 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.reportRuntimeError1("msg.access.prohibited",
+ cl.getName());
+ }
+ this.includePrivate = cx.hasFeature(
+ Context.FEATURE_ENHANCED_JAVA_ACCESS);
+ this.members = new Hashtable(23);
+ this.staticMembers = new Hashtable(7);
+ this.cl = cl;
+ reflect(scope, includeProtected);
+ } finally {
+ Context.exit();
+ }
+ }
+
+ boolean has(String name, boolean isStatic)
+ {
+ Hashtable 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)
+ {
+ Hashtable 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)
+ {
+ Hashtable 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.reportRuntimeError1(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.reportRuntimeError3(
+ "msg.java.internal.field.type",
+ value.getClass().getName(), field,
+ javaObject.getClass().getName());
+ }
+ }
+ }
+
+ Object[] getIds(boolean isStatic)
+ {
+ Hashtable ht = isStatic ? staticMembers : members;
+ int len = ht.size();
+ Object[] result = new Object[len];
+ Enumeration keys = ht.keys();
+ for (int i=0; i < len; i++)
+ result[i] = keys.nextElement();
+ return result;
+ }
+
+ static String javaSignature(Class type)
+ {
+ if (!type.isArray()) {
+ return type.getName();
+ } else {
+ int arrayDimension = 0;
+ do {
+ ++arrayDimension;
+ type = type.getComponentType();
+ } while (type.isArray());
+ String name = type.getName();
+ String suffix = "[]";
+ if (arrayDimension == 1) {
+ return name.concat(suffix);
+ } else {
+ int length = name.length() + arrayDimension * suffix.length();
+ StringBuffer sb = new StringBuffer(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 "()"; }
+ StringBuffer sb = new StringBuffer();
+ 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; }
+
+ Hashtable ht = isStatic ? staticMembers : members;
+ MemberBox[] methodsOrCtors = null;
+ boolean isCtor = (isStatic && sigStart == 0);
+
+ if (isCtor) {
+ // Explicit request for an overloaded constructor
+ methodsOrCtors = ctors;
+ } 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 (int i = 0; i < methodsOrCtors.length; i++) {
+ Class[] type = methodsOrCtors[i].argTypes;
+ String sig = liveConnectSignature(type);
+ if (sigStart + sig.length() == name.length()
+ && name.regionMatches(sigStart, sig, 0, sig.length()))
+ {
+ return methodsOrCtors[i];
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private Object getExplicitFunction(Scriptable scope, String name,
+ Object javaObject, boolean isStatic)
+ {
+ Hashtable 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 static Method[] discoverAccessibleMethods(Class clazz,
+ boolean includeProtected,
+ boolean includePrivate)
+ {
+ Map map = new HashMap();
+ discoverAccessibleMethods(clazz, map, includeProtected, includePrivate);
+ return (Method[])map.values().toArray(new Method[map.size()]);
+ }
+
+ private static void discoverAccessibleMethods(Class clazz, Map map,
+ boolean includeProtected,
+ boolean includePrivate)
+ {
+ if (Modifier.isPublic(clazz.getModifiers()) || includePrivate) {
+ try {
+ if (includeProtected || includePrivate) {
+ while (clazz != null) {
+ try {
+ Method[] methods = clazz.getDeclaredMethods();
+ for (int i = 0; i < methods.length; i++) {
+ Method method = methods[i];
+ int mods = method.getModifiers();
+
+ if (Modifier.isPublic(mods) ||
+ Modifier.isProtected(mods) ||
+ includePrivate)
+ {
+ if (includePrivate)
+ method.setAccessible(true);
+ map.put(new MethodSignature(method), method);
+ }
+ }
+ 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 (int i = 0; i < methods.length; i++) {
+ Method method = methods[i];
+ MethodSignature sig
+ = new MethodSignature(method);
+ if (map.get(sig) == null)
+ map.put(sig, method);
+ }
+ break; // getMethods gets superclass methods, no
+ // need to loop any more
+ }
+ }
+ } else {
+ Method[] methods = clazz.getMethods();
+ for (int i = 0; i < methods.length; i++) {
+ Method method = methods[i];
+ MethodSignature sig = new MethodSignature(method);
+ map.put(sig, method);
+ }
+ }
+ 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 (int i = 0; i < interfaces.length; i++) {
+ discoverAccessibleMethods(interfaces[i], map, includeProtected,
+ includePrivate);
+ }
+ Class superclass = clazz.getSuperclass();
+ if (superclass != null) {
+ discoverAccessibleMethods(superclass, map, includeProtected,
+ includePrivate);
+ }
+ }
+
+ private 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());
+ }
+
+ 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;
+ }
+
+ public int hashCode()
+ {
+ return name.hashCode() ^ args.length;
+ }
+ }
+
+ private void reflect(Scriptable scope, boolean includeProtected)
+ {
+ // 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 (int i = 0; i < methods.length; i++) {
+ Method method = methods[i];
+ int mods = method.getModifiers();
+ boolean isStatic = Modifier.isStatic(mods);
+ Hashtable ht = isStatic ? staticMembers : members;
+ String name = method.getName();
+ 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);
+ Hashtable ht = (isStatic) ? staticMembers : members;
+ Enumeration e = ht.keys();
+ while (e.hasMoreElements()) {
+ String name = (String)e.nextElement();
+ MemberBox[] methodBoxes;
+ Object value = ht.get(name);
+ 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(name, fun);
+ }
+ }
+
+ // Reflect fields.
+ Field[] fields = getAccessibleFields();
+ for (int i = 0; i < fields.length; i++) {
+ Field field = fields[i];
+ String name = field.getName();
+ int mods = field.getModifiers();
+ if (!includePrivate && !Modifier.isPublic(mods)) {
+ continue;
+ }
+ try {
+ boolean isStatic = Modifier.isStatic(mods);
+ Hashtable 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);
+ Hashtable fmht = isStatic ? staticFieldAndMethods
+ : fieldAndMethods;
+ if (fmht == null) {
+ fmht = new Hashtable(4);
+ 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 propeties 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);
+ Hashtable ht = (isStatic) ? staticMembers : members;
+
+ Hashtable toAdd = new Hashtable();
+
+ // Now, For each member, make "bean" properties.
+ for (Enumeration e = ht.keys(); e.hasMoreElements(); ) {
+
+ // Is this a getter?
+ String name = (String) e.nextElement();
+ 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 (ht.containsKey(beanPropertyName)
+ || toAdd.containsKey(beanPropertyName)) {
+ 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.
+ for (Enumeration e = toAdd.keys(); e.hasMoreElements();) {
+ Object key = e.nextElement();
+ Object value = toAdd.get(key);
+ ht.put(key, value);
+ }
+ }
+
+ // Reflect constructors
+ Constructor[] constructors = getAccessibleConstructors();
+ ctors = new MemberBox[constructors.length];
+ for (int i = 0; i != constructors.length; ++i) {
+ ctors[i] = new MemberBox(constructors[i]);
+ }
+ }
+
+ private Constructor[] getAccessibleConstructors()
+ {
+ // 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();
+ Constructor.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() {
+ if (includePrivate) {
+ try {
+ ArrayList 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 (int i = 0; i < declared.length; i++) {
+ declared[i].setAccessible(true);
+ fieldsList.add(declared[i]);
+ }
+ // walk up superclass chain. no need to deal specially with
+ // interfaces, since they can't have fields
+ currentClass = currentClass.getSuperclass();
+ }
+
+ return (Field[]) fieldsList.toArray(
+ new Field[fieldsList.size()]);
+ } catch (SecurityException e) {
+ // fall through to !includePrivate case
+ }
+ }
+ return cl.getFields();
+ }
+
+ private MemberBox findGetter(boolean isStatic, Hashtable 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 (int methodIdx = 0; methodIdx < methods.length; methodIdx++) {
+ MemberBox method = methods[methodIdx];
+ // 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 (int i = 0; i < methods.length; ++i) {
+ MemberBox method = methods[i];
+ 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 (int i = 0; i < methods.length; ++i) {
+ MemberBox method = methods[i];
+ if (!isStatic || method.isStatic()) {
+ if (method.method().getReturnType() == Void.TYPE) {
+ if (method.argTypes.length == 1) {
+ return method;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ Hashtable getFieldAndMethodsObjects(Scriptable scope, Object javaObject,
+ boolean isStatic)
+ {
+ Hashtable ht = isStatic ? staticFieldAndMethods : fieldAndMethods;
+ if (ht == null)
+ return null;
+ int len = ht.size();
+ Hashtable result = new Hashtable(len);
+ Enumeration e = ht.elements();
+ while (len-- > 0) {
+ FieldAndMethods fam = (FieldAndMethods) e.nextElement();
+ 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;
+ scope = ScriptableObject.getTopLevelScope(scope);
+ ClassCache cache = ClassCache.get(scope);
+ Map<Class<?>,JavaMembers> ct = cache.getClassCacheMap();
+
+ Class cl = dynamicType;
+ for (;;) {
+ members = ct.get(cl);
+ if (members != null) {
+ return members;
+ }
+ try {
+ members = new JavaMembers(scope, 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(cl, members);
+ return members;
+ }
+
+ RuntimeException reportMemberNotFound(String memberName)
+ {
+ return Context.reportRuntimeError2(
+ "msg.java.member.not.found", cl.getName(), memberName);
+ }
+
+ private Class cl;
+ private Hashtable members;
+ private Hashtable fieldAndMethods;
+ private Hashtable staticMembers;
+ private Hashtable staticFieldAndMethods;
+ MemberBox[] ctors;
+ private boolean includePrivate;
+}
+
+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
+{
+ static final long serialVersionUID = -9222428244284796755L;
+
+ FieldAndMethods(Scriptable scope, MemberBox[] methods, Field field)
+ {
+ super(methods);
+ this.field = field;
+ setParentScope(scope);
+ setPrototype(ScriptableObject.getFunctionPrototype(scope));
+ }
+
+ 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.reportRuntimeError1(
+ "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/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaScriptException.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaScriptException.java
new file mode 100644
index 0000000..11ebedf
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaScriptException.java
@@ -0,0 +1,117 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Bojan Cekrlic
+ * Hannes Wallnoefer
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// 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
+{
+ static final long serialVersionUID = -7666130513694669293L;
+
+ /**
+ * @deprecated
+ * Use {@link WrappedException#WrappedException(Throwable)} to report
+ * exceptions in Java code.
+ */
+ 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;
+ }
+
+ public String details()
+ {
+ try {
+ return ScriptRuntime.toString(value);
+ } catch (RuntimeException rte) {
+ // ScriptRuntime.toString may throw a RuntimeException
+ if (value == null) {
+ return "null";
+ } else if (value instanceof Scriptable) {
+ return ScriptRuntime.defaultObjectToString((Scriptable)value);
+ } else {
+ return value.toString();
+ }
+ }
+ }
+
+ /**
+ * @return the value wrapped by this exception
+ */
+ public Object getValue()
+ {
+ return value;
+ }
+
+ /**
+ * @deprecated Use {@link RhinoException#sourceName()} from the super class.
+ */
+ public String getSourceName()
+ {
+ return sourceName();
+ }
+
+ /**
+ * @deprecated Use {@link RhinoException#lineNumber()} from the super class.
+ */
+ public int getLineNumber()
+ {
+ return lineNumber();
+ }
+
+ private Object value;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Kit.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Kit.java
new file mode 100644
index 0000000..f7b4cad
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Kit.java
@@ -0,0 +1,486 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov, igor@fastmail.fm
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.lang.reflect.Method;
+import java.util.Hashtable;
+
+/**
+ * Collection of utilities
+ */
+
+public class Kit
+{
+ /**
+ * Reflection of Throwable.initCause(Throwable) from JDK 1.4
+ * or nul if it is not available.
+ */
+ private static Method Throwable_initCause = null;
+
+ static {
+ // Are we running on a JDK 1.4 or later system?
+ try {
+ Class ThrowableClass = Kit.classOrNull("java.lang.Throwable");
+ Class[] signature = { ThrowableClass };
+ Throwable_initCause
+ = ThrowableClass.getMethod("initCause", signature);
+ } catch (Exception ex) {
+ // Assume any exceptions means the method does not exist.
+ }
+ }
+
+ 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;
+ }
+
+ 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 accesible 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 initCause methods exists in Throwable, call
+ * <tt>ex.initCause(cause)</tt> or otherwise do nothing.
+ * @return The <tt>ex</tt> argument.
+ */
+ public static RuntimeException initCause(RuntimeException ex,
+ Throwable cause)
+ {
+ if (Throwable_initCause != null) {
+ Object[] args = { cause };
+ try {
+ Throwable_initCause.invoke(ex, args);
+ } catch (Exception e) {
+ // Ignore any exceptions
+ }
+ }
+ return ex;
+ }
+
+ /**
+ * Split string into array of strings using semicolon as string terminator
+ * (; after the last string is required).
+ */
+ public static String[] semicolonSplit(String s)
+ {
+ String[] array = null;
+ for (;;) {
+ // loop 2 times: first to count semicolons and then to fill array
+ int count = 0;
+ int cursor = 0;
+ for (;;) {
+ int next = s.indexOf(';', cursor);
+ if (next < 0) {
+ break;
+ }
+ if (array != null) {
+ array[count] = s.substring(cursor, next);
+ }
+ ++count;
+ cursor = next + 1;
+ }
+ // after the last semicolon
+ if (array == null) {
+ // array size counting state:
+ // check for required terminating ';'
+ if (cursor != s.length())
+ throw new IllegalArgumentException();
+ array = new String[count];
+ } else {
+ // array filling state: stop the loop
+ break;
+ }
+ }
+ return array;
+ }
+
+ /**
+ * If character <tt>c</tt> is a hexadecimal digit, return
+ * <tt>accumulator</tt> * 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 <i>listener</i> to <i>bag</i> of listeners.
+ * The function does not modify <i>bag</i> and return a new collection
+ * containing <i>listener</i> and all listeners from <i>bag</i>.
+ * Bag without listeners always represented as the null value.
+ * <p>
+ * Usage example:
+ * <pre>
+ * 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);
+ * }
+ * }
+ * }
+ * </pre>
+ *
+ * @param listener Listener to add to <i>bag</i>
+ * @param bag Current collection of listeners.
+ * @return A new bag containing all listeners from <i>bag</i> and
+ * <i>listener</i>.
+ * @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 <i>listener</i> from <i>bag</i> of listeners.
+ * The function does not modify <i>bag</i> and return a new collection
+ * containing all listeners from <i>bag</i> except <i>listener</i>.
+ * If <i>bag</i> does not contain <i>listener</i>, the function returns
+ * <i>bag</i>.
+ * <p>
+ * For usage example, see {@link #addListener(Object bag, Object listener)}.
+ *
+ * @param listener Listener to remove from <i>bag</i>
+ * @param bag Current collection of listeners.
+ * @return A new bag containing all listeners from <i>bag</i> except
+ * <i>listener</i>.
+ * @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 <i>index</i> position in <i>bag</i> or null if
+ * <i>index</i> equals to number of listeners in <i>bag</i>.
+ * <p>
+ * 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(Hashtable 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 final static class ComplexKey
+ {
+ private Object key1;
+ private Object key2;
+ private int hash;
+
+ ComplexKey(Object key1, Object key2)
+ {
+ this.key1 = key1;
+ this.key2 = key2;
+ }
+
+ public boolean equals(Object anotherObj)
+ {
+ if (!(anotherObj instanceof ComplexKey))
+ return false;
+ ComplexKey another = (ComplexKey)anotherObj;
+ return key1.equals(another.key1) && key2.equals(another.key2);
+ }
+
+ 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 r)
+ throws IOException
+ {
+ char[] buffer = new char[512];
+ int cursor = 0;
+ for (;;) {
+ int n = r.read(buffer, cursor, buffer.length - cursor);
+ if (n < 0) { break; }
+ cursor += n;
+ if (cursor == buffer.length) {
+ char[] tmp = new char[buffer.length * 2];
+ System.arraycopy(buffer, 0, tmp, 0, cursor);
+ buffer = tmp;
+ }
+ }
+ return new String(buffer, 0, cursor);
+ }
+
+ 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) {
+ byte[] tmp = new byte[buffer.length * 2];
+ System.arraycopy(buffer, 0, tmp, 0, cursor);
+ buffer = tmp;
+ }
+ }
+ 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 <tt>throw Kit.codeBug()</tt> if plain
+ * <tt>Kit.codeBug()</tt> 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;
+ }
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/LazilyLoadedCtor.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/LazilyLoadedCtor.java
new file mode 100644
index 0000000..4153372
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/LazilyLoadedCtor.java
@@ -0,0 +1,136 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.lang.reflect.*;
+
+/**
+ * Avoid loading classes unless they are used.
+ *
+ * <p> This improves startup time and average memory usage.
+ */
+public final class LazilyLoadedCtor implements java.io.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 Object initializedValue;
+ private int state;
+
+ public LazilyLoadedCtor(ScriptableObject scope, String propertyName,
+ String className, boolean sealed)
+ {
+
+ this.scope = scope;
+ this.propertyName = propertyName;
+ this.className = className;
+ this.sealed = sealed;
+ 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()
+ {
+ Class cl = Kit.classOrNull(className);
+ if (cl != null) {
+ try {
+ Object value = ScriptableObject.buildClassCtor(scope, cl,
+ sealed, false);
+ if (value != null) {
+ return value;
+ }
+ else {
+ // 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;
+ }
+
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/MemberBox.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/MemberBox.java
new file mode 100644
index 0000000..2d3553f
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/MemberBox.java
@@ -0,0 +1,362 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ * Felix Meschberger
+ * Norris Boyd
+ * Ulrike Mueller <umueller@demandware.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.lang.reflect.*;
+import java.io.*;
+
+/**
+ * Wrappper 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
+{
+ static final long serialVersionUID = 6358550398665688245L;
+
+ private transient Member memberObject;
+ transient Class[] argTypes;
+ transient Object delegateTo;
+ transient boolean vararg;
+
+
+ MemberBox(Method method)
+ {
+ init(method);
+ }
+
+ MemberBox(Constructor constructor)
+ {
+ init(constructor);
+ }
+
+ private void init(Method method)
+ {
+ this.memberObject = method;
+ this.argTypes = method.getParameterTypes();
+ this.vararg = VMBridge.instance.isVarArgs(method);
+ }
+
+ private void init(Constructor constructor)
+ {
+ this.memberObject = constructor;
+ this.argTypes = constructor.getParameterTypes();
+ this.vararg = VMBridge.instance.isVarArgs(constructor);
+ }
+
+ 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());
+ }
+
+ String getName()
+ {
+ return memberObject.getName();
+ }
+
+ Class getDeclaringClass()
+ {
+ return memberObject.getDeclaringClass();
+ }
+
+ String toJavaDeclaration()
+ {
+ StringBuffer sb = new StringBuffer();
+ 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();
+ }
+
+ public String toString()
+ {
+ return memberObject.toString();
+ }
+
+ Object invoke(Object target, Object[] args)
+ {
+ Method method = method();
+ 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 (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);
+ } else {
+ 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/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeArray.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeArray.java
new file mode 100644
index 0000000..b170ff4
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeArray.java
@@ -0,0 +1,1727 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Mike McCabe
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.util.Arrays;
+
+/**
+ * This class implements the Array native object.
+ * @author Norris Boyd
+ * @author Mike McCabe
+ */
+public class NativeArray extends IdScriptableObject
+{
+ 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 = new Object();
+ private static final Integer NEGATIVE_ONE = new Integer(-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;
+ }
+
+ public String getClassName()
+ {
+ return "Array";
+ }
+
+ private static final int
+ Id_length = 1,
+ MAX_INSTANCE_ID = 1;
+
+ protected int getMaxInstanceId()
+ {
+ return MAX_INSTANCE_ID;
+ }
+
+ protected int findInstanceIdInfo(String s)
+ {
+ if (s.equals("length")) {
+ return instanceIdInfo(DONTENUM | PERMANENT, Id_length);
+ }
+ return super.findInstanceIdInfo(s);
+ }
+
+ protected String getInstanceIdName(int id)
+ {
+ if (id == Id_length) { return "length"; }
+ return super.getInstanceIdName(id);
+ }
+
+ protected Object getInstanceIdValue(int id)
+ {
+ if (id == Id_length) {
+ return ScriptRuntime.wrapNumber(length);
+ }
+ return super.getInstanceIdValue(id);
+ }
+
+ protected void setInstanceIdValue(int id, Object value)
+ {
+ if (id == Id_length) {
+ setLength(value); return;
+ }
+ super.setInstanceIdValue(id, value);
+ }
+
+ protected void fillConstructorProperties(IdFunctionObject ctor)
+ {
+ addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_join,
+ "join", 2);
+ addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_reverse,
+ "reverse", 1);
+ addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_sort,
+ "sort", 2);
+ addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_push,
+ "push", 2);
+ addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_pop,
+ "pop", 2);
+ addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_shift,
+ "shift", 2);
+ addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_unshift,
+ "unshift", 2);
+ addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_splice,
+ "splice", 2);
+ addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_concat,
+ "concat", 2);
+ addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_slice,
+ "slice", 2);
+ addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_indexOf,
+ "indexOf", 2);
+ addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_lastIndexOf,
+ "lastIndexOf", 2);
+ addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_every,
+ "every", 2);
+ addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_filter,
+ "filter", 2);
+ addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_forEach,
+ "forEach", 2);
+ addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_map,
+ "map", 2);
+ addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_some,
+ "some", 2);
+ super.fillConstructorProperties(ctor);
+ }
+
+ 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=1; 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=1; s="pop"; break;
+ case Id_shift: arity=1; s="shift"; break;
+ case Id_unshift: arity=1; s="unshift"; break;
+ case Id_splice: arity=1; s="splice"; break;
+ case Id_concat: arity=1; s="concat"; break;
+ case Id_slice: arity=1; 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;
+ default: throw new IllegalArgumentException(String.valueOf(id));
+ }
+ initPrototypeMethod(ARRAY_TAG, id, s, arity);
+ }
+
+ 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: {
+ thisObj = ScriptRuntime.toObject(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 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, thisObj, args);
+
+ case Id_reverse:
+ return js_reverse(cx, thisObj, args);
+
+ case Id_sort:
+ return js_sort(cx, scope, thisObj, args);
+
+ case Id_push:
+ return js_push(cx, thisObj, args);
+
+ case Id_pop:
+ return js_pop(cx, thisObj, args);
+
+ case Id_shift:
+ return js_shift(cx, thisObj, args);
+
+ case Id_unshift:
+ return js_unshift(cx, 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, thisObj, args);
+
+ case Id_indexOf:
+ return indexOfHelper(cx, thisObj, args, false);
+
+ case Id_lastIndexOf:
+ return indexOfHelper(cx, thisObj, args, true);
+
+ case Id_every:
+ case Id_filter:
+ case Id_forEach:
+ case Id_map:
+ case Id_some:
+ return iterativeMethod(cx, id, scope, thisObj, args);
+ }
+ throw new IllegalArgumentException(String.valueOf(id));
+ }
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ // if id is an array index (ECMA 15.4.0), return the number,
+ // otherwise return -1L
+ private static long toArrayIndex(String id)
+ {
+ double d = ScriptRuntime.toNumber(id);
+ if (d == d) {
+ long index = ScriptRuntime.toUint32(d);
+ if (index == d && index != 4294967295L) {
+ // 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;
+ }
+
+ 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;
+ 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;
+ }
+
+ public void put(int index, Scriptable start, Object value)
+ {
+ if (start == this && !isSealed() && dense != null && 0 <= index &&
+ (denseOnly || !isGetterOrSetter(null, index, true)))
+ {
+ if (index < dense.length) {
+ dense[index] = value;
+ if (this.length <= index)
+ this.length = (long)index + 1;
+ return;
+ } else if (denseOnly && index < dense.length * GROW_FACTOR &&
+ ensureCapacity(index+1))
+ {
+ dense[index] = value;
+ this.length = (long)index + 1;
+ return;
+ } else {
+ denseOnly = false;
+ }
+ }
+ super.put(index, start, value);
+ if (start == this) {
+ // 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;
+ }
+ }
+ }
+
+ 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);
+ }
+ }
+
+ public Object[] getIds()
+ {
+ Object[] superIds = super.getIds();
+ 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] = new Integer(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 Object getDefaultValue(Class hint)
+ {
+ if (hint == ScriptRuntime.NumberClass) {
+ Context cx = Context.getContext();
+ if (cx.getLanguageVersion() == Context.VERSION_1_2)
+ return new Long(length);
+ }
+ return super.getDefaultValue(hint);
+ }
+
+ /**
+ * 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);
+ } else {
+ Object arg0 = args[0];
+ if (args.length > 1 || !(arg0 instanceof Number)) {
+ return new NativeArray(args);
+ } else {
+ long len = ScriptRuntime.toUint32(arg0);
+ if (len != ((Number)arg0).doubleValue())
+ throw Context.reportRuntimeError0("msg.arraylength.bad");
+ return new NativeArray(len);
+ }
+ }
+ }
+
+ public long getLength() {
+ return length;
+ }
+
+ /** @deprecated Use {@link #getLength()} instead. */
+ 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.
+ * ?
+ */
+
+ double d = ScriptRuntime.toNumber(val);
+ long longVal = ScriptRuntime.toUint32(d);
+ if (longVal != d)
+ throw Context.reportRuntimeError0("msg.arraylength.bad");
+
+ if (denseOnly) {
+ if (longVal < length) {
+ // downcast okay because denseOnly
+ Arrays.fill(dense, (int) longVal, dense.length, NOT_FOUND);
+ length = longVal;
+ return;
+ } else if (longVal < MAX_PRE_GROW_SIZE &&
+ longVal < (length * GROW_FACTOR) &&
+ ensureCapacity((int)longVal))
+ {
+ length = longVal;
+ 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;
+ }
+
+ /* 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 both give numeric lengths within Uint32 range.
+ if (obj instanceof NativeString) {
+ return ((NativeString)obj).getLength();
+ } else if (obj instanceof NativeArray) {
+ return ((NativeArray)obj).getLength();
+ }
+ return ScriptRuntime.toUint32(
+ ScriptRuntime.getObjectProp(obj, "length", cx));
+ }
+
+ private static Object setLengthProperty(Context cx, Scriptable target,
+ long length)
+ {
+ return ScriptRuntime.setObjectProp(
+ target, "length", ScriptRuntime.wrapNumber(length), cx);
+ }
+
+ /* 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)
+ {
+ if (index > Integer.MAX_VALUE) {
+ String id = Long.toString(index);
+ return ScriptRuntime.getObjectProp(target, id, cx);
+ } else {
+ return ScriptRuntime.getObjectIndex(target, (int)index, cx);
+ }
+ }
+
+ private static void setElem(Context cx, Scriptable target, long index,
+ Object value)
+ {
+ if (index > Integer.MAX_VALUE) {
+ String id = Long.toString(index);
+ ScriptRuntime.setObjectProp(target, id, value, cx);
+ } else {
+ ScriptRuntime.setObjectIndex(target, (int)index, value, cx);
+ }
+ }
+
+ private static String toStringHelper(Context cx, Scriptable scope,
+ Scriptable thisObj,
+ boolean toSource, boolean toLocale)
+ {
+ /* It's probably redundant to handle long lengths in this
+ * function; StringBuffers are limited to 2^31 in java.
+ */
+
+ long length = getLengthProperty(cx, thisObj);
+
+ StringBuffer result = new StringBuffer(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(thisObj);
+ }
+
+ // Make sure cx.iterating is set to null when done
+ // so we don't leak memory
+ try {
+ if (!iterating) {
+ cx.iterating.put(thisObj, 0); // stop recursion.
+ for (i = 0; i < length; i++) {
+ if (i > 0) result.append(separator);
+ Object elem = getElem(cx, thisObj, i);
+ if (elem == null || elem == Undefined.instance) {
+ haslast = false;
+ continue;
+ }
+ haslast = true;
+
+ if (toSource) {
+ result.append(ScriptRuntime.uneval(cx, scope, elem));
+
+ } else if (elem instanceof String) {
+ String s = (String)elem;
+ if (toSource) {
+ result.append('\"');
+ result.append(ScriptRuntime.escapeString(s));
+ result.append('\"');
+ } else {
+ result.append(s);
+ }
+
+ } else {
+ if (toLocale)
+ {
+ Callable fun;
+ Scriptable funThis;
+ fun = ScriptRuntime.getPropFunctionAndThis(
+ elem, "toLocaleString", cx);
+ funThis = ScriptRuntime.lastStoredScriptable(cx);
+ elem = fun.call(cx, scope, funThis,
+ ScriptRuntime.emptyArgs);
+ }
+ result.append(ScriptRuntime.toString(elem));
+ }
+ }
+ }
+ } 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 thisObj,
+ Object[] args)
+ {
+ long llength = getLengthProperty(cx, thisObj);
+ int length = (int)llength;
+ if (llength != length) {
+ throw Context.reportRuntimeError1(
+ "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 (thisObj instanceof NativeArray) {
+ NativeArray na = (NativeArray) thisObj;
+ if (na.denseOnly) {
+ StringBuffer sb = new StringBuffer();
+ 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, thisObj, 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();
+ StringBuffer sb = new StringBuffer(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 thisObj,
+ Object[] args)
+ {
+ if (thisObj instanceof NativeArray) {
+ NativeArray na = (NativeArray) thisObj;
+ 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 thisObj;
+ }
+ }
+ long len = getLengthProperty(cx, thisObj);
+
+ long half = len / 2;
+ for(long i=0; i < half; i++) {
+ long j = len - i - 1;
+ Object temp1 = getElem(cx, thisObj, i);
+ Object temp2 = getElem(cx, thisObj, j);
+ setElem(cx, thisObj, i, temp2);
+ setElem(cx, thisObj, j, temp1);
+ }
+ return thisObj;
+ }
+
+ /**
+ * See ECMA 15.4.4.5
+ */
+ private static Scriptable js_sort(Context cx, Scriptable scope,
+ Scriptable thisObj, Object[] args)
+ {
+ long length = getLengthProperty(cx, thisObj);
+
+ if (length <= 1) { return thisObj; }
+
+ Object compare;
+ Object[] cmpBuf;
+
+ if (args.length > 0 && Undefined.instance != args[0]) {
+ // sort with given compare function
+ compare = args[0];
+ cmpBuf = new Object[2]; // Buffer for cmp arguments
+ } else {
+ // sort with default compare
+ compare = null;
+ cmpBuf = null;
+ }
+ if (thisObj instanceof NativeArray) {
+ NativeArray na = (NativeArray) thisObj;
+ if (na.denseOnly) {
+ int ilength = (int) length;
+ heapsort(cx, scope, na.dense, ilength, compare, cmpBuf);
+ return thisObj;
+ }
+ }
+
+ // Should we use the extended sort function, or the faster one?
+ if (length >= Integer.MAX_VALUE) {
+ heapsort_extended(cx, scope, thisObj, length, compare, cmpBuf);
+ } else {
+ int ilength = (int)length;
+ // copy the JS array into a working array, so it can be
+ // sorted cheaply.
+ Object[] working = new Object[ilength];
+ for (int i = 0; i != ilength; ++i) {
+ working[i] = getElem(cx, thisObj, i);
+ }
+
+ heapsort(cx, scope, working, ilength, compare, cmpBuf);
+
+ // copy the working array back into thisObj
+ for (int i = 0; i != ilength; ++i) {
+ setElem(cx, thisObj, i, working[i]);
+ }
+ }
+ return thisObj;
+ }
+
+ // Return true only if x > y
+ private static boolean isBigger(Context cx, Scriptable scope,
+ Object x, Object y,
+ Object cmp, Object[] cmpBuf)
+ {
+ if (cmp == null) {
+ if (cmpBuf != null) Kit.codeBug();
+ } else {
+ if (cmpBuf == null || cmpBuf.length != 2) Kit.codeBug();
+ }
+
+ Object undef = Undefined.instance;
+ Object notfound = Scriptable.NOT_FOUND;
+
+ // sort undefined to end
+ if (y == undef || y == notfound) {
+ return false; // x can not be bigger then undef
+ } else if (x == undef || x == notfound) {
+ return true; // y != undef here, so x > y
+ }
+
+ if (cmp == null) {
+ // if no cmp function supplied, sort lexicographically
+ String a = ScriptRuntime.toString(x);
+ String b = ScriptRuntime.toString(y);
+ return a.compareTo(b) > 0;
+ }
+ else {
+ // assemble args and call supplied JS cmp function
+ cmpBuf[0] = x;
+ cmpBuf[1] = y;
+ Callable fun = ScriptRuntime.getValueFunctionAndThis(cmp, cx);
+ Scriptable funThis = ScriptRuntime.lastStoredScriptable(cx);
+
+ Object ret = fun.call(cx, scope, funThis, cmpBuf);
+ double d = ScriptRuntime.toNumber(ret);
+
+ // XXX what to do when cmp function returns NaN? ECMA states
+ // that it's then not a 'consistent comparison function'... but
+ // then what do we do? Back out and start over with the generic
+ // cmp function when we see a NaN? Throw an error?
+
+ // for now, just ignore it:
+
+ return d > 0;
+ }
+ }
+
+/** Heapsort implementation.
+ * See "Introduction to Algorithms" by Cormen, Leiserson, Rivest for details.
+ * Adjusted for zero based indexes.
+ */
+ private static void heapsort(Context cx, Scriptable scope,
+ Object[] array, int length,
+ Object cmp, Object[] cmpBuf)
+ {
+ if (length <= 1) Kit.codeBug();
+
+ // Build heap
+ for (int i = length / 2; i != 0;) {
+ --i;
+ Object pivot = array[i];
+ heapify(cx, scope, pivot, array, i, length, cmp, cmpBuf);
+ }
+
+ // Sort heap
+ for (int i = length; i != 1;) {
+ --i;
+ Object pivot = array[i];
+ array[i] = array[0];
+ heapify(cx, scope, pivot, array, 0, i, cmp, cmpBuf);
+ }
+ }
+
+/** pivot and child heaps of i should be made into heap starting at i,
+ * original array[i] is never used to have less array access during sorting.
+ */
+ private static void heapify(Context cx, Scriptable scope,
+ Object pivot, Object[] array, int i, int end,
+ Object cmp, Object[] cmpBuf)
+ {
+ for (;;) {
+ int child = i * 2 + 1;
+ if (child >= end) {
+ break;
+ }
+ Object childVal = array[child];
+ if (child + 1 < end) {
+ Object nextVal = array[child + 1];
+ if (isBigger(cx, scope, nextVal, childVal, cmp, cmpBuf)) {
+ ++child; childVal = nextVal;
+ }
+ }
+ if (!isBigger(cx, scope, childVal, pivot, cmp, cmpBuf)) {
+ break;
+ }
+ array[i] = childVal;
+ i = child;
+ }
+ array[i] = pivot;
+ }
+
+/** Version of heapsort that call getElem/setElem on target to query/assign
+ * array elements instead of Java array access
+ */
+ private static void heapsort_extended(Context cx, Scriptable scope,
+ Scriptable target, long length,
+ Object cmp, Object[] cmpBuf)
+ {
+ if (length <= 1) Kit.codeBug();
+
+ // Build heap
+ for (long i = length / 2; i != 0;) {
+ --i;
+ Object pivot = getElem(cx, target, i);
+ heapify_extended(cx, scope, pivot, target, i, length, cmp, cmpBuf);
+ }
+
+ // Sort heap
+ for (long i = length; i != 1;) {
+ --i;
+ Object pivot = getElem(cx, target, i);
+ setElem(cx, target, i, getElem(cx, target, 0));
+ heapify_extended(cx, scope, pivot, target, 0, i, cmp, cmpBuf);
+ }
+ }
+
+ private static void heapify_extended(Context cx, Scriptable scope,
+ Object pivot, Scriptable target,
+ long i, long end,
+ Object cmp, Object[] cmpBuf)
+ {
+ for (;;) {
+ long child = i * 2 + 1;
+ if (child >= end) {
+ break;
+ }
+ Object childVal = getElem(cx, target, child);
+ if (child + 1 < end) {
+ Object nextVal = getElem(cx, target, child + 1);
+ if (isBigger(cx, scope, nextVal, childVal, cmp, cmpBuf)) {
+ ++child; childVal = nextVal;
+ }
+ }
+ if (!isBigger(cx, scope, childVal, pivot, cmp, cmpBuf)) {
+ break;
+ }
+ setElem(cx, target, i, childVal);
+ i = child;
+ }
+ setElem(cx, target, i, pivot);
+ }
+
+ /**
+ * Non-ECMA methods.
+ */
+
+ private static Object js_push(Context cx, Scriptable thisObj,
+ Object[] args)
+ {
+ if (thisObj instanceof NativeArray) {
+ NativeArray na = (NativeArray) thisObj;
+ 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];
+ }
+ return ScriptRuntime.wrapNumber(na.length);
+ }
+ }
+ long length = getLengthProperty(cx, thisObj);
+ for (int i = 0; i < args.length; i++) {
+ setElem(cx, thisObj, length + i, args[i]);
+ }
+
+ length += args.length;
+ Object lengthObj = setLengthProperty(cx, thisObj, 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];
+
+ else
+ return lengthObj;
+ }
+
+ private static Object js_pop(Context cx, Scriptable thisObj,
+ Object[] args)
+ {
+ Object result;
+ if (thisObj instanceof NativeArray) {
+ NativeArray na = (NativeArray) thisObj;
+ if (na.denseOnly && na.length > 0) {
+ na.length--;
+ result = na.dense[(int)na.length];
+ na.dense[(int)na.length] = NOT_FOUND;
+ return result;
+ }
+ }
+ long length = getLengthProperty(cx, thisObj);
+ if (length > 0) {
+ length--;
+
+ // Get the to-be-deleted property's value.
+ result = getElem(cx, thisObj, length);
+
+ // We don't need to delete the last property, because
+ // setLength does that for us.
+ } 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, thisObj, length);
+
+ return result;
+ }
+
+ private static Object js_shift(Context cx, Scriptable thisObj,
+ Object[] args)
+ {
+ if (thisObj instanceof NativeArray) {
+ NativeArray na = (NativeArray) thisObj;
+ if (na.denseOnly && na.length > 0) {
+ na.length--;
+ 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;
+ }
+ }
+ Object result;
+ long length = getLengthProperty(cx, thisObj);
+ if (length > 0) {
+ long i = 0;
+ length--;
+
+ // Get the to-be-deleted property's value.
+ result = getElem(cx, thisObj, 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 = getElem(cx, thisObj, i);
+ setElem(cx, thisObj, i - 1, temp);
+ }
+ }
+ // We don't need to delete the last property, because
+ // setLength does that for us.
+ } else {
+ result = Undefined.instance;
+ }
+ setLengthProperty(cx, thisObj, length);
+ return result;
+ }
+
+ private static Object js_unshift(Context cx, Scriptable thisObj,
+ Object[] args)
+ {
+ if (thisObj instanceof NativeArray) {
+ NativeArray na = (NativeArray) thisObj;
+ 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;
+ return ScriptRuntime.wrapNumber(na.length);
+ }
+ }
+ long length = getLengthProperty(cx, thisObj);
+ int argc = args.length;
+
+ if (args.length > 0) {
+ /* 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 = getElem(cx, thisObj, last);
+ setElem(cx, thisObj, last + argc, temp);
+ }
+ }
+
+ /* Copy from argv to the bottom of the array. */
+ for (int i = 0; i < args.length; i++) {
+ setElem(cx, thisObj, i, args[i]);
+ }
+
+ /* Follow Perl by returning the new array length. */
+ length += args.length;
+ return setLengthProperty(cx, thisObj, length);
+ }
+ return ScriptRuntime.wrapNumber(length);
+ }
+
+ private static Object js_splice(Context cx, Scriptable scope,
+ Scriptable thisObj, Object[] args)
+ {
+ NativeArray na = null;
+ boolean denseMode = false;
+ if (thisObj instanceof NativeArray) {
+ na = (NativeArray) thisObj;
+ denseMode = na.denseOnly;
+ }
+
+ /* create an empty Array to return. */
+ scope = getTopLevelScope(scope);
+ int argc = args.length;
+ if (argc == 0)
+ return ScriptRuntime.newObject(cx, scope, "Array", null);
+ long length = getLengthProperty(cx, thisObj);
+
+ /* Convert the first argument into a starting index. */
+ long begin = toSliceIndex(ScriptRuntime.toInteger(args[0]), length);
+ argc--;
+
+ /* Convert the second argument into count */
+ long count;
+ if (args.length == 1) {
+ count = length - begin;
+ } else {
+ double dcount = ScriptRuntime.toInteger(args[1]);
+ if (dcount < 0) {
+ count = 0;
+ } else if (dcount > (length - begin)) {
+ count = length - begin;
+ } else {
+ count = (long)dcount;
+ }
+ argc--;
+ }
+
+ long end = begin + count;
+
+ /* If there are elements to remove, put them into the return value. */
+ Object result;
+ if (count != 0) {
+ if (count == 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, thisObj, 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 = ScriptRuntime.newObject(cx, scope,
+ "Array", null);
+ for (long last = begin; last != end; last++) {
+ Object temp = getElem(cx, thisObj, last);
+ setElem(cx, resultArray, last - begin, temp);
+ }
+ result = resultArray;
+ }
+ }
+ } else { // (count == 0)
+ if (cx.getLanguageVersion() == Context.VERSION_1_2) {
+ /* Emulate C JS1.2; if no elements are removed, return undefined. */
+ result = Undefined.instance;
+ } else {
+ result = ScriptRuntime.newObject(cx, scope, "Array", null);
+ }
+ }
+
+ /* Find the direction (up or down) to copy and make way for argv. */
+ long delta = argc - count;
+ 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;
+ return result;
+ }
+
+ if (delta > 0) {
+ for (long last = length - 1; last >= end; last--) {
+ Object temp = getElem(cx, thisObj, last);
+ setElem(cx, thisObj, last + delta, temp);
+ }
+ } else if (delta < 0) {
+ for (long last = end; last < length; last++) {
+ Object temp = getElem(cx, thisObj, last);
+ setElem(cx, thisObj, last + delta, temp);
+ }
+ }
+
+ /* Copy from argv into the hole to complete the splice. */
+ int argoffset = args.length - argc;
+ for (int i = 0; i < argc; i++) {
+ setElem(cx, thisObj, begin + i, args[i + argoffset]);
+ }
+
+ /* Update length in case we deleted elements from the end. */
+ setLengthProperty(cx, thisObj, length + delta);
+ return result;
+ }
+
+ /*
+ * See Ecma 262v3 15.4.4.4
+ */
+ private static Scriptable js_concat(Context cx, Scriptable scope,
+ Scriptable thisObj, Object[] args)
+ {
+ // create an empty Array to return.
+ scope = getTopLevelScope(scope);
+ Function ctor = ScriptRuntime.getExistingCtor(cx, scope, "Array");
+ Scriptable result = ctor.construct(cx, scope, ScriptRuntime.emptyArgs);
+ if (thisObj instanceof NativeArray && result instanceof NativeArray) {
+ NativeArray denseThis = (NativeArray) thisObj;
+ NativeArray denseResult = (NativeArray) result;
+ if (denseThis.denseOnly && denseResult.denseOnly) {
+ // First calculate length of resulting array
+ boolean canUseDense = true;
+ int length = (int) denseThis.length;
+ for (int i = 0; i < args.length && canUseDense; i++) {
+ if (ScriptRuntime.instanceOf(args[i], ctor, cx)) {
+ // only try to use dense approach for Array-like
+ // objects that are actually NativeArrays
+ canUseDense = args[i] instanceof NativeArray;
+ length += ((NativeArray) args[i]).length;
+ } else {
+ length++;
+ }
+ }
+ if (canUseDense && denseResult.ensureCapacity(length)) {
+ System.arraycopy(denseThis.dense, 0, denseResult.dense,
+ 0, (int) denseThis.length);
+ int cursor = (int) denseThis.length;
+ for (int i = 0; i < args.length && canUseDense; i++) {
+ if (args[i] instanceof NativeArray) {
+ NativeArray arg = (NativeArray) args[i];
+ System.arraycopy(arg.dense, 0,
+ denseResult.dense, cursor,
+ (int)arg.length);
+ cursor += (int)arg.length;
+ } else {
+ denseResult.dense[cursor++] = args[i];
+ }
+ }
+ denseResult.length = length;
+ return result;
+ }
+ }
+ }
+
+ long length;
+ long slot = 0;
+
+ /* Put the target in the result array; only add it as an array
+ * if it looks like one.
+ */
+ if (ScriptRuntime.instanceOf(thisObj, ctor, cx)) {
+ length = getLengthProperty(cx, thisObj);
+
+ // Copy from the target object into the result
+ for (slot = 0; slot < length; slot++) {
+ Object temp = getElem(cx, thisObj, slot);
+ setElem(cx, result, slot, temp);
+ }
+ } else {
+ setElem(cx, result, slot++, thisObj);
+ }
+
+ /* Copy from the arguments into the result. If any argument
+ * has a numeric length property, treat it as an array and add
+ * elements separately; otherwise, just copy the argument.
+ */
+ for (int i = 0; i < args.length; i++) {
+ if (ScriptRuntime.instanceOf(args[i], ctor, cx)) {
+ // ScriptRuntime.instanceOf => instanceof Scriptable
+ Scriptable arg = (Scriptable)args[i];
+ length = getLengthProperty(cx, arg);
+ for (long j = 0; j < length; j++, slot++) {
+ Object temp = getElem(cx, arg, j);
+ setElem(cx, result, slot, temp);
+ }
+ } else {
+ setElem(cx, result, slot++, args[i]);
+ }
+ }
+ return result;
+ }
+
+ private Scriptable js_slice(Context cx, Scriptable thisObj,
+ Object[] args)
+ {
+ Scriptable scope = getTopLevelScope(this);
+ Scriptable result = ScriptRuntime.newObject(cx, scope, "Array", null);
+ long length = getLengthProperty(cx, thisObj);
+
+ long begin, end;
+ if (args.length == 0) {
+ begin = 0;
+ end = length;
+ } else {
+ begin = toSliceIndex(ScriptRuntime.toInteger(args[0]), length);
+ if (args.length == 1) {
+ end = length;
+ } else {
+ end = toSliceIndex(ScriptRuntime.toInteger(args[1]), length);
+ }
+ }
+
+ for (long slot = begin; slot < end; slot++) {
+ Object temp = getElem(cx, thisObj, slot);
+ setElem(cx, result, slot - begin, temp);
+ }
+
+ 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;
+ }
+
+ /**
+ * Implements the methods "indexOf" and "lastIndexOf".
+ */
+ private Object indexOfHelper(Context cx, Scriptable thisObj,
+ Object[] args, boolean isLast)
+ {
+ Object compareTo = args.length > 0 ? args[0] : Undefined.instance;
+ long length = getLengthProperty(cx, thisObj);
+ long start;
+ if (isLast) {
+ // lastIndexOf
+ /*
+ * 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.
+ */
+ if (args.length < 2) {
+ // default
+ start = length-1;
+ } else {
+ start = ScriptRuntime.toInt32(ScriptRuntime.toNumber(args[1]));
+ if (start >= length)
+ start = length-1;
+ else if (start < 0)
+ start += length;
+ // Note that start may be negative, but that's okay
+ // as the result of -1 will fall out from the code below
+ }
+ } else {
+ // indexOf
+ /*
+ * 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.
+ */
+ if (args.length < 2) {
+ // default
+ start = 0;
+ } else {
+ start = ScriptRuntime.toInt32(ScriptRuntime.toNumber(args[1]));
+ if (start < 0) {
+ start += length;
+ if (start < 0)
+ start = 0;
+ }
+ // Note that start may be > length-1, but that's okay
+ // as the result of -1 will fall out from the code below
+ }
+ }
+ if (thisObj instanceof NativeArray) {
+ NativeArray na = (NativeArray) thisObj;
+ if (na.denseOnly) {
+ if (isLast) {
+ for (int i=(int)start; i >= 0; i--) {
+ if (na.dense[i] != Scriptable.NOT_FOUND &&
+ ScriptRuntime.shallowEq(na.dense[i], compareTo))
+ {
+ return new Long(i);
+ }
+ }
+ } else {
+ for (int i=(int)start; i < length; i++) {
+ if (na.dense[i] != Scriptable.NOT_FOUND &&
+ ScriptRuntime.shallowEq(na.dense[i], compareTo))
+ {
+ return new Long(i);
+ }
+ }
+ }
+ return NEGATIVE_ONE;
+ }
+ }
+ if (isLast) {
+ for (long i=start; i >= 0; i--) {
+ if (ScriptRuntime.shallowEq(getElem(cx, thisObj, i), compareTo)) {
+ return new Long(i);
+ }
+ }
+ } else {
+ for (long i=start; i < length; i++) {
+ if (ScriptRuntime.shallowEq(getElem(cx, thisObj, i), compareTo)) {
+ return new Long(i);
+ }
+ }
+ }
+ return NEGATIVE_ONE;
+ }
+
+ /**
+ * Implements the methods "every", "filter", "forEach", "map", and "some".
+ */
+ private Object iterativeMethod(Context cx, int id, Scriptable scope,
+ Scriptable thisObj, Object[] args)
+ {
+ Object callbackArg = args.length > 0 ? args[0] : Undefined.instance;
+ if (callbackArg == null || !(callbackArg instanceof Function)) {
+ throw ScriptRuntime.notFunctionError(
+ ScriptRuntime.toString(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]);
+ }
+ long length = getLengthProperty(cx, thisObj);
+ Scriptable array = ScriptRuntime.newObject(cx, scope, "Array", null);
+ long j=0;
+ for (long i=0; i < length; i++) {
+ Object[] innerArgs = new Object[3];
+ Object elem = (i > Integer.MAX_VALUE)
+ ? ScriptableObject.getProperty(thisObj, Long.toString(i))
+ : ScriptableObject.getProperty(thisObj, (int)i);
+ if (elem == Scriptable.NOT_FOUND) {
+ continue;
+ }
+ innerArgs[0] = elem;
+ innerArgs[1] = new Long(i);
+ innerArgs[2] = thisObj;
+ 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))
+ setElem(cx, array, j++, innerArgs[0]);
+ break;
+ case Id_forEach:
+ break;
+ case Id_map:
+ setElem(cx, array, i, result);
+ break;
+ case Id_some:
+ if (ScriptRuntime.toBoolean(result))
+ return Boolean.TRUE;
+ 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_forEach:
+ default:
+ return Undefined.instance;
+ }
+ }
+
+// #string_id_map#
+
+ protected int findPrototypeId(String s)
+ {
+ int id;
+// #generated# Last update: 2005-09-26 15:47:42 EDT
+ L0: { id = 0; String X = null; int c;
+ L: switch (s.length()) {
+ case 3: c=s.charAt(0);
+ if (c=='m') { if (s.charAt(2)=='p' && s.charAt(1)=='a') {id=Id_map; break L0;} }
+ else if (c=='p') { if (s.charAt(2)=='p' && s.charAt(1)=='o') {id=Id_pop; break L0;} }
+ break L;
+ case 4: switch (s.charAt(2)) {
+ case 'i': X="join";id=Id_join; break L;
+ case 'm': X="some";id=Id_some; break L;
+ case 'r': X="sort";id=Id_sort; break L;
+ case 's': X="push";id=Id_push; break L;
+ } break L;
+ case 5: c=s.charAt(1);
+ if (c=='h') { X="shift";id=Id_shift; }
+ else if (c=='l') { X="slice";id=Id_slice; }
+ else if (c=='v') { X="every";id=Id_every; }
+ break L;
+ case 6: c=s.charAt(0);
+ if (c=='c') { X="concat";id=Id_concat; }
+ else if (c=='f') { X="filter";id=Id_filter; }
+ else if (c=='s') { X="splice";id=Id_splice; }
+ break L;
+ case 7: switch (s.charAt(0)) {
+ case 'f': X="forEach";id=Id_forEach; break L;
+ case 'i': X="indexOf";id=Id_indexOf; break L;
+ case 'r': X="reverse";id=Id_reverse; break L;
+ case 'u': X="unshift";id=Id_unshift; break L;
+ } break L;
+ case 8: c=s.charAt(3);
+ if (c=='o') { X="toSource";id=Id_toSource; }
+ else if (c=='t') { X="toString";id=Id_toString; }
+ break L;
+ case 11: c=s.charAt(0);
+ if (c=='c') { X="constructor";id=Id_constructor; }
+ else if (c=='l') { X="lastIndexOf";id=Id_lastIndexOf; }
+ break L;
+ case 14: X="toLocaleString";id=Id_toLocaleString; break L;
+ }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ }
+// #/generated#
+ 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,
+
+ MAX_PROTOTYPE_ID = 21;
+
+// #/string_id_map#
+
+ 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;
+
+ /**
+ * Internal representation of the JavaScript array's length property.
+ */
+ private long length;
+
+ /**
+ * 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 <code>dense</code>.
+ */
+ private boolean denseOnly;
+
+ /**
+ * The maximum size of <code>dense</code> that will be allocated initially.
+ */
+ private static int maximumInitialCapacity = 10000;
+
+ /**
+ * The default capacity for <code>dense</code>.
+ */
+ private static final int DEFAULT_INITIAL_CAPACITY = 10;
+
+ /**
+ * The factor to grow <code>dense</code> 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/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeBoolean.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeBoolean.java
new file mode 100644
index 0000000..b6a106a
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeBoolean.java
@@ -0,0 +1,170 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ * Mike McCabe
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+/**
+ * This class implements the Boolean native object.
+ * See ECMA 15.6.
+ * @author Norris Boyd
+ */
+final class NativeBoolean extends IdScriptableObject
+{
+ static final long serialVersionUID = -3716996899943880933L;
+
+ private static final Object BOOLEAN_TAG = new Object();
+
+ static void init(Scriptable scope, boolean sealed)
+ {
+ NativeBoolean obj = new NativeBoolean(false);
+ obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
+ }
+
+ private NativeBoolean(boolean b)
+ {
+ booleanValue = b;
+ }
+
+ public String getClassName()
+ {
+ return "Boolean";
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ 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 {
+ b = args[0] instanceof ScriptableObject &&
+ ((ScriptableObject) args[0]).avoidObjectDetection()
+ ? true
+ : 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
+
+ if (!(thisObj instanceof NativeBoolean))
+ throw incompatibleCallError(f);
+ boolean value = ((NativeBoolean)thisObj).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));
+ }
+
+// #string_id_map#
+
+ protected int findPrototypeId(String s)
+ {
+ int id;
+// #generated# Last update: 2007-05-09 08:15:31 EDT
+ L0: { id = 0; String X = null; int c;
+ int s_length = s.length();
+ if (s_length==7) { X="valueOf";id=Id_valueOf; }
+ else if (s_length==8) {
+ c=s.charAt(3);
+ if (c=='o') { X="toSource";id=Id_toSource; }
+ else if (c=='t') { X="toString";id=Id_toString; }
+ }
+ else if (s_length==11) { X="constructor";id=Id_constructor; }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ break L0;
+ }
+// #/generated#
+ return id;
+ }
+
+ private static final int
+ Id_constructor = 1,
+ Id_toString = 2,
+ Id_toSource = 3,
+ Id_valueOf = 4,
+ MAX_PROTOTYPE_ID = 4;
+
+// #/string_id_map#
+
+ private boolean booleanValue;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeCall.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeCall.java
new file mode 100644
index 0000000..b196ac3
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeCall.java
@@ -0,0 +1,154 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Bob Jervis
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+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
+{
+ static final long serialVersionUID = -7471457301304454454L;
+
+ private static final Object CALL_TAG = new Object();
+
+ 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)
+ {
+ this.function = function;
+
+ setParentScope(scope);
+ // leave prototype null
+
+ this.originalArgs = (args == null) ? ScriptRuntime.emptyArgs : args;
+
+ // 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)) {
+ defineProperty("arguments", new Arguments(this), 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
+ defineProperty(name, Undefined.instance, PERMANENT);
+ }
+ }
+ }
+ }
+
+ public String getClassName()
+ {
+ return "Call";
+ }
+
+ protected int findPrototypeId(String s)
+ {
+ return s.equals("constructor") ? Id_constructor : 0;
+ }
+
+ 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);
+ }
+
+ 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.reportRuntimeError1("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));
+ }
+
+ private static final int
+ Id_constructor = 1,
+ MAX_PROTOTYPE_ID = 1;
+
+ NativeFunction function;
+ Object[] originalArgs;
+
+ transient NativeCall parentActivationCall;
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeDate.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeDate.java
new file mode 100644
index 0000000..75d41ab
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeDate.java
@@ -0,0 +1,1604 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Peter Annema
+ * Norris Boyd
+ * Mike McCabe
+ * Ilya Frank
+ *
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.util.Date;
+import java.text.DateFormat;
+
+/**
+ * This class implements the Date native object.
+ * See ECMA 15.9.
+ * @author Mike McCabe
+ */
+final class NativeDate extends IdScriptableObject
+{
+ static final long serialVersionUID = -8307438915861678966L;
+
+ private static final Object DATE_TAG = new Object();
+
+ 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()
+ {
+ if (thisTimeZone == null) {
+ // j.u.TimeZone is synchronized, so setting class statics from it
+ // should be OK.
+ thisTimeZone = java.util.TimeZone.getDefault();
+ LocalTZA = thisTimeZone.getRawOffset();
+ }
+ }
+
+ public String getClassName()
+ {
+ return "Date";
+ }
+
+ public Object getDefaultValue(Class typeHint)
+ {
+ if (typeHint == null)
+ typeHint = ScriptRuntime.StringClass;
+ return super.getDefaultValue(typeHint);
+ }
+
+ double getJSTimeValue()
+ {
+ return date;
+ }
+
+ 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", 1);
+ super.fillConstructorProperties(ctor);
+ }
+
+ 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_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;
+ default: throw new IllegalArgumentException(String.valueOf(id));
+ }
+ initPrototypeMethod(DATE_TAG, id, s, arity);
+ }
+
+ 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);
+ }
+ }
+
+ // The rest of Date.prototype methods require thisObj to be Date
+
+ if (!(thisObj instanceof NativeDate))
+ throw incompatibleCallError(f);
+ NativeDate realThis = (NativeDate)thisObj;
+ double t = realThis.date;
+
+ switch (id) {
+
+ case Id_toString:
+ case Id_toTimeString:
+ case Id_toDateString:
+ if (t == t) {
+ return date_format(t, id);
+ }
+ return js_NaN_date_str;
+
+ case Id_toLocaleString:
+ case Id_toLocaleTimeString:
+ case Id_toLocaleDateString:
+ if (t == t) {
+ return toLocale_helper(t, id);
+ }
+ return js_NaN_date_str;
+
+ case Id_toUTCString:
+ if (t == 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 (t == 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 (t == t) {
+ if (id == Id_getMonth) t = LocalTime(t);
+ t = MonthFromTime(t);
+ }
+ return ScriptRuntime.wrapNumber(t);
+
+ case Id_getDate:
+ case Id_getUTCDate:
+ if (t == t) {
+ if (id == Id_getDate) t = LocalTime(t);
+ t = DateFromTime(t);
+ }
+ return ScriptRuntime.wrapNumber(t);
+
+ case Id_getDay:
+ case Id_getUTCDay:
+ if (t == t) {
+ if (id == Id_getDay) t = LocalTime(t);
+ t = WeekDay(t);
+ }
+ return ScriptRuntime.wrapNumber(t);
+
+ case Id_getHours:
+ case Id_getUTCHours:
+ if (t == t) {
+ if (id == Id_getHours) t = LocalTime(t);
+ t = HourFromTime(t);
+ }
+ return ScriptRuntime.wrapNumber(t);
+
+ case Id_getMinutes:
+ case Id_getUTCMinutes:
+ if (t == t) {
+ if (id == Id_getMinutes) t = LocalTime(t);
+ t = MinFromTime(t);
+ }
+ return ScriptRuntime.wrapNumber(t);
+
+ case Id_getSeconds:
+ case Id_getUTCSeconds:
+ if (t == t) {
+ if (id == Id_getSeconds) t = LocalTime(t);
+ t = SecFromTime(t);
+ }
+ return ScriptRuntime.wrapNumber(t);
+
+ case Id_getMilliseconds:
+ case Id_getUTCMilliseconds:
+ if (t == t) {
+ if (id == Id_getMilliseconds) t = LocalTime(t);
+ t = msFromTime(t);
+ }
+ return ScriptRuntime.wrapNumber(t);
+
+ case Id_getTimezoneOffset:
+ if (t == 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 (year != year || Double.isInfinite(year)) {
+ t = ScriptRuntime.NaN;
+ } else {
+ if (t != 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);
+
+ 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)
+ {
+ int lo = (int) Math.floor((t / msPerDay) / 366) + 1970;
+ int hi = (int) Math.floor((t / msPerDay) / 365) + 1970;
+ int mid;
+
+ /* above doesn't work for negative dates... */
+ if (hi < lo) {
+ int temp = lo;
+ lo = hi;
+ hi = temp;
+ }
+
+ /* Use a simple binary search algorithm to find the right
+ year. This seems like brute force... but the computation
+ of hi and lo years above lands within one year of the
+ correct answer for years within a thousand years of
+ 1970; the loop below only requires six iterations
+ for year 270000. */
+ while (hi > lo) {
+ mid = (hi + lo) / 2;
+ if (TimeFromYear(mid) > t) {
+ hi = mid - 1;
+ } else {
+ lo = mid + 1;
+ if (TimeFromYear(lo) > t) {
+ return mid;
+ }
+ }
+ }
+ return lo;
+ }
+
+ 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 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();
+ }
+
+ /* Should be possible to determine the need for this dynamically
+ * if we go with the workaround... I'm not using it now, because I
+ * can't think of any clean way to make toLocaleString() and the
+ * time zone (comment) in toString match the generated string
+ * values. Currently it's wrong-but-consistent in all but the
+ * most recent betas of the JRE - seems to work in 1.1.7.
+ */
+ private final static boolean TZO_WORKAROUND = false;
+ 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
+ // outside 1970-2038.
+ if (t < 0.0 || t > 2145916800000.0) {
+ int year = EquivalentYear(YearFromTime(t));
+ double day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
+ t = MakeDate(day, TimeWithinDay(t));
+ }
+ if (!TZO_WORKAROUND) {
+ Date date = new Date((long) t);
+ if (thisTimeZone.inDaylightTime(date))
+ return msPerHour;
+ else
+ return 0;
+ } else {
+ /* Use getOffset if inDaylightTime() is broken, because it
+ * seems to work acceptably. We don't switch over to it
+ * entirely, because it requires (expensive) exploded date arguments,
+ * and the api makes it impossible to handle dst
+ * changeovers cleanly.
+ */
+
+ // Hardcode the assumption that the changeover always
+ // happens at 2:00 AM:
+ t += LocalTZA + (HourFromTime(t) <= 2 ? msPerHour : 0);
+
+ int year = YearFromTime(t);
+ double offset = thisTimeZone.getOffset(year > 0 ? 1 : 0,
+ year,
+ MonthFromTime(t),
+ DateFromTime(t),
+ WeekDay(t),
+ (int)TimeWithinDay(t));
+
+ if ((offset - LocalTZA) != 0)
+ return msPerHour;
+ else
+ return 0;
+ // return offset - LocalTZA;
+ }
+ }
+
+ /*
+ * 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 1974;
+ case 3: return 1975;
+ 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 (d != 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.);
+ else
+ 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 (d != 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)
+ {
+ return TimeClip(date_msecFromArgs(args));
+ }
+
+ private static double date_parseString(String s)
+ {
+ 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);
+ } else {
+ return msec + tzoffset * msPerMinute;
+ }
+ }
+
+ private static String date_format(double t, int methodId)
+ {
+ StringBuffer result = new StringBuffer(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);
+
+ if (timeZoneFormatter == null)
+ timeZoneFormatter = new java.text.SimpleDateFormat("zzz");
+
+ // Find an equivalent year before getting the timezone
+ // comment. See DaylightSavingTA.
+ if (t < 0.0 || t > 2145916800000.0) {
+ int equiv = EquivalentYear(YearFromTime(local));
+ double day = MakeDay(equiv, MonthFromTime(t), DateFromTime(t));
+ t = MakeDate(day, TimeWithinDay(t));
+ }
+ result.append(" (");
+ java.util.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 Scriptable)
+ arg0 = ((Scriptable) arg0).getDefaultValue(null);
+ double date;
+ if (arg0 instanceof String) {
+ // it's a string; parse it.
+ date = date_parseString((String)arg0);
+ } 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)
+ {
+ java.text.DateFormat formatter;
+ switch (methodId) {
+ case Id_toLocaleString:
+ if (localeDateTimeFormatter == null) {
+ localeDateTimeFormatter
+ = DateFormat.getDateTimeInstance(DateFormat.LONG,
+ DateFormat.LONG);
+ }
+ formatter = localeDateTimeFormatter;
+ break;
+ case Id_toLocaleTimeString:
+ if (localeTimeFormatter == null) {
+ localeTimeFormatter
+ = DateFormat.getTimeInstance(DateFormat.LONG);
+ }
+ formatter = localeTimeFormatter;
+ break;
+ case Id_toLocaleDateString:
+ if (localeDateFormatter == null) {
+ localeDateFormatter
+ = DateFormat.getDateInstance(DateFormat.LONG);
+ }
+ formatter = localeDateFormatter;
+ break;
+ default: formatter = null; // unreachable
+ }
+
+ synchronized (formatter) {
+ return formatter.format(new Date((long) t));
+ }
+ }
+
+ private static String js_toUTCString(double date)
+ {
+ StringBuffer result = new StringBuffer(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 void append0PaddedUint(StringBuffer 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(StringBuffer 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(StringBuffer 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)
+ {
+ 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:
+ Kit.codeBug();
+ maxargs = 0;
+ }
+
+ int i;
+ double conv[] = new double[4];
+ double hour, min, sec, msec;
+ double lorutime; /* Local or UTC version of date */
+
+ double time;
+ double result;
+
+ /* just return NaN if the date is already NaN */
+ if (date != date)
+ return date;
+
+ /* 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.
+ */
+ if (args.length == 0)
+ args = ScriptRuntime.padArguments(args, 1);
+
+ for (i = 0; i < args.length && i < maxargs; i++) {
+ conv[i] = ScriptRuntime.toNumber(args[i]);
+
+ // limit checks that happen in MakeTime in ECMA.
+ if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {
+ return ScriptRuntime.NaN;
+ }
+ conv[i] = ScriptRuntime.toInteger(conv[i]);
+ }
+
+ if (local)
+ lorutime = LocalTime(date);
+ else
+ lorutime = date;
+
+ i = 0;
+ int stop = args.length;
+
+ if (maxargs >= 4 && i < stop)
+ hour = conv[i++];
+ else
+ hour = HourFromTime(lorutime);
+
+ if (maxargs >= 3 && i < stop)
+ min = conv[i++];
+ else
+ min = MinFromTime(lorutime);
+
+ if (maxargs >= 2 && i < stop)
+ sec = conv[i++];
+ else
+ sec = SecFromTime(lorutime);
+
+ if (maxargs >= 1 && i < stop)
+ msec = conv[i++];
+ else
+ msec = msFromTime(lorutime);
+
+ time = MakeTime(hour, min, sec, msec);
+ result = MakeDate(Day(lorutime), time);
+
+ if (local)
+ result = internalUTC(result);
+ date = TimeClip(result);
+
+ return date;
+ }
+
+ private static double makeDate(double date, Object[] args, int methodId)
+ {
+ 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:
+ Kit.codeBug();
+ maxargs = 0;
+ }
+
+ int i;
+ double conv[] = new double[3];
+ double year, month, day;
+ double lorutime; /* local or UTC version of date */
+ double result;
+
+ /* See arg padding comment in makeTime.*/
+ if (args.length == 0)
+ args = ScriptRuntime.padArguments(args, 1);
+
+ for (i = 0; i < args.length && i < maxargs; i++) {
+ conv[i] = ScriptRuntime.toNumber(args[i]);
+
+ // limit checks that happen in MakeDate in ECMA.
+ if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {
+ return ScriptRuntime.NaN;
+ }
+ conv[i] = ScriptRuntime.toInteger(conv[i]);
+ }
+
+ /* return NaN if date is NaN and we're not setting the year,
+ * If we are, use 0 as the time. */
+ if (date != date) {
+ if (args.length < 3) {
+ return ScriptRuntime.NaN;
+ } else {
+ lorutime = 0;
+ }
+ } else {
+ if (local)
+ lorutime = LocalTime(date);
+ else
+ lorutime = date;
+ }
+
+ i = 0;
+ int stop = args.length;
+
+ if (maxargs >= 3 && i < stop)
+ year = conv[i++];
+ else
+ year = YearFromTime(lorutime);
+
+ if (maxargs >= 2 && i < stop)
+ month = conv[i++];
+ else
+ month = MonthFromTime(lorutime);
+
+ if (maxargs >= 1 && i < stop)
+ day = conv[i++];
+ else
+ day = DateFromTime(lorutime);
+
+ day = MakeDay(year, month, day); /* day within year */
+ result = MakeDate(day, TimeWithinDay(lorutime));
+
+ if (local)
+ result = internalUTC(result);
+
+ date = TimeClip(result);
+
+ return date;
+ }
+
+// #string_id_map#
+
+ protected int findPrototypeId(String s)
+ {
+ int id;
+// #generated# Last update: 2007-05-09 08:15:38 EDT
+ L0: { id = 0; String X = null; int c;
+ L: switch (s.length()) {
+ case 6: X="getDay";id=Id_getDay; break L;
+ case 7: switch (s.charAt(3)) {
+ case 'D': c=s.charAt(0);
+ if (c=='g') { X="getDate";id=Id_getDate; }
+ else if (c=='s') { X="setDate";id=Id_setDate; }
+ break L;
+ case 'T': c=s.charAt(0);
+ if (c=='g') { X="getTime";id=Id_getTime; }
+ else if (c=='s') { X="setTime";id=Id_setTime; }
+ break L;
+ case 'Y': c=s.charAt(0);
+ if (c=='g') { X="getYear";id=Id_getYear; }
+ else if (c=='s') { X="setYear";id=Id_setYear; }
+ break L;
+ case 'u': X="valueOf";id=Id_valueOf; break L;
+ } break L;
+ case 8: switch (s.charAt(3)) {
+ case 'H': c=s.charAt(0);
+ if (c=='g') { X="getHours";id=Id_getHours; }
+ else if (c=='s') { X="setHours";id=Id_setHours; }
+ break L;
+ case 'M': c=s.charAt(0);
+ if (c=='g') { X="getMonth";id=Id_getMonth; }
+ else if (c=='s') { X="setMonth";id=Id_setMonth; }
+ break L;
+ case 'o': X="toSource";id=Id_toSource; break L;
+ case 't': X="toString";id=Id_toString; break L;
+ } break L;
+ case 9: X="getUTCDay";id=Id_getUTCDay; break L;
+ case 10: c=s.charAt(3);
+ if (c=='M') {
+ c=s.charAt(0);
+ if (c=='g') { X="getMinutes";id=Id_getMinutes; }
+ else if (c=='s') { X="setMinutes";id=Id_setMinutes; }
+ }
+ else if (c=='S') {
+ c=s.charAt(0);
+ if (c=='g') { X="getSeconds";id=Id_getSeconds; }
+ else if (c=='s') { X="setSeconds";id=Id_setSeconds; }
+ }
+ else if (c=='U') {
+ c=s.charAt(0);
+ if (c=='g') { X="getUTCDate";id=Id_getUTCDate; }
+ else if (c=='s') { X="setUTCDate";id=Id_setUTCDate; }
+ }
+ break L;
+ case 11: switch (s.charAt(3)) {
+ case 'F': c=s.charAt(0);
+ if (c=='g') { X="getFullYear";id=Id_getFullYear; }
+ else if (c=='s') { X="setFullYear";id=Id_setFullYear; }
+ break L;
+ case 'M': X="toGMTString";id=Id_toGMTString; break L;
+ case 'T': X="toUTCString";id=Id_toUTCString; break L;
+ case 'U': c=s.charAt(0);
+ if (c=='g') {
+ c=s.charAt(9);
+ if (c=='r') { X="getUTCHours";id=Id_getUTCHours; }
+ else if (c=='t') { X="getUTCMonth";id=Id_getUTCMonth; }
+ }
+ else if (c=='s') {
+ c=s.charAt(9);
+ if (c=='r') { X="setUTCHours";id=Id_setUTCHours; }
+ else if (c=='t') { X="setUTCMonth";id=Id_setUTCMonth; }
+ }
+ break L;
+ case 's': X="constructor";id=Id_constructor; break L;
+ } break L;
+ case 12: c=s.charAt(2);
+ if (c=='D') { X="toDateString";id=Id_toDateString; }
+ else if (c=='T') { X="toTimeString";id=Id_toTimeString; }
+ break L;
+ case 13: c=s.charAt(0);
+ if (c=='g') {
+ c=s.charAt(6);
+ if (c=='M') { X="getUTCMinutes";id=Id_getUTCMinutes; }
+ else if (c=='S') { X="getUTCSeconds";id=Id_getUTCSeconds; }
+ }
+ else if (c=='s') {
+ c=s.charAt(6);
+ if (c=='M') { X="setUTCMinutes";id=Id_setUTCMinutes; }
+ else if (c=='S') { X="setUTCSeconds";id=Id_setUTCSeconds; }
+ }
+ break L;
+ case 14: c=s.charAt(0);
+ if (c=='g') { X="getUTCFullYear";id=Id_getUTCFullYear; }
+ else if (c=='s') { X="setUTCFullYear";id=Id_setUTCFullYear; }
+ else if (c=='t') { X="toLocaleString";id=Id_toLocaleString; }
+ break L;
+ case 15: c=s.charAt(0);
+ if (c=='g') { X="getMilliseconds";id=Id_getMilliseconds; }
+ else if (c=='s') { X="setMilliseconds";id=Id_setMilliseconds; }
+ break L;
+ case 17: X="getTimezoneOffset";id=Id_getTimezoneOffset; break L;
+ case 18: c=s.charAt(0);
+ if (c=='g') { X="getUTCMilliseconds";id=Id_getUTCMilliseconds; }
+ else if (c=='s') { X="setUTCMilliseconds";id=Id_setUTCMilliseconds; }
+ else if (c=='t') {
+ c=s.charAt(8);
+ if (c=='D') { X="toLocaleDateString";id=Id_toLocaleDateString; }
+ else if (c=='T') { X="toLocaleTimeString";id=Id_toLocaleTimeString; }
+ }
+ break L;
+ }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ break L0;
+ }
+// #/generated#
+ 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,
+
+ MAX_PROTOTYPE_ID = 45;
+
+ private static final int
+ Id_toGMTString = Id_toUTCString; // Alias, see Ecma B.2.6
+// #/string_id_map#
+
+ /* cached values */
+ private static java.util.TimeZone thisTimeZone;
+ private static double LocalTZA;
+ private static java.text.DateFormat timeZoneFormatter;
+ private static java.text.DateFormat localeDateTimeFormatter;
+ private static java.text.DateFormat localeDateFormatter;
+ private static java.text.DateFormat localeTimeFormatter;
+
+ private double date;
+}
+
+
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeError.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeError.java
new file mode 100644
index 0000000..4aff10c
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeError.java
@@ -0,0 +1,227 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ * Roger Lawrence
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+package org.mozilla.javascript;
+
+/**
+ *
+ * The class of error objects
+ *
+ * ECMA 15.11
+ */
+final class NativeError extends IdScriptableObject
+{
+ static final long serialVersionUID = -5338413581437645187L;
+
+ private static final Object ERROR_TAG = new Object();
+
+ 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", new Integer(0));
+ obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, 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) {
+ ScriptableObject.putProperty(obj, "message",
+ ScriptRuntime.toString(args[0]));
+ if (arglen >= 2) {
+ ScriptableObject.putProperty(obj, "fileName", args[1]);
+ if (arglen >= 3) {
+ int line = ScriptRuntime.toInt32(args[2]);
+ ScriptableObject.putProperty(obj, "lineNumber",
+ new Integer(line));
+ }
+ }
+ }
+ if(arglen < 3 && cx.hasFeature(Context.FEATURE_LOCATION_INFORMATION_IN_ERROR)) {
+ // Fill in fileName and lineNumber automatically when not specified
+ // explicitly, see Bugzilla issue #342807
+ int[] linep = new int[1];
+ String fileName = Context.getSourcePositionFromStack(linep);
+ ScriptableObject.putProperty(obj, "lineNumber",
+ new Integer(linep[0]));
+ if(arglen < 2) {
+ ScriptableObject.putProperty(obj, "fileName", fileName);
+ }
+ }
+ return obj;
+ }
+
+ public String getClassName()
+ {
+ return "Error";
+ }
+
+ public String toString()
+ {
+ return js_toString(this);
+ }
+
+ 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);
+ }
+
+ 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:
+ return make(cx, scope, f, args);
+
+ case Id_toString:
+ return js_toString(thisObj);
+
+ case Id_toSource:
+ return js_toSource(cx, scope, thisObj);
+ }
+ throw new IllegalArgumentException(String.valueOf(id));
+ }
+
+ private static String js_toString(Scriptable thisObj)
+ {
+ return getString(thisObj, "name")+": "+getString(thisObj, "message");
+ }
+
+ 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");
+
+ StringBuffer sb = new StringBuffer();
+ 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 String getString(Scriptable obj, String id)
+ {
+ Object value = ScriptableObject.getProperty(obj, id);
+ if (value == NOT_FOUND) return "";
+ return ScriptRuntime.toString(value);
+ }
+
+ protected int findPrototypeId(String s)
+ {
+ int id;
+// #string_id_map#
+// #generated# Last update: 2007-05-09 08:15:45 EDT
+ L0: { id = 0; String X = null; int c;
+ int s_length = s.length();
+ if (s_length==8) {
+ c=s.charAt(3);
+ if (c=='o') { X="toSource";id=Id_toSource; }
+ else if (c=='t') { X="toString";id=Id_toString; }
+ }
+ else if (s_length==11) { X="constructor";id=Id_constructor; }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ break L0;
+ }
+// #/generated#
+ return id;
+ }
+
+ private static final int
+ Id_constructor = 1,
+ Id_toString = 2,
+ Id_toSource = 3,
+
+ MAX_PROTOTYPE_ID = 3;
+
+// #/string_id_map#
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeFunction.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeFunction.java
new file mode 100644
index 0000000..ac70556
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeFunction.java
@@ -0,0 +1,169 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ * Bob Jervis
+ * Roger Lawrence
+ * Mike McCabe
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+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
+{
+
+ public final void initScriptFunction(Context cx, Scriptable scope)
+ {
+ ScriptRuntime.setFunctionProtoAndParent(this, scope);
+ }
+
+ /**
+ * @param indent How much to indent the decompiled result
+ *
+ * @param flags Flags specifying format of decompilation output
+ */
+ final String decompile(int indent, int flags)
+ {
+ String encodedSource = getEncodedSource();
+ if (encodedSource == null) {
+ return super.decompile(indent, flags);
+ } else {
+ UintMap properties = new UintMap(1);
+ properties.put(Decompiler.INITIAL_INDENT_PROP, indent);
+ return Decompiler.decompile(encodedSource, flags, properties);
+ }
+ }
+
+ 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;
+ }
+
+ 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.
+ */
+ 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 <tt>index < {@link #getParamCount()}</tt>, 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 <tt>index < {@link #getParamCount()}</tt>, 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/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGenerator.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGenerator.java
new file mode 100644
index 0000000..0a8da9f
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGenerator.java
@@ -0,0 +1,281 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+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 Object GENERATOR_TAG = new Object();
+
+ 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;
+
+ public String getClassName() {
+ return "Generator";
+ }
+
+ /**
+ * Close the generator if it is still open.
+ */
+ public void finalize() throws Throwable {
+ if (savedState != null) {
+ // This is a little tricky since we are most likely running in
+ // a different thread. We need to get a Context to run this, and
+ // we must call "doTopCall" since this will likely be the outermost
+ // JavaScript frame on this thread.
+ Context cx = Context.getCurrentContext();
+ ContextFactory factory = cx != null ? cx.getFactory()
+ : ContextFactory.getGlobal();
+ Scriptable scope = ScriptableObject.getTopLevelScope(this);
+ factory.call(new CloseGeneratorAction(this));
+ }
+ }
+
+ private static class CloseGeneratorAction implements ContextAction {
+ private NativeGenerator generator;
+
+ CloseGeneratorAction(NativeGenerator generator) {
+ this.generator = generator;
+ }
+
+ public Object run(Context cx) {
+ Scriptable scope = ScriptableObject.getTopLevelScope(generator);
+ Callable closeGenerator = new Callable() {
+ public Object call(Context cx, Scriptable scope,
+ Scriptable thisObj, Object[] args) {
+ return ((NativeGenerator)thisObj).resume(cx, scope,
+ GENERATOR_CLOSE, new GeneratorClosedException());
+ }
+ };
+ return ScriptRuntime.doTopCall(closeGenerator, cx, scope,
+ generator, null);
+ }
+ }
+
+ 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);
+ }
+
+ 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();
+
+ if (!(thisObj instanceof NativeGenerator))
+ throw incompatibleCallError(f);
+
+ NativeGenerator generator = (NativeGenerator) thisObj;
+
+ 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.typeError0("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.typeError0("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;
+ }
+ }
+
+// #string_id_map#
+
+ protected int findPrototypeId(String s) {
+ int id;
+// #generated# Last update: 2007-06-14 13:13:03 EDT
+ L0: { id = 0; String X = null; int c;
+ int s_length = s.length();
+ if (s_length==4) {
+ c=s.charAt(0);
+ if (c=='n') { X="next";id=Id_next; }
+ else if (c=='s') { X="send";id=Id_send; }
+ }
+ else if (s_length==5) {
+ c=s.charAt(0);
+ if (c=='c') { X="close";id=Id_close; }
+ else if (c=='t') { X="throw";id=Id_throw; }
+ }
+ else if (s_length==12) { X="__iterator__";id=Id___iterator__; }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ break L0;
+ }
+// #/generated#
+ 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;
+
+// #/string_id_map#
+ 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 {
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGlobal.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGlobal.java
new file mode 100644
index 0000000..58faad4
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGlobal.java
@@ -0,0 +1,790 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ * Mike McCabe
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+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,
+ ScriptableObject.DONTENUM);
+ ScriptableObject.defineProperty(
+ scope, "Infinity",
+ ScriptRuntime.wrapNumber(Double.POSITIVE_INFINITY),
+ ScriptableObject.DONTENUM);
+ ScriptableObject.defineProperty(
+ scope, "undefined", Undefined.instance,
+ ScriptableObject.DONTENUM);
+
+ String[] errorMethods = Kit.semicolonSplit(""
+ +"ConversionError;"
+ +"EvalError;"
+ +"RangeError;"
+ +"ReferenceError;"
+ +"SyntaxError;"
+ +"TypeError;"
+ +"URIError;"
+ +"InternalError;"
+ +"JavaException;"
+ );
+
+ /*
+ Each error constructor gets its own Error object as a prototype,
+ with the 'name' property set to the name of the error.
+ */
+ for (int i = 0; i < errorMethods.length; i++) {
+ String name = errorMethods[i];
+ Scriptable errorProto = ScriptRuntime.
+ newObject(cx, scope, "Error",
+ ScriptRuntime.emptyArgs);
+ errorProto.put("name", errorProto, name);
+ if (sealed) {
+ if (errorProto instanceof ScriptableObject) {
+ ((ScriptableObject)errorProto).sealObject();
+ }
+ }
+ IdFunctionObject ctor = new IdFunctionObject(obj, FTAG,
+ Id_new_CommonError,
+ name, 1, scope);
+ ctor.markAsConstructor(errorProto);
+ if (sealed) {
+ ctor.sealObject();
+ }
+ ctor.exportAsScopeProperty();
+ }
+ }
+
+ 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: {
+ boolean result;
+ if (args.length < 1) {
+ result = false;
+ } else {
+ double d = ScriptRuntime.toNumber(args[0]);
+ result = (d == d
+ && d != Double.POSITIVE_INFINITY
+ && d != Double.NEGATIVE_INFINITY);
+ }
+ return ScriptRuntime.wrapBoolean(result);
+ }
+
+ 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 = (d != 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.
+ */
+ private 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 (!Character.isWhitespace(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') {
+ radix = 8;
+ start++;
+ }
+ }
+ }
+
+ double d = ScriptRuntime.stringToNumber(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]
+ */
+ private 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 (!TokenStream.isJSSpace(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;
+ 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;
+ exponent = i;
+ continue;
+
+ case '+':
+ case '-':
+ // Only allow '+' or '-' after 'e' or 'E'
+ if (exponent != i-1)
+ break;
+ continue;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ continue;
+
+ default:
+ break;
+ }
+ break;
+ }
+ 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 imbedding. Blame
+ * for the strange constant names should be directed there.
+ */
+
+ private 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 (d != d || ((mask = (int) d) != d) ||
+ 0 != (mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH)))
+ {
+ throw Context.reportRuntimeError0("msg.bad.esc.mask");
+ }
+ }
+
+ StringBuffer 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 StringBuffer(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 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;
+ }
+
+ private Object js_eval(Context cx, Scriptable scope, Object[] args)
+ {
+ String m = ScriptRuntime.getMessage1("msg.cant.call.indirect", "eval");
+ throw NativeGlobal.constructError(cx, "EvalError", m, scope);
+ }
+
+ 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.
+ */
+ 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.
+ */
+ 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;
+ StringBuffer 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 StringBuffer(length + 3);
+ sb.append(str);
+ sb.setLength(k);
+ utf8buf = new byte[6];
+ }
+ if (0xDC00 <= C && C <= 0xDFFF) {
+ throw Context.reportRuntimeError0("msg.bad.uri");
+ }
+ int V;
+ if (C < 0xD800 || 0xDBFF < C) {
+ V = C;
+ } else {
+ k++;
+ if (k == length) {
+ throw Context.reportRuntimeError0("msg.bad.uri");
+ }
+ char C2 = str.charAt(k);
+ if (!(0xDC00 <= C2 && C2 <= 0xDFFF)) {
+ throw Context.reportRuntimeError0("msg.bad.uri");
+ }
+ 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 Context.reportRuntimeError0("msg.bad.uri");
+ int B = unHex(str.charAt(k + 1), str.charAt(k + 2));
+ if (B < 0) throw Context.reportRuntimeError0("msg.bad.uri");
+ 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 Context.reportRuntimeError0("msg.bad.uri");
+ } 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 Context.reportRuntimeError0("msg.bad.uri");
+ }
+ if (k + 3 * utf8Tail > length)
+ throw Context.reportRuntimeError0("msg.bad.uri");
+ for (int j = 0; j != utf8Tail; j++) {
+ if (str.charAt(k) != '%')
+ throw Context.reportRuntimeError0("msg.bad.uri");
+ B = unHex(str.charAt(k + 1), str.charAt(k + 2));
+ if (B < 0 || (B & 0xC0) != 0x80)
+ throw Context.reportRuntimeError0("msg.bad.uri");
+ ucs4Char = (ucs4Char << 6) | (B & 0x3F);
+ k += 3;
+ }
+ // Check for overlongs and other should-not-present codes
+ if (ucs4Char < minUcs4Char
+ || ucs4Char == 0xFFFE || ucs4Char == 0xFFFF)
+ {
+ ucs4Char = 0xFFFD;
+ }
+ if (ucs4Char >= 0x10000) {
+ ucs4Char -= 0x10000;
+ if (ucs4Char > 0xFFFFF)
+ throw Context.reportRuntimeError0("msg.bad.uri");
+ 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 final String URI_DECODE_RESERVED = ";/?:@&=+$,#";
+
+ /* 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 = new Object();
+
+ 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/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeIterator.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeIterator.java
new file mode 100644
index 0000000..c61f417
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeIterator.java
@@ -0,0 +1,260 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+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 Object ITERATOR_TAG = new Object();
+
+ static void init(ScriptableObject scope, boolean sealed) {
+ // Iterator
+ NativeIterator iterator = new NativeIterator();
+ iterator.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
+
+ // Generator
+ 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__";
+
+ static class StopIteration extends NativeObject {
+ public String getClassName() {
+ return STOP_ITERATION;
+ }
+
+ /* StopIteration has custom instanceof behavior since it
+ * doesn't have a constructor.
+ */
+ public boolean hasInstance(Scriptable instance) {
+ return instance instanceof StopIteration;
+ }
+ }
+
+ public String getClassName() {
+ return "Iterator";
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ if (!(thisObj instanceof NativeIterator))
+ throw incompatibleCallError(f);
+
+ NativeIterator iterator = (NativeIterator) thisObj;
+
+ 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)
+ {
+ throw ScriptRuntime.typeError1("msg.no.properties",
+ ScriptRuntime.toString(args[0]));
+ }
+ Scriptable obj = ScriptRuntime.toObject(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 =
+ VMBridge.instance.getJavaIterator(cx, scope, 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,
+ keyOnly ? ScriptRuntime.ENUMERATE_KEYS_NO_ITERATOR
+ : ScriptRuntime.ENUMERATE_ARRAY_NO_ITERATOR);
+ ScriptRuntime.setEnumNumbers(objectIterator, true);
+ NativeIterator result = new NativeIterator(objectIterator);
+ result.setPrototype(NativeIterator.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);
+ }
+
+ static public 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;
+ }
+
+// #string_id_map#
+
+ protected int findPrototypeId(String s) {
+ int id;
+// #generated# Last update: 2007-06-11 09:43:19 EDT
+ L0: { id = 0; String X = null;
+ int s_length = s.length();
+ if (s_length==4) { X="next";id=Id_next; }
+ else if (s_length==11) { X="constructor";id=Id_constructor; }
+ else if (s_length==12) { X="__iterator__";id=Id___iterator__; }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ break L0;
+ }
+// #/generated#
+ return id;
+ }
+
+ private static final int
+ Id_constructor = 1,
+ Id_next = 2,
+ Id___iterator__ = 3,
+ MAX_PROTOTYPE_ID = 3;
+
+// #/string_id_map#
+
+ private Object objectIterator;
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaArray.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaArray.java
new file mode 100644
index 0000000..2f711a0
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaArray.java
@@ -0,0 +1,168 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ * Frank Mitchell
+ * Mike Shaver
+ * Kemal Bayram
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+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
+{
+ static final long serialVersionUID = -924022554283675333L;
+
+ public String getClassName() {
+ return "JavaArray";
+ }
+
+ public static NativeJavaArray wrap(Scriptable scope, Object array) {
+ return new NativeJavaArray(scope, array);
+ }
+
+ 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();
+ }
+
+ public boolean has(String id, Scriptable start) {
+ return id.equals("length") || super.has(id, start);
+ }
+
+ public boolean has(int index, Scriptable start) {
+ return 0 <= index && index < length;
+ }
+
+ public Object get(String id, Scriptable start) {
+ if (id.equals("length"))
+ return new Integer(length);
+ Object result = super.get(id, start);
+ if (result == NOT_FOUND &&
+ !ScriptableObject.hasProperty(getPrototype(), id))
+ {
+ throw Context.reportRuntimeError2(
+ "msg.java.member.not.found", array.getClass().getName(), id);
+ }
+ return result;
+ }
+
+ 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;
+ }
+
+ public void put(String id, Scriptable start, Object value) {
+ // Ignore assignments to "length"--it's readonly.
+ if (!id.equals("length"))
+ throw Context.reportRuntimeError1(
+ "msg.java.array.member.not.found", id);
+ }
+
+ 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.reportRuntimeError2(
+ "msg.java.array.index.out.of.bounds", String.valueOf(index),
+ String.valueOf(length - 1));
+ }
+ }
+
+ 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;
+ }
+
+ public Object[] getIds() {
+ Object[] result = new Object[length];
+ int i = length;
+ while (--i >= 0)
+ result[i] = new Integer(i);
+ return result;
+ }
+
+ public boolean hasInstance(Scriptable value) {
+ if (!(value instanceof Wrapper))
+ return false;
+ Object instance = ((Wrapper)value).unwrap();
+ return cls.isInstance(instance);
+ }
+
+ public Scriptable getPrototype() {
+ if (prototype == null) {
+ prototype =
+ ScriptableObject.getClassPrototype(this.getParentScope(),
+ "Array");
+ }
+ return prototype;
+ }
+
+ Object array;
+ int length;
+ Class cls;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaClass.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaClass.java
new file mode 100644
index 0000000..ab8af5c
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaClass.java
@@ -0,0 +1,320 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Frank Mitchell
+ * Mike Shaver
+ * Kurt Westerfeld
+ * Kemal Bayram
+ * Ulrike Mueller <umueller@demandware.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.lang.reflect.*;
+import java.util.Hashtable;
+
+/**
+ * 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
+{
+ 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.parent = scope;
+ this.javaObject = cl;
+ initMembers();
+ }
+
+ protected void initMembers() {
+ Class cl = (Class)javaObject;
+ members = JavaMembers.lookupClass(parent, cl, cl, false);
+ staticFieldAndMethods
+ = members.getFieldAndMethodsObjects(this, cl, true);
+ }
+
+ public String getClassName() {
+ return "JavaClass";
+ }
+
+ public boolean has(String name, Scriptable start) {
+ return members.has(name, true) || javaClassPropertyName.equals(name);
+ }
+
+ 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);
+ }
+
+ if (javaClassPropertyName.equals(name)) {
+ Context cx = Context.getContext();
+ Scriptable scope = ScriptableObject.getTopLevelScope(start);
+ return cx.getWrapFactory().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) {
+ NativeJavaClass nestedValue = new NativeJavaClass
+ (ScriptableObject.getTopLevelScope(this), nestedClass);
+ nestedValue.setParentScope(this);
+ return nestedValue;
+ }
+
+ throw members.reportMemberNotFound(name);
+ }
+
+ public void put(String name, Scriptable start, Object value) {
+ members.put(this, name, javaObject, value, true);
+ }
+
+ public Object[] getIds() {
+ return members.getIds(true);
+ }
+
+ public Class getClassObject() {
+ return (Class) super.unwrap();
+ }
+
+ 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;
+ }
+
+ 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);
+ }
+
+ public Scriptable construct(Context cx, Scriptable scope, Object[] args)
+ {
+ Class classObject = getClassObject();
+ int modifiers = classObject.getModifiers();
+ if (! (Modifier.isInterface(modifiers) ||
+ Modifier.isAbstract(modifiers)))
+ {
+ MemberBox[] ctors = members.ctors;
+ int index = NativeJavaMethod.findFunction(cx, ctors, args);
+ if (index < 0) {
+ String sig = NativeJavaMethod.scriptSignature(args);
+ throw Context.reportRuntimeError2(
+ "msg.no.java.ctor", classObject.getName(), sig);
+ }
+
+ // Found the constructor, so try invoking it.
+ return constructSpecific(cx, scope, args, ctors[index]);
+ } else {
+ Scriptable topLevel = ScriptableObject.getTopLevelScope(this);
+ String msg = "";
+ try {
+ // trying to construct an interface; use JavaAdapter to
+ // construct a new class on the fly that implements this
+ // interface.
+ Object v = topLevel.get("JavaAdapter", topLevel);
+ if (v != NOT_FOUND) {
+ Function f = (Function) v;
+ 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.reportRuntimeError2(
+ "msg.cant.instantiate", msg, classObject.getName());
+ }
+ }
+
+ static Scriptable constructSpecific(Context cx, Scriptable scope,
+ Object[] args, MemberBox ctor)
+ {
+ Scriptable topLevel = ScriptableObject.getTopLevelScope(scope);
+ 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;
+ }
+ }
+ }
+
+ Object instance = ctor.newInstance(args);
+ // we need to force this to be wrapped, because construct _has_
+ // to return a scriptable
+ return cx.getWrapFactory().wrapNewObject(cx, topLevel, instance);
+ }
+
+ 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.
+ */
+ 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);
+ } else {
+ return Kit.classOrNull(loader, nestedClassName);
+ }
+ }
+
+ private Hashtable staticFieldAndMethods;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaConstructor.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaConstructor.java
new file mode 100644
index 0000000..530bf81
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaConstructor.java
@@ -0,0 +1,85 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Frank Mitchell
+ * Mike Shaver
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+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
+{
+ static final long serialVersionUID = -8149253217482668463L;
+
+ MemberBox ctor;
+
+ public NativeJavaConstructor(MemberBox ctor)
+ {
+ this.ctor = ctor;
+ }
+
+ public Object call(Context cx, Scriptable scope, Scriptable thisObj,
+ Object[] args)
+ {
+ return NativeJavaClass.constructSpecific(cx, scope, args, ctor);
+ }
+
+ public String getFunctionName()
+ {
+ String sig = JavaMembers.liveConnectSignature(ctor.argTypes);
+ return "<init>".concat(sig);
+ }
+
+ public String toString()
+ {
+ return "[JavaConstructor " + ctor.getName() + "]";
+ }
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaMethod.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaMethod.java
new file mode 100644
index 0000000..eb66f40
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaMethod.java
@@ -0,0 +1,576 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Frank Mitchell
+ * Mike Shaver
+ * Ulrike Mueller <umueller@demandware.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.lang.reflect.*;
+
+/**
+ * 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
+{
+ static final long serialVersionUID = -3440381785576412928L;
+
+ NativeJavaMethod(MemberBox[] methods)
+ {
+ this.functionName = methods[0].getName();
+ 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);
+ }
+
+ public String getFunctionName()
+ {
+ return functionName;
+ }
+
+ static String scriptSignature(Object[] values)
+ {
+ StringBuffer sig = new StringBuffer();
+ 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();
+ }
+
+ String decompile(int indent, int flags)
+ {
+ StringBuffer sb = new StringBuffer();
+ 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();
+ }
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0, N = methods.length; i != N; ++i) {
+ Method method = methods[i].method();
+ sb.append(JavaMembers.javaSignature(method.getReturnType()));
+ sb.append(' ');
+ sb.append(method.getName());
+ sb.append(JavaMembers.liveConnectSignature(methods[i].argTypes));
+ sb.append('\n');
+ }
+ return sb.toString();
+ }
+
+ 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 = findFunction(cx, methods, args);
+ if (index < 0) {
+ Class c = methods[0].method().getDeclaringClass();
+ String sig = c.getName() + '.' + getFunctionName() + '(' +
+ scriptSignature(args) + ')';
+ throw Context.reportRuntimeError1("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.reportRuntimeError3(
+ "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;
+ }
+
+ /**
+ * 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.member().getModifiers() & Modifier.PUBLIC) !=
+ (member.member().getModifiers() & Modifier.PUBLIC))
+ {
+ // When FEATURE_ENHANCED_JAVA_ACCESS gives us access
+ // to non-public members, continue to prefer public
+ // methods in overloading
+ if ((bestFit.member().getModifiers() & Modifier.PUBLIC) == 0)
+ ++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 heirarchy, even if
+ // a derived class's parameters match exactly.
+ // We want to call the dervied class's method.
+ if (bestFit.isStatic()
+ && bestFit.getDeclaringClass().isAssignableFrom(
+ member.getDeclaringClass()))
+ {
+ // On some JVMs, Class.getMethods will return all
+ // static methods of the class heirarchy, even if
+ // a derived class's parameters match exactly.
+ // We want to call the dervied 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
+ StringBuffer buf = new StringBuffer();
+ 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].isMethod()) {
+ throw Context.reportRuntimeError3(
+ "msg.constructor.ambiguous",
+ memberName, scriptSignature(args), buf.toString());
+ } else {
+ throw Context.reportRuntimeError4(
+ "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 )
+ {
+ // TODO: This test is pretty primitive. It bascially prefers
+ // a matching no vararg method over a vararg method independent
+ // of the type conversion cost. This can lead to unexpected results.
+ int alength = args.length;
+ if (!vararg1 && vararg2) {
+ // prefer the no vararg signature
+ return PREFERENCE_FIRST_ARG;
+ } else if (vararg1 && !vararg2) {
+ // prefer the no vararg signature
+ return PREFERENCE_SECOND_ARG;
+ } else if (vararg1 && vararg2) {
+ if (sig1.length < sig2.length) {
+ // prefer the signature with more explicit types
+ return PREFERENCE_SECOND_ARG;
+ } else if (sig1.length > sig2.length) {
+ // prefer the signature with more explicit types
+ return PREFERENCE_FIRST_ARG;
+ } else {
+ // Both are varargs and have the same length, so make the
+ // decision with the explicit args.
+ alength = Math.min(args.length, sig1.length-1);
+ }
+ }
+
+ int totalPreference = 0;
+ for (int j = 0; j < alength; j++) {
+ Class type1 = sig1[j];
+ Class type2 = 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) {
+ StringBuffer sb = new StringBuffer();
+ 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;
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaObject.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaObject.java
new file mode 100644
index 0000000..3d27852
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaObject.java
@@ -0,0 +1,1002 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ * Frank Mitchell
+ * Mike Shaver
+ * Kemal Bayram
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.Hashtable;
+import java.util.Date;
+
+/**
+ * This class reflects non-Array Java objects into the JavaScript environment. It
+ * reflect fields directly, and uses NativeJavaMethod objects to reflect (possibly
+ * overloaded) methods.<p>
+ *
+ * @author Mike Shaver
+ * @see NativeJavaArray
+ * @see NativeJavaPackage
+ * @see NativeJavaClass
+ */
+
+public class NativeJavaObject implements Scriptable, Wrapper, Serializable
+{
+ static final long serialVersionUID = -6948590651130498591L;
+
+ 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 = ScriptableObject.getVeryTopLevelScope(scope); // APPJET
+ 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);
+ }
+
+ public boolean has(String name, Scriptable start) {
+ return members.has(name, false);
+ }
+
+ public boolean has(int index, Scriptable start) {
+ return false;
+ }
+
+ 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);
+ }
+
+ public Object get(int index, Scriptable start) {
+ throw members.reportMemberNotFound(Integer.toString(index));
+ }
+
+ 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);
+ }
+
+ public void put(int index, Scriptable start, Object value) {
+ throw members.reportMemberNotFound(Integer.toString(index));
+ }
+
+ public boolean hasInstance(Scriptable value) {
+ // This is an instance of a Java class, so always return false
+ return false;
+ }
+
+ public void delete(String name) {
+ }
+
+ public void delete(int index) {
+ }
+
+ public Scriptable getPrototype() {
+ if (prototype == null && javaObject instanceof String) {
+ return ScriptableObject.getClassPrototype(parent, "String");
+ }
+ return prototype;
+ }
+
+ /**
+ * Sets the prototype of the object.
+ */
+ public void setPrototype(Scriptable m) {
+ prototype = m;
+ }
+
+ /**
+ * Returns the parent (enclosing) scope of the object.
+ */
+ public Scriptable getParentScope() {
+ return parent;
+ }
+
+ /**
+ * Sets the parent (enclosing) scope of the object.
+ */
+ public void setParentScope(Scriptable m) {
+ parent = m;
+ }
+
+ public Object[] getIds() {
+ return members.getIds(false);
+ }
+
+/**
+@deprecated Use {@link Context#getWrapFactory()} together with calling {@link
+WrapFactory#wrap(Context, Scriptable, Object, Class)}
+*/
+ public static Object wrap(Scriptable scope, Object obj, Class staticType) {
+
+ Context cx = Context.getContext();
+ return cx.getWrapFactory().wrap(cx, scope, obj, staticType);
+ }
+
+ public Object unwrap() {
+ return javaObject;
+ }
+
+ public String getClassName() {
+ return "JavaObject";
+ }
+
+ public Object getDefaultValue(Class hint)
+ {
+ Object value;
+ if (hint == null) {
+ if (javaObject instanceof Boolean) {
+ hint = ScriptRuntime.BooleanClass;
+ }
+ }
+ 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.reportRuntimeError0("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 = ScriptRuntime.wrapNumber(b ? 1.0 : 0.0);
+ } 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
+
+ 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
+ * <a href="http://www.mozilla.org/js/liveconnect/lc3_method_overloading.html">
+ * "preferred method conversions" from Live Connect 3</a>
+ */
+ 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:
+ 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.ObjectClass) {
+ return 10;
+ }
+ 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 == fromObj.getClass()) {
+ // No conversion required
+ 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 1;
+ }
+ }
+ else if (to == ScriptRuntime.ObjectClass) {
+ return 2;
+ }
+ else if (to == ScriptRuntime.StringClass) {
+ return 3;
+ }
+ 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 Function) {
+ // See comments in coerceType
+ if (to.getMethods().length == 1) {
+ return 1;
+ }
+ }
+ return 11;
+ }
+ else if (to.isPrimitive() && to != Boolean.TYPE) {
+ return 3 + 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 String) {
+ return JSTYPE_STRING;
+ }
+ 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;
+ }
+ else {
+ 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)
+ */
+ 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;
+ }
+
+ switch (getJSTypeCode(value)) {
+
+ 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";
+ }
+ else {
+ 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:
+ if (type == ScriptRuntime.StringClass) {
+ return ScriptRuntime.toString(value);
+ }
+ else if (type == ScriptRuntime.ObjectClass) {
+ return coerceToNumber(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;
+ }
+ 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 (((String)value).length() == 1) {
+ return new Character(((String)value).charAt(0));
+ }
+ else {
+ 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);
+ }
+ else {
+ if (type == ScriptRuntime.StringClass) {
+ return value.toString();
+ }
+ else {
+ if (type.isInstance(value)) {
+ return value;
+ }
+ else {
+ 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, coerceType(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 Callable) {
+ // Try to use function as implementation of Java interface.
+ //
+ // XXX: Curently 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.
+ if (value instanceof ScriptableObject) {
+ ScriptableObject so = (ScriptableObject)value;
+ 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, (Callable)value);
+ // Store for later retrival
+ glue = so.associateValue(key, glue);
+ return glue;
+ }
+ reportConversionError(value, type);
+ } else {
+ reportConversionError(value, type);
+ }
+ break;
+ }
+
+ return value;
+ }
+
+ 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 new Character((char)toInteger(value,
+ ScriptRuntime.CharacterClass,
+ Character.MIN_VALUE,
+ Character.MAX_VALUE));
+ }
+
+ // Double, Float
+ if (type == ScriptRuntime.ObjectClass ||
+ type == ScriptRuntime.DoubleClass || type == Double.TYPE) {
+ return valueClass == ScriptRuntime.DoubleClass
+ ? value
+ : new Double(toDouble(value));
+ }
+
+ if (type == ScriptRuntime.FloatClass || type == Float.TYPE) {
+ if (valueClass == ScriptRuntime.FloatClass) {
+ return value;
+ }
+ else {
+ double number = toDouble(value);
+ if (Double.isInfinite(number) || Double.isNaN(number)
+ || number == 0.0) {
+ return new Float((float)number);
+ }
+ else {
+ double absNumber = Math.abs(number);
+ if (absNumber < Float.MIN_VALUE) {
+ return new Float((number > 0.0) ? +0.0 : -0.0);
+ }
+ else if (absNumber > Float.MAX_VALUE) {
+ return new Float((number > 0.0) ?
+ Float.POSITIVE_INFINITY :
+ Float.NEGATIVE_INFINITY);
+ }
+ else {
+ return new Float((float)number);
+ }
+ }
+ }
+ }
+
+ // Integer, Long, Short, Byte
+ if (type == ScriptRuntime.IntegerClass || type == Integer.TYPE) {
+ if (valueClass == ScriptRuntime.IntegerClass) {
+ return value;
+ }
+ else {
+ return new Integer((int)toInteger(value,
+ ScriptRuntime.IntegerClass,
+ Integer.MIN_VALUE,
+ Integer.MAX_VALUE));
+ }
+ }
+
+ if (type == ScriptRuntime.LongClass || type == Long.TYPE) {
+ if (valueClass == ScriptRuntime.LongClass) {
+ return value;
+ } else {
+ /* 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 new Long(toInteger(value,
+ ScriptRuntime.LongClass,
+ min,
+ max));
+ }
+ }
+
+ if (type == ScriptRuntime.ShortClass || type == Short.TYPE) {
+ if (valueClass == ScriptRuntime.ShortClass) {
+ return value;
+ }
+ else {
+ return new Short((short)toInteger(value,
+ ScriptRuntime.ShortClass,
+ Short.MIN_VALUE,
+ Short.MAX_VALUE));
+ }
+ }
+
+ if (type == ScriptRuntime.ByteClass || type == Byte.TYPE) {
+ if (valueClass == ScriptRuntime.ByteClass) {
+ return value;
+ }
+ else {
+ return new Byte((byte)toInteger(value,
+ ScriptRuntime.ByteClass,
+ Byte.MIN_VALUE,
+ Byte.MAX_VALUE));
+ }
+ }
+
+ return new Double(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());
+ }
+ else {
+ 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.reportRuntimeError2(
+ "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.getClass().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();
+ }
+
+ /**
+ * 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 Hashtable fieldAndMethods;
+ private transient boolean isAdapter;
+
+ private static final Object COERCED_INTERFACE_KEY = new Object();
+ 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 (Exception ex) {
+ adapter_writeAdapterObject = null;
+ adapter_readAdapterObject = null;
+ }
+ }
+ }
+
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaPackage.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaPackage.java
new file mode 100644
index 0000000..71f09f7
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaPackage.java
@@ -0,0 +1,199 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Frank Mitchell
+ * Mike Shaver
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+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 NativeJavaPackage extends ScriptableObject
+{
+ 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.
+ */
+ public NativeJavaPackage(String packageName, ClassLoader classLoader) {
+ this(false, packageName, classLoader);
+ }
+
+ /**
+ * @deprecated NativeJavaPackage is an internal class, do not use
+ * it directly.
+ */
+ public NativeJavaPackage(String packageName) {
+ this(false, packageName,
+ Context.getCurrentContext().getApplicationClassLoader());
+ }
+
+ public String getClassName() {
+ return "JavaPackage";
+ }
+
+ public boolean has(String id, Scriptable start) {
+ return true;
+ }
+
+ public boolean has(int index, Scriptable start) {
+ return false;
+ }
+
+ public void put(String id, Scriptable start, Object value) {
+ // Can't add properties to Java packages. Sorry.
+ }
+
+ public void put(int index, Scriptable start, Object value) {
+ throw Context.reportRuntimeError0("msg.pkg.int");
+ }
+
+ public Object get(String id, Scriptable start) {
+ return getPkgProperty(id, start, true);
+ }
+
+ 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
+ void forcePackage(String name, Scriptable scope)
+ {
+ NativeJavaPackage pkg;
+ int end = name.indexOf('.');
+ if (end == -1) {
+ end = name.length();
+ }
+
+ String id = name.substring(0, end);
+ Object cached = super.get(id, this);
+ if (cached != null && cached instanceof NativeJavaPackage) {
+ pkg = (NativeJavaPackage) cached;
+ } else {
+ String newPackage = packageName.length() == 0
+ ? id
+ : packageName + "." + id;
+ pkg = new NativeJavaPackage(true, newPackage, classLoader);
+ ScriptRuntime.setObjectProtoAndParent(pkg, scope);
+ super.put(id, this, pkg);
+ }
+ if (end < name.length()) {
+ pkg.forcePackage(name.substring(end+1), scope);
+ }
+ }
+
+ synchronized Object getPkgProperty(String name, Scriptable start,
+ boolean createPkg)
+ {
+ Object cached = super.get(name, start);
+ if (cached != NOT_FOUND)
+ return cached;
+
+ 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) {
+ newValue = new NativeJavaClass(getTopLevelScope(this), cl);
+ newValue.setPrototype(getPrototype());
+ }
+ }
+ if (newValue == null && createPkg) {
+ NativeJavaPackage pkg;
+ pkg = new NativeJavaPackage(true, className, classLoader);
+ ScriptRuntime.setObjectProtoAndParent(pkg, getParentScope());
+ newValue = pkg;
+ }
+ 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;
+ }
+
+ public Object getDefaultValue(Class ignored) {
+ return toString();
+ }
+
+ public String toString() {
+ return "[JavaPackage " + packageName + "]";
+ }
+
+ public boolean equals(Object obj) {
+ if(obj instanceof NativeJavaPackage) {
+ NativeJavaPackage njp = (NativeJavaPackage)obj;
+ return packageName.equals(njp.packageName) && classLoader == njp.classLoader;
+ }
+ return false;
+ }
+
+ public int hashCode() {
+ return packageName.hashCode() ^ (classLoader == null ? 0 : classLoader.hashCode());
+ }
+
+ private String packageName;
+ private ClassLoader classLoader;
+} \ No newline at end of file
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaTopPackage.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaTopPackage.java
new file mode 100644
index 0000000..b5c9b49
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaTopPackage.java
@@ -0,0 +1,187 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Frank Mitchell
+ * Mike Shaver
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+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
+{
+ 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;"
+ +"java.lang.reflect;"
+ +"java.io;"
+ +"java.math;"
+ +"java.net;"
+ +"java.util;"
+ +"java.util.zip;"
+ +"java.text;"
+ +"java.text.resources;"
+ +"java.applet;"
+ +"javax.swing;"
+ ;
+
+ NativeJavaTopPackage(ClassLoader loader)
+ {
+ super(true, "", loader);
+ }
+
+ public Object call(Context cx, Scriptable scope, Scriptable thisObj,
+ Object[] args)
+ {
+ return construct(cx, scope, args);
+ }
+
+ 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.reportRuntimeError0("msg.not.classloader");
+ return null;
+ }
+ return new NativeJavaPackage(true, "", loader);
+ }
+
+ 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);
+
+ String[] names = Kit.semicolonSplit(commonPackages);
+ for (int i = 0; i != names.length; ++i) {
+ top.forcePackage(names[i], 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 = { "java", "javax", "org", "com", "edu", "net" };
+ 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);
+ }
+ }
+
+ 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.reportRuntimeError0("msg.not.java.obj");
+ }
+
+ private static final Object FTAG = new Object();
+ private static final int Id_getClass = 1;
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeMath.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeMath.java
new file mode 100644
index 0000000..36b66b4
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeMath.java
@@ -0,0 +1,399 @@
+/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+/**
+ * This class implements the Math native object.
+ * See ECMA 15.8.
+ * @author Norris Boyd
+ */
+
+final class NativeMath extends IdScriptableObject
+{
+ static final long serialVersionUID = -8838847185801131569L;
+
+ private static final Object MATH_TAG = new Object();
+
+ 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()
+ {
+ }
+
+ public String getClassName() { return "Math"; }
+
+ 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_asin: arity = 1; name = "asin"; break;
+ case Id_atan: arity = 1; name = "atan"; break;
+ case Id_atan2: arity = 2; name = "atan2"; break;
+ case Id_ceil: arity = 1; name = "ceil"; break;
+ case Id_cos: arity = 1; name = "cos"; break;
+ case Id_exp: arity = 1; name = "exp"; break;
+ case Id_floor: arity = 1; name = "floor"; break;
+ case Id_log: arity = 1; name = "log"; 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_sin: arity = 1; name = "sin"; break;
+ case Id_sqrt: arity = 1; name = "sqrt"; break;
+ case Id_tan: arity = 1; name = "tan"; 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 = 1.4426950408889634; 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);
+ }
+ }
+
+ 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 (x == x && -1.0 <= x && x <= 1.0) {
+ x = (methodId == Id_acos) ? Math.acos(x) : Math.asin(x);
+ } else {
+ x = Double.NaN;
+ }
+ break;
+
+ case Id_atan:
+ x = ScriptRuntime.toNumber(args, 0);
+ x = Math.atan(x);
+ break;
+
+ case Id_atan2:
+ x = ScriptRuntime.toNumber(args, 0);
+ x = Math.atan2(x, ScriptRuntime.toNumber(args, 1));
+ break;
+
+ case Id_ceil:
+ x = ScriptRuntime.toNumber(args, 0);
+ x = Math.ceil(x);
+ break;
+
+ case Id_cos:
+ x = ScriptRuntime.toNumber(args, 0);
+ x = (x == Double.POSITIVE_INFINITY
+ || x == Double.NEGATIVE_INFINITY)
+ ? Double.NaN : Math.cos(x);
+ 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_floor:
+ x = ScriptRuntime.toNumber(args, 0);
+ x = Math.floor(x);
+ 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_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 (d != d) {
+ x = d; // NaN
+ break;
+ }
+ 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 (x == x && x != Double.POSITIVE_INFINITY
+ && x != Double.NEGATIVE_INFINITY)
+ {
+ // 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_sin:
+ x = ScriptRuntime.toNumber(args, 0);
+ x = (x == Double.POSITIVE_INFINITY
+ || x == Double.NEGATIVE_INFINITY)
+ ? Double.NaN : Math.sin(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;
+
+ default: throw new IllegalStateException(String.valueOf(methodId));
+ }
+ return ScriptRuntime.wrapNumber(x);
+ }
+
+ // See Ecma 15.8.2.13
+ private double js_pow(double x, double y) {
+ double result;
+ if (y != 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 dirrerences 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 (result != 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;
+ }
+
+// #string_id_map#
+
+ protected int findPrototypeId(String s)
+ {
+ int id;
+// #generated# Last update: 2004-03-17 13:51:32 CET
+ L0: { id = 0; String X = null; int c;
+ L: switch (s.length()) {
+ case 1: if (s.charAt(0)=='E') {id=Id_E; break L0;} break L;
+ case 2: if (s.charAt(0)=='P' && s.charAt(1)=='I') {id=Id_PI; break L0;} break L;
+ case 3: switch (s.charAt(0)) {
+ case 'L': if (s.charAt(2)=='2' && s.charAt(1)=='N') {id=Id_LN2; break L0;} break L;
+ case 'a': if (s.charAt(2)=='s' && s.charAt(1)=='b') {id=Id_abs; break L0;} break L;
+ case 'c': if (s.charAt(2)=='s' && s.charAt(1)=='o') {id=Id_cos; break L0;} break L;
+ case 'e': if (s.charAt(2)=='p' && s.charAt(1)=='x') {id=Id_exp; break L0;} break L;
+ case 'l': if (s.charAt(2)=='g' && s.charAt(1)=='o') {id=Id_log; break L0;} break L;
+ case 'm': c=s.charAt(2);
+ if (c=='n') { if (s.charAt(1)=='i') {id=Id_min; break L0;} }
+ else if (c=='x') { if (s.charAt(1)=='a') {id=Id_max; break L0;} }
+ break L;
+ case 'p': if (s.charAt(2)=='w' && s.charAt(1)=='o') {id=Id_pow; break L0;} break L;
+ case 's': if (s.charAt(2)=='n' && s.charAt(1)=='i') {id=Id_sin; break L0;} break L;
+ case 't': if (s.charAt(2)=='n' && s.charAt(1)=='a') {id=Id_tan; break L0;} break L;
+ } break L;
+ case 4: switch (s.charAt(1)) {
+ case 'N': X="LN10";id=Id_LN10; break L;
+ case 'c': X="acos";id=Id_acos; break L;
+ case 'e': X="ceil";id=Id_ceil; break L;
+ case 'q': X="sqrt";id=Id_sqrt; break L;
+ case 's': X="asin";id=Id_asin; break L;
+ case 't': X="atan";id=Id_atan; break L;
+ } break L;
+ case 5: switch (s.charAt(0)) {
+ case 'L': X="LOG2E";id=Id_LOG2E; break L;
+ case 'S': X="SQRT2";id=Id_SQRT2; break L;
+ case 'a': X="atan2";id=Id_atan2; break L;
+ case 'f': X="floor";id=Id_floor; break L;
+ case 'r': X="round";id=Id_round; break L;
+ } break L;
+ case 6: c=s.charAt(0);
+ if (c=='L') { X="LOG10E";id=Id_LOG10E; }
+ else if (c=='r') { X="random";id=Id_random; }
+ break L;
+ case 7: X="SQRT1_2";id=Id_SQRT1_2; break L;
+ case 8: X="toSource";id=Id_toSource; break L;
+ }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ }
+// #/generated#
+ 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,
+
+ LAST_METHOD_ID = 19;
+
+ 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;
+
+// #/string_id_map#
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeNumber.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeNumber.java
new file mode 100644
index 0000000..8fc9fb0
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeNumber.java
@@ -0,0 +1,244 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ * Mike McCabe
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+/**
+ * This class implements the Number native object.
+ *
+ * See ECMA 15.7.
+ *
+ * @author Norris Boyd
+ */
+final class NativeNumber extends IdScriptableObject
+{
+ static final long serialVersionUID = 3504516769741512101L;
+
+ private static final Object NUMBER_TAG = new Object();
+
+ private static final int MAX_PRECISION = 100;
+
+ static void init(Scriptable scope, boolean sealed)
+ {
+ NativeNumber obj = new NativeNumber(0.0);
+ obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
+ }
+
+ private NativeNumber(double number)
+ {
+ doubleValue = number;
+ }
+
+ public String getClassName()
+ {
+ return "Number";
+ }
+
+ 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);
+
+ super.fillConstructorProperties(ctor);
+ }
+
+ 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);
+ }
+
+ 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.toNumber(args[0]) : 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);
+ }
+
+ // The rest of Number.prototype methods require thisObj to be Number
+
+ if (!(thisObj instanceof NativeNumber))
+ throw incompatibleCallError(f);
+ double value = ((NativeNumber)thisObj).doubleValue;
+
+ switch (id) {
+
+ case Id_toString:
+ case Id_toLocaleString:
+ {
+ // toLocaleString is just an alias for toString for now
+ int base = (args.length == 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:
+ return num_to(value, args, DToA.DTOSTR_FIXED,
+ DToA.DTOSTR_FIXED, -20, 0);
+
+ case Id_toExponential:
+ return num_to(value, args, DToA.DTOSTR_STANDARD_EXPONENTIAL,
+ DToA.DTOSTR_EXPONENTIAL, 0, 1);
+
+ case Id_toPrecision:
+ return num_to(value, args, DToA.DTOSTR_STANDARD,
+ DToA.DTOSTR_PRECISION, 1, 0);
+
+ default: throw new IllegalArgumentException(String.valueOf(id));
+ }
+ }
+
+ 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. */
+ precision = ScriptRuntime.toInt32(args[0]);
+ if (precision < precisionMin || precision > MAX_PRECISION) {
+ String msg = ScriptRuntime.getMessage1(
+ "msg.bad.precision", ScriptRuntime.toString(args[0]));
+ throw ScriptRuntime.constructError("RangeError", msg);
+ }
+ }
+ StringBuffer sb = new StringBuffer();
+ DToA.JS_dtostr(sb, oneArgMode, precision + precisionOffset, val);
+ return sb.toString();
+ }
+
+// #string_id_map#
+
+ protected int findPrototypeId(String s)
+ {
+ int id;
+// #generated# Last update: 2007-05-09 08:15:50 EDT
+ L0: { id = 0; String X = null; int c;
+ L: switch (s.length()) {
+ case 7: c=s.charAt(0);
+ if (c=='t') { X="toFixed";id=Id_toFixed; }
+ else if (c=='v') { X="valueOf";id=Id_valueOf; }
+ break L;
+ case 8: c=s.charAt(3);
+ if (c=='o') { X="toSource";id=Id_toSource; }
+ else if (c=='t') { X="toString";id=Id_toString; }
+ break L;
+ case 11: c=s.charAt(0);
+ if (c=='c') { X="constructor";id=Id_constructor; }
+ else if (c=='t') { X="toPrecision";id=Id_toPrecision; }
+ break L;
+ case 13: X="toExponential";id=Id_toExponential; break L;
+ case 14: X="toLocaleString";id=Id_toLocaleString; break L;
+ }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ break L0;
+ }
+// #/generated#
+ return id;
+ }
+
+ private static final int
+ 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;
+
+// #/string_id_map#
+
+ private double doubleValue;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeObject.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeObject.java
new file mode 100644
index 0000000..19aff63
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeObject.java
@@ -0,0 +1,316 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ * Bob Jervis
+ * Mike McCabe
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+/**
+ * This class implements the Object native object.
+ * See ECMA 15.2.
+ * @author Norris Boyd
+ */
+public class NativeObject extends IdScriptableObject
+{
+ static final long serialVersionUID = -6345305608474346996L;
+
+ private static final Object OBJECT_TAG = new Object();
+
+ static void init(Scriptable scope, boolean sealed)
+ {
+ NativeObject obj = new NativeObject();
+ obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
+ }
+
+ public String getClassName()
+ {
+ return "Object";
+ }
+
+ public String toString()
+ {
+ return ScriptRuntime.defaultObjectToString(this);
+ }
+
+ 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);
+ }
+
+ 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
+ || args[0] == Undefined.instance)
+ {
+ return new NativeObject();
+ }
+ return ScriptRuntime.toObject(cx, scope, args[0]);
+ }
+
+ case Id_toLocaleString: // For now just alias toString
+ 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:
+ return thisObj;
+
+ case Id_hasOwnProperty: {
+ boolean result;
+ if (args.length == 0) {
+ result = false;
+ } else {
+ String s = ScriptRuntime.toStringIdOrIndex(cx, args[0]);
+ if (s == null) {
+ int index = ScriptRuntime.lastIndexResult(cx);
+ result = thisObj.has(index, thisObj);
+ } else {
+ result = thisObj.has(s, thisObj);
+ }
+ }
+ return ScriptRuntime.wrapBoolean(result);
+ }
+
+ case Id_propertyIsEnumerable: {
+ boolean result;
+ if (args.length == 0) {
+ result = false;
+ } else {
+ String s = ScriptRuntime.toStringIdOrIndex(cx, args[0]);
+ if (s == null) {
+ int index = ScriptRuntime.lastIndexResult(cx);
+ result = thisObj.has(index, thisObj);
+ if (result && thisObj instanceof ScriptableObject) {
+ ScriptableObject so = (ScriptableObject)thisObj;
+ int attrs = so.getAttributes(index);
+ result = ((attrs & ScriptableObject.DONTENUM) == 0);
+ }
+ } else {
+ result = thisObj.has(s, thisObj);
+ if (result && thisObj instanceof ScriptableObject) {
+ ScriptableObject so = (ScriptableObject)thisObj;
+ int attrs = so.getAttributes(s);
+ result = ((attrs & ScriptableObject.DONTENUM) == 0);
+ }
+ }
+ }
+ return ScriptRuntime.wrapBoolean(result);
+ }
+
+ case Id_isPrototypeOf: {
+ 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.reportRuntimeError2(
+ "msg.extend.scriptable",
+ thisObj.getClass().getName(),
+ String.valueOf(args[0]));
+ }
+ ScriptableObject so = (ScriptableObject)thisObj;
+ String name = ScriptRuntime.toStringIdOrIndex(cx, args[0]);
+ int index = (name != null ? 0
+ : ScriptRuntime.lastIndexResult(cx));
+ Callable getterOrSetter = (Callable)args[1];
+ boolean isSetter = (id == Id___defineSetter__);
+ so.setGetterOrSetter(name, 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;
+ String name = ScriptRuntime.toStringIdOrIndex(cx, args[0]);
+ int index = (name != null ? 0
+ : ScriptRuntime.lastIndexResult(cx));
+ boolean isSetter = (id == Id___lookupSetter__);
+ Object gs;
+ for (;;) {
+ gs = so.getGetterOrSetter(name, index, 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;
+
+ default:
+ throw new IllegalArgumentException(String.valueOf(id));
+ }
+ }
+
+// #string_id_map#
+
+ protected int findPrototypeId(String s)
+ {
+ int id;
+// #generated# Last update: 2007-05-09 08:15:55 EDT
+ L0: { id = 0; String X = null; int c;
+ L: switch (s.length()) {
+ case 7: X="valueOf";id=Id_valueOf; break L;
+ case 8: c=s.charAt(3);
+ if (c=='o') { X="toSource";id=Id_toSource; }
+ else if (c=='t') { X="toString";id=Id_toString; }
+ break L;
+ case 11: X="constructor";id=Id_constructor; break L;
+ case 13: X="isPrototypeOf";id=Id_isPrototypeOf; break L;
+ case 14: c=s.charAt(0);
+ if (c=='h') { X="hasOwnProperty";id=Id_hasOwnProperty; }
+ else if (c=='t') { X="toLocaleString";id=Id_toLocaleString; }
+ break L;
+ case 16: c=s.charAt(2);
+ if (c=='d') {
+ c=s.charAt(8);
+ if (c=='G') { X="__defineGetter__";id=Id___defineGetter__; }
+ else if (c=='S') { X="__defineSetter__";id=Id___defineSetter__; }
+ }
+ else if (c=='l') {
+ c=s.charAt(8);
+ if (c=='G') { X="__lookupGetter__";id=Id___lookupGetter__; }
+ else if (c=='S') { X="__lookupSetter__";id=Id___lookupSetter__; }
+ }
+ break L;
+ case 20: X="propertyIsEnumerable";id=Id_propertyIsEnumerable; break L;
+ }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ break L0;
+ }
+// #/generated#
+ return id;
+ }
+
+ private static final int
+ 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;
+
+// #/string_id_map#
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeScript.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeScript.java
new file mode 100644
index 0000000..7b5191e
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeScript.java
@@ -0,0 +1,221 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ * Roger Lawrence
+ * Mike McCabe
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+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
+{
+ static final long serialVersionUID = -6795101161980121700L;
+
+ private static final Object SCRIPT_TAG = new Object();
+
+ 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".
+ */
+ public String getClassName()
+ {
+ return "Script";
+ }
+
+ public Object call(Context cx, Scriptable scope, Scriptable thisObj,
+ Object[] args)
+ {
+ if (script != null) {
+ return script.exec(cx, scope);
+ }
+ return Undefined.instance;
+ }
+
+ public Scriptable construct(Context cx, Scriptable scope, Object[] args)
+ {
+ throw Context.reportRuntimeError0("msg.script.is.not.constructor");
+ }
+
+ public int getLength()
+ {
+ return 0;
+ }
+
+ public int getArity()
+ {
+ return 0;
+ }
+
+ String decompile(int indent, int flags)
+ {
+ if (script instanceof NativeFunction) {
+ return ((NativeFunction)script).decompile(indent, flags);
+ }
+ return super.decompile(indent, flags);
+ }
+
+ 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);
+ }
+
+ 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.reportRuntimeError1(
+ "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)
+ {
+ if (!(thisObj instanceof NativeScript))
+ throw incompatibleCallError(f);
+ return (NativeScript)thisObj;
+ }
+
+ private static Script compile(Context cx, String source)
+ {
+ int[] linep = { 0 };
+ String filename = Context.getSourcePositionFromStack(linep);
+ if (filename == null) {
+ filename = "<Script object>";
+ linep[0] = 1;
+ }
+ ErrorReporter reporter;
+ reporter = DefaultErrorReporter.forEval(cx.getErrorReporter());
+ return cx.compileString(source, null, reporter, filename,
+ linep[0], null);
+ }
+
+// #string_id_map#
+
+ protected int findPrototypeId(String s)
+ {
+ int id;
+// #generated# Last update: 2007-05-09 08:16:01 EDT
+ L0: { id = 0; String X = null;
+ L: switch (s.length()) {
+ case 4: X="exec";id=Id_exec; break L;
+ case 7: X="compile";id=Id_compile; break L;
+ case 8: X="toString";id=Id_toString; break L;
+ case 11: X="constructor";id=Id_constructor; break L;
+ }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ break L0;
+ }
+// #/generated#
+ return id;
+ }
+
+ private static final int
+ Id_constructor = 1,
+ Id_toString = 2,
+ Id_compile = 3,
+ Id_exec = 4,
+ MAX_PROTOTYPE_ID = 4;
+
+// #/string_id_map#
+
+ private Script script;
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeString.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeString.java
new file mode 100644
index 0000000..972415d
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeString.java
@@ -0,0 +1,983 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Tom Beauvais
+ * Norris Boyd
+ * Mike McCabe
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.text.Collator;
+
+/**
+ * This class implements the String native object.
+ *
+ * See ECMA 15.5.
+ *
+ * String methods for dealing with regular expressions are
+ * ported directly from C. Latest port is from version 1.40.12.19
+ * in the JSFUN13_BRANCH.
+ *
+ * @author Mike McCabe
+ * @author Norris Boyd
+ */
+final class NativeString extends IdScriptableObject
+{
+ static final long serialVersionUID = 920268368584188687L;
+
+ private static final Object STRING_TAG = new Object();
+
+ static void init(Scriptable scope, boolean sealed)
+ {
+ NativeString obj = new NativeString("");
+ obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
+ }
+
+ private NativeString(String s) {
+ string = s;
+ }
+
+ public String getClassName() {
+ return "String";
+ }
+
+ private static final int
+ Id_length = 1,
+ MAX_INSTANCE_ID = 1;
+
+ protected int getMaxInstanceId()
+ {
+ return MAX_INSTANCE_ID;
+ }
+
+ protected int findInstanceIdInfo(String s)
+ {
+ if (s.equals("length")) {
+ return instanceIdInfo(DONTENUM | READONLY | PERMANENT, Id_length);
+ }
+ return super.findInstanceIdInfo(s);
+ }
+
+ protected String getInstanceIdName(int id)
+ {
+ if (id == Id_length) { return "length"; }
+ return super.getInstanceIdName(id);
+ }
+
+ protected Object getInstanceIdValue(int id)
+ {
+ if (id == Id_length) {
+ return ScriptRuntime.wrapInt(string.length());
+ }
+ return super.getInstanceIdValue(id);
+ }
+
+ protected void fillConstructorProperties(IdFunctionObject ctor)
+ {
+ addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_fromCharCode,
+ "fromCharCode", 1);
+ addIdFunctionProperty(ctor, STRING_TAG,
+ ConstructorId_charAt, "charAt", 2);
+ addIdFunctionProperty(ctor, STRING_TAG,
+ ConstructorId_charCodeAt, "charCodeAt", 2);
+ addIdFunctionProperty(ctor, STRING_TAG,
+ ConstructorId_indexOf, "indexOf", 2);
+ addIdFunctionProperty(ctor, STRING_TAG,
+ ConstructorId_lastIndexOf, "lastIndexOf", 2);
+ addIdFunctionProperty(ctor, STRING_TAG,
+ ConstructorId_split, "split", 3);
+ addIdFunctionProperty(ctor, STRING_TAG,
+ ConstructorId_substring, "substring", 3);
+ addIdFunctionProperty(ctor, STRING_TAG,
+ ConstructorId_toLowerCase, "toLowerCase", 1);
+ addIdFunctionProperty(ctor, STRING_TAG,
+ ConstructorId_toUpperCase, "toUpperCase", 1);
+ addIdFunctionProperty(ctor, STRING_TAG,
+ ConstructorId_substr, "substr", 3);
+ addIdFunctionProperty(ctor, STRING_TAG,
+ ConstructorId_concat, "concat", 2);
+ addIdFunctionProperty(ctor, STRING_TAG,
+ ConstructorId_slice, "slice", 3);
+ addIdFunctionProperty(ctor, STRING_TAG,
+ ConstructorId_equalsIgnoreCase, "equalsIgnoreCase", 2);
+ addIdFunctionProperty(ctor, STRING_TAG,
+ ConstructorId_match, "match", 2);
+ addIdFunctionProperty(ctor, STRING_TAG,
+ ConstructorId_search, "search", 2);
+ addIdFunctionProperty(ctor, STRING_TAG,
+ ConstructorId_replace, "replace", 2);
+ addIdFunctionProperty(ctor, STRING_TAG,
+ ConstructorId_localeCompare, "localeCompare", 2);
+ addIdFunctionProperty(ctor, STRING_TAG,
+ ConstructorId_toLocaleLowerCase, "toLocaleLowerCase", 1);
+ addIdFunctionProperty(ctor, STRING_TAG,
+ ConstructorId_fromCharCode, "fromCharCode", 1);
+ super.fillConstructorProperties(ctor);
+ }
+
+ 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;
+ case Id_charAt: arity=1; s="charAt"; break;
+ case Id_charCodeAt: arity=1; s="charCodeAt"; break;
+ case Id_indexOf: arity=1; s="indexOf"; break;
+ case Id_lastIndexOf: arity=1; s="lastIndexOf"; break;
+ case Id_split: arity=2; s="split"; break;
+ case Id_substring: arity=2; s="substring"; break;
+ case Id_toLowerCase: arity=0; s="toLowerCase"; break;
+ case Id_toUpperCase: arity=0; s="toUpperCase"; break;
+ case Id_substr: arity=2; s="substr"; break;
+ case Id_concat: arity=1; s="concat"; break;
+ case Id_slice: arity=2; s="slice"; break;
+ case Id_bold: arity=0; s="bold"; break;
+ case Id_italics: arity=0; s="italics"; break;
+ case Id_fixed: arity=0; s="fixed"; break;
+ case Id_strike: arity=0; s="strike"; break;
+ case Id_small: arity=0; s="small"; break;
+ case Id_big: arity=0; s="big"; break;
+ case Id_blink: arity=0; s="blink"; break;
+ case Id_sup: arity=0; s="sup"; break;
+ case Id_sub: arity=0; s="sub"; break;
+ case Id_fontsize: arity=0; s="fontsize"; break;
+ case Id_fontcolor: arity=0; s="fontcolor"; break;
+ case Id_link: arity=0; s="link"; break;
+ case Id_anchor: arity=0; s="anchor"; break;
+ case Id_equals: arity=1; s="equals"; break;
+ case Id_equalsIgnoreCase: arity=1; s="equalsIgnoreCase"; break;
+ case Id_match: arity=1; s="match"; break;
+ case Id_search: arity=1; s="search"; break;
+ case Id_replace: arity=1; s="replace"; break;
+ case Id_localeCompare: arity=1; s="localeCompare"; break;
+ case Id_toLocaleLowerCase: arity=0; s="toLocaleLowerCase"; break;
+ case Id_toLocaleUpperCase: arity=0; s="toLocaleUpperCase"; break;
+ default: throw new IllegalArgumentException(String.valueOf(id));
+ }
+ initPrototypeMethod(STRING_TAG, id, s, arity);
+ }
+
+ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
+ Scriptable thisObj, Object[] args)
+ {
+ if (!f.hasTag(STRING_TAG)) {
+ return super.execIdCall(f, cx, scope, thisObj, args);
+ }
+ int id = f.methodId();
+ again:
+ for(;;) {
+ switch (id) {
+ case ConstructorId_charAt:
+ case ConstructorId_charCodeAt:
+ case ConstructorId_indexOf:
+ case ConstructorId_lastIndexOf:
+ case ConstructorId_split:
+ case ConstructorId_substring:
+ case ConstructorId_toLowerCase:
+ case ConstructorId_toUpperCase:
+ case ConstructorId_substr:
+ case ConstructorId_concat:
+ case ConstructorId_slice:
+ case ConstructorId_equalsIgnoreCase:
+ case ConstructorId_match:
+ case ConstructorId_search:
+ case ConstructorId_replace:
+ case ConstructorId_localeCompare:
+ case ConstructorId_toLocaleLowerCase: {
+ thisObj = ScriptRuntime.toObject(scope,
+ ScriptRuntime.toString(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_fromCharCode: {
+ int N = args.length;
+ if (N < 1)
+ return "";
+ StringBuffer sb = new StringBuffer(N);
+ for (int i = 0; i != N; ++i) {
+ sb.append(ScriptRuntime.toUint16(args[i]));
+ }
+ return sb.toString();
+ }
+
+ case Id_constructor: {
+ String s = (args.length >= 1)
+ ? ScriptRuntime.toString(args[0]) : "";
+ if (thisObj == null) {
+ // new String(val) creates a new String object.
+ return new NativeString(s);
+ }
+ // String(val) converts val to a string value.
+ return s;
+ }
+
+ case Id_toString:
+ case Id_valueOf:
+ // ECMA 15.5.4.2: 'the toString function is not generic.
+ return realThis(thisObj, f).string;
+
+ case Id_toSource: {
+ String s = realThis(thisObj, f).string;
+ return "(new String(\""+ScriptRuntime.escapeString(s)+"\"))";
+ }
+
+ case Id_charAt:
+ case Id_charCodeAt: {
+ // See ECMA 15.5.4.[4,5]
+ String target = ScriptRuntime.toString(thisObj);
+ double pos = ScriptRuntime.toInteger(args, 0);
+ if (pos < 0 || pos >= target.length()) {
+ if (id == Id_charAt) return "";
+ else return ScriptRuntime.NaNobj;
+ }
+ char c = target.charAt((int)pos);
+ if (id == Id_charAt) return String.valueOf(c);
+ else return ScriptRuntime.wrapInt(c);
+ }
+
+ case Id_indexOf:
+ return ScriptRuntime.wrapInt(js_indexOf(
+ ScriptRuntime.toString(thisObj), args));
+
+ case Id_lastIndexOf:
+ return ScriptRuntime.wrapInt(js_lastIndexOf(
+ ScriptRuntime.toString(thisObj), args));
+
+ case Id_split:
+ return js_split(cx, scope, ScriptRuntime.toString(thisObj),
+ args);
+
+ case Id_substring:
+ return js_substring(cx, ScriptRuntime.toString(thisObj), args);
+
+ case Id_toLowerCase:
+ // See ECMA 15.5.4.11
+ return ScriptRuntime.toString(thisObj).toLowerCase();
+
+ case Id_toUpperCase:
+ // See ECMA 15.5.4.12
+ return ScriptRuntime.toString(thisObj).toUpperCase();
+
+ case Id_substr:
+ return js_substr(ScriptRuntime.toString(thisObj), args);
+
+ case Id_concat:
+ return js_concat(ScriptRuntime.toString(thisObj), args);
+
+ case Id_slice:
+ return js_slice(ScriptRuntime.toString(thisObj), args);
+
+ case Id_bold:
+ return tagify(thisObj, "b", null, null);
+
+ case Id_italics:
+ return tagify(thisObj, "i", null, null);
+
+ case Id_fixed:
+ return tagify(thisObj, "tt", null, null);
+
+ case Id_strike:
+ return tagify(thisObj, "strike", null, null);
+
+ case Id_small:
+ return tagify(thisObj, "small", null, null);
+
+ case Id_big:
+ return tagify(thisObj, "big", null, null);
+
+ case Id_blink:
+ return tagify(thisObj, "blink", null, null);
+
+ case Id_sup:
+ return tagify(thisObj, "sup", null, null);
+
+ case Id_sub:
+ return tagify(thisObj, "sub", null, null);
+
+ case Id_fontsize:
+ return tagify(thisObj, "font", "size", args);
+
+ case Id_fontcolor:
+ return tagify(thisObj, "font", "color", args);
+
+ case Id_link:
+ return tagify(thisObj, "a", "href", args);
+
+ case Id_anchor:
+ return tagify(thisObj, "a", "name", args);
+
+ case Id_equals:
+ case Id_equalsIgnoreCase: {
+ String s1 = ScriptRuntime.toString(thisObj);
+ String s2 = ScriptRuntime.toString(args, 0);
+ return ScriptRuntime.wrapBoolean(
+ (id == Id_equals) ? s1.equals(s2)
+ : s1.equalsIgnoreCase(s2));
+ }
+
+ case Id_match:
+ case Id_search:
+ case Id_replace:
+ {
+ int actionType;
+ if (id == Id_match) {
+ actionType = RegExpProxy.RA_MATCH;
+ } else if (id == Id_search) {
+ actionType = RegExpProxy.RA_SEARCH;
+ } else {
+ actionType = RegExpProxy.RA_REPLACE;
+ }
+ return ScriptRuntime.checkRegExpProxy(cx).
+ action(cx, scope, thisObj, args, actionType);
+ }
+ // ECMA-262 1 5.5.4.9
+ case Id_localeCompare:
+ {
+ // For now, create and configure a collator instance. I can't
+ // actually imagine that this'd be slower than caching them
+ // a la ClassCache, so we aren't trying to outsmart ourselves
+ // with a caching mechanism for now.
+ Collator collator = Collator.getInstance(cx.getLocale());
+ collator.setStrength(Collator.IDENTICAL);
+ collator.setDecomposition(Collator.CANONICAL_DECOMPOSITION);
+ return ScriptRuntime.wrapNumber(collator.compare(
+ ScriptRuntime.toString(thisObj),
+ ScriptRuntime.toString(args, 0)));
+ }
+ case Id_toLocaleLowerCase:
+ {
+ return ScriptRuntime.toString(thisObj)
+ .toLowerCase(cx.getLocale());
+ }
+ case Id_toLocaleUpperCase:
+ {
+ return ScriptRuntime.toString(thisObj)
+ .toUpperCase(cx.getLocale());
+ }
+ }
+ throw new IllegalArgumentException(String.valueOf(id));
+ }
+ }
+
+ private static NativeString realThis(Scriptable thisObj, IdFunctionObject f)
+ {
+ if (!(thisObj instanceof NativeString))
+ throw incompatibleCallError(f);
+ return (NativeString)thisObj;
+ }
+
+ /*
+ * HTML composition aids.
+ */
+ private static String tagify(Object thisObj, String tag,
+ String attribute, Object[] args)
+ {
+ String str = ScriptRuntime.toString(thisObj);
+ StringBuffer result = new StringBuffer();
+ result.append('<');
+ result.append(tag);
+ if (attribute != null) {
+ result.append(' ');
+ result.append(attribute);
+ result.append("=\"");
+ result.append(ScriptRuntime.toString(args, 0));
+ result.append('"');
+ }
+ result.append('>');
+ result.append(str);
+ result.append("</");
+ result.append(tag);
+ result.append('>');
+ return result.toString();
+ }
+
+ public String toString() {
+ return string;
+ }
+
+ /* Make array-style property lookup work for strings.
+ * XXX is this ECMA? A version check is probably needed. In js too.
+ */
+ public Object get(int index, Scriptable start) {
+ if (0 <= index && index < string.length()) {
+ return string.substring(index, index + 1);
+ }
+ return super.get(index, start);
+ }
+
+ public void put(int index, Scriptable start, Object value) {
+ if (0 <= index && index < string.length()) {
+ return;
+ }
+ super.put(index, start, value);
+ }
+
+ /*
+ *
+ * See ECMA 15.5.4.6. Uses Java String.indexOf()
+ * OPT to add - BMH searching from jsstr.c.
+ */
+ private static int js_indexOf(String target, Object[] args) {
+ String search = ScriptRuntime.toString(args, 0);
+ double begin = ScriptRuntime.toInteger(args, 1);
+
+ if (begin > target.length()) {
+ return -1;
+ } else {
+ if (begin < 0)
+ begin = 0;
+ return target.indexOf(search, (int)begin);
+ }
+ }
+
+ /*
+ *
+ * See ECMA 15.5.4.7
+ *
+ */
+ private static int js_lastIndexOf(String target, Object[] args) {
+ String search = ScriptRuntime.toString(args, 0);
+ double end = ScriptRuntime.toNumber(args, 1);
+
+ if (end != end || end > target.length())
+ end = target.length();
+ else if (end < 0)
+ end = 0;
+
+ return target.lastIndexOf(search, (int)end);
+ }
+
+ /*
+ * Used by js_split to find the next split point in target,
+ * starting at offset ip and looking either for the given
+ * separator substring, or for the next re match. ip and
+ * matchlen must be reference variables (assumed to be arrays of
+ * length 1) so they can be updated in the leading whitespace or
+ * re case.
+ *
+ * Return -1 on end of string, >= 0 for a valid index of the next
+ * separator occurrence if found, or the string length if no
+ * separator is found.
+ */
+ private static int find_split(Context cx, Scriptable scope, String target,
+ String separator, int version,
+ RegExpProxy reProxy, Scriptable re,
+ int[] ip, int[] matchlen, boolean[] matched,
+ String[][] parensp)
+ {
+ int i = ip[0];
+ int length = target.length();
+
+ /*
+ * Perl4 special case for str.split(' '), only if the user has selected
+ * JavaScript1.2 explicitly. Split on whitespace, and skip leading w/s.
+ * Strange but true, apparently modeled after awk.
+ */
+ if (version == Context.VERSION_1_2 &&
+ re == null && separator.length() == 1 && separator.charAt(0) == ' ')
+ {
+ /* Skip leading whitespace if at front of str. */
+ if (i == 0) {
+ while (i < length && Character.isWhitespace(target.charAt(i)))
+ i++;
+ ip[0] = i;
+ }
+
+ /* Don't delimit whitespace at end of string. */
+ if (i == length)
+ return -1;
+
+ /* Skip over the non-whitespace chars. */
+ while (i < length
+ && !Character.isWhitespace(target.charAt(i)))
+ i++;
+
+ /* Now skip the next run of whitespace. */
+ int j = i;
+ while (j < length && Character.isWhitespace(target.charAt(j)))
+ j++;
+
+ /* Update matchlen to count delimiter chars. */
+ matchlen[0] = j - i;
+ return i;
+ }
+
+ /*
+ * Stop if past end of string. If at end of string, we will
+ * return target length, so that
+ *
+ * "ab,".split(',') => new Array("ab", "")
+ *
+ * and the resulting array converts back to the string "ab,"
+ * for symmetry. NB: This differs from perl, which drops the
+ * trailing empty substring if the LIMIT argument is omitted.
+ */
+ if (i > length)
+ return -1;
+
+ /*
+ * Match a regular expression against the separator at or
+ * above index i. Return -1 at end of string instead of
+ * trying for a match, so we don't get stuck in a loop.
+ */
+ if (re != null) {
+ return reProxy.find_split(cx, scope, target, separator, re,
+ ip, matchlen, matched, parensp);
+ }
+
+ /*
+ * Deviate from ECMA by never splitting an empty string by any separator
+ * string into a non-empty array (an array of length 1 that contains the
+ * empty string).
+ */
+ if (version != Context.VERSION_DEFAULT && version < Context.VERSION_1_3
+ && length == 0)
+ return -1;
+
+ /*
+ * Special case: if sep is the empty string, split str into
+ * one character substrings. Let our caller worry about
+ * whether to split once at end of string into an empty
+ * substring.
+ *
+ * For 1.2 compatibility, at the end of the string, we return the length as
+ * the result, and set the separator length to 1 -- this allows the caller
+ * to include an additional null string at the end of the substring list.
+ */
+ if (separator.length() == 0) {
+ if (version == Context.VERSION_1_2) {
+ if (i == length) {
+ matchlen[0] = 1;
+ return i;
+ }
+ return i + 1;
+ }
+ return (i == length) ? -1 : i + 1;
+ }
+
+ /* Punt to j.l.s.indexOf; return target length if separator is
+ * not found.
+ */
+ if (ip[0] >= length)
+ return length;
+
+ i = target.indexOf(separator, ip[0]);
+
+ return (i != -1) ? i : length;
+ }
+
+ /*
+ * See ECMA 15.5.4.8. Modified to match JS 1.2 - optionally takes
+ * a limit argument and accepts a regular expression as the split
+ * argument.
+ */
+ private static Object js_split(Context cx, Scriptable scope,
+ String target, Object[] args)
+ {
+ // create an empty Array to return;
+ Scriptable top = getTopLevelScope(scope);
+ Scriptable result = ScriptRuntime.newObject(cx, top, "Array", null);
+
+ // return an array consisting of the target if no separator given
+ // don't check against undefined, because we want
+ // 'fooundefinedbar'.split(void 0) to split to ['foo', 'bar']
+ if (args.length < 1) {
+ result.put(0, result, target);
+ return result;
+ }
+
+ // Use the second argument as the split limit, if given.
+ boolean limited = (args.length > 1) && (args[1] != Undefined.instance);
+ long limit = 0; // Initialize to avoid warning.
+ if (limited) {
+ /* Clamp limit between 0 and 1 + string length. */
+ limit = ScriptRuntime.toUint32(args[1]);
+ if (limit > target.length())
+ limit = 1 + target.length();
+ }
+
+ String separator = null;
+ int[] matchlen = new int[1];
+ Scriptable re = null;
+ RegExpProxy reProxy = null;
+ if (args[0] instanceof Scriptable) {
+ reProxy = ScriptRuntime.getRegExpProxy(cx);
+ if (reProxy != null) {
+ Scriptable test = (Scriptable)args[0];
+ if (reProxy.isRegExp(test)) {
+ re = test;
+ }
+ }
+ }
+ if (re == null) {
+ separator = ScriptRuntime.toString(args[0]);
+ matchlen[0] = separator.length();
+ }
+
+ // split target with separator or re
+ int[] ip = { 0 };
+ int match;
+ int len = 0;
+ boolean[] matched = { false };
+ String[][] parens = { null };
+ int version = cx.getLanguageVersion();
+ while ((match = find_split(cx, scope, target, separator, version,
+ reProxy, re, ip, matchlen, matched, parens))
+ >= 0)
+ {
+ if ((limited && len >= limit) || (match > target.length()))
+ break;
+
+ String substr;
+ if (target.length() == 0)
+ substr = target;
+ else
+ substr = target.substring(ip[0], match);
+
+ result.put(len, result, substr);
+ len++;
+ /*
+ * Imitate perl's feature of including parenthesized substrings
+ * that matched part of the delimiter in the new array, after the
+ * split substring that was delimited.
+ */
+ if (re != null && matched[0] == true) {
+ int size = parens[0].length;
+ for (int num = 0; num < size; num++) {
+ if (limited && len >= limit)
+ break;
+ result.put(len, result, parens[0][num]);
+ len++;
+ }
+ matched[0] = false;
+ }
+ ip[0] = match + matchlen[0];
+
+ if (version < Context.VERSION_1_3
+ && version != Context.VERSION_DEFAULT)
+ {
+ /*
+ * Deviate from ECMA to imitate Perl, which omits a final
+ * split unless a limit argument is given and big enough.
+ */
+ if (!limited && ip[0] == target.length())
+ break;
+ }
+ }
+ return result;
+ }
+
+ /*
+ * See ECMA 15.5.4.15
+ */
+ private static String js_substring(Context cx, String target,
+ Object[] args)
+ {
+ int length = target.length();
+ double start = ScriptRuntime.toInteger(args, 0);
+ double end;
+
+ if (start < 0)
+ start = 0;
+ else if (start > length)
+ start = length;
+
+ if (args.length <= 1 || args[1] == Undefined.instance) {
+ end = length;
+ } else {
+ end = ScriptRuntime.toInteger(args[1]);
+ if (end < 0)
+ end = 0;
+ else if (end > length)
+ end = length;
+
+ // swap if end < start
+ if (end < start) {
+ if (cx.getLanguageVersion() != Context.VERSION_1_2) {
+ double temp = start;
+ start = end;
+ end = temp;
+ } else {
+ // Emulate old JDK1.0 java.lang.String.substring()
+ end = start;
+ }
+ }
+ }
+ return target.substring((int)start, (int)end);
+ }
+
+ int getLength() {
+ return string.length();
+ }
+
+ /*
+ * Non-ECMA methods.
+ */
+ private static String js_substr(String target, Object[] args) {
+ if (args.length < 1)
+ return target;
+
+ double begin = ScriptRuntime.toInteger(args[0]);
+ double end;
+ int length = target.length();
+
+ if (begin < 0) {
+ begin += length;
+ if (begin < 0)
+ begin = 0;
+ } else if (begin > length) {
+ begin = length;
+ }
+
+ if (args.length == 1) {
+ end = length;
+ } else {
+ end = ScriptRuntime.toInteger(args[1]);
+ if (end < 0)
+ end = 0;
+ end += begin;
+ if (end > length)
+ end = length;
+ }
+
+ return target.substring((int)begin, (int)end);
+ }
+
+ /*
+ * Python-esque sequence operations.
+ */
+ private static String js_concat(String target, Object[] args) {
+ int N = args.length;
+ if (N == 0) { return target; }
+ else if (N == 1) {
+ String arg = ScriptRuntime.toString(args[0]);
+ return target.concat(arg);
+ }
+
+ // Find total capacity for the final string to avoid unnecessary
+ // re-allocations in StringBuffer
+ int size = target.length();
+ String[] argsAsStrings = new String[N];
+ for (int i = 0; i != N; ++i) {
+ String s = ScriptRuntime.toString(args[i]);
+ argsAsStrings[i] = s;
+ size += s.length();
+ }
+
+ StringBuffer result = new StringBuffer(size);
+ result.append(target);
+ for (int i = 0; i != N; ++i) {
+ result.append(argsAsStrings[i]);
+ }
+ return result.toString();
+ }
+
+ private static String js_slice(String target, Object[] args) {
+ if (args.length != 0) {
+ double begin = ScriptRuntime.toInteger(args[0]);
+ double end;
+ int length = target.length();
+ if (begin < 0) {
+ begin += length;
+ if (begin < 0)
+ begin = 0;
+ } else if (begin > length) {
+ begin = length;
+ }
+
+ if (args.length == 1) {
+ end = length;
+ } else {
+ end = ScriptRuntime.toInteger(args[1]);
+ if (end < 0) {
+ end += length;
+ if (end < 0)
+ end = 0;
+ } else if (end > length) {
+ end = length;
+ }
+ if (end < begin)
+ end = begin;
+ }
+ return target.substring((int)begin, (int)end);
+ }
+ return target;
+ }
+
+// #string_id_map#
+
+ protected int findPrototypeId(String s)
+ {
+ int id;
+// #generated# Last update: 2007-05-01 22:11:49 EDT
+ L0: { id = 0; String X = null; int c;
+ L: switch (s.length()) {
+ case 3: c=s.charAt(2);
+ if (c=='b') { if (s.charAt(0)=='s' && s.charAt(1)=='u') {id=Id_sub; break L0;} }
+ else if (c=='g') { if (s.charAt(0)=='b' && s.charAt(1)=='i') {id=Id_big; break L0;} }
+ else if (c=='p') { if (s.charAt(0)=='s' && s.charAt(1)=='u') {id=Id_sup; break L0;} }
+ break L;
+ case 4: c=s.charAt(0);
+ if (c=='b') { X="bold";id=Id_bold; }
+ else if (c=='l') { X="link";id=Id_link; }
+ break L;
+ case 5: switch (s.charAt(4)) {
+ case 'd': X="fixed";id=Id_fixed; break L;
+ case 'e': X="slice";id=Id_slice; break L;
+ case 'h': X="match";id=Id_match; break L;
+ case 'k': X="blink";id=Id_blink; break L;
+ case 'l': X="small";id=Id_small; break L;
+ case 't': X="split";id=Id_split; break L;
+ } break L;
+ case 6: switch (s.charAt(1)) {
+ case 'e': X="search";id=Id_search; break L;
+ case 'h': X="charAt";id=Id_charAt; break L;
+ case 'n': X="anchor";id=Id_anchor; break L;
+ case 'o': X="concat";id=Id_concat; break L;
+ case 'q': X="equals";id=Id_equals; break L;
+ case 't': X="strike";id=Id_strike; break L;
+ case 'u': X="substr";id=Id_substr; break L;
+ } break L;
+ case 7: switch (s.charAt(1)) {
+ case 'a': X="valueOf";id=Id_valueOf; break L;
+ case 'e': X="replace";id=Id_replace; break L;
+ case 'n': X="indexOf";id=Id_indexOf; break L;
+ case 't': X="italics";id=Id_italics; break L;
+ } break L;
+ case 8: c=s.charAt(4);
+ if (c=='r') { X="toString";id=Id_toString; }
+ else if (c=='s') { X="fontsize";id=Id_fontsize; }
+ else if (c=='u') { X="toSource";id=Id_toSource; }
+ break L;
+ case 9: c=s.charAt(0);
+ if (c=='f') { X="fontcolor";id=Id_fontcolor; }
+ else if (c=='s') { X="substring";id=Id_substring; }
+ break L;
+ case 10: X="charCodeAt";id=Id_charCodeAt; break L;
+ case 11: switch (s.charAt(2)) {
+ case 'L': X="toLowerCase";id=Id_toLowerCase; break L;
+ case 'U': X="toUpperCase";id=Id_toUpperCase; break L;
+ case 'n': X="constructor";id=Id_constructor; break L;
+ case 's': X="lastIndexOf";id=Id_lastIndexOf; break L;
+ } break L;
+ case 13: X="localeCompare";id=Id_localeCompare; break L;
+ case 16: X="equalsIgnoreCase";id=Id_equalsIgnoreCase; break L;
+ case 17: c=s.charAt(8);
+ if (c=='L') { X="toLocaleLowerCase";id=Id_toLocaleLowerCase; }
+ else if (c=='U') { X="toLocaleUpperCase";id=Id_toLocaleUpperCase; }
+ break L;
+ }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ break L0;
+ }
+// #/generated#
+ return id;
+ }
+
+ private static final int
+ ConstructorId_fromCharCode = -1,
+
+ Id_constructor = 1,
+ Id_toString = 2,
+ Id_toSource = 3,
+ Id_valueOf = 4,
+ Id_charAt = 5,
+ Id_charCodeAt = 6,
+ Id_indexOf = 7,
+ Id_lastIndexOf = 8,
+ Id_split = 9,
+ Id_substring = 10,
+ Id_toLowerCase = 11,
+ Id_toUpperCase = 12,
+ Id_substr = 13,
+ Id_concat = 14,
+ Id_slice = 15,
+ Id_bold = 16,
+ Id_italics = 17,
+ Id_fixed = 18,
+ Id_strike = 19,
+ Id_small = 20,
+ Id_big = 21,
+ Id_blink = 22,
+ Id_sup = 23,
+ Id_sub = 24,
+ Id_fontsize = 25,
+ Id_fontcolor = 26,
+ Id_link = 27,
+ Id_anchor = 28,
+ Id_equals = 29,
+ Id_equalsIgnoreCase = 30,
+ Id_match = 31,
+ Id_search = 32,
+ Id_replace = 33,
+ Id_localeCompare = 34,
+ Id_toLocaleLowerCase = 35,
+ Id_toLocaleUpperCase = 36,
+ MAX_PROTOTYPE_ID = 36;
+
+// #/string_id_map#
+
+ private static final int
+ ConstructorId_charAt = -Id_charAt,
+ ConstructorId_charCodeAt = -Id_charCodeAt,
+ ConstructorId_indexOf = -Id_indexOf,
+ ConstructorId_lastIndexOf = -Id_lastIndexOf,
+ ConstructorId_split = -Id_split,
+ ConstructorId_substring = -Id_substring,
+ ConstructorId_toLowerCase = -Id_toLowerCase,
+ ConstructorId_toUpperCase = -Id_toUpperCase,
+ ConstructorId_substr = -Id_substr,
+ ConstructorId_concat = -Id_concat,
+ ConstructorId_slice = -Id_slice,
+ ConstructorId_equalsIgnoreCase = -Id_equalsIgnoreCase,
+ ConstructorId_match = -Id_match,
+ ConstructorId_search = -Id_search,
+ ConstructorId_replace = -Id_replace,
+ ConstructorId_localeCompare = -Id_localeCompare,
+ ConstructorId_toLocaleLowerCase = -Id_toLocaleLowerCase;
+
+ private String string;
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeWith.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeWith.java
new file mode 100644
index 0000000..83683b2
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeWith.java
@@ -0,0 +1,207 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.io.Serializable;
+
+/**
+ * This class implements the object lookup required for the
+ * <code>with</code> statement.
+ * It simply delegates every action to its prototype except
+ * for operations on its parent.
+ */
+public class NativeWith implements Scriptable, IdFunctionCall, Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ static void init(Scriptable scope, boolean sealed)
+ {
+ NativeWith obj = new NativeWith();
+
+ obj.setParentScope(scope);
+ obj.setPrototype(ScriptableObject.getObjectPrototype(scope));
+
+ IdFunctionObject ctor = new IdFunctionObject(obj, FTAG, Id_constructor,
+ "With", 0, scope);
+ ctor.markAsConstructor(obj);
+ if (sealed) {
+ ctor.sealObject();
+ }
+ ctor.exportAsScopeProperty();
+ }
+
+ private NativeWith() {
+ }
+
+ protected NativeWith(Scriptable parent, Scriptable prototype) {
+ this.parent = parent;
+ this.prototype = prototype;
+ }
+
+ public String getClassName() {
+ return "With";
+ }
+
+ public boolean has(String id, Scriptable start)
+ {
+ return prototype.has(id, prototype);
+ }
+
+ public boolean has(int index, Scriptable start)
+ {
+ return prototype.has(index, prototype);
+ }
+
+ public Object get(String id, Scriptable start)
+ {
+ if (start == this)
+ start = prototype;
+ return prototype.get(id, start);
+ }
+
+ public Object get(int index, Scriptable start)
+ {
+ if (start == this)
+ start = prototype;
+ return prototype.get(index, start);
+ }
+
+ public void put(String id, Scriptable start, Object value)
+ {
+ if (start == this)
+ start = prototype;
+ prototype.put(id, start, value);
+ }
+
+ public void put(int index, Scriptable start, Object value)
+ {
+ if (start == this)
+ start = prototype;
+ prototype.put(index, start, value);
+ }
+
+ public void delete(String id)
+ {
+ prototype.delete(id);
+ }
+
+ public void delete(int index)
+ {
+ prototype.delete(index);
+ }
+
+ public Scriptable getPrototype() {
+ return prototype;
+ }
+
+ public void setPrototype(Scriptable prototype) {
+ this.prototype = prototype;
+ }
+
+ public Scriptable getParentScope() {
+ return parent;
+ }
+
+ public void setParentScope(Scriptable parent) {
+ this.parent = parent;
+ }
+
+ public Object[] getIds() {
+ return prototype.getIds();
+ }
+
+ public Object getDefaultValue(Class typeHint) {
+ return prototype.getDefaultValue(typeHint);
+ }
+
+ public boolean hasInstance(Scriptable value) {
+ return prototype.hasInstance(value);
+ }
+
+ /**
+ * Must return null to continue looping or the final collection result.
+ */
+ protected Object updateDotQuery(boolean value)
+ {
+ // NativeWith itself does not support it
+ throw new IllegalStateException();
+ }
+
+ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
+ Scriptable thisObj, Object[] args)
+ {
+ if (f.hasTag(FTAG)) {
+ if (f.methodId() == Id_constructor) {
+ throw Context.reportRuntimeError1("msg.cant.call.indirect", "With");
+ }
+ }
+ throw f.unknown();
+ }
+
+ static boolean isWithFunction(Object functionObj)
+ {
+ if (functionObj instanceof IdFunctionObject) {
+ IdFunctionObject f = (IdFunctionObject)functionObj;
+ return f.hasTag(FTAG) && f.methodId() == Id_constructor;
+ }
+ return false;
+ }
+
+ static Object newWithSpecial(Context cx, Scriptable scope, Object[] args)
+ {
+ ScriptRuntime.checkDeprecated(cx, "With");
+ scope = ScriptableObject.getTopLevelScope(scope);
+ NativeWith thisObj = new NativeWith();
+ thisObj.setPrototype(args.length == 0
+ ? ScriptableObject.getClassPrototype(scope,
+ "Object")
+ : ScriptRuntime.toObject(cx, scope, args[0]));
+ thisObj.setParentScope(scope);
+ return thisObj;
+ }
+
+ private static final Object FTAG = new Object();
+
+ private static final int
+ Id_constructor = 1;
+
+ protected Scriptable prototype;
+ protected Scriptable parent;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Node.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Node.java
new file mode 100644
index 0000000..4298388
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Node.java
@@ -0,0 +1,1394 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Roshan James
+ * Roger Lawrence
+ * Mike McCabe
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.util.Map;
+import java.util.LinkedHashMap;
+import java.util.Iterator;
+import java.util.Collections;
+
+/**
+ * This class implements the root of the intermediate representation.
+ *
+ * @author Norris Boyd
+ * @author Mike McCabe
+ */
+
+public class Node
+{
+ public static final int
+ FUNCTION_PROP = 1,
+ LOCAL_PROP = 2,
+ LOCAL_BLOCK_PROP = 3,
+ REGEXP_PROP = 4,
+ CASEARRAY_PROP = 5,
+ /*
+ the following properties are defined and manipulated by the
+ optimizer -
+ TARGETBLOCK_PROP - the block referenced by a branch node
+ VARIABLE_PROP - the variable referenced by a BIND or NAME node
+ ISNUMBER_PROP - this node generates code on Number children and
+ delivers a Number result (as opposed to Objects)
+ DIRECTCALL_PROP - this call node should emit code to test the function
+ object against the known class and call diret if it
+ matches.
+ */
+
+ TARGETBLOCK_PROP = 6,
+ VARIABLE_PROP = 7,
+ ISNUMBER_PROP = 8,
+ DIRECTCALL_PROP = 9,
+ SPECIALCALL_PROP = 10,
+ SKIP_INDEXES_PROP = 11, // array of skipped indexes of array literal
+ OBJECT_IDS_PROP = 12, // array of properties for object literal
+ INCRDECR_PROP = 13, // pre or post type of increment/decerement
+ CATCH_SCOPE_PROP = 14, // index of catch scope block in catch
+ LABEL_ID_PROP = 15, // label id: code generation uses it
+ MEMBER_TYPE_PROP = 16, // type of element access operation
+ NAME_PROP = 17, // property name
+ CONTROL_BLOCK_PROP = 18, // flags a control block that can drop off
+ PARENTHESIZED_PROP = 19, // expression is parenthesized
+ GENERATOR_END_PROP = 20,
+ DESTRUCTURING_ARRAY_LENGTH = 21,
+ DESTRUCTURING_NAMES= 22,
+ LAST_PROP = 22;
+
+ // values of ISNUMBER_PROP to specify
+ // which of the children are Number types
+ public static final int
+ BOTH = 0,
+ LEFT = 1,
+ RIGHT = 2;
+
+ public static final int // values for SPECIALCALL_PROP
+ NON_SPECIALCALL = 0,
+ SPECIALCALL_EVAL = 1,
+ SPECIALCALL_WITH = 2;
+
+ public static final int // flags for INCRDECR_PROP
+ DECR_FLAG = 0x1,
+ POST_FLAG = 0x2;
+
+ public static final int // flags for MEMBER_TYPE_PROP
+ PROPERTY_FLAG = 0x1, // property access: element is valid name
+ ATTRIBUTE_FLAG = 0x2, // x.@y or x..@y
+ DESCENDANTS_FLAG = 0x4; // x..y or x..@i
+
+ private static class NumberNode extends Node
+ {
+ NumberNode(double number)
+ {
+ super(Token.NUMBER);
+ this.number = number;
+ }
+
+ double number;
+ }
+
+ private static class StringNode extends Node
+ {
+ StringNode(int type, String str) {
+ super(type);
+ this.str = str;
+ }
+
+ String str;
+ Node.Scope scope;
+ }
+
+ public static class Jump extends Node
+ {
+ public Jump(int type)
+ {
+ super(type);
+ }
+
+ Jump(int type, int lineno)
+ {
+ super(type, lineno);
+ }
+
+ Jump(int type, Node child)
+ {
+ super(type, child);
+ }
+
+ Jump(int type, Node child, int lineno)
+ {
+ super(type, child, lineno);
+ }
+
+ public final Jump getJumpStatement()
+ {
+ if (!(type == Token.BREAK || type == Token.CONTINUE)) Kit.codeBug();
+ return jumpNode;
+ }
+
+ public final void setJumpStatement(Jump jumpStatement)
+ {
+ if (!(type == Token.BREAK || type == Token.CONTINUE)) Kit.codeBug();
+ if (jumpStatement == null) Kit.codeBug();
+ if (this.jumpNode != null) Kit.codeBug(); //only once
+ this.jumpNode = jumpStatement;
+ }
+
+ public final Node getDefault()
+ {
+ if (!(type == Token.SWITCH)) Kit.codeBug();
+ return target2;
+ }
+
+ public final void setDefault(Node defaultTarget)
+ {
+ if (!(type == Token.SWITCH)) Kit.codeBug();
+ if (defaultTarget.type != Token.TARGET) Kit.codeBug();
+ if (target2 != null) Kit.codeBug(); //only once
+ target2 = defaultTarget;
+ }
+
+ public final Node getFinally()
+ {
+ if (!(type == Token.TRY)) Kit.codeBug();
+ return target2;
+ }
+
+ public final void setFinally(Node finallyTarget)
+ {
+ if (!(type == Token.TRY)) Kit.codeBug();
+ if (finallyTarget.type != Token.TARGET) Kit.codeBug();
+ if (target2 != null) Kit.codeBug(); //only once
+ target2 = finallyTarget;
+ }
+
+ public final Jump getLoop()
+ {
+ if (!(type == Token.LABEL)) Kit.codeBug();
+ return jumpNode;
+ }
+
+ public final void setLoop(Jump loop)
+ {
+ if (!(type == Token.LABEL)) Kit.codeBug();
+ if (loop == null) Kit.codeBug();
+ if (jumpNode != null) Kit.codeBug(); //only once
+ jumpNode = loop;
+ }
+
+ public final Node getContinue()
+ {
+ if (type != Token.LOOP) Kit.codeBug();
+ return target2;
+ }
+
+ public final void setContinue(Node continueTarget)
+ {
+ if (type != Token.LOOP) Kit.codeBug();
+ if (continueTarget.type != Token.TARGET) Kit.codeBug();
+ if (target2 != null) Kit.codeBug(); //only once
+ target2 = continueTarget;
+ }
+
+ public Node target;
+ private Node target2;
+ private Jump jumpNode;
+ }
+
+ static class Symbol {
+ Symbol(int declType, String name) {
+ this.declType = declType;
+ this.name = name;
+ this.index = -1;
+ }
+ /**
+ * One of Token.FUNCTION, Token.LP (for parameters), Token.VAR,
+ * Token.LET, or Token.CONST
+ */
+ int declType;
+ int index;
+ String name;
+ Node.Scope containingTable;
+ }
+
+ static class Scope extends Jump {
+ public Scope(int nodeType) {
+ super(nodeType);
+ }
+
+ public Scope(int nodeType, int lineno) {
+ super(nodeType, lineno);
+ }
+
+ public Scope(int nodeType, Node n, int lineno) {
+ super(nodeType, n, lineno);
+ }
+
+ /*
+ * Creates a new scope node, moving symbol table information
+ * from "scope" to the new node, and making "scope" a nested
+ * scope contained by the new node.
+ * Useful for injecting a new scope in a scope chain.
+ */
+ public static Scope splitScope(Scope scope) {
+ Scope result = new Scope(scope.getType());
+ result.symbolTable = scope.symbolTable;
+ scope.symbolTable = null;
+ result.parent = scope.parent;
+ scope.parent = result;
+ result.top = scope.top;
+ return result;
+ }
+
+ public static void joinScopes(Scope source, Scope dest) {
+ source.ensureSymbolTable();
+ dest.ensureSymbolTable();
+ if (!Collections.disjoint(source.symbolTable.keySet(),
+ dest.symbolTable.keySet()))
+ {
+ throw Kit.codeBug();
+ }
+ dest.symbolTable.putAll(source.symbolTable);
+ }
+
+ public void setParent(Scope parent) {
+ this.parent = parent;
+ this.top = parent == null ? (ScriptOrFnNode)this : parent.top;
+ }
+
+ public Scope getParentScope() {
+ return parent;
+ }
+
+ public Scope getDefiningScope(String name) {
+ for (Scope sn=this; sn != null; sn = sn.parent) {
+ if (sn.symbolTable == null)
+ continue;
+ if (sn.symbolTable.containsKey(name))
+ return sn;
+ }
+ return null;
+ }
+
+ public Symbol getSymbol(String name) {
+ return symbolTable == null ? null : symbolTable.get(name);
+ }
+
+ public void putSymbol(String name, Symbol symbol) {
+ ensureSymbolTable();
+ symbolTable.put(name, symbol);
+ symbol.containingTable = this;
+ top.addSymbol(symbol);
+ }
+
+ public Map<String,Symbol> getSymbolTable() {
+ return symbolTable;
+ }
+
+ private void ensureSymbolTable() {
+ if (symbolTable == null) {
+ symbolTable = new LinkedHashMap<String,Symbol>(5);
+ }
+ }
+
+ // Use LinkedHashMap so that the iteration order is the insertion order
+ protected LinkedHashMap<String,Symbol> symbolTable;
+ private Scope parent;
+ private ScriptOrFnNode top;
+ }
+
+ private static class PropListItem
+ {
+ PropListItem next;
+ int type;
+ int intValue;
+ Object objectValue;
+ }
+
+
+ public Node(int nodeType) {
+ type = nodeType;
+ }
+
+ public Node(int nodeType, Node child) {
+ type = nodeType;
+ first = last = child;
+ child.next = null;
+ }
+
+ public Node(int nodeType, Node left, Node right) {
+ type = nodeType;
+ first = left;
+ last = right;
+ left.next = right;
+ right.next = null;
+ }
+
+ public Node(int nodeType, Node left, Node mid, Node right) {
+ type = nodeType;
+ first = left;
+ last = right;
+ left.next = mid;
+ mid.next = right;
+ right.next = null;
+ }
+
+ public Node(int nodeType, int line) {
+ type = nodeType;
+ lineno = line;
+ }
+
+ public Node(int nodeType, Node child, int line) {
+ this(nodeType, child);
+ lineno = line;
+ }
+
+ public Node(int nodeType, Node left, Node right, int line) {
+ this(nodeType, left, right);
+ lineno = line;
+ }
+
+ public Node(int nodeType, Node left, Node mid, Node right, int line) {
+ this(nodeType, left, mid, right);
+ lineno = line;
+ }
+
+ public static Node newNumber(double number) {
+ return new NumberNode(number);
+ }
+
+ public static Node newString(String str) {
+ return new StringNode(Token.STRING, str);
+ }
+
+ public static Node newString(int type, String str) {
+ return new StringNode(type, str);
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public void setType(int type) {
+ this.type = type;
+ }
+
+ public boolean hasChildren() {
+ return first != null;
+ }
+
+ public Node getFirstChild() {
+ return first;
+ }
+
+ public Node getLastChild() {
+ return last;
+ }
+
+ public Node getNext() {
+ return next;
+ }
+
+ public Node getChildBefore(Node child) {
+ if (child == first)
+ return null;
+ Node n = first;
+ while (n.next != child) {
+ n = n.next;
+ if (n == null)
+ throw new RuntimeException("node is not a child");
+ }
+ return n;
+ }
+
+ public Node getLastSibling() {
+ Node n = this;
+ while (n.next != null) {
+ n = n.next;
+ }
+ return n;
+ }
+
+ public void addChildToFront(Node child) {
+ child.next = first;
+ first = child;
+ if (last == null) {
+ last = child;
+ }
+ }
+
+ public void addChildToBack(Node child) {
+ child.next = null;
+ if (last == null) {
+ first = last = child;
+ return;
+ }
+ last.next = child;
+ last = child;
+ }
+
+ public void addChildrenToFront(Node children) {
+ Node lastSib = children.getLastSibling();
+ lastSib.next = first;
+ first = children;
+ if (last == null) {
+ last = lastSib;
+ }
+ }
+
+ public void addChildrenToBack(Node children) {
+ if (last != null) {
+ last.next = children;
+ }
+ last = children.getLastSibling();
+ if (first == null) {
+ first = children;
+ }
+ }
+
+ /**
+ * Add 'child' before 'node'.
+ */
+ public void addChildBefore(Node newChild, Node node) {
+ if (newChild.next != null)
+ throw new RuntimeException(
+ "newChild had siblings in addChildBefore");
+ if (first == node) {
+ newChild.next = first;
+ first = newChild;
+ return;
+ }
+ Node prev = getChildBefore(node);
+ addChildAfter(newChild, prev);
+ }
+
+ /**
+ * Add 'child' after 'node'.
+ */
+ public void addChildAfter(Node newChild, Node node) {
+ if (newChild.next != null)
+ throw new RuntimeException(
+ "newChild had siblings in addChildAfter");
+ newChild.next = node.next;
+ node.next = newChild;
+ if (last == node)
+ last = newChild;
+ }
+
+ public void removeChild(Node child) {
+ Node prev = getChildBefore(child);
+ if (prev == null)
+ first = first.next;
+ else
+ prev.next = child.next;
+ if (child == last) last = prev;
+ child.next = null;
+ }
+
+ public void replaceChild(Node child, Node newChild) {
+ newChild.next = child.next;
+ if (child == first) {
+ first = newChild;
+ } else {
+ Node prev = getChildBefore(child);
+ prev.next = newChild;
+ }
+ if (child == last)
+ last = newChild;
+ child.next = null;
+ }
+
+ public void replaceChildAfter(Node prevChild, Node newChild) {
+ Node child = prevChild.next;
+ newChild.next = child.next;
+ prevChild.next = newChild;
+ if (child == last)
+ last = newChild;
+ child.next = null;
+ }
+
+ private static final String propToString(int propType)
+ {
+ if (Token.printTrees) {
+ // If Context.printTrees is false, the compiler
+ // can remove all these strings.
+ switch (propType) {
+ case FUNCTION_PROP: return "function";
+ case LOCAL_PROP: return "local";
+ case LOCAL_BLOCK_PROP: return "local_block";
+ case REGEXP_PROP: return "regexp";
+ case CASEARRAY_PROP: return "casearray";
+
+ case TARGETBLOCK_PROP: return "targetblock";
+ case VARIABLE_PROP: return "variable";
+ case ISNUMBER_PROP: return "isnumber";
+ case DIRECTCALL_PROP: return "directcall";
+
+ case SPECIALCALL_PROP: return "specialcall";
+ case SKIP_INDEXES_PROP: return "skip_indexes";
+ case OBJECT_IDS_PROP: return "object_ids_prop";
+ case INCRDECR_PROP: return "incrdecr_prop";
+ case CATCH_SCOPE_PROP: return "catch_scope_prop";
+ case LABEL_ID_PROP: return "label_id_prop";
+ case MEMBER_TYPE_PROP: return "member_type_prop";
+ case NAME_PROP: return "name_prop";
+ case CONTROL_BLOCK_PROP: return "control_block_prop";
+ case PARENTHESIZED_PROP: return "parenthesized_prop";
+ case GENERATOR_END_PROP: return "generator_end";
+ case DESTRUCTURING_ARRAY_LENGTH:
+ return "destructuring_array_length";
+ case DESTRUCTURING_NAMES:return "destructuring_names";
+
+ default: Kit.codeBug();
+ }
+ }
+ return null;
+ }
+
+ private PropListItem lookupProperty(int propType)
+ {
+ PropListItem x = propListHead;
+ while (x != null && propType != x.type) {
+ x = x.next;
+ }
+ return x;
+ }
+
+ private PropListItem ensureProperty(int propType)
+ {
+ PropListItem item = lookupProperty(propType);
+ if (item == null) {
+ item = new PropListItem();
+ item.type = propType;
+ item.next = propListHead;
+ propListHead = item;
+ }
+ return item;
+ }
+
+ public void removeProp(int propType)
+ {
+ PropListItem x = propListHead;
+ if (x != null) {
+ PropListItem prev = null;
+ while (x.type != propType) {
+ prev = x;
+ x = x.next;
+ if (x == null) { return; }
+ }
+ if (prev == null) {
+ propListHead = x.next;
+ } else {
+ prev.next = x.next;
+ }
+ }
+ }
+
+ public Object getProp(int propType)
+ {
+ PropListItem item = lookupProperty(propType);
+ if (item == null) { return null; }
+ return item.objectValue;
+ }
+
+ public int getIntProp(int propType, int defaultValue)
+ {
+ PropListItem item = lookupProperty(propType);
+ if (item == null) { return defaultValue; }
+ return item.intValue;
+ }
+
+ public int getExistingIntProp(int propType)
+ {
+ PropListItem item = lookupProperty(propType);
+ if (item == null) { Kit.codeBug(); }
+ return item.intValue;
+ }
+
+ public void putProp(int propType, Object prop)
+ {
+ if (prop == null) {
+ removeProp(propType);
+ } else {
+ PropListItem item = ensureProperty(propType);
+ item.objectValue = prop;
+ }
+ }
+
+ public void putIntProp(int propType, int prop)
+ {
+ PropListItem item = ensureProperty(propType);
+ item.intValue = prop;
+ }
+
+ public int getLineno() {
+ return lineno;
+ }
+
+ /** Can only be called when <tt>getType() == Token.NUMBER</tt> */
+ public final double getDouble() {
+ return ((NumberNode)this).number;
+ }
+
+ public final void setDouble(double number) {
+ ((NumberNode)this).number = number;
+ }
+
+ /** Can only be called when node has String context. */
+ public final String getString() {
+ return ((StringNode)this).str;
+ }
+
+ /** Can only be called when node has String context. */
+ public final void setString(String s) {
+ if (s == null) Kit.codeBug();
+ ((StringNode)this).str = s;
+ }
+
+ /** Can only be called when node has String context. */
+ public final Scope getScope() {
+ return ((StringNode)this).scope;
+ }
+
+ /** Can only be called when node has String context. */
+ public final void setScope(Scope s) {
+ if (s == null) Kit.codeBug();
+ if (!(this instanceof StringNode)) {
+ throw Kit.codeBug();
+ }
+ ((StringNode)this).scope = s;
+ }
+
+ public static Node newTarget()
+ {
+ return new Node(Token.TARGET);
+ }
+
+ public final int labelId()
+ {
+ if (type != Token.TARGET && type != Token.YIELD) Kit.codeBug();
+ return getIntProp(LABEL_ID_PROP, -1);
+ }
+
+ public void labelId(int labelId)
+ {
+ if (type != Token.TARGET && type != Token.YIELD) Kit.codeBug();
+ putIntProp(LABEL_ID_PROP, labelId);
+ }
+
+
+ /**
+ * Does consistent-return analysis on the function body when strict mode is
+ * enabled.
+ *
+ * function (x) { return (x+1) }
+ * is ok, but
+ * function (x) { if (x < 0) return (x+1); }
+ * is not becuase the function can potentially return a value when the
+ * condition is satisfied and if not, the function does not explicitly
+ * return value.
+ *
+ * This extends to checking mismatches such as "return" and "return <value>"
+ * used in the same function. Warnings are not emitted if inconsistent
+ * returns exist in code that can be statically shown to be unreachable.
+ * Ex.
+ * function (x) { while (true) { ... if (..) { return value } ... } }
+ * emits no warning. However if the loop had a break statement, then a
+ * warning would be emitted.
+ *
+ * The consistency analysis looks at control structures such as loops, ifs,
+ * switch, try-catch-finally blocks, examines the reachable code paths and
+ * warns the user about an inconsistent set of termination possibilities.
+ *
+ * Caveat: Since the parser flattens many control structures into almost
+ * straight-line code with gotos, it makes such analysis hard. Hence this
+ * analyser is written to taken advantage of patterns of code generated by
+ * the parser (for loops, try blocks and such) and does not do a full
+ * control flow analysis of the gotos and break/continue statements.
+ * Future changes to the parser will affect this analysis.
+ */
+
+ /**
+ * These flags enumerate the possible ways a statement/function can
+ * terminate. These flags are used by endCheck() and by the Parser to
+ * detect inconsistent return usage.
+ *
+ * END_UNREACHED is reserved for code paths that are assumed to always be
+ * able to execute (example: throw, continue)
+ *
+ * END_DROPS_OFF indicates if the statement can transfer control to the
+ * next one. Statement such as return dont. A compound statement may have
+ * some branch that drops off control to the next statement.
+ *
+ * END_RETURNS indicates that the statement can return (without arguments)
+ * END_RETURNS_VALUE indicates that the statement can return a value.
+ *
+ * A compound statement such as
+ * if (condition) {
+ * return value;
+ * }
+ * Will be detected as (END_DROPS_OFF | END_RETURN_VALUE) by endCheck()
+ */
+ static final int END_UNREACHED = 0;
+ static final int END_DROPS_OFF = 1;
+ static final int END_RETURNS = 2;
+ static final int END_RETURNS_VALUE = 4;
+ static final int END_YIELDS = 8;
+
+ /**
+ * Checks that every return usage in a function body is consistent with the
+ * requirements of strict-mode.
+ * @return true if the function satisfies strict mode requirement.
+ */
+ public boolean hasConsistentReturnUsage()
+ {
+ int n = endCheck();
+ return (n & END_RETURNS_VALUE) == 0 ||
+ (n & (END_DROPS_OFF|END_RETURNS|END_YIELDS)) == 0;
+ }
+
+ /**
+ * Returns in the then and else blocks must be consistent with each other.
+ * If there is no else block, then the return statement can fall through.
+ * @return logical OR of END_* flags
+ */
+ private int endCheckIf()
+ {
+ Node th, el;
+ int rv = END_UNREACHED;
+
+ th = next;
+ el = ((Jump)this).target;
+
+ rv = th.endCheck();
+
+ if (el != null)
+ rv |= el.endCheck();
+ else
+ rv |= END_DROPS_OFF;
+
+ return rv;
+ }
+
+ /**
+ * Consistency of return statements is checked between the case statements.
+ * If there is no default, then the switch can fall through. If there is a
+ * default,we check to see if all code paths in the default return or if
+ * there is a code path that can fall through.
+ * @return logical OR of END_* flags
+ */
+ private int endCheckSwitch()
+ {
+ Node n;
+ int rv = END_UNREACHED;
+
+ // examine the cases
+ for (n = first.next; n != null; n = n.next)
+ {
+ if (n.type == Token.CASE) {
+ rv |= ((Jump)n).target.endCheck();
+ } else
+ break;
+ }
+
+ // we don't care how the cases drop into each other
+ rv &= ~END_DROPS_OFF;
+
+ // examine the default
+ n = ((Jump)this).getDefault();
+ if (n != null)
+ rv |= n.endCheck();
+ else
+ rv |= END_DROPS_OFF;
+
+ // remove the switch block
+ rv |= getIntProp(CONTROL_BLOCK_PROP, END_UNREACHED);
+
+ return rv;
+ }
+
+ /**
+ * If the block has a finally, return consistency is checked in the
+ * finally block. If all code paths in the finally returns, then the
+ * returns in the try-catch blocks don't matter. If there is a code path
+ * that does not return or if there is no finally block, the returns
+ * of the try and catch blocks are checked for mismatch.
+ * @return logical OR of END_* flags
+ */
+ private int endCheckTry()
+ {
+ Node n;
+ int rv = END_UNREACHED;
+
+ // check the finally if it exists
+ n = ((Jump)this).getFinally();
+ if(n != null) {
+ rv = n.next.first.endCheck();
+ } else {
+ rv = END_DROPS_OFF;
+ }
+
+ // if the finally block always returns, then none of the returns
+ // in the try or catch blocks matter
+ if ((rv & END_DROPS_OFF) != 0) {
+ rv &= ~END_DROPS_OFF;
+
+ // examine the try block
+ rv |= first.endCheck();
+
+ // check each catch block
+ n = ((Jump)this).target;
+ if (n != null)
+ {
+ // point to the first catch_scope
+ for (n = n.next.first; n != null; n = n.next.next)
+ {
+ // check the block of user code in the catch_scope
+ rv |= n.next.first.next.first.endCheck();
+ }
+ }
+ }
+
+ return rv;
+ }
+
+ /**
+ * Return statement in the loop body must be consistent. The default
+ * assumption for any kind of a loop is that it will eventually terminate.
+ * The only exception is a loop with a constant true condition. Code that
+ * follows such a loop is examined only if one can statically determine
+ * that there is a break out of the loop.
+ * for(<> ; <>; <>) {}
+ * for(<> in <> ) {}
+ * while(<>) { }
+ * do { } while(<>)
+ * @return logical OR of END_* flags
+ */
+ private int endCheckLoop()
+ {
+ Node n;
+ int rv = END_UNREACHED;
+
+ // To find the loop body, we look at the second to last node of the
+ // loop node, which should be the predicate that the loop should
+ // satisfy.
+ // The target of the predicate is the loop-body for all 4 kinds of
+ // loops.
+ for (n = first; n.next != last; n = n.next) {
+ /* skip */
+ }
+ if (n.type != Token.IFEQ)
+ return END_DROPS_OFF;
+
+ // The target's next is the loop body block
+ rv = ((Jump)n).target.next.endCheck();
+
+ // check to see if the loop condition is true
+ if (n.first.type == Token.TRUE)
+ rv &= ~END_DROPS_OFF;
+
+ // look for effect of breaks
+ rv |= getIntProp(CONTROL_BLOCK_PROP, END_UNREACHED);
+
+ return rv;
+ }
+
+
+ /**
+ * A general block of code is examined statement by statement. If any
+ * statement (even compound ones) returns in all branches, then subsequent
+ * statements are not examined.
+ * @return logical OR of END_* flags
+ */
+ private int endCheckBlock()
+ {
+ Node n;
+ int rv = END_DROPS_OFF;
+
+ // check each statment and if the statement can continue onto the next
+ // one, then check the next statement
+ for (n=first; ((rv & END_DROPS_OFF) != 0) && n != null; n = n.next)
+ {
+ rv &= ~END_DROPS_OFF;
+ rv |= n.endCheck();
+ }
+ return rv;
+ }
+
+ /**
+ * A labelled statement implies that there maybe a break to the label. The
+ * function processes the labelled statement and then checks the
+ * CONTROL_BLOCK_PROP property to see if there is ever a break to the
+ * particular label.
+ * @return logical OR of END_* flags
+ */
+ private int endCheckLabel()
+ {
+ int rv = END_UNREACHED;
+
+ rv = next.endCheck();
+ rv |= getIntProp(CONTROL_BLOCK_PROP, END_UNREACHED);
+
+ return rv;
+ }
+
+ /**
+ * When a break is encountered annotate the statement being broken
+ * out of by setting its CONTROL_BLOCK_PROP property.
+ * @return logical OR of END_* flags
+ */
+ private int endCheckBreak()
+ {
+ Node n = ((Jump) this).jumpNode;
+ n.putIntProp(CONTROL_BLOCK_PROP, END_DROPS_OFF);
+ return END_UNREACHED;
+ }
+
+ /**
+ * endCheck() examines the body of a function, doing a basic reachability
+ * analysis and returns a combination of flags END_* flags that indicate
+ * how the function execution can terminate. These constitute only the
+ * pessimistic set of termination conditions. It is possible that at
+ * runtime certain code paths will never be actually taken. Hence this
+ * analysis will flag errors in cases where there may not be errors.
+ * @return logical OR of END_* flags
+ */
+ private int endCheck()
+ {
+ switch(type)
+ {
+ case Token.BREAK:
+ return endCheckBreak();
+
+ case Token.EXPR_VOID:
+ if (this.first != null)
+ return first.endCheck();
+ return END_DROPS_OFF;
+
+ case Token.YIELD:
+ return END_YIELDS;
+
+ case Token.CONTINUE:
+ case Token.THROW:
+ return END_UNREACHED;
+
+ case Token.RETURN:
+ if (this.first != null)
+ return END_RETURNS_VALUE;
+ else
+ return END_RETURNS;
+
+ case Token.TARGET:
+ if (next != null)
+ return next.endCheck();
+ else
+ return END_DROPS_OFF;
+
+ case Token.LOOP:
+ return endCheckLoop();
+
+ case Token.LOCAL_BLOCK:
+ case Token.BLOCK:
+ // there are several special kinds of blocks
+ if (first == null)
+ return END_DROPS_OFF;
+
+ switch(first.type) {
+ case Token.LABEL:
+ return first.endCheckLabel();
+
+ case Token.IFNE:
+ return first.endCheckIf();
+
+ case Token.SWITCH:
+ return first.endCheckSwitch();
+
+ case Token.TRY:
+ return first.endCheckTry();
+
+ default:
+ return endCheckBlock();
+ }
+
+ default:
+ return END_DROPS_OFF;
+ }
+ }
+
+ public boolean hasSideEffects()
+ {
+ switch (type) {
+ case Token.EXPR_VOID:
+ case Token.COMMA:
+ if (last != null)
+ return last.hasSideEffects();
+ else
+ return true;
+
+ case Token.HOOK:
+ if (first == null ||
+ first.next == null ||
+ first.next.next == null)
+ Kit.codeBug();
+ return first.next.hasSideEffects() &&
+ first.next.next.hasSideEffects();
+
+ case Token.ERROR: // Avoid cascaded error messages
+ case Token.EXPR_RESULT:
+ case Token.ASSIGN:
+ case Token.ASSIGN_ADD:
+ case Token.ASSIGN_SUB:
+ case Token.ASSIGN_MUL:
+ case Token.ASSIGN_DIV:
+ case Token.ASSIGN_MOD:
+ case Token.ASSIGN_BITOR:
+ case Token.ASSIGN_BITXOR:
+ case Token.ASSIGN_BITAND:
+ case Token.ASSIGN_LSH:
+ case Token.ASSIGN_RSH:
+ case Token.ASSIGN_URSH:
+ case Token.ENTERWITH:
+ case Token.LEAVEWITH:
+ case Token.RETURN:
+ case Token.GOTO:
+ case Token.IFEQ:
+ case Token.IFNE:
+ case Token.NEW:
+ case Token.DELPROP:
+ case Token.SETNAME:
+ case Token.SETPROP:
+ case Token.SETELEM:
+ case Token.CALL:
+ case Token.THROW:
+ case Token.RETHROW:
+ case Token.SETVAR:
+ case Token.CATCH_SCOPE:
+ case Token.RETURN_RESULT:
+ case Token.SET_REF:
+ case Token.DEL_REF:
+ case Token.REF_CALL:
+ case Token.TRY:
+ case Token.SEMI:
+ case Token.INC:
+ case Token.DEC:
+ case Token.EXPORT:
+ case Token.IMPORT:
+ case Token.IF:
+ case Token.ELSE:
+ case Token.SWITCH:
+ case Token.WHILE:
+ case Token.DO:
+ case Token.FOR:
+ case Token.BREAK:
+ case Token.CONTINUE:
+ case Token.VAR:
+ case Token.CONST:
+ case Token.LET:
+ case Token.LETEXPR:
+ case Token.WITH:
+ case Token.WITHEXPR:
+ case Token.CATCH:
+ case Token.FINALLY:
+ case Token.BLOCK:
+ case Token.LABEL:
+ case Token.TARGET:
+ case Token.LOOP:
+ case Token.JSR:
+ case Token.SETPROP_OP:
+ case Token.SETELEM_OP:
+ case Token.LOCAL_BLOCK:
+ case Token.SET_REF_OP:
+ case Token.YIELD:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ public String toString()
+ {
+ if (Token.printTrees) {
+ StringBuffer sb = new StringBuffer();
+ toString(new ObjToIntMap(), sb);
+ return sb.toString();
+ }
+ return String.valueOf(type);
+ }
+
+ private void toString(ObjToIntMap printIds, StringBuffer sb)
+ {
+ if (Token.printTrees) {
+ sb.append(Token.name(type));
+ if (this instanceof StringNode) {
+ sb.append(' ');
+ sb.append(getString());
+ Scope scope = getScope();
+ if (scope != null) {
+ sb.append("[scope: ");
+ appendPrintId(scope, printIds, sb);
+ sb.append("]");
+ }
+ } else if (this instanceof Node.Scope) {
+ if (this instanceof ScriptOrFnNode) {
+ ScriptOrFnNode sof = (ScriptOrFnNode)this;
+ if (this instanceof FunctionNode) {
+ FunctionNode fn = (FunctionNode)this;
+ sb.append(' ');
+ sb.append(fn.getFunctionName());
+ }
+ sb.append(" [source name: ");
+ sb.append(sof.getSourceName());
+ sb.append("] [encoded source length: ");
+ sb.append(sof.getEncodedSourceEnd()
+ - sof.getEncodedSourceStart());
+ sb.append("] [base line: ");
+ sb.append(sof.getBaseLineno());
+ sb.append("] [end line: ");
+ sb.append(sof.getEndLineno());
+ sb.append(']');
+ }
+ if (((Node.Scope)this).symbolTable != null) {
+ sb.append(" [scope ");
+ appendPrintId(this, printIds, sb);
+ sb.append(": ");
+ Iterator iter = ((Node.Scope) this).symbolTable.keySet()
+ .iterator();
+ while (iter.hasNext()) {
+ sb.append(iter.next());
+ sb.append(" ");
+ }
+ sb.append("]");
+ }
+ } else if (this instanceof Jump) {
+ Jump jump = (Jump)this;
+ if (type == Token.BREAK || type == Token.CONTINUE) {
+ sb.append(" [label: ");
+ appendPrintId(jump.getJumpStatement(), printIds, sb);
+ sb.append(']');
+ } else if (type == Token.TRY) {
+ Node catchNode = jump.target;
+ Node finallyTarget = jump.getFinally();
+ if (catchNode != null) {
+ sb.append(" [catch: ");
+ appendPrintId(catchNode, printIds, sb);
+ sb.append(']');
+ }
+ if (finallyTarget != null) {
+ sb.append(" [finally: ");
+ appendPrintId(finallyTarget, printIds, sb);
+ sb.append(']');
+ }
+ } else if (type == Token.LABEL || type == Token.LOOP
+ || type == Token.SWITCH)
+ {
+ sb.append(" [break: ");
+ appendPrintId(jump.target, printIds, sb);
+ sb.append(']');
+ if (type == Token.LOOP) {
+ sb.append(" [continue: ");
+ appendPrintId(jump.getContinue(), printIds, sb);
+ sb.append(']');
+ }
+ } else {
+ sb.append(" [target: ");
+ appendPrintId(jump.target, printIds, sb);
+ sb.append(']');
+ }
+ } else if (type == Token.NUMBER) {
+ sb.append(' ');
+ sb.append(getDouble());
+ } else if (type == Token.TARGET) {
+ sb.append(' ');
+ appendPrintId(this, printIds, sb);
+ }
+ if (lineno != -1) {
+ sb.append(' ');
+ sb.append(lineno);
+ }
+
+ for (PropListItem x = propListHead; x != null; x = x.next) {
+ int type = x.type;
+ sb.append(" [");
+ sb.append(propToString(type));
+ sb.append(": ");
+ String value;
+ switch (type) {
+ case TARGETBLOCK_PROP : // can't add this as it recurses
+ value = "target block property";
+ break;
+ case LOCAL_BLOCK_PROP : // can't add this as it is dull
+ value = "last local block";
+ break;
+ case ISNUMBER_PROP:
+ switch (x.intValue) {
+ case BOTH:
+ value = "both";
+ break;
+ case RIGHT:
+ value = "right";
+ break;
+ case LEFT:
+ value = "left";
+ break;
+ default:
+ throw Kit.codeBug();
+ }
+ break;
+ case SPECIALCALL_PROP:
+ switch (x.intValue) {
+ case SPECIALCALL_EVAL:
+ value = "eval";
+ break;
+ case SPECIALCALL_WITH:
+ value = "with";
+ break;
+ default:
+ // NON_SPECIALCALL should not be stored
+ throw Kit.codeBug();
+ }
+ break;
+ case OBJECT_IDS_PROP: {
+ Object[] a = (Object[]) x.objectValue;
+ value = "[";
+ for (int i=0; i < a.length; i++) {
+ value += a[i].toString();
+ if (i+1 < a.length)
+ value += ", ";
+ }
+ value += "]";
+ break;
+ }
+ default :
+ Object obj = x.objectValue;
+ if (obj != null) {
+ value = obj.toString();
+ } else {
+ value = String.valueOf(x.intValue);
+ }
+ break;
+ }
+ sb.append(value);
+ sb.append(']');
+ }
+ }
+ }
+
+ public String toStringTree(ScriptOrFnNode treeTop) {
+ if (Token.printTrees) {
+ StringBuffer sb = new StringBuffer();
+ toStringTreeHelper(treeTop, this, null, 0, sb);
+ return sb.toString();
+ }
+ return null;
+ }
+
+ private static void toStringTreeHelper(ScriptOrFnNode treeTop, Node n,
+ ObjToIntMap printIds,
+ int level, StringBuffer sb)
+ {
+ if (Token.printTrees) {
+ if (printIds == null) {
+ printIds = new ObjToIntMap();
+ generatePrintIds(treeTop, printIds);
+ }
+ for (int i = 0; i != level; ++i) {
+ sb.append(" ");
+ }
+ n.toString(printIds, sb);
+ sb.append('\n');
+ for (Node cursor = n.getFirstChild(); cursor != null;
+ cursor = cursor.getNext())
+ {
+ if (cursor.getType() == Token.FUNCTION) {
+ int fnIndex = cursor.getExistingIntProp(Node.FUNCTION_PROP);
+ FunctionNode fn = treeTop.getFunctionNode(fnIndex);
+ toStringTreeHelper(fn, fn, null, level + 1, sb);
+ } else {
+ toStringTreeHelper(treeTop, cursor, printIds, level + 1, sb);
+ }
+ }
+ }
+ }
+
+ private static void generatePrintIds(Node n, ObjToIntMap map)
+ {
+ if (Token.printTrees) {
+ map.put(n, map.size());
+ for (Node cursor = n.getFirstChild(); cursor != null;
+ cursor = cursor.getNext())
+ {
+ generatePrintIds(cursor, map);
+ }
+ }
+ }
+
+ private static void appendPrintId(Node n, ObjToIntMap printIds,
+ StringBuffer sb)
+ {
+ if (Token.printTrees) {
+ if (n != null) {
+ int id = printIds.get(n, -1);
+ sb.append('#');
+ if (id != -1) {
+ sb.append(id + 1);
+ } else {
+ sb.append("<not_available>");
+ }
+ }
+ }
+ }
+
+ int type; // type of the node; Token.NAME for example
+ Node next; // next sibling
+ private Node first; // first element of a linked list of children
+ private Node last; // last element of a linked list of children
+ protected int lineno = -1;
+
+ /*APPJET*/public int statementEndLineNum = -1;
+
+ /**
+ * Linked list of properties. Since vast majority of nodes would have
+ * no more then 2 properties, linked list saves memory and provides
+ * fast lookup. If this does not holds, propListHead can be replaced
+ * by UintMap.
+ */
+ private PropListItem propListHead;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NodeTransformer.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NodeTransformer.java
new file mode 100644
index 0000000..201c6f2
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NodeTransformer.java
@@ -0,0 +1,565 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ * Bob Jervis
+ * Roger Lawrence
+ * Mike McCabe
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class transforms a tree to a lower-level representation for codegen.
+ *
+ * @see Node
+ * @author Norris Boyd
+ */
+
+public class NodeTransformer
+{
+
+ public NodeTransformer()
+ {
+ }
+
+ public final void transform(ScriptOrFnNode tree)
+ {
+ transformCompilationUnit(tree);
+ for (int i = 0; i != tree.getFunctionCount(); ++i) {
+ FunctionNode fn = tree.getFunctionNode(i);
+ transform(fn);
+ }
+ }
+
+ private void transformCompilationUnit(ScriptOrFnNode tree)
+ {
+ loops = new ObjArray();
+ loopEnds = new ObjArray();
+
+ // to save against upchecks if no finally blocks are used.
+ hasFinally = false;
+
+ // Flatten all only if we are not using scope objects for block scope
+ boolean createScopeObjects = tree.getType() != Token.FUNCTION ||
+ ((FunctionNode)tree).requiresActivation();
+ tree.flattenSymbolTable(!createScopeObjects);
+
+ //uncomment to print tree before transformation
+ //if (Token.printTrees) System.out.println(tree.toStringTree(tree));
+ transformCompilationUnit_r(tree, tree, tree, createScopeObjects);
+ }
+
+ private void transformCompilationUnit_r(final ScriptOrFnNode tree,
+ final Node parent,
+ Node.Scope scope,
+ boolean createScopeObjects)
+ {
+ Node node = null;
+ siblingLoop:
+ for (;;) {
+ Node previous = null;
+ if (node == null) {
+ node = parent.getFirstChild();
+ } else {
+ previous = node;
+ node = node.getNext();
+ }
+ if (node == null) {
+ break;
+ }
+
+ int type = node.getType();
+ if (createScopeObjects &&
+ (type == Token.BLOCK || type == Token.LOOP ||
+ type == Token.ARRAYCOMP) &&
+ (node instanceof Node.Scope))
+ {
+ Node.Scope newScope = (Node.Scope) node;
+ if (newScope.symbolTable != null) {
+ // transform to let statement so we get a with statement
+ // created to contain scoped let variables
+ Node let = new Node(type == Token.ARRAYCOMP ? Token.LETEXPR
+ : Token.LET);
+ Node innerLet = new Node(Token.LET);
+ let.addChildToBack(innerLet);
+ for (String name: newScope.symbolTable.keySet()) {
+ innerLet.addChildToBack(Node.newString(Token.NAME, name));
+ }
+ newScope.symbolTable = null; // so we don't transform again
+ Node oldNode = node;
+ node = replaceCurrent(parent, previous, node, let);
+ type = node.getType();
+ let.addChildToBack(oldNode);
+ }
+ }
+
+ switch (type) {
+
+ case Token.LABEL:
+ case Token.SWITCH:
+ case Token.LOOP:
+ loops.push(node);
+ loopEnds.push(((Node.Jump)node).target);
+ break;
+
+ case Token.WITH:
+ {
+ loops.push(node);
+ Node leave = node.getNext();
+ if (leave.getType() != Token.LEAVEWITH) {
+ Kit.codeBug();
+ }
+ loopEnds.push(leave);
+ break;
+ }
+
+ case Token.TRY:
+ {
+ Node.Jump jump = (Node.Jump)node;
+ Node finallytarget = jump.getFinally();
+ if (finallytarget != null) {
+ hasFinally = true;
+ loops.push(node);
+ loopEnds.push(finallytarget);
+ }
+ break;
+ }
+
+ case Token.TARGET:
+ case Token.LEAVEWITH:
+ if (!loopEnds.isEmpty() && loopEnds.peek() == node) {
+ loopEnds.pop();
+ loops.pop();
+ }
+ break;
+
+ case Token.YIELD:
+ ((FunctionNode)tree).addResumptionPoint(node);
+ break;
+
+ case Token.RETURN:
+ {
+ boolean isGenerator = tree.getType() == Token.FUNCTION
+ && ((FunctionNode)tree).isGenerator();
+ if (isGenerator) {
+ node.putIntProp(Node.GENERATOR_END_PROP, 1);
+ }
+ /* If we didn't support try/finally, it wouldn't be
+ * necessary to put LEAVEWITH nodes here... but as
+ * we do need a series of JSR FINALLY nodes before
+ * each RETURN, we need to ensure that each finally
+ * block gets the correct scope... which could mean
+ * that some LEAVEWITH nodes are necessary.
+ */
+ if (!hasFinally)
+ break; // skip the whole mess.
+ Node unwindBlock = null;
+ for (int i=loops.size()-1; i >= 0; i--) {
+ Node n = (Node) loops.get(i);
+ int elemtype = n.getType();
+ if (elemtype == Token.TRY || elemtype == Token.WITH) {
+ Node unwind;
+ if (elemtype == Token.TRY) {
+ Node.Jump jsrnode = new Node.Jump(Token.JSR);
+ Node jsrtarget = ((Node.Jump)n).getFinally();
+ jsrnode.target = jsrtarget;
+ unwind = jsrnode;
+ } else {
+ unwind = new Node(Token.LEAVEWITH);
+ }
+ if (unwindBlock == null) {
+ unwindBlock = new Node(Token.BLOCK,
+ node.getLineno());
+ }
+ unwindBlock.addChildToBack(unwind);
+ }
+ }
+ if (unwindBlock != null) {
+ Node returnNode = node;
+ Node returnExpr = returnNode.getFirstChild();
+ node = replaceCurrent(parent, previous, node, unwindBlock);
+ if (returnExpr == null || isGenerator) {
+ unwindBlock.addChildToBack(returnNode);
+ } else {
+ Node store = new Node(Token.EXPR_RESULT, returnExpr);
+ unwindBlock.addChildToFront(store);
+ returnNode = new Node(Token.RETURN_RESULT);
+ unwindBlock.addChildToBack(returnNode);
+ // transform return expression
+ transformCompilationUnit_r(tree, store, scope,
+ createScopeObjects);
+ }
+ // skip transformCompilationUnit_r to avoid infinite loop
+ continue siblingLoop;
+ }
+ break;
+ }
+
+ case Token.BREAK:
+ case Token.CONTINUE:
+ {
+ Node.Jump jump = (Node.Jump)node;
+ Node.Jump jumpStatement = jump.getJumpStatement();
+ if (jumpStatement == null) Kit.codeBug();
+
+ for (int i = loops.size(); ;) {
+ if (i == 0) {
+ // Parser/IRFactory ensure that break/continue
+ // always has a jump statement associated with it
+ // which should be found
+ throw Kit.codeBug();
+ }
+ --i;
+ Node n = (Node) loops.get(i);
+ if (n == jumpStatement) {
+ break;
+ }
+
+ int elemtype = n.getType();
+ if (elemtype == Token.WITH) {
+ Node leave = new Node(Token.LEAVEWITH);
+ previous = addBeforeCurrent(parent, previous, node,
+ leave);
+ } else if (elemtype == Token.TRY) {
+ Node.Jump tryNode = (Node.Jump)n;
+ Node.Jump jsrFinally = new Node.Jump(Token.JSR);
+ jsrFinally.target = tryNode.getFinally();
+ previous = addBeforeCurrent(parent, previous, node,
+ jsrFinally);
+ }
+ }
+
+ if (type == Token.BREAK) {
+ jump.target = jumpStatement.target;
+ } else {
+ jump.target = jumpStatement.getContinue();
+ }
+ jump.setType(Token.GOTO);
+
+ break;
+ }
+
+ case Token.CALL:
+ visitCall(node, tree);
+ break;
+
+ case Token.NEW:
+ visitNew(node, tree);
+ break;
+
+ case Token.LETEXPR:
+ case Token.LET: {
+ Node child = node.getFirstChild();
+ if (child.getType() == Token.LET) {
+ // We have a let statement or expression rather than a
+ // let declaration
+ boolean createWith = tree.getType() != Token.FUNCTION
+ || ((FunctionNode)tree).requiresActivation();
+ node = visitLet(createWith, parent, previous, node);
+ break;
+ } else {
+ // fall through to process let declaration...
+ }
+ }
+ /* fall through */
+ case Token.CONST:
+ case Token.VAR:
+ {
+ Node result = new Node(Token.BLOCK);
+ for (Node cursor = node.getFirstChild(); cursor != null;) {
+ // Move cursor to next before createAssignment gets chance
+ // to change n.next
+ Node n = cursor;
+ cursor = cursor.getNext();
+ if (n.getType() == Token.NAME) {
+ if (!n.hasChildren())
+ continue;
+ Node init = n.getFirstChild();
+ n.removeChild(init);
+ n.setType(Token.BINDNAME);
+ n = new Node(type == Token.CONST ?
+ Token.SETCONST :
+ Token.SETNAME,
+ n, init);
+ } else {
+ // May be a destructuring assignment already transformed
+ // to a LETEXPR
+ if (n.getType() != Token.LETEXPR)
+ throw Kit.codeBug();
+ }
+ Node pop = new Node(Token.EXPR_VOID, n, node.getLineno());
+ result.addChildToBack(pop);
+ }
+ node = replaceCurrent(parent, previous, node, result);
+ break;
+ }
+
+ case Token.TYPEOFNAME: {
+ Node.Scope defining = scope.getDefiningScope(node.getString());
+ if (defining != null) {
+ node.setScope(defining);
+ }
+ }
+ break;
+
+ case Token.TYPEOF:
+ case Token.IFNE: {
+ /* We want to suppress warnings for undefined property o.p
+ * for the following constructs: typeof o.p, if (o.p),
+ * if (!o.p), if (o.p == undefined), if (undefined == o.p)
+ */
+ Node child = node.getFirstChild();
+ if (type == Token.IFNE) {
+ while (child.getType() == Token.NOT) {
+ child = child.getFirstChild();
+ }
+ if (child.getType() == Token.EQ ||
+ child.getType() == Token.NE)
+ {
+ Node first = child.getFirstChild();
+ Node last = child.getLastChild();
+ if (first.getType() == Token.NAME &&
+ first.getString().equals("undefined"))
+ child = last;
+ else if (last.getType() == Token.NAME &&
+ last.getString().equals("undefined"))
+ child = first;
+ }
+ }
+ if (child.getType() == Token.GETPROP)
+ child.setType(Token.GETPROPNOWARN);
+ break;
+ }
+
+ case Token.NAME:
+ case Token.SETNAME:
+ case Token.SETCONST:
+ case Token.DELPROP:
+ {
+ // Turn name to var for faster access if possible
+ if (createScopeObjects) {
+ break;
+ }
+ Node nameSource;
+ if (type == Token.NAME) {
+ nameSource = node;
+ } else {
+ nameSource = node.getFirstChild();
+ if (nameSource.getType() != Token.BINDNAME) {
+ if (type == Token.DELPROP) {
+ break;
+ }
+ throw Kit.codeBug();
+ }
+ }
+ if (nameSource.getScope() != null) {
+ break; // already have a scope set
+ }
+ String name = nameSource.getString();
+ Node.Scope defining = scope.getDefiningScope(name);
+ if (defining != null) {
+ nameSource.setScope(defining);
+ if (type == Token.NAME) {
+ node.setType(Token.GETVAR);
+ } else if (type == Token.SETNAME) {
+ node.setType(Token.SETVAR);
+ nameSource.setType(Token.STRING);
+ } else if (type == Token.SETCONST) {
+ node.setType(Token.SETCONSTVAR);
+ nameSource.setType(Token.STRING);
+ } else if (type == Token.DELPROP) {
+ // Local variables are by definition permanent
+ Node n = new Node(Token.FALSE);
+ node = replaceCurrent(parent, previous, node, n);
+ } else {
+ throw Kit.codeBug();
+ }
+ }
+ break;
+ }
+ }
+
+ transformCompilationUnit_r(tree, node,
+ node instanceof Node.Scope ? (Node.Scope)node : scope,
+ createScopeObjects);
+ }
+ }
+
+ protected void visitNew(Node node, ScriptOrFnNode tree) {
+ }
+
+ protected void visitCall(Node node, ScriptOrFnNode tree) {
+ }
+
+ protected Node visitLet(boolean createWith, Node parent, Node previous,
+ Node scopeNode)
+ {
+ Node vars = scopeNode.getFirstChild();
+ Node body = vars.getNext();
+ scopeNode.removeChild(vars);
+ scopeNode.removeChild(body);
+ boolean isExpression = scopeNode.getType() == Token.LETEXPR;
+ Node result;
+ Node newVars;
+ if (createWith) {
+ result = new Node(isExpression ? Token.WITHEXPR : Token.BLOCK);
+ result = replaceCurrent(parent, previous, scopeNode, result);
+ ArrayList<Object> list = new ArrayList<Object>();
+ Node objectLiteral = new Node(Token.OBJECTLIT);
+ for (Node v=vars.getFirstChild(); v != null; v = v.getNext()) {
+ Node current = v;
+ if (current.getType() == Token.LETEXPR) {
+ // destructuring in let expr, e.g. let ([x, y] = [3, 4]) {}
+ List<?> destructuringNames = (List<?>)
+ current.getProp(Node.DESTRUCTURING_NAMES);
+ Node c = current.getFirstChild();
+ if (c.getType() != Token.LET) throw Kit.codeBug();
+ // Add initialization code to front of body
+ if (isExpression) {
+ body = new Node(Token.COMMA, c.getNext(), body);
+ } else {
+ body = new Node(Token.BLOCK,
+ new Node(Token.EXPR_VOID, c.getNext()),
+ body);
+ }
+ // Update "list" and "objectLiteral" for the variables
+ // defined in the destructuring assignment
+ if (destructuringNames != null) {
+ list.addAll(destructuringNames);
+ for (int i=0; i < destructuringNames.size(); i++) {
+ objectLiteral.addChildToBack(
+ new Node(Token.VOID, Node.newNumber(0.0)));
+ }
+ }
+ current = c.getFirstChild(); // should be a NAME, checked below
+ }
+ if (current.getType() != Token.NAME) throw Kit.codeBug();
+ list.add(ScriptRuntime.getIndexObject(current.getString()));
+ Node init = current.getFirstChild();
+ if (init == null) {
+ init = new Node(Token.VOID, Node.newNumber(0.0));
+ }
+ objectLiteral.addChildToBack(init);
+ }
+ objectLiteral.putProp(Node.OBJECT_IDS_PROP, list.toArray());
+ newVars = new Node(Token.ENTERWITH, objectLiteral);
+ result.addChildToBack(newVars);
+ result.addChildToBack(new Node(Token.WITH, body));
+ result.addChildToBack(new Node(Token.LEAVEWITH));
+ } else {
+ result = new Node(isExpression ? Token.COMMA : Token.BLOCK);
+ result = replaceCurrent(parent, previous, scopeNode, result);
+ newVars = new Node(Token.COMMA);
+ for (Node v=vars.getFirstChild(); v != null; v = v.getNext()) {
+ Node current = v;
+ if (current.getType() == Token.LETEXPR) {
+ // destructuring in let expr, e.g. let ([x, y] = [3, 4]) {}
+ Node c = current.getFirstChild();
+ if (c.getType() != Token.LET) throw Kit.codeBug();
+ // Add initialization code to front of body
+ if (isExpression) {
+ body = new Node(Token.COMMA, c.getNext(), body);
+ } else {
+ body = new Node(Token.BLOCK,
+ new Node(Token.EXPR_VOID, c.getNext()),
+ body);
+ }
+ // We're removing the LETEXPR, so move the symbols
+ Node.Scope.joinScopes((Node.Scope)current,
+ (Node.Scope)scopeNode);
+ current = c.getFirstChild(); // should be a NAME, checked below
+ }
+ if (current.getType() != Token.NAME) throw Kit.codeBug();
+ Node stringNode = Node.newString(current.getString());
+ stringNode.setScope((Node.Scope)scopeNode);
+ Node init = current.getFirstChild();
+ if (init == null) {
+ init = new Node(Token.VOID, Node.newNumber(0.0));
+ }
+ newVars.addChildToBack(new Node(Token.SETVAR, stringNode, init));
+ }
+ if (isExpression) {
+ result.addChildToBack(newVars);
+ scopeNode.setType(Token.COMMA);
+ result.addChildToBack(scopeNode);
+ scopeNode.addChildToBack(body);
+ } else {
+ result.addChildToBack(new Node(Token.EXPR_VOID, newVars));
+ scopeNode.setType(Token.BLOCK);
+ result.addChildToBack(scopeNode);
+ scopeNode.addChildrenToBack(body);
+ }
+ }
+ return result;
+ }
+
+ private static Node addBeforeCurrent(Node parent, Node previous,
+ Node current, Node toAdd)
+ {
+ if (previous == null) {
+ if (!(current == parent.getFirstChild())) Kit.codeBug();
+ parent.addChildToFront(toAdd);
+ } else {
+ if (!(current == previous.getNext())) Kit.codeBug();
+ parent.addChildAfter(toAdd, previous);
+ }
+ return toAdd;
+ }
+
+ private static Node replaceCurrent(Node parent, Node previous,
+ Node current, Node replacement)
+ {
+ if (previous == null) {
+ if (!(current == parent.getFirstChild())) Kit.codeBug();
+ parent.replaceChild(current, replacement);
+ } else if (previous.next == current) {
+ // Check cachedPrev.next == current is necessary due to possible
+ // tree mutations
+ parent.replaceChildAfter(previous, replacement);
+ } else {
+ parent.replaceChild(current, replacement);
+ }
+ return replacement;
+ }
+
+ private ObjArray loops;
+ private ObjArray loopEnds;
+ private boolean hasFinally;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ObjArray.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ObjArray.java
new file mode 100644
index 0000000..a9636a3
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ObjArray.java
@@ -0,0 +1,388 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.io.Serializable;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+/**
+Implementation of resizable array with focus on minimizing memory usage by storing few initial array elements in object fields. Can also be used as a stack.
+*/
+
+public class ObjArray implements Serializable
+{
+ static final long serialVersionUID = 4174889037736658296L;
+
+ public ObjArray() { }
+
+ public final boolean isSealed()
+ {
+ return sealed;
+ }
+
+ public final void seal()
+ {
+ sealed = true;
+ }
+
+ public final boolean isEmpty()
+ {
+ return size == 0;
+ }
+
+ public final int size()
+ {
+ return size;
+ }
+
+ public final void setSize(int newSize)
+ {
+ if (newSize < 0) throw new IllegalArgumentException();
+ if (sealed) throw onSeledMutation();
+ int N = size;
+ if (newSize < N) {
+ for (int i = newSize; i != N; ++i) {
+ setImpl(i, null);
+ }
+ } else if (newSize > N) {
+ if (newSize > FIELDS_STORE_SIZE) {
+ ensureCapacity(newSize);
+ }
+ }
+ size = newSize;
+ }
+
+ public final Object get(int index)
+ {
+ if (!(0 <= index && index < size)) throw onInvalidIndex(index, size);
+ return getImpl(index);
+ }
+
+ public final void set(int index, Object value)
+ {
+ if (!(0 <= index && index < size)) throw onInvalidIndex(index, size);
+ if (sealed) throw onSeledMutation();
+ setImpl(index, value);
+ }
+
+ private Object getImpl(int index)
+ {
+ switch (index) {
+ case 0: return f0;
+ case 1: return f1;
+ case 2: return f2;
+ case 3: return f3;
+ case 4: return f4;
+ }
+ return data[index - FIELDS_STORE_SIZE];
+ }
+
+ private void setImpl(int index, Object value)
+ {
+ switch (index) {
+ case 0: f0 = value; break;
+ case 1: f1 = value; break;
+ case 2: f2 = value; break;
+ case 3: f3 = value; break;
+ case 4: f4 = value; break;
+ default: data[index - FIELDS_STORE_SIZE] = value;
+ }
+
+ }
+
+ public int indexOf(Object obj)
+ {
+ int N = size;
+ for (int i = 0; i != N; ++i) {
+ Object current = getImpl(i);
+ if (current == obj || (current != null && current.equals(obj))) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public int lastIndexOf(Object obj)
+ {
+ for (int i = size; i != 0;) {
+ --i;
+ Object current = getImpl(i);
+ if (current == obj || (current != null && current.equals(obj))) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public final Object peek()
+ {
+ int N = size;
+ if (N == 0) throw onEmptyStackTopRead();
+ return getImpl(N - 1);
+ }
+
+ public final Object pop()
+ {
+ if (sealed) throw onSeledMutation();
+ int N = size;
+ --N;
+ Object top;
+ switch (N) {
+ case -1: throw onEmptyStackTopRead();
+ case 0: top = f0; f0 = null; break;
+ case 1: top = f1; f1 = null; break;
+ case 2: top = f2; f2 = null; break;
+ case 3: top = f3; f3 = null; break;
+ case 4: top = f4; f4 = null; break;
+ default:
+ top = data[N - FIELDS_STORE_SIZE];
+ data[N - FIELDS_STORE_SIZE] = null;
+ }
+ size = N;
+ return top;
+ }
+
+ public final void push(Object value)
+ {
+ add(value);
+ }
+
+ public final void add(Object value)
+ {
+ if (sealed) throw onSeledMutation();
+ int N = size;
+ if (N >= FIELDS_STORE_SIZE) {
+ ensureCapacity(N + 1);
+ }
+ size = N + 1;
+ setImpl(N, value);
+ }
+
+ public final void add(int index, Object value)
+ {
+ int N = size;
+ if (!(0 <= index && index <= N)) throw onInvalidIndex(index, N + 1);
+ if (sealed) throw onSeledMutation();
+ Object tmp;
+ switch (index) {
+ case 0:
+ if (N == 0) { f0 = value; break; }
+ tmp = f0; f0 = value; value = tmp;
+ case 1:
+ if (N == 1) { f1 = value; break; }
+ tmp = f1; f1 = value; value = tmp;
+ case 2:
+ if (N == 2) { f2 = value; break; }
+ tmp = f2; f2 = value; value = tmp;
+ case 3:
+ if (N == 3) { f3 = value; break; }
+ tmp = f3; f3 = value; value = tmp;
+ case 4:
+ if (N == 4) { f4 = value; break; }
+ tmp = f4; f4 = value; value = tmp;
+
+ index = FIELDS_STORE_SIZE;
+ default:
+ ensureCapacity(N + 1);
+ if (index != N) {
+ System.arraycopy(data, index - FIELDS_STORE_SIZE,
+ data, index - FIELDS_STORE_SIZE + 1,
+ N - index);
+ }
+ data[index - FIELDS_STORE_SIZE] = value;
+ }
+ size = N + 1;
+ }
+
+ public final void remove(int index)
+ {
+ int N = size;
+ if (!(0 <= index && index < N)) throw onInvalidIndex(index, N);
+ if (sealed) throw onSeledMutation();
+ --N;
+ switch (index) {
+ case 0:
+ if (N == 0) { f0 = null; break; }
+ f0 = f1;
+ case 1:
+ if (N == 1) { f1 = null; break; }
+ f1 = f2;
+ case 2:
+ if (N == 2) { f2 = null; break; }
+ f2 = f3;
+ case 3:
+ if (N == 3) { f3 = null; break; }
+ f3 = f4;
+ case 4:
+ if (N == 4) { f4 = null; break; }
+ f4 = data[0];
+
+ index = FIELDS_STORE_SIZE;
+ default:
+ if (index != N) {
+ System.arraycopy(data, index - FIELDS_STORE_SIZE + 1,
+ data, index - FIELDS_STORE_SIZE,
+ N - index);
+ }
+ data[N - FIELDS_STORE_SIZE] = null;
+ }
+ size = N;
+ }
+
+ public final void clear()
+ {
+ if (sealed) throw onSeledMutation();
+ int N = size;
+ for (int i = 0; i != N; ++i) {
+ setImpl(i, null);
+ }
+ size = 0;
+ }
+
+ public final Object[] toArray()
+ {
+ Object[] array = new Object[size];
+ toArray(array, 0);
+ return array;
+ }
+
+ public final void toArray(Object[] array)
+ {
+ toArray(array, 0);
+ }
+
+ public final void toArray(Object[] array, int offset)
+ {
+ int N = size;
+ switch (N) {
+ default:
+ System.arraycopy(data, 0, array, offset + FIELDS_STORE_SIZE,
+ N - FIELDS_STORE_SIZE);
+ case 5: array[offset + 4] = f4;
+ case 4: array[offset + 3] = f3;
+ case 3: array[offset + 2] = f2;
+ case 2: array[offset + 1] = f1;
+ case 1: array[offset + 0] = f0;
+ case 0: break;
+ }
+ }
+
+ private void ensureCapacity(int minimalCapacity)
+ {
+ int required = minimalCapacity - FIELDS_STORE_SIZE;
+ if (required <= 0) throw new IllegalArgumentException();
+ if (data == null) {
+ int alloc = FIELDS_STORE_SIZE * 2;
+ if (alloc < required) {
+ alloc = required;
+ }
+ data = new Object[alloc];
+ } else {
+ int alloc = data.length;
+ if (alloc < required) {
+ if (alloc <= FIELDS_STORE_SIZE) {
+ alloc = FIELDS_STORE_SIZE * 2;
+ } else {
+ alloc *= 2;
+ }
+ if (alloc < required) {
+ alloc = required;
+ }
+ Object[] tmp = new Object[alloc];
+ if (size > FIELDS_STORE_SIZE) {
+ System.arraycopy(data, 0, tmp, 0,
+ size - FIELDS_STORE_SIZE);
+ }
+ data = tmp;
+ }
+ }
+ }
+
+ private static RuntimeException onInvalidIndex(int index, int upperBound)
+ {
+ // \u2209 is "NOT ELEMENT OF"
+ String msg = index+" \u2209 [0, "+upperBound+')';
+ throw new IndexOutOfBoundsException(msg);
+ }
+
+ private static RuntimeException onEmptyStackTopRead()
+ {
+ throw new RuntimeException("Empty stack");
+ }
+
+ private static RuntimeException onSeledMutation()
+ {
+ throw new IllegalStateException("Attempt to modify sealed array");
+ }
+
+ private void writeObject(ObjectOutputStream os) throws IOException
+ {
+ os.defaultWriteObject();
+ int N = size;
+ for (int i = 0; i != N; ++i) {
+ Object obj = getImpl(i);
+ os.writeObject(obj);
+ }
+ }
+
+ private void readObject(ObjectInputStream is)
+ throws IOException, ClassNotFoundException
+ {
+ is.defaultReadObject(); // It reads size
+ int N = size;
+ if (N > FIELDS_STORE_SIZE) {
+ data = new Object[N - FIELDS_STORE_SIZE];
+ }
+ for (int i = 0; i != N; ++i) {
+ Object obj = is.readObject();
+ setImpl(i, obj);
+ }
+ }
+
+// Number of data elements
+ private int size;
+
+ private boolean sealed;
+
+ private static final int FIELDS_STORE_SIZE = 5;
+ private transient Object f0, f1, f2, f3, f4;
+ private transient Object[] data;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ObjToIntMap.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ObjToIntMap.java
new file mode 100644
index 0000000..4aa7d23
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ObjToIntMap.java
@@ -0,0 +1,697 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.io.Serializable;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+/**
+ * Map to associate objects to integers.
+ * The map does not synchronize any of its operation, so either use
+ * it from a single thread or do own synchronization or perform all mutation
+ * operations on one thread before passing the map to others
+ *
+ * @author Igor Bukanov
+ *
+ */
+
+public class ObjToIntMap implements Serializable
+{
+ static final long serialVersionUID = -1542220580748809402L;
+
+// Map implementation via hashtable,
+// follows "The Art of Computer Programming" by Donald E. Knuth
+
+// ObjToIntMap is a copy cat of ObjToIntMap with API adjusted to object keys
+
+ public static class Iterator {
+
+ Iterator(ObjToIntMap master) {
+ this.master = master;
+ }
+
+ final void init(Object[] keys, int[] values, int keyCount) {
+ this.keys = keys;
+ this.values = values;
+ this.cursor = -1;
+ this.remaining = keyCount;
+ }
+
+ public void start() {
+ master.initIterator(this);
+ next();
+ }
+
+ public boolean done() {
+ return remaining < 0;
+ }
+
+ public void next() {
+ if (remaining == -1) Kit.codeBug();
+ if (remaining == 0) {
+ remaining = -1;
+ cursor = -1;
+ }else {
+ for (++cursor; ; ++cursor) {
+ Object key = keys[cursor];
+ if (key != null && key != DELETED) {
+ --remaining;
+ break;
+ }
+ }
+ }
+ }
+
+ public Object getKey() {
+ Object key = keys[cursor];
+ if (key == UniqueTag.NULL_VALUE) { key = null; }
+ return key;
+ }
+
+ public int getValue() {
+ return values[cursor];
+ }
+
+ public void setValue(int value) {
+ values[cursor] = value;
+ }
+
+ ObjToIntMap master;
+ private int cursor;
+ private int remaining;
+ private Object[] keys;
+ private int[] values;
+ }
+
+ public ObjToIntMap() {
+ this(4);
+ }
+
+ public ObjToIntMap(int keyCountHint) {
+ if (keyCountHint < 0) Kit.codeBug();
+ // Table grow when number of stored keys >= 3/4 of max capacity
+ int minimalCapacity = keyCountHint * 4 / 3;
+ int i;
+ for (i = 2; (1 << i) < minimalCapacity; ++i) { }
+ power = i;
+ if (check && power < 2) Kit.codeBug();
+ }
+
+ public boolean isEmpty() {
+ return keyCount == 0;
+ }
+
+ public int size() {
+ return keyCount;
+ }
+
+ public boolean has(Object key) {
+ if (key == null) { key = UniqueTag.NULL_VALUE; }
+ return 0 <= findIndex(key);
+ }
+
+ /**
+ * Get integer value assigned with key.
+ * @return key integer value or defaultValue if key is absent
+ */
+ public int get(Object key, int defaultValue) {
+ if (key == null) { key = UniqueTag.NULL_VALUE; }
+ int index = findIndex(key);
+ if (0 <= index) {
+ return values[index];
+ }
+ return defaultValue;
+ }
+
+ /**
+ * Get integer value assigned with key.
+ * @return key integer value
+ * @throws RuntimeException if key does not exist
+ */
+ public int getExisting(Object key) {
+ if (key == null) { key = UniqueTag.NULL_VALUE; }
+ int index = findIndex(key);
+ if (0 <= index) {
+ return values[index];
+ }
+ // Key must exist
+ Kit.codeBug();
+ return 0;
+ }
+
+ public void put(Object key, int value) {
+ if (key == null) { key = UniqueTag.NULL_VALUE; }
+ int index = ensureIndex(key);
+ values[index] = value;
+ }
+
+ /**
+ * If table already contains a key that equals to keyArg, return that key
+ * while setting its value to zero, otherwise add keyArg with 0 value to
+ * the table and return it.
+ */
+ public Object intern(Object keyArg) {
+ boolean nullKey = false;
+ if (keyArg == null) {
+ nullKey = true;
+ keyArg = UniqueTag.NULL_VALUE;
+ }
+ int index = ensureIndex(keyArg);
+ values[index] = 0;
+ return (nullKey) ? null : keys[index];
+ }
+
+ public void remove(Object key) {
+ if (key == null) { key = UniqueTag.NULL_VALUE; }
+ int index = findIndex(key);
+ if (0 <= index) {
+ keys[index] = DELETED;
+ --keyCount;
+ }
+ }
+
+ public void clear() {
+ int i = keys.length;
+ while (i != 0) {
+ keys[--i] = null;
+ }
+ keyCount = 0;
+ occupiedCount = 0;
+ }
+
+ public Iterator newIterator() {
+ return new Iterator(this);
+ }
+
+ // The sole purpose of the method is to avoid accessing private fields
+ // from the Iterator inner class to workaround JDK 1.1 compiler bug which
+ // generates code triggering VerifierError on recent JVMs
+ final void initIterator(Iterator i) {
+ i.init(keys, values, keyCount);
+ }
+
+ /** Return array of present keys */
+ public Object[] getKeys() {
+ Object[] array = new Object[keyCount];
+ getKeys(array, 0);
+ return array;
+ }
+
+ public void getKeys(Object[] array, int offset) {
+ int count = keyCount;
+ for (int i = 0; count != 0; ++i) {
+ Object key = keys[i];
+ if (key != null && key != DELETED) {
+ if (key == UniqueTag.NULL_VALUE) { key = null; }
+ array[offset] = key;
+ ++offset;
+ --count;
+ }
+ }
+ }
+
+ private static int tableLookupStep(int fraction, int mask, int power) {
+ int shift = 32 - 2 * power;
+ if (shift >= 0) {
+ return ((fraction >>> shift) & mask) | 1;
+ }
+ else {
+ return (fraction & (mask >>> -shift)) | 1;
+ }
+ }
+
+ private int findIndex(Object key) {
+ if (keys != null) {
+ int hash = key.hashCode();
+ int fraction = hash * A;
+ int index = fraction >>> (32 - power);
+ Object test = keys[index];
+ if (test != null) {
+ int N = 1 << power;
+ if (test == key
+ || (values[N + index] == hash && test.equals(key)))
+ {
+ return index;
+ }
+ // Search in table after first failed attempt
+ int mask = N - 1;
+ int step = tableLookupStep(fraction, mask, power);
+ int n = 0;
+ for (;;) {
+ if (check) {
+ if (n >= occupiedCount) Kit.codeBug();
+ ++n;
+ }
+ index = (index + step) & mask;
+ test = keys[index];
+ if (test == null) {
+ break;
+ }
+ if (test == key
+ || (values[N + index] == hash && test.equals(key)))
+ {
+ return index;
+ }
+ }
+ }
+ }
+ return -1;
+ }
+
+// Insert key that is not present to table without deleted entries
+// and enough free space
+ private int insertNewKey(Object key, int hash) {
+ if (check && occupiedCount != keyCount) Kit.codeBug();
+ if (check && keyCount == 1 << power) Kit.codeBug();
+ int fraction = hash * A;
+ int index = fraction >>> (32 - power);
+ int N = 1 << power;
+ if (keys[index] != null) {
+ int mask = N - 1;
+ int step = tableLookupStep(fraction, mask, power);
+ int firstIndex = index;
+ do {
+ if (check && keys[index] == DELETED) Kit.codeBug();
+ index = (index + step) & mask;
+ if (check && firstIndex == index) Kit.codeBug();
+ } while (keys[index] != null);
+ }
+ keys[index] = key;
+ values[N + index] = hash;
+ ++occupiedCount;
+ ++keyCount;
+
+ return index;
+ }
+
+ private void rehashTable() {
+ if (keys == null) {
+ if (check && keyCount != 0) Kit.codeBug();
+ if (check && occupiedCount != 0) Kit.codeBug();
+ int N = 1 << power;
+ keys = new Object[N];
+ values = new int[2 * N];
+ }
+ else {
+ // Check if removing deleted entries would free enough space
+ if (keyCount * 2 >= occupiedCount) {
+ // Need to grow: less then half of deleted entries
+ ++power;
+ }
+ int N = 1 << power;
+ Object[] oldKeys = keys;
+ int[] oldValues = values;
+ int oldN = oldKeys.length;
+ keys = new Object[N];
+ values = new int[2 * N];
+
+ int remaining = keyCount;
+ occupiedCount = keyCount = 0;
+ for (int i = 0; remaining != 0; ++i) {
+ Object key = oldKeys[i];
+ if (key != null && key != DELETED) {
+ int keyHash = oldValues[oldN + i];
+ int index = insertNewKey(key, keyHash);
+ values[index] = oldValues[i];
+ --remaining;
+ }
+ }
+ }
+ }
+
+// Ensure key index creating one if necessary
+ private int ensureIndex(Object key) {
+ int hash = key.hashCode();
+ int index = -1;
+ int firstDeleted = -1;
+ if (keys != null) {
+ int fraction = hash * A;
+ index = fraction >>> (32 - power);
+ Object test = keys[index];
+ if (test != null) {
+ int N = 1 << power;
+ if (test == key
+ || (values[N + index] == hash && test.equals(key)))
+ {
+ return index;
+ }
+ if (test == DELETED) {
+ firstDeleted = index;
+ }
+
+ // Search in table after first failed attempt
+ int mask = N - 1;
+ int step = tableLookupStep(fraction, mask, power);
+ int n = 0;
+ for (;;) {
+ if (check) {
+ if (n >= occupiedCount) Kit.codeBug();
+ ++n;
+ }
+ index = (index + step) & mask;
+ test = keys[index];
+ if (test == null) {
+ break;
+ }
+ if (test == key
+ || (values[N + index] == hash && test.equals(key)))
+ {
+ return index;
+ }
+ if (test == DELETED && firstDeleted < 0) {
+ firstDeleted = index;
+ }
+ }
+ }
+ }
+ // Inserting of new key
+ if (check && keys != null && keys[index] != null)
+ Kit.codeBug();
+ if (firstDeleted >= 0) {
+ index = firstDeleted;
+ }
+ else {
+ // Need to consume empty entry: check occupation level
+ if (keys == null || occupiedCount * 4 >= (1 << power) * 3) {
+ // Too litle unused entries: rehash
+ rehashTable();
+ return insertNewKey(key, hash);
+ }
+ ++occupiedCount;
+ }
+ keys[index] = key;
+ values[(1 << power) + index] = hash;
+ ++keyCount;
+ return index;
+ }
+
+ private void writeObject(ObjectOutputStream out)
+ throws IOException
+ {
+ out.defaultWriteObject();
+
+ int count = keyCount;
+ for (int i = 0; count != 0; ++i) {
+ Object key = keys[i];
+ if (key != null && key != DELETED) {
+ --count;
+ out.writeObject(key);
+ out.writeInt(values[i]);
+ }
+ }
+ }
+
+ private void readObject(ObjectInputStream in)
+ throws IOException, ClassNotFoundException
+ {
+ in.defaultReadObject();
+
+ int writtenKeyCount = keyCount;
+ if (writtenKeyCount != 0) {
+ keyCount = 0;
+ int N = 1 << power;
+ keys = new Object[N];
+ values = new int[2 * N];
+ for (int i = 0; i != writtenKeyCount; ++i) {
+ Object key = in.readObject();
+ int hash = key.hashCode();
+ int index = insertNewKey(key, hash);
+ values[index] = in.readInt();
+ }
+ }
+ }
+
+// A == golden_ratio * (1 << 32) = ((sqrt(5) - 1) / 2) * (1 << 32)
+// See Knuth etc.
+ private static final int A = 0x9e3779b9;
+
+ private static final Object DELETED = new Object();
+
+// Structure of kyes and values arrays (N == 1 << power):
+// keys[0 <= i < N]: key value or null or DELETED mark
+// values[0 <= i < N]: value of key at keys[i]
+// values[N <= i < 2*N]: hash code of key at keys[i-N]
+
+ private transient Object[] keys;
+ private transient int[] values;
+
+ private int power;
+ private int keyCount;
+ private transient int occupiedCount; // == keyCount + deleted_count
+
+// If true, enables consitency checks
+ private static final boolean check = false;
+
+/* TEST START
+
+ public static void main(String[] args) {
+ if (!check) {
+ System.err.println("Set check to true and re-run");
+ throw new RuntimeException("Set check to true and re-run");
+ }
+
+ ObjToIntMap map;
+ map = new ObjToIntMap(0);
+ testHash(map, 3);
+ map = new ObjToIntMap(0);
+ testHash(map, 10 * 1000);
+ map = new ObjToIntMap();
+ testHash(map, 10 * 1000);
+ map = new ObjToIntMap(30 * 1000);
+ testHash(map, 10 * 100);
+ map.clear();
+ testHash(map, 4);
+ map = new ObjToIntMap(0);
+ testHash(map, 10 * 100);
+ }
+
+ private static void testHash(ObjToIntMap map, int N) {
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(i);
+ check(-1 == map.get(key, -1));
+ map.put(key, i);
+ check(i == map.get(key, -1));
+ }
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(i);
+ map.put(key, i);
+ check(i == map.get(key, -1));
+ }
+
+ check(map.size() == N);
+
+ System.out.print("."); System.out.flush();
+ Object[] keys = map.getKeys();
+ check(keys.length == N);
+ for (int i = 0; i != N; ++i) {
+ Object key = keys[i];
+ check(map.has(key));
+ }
+
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(i);
+ check(i == map.get(key, -1));
+ }
+
+ int Nsqrt = -1;
+ for (int i = 0; ; ++i) {
+ if (i * i >= N) {
+ Nsqrt = i;
+ break;
+ }
+ }
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(i * i);
+ map.put(key, i);
+ check(i == map.get(key, -1));
+ }
+
+ check(map.size() == 2 * N - Nsqrt);
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(i * i);
+ check(i == map.get(key, -1));
+ }
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(-1 - i * i);
+ map.put(key, i);
+ check(i == map.get(key, -1));
+ }
+
+ check(map.size() == 3 * N - Nsqrt);
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(-1 - i * i);
+ map.remove(key);
+ check(!map.has(key));
+ }
+
+ check(map.size() == 2 * N - Nsqrt);
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(i * i);
+ check(i == map.get(key, -1));
+ }
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(i);
+ int j = intSqrt(i);
+ if (j * j == i) {
+ check(j == map.get(key, -1));
+ }else {
+ check(i == map.get(key, -1));
+ }
+ }
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(i * i);
+ map.remove(key);
+ check(-2 == map.get(key, -2));
+ }
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(i);
+ map.put(key, i);
+ check(i == map.get(key, -2));
+ }
+
+ check(map.size() == N);
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(i);
+ check(i == map.get(key, -1));
+ }
+
+ System.out.print("."); System.out.flush();
+ ObjToIntMap copy = (ObjToIntMap)writeAndRead(map);
+ check(copy.size() == N);
+
+ for (int i = 0; i != N; ++i) {
+ Object key = testKey(i);
+ check(i == copy.get(key, -1));
+ }
+
+ System.out.print("."); System.out.flush();
+ checkSameMaps(copy, map);
+
+ System.out.println(); System.out.flush();
+ }
+
+ private static void checkSameMaps(ObjToIntMap map1, ObjToIntMap map2) {
+ check(map1.size() == map2.size());
+ Object[] keys = map1.getKeys();
+ check(keys.length == map1.size());
+ for (int i = 0; i != keys.length; ++i) {
+ check(map1.get(keys[i], -1) == map2.get(keys[i], -1));
+ }
+ }
+
+ private static void check(boolean condition) {
+ if (!condition) Kit.codeBug();
+ }
+
+ private static Object[] testPool;
+
+ private static Object testKey(int i) {
+ int MAX_POOL = 100;
+ if (0 <= i && i < MAX_POOL) {
+ if (testPool != null && testPool[i] != null) {
+ return testPool[i];
+ }
+ }
+ Object x = new Double(i + 0.5);
+ if (0 <= i && i < MAX_POOL) {
+ if (testPool == null) {
+ testPool = new Object[MAX_POOL];
+ }
+ testPool[i] = x;
+ }
+ return x;
+ }
+
+ private static int intSqrt(int i) {
+ int approx = (int)Math.sqrt(i) + 1;
+ while (approx * approx > i) {
+ --approx;
+ }
+ return approx;
+ }
+
+ private static Object writeAndRead(Object obj) {
+ try {
+ java.io.ByteArrayOutputStream
+ bos = new java.io.ByteArrayOutputStream();
+ java.io.ObjectOutputStream
+ out = new java.io.ObjectOutputStream(bos);
+ out.writeObject(obj);
+ out.close();
+ byte[] data = bos.toByteArray();
+ java.io.ByteArrayInputStream
+ bis = new java.io.ByteArrayInputStream(data);
+ java.io.ObjectInputStream
+ in = new java.io.ObjectInputStream(bis);
+ Object result = in.readObject();
+ in.close();
+ return result;
+ }catch (Exception ex) {
+ throw new RuntimeException("Unexpected");
+ }
+ }
+
+// TEST END */
+
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Parser.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Parser.java
new file mode 100644
index 0000000..80cb937
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Parser.java
@@ -0,0 +1,2554 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Mike Ang
+ * Igor Bukanov
+ * Yuh-Ruey Chen
+ * Ethan Hugg
+ * Bob Jervis
+ * Terry Lucas
+ * Mike McCabe
+ * Milen Nankov
+ * Norris Boyd
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.io.Reader;
+import java.io.IOException;
+import java.util.Hashtable;
+
+/**
+ * This class implements the JavaScript parser.
+ *
+ * It is based on the C source files jsparse.c and jsparse.h
+ * in the jsref package.
+ *
+ * @see TokenStream
+ *
+ * @author Mike McCabe
+ * @author Brendan Eich
+ */
+
+public class Parser
+{
+ // TokenInformation flags : currentFlaggedToken stores them together
+ // with token type
+ final static int
+ CLEAR_TI_MASK = 0xFFFF, // mask to clear token information bits
+ TI_AFTER_EOL = 1 << 16, // first token of the source line
+ TI_CHECK_LABEL = 1 << 17; // indicates to check for label
+
+ CompilerEnvirons compilerEnv;
+ private ErrorReporter errorReporter;
+ /*APPJET*//*no longer:private*/ String sourceURI;
+ boolean calledByCompileFunction;
+
+ /*APPJET*//*no longer:private*/ TokenStream ts;
+ private int currentFlaggedToken;
+ /*APPJET*//*no longer:private*/ int syntaxErrorCount;
+
+ private IRFactory nf;
+
+ private int nestingOfFunction;
+
+ private Decompiler decompiler;
+ private String encodedSource;
+
+// The following are per function variables and should be saved/restored
+// during function parsing.
+// XXX Move to separated class?
+ ScriptOrFnNode currentScriptOrFn;
+ Node.Scope currentScope;
+ private int nestingOfWith;
+ private Hashtable labelSet; // map of label names into nodes
+ private ObjArray loopSet;
+ private ObjArray loopAndSwitchSet;
+ private boolean hasReturnValue;
+ private int endFlags;
+// end of per function variables
+
+ /*APPJET*/public int lastConsumedTokenLine = -1;
+
+ public int getCurrentLineNumber() {
+ return ts.getLineno();
+ }
+
+ // Exception to unwind
+ private static class ParserException extends RuntimeException
+ {
+ static final long serialVersionUID = 5882582646773765630L;
+ }
+
+ public Parser(CompilerEnvirons compilerEnv, ErrorReporter errorReporter)
+ {
+ this.compilerEnv = compilerEnv;
+ this.errorReporter = errorReporter;
+ }
+
+ protected Decompiler createDecompiler(CompilerEnvirons compilerEnv)
+ {
+ return new Decompiler();
+ }
+
+ void addStrictWarning(String messageId, String messageArg)
+ {
+ if (compilerEnv.isStrictMode())
+ addWarning(messageId, messageArg);
+ }
+
+ void addWarning(String messageId, String messageArg)
+ {
+ String message = ScriptRuntime.getMessage1(messageId, messageArg);
+ if (compilerEnv.reportWarningAsError()) {
+ ++syntaxErrorCount;
+ errorReporter.error(message, sourceURI, ts.getLineno(),
+ ts.getLine(), ts.getOffset());
+ } else
+ errorReporter.warning(message, sourceURI, ts.getLineno(),
+ ts.getLine(), ts.getOffset());
+ }
+
+ void addError(String messageId)
+ {
+ ++syntaxErrorCount;
+ String message = ScriptRuntime.getMessage0(messageId);
+ errorReporter.error(message, sourceURI, ts.getLineno(),
+ ts.getLine(), ts.getOffset());
+ }
+
+ void addError(String messageId, String messageArg)
+ {
+ ++syntaxErrorCount;
+ String message = ScriptRuntime.getMessage1(messageId, messageArg);
+ errorReporter.error(message, sourceURI, ts.getLineno(),
+ ts.getLine(), ts.getOffset());
+ }
+
+ RuntimeException reportError(String messageId)
+ {
+ addError(messageId);
+
+ // Throw a ParserException exception to unwind the recursive descent
+ // parse.
+ throw new ParserException();
+ }
+
+ /*APPJET*//*added method*/
+ RuntimeException reportError(String messageId, String messageArg)
+ {
+ addError(messageId, messageArg);
+
+ // Throw a ParserException exception to unwind the recursive descent
+ // parse.
+ throw new ParserException();
+ }
+
+ /*APPJET*//*no longer: private*/int peekToken()
+ throws IOException
+ {
+ int tt = currentFlaggedToken;
+ if (tt == Token.EOF) {
+ tt = ts.getToken();
+ if (tt == Token.EOL) {
+ do {
+ tt = ts.getToken();
+ } while (tt == Token.EOL);
+ tt |= TI_AFTER_EOL;
+ }
+ currentFlaggedToken = tt;
+ }
+ return tt & CLEAR_TI_MASK;
+ }
+
+ private int peekFlaggedToken()
+ throws IOException
+ {
+ peekToken();
+ return currentFlaggedToken;
+ }
+
+ /*APPJET*//*no longer:private*/ void consumeToken()
+ {
+ currentFlaggedToken = Token.EOF;
+ /*APPJET*/lastConsumedTokenLine = ts.getLineno();
+ }
+
+ private int nextToken()
+ throws IOException
+ {
+ int tt = peekToken();
+ consumeToken();
+ return tt;
+ }
+
+ private int nextFlaggedToken()
+ throws IOException
+ {
+ peekToken();
+ int ttFlagged = currentFlaggedToken;
+ consumeToken();
+ return ttFlagged;
+ }
+
+ private boolean matchToken(int toMatch)
+ throws IOException
+ {
+ int tt = peekToken();
+ if (tt != toMatch) {
+ return false;
+ }
+ consumeToken();
+ return true;
+ }
+
+ private int peekTokenOrEOL()
+ throws IOException
+ {
+ int tt = peekToken();
+ // Check for last peeked token flags
+ if ((currentFlaggedToken & TI_AFTER_EOL) != 0) {
+ tt = Token.EOL;
+ }
+ return tt;
+ }
+
+ private void setCheckForLabel()
+ {
+ if ((currentFlaggedToken & CLEAR_TI_MASK) != Token.NAME)
+ throw Kit.codeBug();
+ currentFlaggedToken |= TI_CHECK_LABEL;
+ }
+
+ private void mustMatchToken(int toMatch, String messageId)
+ throws IOException, ParserException
+ {
+ if (!matchToken(toMatch)) {
+ reportError(messageId);
+ }
+ }
+
+ /*APPJET*//*added method*/
+ private void mustMatchToken(int toMatch, String messageId, String messageArg)
+ throws IOException, ParserException
+ {
+ if (!matchToken(toMatch)) {
+ reportError(messageId, messageArg);
+ }
+ }
+
+ private void mustHaveXML()
+ {
+ if (!compilerEnv.isXmlAvailable()) {
+ reportError("msg.XML.not.available");
+ }
+ }
+
+ public String getEncodedSource()
+ {
+ return encodedSource;
+ }
+
+ public boolean eof()
+ {
+ return ts.eof();
+ }
+
+ boolean insideFunction()
+ {
+ return nestingOfFunction != 0;
+ }
+
+ void pushScope(Node node) {
+ Node.Scope scopeNode = (Node.Scope) node;
+ if (scopeNode.getParentScope() != null) throw Kit.codeBug();
+ scopeNode.setParent(currentScope);
+ currentScope = scopeNode;
+ }
+
+ void popScope() {
+ currentScope = currentScope.getParentScope();
+ }
+
+ private Node enterLoop(Node loopLabel, boolean doPushScope)
+ {
+ Node loop = nf.createLoopNode(loopLabel, ts.getLineno());
+ if (loopSet == null) {
+ loopSet = new ObjArray();
+ if (loopAndSwitchSet == null) {
+ loopAndSwitchSet = new ObjArray();
+ }
+ }
+ loopSet.push(loop);
+ loopAndSwitchSet.push(loop);
+ if (doPushScope) {
+ pushScope(loop);
+ }
+ return loop;
+ }
+
+ private void exitLoop(boolean doPopScope)
+ {
+ loopSet.pop();
+ loopAndSwitchSet.pop();
+ if (doPopScope) {
+ popScope();
+ }
+ }
+
+ private Node enterSwitch(Node switchSelector, int lineno)
+ {
+ Node switchNode = nf.createSwitch(switchSelector, lineno);
+ if (loopAndSwitchSet == null) {
+ loopAndSwitchSet = new ObjArray();
+ }
+ loopAndSwitchSet.push(switchNode);
+ return switchNode;
+ }
+
+ private void exitSwitch()
+ {
+ loopAndSwitchSet.pop();
+ }
+
+ /*
+ * Build a parse tree from the given sourceString.
+ *
+ * @return an Object representing the parsed
+ * program. If the parse fails, null will be returned. (The
+ * parse failure will result in a call to the ErrorReporter from
+ * CompilerEnvirons.)
+ */
+ public ScriptOrFnNode parse(String sourceString,
+ String sourceURI, int lineno)
+ {
+ this.sourceURI = sourceURI;
+ this.ts = new TokenStream(this, null, sourceString, lineno);
+ try {
+ return parse();
+ } catch (IOException ex) {
+ // Should never happen
+ throw new IllegalStateException();
+ }
+ }
+
+ /*
+ * Build a parse tree from the given sourceString.
+ *
+ * @return an Object representing the parsed
+ * program. If the parse fails, null will be returned. (The
+ * parse failure will result in a call to the ErrorReporter from
+ * CompilerEnvirons.)
+ */
+ public ScriptOrFnNode parse(Reader sourceReader,
+ String sourceURI, int lineno)
+ throws IOException
+ {
+ this.sourceURI = sourceURI;
+ this.ts = new TokenStream(this, sourceReader, null, lineno);
+ return parse();
+ }
+
+ private ScriptOrFnNode parse()
+ throws IOException
+ {
+ this.decompiler = createDecompiler(compilerEnv);
+ this.nf = new IRFactory(this);
+ currentScriptOrFn = nf.createScript();
+ currentScope = currentScriptOrFn;
+ int sourceStartOffset = decompiler.getCurrentOffset();
+ this.encodedSource = null;
+ decompiler.addToken(Token.SCRIPT);
+
+ this.currentFlaggedToken = Token.EOF;
+ this.syntaxErrorCount = 0;
+
+ int baseLineno = ts.getLineno(); // line number where source starts
+
+ /*APPJET*/lastConsumedTokenLine = baseLineno;
+
+ /* so we have something to add nodes to until
+ * we've collected all the source */
+ Node pn = nf.createLeaf(Token.BLOCK);
+
+ try {
+ for (;;) {
+ int tt = peekToken();
+
+ if (tt <= Token.EOF) {
+ break;
+ }
+
+ Node n;
+ if (tt == Token.FUNCTION) {
+ consumeToken();
+ try {
+ n = function(calledByCompileFunction
+ ? FunctionNode.FUNCTION_EXPRESSION
+ : FunctionNode.FUNCTION_STATEMENT);
+ } catch (ParserException e) {
+ break;
+ }
+ } else {
+ n = statement();
+ }
+ nf.addChildToBack(pn, n);
+ }
+ } catch (StackOverflowError ex) {
+ String msg = ScriptRuntime.getMessage0(
+ "msg.too.deep.parser.recursion");
+ throw Context.reportRuntimeError(msg, sourceURI,
+ ts.getLineno(), null, 0);
+ }
+
+ if (this.syntaxErrorCount != 0) {
+ String msg = String.valueOf(this.syntaxErrorCount);
+ msg = ScriptRuntime.getMessage1("msg.got.syntax.errors", msg);
+ throw errorReporter.runtimeError(msg, sourceURI, baseLineno,
+ null, 0);
+ }
+
+ currentScriptOrFn.setSourceName(sourceURI);
+ currentScriptOrFn.setBaseLineno(baseLineno);
+ currentScriptOrFn.setEndLineno(ts.getLineno());
+
+ int sourceEndOffset = decompiler.getCurrentOffset();
+ currentScriptOrFn.setEncodedSourceBounds(sourceStartOffset,
+ sourceEndOffset);
+
+ nf.initScript(currentScriptOrFn, pn);
+
+ if (compilerEnv.isGeneratingSource()) {
+ encodedSource = decompiler.getEncodedSource();
+ }
+ this.decompiler = null; // It helps GC
+
+ return currentScriptOrFn;
+ }
+
+ /*
+ * The C version of this function takes an argument list,
+ * which doesn't seem to be needed for tree generation...
+ * it'd only be useful for checking argument hiding, which
+ * I'm not doing anyway...
+ */
+ private Node parseFunctionBody()
+ throws IOException
+ {
+ ++nestingOfFunction;
+ Node pn = nf.createBlock(ts.getLineno());
+ try {
+ bodyLoop: for (;;) {
+ Node n;
+ int tt = peekToken();
+ switch (tt) {
+ case Token.ERROR:
+ case Token.EOF:
+ case Token.RC:
+ break bodyLoop;
+
+ case Token.FUNCTION:
+ consumeToken();
+ n = function(FunctionNode.FUNCTION_STATEMENT);
+ break;
+ default:
+ n = statement();
+ break;
+ }
+ nf.addChildToBack(pn, n);
+ }
+ } catch (ParserException e) {
+ // Ignore it
+ } finally {
+ --nestingOfFunction;
+ }
+
+ return pn;
+ }
+
+ private Node function(int functionType)
+ throws IOException, ParserException
+ {
+ int syntheticType = functionType;
+ int baseLineno = ts.getLineno(); // line number where source starts
+
+ int functionSourceStart = decompiler.markFunctionStart(functionType);
+ String name;
+ Node memberExprNode = null;
+ if (matchToken(Token.NAME)) {
+ name = ts.getString();
+ decompiler.addName(name);
+ if (!matchToken(Token.LP)) {
+ if (compilerEnv.isAllowMemberExprAsFunctionName()) {
+ // Extension to ECMA: if 'function <name>' does not follow
+ // by '(', assume <name> starts memberExpr
+ Node memberExprHead = nf.createName(name);
+ name = "";
+ memberExprNode = memberExprTail(false, memberExprHead);
+ }
+ mustMatchToken(Token.LP, "msg.no.paren.parms");
+ }
+ } else if (matchToken(Token.LP)) {
+ // Anonymous function
+ name = "";
+ } else {
+ name = "";
+ if (compilerEnv.isAllowMemberExprAsFunctionName()) {
+ // Note that memberExpr can not start with '(' like
+ // in function (1+2).toString(), because 'function (' already
+ // processed as anonymous function
+ memberExprNode = memberExpr(false);
+ }
+ mustMatchToken(Token.LP, "msg.no.paren.parms");
+ }
+
+ if (memberExprNode != null) {
+ syntheticType = FunctionNode.FUNCTION_EXPRESSION;
+ }
+
+ if (syntheticType != FunctionNode.FUNCTION_EXPRESSION &&
+ name.length() > 0)
+ {
+ // Function statements define a symbol in the enclosing scope
+ defineSymbol(Token.FUNCTION, name);
+ }
+
+ boolean nested = insideFunction();
+
+ FunctionNode fnNode = nf.createFunction(name);
+ if (nested || nestingOfWith > 0) {
+ // 1. Nested functions are not affected by the dynamic scope flag
+ // as dynamic scope is already a parent of their scope.
+ // 2. Functions defined under the with statement also immune to
+ // this setup, in which case dynamic scope is ignored in favor
+ // of with object.
+ fnNode.itsIgnoreDynamicScope = true;
+ }
+ int functionIndex = currentScriptOrFn.addFunction(fnNode);
+
+ int functionSourceEnd;
+
+ ScriptOrFnNode savedScriptOrFn = currentScriptOrFn;
+ currentScriptOrFn = fnNode;
+ Node.Scope savedCurrentScope = currentScope;
+ currentScope = fnNode;
+ int savedNestingOfWith = nestingOfWith;
+ nestingOfWith = 0;
+ Hashtable savedLabelSet = labelSet;
+ labelSet = null;
+ ObjArray savedLoopSet = loopSet;
+ loopSet = null;
+ ObjArray savedLoopAndSwitchSet = loopAndSwitchSet;
+ loopAndSwitchSet = null;
+ boolean savedHasReturnValue = hasReturnValue;
+ int savedFunctionEndFlags = endFlags;
+
+ Node destructuring = null;
+ Node body;
+ try {
+ decompiler.addToken(Token.LP);
+ if (!matchToken(Token.RP)) {
+ boolean first = true;
+ do {
+ if (!first)
+ decompiler.addToken(Token.COMMA);
+ first = false;
+ int tt = peekToken();
+ if (tt == Token.LB || tt == Token.LC) {
+ // Destructuring assignment for parameters: add a
+ // dummy parameter name, and add a statement to the
+ // body to initialize variables from the destructuring
+ // assignment
+ if (destructuring == null) {
+ destructuring = new Node(Token.COMMA);
+ }
+ String parmName = currentScriptOrFn.getNextTempName();
+ defineSymbol(Token.LP, parmName);
+ destructuring.addChildToBack(
+ nf.createDestructuringAssignment(Token.VAR,
+ primaryExpr(), nf.createName(parmName)));
+ } else {
+ mustMatchToken(Token.NAME, "msg.no.parm");
+ String s = ts.getString();
+ defineSymbol(Token.LP, s);
+ decompiler.addName(s);
+ }
+ } while (matchToken(Token.COMMA));
+
+ mustMatchToken(Token.RP, "msg.no.paren.after.parms");
+ }
+ decompiler.addToken(Token.RP);
+
+ mustMatchToken(Token.LC, "msg.no.brace.body");
+ decompiler.addEOL(Token.LC);
+ body = parseFunctionBody();
+ if (destructuring != null) {
+ body.addChildToFront(
+ new Node(Token.EXPR_VOID, destructuring, ts.getLineno()));
+ }
+ mustMatchToken(Token.RC, "msg.no.brace.after.body");
+
+ if (compilerEnv.isStrictMode() && !body.hasConsistentReturnUsage())
+ {
+ String msg = name.length() > 0 ? "msg.no.return.value"
+ : "msg.anon.no.return.value";
+ addStrictWarning(msg, name);
+ }
+
+ if (syntheticType == FunctionNode.FUNCTION_EXPRESSION &&
+ name.length() > 0 && currentScope.getSymbol(name) == null)
+ {
+ // Function expressions define a name only in the body of the
+ // function, and only if not hidden by a parameter name
+ defineSymbol(Token.FUNCTION, name);
+ }
+
+ decompiler.addToken(Token.RC);
+ functionSourceEnd = decompiler.markFunctionEnd(functionSourceStart);
+ if (functionType != FunctionNode.FUNCTION_EXPRESSION) {
+ // Add EOL only if function is not part of expression
+ // since it gets SEMI + EOL from Statement in that case
+ decompiler.addToken(Token.EOL);
+ }
+ }
+ finally {
+ hasReturnValue = savedHasReturnValue;
+ endFlags = savedFunctionEndFlags;
+ loopAndSwitchSet = savedLoopAndSwitchSet;
+ loopSet = savedLoopSet;
+ labelSet = savedLabelSet;
+ nestingOfWith = savedNestingOfWith;
+ currentScriptOrFn = savedScriptOrFn;
+ currentScope = savedCurrentScope;
+ }
+
+ fnNode.setEncodedSourceBounds(functionSourceStart, functionSourceEnd);
+ fnNode.setSourceName(sourceURI);
+ fnNode.setBaseLineno(baseLineno);
+ fnNode.setEndLineno(ts.getLineno());
+
+ Node pn = nf.initFunction(fnNode, functionIndex, body, syntheticType);
+ if (memberExprNode != null) {
+ pn = nf.createAssignment(Token.ASSIGN, memberExprNode, pn);
+ if (functionType != FunctionNode.FUNCTION_EXPRESSION) {
+ // XXX check JScript behavior: should it be createExprStatement?
+ pn = nf.createExprStatementNoReturn(pn, baseLineno);
+ }
+ }
+ return pn;
+ }
+
+ private Node statements(Node scope)
+ throws IOException
+ {
+ Node pn = scope != null ? scope : nf.createBlock(ts.getLineno());
+
+ int tt;
+ while ((tt = peekToken()) > Token.EOF && tt != Token.RC) {
+ nf.addChildToBack(pn, statement());
+ }
+
+ return pn;
+ }
+
+ private Node condition()
+ throws IOException, ParserException
+ {
+ mustMatchToken(Token.LP, "msg.no.paren.cond");
+ decompiler.addToken(Token.LP);
+ Node pn = expr(false);
+ mustMatchToken(Token.RP, "msg.no.paren.after.cond");
+ decompiler.addToken(Token.RP);
+
+ // Report strict warning on code like "if (a = 7) ...". Suppress the
+ // warning if the condition is parenthesized, like "if ((a = 7)) ...".
+ if (pn.getProp(Node.PARENTHESIZED_PROP) == null &&
+ (pn.getType() == Token.SETNAME || pn.getType() == Token.SETPROP ||
+ pn.getType() == Token.SETELEM))
+ {
+ addStrictWarning("msg.equal.as.assign", "");
+ }
+ return pn;
+ }
+
+ // match a NAME; return null if no match.
+ private Node matchJumpLabelName()
+ throws IOException, ParserException
+ {
+ Node label = null;
+
+ int tt = peekTokenOrEOL();
+ if (tt == Token.NAME) {
+ consumeToken();
+ String name = ts.getString();
+ decompiler.addName(name);
+ if (labelSet != null) {
+ label = (Node)labelSet.get(name);
+ }
+ if (label == null) {
+ reportError("msg.undef.label");
+ }
+ }
+
+ return label;
+ }
+
+ private Node statement()
+ throws IOException
+ {
+ try {
+ Node pn = statementHelper(null);
+ if (pn != null) {
+ if (compilerEnv.isStrictMode() && !pn.hasSideEffects())
+ addStrictWarning("msg.no.side.effects", "");
+ return pn;
+ }
+ } catch (ParserException e) { }
+
+ // skip to end of statement
+ int lineno = ts.getLineno();
+ guessingStatementEnd: for (;;) {
+ int tt = peekTokenOrEOL();
+ consumeToken();
+ switch (tt) {
+ case Token.ERROR:
+ case Token.EOF:
+ case Token.EOL:
+ case Token.SEMI:
+ break guessingStatementEnd;
+ }
+ }
+ return nf.createExprStatement(nf.createName("error"), lineno);
+ }
+
+ /*APPJET*/ /*begin*/
+ private Node statementHelper(Node statementLabel)
+ throws IOException, ParserException {
+
+ Node pn = statementHelper0(statementLabel);
+ if (pn != null && pn.getType() != Token.BLOCK && pn.getType() != Token.LOOP) {
+ pn.statementEndLineNum = lastConsumedTokenLine;
+ }
+ return pn;
+ }
+ /*end*/
+
+ private Node statementHelper0(Node statementLabel) /*APPJET*/
+ throws IOException, ParserException
+ {
+ Node pn = null;
+ int tt = peekToken();
+
+ switch (tt) {
+ case Token.IF: {
+ consumeToken();
+
+ decompiler.addToken(Token.IF);
+ int lineno = ts.getLineno();
+ Node cond = condition();
+ /*APPJET*/cond.lineno = lineno;
+ /*APPJET*/cond.statementEndLineNum = lastConsumedTokenLine;
+ decompiler.addEOL(Token.LC);
+ Node ifTrue = statement();
+ Node ifFalse = null;
+ if (matchToken(Token.ELSE)) {
+ decompiler.addToken(Token.RC);
+ decompiler.addToken(Token.ELSE);
+ decompiler.addEOL(Token.LC);
+ ifFalse = statement();
+ }
+ decompiler.addEOL(Token.RC);
+ pn = nf.createIf(cond, ifTrue, ifFalse, lineno);
+ return pn;
+ }
+
+ case Token.SWITCH: {
+ consumeToken();
+
+ decompiler.addToken(Token.SWITCH);
+ int lineno = ts.getLineno();
+ mustMatchToken(Token.LP, "msg.no.paren.switch");
+ decompiler.addToken(Token.LP);
+ /*APPJET*/Node toSwitchOn = expr(false);
+ /*APPJET*/toSwitchOn.lineno = lineno;
+ /*APPJET*/toSwitchOn.statementEndLineNum = lastConsumedTokenLine;
+ pn = enterSwitch(toSwitchOn, lineno); /*APPJET*/
+ try {
+ mustMatchToken(Token.RP, "msg.no.paren.after.switch");
+ decompiler.addToken(Token.RP);
+ mustMatchToken(Token.LC, "msg.no.brace.switch");
+ decompiler.addEOL(Token.LC);
+
+ boolean hasDefault = false;
+ switchLoop: for (;;) {
+ tt = nextToken();
+ Node caseExpression;
+ switch (tt) {
+ case Token.RC:
+ break switchLoop;
+
+ case Token.CASE:
+ decompiler.addToken(Token.CASE);
+ caseExpression = expr(false);
+ mustMatchToken(Token.COLON, "msg.no.colon.case");
+ decompiler.addEOL(Token.COLON);
+ break;
+
+ case Token.DEFAULT:
+ if (hasDefault) {
+ reportError("msg.double.switch.default");
+ }
+ decompiler.addToken(Token.DEFAULT);
+ hasDefault = true;
+ caseExpression = null;
+ mustMatchToken(Token.COLON, "msg.no.colon.case");
+ decompiler.addEOL(Token.COLON);
+ break;
+
+ default:
+ reportError("msg.bad.switch");
+ break switchLoop;
+ }
+
+ Node block = nf.createLeaf(Token.BLOCK);
+ while ((tt = peekToken()) != Token.RC
+ && tt != Token.CASE
+ && tt != Token.DEFAULT
+ && tt != Token.EOF)
+ {
+ nf.addChildToBack(block, statement());
+ }
+
+ // caseExpression == null => add default label
+ nf.addSwitchCase(pn, caseExpression, block);
+ }
+ decompiler.addEOL(Token.RC);
+ nf.closeSwitch(pn);
+ } finally {
+ exitSwitch();
+ }
+ return pn;
+ }
+
+ case Token.WHILE: {
+ consumeToken();
+ decompiler.addToken(Token.WHILE);
+
+ Node loop = enterLoop(statementLabel, true);
+ try {
+ /*APPJET*/int lineno = ts.getLineno();
+ Node cond = condition();
+ /*APPJET*/cond.lineno = lineno;
+ /*APPJET*/cond.statementEndLineNum = lastConsumedTokenLine;
+ decompiler.addEOL(Token.LC);
+ Node body = statement();
+ decompiler.addEOL(Token.RC);
+ pn = nf.createWhile(loop, cond, body);
+ } finally {
+ exitLoop(true);
+ }
+ return pn;
+ }
+
+ case Token.DO: {
+ consumeToken();
+ decompiler.addToken(Token.DO);
+ decompiler.addEOL(Token.LC);
+
+ Node loop = enterLoop(statementLabel, true);
+ try {
+ Node body = statement();
+ decompiler.addToken(Token.RC);
+ mustMatchToken(Token.WHILE, "msg.no.while.do");
+ decompiler.addToken(Token.WHILE);
+ Node cond = condition();
+ pn = nf.createDoWhile(loop, body, cond);
+ } finally {
+ exitLoop(true);
+ }
+ // Always auto-insert semicolon to follow SpiderMonkey:
+ // It is required by ECMAScript but is ignored by the rest of
+ // world, see bug 238945
+ matchToken(Token.SEMI);
+ decompiler.addEOL(Token.SEMI);
+ return pn;
+ }
+
+ case Token.FOR: {
+ consumeToken();
+ boolean isForEach = false;
+ decompiler.addToken(Token.FOR);
+
+ Node loop = enterLoop(statementLabel, true);
+ try {
+ Node init; // Node init is also foo in 'foo in object'
+ Node cond; // Node cond is also object in 'foo in object'
+ Node incr = null;
+ Node body;
+ int declType = -1;
+
+ // See if this is a for each () instead of just a for ()
+ if (matchToken(Token.NAME)) {
+ decompiler.addName(ts.getString());
+ if (ts.getString().equals("each")) {
+ isForEach = true;
+ } else {
+ reportError("msg.no.paren.for");
+ }
+ }
+
+ mustMatchToken(Token.LP, "msg.no.paren.for");
+ decompiler.addToken(Token.LP);
+ tt = peekToken();
+ if (tt == Token.SEMI) {
+ init = nf.createLeaf(Token.EMPTY);
+ } else {
+ if (tt == Token.VAR || tt == Token.LET) {
+ // set init to a var list or initial
+ consumeToken(); // consume the token
+ decompiler.addToken(tt);
+ init = variables(true, tt);
+ declType = tt;
+ }
+ else {
+ init = expr(true);
+ }
+ }
+
+ if (matchToken(Token.IN)) {
+ decompiler.addToken(Token.IN);
+ // 'cond' is the object over which we're iterating
+ cond = expr(false);
+ } else { // ordinary for loop
+ mustMatchToken(Token.SEMI, "msg.no.semi.for");
+ decompiler.addToken(Token.SEMI);
+ if (peekToken() == Token.SEMI) {
+ // no loop condition
+ cond = nf.createLeaf(Token.EMPTY);
+ } else {
+ cond = expr(false);
+ }
+
+ mustMatchToken(Token.SEMI, "msg.no.semi.for.cond");
+ decompiler.addToken(Token.SEMI);
+ if (peekToken() == Token.RP) {
+ incr = nf.createLeaf(Token.EMPTY);
+ } else {
+ incr = expr(false);
+ }
+ }
+
+ mustMatchToken(Token.RP, "msg.no.paren.for.ctrl");
+ decompiler.addToken(Token.RP);
+ decompiler.addEOL(Token.LC);
+ /*APPJET*/int parenEndLine = lastConsumedTokenLine;
+ body = statement();
+ decompiler.addEOL(Token.RC);
+
+ if (incr == null) {
+ // cond could be null if 'in obj' got eaten
+ // by the init node.
+ pn = nf.createForIn(declType, loop, init, cond, body,
+ isForEach);
+ } else {
+ pn = nf.createFor(loop, init, cond, incr, body);
+ }
+ /*APPJET*/ // use the LOOP object to hold the range of the paren'd expr
+ /*APPJET*/pn.statementEndLineNum = parenEndLine;
+ } finally {
+ exitLoop(true);
+ }
+ return pn;
+ }
+
+ case Token.TRY: {
+ consumeToken();
+ int lineno = ts.getLineno();
+
+ Node tryblock;
+ Node catchblocks = null;
+ Node finallyblock = null;
+
+ decompiler.addToken(Token.TRY);
+ if (peekToken() != Token.LC) {
+ reportError("msg.no.brace.try");
+ }
+ decompiler.addEOL(Token.LC);
+ tryblock = statement();
+ decompiler.addEOL(Token.RC);
+
+ catchblocks = nf.createLeaf(Token.BLOCK);
+
+ boolean sawDefaultCatch = false;
+ int peek = peekToken();
+ if (peek == Token.CATCH) {
+ while (matchToken(Token.CATCH)) {
+ if (sawDefaultCatch) {
+ reportError("msg.catch.unreachable");
+ }
+ decompiler.addToken(Token.CATCH);
+ mustMatchToken(Token.LP, "msg.no.paren.catch");
+ decompiler.addToken(Token.LP);
+
+ mustMatchToken(Token.NAME, "msg.bad.catchcond");
+ String varName = ts.getString();
+ decompiler.addName(varName);
+
+ Node catchCond = null;
+ if (matchToken(Token.IF)) {
+ decompiler.addToken(Token.IF);
+ catchCond = expr(false);
+ } else {
+ sawDefaultCatch = true;
+ }
+
+ mustMatchToken(Token.RP, "msg.bad.catchcond");
+ decompiler.addToken(Token.RP);
+ mustMatchToken(Token.LC, "msg.no.brace.catchblock");
+ decompiler.addEOL(Token.LC);
+
+ nf.addChildToBack(catchblocks,
+ nf.createCatch(varName, catchCond,
+ statements(null),
+ ts.getLineno()));
+
+ mustMatchToken(Token.RC, "msg.no.brace.after.body");
+ decompiler.addEOL(Token.RC);
+ }
+ } else if (peek != Token.FINALLY) {
+ mustMatchToken(Token.FINALLY, "msg.try.no.catchfinally");
+ }
+
+ if (matchToken(Token.FINALLY)) {
+ decompiler.addToken(Token.FINALLY);
+ decompiler.addEOL(Token.LC);
+ finallyblock = statement();
+ decompiler.addEOL(Token.RC);
+ }
+
+ pn = nf.createTryCatchFinally(tryblock, catchblocks,
+ finallyblock, lineno);
+
+ return pn;
+ }
+
+ case Token.THROW: {
+ consumeToken();
+ if (peekTokenOrEOL() == Token.EOL) {
+ // ECMAScript does not allow new lines before throw expression,
+ // see bug 256617
+ reportError("msg.bad.throw.eol");
+ }
+
+ int lineno = ts.getLineno();
+ decompiler.addToken(Token.THROW);
+ pn = nf.createThrow(expr(false), lineno);
+ break;
+ }
+
+ case Token.BREAK: {
+ consumeToken();
+ int lineno = ts.getLineno();
+
+ decompiler.addToken(Token.BREAK);
+
+ // matchJumpLabelName only matches if there is one
+ Node breakStatement = matchJumpLabelName();
+ if (breakStatement == null) {
+ if (loopAndSwitchSet == null || loopAndSwitchSet.size() == 0) {
+ reportError("msg.bad.break");
+ return null;
+ }
+ breakStatement = (Node)loopAndSwitchSet.peek();
+ }
+ pn = nf.createBreak(breakStatement, lineno);
+ break;
+ }
+
+ case Token.CONTINUE: {
+ consumeToken();
+ int lineno = ts.getLineno();
+
+ decompiler.addToken(Token.CONTINUE);
+
+ Node loop;
+ // matchJumpLabelName only matches if there is one
+ Node label = matchJumpLabelName();
+ if (label == null) {
+ if (loopSet == null || loopSet.size() == 0) {
+ reportError("msg.continue.outside");
+ return null;
+ }
+ loop = (Node)loopSet.peek();
+ } else {
+ loop = nf.getLabelLoop(label);
+ if (loop == null) {
+ reportError("msg.continue.nonloop");
+ return null;
+ }
+ }
+ pn = nf.createContinue(loop, lineno);
+ break;
+ }
+
+ case Token.WITH: {
+ consumeToken();
+
+ decompiler.addToken(Token.WITH);
+ int lineno = ts.getLineno();
+ mustMatchToken(Token.LP, "msg.no.paren.with");
+ decompiler.addToken(Token.LP);
+ Node obj = expr(false);
+ /*APPJET*/obj.lineno = lineno;
+ /*APPJET*/obj.statementEndLineNum = lastConsumedTokenLine;
+ mustMatchToken(Token.RP, "msg.no.paren.after.with");
+ decompiler.addToken(Token.RP);
+ decompiler.addEOL(Token.LC);
+
+ ++nestingOfWith;
+ Node body;
+ try {
+ body = statement();
+ } finally {
+ --nestingOfWith;
+ }
+
+ decompiler.addEOL(Token.RC);
+
+ pn = nf.createWith(obj, body, lineno);
+ return pn;
+ }
+
+ case Token.CONST:
+ case Token.VAR: {
+ consumeToken();
+ decompiler.addToken(tt);
+ pn = variables(false, tt);
+ break;
+ }
+
+ case Token.LET: {
+ consumeToken();
+ decompiler.addToken(Token.LET);
+ if (peekToken() == Token.LP) {
+ pn = let(true);
+ } else {
+ pn = variables(false, tt);
+ }
+ return pn;
+ }
+
+ case Token.RETURN:
+ case Token.YIELD: {
+ pn = returnOrYield(tt, false);
+ break;
+ }
+
+ case Token.DEBUGGER:
+ consumeToken();
+ decompiler.addToken(Token.DEBUGGER);
+ pn = nf.createDebugger(ts.getLineno());
+ break;
+
+ case Token.LC:
+ consumeToken();
+ if (statementLabel != null) {
+ decompiler.addToken(Token.LC);
+ }
+ Node scope = nf.createScopeNode(Token.BLOCK, ts.getLineno());
+ pushScope(scope);
+ try {
+ statements(scope);
+ mustMatchToken(Token.RC, "msg.no.brace.block");
+ if (statementLabel != null) {
+ decompiler.addEOL(Token.RC);
+ }
+ return scope;
+ } finally {
+ popScope();
+ }
+
+ case Token.ERROR:
+ // Fall thru, to have a node for error recovery to work on
+ case Token.SEMI:
+ consumeToken();
+ pn = nf.createLeaf(Token.EMPTY);
+ return pn;
+
+ case Token.FUNCTION: {
+ consumeToken();
+ pn = function(FunctionNode.FUNCTION_EXPRESSION_STATEMENT);
+ return pn;
+ }
+
+ case Token.DEFAULT :
+ consumeToken();
+ mustHaveXML();
+
+ decompiler.addToken(Token.DEFAULT);
+ int nsLine = ts.getLineno();
+
+ if (!(matchToken(Token.NAME)
+ && ts.getString().equals("xml")))
+ {
+ reportError("msg.bad.namespace");
+ }
+ decompiler.addName(" xml");
+
+ if (!(matchToken(Token.NAME)
+ && ts.getString().equals("namespace")))
+ {
+ reportError("msg.bad.namespace");
+ }
+ decompiler.addName(" namespace");
+
+ if (!matchToken(Token.ASSIGN)) {
+ reportError("msg.bad.namespace");
+ }
+ decompiler.addToken(Token.ASSIGN);
+
+ Node expr = expr(false);
+ pn = nf.createDefaultNamespace(expr, nsLine);
+ break;
+
+ case Token.NAME: {
+ int lineno = ts.getLineno();
+ String name = ts.getString();
+ setCheckForLabel();
+ pn = expr(false);
+ if (pn.getType() != Token.LABEL) {
+ pn = nf.createExprStatement(pn, lineno);
+ } else {
+ // Parsed the label: push back token should be
+ // colon that primaryExpr left untouched.
+ if (peekToken() != Token.COLON) Kit.codeBug();
+ consumeToken();
+ // depend on decompiling lookahead to guess that that
+ // last name was a label.
+ decompiler.addName(name);
+ decompiler.addEOL(Token.COLON);
+
+ if (labelSet == null) {
+ labelSet = new Hashtable();
+ } else if (labelSet.containsKey(name)) {
+ reportError("msg.dup.label");
+ }
+
+ boolean firstLabel;
+ if (statementLabel == null) {
+ firstLabel = true;
+ statementLabel = pn;
+ } else {
+ // Discard multiple label nodes and use only
+ // the first: it allows to simplify IRFactory
+ firstLabel = false;
+ }
+ labelSet.put(name, statementLabel);
+ try {
+ pn = statementHelper(statementLabel);
+ } finally {
+ labelSet.remove(name);
+ }
+ if (firstLabel) {
+ pn = nf.createLabeledStatement(statementLabel, pn);
+ }
+ return pn;
+ }
+ break;
+ }
+
+ default: {
+ int lineno = ts.getLineno();
+ pn = expr(false);
+ pn = nf.createExprStatement(pn, lineno);
+ break;
+ }
+ }
+
+ int ttFlagged = peekFlaggedToken();
+ switch (ttFlagged & CLEAR_TI_MASK) {
+ case Token.SEMI:
+ // Consume ';' as a part of expression
+ consumeToken();
+ break;
+ case Token.ERROR:
+ case Token.EOF:
+ case Token.RC:
+ // Autoinsert ;
+ break;
+ default:
+ if ((ttFlagged & TI_AFTER_EOL) == 0) {
+ // Report error if no EOL or autoinsert ; otherwise
+ reportError("msg.no.semi.stmt");
+ }
+ break;
+ }
+ decompiler.addEOL(Token.SEMI);
+
+ return pn;
+ }
+
+ /**
+ * Returns whether or not the bits in the mask have changed to all set.
+ * @param before bits before change
+ * @param after bits after change
+ * @param mask mask for bits
+ * @return true if all the bits in the mask are set in "after" but not
+ * "before"
+ */
+ private static final boolean nowAllSet(int before, int after, int mask)
+ {
+ return ((before & mask) != mask) && ((after & mask) == mask);
+ }
+
+ private Node returnOrYield(int tt, boolean exprContext)
+ throws IOException, ParserException
+ {
+ if (!insideFunction()) {
+ reportError(tt == Token.RETURN ? "msg.bad.return"
+ : "msg.bad.yield");
+ }
+ consumeToken();
+ decompiler.addToken(tt);
+ int lineno = ts.getLineno();
+
+ Node e;
+ /* This is ugly, but we don't want to require a semicolon. */
+ switch (peekTokenOrEOL()) {
+ case Token.SEMI:
+ case Token.RC:
+ case Token.EOF:
+ case Token.EOL:
+ case Token.ERROR:
+ case Token.RB:
+ case Token.RP:
+ case Token.YIELD:
+ e = null;
+ break;
+ default:
+ e = expr(false);
+ break;
+ }
+
+ int before = endFlags;
+ Node ret;
+
+ if (tt == Token.RETURN) {
+ if (e == null ) {
+ endFlags |= Node.END_RETURNS;
+ } else {
+ endFlags |= Node.END_RETURNS_VALUE;
+ hasReturnValue = true;
+ }
+ ret = nf.createReturn(e, lineno);
+
+ // see if we need a strict mode warning
+ if (nowAllSet(before, endFlags,
+ Node.END_RETURNS|Node.END_RETURNS_VALUE))
+ {
+ addStrictWarning("msg.return.inconsistent", "");
+ }
+ } else {
+ endFlags |= Node.END_YIELDS;
+ ret = nf.createYield(e, lineno);
+ if (!exprContext)
+ ret = new Node(Token.EXPR_VOID, ret, lineno);
+ }
+
+ // see if we are mixing yields and value returns.
+ if (nowAllSet(before, endFlags,
+ Node.END_YIELDS|Node.END_RETURNS_VALUE))
+ {
+ String name = ((FunctionNode)currentScriptOrFn).getFunctionName();
+ if (name.length() == 0)
+ addError("msg.anon.generator.returns", "");
+ else
+ addError("msg.generator.returns", name);
+ }
+
+ return ret;
+ }
+
+ /**
+ * Parse a 'var' or 'const' statement, or a 'var' init list in a for
+ * statement.
+ * @param inFor true if we are currently in the midst of the init
+ * clause of a for.
+ * @param inStatement true if called in a statement (as opposed to an
+ * expression) context
+ * @param declType A token value: either VAR, CONST, or LET depending on
+ * context.
+ * @return The parsed statement
+ * @throws IOException
+ * @throws ParserException
+ */
+ private Node variables(boolean inFor, int declType)
+ throws IOException, ParserException
+ {
+ Node result = nf.createVariables(declType, ts.getLineno());
+ boolean first = true;
+ for (;;) {
+ Node destructuring = null;
+ String s = null;
+ int tt = peekToken();
+ if (tt == Token.LB || tt == Token.LC) {
+ // Destructuring assignment, e.g., var [a,b] = ...
+ destructuring = primaryExpr();
+ } else {
+ // Simple variable name
+ mustMatchToken(Token.NAME, "msg.bad.var",
+ Token.name(declType).toLowerCase());
+ s = ts.getString();
+
+ if (!first)
+ decompiler.addToken(Token.COMMA);
+ first = false;
+
+ decompiler.addName(s);
+ defineSymbol(declType, s);
+ }
+
+ Node init = null;
+ if (matchToken(Token.ASSIGN)) {
+ decompiler.addToken(Token.ASSIGN);
+ init = assignExpr(inFor);
+ }
+
+ if (destructuring != null) {
+ if (init == null) {
+ if (!inFor)
+ reportError("msg.destruct.assign.no.init");
+ nf.addChildToBack(result, destructuring);
+ } else {
+ nf.addChildToBack(result,
+ nf.createDestructuringAssignment(declType,
+ destructuring, init));
+ }
+ } else {
+ Node name = nf.createName(s);
+ if (init != null)
+ nf.addChildToBack(name, init);
+ nf.addChildToBack(result, name);
+ }
+
+ if (!matchToken(Token.COMMA))
+ break;
+ }
+ return result;
+ }
+
+
+ private Node let(boolean isStatement)
+ throws IOException, ParserException
+ {
+ mustMatchToken(Token.LP, "msg.no.paren.after.let");
+ decompiler.addToken(Token.LP);
+ Node result = nf.createScopeNode(Token.LET, ts.getLineno());
+ pushScope(result);
+ try {
+ Node vars = variables(false, Token.LET);
+ nf.addChildToBack(result, vars);
+ mustMatchToken(Token.RP, "msg.no.paren.let");
+ decompiler.addToken(Token.RP);
+ if (isStatement && peekToken() == Token.LC) {
+ // let statement
+ consumeToken();
+ decompiler.addEOL(Token.LC);
+ nf.addChildToBack(result, statements(null));
+ mustMatchToken(Token.RC, "msg.no.curly.let");
+ decompiler.addToken(Token.RC);
+ } else {
+ // let expression
+ result.setType(Token.LETEXPR);
+ nf.addChildToBack(result, expr(false));
+ if (isStatement) {
+ // let expression in statement context
+ result = nf.createExprStatement(result, ts.getLineno());
+ }
+ }
+ } finally {
+ popScope();
+ }
+ return result;
+ }
+
+ void defineSymbol(int declType, String name) {
+ Node.Scope definingScope = currentScope.getDefiningScope(name);
+ Node.Scope.Symbol symbol = definingScope != null
+ ? definingScope.getSymbol(name)
+ : null;
+ boolean error = false;
+ if (symbol != null && (symbol.declType == Token.CONST ||
+ declType == Token.CONST))
+ {
+ error = true;
+ } else {
+ switch (declType) {
+ case Token.LET:
+ if (symbol != null && definingScope == currentScope) {
+ error = symbol.declType == Token.LET;
+ }
+ currentScope.putSymbol(name,
+ new Node.Scope.Symbol(declType, name));
+ break;
+
+ case Token.VAR:
+ case Token.CONST:
+ case Token.FUNCTION:
+ if (symbol != null) {
+ if (symbol.declType == Token.VAR)
+ addStrictWarning("msg.var.redecl", name);
+ else if (symbol.declType == Token.LP) {
+ addStrictWarning("msg.var.hides.arg", name);
+ }
+ } else {
+ currentScriptOrFn.putSymbol(name,
+ new Node.Scope.Symbol(declType, name));
+ }
+ break;
+
+ case Token.LP:
+ if (symbol != null) {
+ // must be duplicate parameter. Second parameter hides the
+ // first, so go ahead and add the second pararameter
+ addWarning("msg.dup.parms", name);
+ }
+ currentScriptOrFn.putSymbol(name,
+ new Node.Scope.Symbol(declType, name));
+ break;
+
+ default:
+ throw Kit.codeBug();
+ }
+ }
+ if (error) {
+ addError(symbol.declType == Token.CONST ? "msg.const.redecl" :
+ symbol.declType == Token.LET ? "msg.let.redecl" :
+ symbol.declType == Token.VAR ? "msg.var.redecl" :
+ symbol.declType == Token.FUNCTION ? "msg.fn.redecl" :
+ "msg.parm.redecl", name);
+ }
+ }
+
+ private Node expr(boolean inForInit)
+ throws IOException, ParserException
+ {
+ Node pn = assignExpr(inForInit);
+ while (matchToken(Token.COMMA)) {
+ decompiler.addToken(Token.COMMA);
+ if (compilerEnv.isStrictMode() && !pn.hasSideEffects())
+ addStrictWarning("msg.no.side.effects", "");
+ if (peekToken() == Token.YIELD) {
+ reportError("msg.yield.parenthesized");
+ }
+ pn = nf.createBinary(Token.COMMA, pn, assignExpr(inForInit));
+ }
+ return pn;
+ }
+
+ private Node assignExpr(boolean inForInit)
+ throws IOException, ParserException
+ {
+ int tt = peekToken();
+ if (tt == Token.YIELD) {
+ consumeToken();
+ return returnOrYield(tt, true);
+ }
+ Node pn = condExpr(inForInit);
+
+ tt = peekToken();
+ if (Token.FIRST_ASSIGN <= tt && tt <= Token.LAST_ASSIGN) {
+ consumeToken();
+ decompiler.addToken(tt);
+ pn = nf.createAssignment(tt, pn, assignExpr(inForInit));
+ }
+
+ return pn;
+ }
+
+ private Node condExpr(boolean inForInit)
+ throws IOException, ParserException
+ {
+ Node pn = orExpr(inForInit);
+
+ if (matchToken(Token.HOOK)) {
+ decompiler.addToken(Token.HOOK);
+ Node ifTrue = assignExpr(false);
+ mustMatchToken(Token.COLON, "msg.no.colon.cond");
+ decompiler.addToken(Token.COLON);
+ Node ifFalse = assignExpr(inForInit);
+ return nf.createCondExpr(pn, ifTrue, ifFalse);
+ }
+
+ return pn;
+ }
+
+ private Node orExpr(boolean inForInit)
+ throws IOException, ParserException
+ {
+ Node pn = andExpr(inForInit);
+ if (matchToken(Token.OR)) {
+ decompiler.addToken(Token.OR);
+ pn = nf.createBinary(Token.OR, pn, orExpr(inForInit));
+ }
+
+ return pn;
+ }
+
+ private Node andExpr(boolean inForInit)
+ throws IOException, ParserException
+ {
+ Node pn = bitOrExpr(inForInit);
+ if (matchToken(Token.AND)) {
+ decompiler.addToken(Token.AND);
+ pn = nf.createBinary(Token.AND, pn, andExpr(inForInit));
+ }
+
+ return pn;
+ }
+
+ private Node bitOrExpr(boolean inForInit)
+ throws IOException, ParserException
+ {
+ Node pn = bitXorExpr(inForInit);
+ while (matchToken(Token.BITOR)) {
+ decompiler.addToken(Token.BITOR);
+ pn = nf.createBinary(Token.BITOR, pn, bitXorExpr(inForInit));
+ }
+ return pn;
+ }
+
+ private Node bitXorExpr(boolean inForInit)
+ throws IOException, ParserException
+ {
+ Node pn = bitAndExpr(inForInit);
+ while (matchToken(Token.BITXOR)) {
+ decompiler.addToken(Token.BITXOR);
+ pn = nf.createBinary(Token.BITXOR, pn, bitAndExpr(inForInit));
+ }
+ return pn;
+ }
+
+ private Node bitAndExpr(boolean inForInit)
+ throws IOException, ParserException
+ {
+ Node pn = eqExpr(inForInit);
+ while (matchToken(Token.BITAND)) {
+ decompiler.addToken(Token.BITAND);
+ pn = nf.createBinary(Token.BITAND, pn, eqExpr(inForInit));
+ }
+ return pn;
+ }
+
+ private Node eqExpr(boolean inForInit)
+ throws IOException, ParserException
+ {
+ Node pn = relExpr(inForInit);
+ for (;;) {
+ int tt = peekToken();
+ switch (tt) {
+ case Token.EQ:
+ case Token.NE:
+ case Token.SHEQ:
+ case Token.SHNE:
+ consumeToken();
+ int decompilerToken = tt;
+ int parseToken = tt;
+ if (compilerEnv.getLanguageVersion() == Context.VERSION_1_2) {
+ // JavaScript 1.2 uses shallow equality for == and != .
+ // In addition, convert === and !== for decompiler into
+ // == and != since the decompiler is supposed to show
+ // canonical source and in 1.2 ===, !== are allowed
+ // only as an alias to ==, !=.
+ switch (tt) {
+ case Token.EQ:
+ parseToken = Token.SHEQ;
+ break;
+ case Token.NE:
+ parseToken = Token.SHNE;
+ break;
+ case Token.SHEQ:
+ decompilerToken = Token.EQ;
+ break;
+ case Token.SHNE:
+ decompilerToken = Token.NE;
+ break;
+ }
+ }
+ decompiler.addToken(decompilerToken);
+ pn = nf.createBinary(parseToken, pn, relExpr(inForInit));
+ continue;
+ }
+ break;
+ }
+ return pn;
+ }
+
+ private Node relExpr(boolean inForInit)
+ throws IOException, ParserException
+ {
+ Node pn = shiftExpr();
+ for (;;) {
+ int tt = peekToken();
+ switch (tt) {
+ case Token.IN:
+ if (inForInit)
+ break;
+ // fall through
+ case Token.INSTANCEOF:
+ case Token.LE:
+ case Token.LT:
+ case Token.GE:
+ case Token.GT:
+ consumeToken();
+ decompiler.addToken(tt);
+ pn = nf.createBinary(tt, pn, shiftExpr());
+ continue;
+ }
+ break;
+ }
+ return pn;
+ }
+
+ private Node shiftExpr()
+ throws IOException, ParserException
+ {
+ Node pn = addExpr();
+ for (;;) {
+ int tt = peekToken();
+ switch (tt) {
+ case Token.LSH:
+ case Token.URSH:
+ case Token.RSH:
+ consumeToken();
+ decompiler.addToken(tt);
+ pn = nf.createBinary(tt, pn, addExpr());
+ continue;
+ }
+ break;
+ }
+ return pn;
+ }
+
+ private Node addExpr()
+ throws IOException, ParserException
+ {
+ Node pn = mulExpr();
+ for (;;) {
+ int tt = peekToken();
+ if (tt == Token.ADD || tt == Token.SUB) {
+ consumeToken();
+ decompiler.addToken(tt);
+ // flushNewLines
+ pn = nf.createBinary(tt, pn, mulExpr());
+ continue;
+ }
+ break;
+ }
+
+ return pn;
+ }
+
+ private Node mulExpr()
+ throws IOException, ParserException
+ {
+ Node pn = unaryExpr();
+ for (;;) {
+ int tt = peekToken();
+ switch (tt) {
+ case Token.MUL:
+ case Token.DIV:
+ case Token.MOD:
+ consumeToken();
+ decompiler.addToken(tt);
+ pn = nf.createBinary(tt, pn, unaryExpr());
+ continue;
+ }
+ break;
+ }
+
+ return pn;
+ }
+
+ private Node unaryExpr()
+ throws IOException, ParserException
+ {
+ int tt;
+
+ tt = peekToken();
+
+ switch(tt) {
+ case Token.VOID:
+ case Token.NOT:
+ case Token.BITNOT:
+ case Token.TYPEOF:
+ consumeToken();
+ decompiler.addToken(tt);
+ return nf.createUnary(tt, unaryExpr());
+
+ case Token.ADD:
+ consumeToken();
+ // Convert to special POS token in decompiler and parse tree
+ decompiler.addToken(Token.POS);
+ return nf.createUnary(Token.POS, unaryExpr());
+
+ case Token.SUB:
+ consumeToken();
+ // Convert to special NEG token in decompiler and parse tree
+ decompiler.addToken(Token.NEG);
+ return nf.createUnary(Token.NEG, unaryExpr());
+
+ case Token.INC:
+ case Token.DEC:
+ consumeToken();
+ decompiler.addToken(tt);
+ return nf.createIncDec(tt, false, memberExpr(true));
+
+ case Token.DELPROP:
+ consumeToken();
+ decompiler.addToken(Token.DELPROP);
+ return nf.createUnary(Token.DELPROP, unaryExpr());
+
+ case Token.ERROR:
+ consumeToken();
+ break;
+
+ // XML stream encountered in expression.
+ case Token.LT:
+ if (compilerEnv.isXmlAvailable()) {
+ consumeToken();
+ Node pn = xmlInitializer();
+ return memberExprTail(true, pn);
+ }
+ // Fall thru to the default handling of RELOP
+
+ default:
+ Node pn = memberExpr(true);
+
+ // Don't look across a newline boundary for a postfix incop.
+ tt = peekTokenOrEOL();
+ if (tt == Token.INC || tt == Token.DEC) {
+ consumeToken();
+ decompiler.addToken(tt);
+ return nf.createIncDec(tt, true, pn);
+ }
+ return pn;
+ }
+ return nf.createName("error"); // Only reached on error.Try to continue.
+
+ }
+
+ private Node xmlInitializer() throws IOException
+ {
+ int tt = ts.getFirstXMLToken();
+ if (tt != Token.XML && tt != Token.XMLEND) {
+ reportError("msg.syntax");
+ return null;
+ }
+
+ /* Make a NEW node to append to. */
+ Node pnXML = nf.createLeaf(Token.NEW);
+
+ String xml = ts.getString();
+ boolean fAnonymous = xml.trim().startsWith("<>");
+
+ Node pn = nf.createName(fAnonymous ? "XMLList" : "XML");
+ nf.addChildToBack(pnXML, pn);
+
+ pn = null;
+ Node expr;
+ for (;;tt = ts.getNextXMLToken()) {
+ switch (tt) {
+ case Token.XML:
+ xml = ts.getString();
+ decompiler.addName(xml);
+ mustMatchToken(Token.LC, "msg.syntax");
+ decompiler.addToken(Token.LC);
+ expr = (peekToken() == Token.RC)
+ ? nf.createString("")
+ : expr(false);
+ mustMatchToken(Token.RC, "msg.syntax");
+ decompiler.addToken(Token.RC);
+ if (pn == null) {
+ pn = nf.createString(xml);
+ } else {
+ pn = nf.createBinary(Token.ADD, pn, nf.createString(xml));
+ }
+ if (ts.isXMLAttribute()) {
+ /* Need to put the result in double quotes */
+ expr = nf.createUnary(Token.ESCXMLATTR, expr);
+ Node prepend = nf.createBinary(Token.ADD,
+ nf.createString("\""),
+ expr);
+ expr = nf.createBinary(Token.ADD,
+ prepend,
+ nf.createString("\""));
+ } else {
+ expr = nf.createUnary(Token.ESCXMLTEXT, expr);
+ }
+ pn = nf.createBinary(Token.ADD, pn, expr);
+ break;
+ case Token.XMLEND:
+ xml = ts.getString();
+ decompiler.addName(xml);
+ if (pn == null) {
+ pn = nf.createString(xml);
+ } else {
+ pn = nf.createBinary(Token.ADD, pn, nf.createString(xml));
+ }
+
+ nf.addChildToBack(pnXML, pn);
+ return pnXML;
+ default:
+ reportError("msg.syntax");
+ return null;
+ }
+ }
+ }
+
+ private void argumentList(Node listNode)
+ throws IOException, ParserException
+ {
+ boolean matched;
+ matched = matchToken(Token.RP);
+ if (!matched) {
+ boolean first = true;
+ do {
+ if (!first)
+ decompiler.addToken(Token.COMMA);
+ first = false;
+ if (peekToken() == Token.YIELD) {
+ reportError("msg.yield.parenthesized");
+ }
+ nf.addChildToBack(listNode, assignExpr(false));
+ } while (matchToken(Token.COMMA));
+
+ mustMatchToken(Token.RP, "msg.no.paren.arg");
+ }
+ decompiler.addToken(Token.RP);
+ }
+
+ private Node memberExpr(boolean allowCallSyntax)
+ throws IOException, ParserException
+ {
+ int tt;
+
+ Node pn;
+
+ /* Check for new expressions. */
+ tt = peekToken();
+ if (tt == Token.NEW) {
+ /* Eat the NEW token. */
+ consumeToken();
+ decompiler.addToken(Token.NEW);
+
+ /* Make a NEW node to append to. */
+ pn = nf.createCallOrNew(Token.NEW, memberExpr(false));
+
+ if (matchToken(Token.LP)) {
+ decompiler.addToken(Token.LP);
+ /* Add the arguments to pn, if any are supplied. */
+ argumentList(pn);
+ }
+
+ /* XXX there's a check in the C source against
+ * "too many constructor arguments" - how many
+ * do we claim to support?
+ */
+
+ /* Experimental syntax: allow an object literal to follow a new expression,
+ * which will mean a kind of anonymous class built with the JavaAdapter.
+ * the object literal will be passed as an additional argument to the constructor.
+ */
+ tt = peekToken();
+ if (tt == Token.LC) {
+ nf.addChildToBack(pn, primaryExpr());
+ }
+ } else {
+ pn = primaryExpr();
+ }
+
+ return memberExprTail(allowCallSyntax, pn);
+ }
+
+ private Node memberExprTail(boolean allowCallSyntax, Node pn)
+ throws IOException, ParserException
+ {
+ tailLoop:
+ for (;;) {
+ int tt = peekToken();
+ switch (tt) {
+
+ case Token.DOT:
+ case Token.DOTDOT:
+ {
+ int memberTypeFlags;
+ String s;
+
+ consumeToken();
+ decompiler.addToken(tt);
+ memberTypeFlags = 0;
+ if (tt == Token.DOTDOT) {
+ mustHaveXML();
+ memberTypeFlags = Node.DESCENDANTS_FLAG;
+ }
+ if (!compilerEnv.isXmlAvailable()) {
+ mustMatchToken(Token.NAME, "msg.no.name.after.dot");
+ s = ts.getString();
+ decompiler.addName(s);
+ pn = nf.createPropertyGet(pn, null, s, memberTypeFlags);
+ break;
+ }
+
+ tt = nextToken();
+ switch (tt) {
+
+ // needed for generator.throw();
+ case Token.THROW:
+ decompiler.addName("throw");
+ pn = propertyName(pn, "throw", memberTypeFlags);
+ break;
+
+ // handles: name, ns::name, ns::*, ns::[expr]
+ case Token.NAME:
+ s = ts.getString();
+ decompiler.addName(s);
+ pn = propertyName(pn, s, memberTypeFlags);
+ break;
+
+ // handles: *, *::name, *::*, *::[expr]
+ case Token.MUL:
+ decompiler.addName("*");
+ pn = propertyName(pn, "*", memberTypeFlags);
+ break;
+
+ // handles: '@attr', '@ns::attr', '@ns::*', '@ns::*',
+ // '@::attr', '@::*', '@*', '@*::attr', '@*::*'
+ case Token.XMLATTR:
+ decompiler.addToken(Token.XMLATTR);
+ pn = attributeAccess(pn, memberTypeFlags);
+ break;
+
+ default:
+ reportError("msg.no.name.after.dot");
+ }
+ }
+ break;
+
+ case Token.DOTQUERY:
+ consumeToken();
+ mustHaveXML();
+ decompiler.addToken(Token.DOTQUERY);
+ pn = nf.createDotQuery(pn, expr(false), ts.getLineno());
+ mustMatchToken(Token.RP, "msg.no.paren");
+ decompiler.addToken(Token.RP);
+ break;
+
+ case Token.LB:
+ consumeToken();
+ decompiler.addToken(Token.LB);
+ pn = nf.createElementGet(pn, null, expr(false), 0);
+ mustMatchToken(Token.RB, "msg.no.bracket.index");
+ decompiler.addToken(Token.RB);
+ break;
+
+ case Token.LP:
+ if (!allowCallSyntax) {
+ break tailLoop;
+ }
+ consumeToken();
+ decompiler.addToken(Token.LP);
+ pn = nf.createCallOrNew(Token.CALL, pn);
+ /* Add the arguments to pn, if any are supplied. */
+ argumentList(pn);
+ break;
+
+ default:
+ break tailLoop;
+ }
+ }
+ return pn;
+ }
+
+ /*
+ * Xml attribute expression:
+ * '@attr', '@ns::attr', '@ns::*', '@ns::*', '@*', '@*::attr', '@*::*'
+ */
+ private Node attributeAccess(Node pn, int memberTypeFlags)
+ throws IOException
+ {
+ memberTypeFlags |= Node.ATTRIBUTE_FLAG;
+ int tt = nextToken();
+
+ switch (tt) {
+ // handles: @name, @ns::name, @ns::*, @ns::[expr]
+ case Token.NAME:
+ {
+ String s = ts.getString();
+ decompiler.addName(s);
+ pn = propertyName(pn, s, memberTypeFlags);
+ }
+ break;
+
+ // handles: @*, @*::name, @*::*, @*::[expr]
+ case Token.MUL:
+ decompiler.addName("*");
+ pn = propertyName(pn, "*", memberTypeFlags);
+ break;
+
+ // handles @[expr]
+ case Token.LB:
+ decompiler.addToken(Token.LB);
+ pn = nf.createElementGet(pn, null, expr(false), memberTypeFlags);
+ mustMatchToken(Token.RB, "msg.no.bracket.index");
+ decompiler.addToken(Token.RB);
+ break;
+
+ default:
+ reportError("msg.no.name.after.xmlAttr");
+ pn = nf.createPropertyGet(pn, null, "?", memberTypeFlags);
+ break;
+ }
+
+ return pn;
+ }
+
+ /**
+ * Check if :: follows name in which case it becomes qualified name
+ */
+ private Node propertyName(Node pn, String name, int memberTypeFlags)
+ throws IOException, ParserException
+ {
+ String namespace = null;
+ if (matchToken(Token.COLONCOLON)) {
+ decompiler.addToken(Token.COLONCOLON);
+ namespace = name;
+
+ int tt = nextToken();
+ switch (tt) {
+ // handles name::name
+ case Token.NAME:
+ name = ts.getString();
+ decompiler.addName(name);
+ break;
+
+ // handles name::*
+ case Token.MUL:
+ decompiler.addName("*");
+ name = "*";
+ break;
+
+ // handles name::[expr]
+ case Token.LB:
+ decompiler.addToken(Token.LB);
+ pn = nf.createElementGet(pn, namespace, expr(false),
+ memberTypeFlags);
+ mustMatchToken(Token.RB, "msg.no.bracket.index");
+ decompiler.addToken(Token.RB);
+ return pn;
+
+ default:
+ reportError("msg.no.name.after.coloncolon");
+ name = "?";
+ }
+ }
+
+ pn = nf.createPropertyGet(pn, namespace, name, memberTypeFlags);
+ return pn;
+ }
+
+ private Node arrayComprehension(String arrayName, Node expr)
+ throws IOException, ParserException
+ {
+ if (nextToken() != Token.FOR)
+ throw Kit.codeBug(); // shouldn't be here if next token isn't 'for'
+ decompiler.addName(" "); // space after array literal expr
+ decompiler.addToken(Token.FOR);
+ boolean isForEach = false;
+ if (matchToken(Token.NAME)) {
+ decompiler.addName(ts.getString());
+ if (ts.getString().equals("each")) {
+ isForEach = true;
+ } else {
+ reportError("msg.no.paren.for");
+ }
+ }
+ mustMatchToken(Token.LP, "msg.no.paren.for");
+ decompiler.addToken(Token.LP);
+ String name;
+ int tt = peekToken();
+ if (tt == Token.LB || tt == Token.LC) {
+ // handle destructuring assignment
+ name = currentScriptOrFn.getNextTempName();
+ defineSymbol(Token.LP, name);
+ expr = nf.createBinary(Token.COMMA,
+ nf.createAssignment(Token.ASSIGN, primaryExpr(),
+ nf.createName(name)),
+ expr);
+ } else if (tt == Token.NAME) {
+ consumeToken();
+ name = ts.getString();
+ decompiler.addName(name);
+ } else {
+ reportError("msg.bad.var");
+ return nf.createNumber(0);
+ }
+
+ Node init = nf.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);
+
+ mustMatchToken(Token.IN, "msg.in.after.for.name");
+ decompiler.addToken(Token.IN);
+ Node iterator = expr(false);
+ mustMatchToken(Token.RP, "msg.no.paren.for.ctrl");
+ decompiler.addToken(Token.RP);
+
+ Node body;
+ tt = peekToken();
+ if (tt == Token.FOR) {
+ body = arrayComprehension(arrayName, expr);
+ } else {
+ Node call = nf.createCallOrNew(Token.CALL,
+ nf.createPropertyGet(nf.createName(arrayName), null,
+ "push", 0));
+ call.addChildToBack(expr);
+ body = new Node(Token.EXPR_VOID, call, ts.getLineno());
+ if (tt == Token.IF) {
+ consumeToken();
+ decompiler.addToken(Token.IF);
+ int lineno = ts.getLineno();
+ Node cond = condition();
+ body = nf.createIf(cond, body, null, lineno);
+ }
+ mustMatchToken(Token.RB, "msg.no.bracket.arg");
+ decompiler.addToken(Token.RB);
+ }
+
+ Node loop = enterLoop(null, true);
+ try {
+ return nf.createForIn(Token.LET, loop, init, iterator, body,
+ isForEach);
+ } finally {
+ exitLoop(false);
+ }
+ }
+
+ private Node primaryExpr()
+ throws IOException, ParserException
+ {
+ Node pn;
+
+ int ttFlagged = nextFlaggedToken();
+ int tt = ttFlagged & CLEAR_TI_MASK;
+
+ switch(tt) {
+
+ case Token.FUNCTION:
+ return function(FunctionNode.FUNCTION_EXPRESSION);
+
+ case Token.LB: {
+ ObjArray elems = new ObjArray();
+ int skipCount = 0;
+ int destructuringLen = 0;
+ decompiler.addToken(Token.LB);
+ boolean after_lb_or_comma = true;
+ for (;;) {
+ tt = peekToken();
+
+ if (tt == Token.COMMA) {
+ consumeToken();
+ decompiler.addToken(Token.COMMA);
+ if (!after_lb_or_comma) {
+ after_lb_or_comma = true;
+ } else {
+ elems.add(null);
+ ++skipCount;
+ }
+ } else if (tt == Token.RB) {
+ consumeToken();
+ decompiler.addToken(Token.RB);
+ // for ([a,] in obj) is legal, but for ([a] in obj) is
+ // not since we have both key and value supplied. The
+ // trick is that [a,] and [a] are equivalent in other
+ // array literal contexts. So we calculate a special
+ // length value just for destructuring assignment.
+ destructuringLen = elems.size() +
+ (after_lb_or_comma ? 1 : 0);
+ break;
+ } else if (skipCount == 0 && elems.size() == 1 &&
+ tt == Token.FOR)
+ {
+ Node scopeNode = nf.createScopeNode(Token.ARRAYCOMP,
+ ts.getLineno());
+ String tempName = currentScriptOrFn.getNextTempName();
+ pushScope(scopeNode);
+ try {
+ defineSymbol(Token.LET, tempName);
+ Node expr = (Node) elems.get(0);
+ Node block = nf.createBlock(ts.getLineno());
+ Node init = new Node(Token.EXPR_VOID,
+ nf.createAssignment(Token.ASSIGN,
+ nf.createName(tempName),
+ nf.createCallOrNew(Token.NEW,
+ nf.createName("Array"))), ts.getLineno());
+ block.addChildToBack(init);
+ block.addChildToBack(arrayComprehension(tempName,
+ expr));
+ scopeNode.addChildToBack(block);
+ scopeNode.addChildToBack(nf.createName(tempName));
+ return scopeNode;
+ } finally {
+ popScope();
+ }
+ } else {
+ if (!after_lb_or_comma) {
+ reportError("msg.no.bracket.arg");
+ }
+ elems.add(assignExpr(false));
+ after_lb_or_comma = false;
+ }
+ }
+ return nf.createArrayLiteral(elems, skipCount, destructuringLen);
+ }
+
+ case Token.LC: {
+ ObjArray elems = new ObjArray();
+ decompiler.addToken(Token.LC);
+ if (!matchToken(Token.RC)) {
+
+ boolean first = true;
+ commaloop:
+ do {
+ Object property;
+
+ if (!first)
+ decompiler.addToken(Token.COMMA);
+ else
+ first = false;
+
+ tt = peekToken();
+ switch(tt) {
+ case Token.NAME:
+ case Token.STRING:
+ consumeToken();
+ // map NAMEs to STRINGs in object literal context
+ // but tell the decompiler the proper type
+ String s = ts.getString();
+ if (tt == Token.NAME) {
+ if (s.equals("get") &&
+ peekToken() == Token.NAME) {
+ decompiler.addToken(Token.GET);
+ consumeToken();
+ s = ts.getString();
+ decompiler.addName(s);
+ property = ScriptRuntime.getIndexObject(s);
+ if (!getterSetterProperty(elems, property,
+ true))
+ break commaloop;
+ break;
+ } else if (s.equals("set") &&
+ peekToken() == Token.NAME) {
+ decompiler.addToken(Token.SET);
+ consumeToken();
+ s = ts.getString();
+ decompiler.addName(s);
+ property = ScriptRuntime.getIndexObject(s);
+ if (!getterSetterProperty(elems, property,
+ false))
+ break commaloop;
+ break;
+ }
+ decompiler.addName(s);
+ } else {
+ decompiler.addString(s);
+ }
+ property = ScriptRuntime.getIndexObject(s);
+ plainProperty(elems, property);
+ break;
+
+ case Token.NUMBER:
+ consumeToken();
+ double n = ts.getNumber();
+ decompiler.addNumber(n);
+ property = ScriptRuntime.getIndexObject(n);
+ plainProperty(elems, property);
+ break;
+
+ case Token.RC:
+ // trailing comma is OK.
+ break commaloop;
+ default:
+ reportError("msg.bad.prop");
+ break commaloop;
+ }
+ } while (matchToken(Token.COMMA));
+
+ mustMatchToken(Token.RC, "msg.no.brace.prop");
+ }
+ decompiler.addToken(Token.RC);
+ return nf.createObjectLiteral(elems);
+ }
+
+ case Token.LET:
+ decompiler.addToken(Token.LET);
+ return let(false);
+
+ case Token.LP:
+
+ /* Brendan's IR-jsparse.c makes a new node tagged with
+ * TOK_LP here... I'm not sure I understand why. Isn't
+ * the grouping already implicit in the structure of the
+ * parse tree? also TOK_LP is already overloaded (I
+ * think) in the C IR as 'function call.' */
+ decompiler.addToken(Token.LP);
+ pn = expr(false);
+ pn.putProp(Node.PARENTHESIZED_PROP, Boolean.TRUE);
+ decompiler.addToken(Token.RP);
+ mustMatchToken(Token.RP, "msg.no.paren");
+ return pn;
+
+ case Token.XMLATTR:
+ mustHaveXML();
+ decompiler.addToken(Token.XMLATTR);
+ pn = attributeAccess(null, 0);
+ return pn;
+
+ case Token.NAME: {
+ String name = ts.getString();
+ if ((ttFlagged & TI_CHECK_LABEL) != 0) {
+ if (peekToken() == Token.COLON) {
+ // Do not consume colon, it is used as unwind indicator
+ // to return to statementHelper.
+ // XXX Better way?
+ return nf.createLabel(ts.getLineno());
+ }
+ }
+
+ decompiler.addName(name);
+ if (compilerEnv.isXmlAvailable()) {
+ pn = propertyName(null, name, 0);
+ } else {
+ pn = nf.createName(name);
+ }
+ return pn;
+ }
+
+ case Token.NUMBER: {
+ double n = ts.getNumber();
+ decompiler.addNumber(n);
+ return nf.createNumber(n);
+ }
+
+ case Token.STRING: {
+ String s = ts.getString();
+ decompiler.addString(s);
+ return nf.createString(s);
+ }
+
+ case Token.DIV:
+ case Token.ASSIGN_DIV: {
+ // Got / or /= which should be treated as regexp in fact
+ ts.readRegExp(tt);
+ String flags = ts.regExpFlags;
+ ts.regExpFlags = null;
+ String re = ts.getString();
+ decompiler.addRegexp(re, flags);
+ /*APPJET*/
+ int index = currentScriptOrFn.addRegexp
+ (re, flags, getCurrentLineNumber());
+ return nf.createRegExp(index);
+ }
+
+ case Token.NULL:
+ case Token.THIS:
+ case Token.FALSE:
+ case Token.TRUE:
+ decompiler.addToken(tt);
+ return nf.createLeaf(tt);
+
+ case Token.RESERVED:
+ reportError("msg.reserved.id");
+ break;
+
+ case Token.ERROR:
+ /* the scanner or one of its subroutines reported the error. */
+ break;
+
+ case Token.EOF:
+ reportError("msg.unexpected.eof");
+ break;
+
+ default:
+ reportError("msg.syntax");
+ break;
+ }
+ return null; // should never reach here
+ }
+
+ private void plainProperty(ObjArray elems, Object property)
+ throws IOException {
+ mustMatchToken(Token.COLON, "msg.no.colon.prop");
+
+ // OBJLIT is used as ':' in object literal for
+ // decompilation to solve spacing ambiguity.
+ decompiler.addToken(Token.OBJECTLIT);
+ elems.add(property);
+ elems.add(assignExpr(false));
+ }
+
+ private boolean getterSetterProperty(ObjArray elems, Object property,
+ boolean isGetter) throws IOException {
+ Node f = function(FunctionNode.FUNCTION_EXPRESSION);
+ if (f.getType() != Token.FUNCTION) {
+ reportError("msg.bad.prop");
+ return false;
+ }
+ int fnIndex = f.getExistingIntProp(Node.FUNCTION_PROP);
+ FunctionNode fn = currentScriptOrFn.getFunctionNode(fnIndex);
+ if (fn.getFunctionName().length() != 0) {
+ reportError("msg.bad.prop");
+ return false;
+ }
+ elems.add(property);
+ if (isGetter) {
+ elems.add(nf.createUnary(Token.GET, f));
+ } else {
+ elems.add(nf.createUnary(Token.SET, f));
+ }
+ return true;
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/PolicySecurityController.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/PolicySecurityController.java
new file mode 100644
index 0000000..c4d3d7e
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/PolicySecurityController.java
@@ -0,0 +1,223 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.lang.ref.SoftReference;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.SecureClassLoader;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import org.mozilla.classfile.ByteCode;
+import org.mozilla.classfile.ClassFileWriter;
+
+/**
+ * A security controller relying on Java {@link Policy} in effect. When you use
+ * this security controller, your securityDomain objects must be instances of
+ * {@link CodeSource} representing the location from where you load your
+ * scripts. Any Java policy "grant" statements matching the URL and certificate
+ * in code sources will apply to the scripts. If you specify any certificates
+ * within your {@link CodeSource} objects, it is your responsibility to verify
+ * (or not) that the script source files are signed in whatever
+ * implementation-specific way you're using.
+ * @author Attila Szegedi
+ */
+public class PolicySecurityController extends SecurityController
+{
+ private static final byte[] secureCallerImplBytecode = loadBytecode();
+
+ // We're storing a CodeSource -> (ClassLoader -> SecureRenderer), since we
+ // need to have one renderer per class loader. We're using weak hash maps
+ // and soft references all the way, since we don't want to interfere with
+ // cleanup of either CodeSource or ClassLoader objects.
+ private static final Map callers = new WeakHashMap();
+
+ public Class getStaticSecurityDomainClassInternal() {
+ return CodeSource.class;
+ }
+
+ private static class Loader extends SecureClassLoader
+ implements GeneratedClassLoader
+ {
+ private final CodeSource codeSource;
+
+ Loader(ClassLoader parent, CodeSource codeSource)
+ {
+ super(parent);
+ this.codeSource = codeSource;
+ }
+
+ public Class defineClass(String name, byte[] data)
+ {
+ return defineClass(name, data, 0, data.length, codeSource);
+ }
+
+ public void linkClass(Class cl)
+ {
+ resolveClass(cl);
+ }
+ }
+
+ public GeneratedClassLoader createClassLoader(final ClassLoader parent,
+ final Object securityDomain)
+ {
+ return (Loader)AccessController.doPrivileged(
+ new PrivilegedAction()
+ {
+ public Object run()
+ {
+ return new Loader(parent, (CodeSource)securityDomain);
+ }
+ });
+ }
+
+ public Object getDynamicSecurityDomain(Object securityDomain)
+ {
+ // No separate notion of dynamic security domain - just return what was
+ // passed in.
+ return securityDomain;
+ }
+
+ public Object callWithDomain(final Object securityDomain, final Context cx,
+ Callable callable, Scriptable scope, Scriptable thisObj,
+ Object[] args)
+ {
+ // Run in doPrivileged as we might be checked for "getClassLoader"
+ // runtime permission
+ final ClassLoader classLoader = (ClassLoader)AccessController.doPrivileged(
+ new PrivilegedAction() {
+ public Object run() {
+ return cx.getApplicationClassLoader();
+ }
+ });
+ final CodeSource codeSource = (CodeSource)securityDomain;
+ Map classLoaderMap;
+ synchronized(callers)
+ {
+ classLoaderMap = (Map)callers.get(codeSource);
+ if(classLoaderMap == null)
+ {
+ classLoaderMap = new WeakHashMap();
+ callers.put(codeSource, classLoaderMap);
+ }
+ }
+ SecureCaller caller;
+ synchronized(classLoaderMap)
+ {
+ SoftReference ref = (SoftReference)classLoaderMap.get(classLoader);
+ if(ref != null)
+ {
+ caller = (SecureCaller)ref.get();
+ }
+ else
+ {
+ caller = null;
+ }
+ if(caller == null)
+ {
+ try
+ {
+ // Run in doPrivileged as we'll be checked for
+ // "createClassLoader" runtime permission
+ caller = (SecureCaller)AccessController.doPrivileged(
+ new PrivilegedExceptionAction()
+ {
+ public Object run() throws Exception
+ {
+ Loader loader = new Loader(classLoader,
+ codeSource);
+ Class c = loader.defineClass(
+ SecureCaller.class.getName() + "Impl",
+ secureCallerImplBytecode);
+ return c.newInstance();
+ }
+ });
+ classLoaderMap.put(classLoader, new SoftReference(caller));
+ }
+ catch(PrivilegedActionException ex)
+ {
+ throw new UndeclaredThrowableException(ex.getCause());
+ }
+ }
+ }
+ return caller.call(callable, cx, scope, thisObj, args);
+ }
+
+ public abstract static class SecureCaller
+ {
+ public abstract Object call(Callable callable, Context cx, Scriptable scope,
+ Scriptable thisObj, Object[] args);
+ }
+
+
+ private static byte[] loadBytecode()
+ {
+ String secureCallerClassName = SecureCaller.class.getName();
+ ClassFileWriter cfw = new ClassFileWriter(
+ secureCallerClassName + "Impl", secureCallerClassName,
+ "<generated>");
+ cfw.startMethod("<init>", "()V", ClassFileWriter.ACC_PUBLIC);
+ cfw.addALoad(0);
+ cfw.addInvoke(ByteCode.INVOKESPECIAL, secureCallerClassName,
+ "<init>", "()V");
+ cfw.add(ByteCode.RETURN);
+ cfw.stopMethod((short)1);
+ String callableCallSig =
+ "Lorg/mozilla/javascript/Context;" +
+ "Lorg/mozilla/javascript/Scriptable;" +
+ "Lorg/mozilla/javascript/Scriptable;" +
+ "[Ljava/lang/Object;)Ljava/lang/Object;";
+
+ cfw.startMethod("call",
+ "(Lorg/mozilla/javascript/Callable;" + callableCallSig,
+ (short)(ClassFileWriter.ACC_PUBLIC
+ | ClassFileWriter.ACC_FINAL));
+ for(int i = 1; i < 6; ++i) {
+ cfw.addALoad(i);
+ }
+ cfw.addInvoke(ByteCode.INVOKEINTERFACE,
+ "org/mozilla/javascript/Callable", "call",
+ "(" + callableCallSig);
+ cfw.add(ByteCode.ARETURN);
+ cfw.stopMethod((short)6);
+ return cfw.toByteArray();
+ }
+} \ No newline at end of file
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Ref.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Ref.java
new file mode 100644
index 0000000..1e237bc
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Ref.java
@@ -0,0 +1,64 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov, igor@fastmail.fm
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.io.Serializable;
+
+/**
+ * Generic notion of reference object that know how to query/modify the
+ * target objects based on some property/index.
+ */
+public abstract class Ref implements Serializable
+{
+ public boolean has(Context cx)
+ {
+ return true;
+ }
+
+ public abstract Object get(Context cx);
+
+ public abstract Object set(Context cx, Object value);
+
+ public boolean delete(Context cx)
+ {
+ return false;
+ }
+
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/RefCallable.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/RefCallable.java
new file mode 100644
index 0000000..6d4b61c
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/RefCallable.java
@@ -0,0 +1,59 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov, igor@mir2.org
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+/**
+ * Object that can allows assignments to the result of function calls.
+ */
+public interface RefCallable extends Callable
+{
+ /**
+ * Perform function call in reference context.
+ * The args array reference should not be stored in any object that is
+ * can be GC-reachable after this method returns. If this is necessary,
+ * for example, to implement {@link Ref} methods, then store args.clone(),
+ * not args array itself.
+ *
+ * @param cx the current Context for this thread
+ * @param thisObj the JavaScript <code>this</code> object
+ * @param args the array of arguments
+ */
+ public Ref refCall(Context cx, Scriptable thisObj, Object[] args);
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/RegExpProxy.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/RegExpProxy.java
new file mode 100644
index 0000000..ac29c6e
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/RegExpProxy.java
@@ -0,0 +1,71 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Roger Lawrence
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+/**
+ * A proxy for the regexp package, so that the regexp package can be
+ * loaded optionally.
+ *
+ * @author Norris Boyd
+ */
+public interface RegExpProxy
+{
+ // Types of regexp actions
+
+ public static final int RA_MATCH = 1;
+ public static final int RA_REPLACE = 2;
+ public static final int RA_SEARCH = 3;
+
+ public boolean isRegExp(Scriptable obj);
+
+ public Object compileRegExp(Context cx, String source, String flags);
+
+ public Scriptable wrapRegExp(Context cx, Scriptable scope,
+ Object compiled);
+
+ public Object action(Context cx, Scriptable scope,
+ Scriptable thisObj, Object[] args,
+ int actionType);
+
+ public int find_split(Context cx, Scriptable scope, String target,
+ String separator, Scriptable re,
+ int[] ip, int[] matchlen,
+ boolean[] matched, String[][] parensp);
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/RhinoException.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/RhinoException.java
new file mode 100644
index 0000000..b7f4a4d
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/RhinoException.java
@@ -0,0 +1,306 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+package org.mozilla.javascript;
+
+import java.io.CharArrayWriter;
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * The class of exceptions thrown by the JavaScript engine.
+ */
+public abstract class RhinoException extends RuntimeException
+{
+ RhinoException()
+ {
+ Evaluator e = Context.createInterpreter();
+ if (e != null)
+ e.captureStackInfo(this);
+ }
+
+ RhinoException(String details)
+ {
+ super(details);
+ Evaluator e = Context.createInterpreter();
+ if (e != null)
+ e.captureStackInfo(this);
+ }
+
+ public final String getMessage()
+ {
+ String details = details();
+ if (sourceName == null || lineNumber <= 0) {
+ return details;
+ }
+ StringBuffer buf = new StringBuffer(details);
+ buf.append(" (");
+ if (sourceName != null) {
+ buf.append(sourceName);
+ }
+ if (lineNumber > 0) {
+ buf.append('#');
+ buf.append(lineNumber);
+ }
+ buf.append(')');
+ return buf.toString();
+ }
+
+ public String details()
+ {
+ return super.getMessage();
+ }
+
+ /**
+ * Get the uri of the script source containing the error, or null
+ * if that information is not available.
+ */
+ public final String sourceName()
+ {
+ return sourceName;
+ }
+
+ /**
+ * Initialize the uri of the script source containing the error.
+ *
+ * @param sourceName the uri of the script source responsible for the error.
+ * It should not be <tt>null</tt>.
+ *
+ * @throws IllegalStateException if the method is called more then once.
+ */
+ public final void initSourceName(String sourceName)
+ {
+ if (sourceName == null) throw new IllegalArgumentException();
+ if (this.sourceName != null) throw new IllegalStateException();
+ this.sourceName = sourceName;
+ }
+
+ /**
+ * Returns the line number of the statement causing the error,
+ * or zero if not available.
+ */
+ public final int lineNumber()
+ {
+ return lineNumber;
+ }
+
+ /**
+ * Initialize the line number of the script statement causing the error.
+ *
+ * @param lineNumber the line number in the script source.
+ * It should be positive number.
+ *
+ * @throws IllegalStateException if the method is called more then once.
+ */
+ public final void initLineNumber(int lineNumber)
+ {
+ if (lineNumber <= 0) throw new IllegalArgumentException(String.valueOf(lineNumber));
+ if (this.lineNumber > 0) throw new IllegalStateException();
+ this.lineNumber = lineNumber;
+ }
+
+ /**
+ * The column number of the location of the error, or zero if unknown.
+ */
+ public final int columnNumber()
+ {
+ return columnNumber;
+ }
+
+ /**
+ * Initialize the column number of the script statement causing the error.
+ *
+ * @param columnNumber the column number in the script source.
+ * It should be positive number.
+ *
+ * @throws IllegalStateException if the method is called more then once.
+ */
+ public final void initColumnNumber(int columnNumber)
+ {
+ if (columnNumber <= 0) throw new IllegalArgumentException(String.valueOf(columnNumber));
+ if (this.columnNumber > 0) throw new IllegalStateException();
+ this.columnNumber = columnNumber;
+ }
+
+ /**
+ * The source text of the line causing the error, or null if unknown.
+ */
+ public final String lineSource()
+ {
+ return lineSource;
+ }
+
+ /**
+ * Initialize the text of the source line containing the error.
+ *
+ * @param lineSource the text of the source line responsible for the error.
+ * It should not be <tt>null</tt>.
+ *
+ * @throws IllegalStateException if the method is called more then once.
+ */
+ public final void initLineSource(String lineSource)
+ {
+ if (lineSource == null) throw new IllegalArgumentException();
+ if (this.lineSource != null) throw new IllegalStateException();
+ this.lineSource = lineSource;
+ }
+
+ final void recordErrorOrigin(String sourceName, int lineNumber,
+ String lineSource, int columnNumber)
+ {
+ // XXX: for compatibility allow for now -1 to mean 0
+ if (lineNumber == -1) {
+ lineNumber = 0;
+ }
+
+ if (sourceName != null) {
+ initSourceName(sourceName);
+ }
+ if (lineNumber != 0) {
+ initLineNumber(lineNumber);
+ }
+ if (lineSource != null) {
+ initLineSource(lineSource);
+ }
+ if (columnNumber != 0) {
+ initColumnNumber(columnNumber);
+ }
+ }
+
+ private String generateStackTrace()
+ {
+ // Get stable reference to work properly with concurrent access
+ CharArrayWriter writer = new CharArrayWriter();
+ super.printStackTrace(new PrintWriter(writer));
+ String origStackTrace = writer.toString();
+ Evaluator e = Context.createInterpreter();
+ if (e != null)
+ return e.getPatchedStack(this, origStackTrace);
+ return null;
+ }
+
+ /**
+ * Get a string representing the script stack of this exception.
+ * If optimization is enabled, this corresponds to all java stack elements
+ * with a source name ending with ".js".
+ * @return a script stack dump
+ * @since 1.6R6
+ */
+ public String getScriptStackTrace()
+ {
+ return getScriptStackTrace(new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ return name.endsWith(".js");
+ }
+ });
+ }
+
+ /**
+ * Get a string representing the script stack of this exception.
+ * If optimization is enabled, this corresponds to all java stack elements
+ * with a source name matching the <code>filter</code>.
+ * @param filter the file name filter to determine whether a file is a
+ * script file
+ * @return a script stack dump
+ * @since 1.6R6
+ */
+ public String getScriptStackTrace(FilenameFilter filter)
+ {
+ List interpreterStack;
+ Evaluator interpreter = Context.createInterpreter();
+ if (interpreter != null)
+ interpreterStack = interpreter.getScriptStack(this);
+ else
+ interpreterStack = new ArrayList();
+ int interpreterStackIndex = 0;
+ StringBuffer buffer = new StringBuffer();
+ String lineSeparator = SecurityUtilities.getSystemProperty("line.separator");
+ StackTraceElement[] stack = getStackTrace();
+ for (int i = 0; i < stack.length; i++) {
+ StackTraceElement e = stack[i];
+ String name = e.getFileName();
+ if (e.getLineNumber() > -1 && name != null &&
+ filter.accept(null, name))
+ {
+ buffer.append("\tat ");
+ buffer.append(e.getFileName());
+ buffer.append(':');
+ buffer.append(e.getLineNumber());
+ buffer.append(lineSeparator);
+ } else if (interpreterStack != null &&
+ "org.mozilla.javascript.Interpreter".equals(e.getClassName()) &&
+ "interpretLoop".equals(e.getMethodName()))
+ {
+ buffer.append(interpreterStack.get(interpreterStackIndex++));
+ }
+ }
+ return buffer.toString();
+ }
+
+ public void printStackTrace(PrintWriter s)
+ {
+ if (interpreterStackInfo == null) {
+ super.printStackTrace(s);
+ } else {
+ s.print(generateStackTrace());
+ }
+ }
+
+ public void printStackTrace(PrintStream s)
+ {
+ if (interpreterStackInfo == null) {
+ super.printStackTrace(s);
+ } else {
+ s.print(generateStackTrace());
+ }
+ }
+
+ private String sourceName;
+ private int lineNumber;
+ private String lineSource;
+ private int columnNumber;
+
+ Object interpreterStackInfo;
+ int[] interpreterLineData;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Script.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Script.java
new file mode 100644
index 0000000..4721ead
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Script.java
@@ -0,0 +1,73 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// API class
+
+package org.mozilla.javascript;
+
+/**
+ * All compiled scripts implement this interface.
+ * <p>
+ * This class encapsulates script execution relative to an
+ * object scope.
+ * @since 1.3
+ * @author Norris Boyd
+ */
+
+public interface Script {
+
+ /**
+ * Execute the script.
+ * <p>
+ * The script is executed in a particular runtime Context, which
+ * must be associated with the current thread.
+ * The script is executed relative to a scope--definitions and
+ * uses of global top-level variables and functions will access
+ * properties of the scope object. For compliant ECMA
+ * programs, the scope must be an object that has been initialized
+ * as a global object using <code>Context.initStandardObjects</code>.
+ * <p>
+ *
+ * @param cx the Context associated with the current thread
+ * @param scope the scope to execute relative to
+ * @return the result of executing the script
+ * @see org.mozilla.javascript.Context#initStandardObjects()
+ */
+ public Object exec(Context cx, Scriptable scope);
+
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptOrFnNode.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptOrFnNode.java
new file mode 100644
index 0000000..9ea6d1f
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptOrFnNode.java
@@ -0,0 +1,241 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ * Bob Jervis
+ * Norris Boyd
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.util.ArrayList;
+
+public class ScriptOrFnNode extends Node.Scope {
+
+ public ScriptOrFnNode(int nodeType) {
+ super(nodeType);
+ symbols = new ArrayList(4);
+ setParent(null);
+ }
+
+ public final String getSourceName() { return sourceName; }
+
+ public final void setSourceName(String sourceName) {
+ this.sourceName = sourceName;
+ }
+
+ public final int getEncodedSourceStart() { return encodedSourceStart; }
+
+ public final int getEncodedSourceEnd() { return encodedSourceEnd; }
+
+ public final void setEncodedSourceBounds(int start, int end) {
+ this.encodedSourceStart = start;
+ this.encodedSourceEnd = end;
+ }
+
+ public final int getBaseLineno() { return this.lineno; }
+
+ public final void setBaseLineno(int lineno) {
+ // One time action
+ if (lineno < 0 || this.lineno >= 0) Kit.codeBug();
+ this.lineno = lineno;
+ }
+
+ public final int getEndLineno() { return endLineno; }
+
+ public final void setEndLineno(int lineno) {
+ // One time action
+ if (lineno < 0 || endLineno >= 0) Kit.codeBug();
+ endLineno = lineno;
+ }
+
+ public final int getFunctionCount() {
+ if (functions == null) { return 0; }
+ return functions.size();
+ }
+
+ public final FunctionNode getFunctionNode(int i) {
+ return (FunctionNode)functions.get(i);
+ }
+
+ public final int addFunction(FunctionNode fnNode) {
+ if (fnNode == null) Kit.codeBug();
+ if (functions == null) { functions = new ObjArray(); }
+ functions.add(fnNode);
+ return functions.size() - 1;
+ }
+
+ public final int getRegexpCount() {
+ if (regexps == null) { return 0; }
+ return regexps.size() / 2;
+ }
+
+ public final String getRegexpString(int index) {
+ return (String)regexps.get(index * 2);
+ }
+
+ public final String getRegexpFlags(int index) {
+ return (String)regexps.get(index * 2 + 1);
+ }
+
+ /*APPJET*/public final int getRegexpLineno(int index) {
+ return (Integer)regexpLinenos.get(index);
+ }
+
+ public final int addRegexp(String string, String flags, /*APPJET*/
+ int lineno) {
+ if (string == null) Kit.codeBug();
+ if (regexps == null) {
+ /*APPJET*/
+ regexps = new ObjArray();
+ regexpLinenos = new ObjArray();
+ }
+ regexps.add(string);
+ regexps.add(flags);
+ /*APPJET*/regexpLinenos.add(lineno);
+ return regexps.size() / 2 - 1;
+ }
+
+ public int getIndexForNameNode(Node nameNode) {
+ if (variableNames == null) throw Kit.codeBug();
+ Node.Scope node = nameNode.getScope();
+ Symbol symbol = node == null ? null
+ : node.getSymbol(nameNode.getString());
+ if (symbol == null)
+ return -1;
+ return symbol.index;
+ }
+
+ public final String getParamOrVarName(int index) {
+ if (variableNames == null) throw Kit.codeBug();
+ return variableNames[index];
+ }
+
+ public final int getParamCount() {
+ return paramCount;
+ }
+
+ public final int getParamAndVarCount() {
+ if (variableNames == null) throw Kit.codeBug();
+ return symbols.size();
+ }
+
+ public final String[] getParamAndVarNames() {
+ if (variableNames == null) throw Kit.codeBug();
+ return variableNames;
+ }
+
+ public final boolean[] getParamAndVarConst() {
+ if (variableNames == null) throw Kit.codeBug();
+ return isConsts;
+ }
+
+ void addSymbol(Symbol symbol) {
+ if (variableNames != null) throw Kit.codeBug();
+ if (symbol.declType == Token.LP) {
+ paramCount++;
+ }
+ symbols.add(symbol);
+ }
+
+ /**
+ * Assign every symbol a unique integer index. Generate arrays of variable
+ * names and constness that can be indexed by those indices.
+ *
+ * @param flattenAllTables if true, flatten all symbol tables, included
+ * nested block scope symbol tables. If false, just flatten the script's
+ * or function's symbol table.
+ */
+ void flattenSymbolTable(boolean flattenAllTables) {
+ if (!flattenAllTables) {
+ ArrayList newSymbols = new ArrayList();
+ if (this.symbolTable != null) {
+ // Just replace "symbols" with the symbols in this object's
+ // symbol table. Can't just work from symbolTable map since
+ // we need to retain duplicate parameters.
+ for (int i=0; i < symbols.size(); i++) {
+ Symbol symbol = (Symbol) symbols.get(i);
+ if (symbol.containingTable == this) {
+ newSymbols.add(symbol);
+ }
+ }
+ }
+ symbols = newSymbols;
+ }
+ variableNames = new String[symbols.size()];
+ isConsts = new boolean[symbols.size()];
+ for (int i=0; i < symbols.size(); i++) {
+ Symbol symbol = (Symbol) symbols.get(i);
+ variableNames[i] = symbol.name;
+ isConsts[i] = symbol.declType == Token.CONST;
+ symbol.index = i;
+ }
+ }
+
+ public final Object getCompilerData()
+ {
+ return compilerData;
+ }
+
+ public final void setCompilerData(Object data)
+ {
+ if (data == null) throw new IllegalArgumentException();
+ // Can only call once
+ if (compilerData != null) throw new IllegalStateException();
+ compilerData = data;
+ }
+
+ public String getNextTempName()
+ {
+ return "$" + tempNumber++;
+ }
+
+ private int encodedSourceStart;
+ private int encodedSourceEnd;
+ private String sourceName;
+ private int endLineno = -1;
+
+ private ObjArray functions;
+ private ObjArray regexps;
+ /*APPJET*/ private ObjArray regexpLinenos;
+
+ private ArrayList symbols;
+ private int paramCount = 0;
+ private String[] variableNames;
+ private boolean[] isConsts;
+
+ private Object compilerData;
+ private int tempNumber = 0;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptRuntime.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptRuntime.java
new file mode 100644
index 0000000..f879581
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptRuntime.java
@@ -0,0 +1,3830 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Patrick Beard
+ * Norris Boyd
+ * Igor Bukanov
+ * Ethan Hugg
+ * Bob Jervis
+ * Roger Lawrence
+ * Terry Lucas
+ * Frank Mitchell
+ * Milen Nankov
+ * Hannes Wallnoefer
+ * Andrew Wason
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.io.Serializable;
+import java.lang.reflect.*;
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+import org.mozilla.javascript.xml.XMLObject;
+import org.mozilla.javascript.xml.XMLLib;
+
+/**
+ * This is the class that implements the runtime.
+ *
+ * @author Norris Boyd
+ */
+
+public class ScriptRuntime {
+
+ /**
+ * No instances should be created.
+ */
+ protected ScriptRuntime() {
+ }
+
+ private static class NoSuchMethodShim implements Callable {
+ String methodName;
+ Callable noSuchMethodMethod;
+
+ NoSuchMethodShim(Callable noSuchMethodMethod, String methodName)
+ {
+ this.noSuchMethodMethod = noSuchMethodMethod;
+ this.methodName = methodName;
+ }
+ /**
+ * Perform the call.
+ *
+ * @param cx the current Context for this thread
+ * @param scope the scope to use to resolve properties.
+ * @param thisObj the JavaScript <code>this</code> object
+ * @param args the array of arguments
+ * @return the result of the call
+ */
+ public Object call(Context cx, Scriptable scope, Scriptable thisObj,
+ Object[] args)
+ {
+ Object[] nestedArgs = new Object[2];
+
+ nestedArgs[0] = methodName;
+ nestedArgs[1] = newArrayLiteral(args, null, cx, scope);
+ return noSuchMethodMethod.call(cx, scope, thisObj, nestedArgs);
+ }
+
+ }
+ /*
+ * There's such a huge space (and some time) waste for the Foo.class
+ * syntax: the compiler sticks in a test of a static field in the
+ * enclosing class for null and the code for creating the class value.
+ * It has to do this since the reference has to get pushed off until
+ * execution time (i.e. can't force an early load), but for the
+ * 'standard' classes - especially those in java.lang, we can trust
+ * that they won't cause problems by being loaded early.
+ */
+
+ public final static Class
+ BooleanClass = Kit.classOrNull("java.lang.Boolean"),
+ ByteClass = Kit.classOrNull("java.lang.Byte"),
+ CharacterClass = Kit.classOrNull("java.lang.Character"),
+ ClassClass = Kit.classOrNull("java.lang.Class"),
+ DoubleClass = Kit.classOrNull("java.lang.Double"),
+ FloatClass = Kit.classOrNull("java.lang.Float"),
+ IntegerClass = Kit.classOrNull("java.lang.Integer"),
+ LongClass = Kit.classOrNull("java.lang.Long"),
+ NumberClass = Kit.classOrNull("java.lang.Number"),
+ ObjectClass = Kit.classOrNull("java.lang.Object"),
+ ShortClass = Kit.classOrNull("java.lang.Short"),
+ StringClass = Kit.classOrNull("java.lang.String"),
+ DateClass = Kit.classOrNull("java.util.Date");
+
+ public final static Class
+ ContextClass
+ = Kit.classOrNull("org.mozilla.javascript.Context"),
+ ContextFactoryClass
+ = Kit.classOrNull("org.mozilla.javascript.ContextFactory"),
+ FunctionClass
+ = Kit.classOrNull("org.mozilla.javascript.Function"),
+ ScriptableClass
+ = Kit.classOrNull("org.mozilla.javascript.Scriptable"),
+ ScriptableObjectClass
+ = Kit.classOrNull("org.mozilla.javascript.ScriptableObject");
+
+ private static final String[] lazilyNames = {
+ "RegExp", "org.mozilla.javascript.regexp.NativeRegExp",
+ "Packages", "org.mozilla.javascript.NativeJavaTopPackage",
+ "java", "org.mozilla.javascript.NativeJavaTopPackage",
+ "javax", "org.mozilla.javascript.NativeJavaTopPackage",
+ "org", "org.mozilla.javascript.NativeJavaTopPackage",
+ "com", "org.mozilla.javascript.NativeJavaTopPackage",
+ "edu", "org.mozilla.javascript.NativeJavaTopPackage",
+ "net", "org.mozilla.javascript.NativeJavaTopPackage",
+ "getClass", "org.mozilla.javascript.NativeJavaTopPackage",
+ "JavaAdapter", "org.mozilla.javascript.JavaAdapter",
+ "JavaImporter", "org.mozilla.javascript.ImporterTopLevel",
+ "Continuation", "org.mozilla.javascript.continuations.Continuation",
+ // TODO Grotesque hack using literal string (xml) just to minimize
+ // changes for now
+ "XML", "(xml)",
+ "XMLList", "(xml)",
+ "Namespace", "(xml)",
+ "QName", "(xml)",
+ };
+
+ private static final Object LIBRARY_SCOPE_KEY = new Object();
+
+ public static boolean isRhinoRuntimeType(Class cl)
+ {
+ if (cl.isPrimitive()) {
+ return (cl != Character.TYPE);
+ } else {
+ return (cl == StringClass || cl == BooleanClass
+ || NumberClass.isAssignableFrom(cl)
+ || ScriptableClass.isAssignableFrom(cl));
+ }
+ }
+
+ public static ScriptableObject initStandardObjects(Context cx,
+ ScriptableObject scope,
+ boolean sealed)
+ {
+ if (scope == null) {
+ scope = new NativeObject();
+ }
+ scope.associateValue(LIBRARY_SCOPE_KEY, scope);
+ (new ClassCache()).associate(scope);
+
+ BaseFunction.init(scope, sealed);
+ NativeObject.init(scope, sealed);
+
+ Scriptable objectProto = ScriptableObject.getObjectPrototype(scope);
+
+ // Function.prototype.__proto__ should be Object.prototype
+ Scriptable functionProto = ScriptableObject.getFunctionPrototype(scope);
+ functionProto.setPrototype(objectProto);
+
+ // Set the prototype of the object passed in if need be
+ if (scope.getPrototype() == null)
+ scope.setPrototype(objectProto);
+
+ // must precede NativeGlobal since it's needed therein
+ NativeError.init(scope, sealed);
+ NativeGlobal.init(cx, scope, sealed);
+
+ NativeArray.init(scope, sealed);
+ if (cx.getOptimizationLevel() > 0) {
+ // When optimizing, attempt to fulfill all requests for new Array(N)
+ // with a higher threshold before switching to a sparse
+ // representation
+ NativeArray.setMaximumInitialCapacity(200000);
+ }
+ NativeString.init(scope, sealed);
+ NativeBoolean.init(scope, sealed);
+ NativeNumber.init(scope, sealed);
+ NativeDate.init(scope, sealed);
+ NativeMath.init(scope, sealed);
+
+ NativeWith.init(scope, sealed);
+ NativeCall.init(scope, sealed);
+ NativeScript.init(scope, sealed);
+
+ NativeIterator.init(scope, sealed); // Also initializes NativeGenerator
+
+ boolean withXml = cx.hasFeature(Context.FEATURE_E4X) &&
+ cx.getE4xImplementationFactory() != null;
+
+ for (int i = 0; i != lazilyNames.length; i += 2) {
+ String topProperty = lazilyNames[i];
+ String className = lazilyNames[i + 1];
+ if (!withXml && className.equals("(xml)")) {
+ continue;
+ } else if (withXml && className.equals("(xml)")) {
+ className = cx.getE4xImplementationFactory().
+ getImplementationClassName();
+ }
+ new LazilyLoadedCtor(scope, topProperty, className, sealed);
+ }
+
+ return scope;
+ }
+
+ public static ScriptableObject getLibraryScopeOrNull(Scriptable scope)
+ {
+ ScriptableObject libScope;
+ libScope = (ScriptableObject)ScriptableObject.
+ getTopScopeValue(scope, LIBRARY_SCOPE_KEY);
+ return libScope;
+ }
+
+ // It is public so NativeRegExp can access it.
+ public static boolean isJSLineTerminator(int c)
+ {
+ // Optimization for faster check for eol character:
+ // they do not have 0xDFD0 bits set
+ if ((c & 0xDFD0) != 0) {
+ return false;
+ }
+ return c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029;
+ }
+
+ public static Boolean wrapBoolean(boolean b)
+ {
+ return b ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ public static Integer wrapInt(int i)
+ {
+ return new Integer(i);
+ }
+
+ public static Number wrapNumber(double x)
+ {
+ if (x != x) {
+ return ScriptRuntime.NaNobj;
+ }
+ return new Double(x);
+ }
+
+ /**
+ * Convert the value to a boolean.
+ *
+ * See ECMA 9.2.
+ */
+ public static boolean toBoolean(Object val)
+ {
+ for (;;) {
+ if (val instanceof Boolean)
+ return ((Boolean) val).booleanValue();
+ if (val == null || val == Undefined.instance)
+ return false;
+ if (val instanceof String)
+ return ((String) val).length() != 0;
+ if (val instanceof Number) {
+ double d = ((Number) val).doubleValue();
+ return (d == d && d != 0.0);
+ }
+ if (val instanceof Scriptable) {
+ if (val instanceof ScriptableObject &&
+ ((ScriptableObject) val).avoidObjectDetection())
+ {
+ return false;
+ }
+ if (Context.getContext().isVersionECMA1()) {
+ // pure ECMA
+ return true;
+ }
+ // ECMA extension
+ val = ((Scriptable) val).getDefaultValue(BooleanClass);
+ if (val instanceof Scriptable)
+ throw errorWithClassName("msg.primitive.expected", val);
+ continue;
+ }
+ warnAboutNonJSObject(val);
+ return true;
+ }
+ }
+
+ /**
+ * Convert the value to a number.
+ *
+ * See ECMA 9.3.
+ */
+ public static double toNumber(Object val)
+ {
+ for (;;) {
+ if (val instanceof Number)
+ return ((Number) val).doubleValue();
+ if (val == null)
+ return +0.0;
+ if (val == Undefined.instance)
+ return NaN;
+ if (val instanceof String)
+ return toNumber((String) val);
+ if (val instanceof Boolean)
+ return ((Boolean) val).booleanValue() ? 1 : +0.0;
+ if (val instanceof Scriptable) {
+ val = ((Scriptable) val).getDefaultValue(NumberClass);
+ if (val instanceof Scriptable)
+ throw errorWithClassName("msg.primitive.expected", val);
+ continue;
+ }
+ warnAboutNonJSObject(val);
+ return NaN;
+ }
+ }
+
+ public static double toNumber(Object[] args, int index) {
+ return (index < args.length) ? toNumber(args[index]) : NaN;
+ }
+
+ // Can not use Double.NaN defined as 0.0d / 0.0 as under the Microsoft VM,
+ // versions 2.01 and 3.0P1, that causes some uses (returns at least) of
+ // Double.NaN to be converted to 1.0.
+ // So we use ScriptRuntime.NaN instead of Double.NaN.
+ public static final double
+ NaN = Double.longBitsToDouble(0x7ff8000000000000L);
+
+ // A similar problem exists for negative zero.
+ public static final double
+ negativeZero = Double.longBitsToDouble(0x8000000000000000L);
+
+ public static final Double NaNobj = new Double(NaN);
+
+ /*
+ * Helper function for toNumber, parseInt, and TokenStream.getToken.
+ */
+ static double stringToNumber(String s, int start, int radix) {
+ char digitMax = '9';
+ char lowerCaseBound = 'a';
+ char upperCaseBound = 'A';
+ int len = s.length();
+ if (radix < 10) {
+ digitMax = (char) ('0' + radix - 1);
+ }
+ if (radix > 10) {
+ lowerCaseBound = (char) ('a' + radix - 10);
+ upperCaseBound = (char) ('A' + radix - 10);
+ }
+ int end;
+ double sum = 0.0;
+ for (end=start; end < len; end++) {
+ char c = s.charAt(end);
+ int newDigit;
+ if ('0' <= c && c <= digitMax)
+ newDigit = c - '0';
+ else if ('a' <= c && c < lowerCaseBound)
+ newDigit = c - 'a' + 10;
+ else if ('A' <= c && c < upperCaseBound)
+ newDigit = c - 'A' + 10;
+ else
+ break;
+ sum = sum*radix + newDigit;
+ }
+ if (start == end) {
+ return NaN;
+ }
+ if (sum >= 9007199254740992.0) {
+ if (radix == 10) {
+ /* If we're accumulating a decimal number and the number
+ * is >= 2^53, then the result from the repeated multiply-add
+ * above may be inaccurate. Call Java to get the correct
+ * answer.
+ */
+ try {
+ return Double.valueOf(s.substring(start, end)).doubleValue();
+ } catch (NumberFormatException nfe) {
+ return NaN;
+ }
+ } else if (radix == 2 || radix == 4 || radix == 8 ||
+ radix == 16 || radix == 32)
+ {
+ /* The number may also be inaccurate for one of these bases.
+ * This happens if the addition in value*radix + digit causes
+ * a round-down to an even least significant mantissa bit
+ * when the first dropped bit is a one. If any of the
+ * following digits in the number (which haven't been added
+ * in yet) are nonzero then the correct action would have
+ * been to round up instead of down. An example of this
+ * occurs when reading the number 0x1000000000000081, which
+ * rounds to 0x1000000000000000 instead of 0x1000000000000100.
+ */
+ int bitShiftInChar = 1;
+ int digit = 0;
+
+ final int SKIP_LEADING_ZEROS = 0;
+ final int FIRST_EXACT_53_BITS = 1;
+ final int AFTER_BIT_53 = 2;
+ final int ZEROS_AFTER_54 = 3;
+ final int MIXED_AFTER_54 = 4;
+
+ int state = SKIP_LEADING_ZEROS;
+ int exactBitsLimit = 53;
+ double factor = 0.0;
+ boolean bit53 = false;
+ // bit54 is the 54th bit (the first dropped from the mantissa)
+ boolean bit54 = false;
+
+ for (;;) {
+ if (bitShiftInChar == 1) {
+ if (start == end)
+ break;
+ digit = s.charAt(start++);
+ if ('0' <= digit && digit <= '9')
+ digit -= '0';
+ else if ('a' <= digit && digit <= 'z')
+ digit -= 'a' - 10;
+ else
+ digit -= 'A' - 10;
+ bitShiftInChar = radix;
+ }
+ bitShiftInChar >>= 1;
+ boolean bit = (digit & bitShiftInChar) != 0;
+
+ switch (state) {
+ case SKIP_LEADING_ZEROS:
+ if (bit) {
+ --exactBitsLimit;
+ sum = 1.0;
+ state = FIRST_EXACT_53_BITS;
+ }
+ break;
+ case FIRST_EXACT_53_BITS:
+ sum *= 2.0;
+ if (bit)
+ sum += 1.0;
+ --exactBitsLimit;
+ if (exactBitsLimit == 0) {
+ bit53 = bit;
+ state = AFTER_BIT_53;
+ }
+ break;
+ case AFTER_BIT_53:
+ bit54 = bit;
+ factor = 2.0;
+ state = ZEROS_AFTER_54;
+ break;
+ case ZEROS_AFTER_54:
+ if (bit) {
+ state = MIXED_AFTER_54;
+ }
+ // fallthrough
+ case MIXED_AFTER_54:
+ factor *= 2;
+ break;
+ }
+ }
+ switch (state) {
+ case SKIP_LEADING_ZEROS:
+ sum = 0.0;
+ break;
+ case FIRST_EXACT_53_BITS:
+ case AFTER_BIT_53:
+ // do nothing
+ break;
+ case ZEROS_AFTER_54:
+ // x1.1 -> x1 + 1 (round up)
+ // x0.1 -> x0 (round down)
+ if (bit54 & bit53)
+ sum += 1.0;
+ sum *= factor;
+ break;
+ case MIXED_AFTER_54:
+ // x.100...1.. -> x + 1 (round up)
+ // x.0anything -> x (round down)
+ if (bit54)
+ sum += 1.0;
+ sum *= factor;
+ break;
+ }
+ }
+ /* We don't worry about inaccurate numbers for any other base. */
+ }
+ return sum;
+ }
+
+
+ /**
+ * ToNumber applied to the String type
+ *
+ * See ECMA 9.3.1
+ */
+ public static double toNumber(String s) {
+ int len = s.length();
+ int start = 0;
+ char startChar;
+ for (;;) {
+ if (start == len) {
+ // Empty or contains only whitespace
+ return +0.0;
+ }
+ startChar = s.charAt(start);
+ if (!Character.isWhitespace(startChar))
+ break;
+ start++;
+ }
+
+ if (startChar == '0') {
+ if (start + 2 < len) {
+ int c1 = s.charAt(start + 1);
+ if (c1 == 'x' || c1 == 'X') {
+ // A hexadecimal number
+ return stringToNumber(s, start + 2, 16);
+ }
+ }
+ } else if (startChar == '+' || startChar == '-') {
+ if (start + 3 < len && s.charAt(start + 1) == '0') {
+ int c2 = s.charAt(start + 2);
+ if (c2 == 'x' || c2 == 'X') {
+ // A hexadecimal number with sign
+ double val = stringToNumber(s, start + 3, 16);
+ return startChar == '-' ? -val : val;
+ }
+ }
+ }
+
+ int end = len - 1;
+ char endChar;
+ while (Character.isWhitespace(endChar = s.charAt(end)))
+ end--;
+ if (endChar == 'y') {
+ // check for "Infinity"
+ if (startChar == '+' || startChar == '-')
+ start++;
+ if (start + 7 == end && s.regionMatches(start, "Infinity", 0, 8))
+ return startChar == '-'
+ ? Double.NEGATIVE_INFINITY
+ : Double.POSITIVE_INFINITY;
+ return NaN;
+ }
+ // A non-hexadecimal, non-infinity number:
+ // just try a normal floating point conversion
+ String sub = s.substring(start, end+1);
+ if (MSJVM_BUG_WORKAROUNDS) {
+ // The MS JVM will accept non-conformant strings
+ // rather than throwing a NumberFormatException
+ // as it should.
+ for (int i=sub.length()-1; i >= 0; i--) {
+ char c = sub.charAt(i);
+ if (('0' <= c && c <= '9') || c == '.' ||
+ c == 'e' || c == 'E' ||
+ c == '+' || c == '-')
+ continue;
+ return NaN;
+ }
+ }
+ try {
+ return Double.valueOf(sub).doubleValue();
+ } catch (NumberFormatException ex) {
+ return NaN;
+ }
+ }
+
+ /**
+ * Helper function for builtin objects that use the varargs form.
+ * ECMA function formal arguments are undefined if not supplied;
+ * this function pads the argument array out to the expected
+ * length, if necessary.
+ */
+ public static Object[] padArguments(Object[] args, int count) {
+ if (count < args.length)
+ return args;
+
+ int i;
+ Object[] result = new Object[count];
+ for (i = 0; i < args.length; i++) {
+ result[i] = args[i];
+ }
+
+ for (; i < count; i++) {
+ result[i] = Undefined.instance;
+ }
+
+ return result;
+ }
+
+ /* Work around Microsoft Java VM bugs. */
+ private final static boolean MSJVM_BUG_WORKAROUNDS = true;
+
+ public static String escapeString(String s)
+ {
+ return escapeString(s, '"');
+ }
+
+ /**
+ * For escaping strings printed by object and array literals; not quite
+ * the same as 'escape.'
+ */
+ public static String escapeString(String s, char escapeQuote)
+ {
+ if (!(escapeQuote == '"' || escapeQuote == '\'')) Kit.codeBug();
+ StringBuffer sb = null;
+
+ for(int i = 0, L = s.length(); i != L; ++i) {
+ int c = s.charAt(i);
+
+ if (' ' <= c && c <= '~' && c != escapeQuote && c != '\\') {
+ // an ordinary print character (like C isprint()) and not "
+ // or \ .
+ if (sb != null) {
+ sb.append((char)c);
+ }
+ continue;
+ }
+ if (sb == null) {
+ sb = new StringBuffer(L + 3);
+ sb.append(s);
+ sb.setLength(i);
+ }
+
+ int escape = -1;
+ switch (c) {
+ case '\b': escape = 'b'; break;
+ case '\f': escape = 'f'; break;
+ case '\n': escape = 'n'; break;
+ case '\r': escape = 'r'; break;
+ case '\t': escape = 't'; break;
+ case 0xb: escape = 'v'; break; // Java lacks \v.
+ case ' ': escape = ' '; break;
+ case '\\': escape = '\\'; break;
+ }
+ if (escape >= 0) {
+ // an \escaped sort of character
+ sb.append('\\');
+ sb.append((char)escape);
+ } else if (c == escapeQuote) {
+ sb.append('\\');
+ sb.append(escapeQuote);
+ } else {
+ int hexSize;
+ if (c < 256) {
+ // 2-digit hex
+ sb.append("\\x");
+ hexSize = 2;
+ } else {
+ // Unicode.
+ 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();
+ }
+
+ static boolean isValidIdentifierName(String s)
+ {
+ int L = s.length();
+ if (L == 0)
+ return false;
+ if (!Character.isJavaIdentifierStart(s.charAt(0)))
+ return false;
+ for (int i = 1; i != L; ++i) {
+ if (!Character.isJavaIdentifierPart(s.charAt(i)))
+ return false;
+ }
+ return !TokenStream.isKeyword(s);
+ }
+
+ /**
+ * Convert the value to a string.
+ *
+ * See ECMA 9.8.
+ */
+ public static String toString(Object val) {
+ for (;;) {
+ if (val == null) {
+ return "null";
+ }
+ if (val == Undefined.instance) {
+ return "undefined";
+ }
+ if (val instanceof String) {
+ return (String)val;
+ }
+ if (val instanceof Number) {
+ // XXX should we just teach NativeNumber.stringValue()
+ // about Numbers?
+ return numberToString(((Number)val).doubleValue(), 10);
+ }
+ if (val instanceof Scriptable) {
+ val = ((Scriptable) val).getDefaultValue(StringClass);
+ if (val instanceof Scriptable) {
+ throw errorWithClassName("msg.primitive.expected", val);
+ }
+ continue;
+ }
+ return val.toString();
+ }
+ }
+
+ static String defaultObjectToString(Scriptable obj)
+ {
+ return "[object " + obj.getClassName() + ']';
+ }
+
+ public static String toString(Object[] args, int index)
+ {
+ return (index < args.length) ? toString(args[index]) : "undefined";
+ }
+
+ /**
+ * Optimized version of toString(Object) for numbers.
+ */
+ public static String toString(double val) {
+ return numberToString(val, 10);
+ }
+
+ public static String numberToString(double d, int base) {
+ if (d != d)
+ return "NaN";
+ if (d == Double.POSITIVE_INFINITY)
+ return "Infinity";
+ if (d == Double.NEGATIVE_INFINITY)
+ return "-Infinity";
+ if (d == 0.0)
+ return "0";
+
+ if ((base < 2) || (base > 36)) {
+ throw Context.reportRuntimeError1(
+ "msg.bad.radix", Integer.toString(base));
+ }
+
+ if (base != 10) {
+ return DToA.JS_dtobasestr(base, d);
+ } else {
+ StringBuffer result = new StringBuffer();
+ DToA.JS_dtostr(result, DToA.DTOSTR_STANDARD, 0, d);
+ return result.toString();
+ }
+
+ }
+
+ static String uneval(Context cx, Scriptable scope, Object value)
+ {
+ if (value == null) {
+ return "null";
+ }
+ if (value == Undefined.instance) {
+ return "undefined";
+ }
+ if (value instanceof String) {
+ String escaped = escapeString((String)value);
+ StringBuffer sb = new StringBuffer(escaped.length() + 2);
+ sb.append('\"');
+ sb.append(escaped);
+ sb.append('\"');
+ return sb.toString();
+ }
+ if (value instanceof Number) {
+ double d = ((Number)value).doubleValue();
+ if (d == 0 && 1 / d < 0) {
+ return "-0";
+ }
+ return toString(d);
+ }
+ if (value instanceof Boolean) {
+ return toString(value);
+ }
+ if (value instanceof Scriptable) {
+ Scriptable obj = (Scriptable)value;
+ // Wrapped Java objects won't have "toSource" and will report
+ // errors for get()s of nonexistent name, so use has() first
+ if (ScriptableObject.hasProperty(obj, "toSource")) {
+ Object v = ScriptableObject.getProperty(obj, "toSource");
+ if (v instanceof Function) {
+ Function f = (Function)v;
+ return toString(f.call(cx, scope, obj, emptyArgs));
+ }
+ }
+ return toString(value);
+ }
+ warnAboutNonJSObject(value);
+ return value.toString();
+ }
+
+ static String defaultObjectToSource(Context cx, Scriptable scope,
+ Scriptable thisObj, Object[] args)
+ {
+ boolean toplevel, iterating;
+ if (cx.iterating == null) {
+ toplevel = true;
+ iterating = false;
+ cx.iterating = new ObjToIntMap(31);
+ } else {
+ toplevel = false;
+ iterating = cx.iterating.has(thisObj);
+ }
+
+ StringBuffer result = new StringBuffer(128);
+ if (toplevel) {
+ result.append("(");
+ }
+ result.append('{');
+
+ // Make sure cx.iterating is set to null when done
+ // so we don't leak memory
+ try {
+ if (!iterating) {
+ cx.iterating.intern(thisObj); // stop recursion.
+ Object[] ids = thisObj.getIds();
+ for (int i=0; i < ids.length; i++) {
+ Object id = ids[i];
+ Object value;
+ if (id instanceof Integer) {
+ int intId = ((Integer)id).intValue();
+ value = thisObj.get(intId, thisObj);
+ if (value == Scriptable.NOT_FOUND)
+ continue; // a property has been removed
+ if (i > 0)
+ result.append(", ");
+ result.append(intId);
+ } else {
+ String strId = (String)id;
+ value = thisObj.get(strId, thisObj);
+ if (value == Scriptable.NOT_FOUND)
+ continue; // a property has been removed
+ if (i > 0)
+ result.append(", ");
+ if (ScriptRuntime.isValidIdentifierName(strId)) {
+ result.append(strId);
+ } else {
+ result.append('\'');
+ result.append(
+ ScriptRuntime.escapeString(strId, '\''));
+ result.append('\'');
+ }
+ }
+ result.append(':');
+ result.append(ScriptRuntime.uneval(cx, scope, value));
+ }
+ }
+ } finally {
+ if (toplevel) {
+ cx.iterating = null;
+ }
+ }
+
+ result.append('}');
+ if (toplevel) {
+ result.append(')');
+ }
+ return result.toString();
+ }
+
+ public static Scriptable toObject(Scriptable scope, Object val)
+ {
+ if (val instanceof Scriptable) {
+ return (Scriptable)val;
+ }
+ return toObject(Context.getContext(), scope, val);
+ }
+
+ public static Scriptable toObjectOrNull(Context cx, Object obj)
+ {
+ if (obj instanceof Scriptable) {
+ return (Scriptable)obj;
+ } else if (obj != null && obj != Undefined.instance) {
+ return toObject(cx, getTopCallScope(cx), obj);
+ }
+ return null;
+ }
+
+ /**
+ * @deprecated Use {@link #toObject(Scriptable, Object)} instead.
+ */
+ public static Scriptable toObject(Scriptable scope, Object val,
+ Class staticClass)
+ {
+ if (val instanceof Scriptable) {
+ return (Scriptable)val;
+ }
+ return toObject(Context.getContext(), scope, val);
+ }
+
+ /**
+ * Convert the value to an object.
+ *
+ * See ECMA 9.9.
+ */
+ public static Scriptable toObject(Context cx, Scriptable scope, Object val)
+ {
+ if (val instanceof Scriptable) {
+ return (Scriptable) val;
+ }
+ if (val == null) {
+ throw typeError0("msg.null.to.object");
+ }
+ if (val == Undefined.instance) {
+ throw typeError0("msg.undef.to.object");
+ }
+ String className = val instanceof String ? "String" :
+ val instanceof Number ? "Number" :
+ val instanceof Boolean ? "Boolean" :
+ null;
+ if (className != null) {
+ Object[] args = { val };
+ scope = ScriptableObject.getTopLevelScope(scope);
+ return newObject(cx, scope, className, args);
+ }
+
+ // Extension: Wrap as a LiveConnect object.
+ Object wrapped = cx.getWrapFactory().wrap(cx, scope, val, null);
+ if (wrapped instanceof Scriptable)
+ return (Scriptable) wrapped;
+ throw errorWithClassName("msg.invalid.type", val);
+ }
+
+ /**
+ * @deprecated Use {@link #toObject(Context, Scriptable, Object)} instead.
+ */
+ public static Scriptable toObject(Context cx, Scriptable scope, Object val,
+ Class staticClass)
+ {
+ return toObject(cx, scope, val);
+ }
+
+ /**
+ * @deprecated The method is only present for compatibility.
+ */
+ public static Object call(Context cx, Object fun, Object thisArg,
+ Object[] args, Scriptable scope)
+ {
+ if (!(fun instanceof Function)) {
+ throw notFunctionError(toString(fun));
+ }
+ Function function = (Function)fun;
+ Scriptable thisObj = toObjectOrNull(cx, thisArg);
+ if (thisObj == null) {
+ throw undefCallError(thisObj, "function");
+ }
+ return function.call(cx, scope, thisObj, args);
+ }
+
+ public static Scriptable newObject(Context cx, Scriptable scope,
+ String constructorName, Object[] args)
+ {
+ scope = ScriptableObject.getVeryTopLevelScope(scope); // APPJET
+ Function ctor = getExistingCtor(cx, scope, constructorName);
+ if (args == null) { args = ScriptRuntime.emptyArgs; }
+ return ctor.construct(cx, scope, args);
+ }
+
+ /**
+ *
+ * See ECMA 9.4.
+ */
+ public static double toInteger(Object val) {
+ return toInteger(toNumber(val));
+ }
+
+ // convenience method
+ public static double toInteger(double d) {
+ // if it's NaN
+ if (d != d)
+ return +0.0;
+
+ if (d == 0.0 ||
+ d == Double.POSITIVE_INFINITY ||
+ d == Double.NEGATIVE_INFINITY)
+ return d;
+
+ if (d > 0.0)
+ return Math.floor(d);
+ else
+ return Math.ceil(d);
+ }
+
+ public static double toInteger(Object[] args, int index) {
+ return (index < args.length) ? toInteger(args[index]) : +0.0;
+ }
+
+ /**
+ *
+ * See ECMA 9.5.
+ */
+ public static int toInt32(Object val)
+ {
+ // short circuit for common integer values
+ if (val instanceof Integer)
+ return ((Integer)val).intValue();
+
+ return toInt32(toNumber(val));
+ }
+
+ public static int toInt32(Object[] args, int index) {
+ return (index < args.length) ? toInt32(args[index]) : 0;
+ }
+
+ public static int toInt32(double d) {
+ int id = (int)d;
+ if (id == d) {
+ // This covers -0.0 as well
+ return id;
+ }
+
+ if (d != d
+ || d == Double.POSITIVE_INFINITY
+ || d == Double.NEGATIVE_INFINITY)
+ {
+ return 0;
+ }
+
+ d = (d >= 0) ? Math.floor(d) : Math.ceil(d);
+
+ double two32 = 4294967296.0;
+ d = Math.IEEEremainder(d, two32);
+ // (double)(long)d == d should hold here
+
+ long l = (long)d;
+ // returning (int)d does not work as d can be outside int range
+ // but the result must always be 32 lower bits of l
+ return (int)l;
+ }
+
+ /**
+ * See ECMA 9.6.
+ * @return long value representing 32 bits unsigned integer
+ */
+ public static long toUint32(double d) {
+ long l = (long)d;
+ if (l == d) {
+ // This covers -0.0 as well
+ return l & 0xffffffffL;
+ }
+
+ if (d != d
+ || d == Double.POSITIVE_INFINITY
+ || d == Double.NEGATIVE_INFINITY)
+ {
+ return 0;
+ }
+
+ d = (d >= 0) ? Math.floor(d) : Math.ceil(d);
+
+ // 0x100000000 gives me a numeric overflow...
+ double two32 = 4294967296.0;
+ l = (long)Math.IEEEremainder(d, two32);
+
+ return l & 0xffffffffL;
+ }
+
+ public static long toUint32(Object val) {
+ return toUint32(toNumber(val));
+ }
+
+ /**
+ *
+ * See ECMA 9.7.
+ */
+ public static char toUint16(Object val) {
+ double d = toNumber(val);
+
+ int i = (int)d;
+ if (i == d) {
+ return (char)i;
+ }
+
+ if (d != d
+ || d == Double.POSITIVE_INFINITY
+ || d == Double.NEGATIVE_INFINITY)
+ {
+ return 0;
+ }
+
+ d = (d >= 0) ? Math.floor(d) : Math.ceil(d);
+
+ int int16 = 0x10000;
+ i = (int)Math.IEEEremainder(d, int16);
+
+ return (char)i;
+ }
+
+ // XXX: this is until setDefaultNamespace will learn how to store NS
+ // properly and separates namespace form Scriptable.get etc.
+ private static final String DEFAULT_NS_TAG = "__default_namespace__";
+
+ public static Object setDefaultNamespace(Object namespace, Context cx)
+ {
+ Scriptable scope = cx.currentActivationCall;
+ if (scope == null) {
+ scope = getTopCallScope(cx);
+ }
+
+ XMLLib xmlLib = currentXMLLib(cx);
+ Object ns = xmlLib.toDefaultXmlNamespace(cx, namespace);
+
+ // XXX : this should be in separated namesapce from Scriptable.get/put
+ if (!scope.has(DEFAULT_NS_TAG, scope)) {
+ // XXX: this is racy of cause
+ ScriptableObject.defineProperty(scope, DEFAULT_NS_TAG, ns,
+ ScriptableObject.PERMANENT
+ | ScriptableObject.DONTENUM);
+ } else {
+ scope.put(DEFAULT_NS_TAG, scope, ns);
+ }
+
+ return Undefined.instance;
+ }
+
+ public static Object searchDefaultNamespace(Context cx)
+ {
+ Scriptable scope = cx.currentActivationCall;
+ if (scope == null) {
+ scope = getTopCallScope(cx);
+ }
+ Object nsObject;
+ for (;;) {
+ Scriptable parent = scope.getParentScope();
+ if (parent == null) {
+ nsObject = ScriptableObject.getProperty(scope, DEFAULT_NS_TAG);
+ if (nsObject == Scriptable.NOT_FOUND) {
+ return null;
+ }
+ break;
+ }
+ nsObject = scope.get(DEFAULT_NS_TAG, scope);
+ if (nsObject != Scriptable.NOT_FOUND) {
+ break;
+ }
+ scope = parent;
+ }
+ return nsObject;
+ }
+
+ public static Object getTopLevelProp(Scriptable scope, String id) {
+ scope = ScriptableObject.getTopLevelScope(scope);
+ return ScriptableObject.getProperty(scope, id);
+ }
+
+ static Function getExistingCtor(Context cx, Scriptable scope,
+ String constructorName)
+ {
+ Object ctorVal = ScriptableObject.getProperty(scope, constructorName);
+ if (ctorVal instanceof Function) {
+ return (Function)ctorVal;
+ }
+ if (ctorVal == Scriptable.NOT_FOUND) {
+ throw Context.reportRuntimeError1(
+ "msg.ctor.not.found", constructorName);
+ } else {
+ throw Context.reportRuntimeError1(
+ "msg.not.ctor", constructorName);
+ }
+ }
+
+ /**
+ * Return -1L if str is not an index or the index value as lower 32
+ * bits of the result.
+ */
+ private static long indexFromString(String str)
+ {
+ // The length of the decimal string representation of
+ // Integer.MAX_VALUE, 2147483647
+ final int MAX_VALUE_LENGTH = 10;
+
+ int len = str.length();
+ if (len > 0) {
+ int i = 0;
+ boolean negate = false;
+ int c = str.charAt(0);
+ if (c == '-') {
+ if (len > 1) {
+ c = str.charAt(1);
+ i = 1;
+ negate = true;
+ }
+ }
+ c -= '0';
+ if (0 <= c && c <= 9
+ && len <= (negate ? MAX_VALUE_LENGTH + 1 : MAX_VALUE_LENGTH))
+ {
+ // Use negative numbers to accumulate index to handle
+ // Integer.MIN_VALUE that is greater by 1 in absolute value
+ // then Integer.MAX_VALUE
+ int index = -c;
+ int oldIndex = 0;
+ i++;
+ if (index != 0) {
+ // Note that 00, 01, 000 etc. are not indexes
+ while (i != len && 0 <= (c = str.charAt(i) - '0') && c <= 9)
+ {
+ oldIndex = index;
+ index = 10 * index - c;
+ i++;
+ }
+ }
+ // Make sure all characters were consumed and that it couldn't
+ // have overflowed.
+ if (i == len &&
+ (oldIndex > (Integer.MIN_VALUE / 10) ||
+ (oldIndex == (Integer.MIN_VALUE / 10) &&
+ c <= (negate ? -(Integer.MIN_VALUE % 10)
+ : (Integer.MAX_VALUE % 10)))))
+ {
+ return 0xFFFFFFFFL & (negate ? index : -index);
+ }
+ }
+ }
+ return -1L;
+ }
+
+ /**
+ * If str is a decimal presentation of Uint32 value, return it as long.
+ * Othewise return -1L;
+ */
+ public static long testUint32String(String str)
+ {
+ // The length of the decimal string representation of
+ // UINT32_MAX_VALUE, 4294967296
+ final int MAX_VALUE_LENGTH = 10;
+
+ int len = str.length();
+ if (1 <= len && len <= MAX_VALUE_LENGTH) {
+ int c = str.charAt(0);
+ c -= '0';
+ if (c == 0) {
+ // Note that 00,01 etc. are not valid Uint32 presentations
+ return (len == 1) ? 0L : -1L;
+ }
+ if (1 <= c && c <= 9) {
+ long v = c;
+ for (int i = 1; i != len; ++i) {
+ c = str.charAt(i) - '0';
+ if (!(0 <= c && c <= 9)) {
+ return -1;
+ }
+ v = 10 * v + c;
+ }
+ // Check for overflow
+ if ((v >>> 32) == 0) {
+ return v;
+ }
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * If s represents index, then return index value wrapped as Integer
+ * and othewise return s.
+ */
+ static Object getIndexObject(String s)
+ {
+ long indexTest = indexFromString(s);
+ if (indexTest >= 0) {
+ return new Integer((int)indexTest);
+ }
+ return s;
+ }
+
+ /**
+ * If d is exact int value, return its value wrapped as Integer
+ * and othewise return d converted to String.
+ */
+ static Object getIndexObject(double d)
+ {
+ int i = (int)d;
+ if (i == d) {
+ return new Integer(i);
+ }
+ return toString(d);
+ }
+
+ /**
+ * If toString(id) is a decimal presentation of int32 value, then id
+ * is index. In this case return null and make the index available
+ * as ScriptRuntime.lastIndexResult(cx). Otherwise return toString(id).
+ */
+ static String toStringIdOrIndex(Context cx, Object id)
+ {
+ if (id instanceof Number) {
+ double d = ((Number)id).doubleValue();
+ int index = (int)d;
+ if (index == d) {
+ storeIndexResult(cx, index);
+ return null;
+ }
+ return toString(id);
+ } else {
+ String s;
+ if (id instanceof String) {
+ s = (String)id;
+ } else {
+ s = toString(id);
+ }
+ long indexTest = indexFromString(s);
+ if (indexTest >= 0) {
+ storeIndexResult(cx, (int)indexTest);
+ return null;
+ }
+ return s;
+ }
+ }
+
+ /**
+ * Call obj.[[Get]](id)
+ */
+ public static Object getObjectElem(Object obj, Object elem, Context cx)
+ {
+ Scriptable sobj = toObjectOrNull(cx, obj);
+ if (sobj == null) {
+ throw undefReadError(obj, elem);
+ }
+ return getObjectElem(sobj, elem, cx);
+ }
+
+ public static Object getObjectElem(Scriptable obj, Object elem,
+ Context cx)
+ {
+ if (obj instanceof XMLObject) {
+ XMLObject xmlObject = (XMLObject)obj;
+ return xmlObject.ecmaGet(cx, elem);
+ }
+
+ Object result;
+
+ String s = toStringIdOrIndex(cx, elem);
+ if (s == null) {
+ int index = lastIndexResult(cx);
+ result = ScriptableObject.getProperty(obj, index);
+ } else {
+ result = ScriptableObject.getProperty(obj, s);
+ }
+
+ if (result == Scriptable.NOT_FOUND) {
+ result = Undefined.instance;
+ }
+
+ return result;
+ }
+
+ /**
+ * Version of getObjectElem when elem is a valid JS identifier name.
+ */
+ public static Object getObjectProp(Object obj, String property,
+ Context cx)
+ {
+ Scriptable sobj = toObjectOrNull(cx, obj);
+ if (sobj == null) {
+ throw undefReadError(obj, property);
+ }
+ return getObjectProp(sobj, property, cx);
+ }
+
+ public static Object getObjectProp(Scriptable obj, String property,
+ Context cx)
+ {
+ if (obj instanceof XMLObject) {
+ // TODO: Change XMLObject to just use Scriptable interface
+ // to avoid paying cost of instanceof check on *every property
+ // lookup* !
+ XMLObject xmlObject = (XMLObject)obj;
+ return xmlObject.ecmaGet(cx, property);
+ }
+
+ Object result = ScriptableObject.getProperty(obj, property);
+ if (result == Scriptable.NOT_FOUND) {
+ if (cx.hasFeature(Context.FEATURE_STRICT_MODE)) {
+ Context.reportWarning(ScriptRuntime.getMessage1(
+ "msg.ref.undefined.prop", property));
+ }
+ result = Undefined.instance;
+ }
+
+ return result;
+ }
+
+ public static Object getObjectPropNoWarn(Object obj, String property,
+ Context cx)
+ {
+ Scriptable sobj = toObjectOrNull(cx, obj);
+ if (sobj == null) {
+ throw undefReadError(obj, property);
+ }
+ if (obj instanceof XMLObject) {
+ // TODO: fix as mentioned in note in method above
+ getObjectProp(sobj, property, cx);
+ }
+ Object result = ScriptableObject.getProperty(sobj, property);
+ if (result == Scriptable.NOT_FOUND) {
+ return Undefined.instance;
+ }
+ return result;
+ }
+
+ /*
+ * A cheaper and less general version of the above for well-known argument
+ * types.
+ */
+ public static Object getObjectIndex(Object obj, double dblIndex,
+ Context cx)
+ {
+ Scriptable sobj = toObjectOrNull(cx, obj);
+ if (sobj == null) {
+ throw undefReadError(obj, toString(dblIndex));
+ }
+
+ int index = (int)dblIndex;
+ if (index == dblIndex) {
+ return getObjectIndex(sobj, index, cx);
+ } else {
+ String s = toString(dblIndex);
+ return getObjectProp(sobj, s, cx);
+ }
+ }
+
+ public static Object getObjectIndex(Scriptable obj, int index,
+ Context cx)
+ {
+ if (obj instanceof XMLObject) {
+ XMLObject xmlObject = (XMLObject)obj;
+ return xmlObject.ecmaGet(cx, new Integer(index));
+ }
+
+ Object result = ScriptableObject.getProperty(obj, index);
+ if (result == Scriptable.NOT_FOUND) {
+ result = Undefined.instance;
+ }
+
+ return result;
+ }
+
+ /*
+ * Call obj.[[Put]](id, value)
+ */
+ public static Object setObjectElem(Object obj, Object elem, Object value,
+ Context cx)
+ {
+ Scriptable sobj = toObjectOrNull(cx, obj);
+ if (sobj == null) {
+ throw undefWriteError(obj, elem, value);
+ }
+ return setObjectElem(sobj, elem, value, cx);
+ }
+
+ public static Object setObjectElem(Scriptable obj, Object elem,
+ Object value, Context cx)
+ {
+ if (obj instanceof XMLObject) {
+ XMLObject xmlObject = (XMLObject)obj;
+ xmlObject.ecmaPut(cx, elem, value);
+ return value;
+ }
+
+ String s = toStringIdOrIndex(cx, elem);
+ if (s == null) {
+ int index = lastIndexResult(cx);
+ ScriptableObject.putProperty(obj, index, value);
+ } else {
+ ScriptableObject.putProperty(obj, s, value);
+ }
+
+ return value;
+ }
+
+ /**
+ * Version of setObjectElem when elem is a valid JS identifier name.
+ */
+ public static Object setObjectProp(Object obj, String property,
+ Object value, Context cx)
+ {
+ Scriptable sobj = toObjectOrNull(cx, obj);
+ if (sobj == null) {
+ throw undefWriteError(obj, property, value);
+ }
+ return setObjectProp(sobj, property, value, cx);
+ }
+
+ public static Object setObjectProp(Scriptable obj, String property,
+ Object value, Context cx)
+ {
+ if (obj instanceof XMLObject) {
+ XMLObject xmlObject = (XMLObject)obj;
+ xmlObject.ecmaPut(cx, property, value);
+ } else {
+ ScriptableObject.putProperty(obj, property, value);
+ }
+ return value;
+ }
+
+ /*
+ * A cheaper and less general version of the above for well-known argument
+ * types.
+ */
+ public static Object setObjectIndex(Object obj, double dblIndex,
+ Object value, Context cx)
+ {
+ Scriptable sobj = toObjectOrNull(cx, obj);
+ if (sobj == null) {
+ throw undefWriteError(obj, String.valueOf(dblIndex), value);
+ }
+
+ int index = (int)dblIndex;
+ if (index == dblIndex) {
+ return setObjectIndex(sobj, index, value, cx);
+ } else {
+ String s = toString(dblIndex);
+ return setObjectProp(sobj, s, value, cx);
+ }
+ }
+
+ public static Object setObjectIndex(Scriptable obj, int index, Object value,
+ Context cx)
+ {
+ if (obj instanceof XMLObject) {
+ XMLObject xmlObject = (XMLObject)obj;
+ xmlObject.ecmaPut(cx, new Integer(index), value);
+ } else {
+ ScriptableObject.putProperty(obj, index, value);
+ }
+ return value;
+ }
+
+ public static boolean deleteObjectElem(Scriptable target, Object elem,
+ Context cx)
+ {
+ boolean result;
+ if (target instanceof XMLObject) {
+ XMLObject xmlObject = (XMLObject)target;
+ result = xmlObject.ecmaDelete(cx, elem);
+ } else {
+ String s = toStringIdOrIndex(cx, elem);
+ if (s == null) {
+ int index = lastIndexResult(cx);
+ result = ScriptableObject.deleteProperty(target, index);
+ } else {
+ result = ScriptableObject.deleteProperty(target, s);
+ }
+ }
+ return result;
+ }
+
+ public static boolean hasObjectElem(Scriptable target, Object elem,
+ Context cx)
+ {
+ boolean result;
+
+ if (target instanceof XMLObject) {
+ XMLObject xmlObject = (XMLObject)target;
+ result = xmlObject.ecmaHas(cx, elem);
+ } else {
+ String s = toStringIdOrIndex(cx, elem);
+ if (s == null) {
+ int index = lastIndexResult(cx);
+ result = ScriptableObject.hasProperty(target, index);
+ } else {
+ result = ScriptableObject.hasProperty(target, s);
+ }
+ }
+
+ return result;
+ }
+
+ public static Object refGet(Ref ref, Context cx)
+ {
+ return ref.get(cx);
+ }
+
+ public static Object refSet(Ref ref, Object value, Context cx)
+ {
+ return ref.set(cx, value);
+ }
+
+ public static Object refDel(Ref ref, Context cx)
+ {
+ return wrapBoolean(ref.delete(cx));
+ }
+
+ static boolean isSpecialProperty(String s)
+ {
+ return s.equals("__proto__") || s.equals("__parent__");
+ }
+
+ public static Ref specialRef(Object obj, String specialProperty,
+ Context cx)
+ {
+ return SpecialRef.createSpecial(cx, obj, specialProperty);
+ }
+
+ /**
+ * The delete operator
+ *
+ * See ECMA 11.4.1
+ *
+ * In ECMA 0.19, the description of the delete operator (11.4.1)
+ * assumes that the [[Delete]] method returns a value. However,
+ * the definition of the [[Delete]] operator (8.6.2.5) does not
+ * define a return value. Here we assume that the [[Delete]]
+ * method doesn't return a value.
+ */
+ public static Object delete(Object obj, Object id, Context cx)
+ {
+ Scriptable sobj = toObjectOrNull(cx, obj);
+ if (sobj == null) {
+ String idStr = (id == null) ? "null" : id.toString();
+ throw typeError2("msg.undef.prop.delete", toString(obj), idStr);
+ }
+ boolean result = deleteObjectElem(sobj, id, cx);
+ return wrapBoolean(result);
+ }
+
+ /**
+ * Looks up a name in the scope chain and returns its value.
+ */
+ public static Object name(Context cx, Scriptable scope, String name)
+ {
+ Scriptable parent = scope.getParentScope();
+ if (parent == null) {
+ Object result = topScopeName(cx, scope, name);
+ if (result == Scriptable.NOT_FOUND) {
+ throw notFoundError(scope, name);
+ }
+ return result;
+ }
+
+ return nameOrFunction(cx, scope, parent, name, false);
+ }
+
+ private static Object nameOrFunction(Context cx, Scriptable scope,
+ Scriptable parentScope, String name,
+ boolean asFunctionCall)
+ {
+ Object result;
+ Scriptable thisObj = scope; // It is used only if asFunctionCall==true.
+
+ XMLObject firstXMLObject = null;
+ for (;;) {
+ if (scope instanceof NativeWith) {
+ Scriptable withObj = scope.getPrototype();
+ if (withObj instanceof XMLObject) {
+ XMLObject xmlObj = (XMLObject)withObj;
+ if (xmlObj.ecmaHas(cx, name)) {
+ // function this should be the target object of with
+ thisObj = xmlObj;
+ result = xmlObj.ecmaGet(cx, name);
+ break;
+ }
+ if (firstXMLObject == null) {
+ firstXMLObject = xmlObj;
+ }
+ } else {
+ result = ScriptableObject.getProperty(withObj, name);
+ if (result != Scriptable.NOT_FOUND) {
+ // function this should be the target object of with
+ thisObj = withObj;
+ break;
+ }
+ }
+ } else if (scope instanceof NativeCall) {
+ // NativeCall does not prototype chain and Scriptable.get
+ // can be called directly.
+ result = scope.get(name, scope);
+ if (result != Scriptable.NOT_FOUND) {
+ if (asFunctionCall) {
+ // ECMA 262 requires that this for nested funtions
+ // should be top scope
+ thisObj = ScriptableObject.
+ getTopLevelScope(parentScope);
+ }
+ break;
+ }
+ } else {
+ // Can happen if Rhino embedding decided that nested
+ // scopes are useful for what ever reasons.
+ result = ScriptableObject.getProperty(scope, name);
+ if (result != Scriptable.NOT_FOUND) {
+ thisObj = scope;
+ break;
+ }
+ }
+ scope = parentScope;
+ parentScope = parentScope.getParentScope();
+ if (parentScope == null) {
+ result = topScopeName(cx, scope, name);
+ if (result == Scriptable.NOT_FOUND) {
+ if (firstXMLObject == null || asFunctionCall) {
+ throw notFoundError(scope, name);
+ }
+ // The name was not found, but we did find an XML
+ // object in the scope chain and we are looking for name,
+ // not function. The result should be an empty XMLList
+ // in name context.
+ result = firstXMLObject.ecmaGet(cx, name);
+ }
+ // For top scope thisObj for functions is always scope itself.
+ thisObj = scope;
+ break;
+ }
+ }
+
+ if (asFunctionCall) {
+ if (!(result instanceof Callable)) {
+ throw notFunctionError(result, name);
+ }
+ storeScriptable(cx, thisObj);
+ }
+
+ return result;
+ }
+
+ private static Object topScopeName(Context cx, Scriptable scope,
+ String name)
+ {
+ if (cx.useDynamicScope) {
+ scope = checkDynamicScope(cx.topCallScope, scope);
+ }
+ return ScriptableObject.getProperty(scope, name);
+ }
+
+
+ /**
+ * Returns the object in the scope chain that has a given property.
+ *
+ * The order of evaluation of an assignment expression involves
+ * evaluating the lhs to a reference, evaluating the rhs, and then
+ * modifying the reference with the rhs value. This method is used
+ * to 'bind' the given name to an object containing that property
+ * so that the side effects of evaluating the rhs do not affect
+ * which property is modified.
+ * Typically used in conjunction with setName.
+ *
+ * See ECMA 10.1.4
+ */
+ public static Scriptable bind(Context cx, Scriptable scope, String id)
+ {
+ Scriptable firstXMLObject = null;
+ Scriptable parent = scope.getParentScope();
+ childScopesChecks: if (parent != null) {
+ // Check for possibly nested "with" scopes first
+ while (scope instanceof NativeWith) {
+ Scriptable withObj = scope.getPrototype();
+ if (withObj instanceof XMLObject) {
+ XMLObject xmlObject = (XMLObject)withObj;
+ if (xmlObject.ecmaHas(cx, id)) {
+ return xmlObject;
+ }
+ if (firstXMLObject == null) {
+ firstXMLObject = xmlObject;
+ }
+ } else {
+ if (ScriptableObject.hasProperty(withObj, id)) {
+ return withObj;
+ }
+ }
+ scope = parent;
+ parent = parent.getParentScope();
+ if (parent == null) {
+ break childScopesChecks;
+ }
+ }
+ for (;;) {
+ if (ScriptableObject.hasProperty(scope, id)) {
+ return scope;
+ }
+ scope = parent;
+ parent = parent.getParentScope();
+ if (parent == null) {
+ break childScopesChecks;
+ }
+ }
+ }
+ // scope here is top scope
+ if (cx.useDynamicScope) {
+ scope = checkDynamicScope(cx.topCallScope, scope);
+ }
+ if (ScriptableObject.hasProperty(scope, id)) {
+ return scope;
+ }
+ // Nothing was found, but since XML objects always bind
+ // return one if found
+ return firstXMLObject;
+ }
+
+ public static Object setName(Scriptable bound, Object value,
+ Context cx, Scriptable scope, String id)
+ {
+ if (bound != null) {
+ if (bound instanceof XMLObject) {
+ XMLObject xmlObject = (XMLObject)bound;
+ xmlObject.ecmaPut(cx, id, value);
+ } else {
+ ScriptableObject.putProperty(bound, id, value);
+ }
+ } else {
+ // "newname = 7;", where 'newname' has not yet
+ // been defined, creates a new property in the
+ // top scope unless strict mode is specified.
+ if (cx.hasFeature(Context.FEATURE_STRICT_MODE) ||
+ cx.hasFeature(Context.FEATURE_STRICT_VARS))
+ {
+ Context.reportWarning(
+ ScriptRuntime.getMessage1("msg.assn.create.strict", id));
+ }
+ // Find the top scope by walking up the scope chain.
+ bound = ScriptableObject.getTopLevelScope(scope);
+ if (cx.useDynamicScope) {
+ bound = checkDynamicScope(cx.topCallScope, bound);
+ }
+ bound.put(id, bound, value);
+ }
+ return value;
+ }
+
+ public static Object setConst(Scriptable bound, Object value,
+ Context cx, String id)
+ {
+ if (bound instanceof XMLObject) {
+ XMLObject xmlObject = (XMLObject)bound;
+ xmlObject.ecmaPut(cx, id, value);
+ } else {
+ ScriptableObject.putConstProperty(bound, id, value);
+ }
+ return value;
+ }
+
+ /**
+ * This is the enumeration needed by the for..in statement.
+ *
+ * See ECMA 12.6.3.
+ *
+ * IdEnumeration maintains a ObjToIntMap to make sure a given
+ * id is enumerated only once across multiple objects in a
+ * prototype chain.
+ *
+ * XXX - ECMA delete doesn't hide properties in the prototype,
+ * but js/ref does. This means that the js/ref for..in can
+ * avoid maintaining a hash table and instead perform lookups
+ * to see if a given property has already been enumerated.
+ *
+ */
+ private static class IdEnumeration implements Serializable
+ {
+ private static final long serialVersionUID = 1L;
+ Scriptable obj;
+ Object[] ids;
+ int index;
+ ObjToIntMap used;
+ Object currentId;
+ int enumType; /* one of ENUM_INIT_KEYS, ENUM_INIT_VALUES,
+ ENUM_INIT_ARRAY */
+
+ // if true, integer ids will be returned as numbers rather than strings
+ boolean enumNumbers;
+
+ Scriptable iterator;
+ }
+
+ public static Scriptable toIterator(Context cx, Scriptable scope,
+ Scriptable obj, boolean keyOnly)
+ {
+ /*APPJET 1.6*//*
+ if (ScriptableObject.hasProperty(obj,
+ NativeIterator.ITERATOR_PROPERTY_NAME))
+ {
+ Object v = ScriptableObject.getProperty(obj,
+ NativeIterator.ITERATOR_PROPERTY_NAME);
+ if (!(v instanceof Callable)) {
+ throw typeError0("msg.invalid.iterator");
+ }
+ Callable f = (Callable) v;
+ Object[] args = new Object[] { keyOnly ? Boolean.TRUE
+ : Boolean.FALSE };
+ v = f.call(cx, scope, obj, args);
+ if (!(v instanceof Scriptable)) {
+ throw typeError0("msg.iterator.primitive");
+ }
+ return (Scriptable) v;
+ }*/
+ return null;
+ }
+
+ // for backwards compatibility with generated class files
+ public static Object enumInit(Object value, Context cx, boolean enumValues)
+ {
+ return enumInit(value, cx, enumValues ? ENUMERATE_VALUES
+ : ENUMERATE_KEYS);
+ }
+
+ public static final int ENUMERATE_KEYS = 0;
+ public static final int ENUMERATE_VALUES = 1;
+ public static final int ENUMERATE_ARRAY = 2;
+ public static final int ENUMERATE_KEYS_NO_ITERATOR = 3;
+ public static final int ENUMERATE_VALUES_NO_ITERATOR = 4;
+ public static final int ENUMERATE_ARRAY_NO_ITERATOR = 5;
+
+ public static Object enumInit(Object value, Context cx, int enumType)
+ {
+ IdEnumeration x = new IdEnumeration();
+ x.obj = toObjectOrNull(cx, value);
+ if (x.obj == null) {
+ // null or undefined do not cause errors but rather lead to empty
+ // "for in" loop
+ return x;
+ }
+ x.enumType = enumType;
+ x.iterator = null;
+ if (enumType != ENUMERATE_KEYS_NO_ITERATOR &&
+ enumType != ENUMERATE_VALUES_NO_ITERATOR &&
+ enumType != ENUMERATE_ARRAY_NO_ITERATOR)
+ {
+ x.iterator = toIterator(cx, x.obj.getParentScope(), x.obj, true);
+ }
+ if (x.iterator == null) {
+ // enumInit should read all initial ids before returning
+ // or "for (a.i in a)" would wrongly enumerate i in a as well
+ enumChangeObject(x);
+ }
+
+ return x;
+ }
+
+ public static void setEnumNumbers(Object enumObj, boolean enumNumbers) {
+ ((IdEnumeration)enumObj).enumNumbers = enumNumbers;
+ }
+
+ public static Boolean enumNext(Object enumObj)
+ {
+ IdEnumeration x = (IdEnumeration)enumObj;
+ if (x.iterator != null) {
+ Object v = ScriptableObject.getProperty(x.iterator, "next");
+ if (!(v instanceof Callable))
+ return Boolean.FALSE;
+ Callable f = (Callable) v;
+ Context cx = Context.enter();
+ try {
+ x.currentId = f.call(cx, x.iterator.getParentScope(),
+ x.iterator, emptyArgs);
+ return Boolean.TRUE;
+ } catch (JavaScriptException e) {
+ if (e.getValue() instanceof NativeIterator.StopIteration) {
+ return Boolean.FALSE;
+ }
+ throw e;
+ } finally {
+ Context.exit();
+ }
+ }
+ for (;;) {
+ if (x.obj == null) {
+ return Boolean.FALSE;
+ }
+ if (x.index == x.ids.length) {
+ x.obj = x.obj.getPrototype();
+ enumChangeObject(x);
+ continue;
+ }
+ Object id = x.ids[x.index++];
+ if (x.used != null && x.used.has(id)) {
+ continue;
+ }
+ if (id instanceof String) {
+ String strId = (String)id;
+ if (!x.obj.has(strId, x.obj))
+ continue; // must have been deleted
+ x.currentId = strId;
+ } else {
+ int intId = ((Number)id).intValue();
+ if (!x.obj.has(intId, x.obj))
+ continue; // must have been deleted
+ x.currentId = x.enumNumbers ? (Object) (new Integer(intId))
+ : String.valueOf(intId);
+ }
+ return Boolean.TRUE;
+ }
+ }
+
+ public static Object enumId(Object enumObj, Context cx)
+ {
+ IdEnumeration x = (IdEnumeration)enumObj;
+ if (x.iterator != null) {
+ return x.currentId;
+ }
+ switch (x.enumType) {
+ case ENUMERATE_KEYS:
+ case ENUMERATE_KEYS_NO_ITERATOR:
+ return x.currentId;
+ case ENUMERATE_VALUES:
+ case ENUMERATE_VALUES_NO_ITERATOR:
+ return enumValue(enumObj, cx);
+ case ENUMERATE_ARRAY:
+ case ENUMERATE_ARRAY_NO_ITERATOR:
+ Object[] elements = { x.currentId, enumValue(enumObj, cx) };
+ return cx.newArray(x.obj.getParentScope(), elements);
+ default:
+ throw Kit.codeBug();
+ }
+ }
+
+ public static Object enumValue(Object enumObj, Context cx) {
+ IdEnumeration x = (IdEnumeration)enumObj;
+
+ Object result;
+
+ String s = toStringIdOrIndex(cx, x.currentId);
+ if (s == null) {
+ int index = lastIndexResult(cx);
+ result = x.obj.get(index, x.obj);
+ } else {
+ result = x.obj.get(s, x.obj);
+ }
+
+ return result;
+ }
+
+ private static void enumChangeObject(IdEnumeration x)
+ {
+ Object[] ids = null;
+ while (x.obj != null) {
+ ids = x.obj.getIds();
+ if (ids.length != 0) {
+ break;
+ }
+ x.obj = x.obj.getPrototype();
+ }
+ if (x.obj != null && x.ids != null) {
+ Object[] previous = x.ids;
+ int L = previous.length;
+ if (x.used == null) {
+ x.used = new ObjToIntMap(L);
+ }
+ for (int i = 0; i != L; ++i) {
+ x.used.intern(previous[i]);
+ }
+ }
+ x.ids = ids;
+ x.index = 0;
+ }
+
+ /**
+ * Prepare for calling name(...): return function corresponding to
+ * name and make current top scope available
+ * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
+ * The caller must call ScriptRuntime.lastStoredScriptable() immediately
+ * after calling this method.
+ */
+ public static Callable getNameFunctionAndThis(String name,
+ Context cx,
+ Scriptable scope)
+ {
+ Scriptable parent = scope.getParentScope();
+ if (parent == null) {
+ Object result = topScopeName(cx, scope, name);
+ if (!(result instanceof Callable)) {
+ if (result == Scriptable.NOT_FOUND) {
+ throw notFoundError(scope, name);
+ } else {
+ throw notFunctionError(result, name);
+ }
+ }
+ // Top scope is not NativeWith or NativeCall => thisObj == scope
+ Scriptable thisObj = scope;
+ storeScriptable(cx, thisObj);
+ return (Callable)result;
+ }
+
+ // name will call storeScriptable(cx, thisObj);
+ return (Callable)nameOrFunction(cx, scope, parent, name, true);
+ }
+
+ /**
+ * Prepare for calling obj[id](...): return function corresponding to
+ * obj[id] and make obj properly converted to Scriptable available
+ * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
+ * The caller must call ScriptRuntime.lastStoredScriptable() immediately
+ * after calling this method.
+ */
+ public static Callable getElemFunctionAndThis(Object obj,
+ Object elem,
+ Context cx)
+ {
+ String s = toStringIdOrIndex(cx, elem);
+ if (s != null) {
+ return getPropFunctionAndThis(obj, s, cx);
+ }
+ int index = lastIndexResult(cx);
+
+ Scriptable thisObj = toObjectOrNull(cx, obj);
+ if (thisObj == null) {
+ throw undefCallError(obj, String.valueOf(index));
+ }
+
+ Object value;
+ for (;;) {
+ // Ignore XML lookup as requred by ECMA 357, 11.2.2.1
+ value = ScriptableObject.getProperty(thisObj, index);
+ if (value != Scriptable.NOT_FOUND) {
+ break;
+ }
+ if (!(thisObj instanceof XMLObject)) {
+ break;
+ }
+ XMLObject xmlObject = (XMLObject)thisObj;
+ Scriptable extra = xmlObject.getExtraMethodSource(cx);
+ if (extra == null) {
+ break;
+ }
+ thisObj = extra;
+ }
+ if (!(value instanceof Callable)) {
+ throw notFunctionError(value, elem);
+ }
+
+ storeScriptable(cx, thisObj);
+ return (Callable)value;
+ }
+
+ /**
+ * Prepare for calling obj.property(...): return function corresponding to
+ * obj.property and make obj properly converted to Scriptable available
+ * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
+ * The caller must call ScriptRuntime.lastStoredScriptable() immediately
+ * after calling this method.
+ */
+ public static Callable getPropFunctionAndThis(Object obj,
+ String property,
+ Context cx)
+ {
+ Scriptable thisObj = toObjectOrNull(cx, obj);
+ if (thisObj == null) {
+ throw undefCallError(obj, property);
+ }
+
+ Object value;
+ for (;;) {
+ // Ignore XML lookup as required by ECMA 357, 11.2.2.1
+ value = ScriptableObject.getProperty(thisObj, property);
+ if (value != Scriptable.NOT_FOUND) {
+ break;
+ }
+ if (!(thisObj instanceof XMLObject)) {
+ break;
+ }
+ XMLObject xmlObject = (XMLObject)thisObj;
+ Scriptable extra = xmlObject.getExtraMethodSource(cx);
+ if (extra == null) {
+ break;
+ }
+ thisObj = extra;
+ }
+
+ if (!(value instanceof Callable)) {
+ Object noSuchMethod = ScriptableObject.getProperty(thisObj, "__noSuchMethod__");
+ if (noSuchMethod instanceof Callable)
+ value = new NoSuchMethodShim((Callable)noSuchMethod, property);
+ else
+ throw notFunctionError(thisObj, value, property);
+ }
+
+ storeScriptable(cx, thisObj);
+ return (Callable)value;
+ }
+
+ /**
+ * Prepare for calling <expression>(...): return function corresponding to
+ * <expression> and make parent scope of the function available
+ * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
+ * The caller must call ScriptRuntime.lastStoredScriptable() immediately
+ * after calling this method.
+ */
+ public static Callable getValueFunctionAndThis(Object value, Context cx)
+ {
+ if (!(value instanceof Callable)) {
+ throw notFunctionError(value);
+ }
+
+ Callable f = (Callable)value;
+ Scriptable thisObj = null;
+ if (f instanceof Scriptable) {
+ thisObj = ((Scriptable)f).getParentScope();
+ }
+ if (thisObj == null) {
+ if (cx.topCallScope == null) throw new IllegalStateException();
+ thisObj = cx.topCallScope;
+ }
+ if (thisObj.getParentScope() != null) {
+ if (thisObj instanceof NativeWith) {
+ // functions defined inside with should have with target
+ // as their thisObj
+ } else if (thisObj instanceof NativeCall) {
+ // nested functions should have top scope as their thisObj
+ thisObj = ScriptableObject.getTopLevelScope(thisObj);
+ }
+ }
+ storeScriptable(cx, thisObj);
+ return f;
+ }
+
+ /**
+ * Perform function call in reference context. Should always
+ * return value that can be passed to
+ * {@link #refGet(Ref, Context)} or {@link #refSet(Ref, Object, Context)}
+ * arbitrary number of times.
+ * The args array reference should not be stored in any object that is
+ * can be GC-reachable after this method returns. If this is necessary,
+ * store args.clone(), not args array itself.
+ */
+ public static Ref callRef(Callable function, Scriptable thisObj,
+ Object[] args, Context cx)
+ {
+ if (function instanceof RefCallable) {
+ RefCallable rfunction = (RefCallable)function;
+ Ref ref = rfunction.refCall(cx, thisObj, args);
+ if (ref == null) {
+ throw new IllegalStateException(rfunction.getClass().getName()+".refCall() returned null");
+ }
+ return ref;
+ }
+ // No runtime support for now
+ String msg = getMessage1("msg.no.ref.from.function",
+ toString(function));
+ throw constructError("ReferenceError", msg);
+ }
+
+ /**
+ * Operator new.
+ *
+ * See ECMA 11.2.2
+ */
+ public static Scriptable newObject(Object fun, Context cx,
+ Scriptable scope, Object[] args)
+ {
+ if (!(fun instanceof Function)) {
+ throw notFunctionError(fun);
+ }
+ Function function = (Function)fun;
+ return function.construct(cx, scope, args);
+ }
+
+ public static Object callSpecial(Context cx, Callable fun,
+ Scriptable thisObj,
+ Object[] args, Scriptable scope,
+ Scriptable callerThis, int callType,
+ String filename, int lineNumber)
+ {
+ if (callType == Node.SPECIALCALL_EVAL) {
+ if (NativeGlobal.isEvalFunction(fun)) {
+ return evalSpecial(cx, scope, callerThis, args,
+ filename, lineNumber);
+ }
+ } else if (callType == Node.SPECIALCALL_WITH) {
+ if (NativeWith.isWithFunction(fun)) {
+ throw Context.reportRuntimeError1("msg.only.from.new",
+ "With");
+ }
+ } else {
+ throw Kit.codeBug();
+ }
+
+ return fun.call(cx, scope, thisObj, args);
+ }
+
+ public static Object newSpecial(Context cx, Object fun,
+ Object[] args, Scriptable scope,
+ int callType)
+ {
+ if (callType == Node.SPECIALCALL_EVAL) {
+ if (NativeGlobal.isEvalFunction(fun)) {
+ throw typeError1("msg.not.ctor", "eval");
+ }
+ } else if (callType == Node.SPECIALCALL_WITH) {
+ if (NativeWith.isWithFunction(fun)) {
+ return NativeWith.newWithSpecial(cx, scope, args);
+ }
+ } else {
+ throw Kit.codeBug();
+ }
+
+ return newObject(fun, cx, scope, args);
+ }
+
+ /**
+ * Function.prototype.apply and Function.prototype.call
+ *
+ * See Ecma 15.3.4.[34]
+ */
+ public static Object applyOrCall(boolean isApply,
+ Context cx, Scriptable scope,
+ Scriptable thisObj, Object[] args)
+ {
+ int L = args.length;
+ Callable function = getCallable(thisObj);
+
+ Scriptable callThis = null;
+ if (L != 0) {
+ callThis = toObjectOrNull(cx, args[0]);
+ }
+ if (callThis == null) {
+ // This covers the case of args[0] == (null|undefined) as well.
+ callThis = getTopCallScope(cx);
+ }
+
+ Object[] callArgs;
+ if (isApply) {
+ // Follow Ecma 15.3.4.3
+ callArgs = L <= 1 ? ScriptRuntime.emptyArgs :
+ getApplyArguments(cx, args[1]);
+ } else {
+ // Follow Ecma 15.3.4.4
+ if (L <= 1) {
+ callArgs = ScriptRuntime.emptyArgs;
+ } else {
+ callArgs = new Object[L - 1];
+ System.arraycopy(args, 1, callArgs, 0, L - 1);
+ }
+ }
+
+ return function.call(cx, scope, callThis, callArgs);
+ }
+
+ static Object[] getApplyArguments(Context cx, Object arg1)
+ {
+ if (arg1 == null || arg1 == Undefined.instance) {
+ return ScriptRuntime.emptyArgs;
+ } else if (arg1 instanceof NativeArray || arg1 instanceof Arguments) {
+ return cx.getElements((Scriptable) arg1);
+ } else {
+ throw ScriptRuntime.typeError0("msg.arg.isnt.array");
+ }
+ }
+
+ static Callable getCallable(Scriptable thisObj)
+ {
+ Callable function;
+ if (thisObj instanceof Callable) {
+ function = (Callable)thisObj;
+ } else {
+ Object value = thisObj.getDefaultValue(ScriptRuntime.FunctionClass);
+ if (!(value instanceof Callable)) {
+ throw ScriptRuntime.notFunctionError(value, thisObj);
+ }
+ function = (Callable)value;
+ }
+ return function;
+ }
+
+ /**
+ * The eval function property of the global object.
+ *
+ * See ECMA 15.1.2.1
+ */
+ public static Object evalSpecial(Context cx, Scriptable scope,
+ Object thisArg, Object[] args,
+ String filename, int lineNumber)
+ {
+ if (args.length < 1)
+ return Undefined.instance;
+ Object x = args[0];
+ if (!(x instanceof String)) {
+ if (cx.hasFeature(Context.FEATURE_STRICT_MODE) ||
+ cx.hasFeature(Context.FEATURE_STRICT_EVAL))
+ {
+ throw Context.reportRuntimeError0("msg.eval.nonstring.strict");
+ }
+ String message = ScriptRuntime.getMessage0("msg.eval.nonstring");
+ Context.reportWarning(message);
+ return x;
+ }
+ if (filename == null) {
+ int[] linep = new int[1];
+ filename = Context.getSourcePositionFromStack(linep);
+ if (filename != null) {
+ lineNumber = linep[0];
+ } else {
+ filename = "";
+ }
+ }
+ String sourceName = ScriptRuntime.
+ makeUrlForGeneratedScript(true, filename, lineNumber);
+
+ ErrorReporter reporter;
+ reporter = DefaultErrorReporter.forEval(cx.getErrorReporter());
+
+ Evaluator evaluator = Context.createInterpreter();
+ if (evaluator == null) {
+ throw new JavaScriptException("Interpreter not present",
+ filename, lineNumber);
+ }
+
+ // Compile with explicit interpreter instance to force interpreter
+ // mode.
+ Script script = cx.compileString((String)x, evaluator,
+ reporter, sourceName, 1, null);
+ evaluator.setEvalScriptFlag(script);
+ Callable c = (Callable)script;
+ return c.call(cx, scope, (Scriptable)thisArg, ScriptRuntime.emptyArgs);
+ }
+
+ /**
+ * The typeof operator
+ */
+ public static String typeof(Object value)
+ {
+ if (value == null)
+ return "object";
+ if (value == Undefined.instance)
+ return "undefined";
+ if (value instanceof Scriptable)
+ {
+ if (value instanceof ScriptableObject &&
+ ((ScriptableObject)value).avoidObjectDetection())
+ {
+ return "undefined";
+ }
+ if (value instanceof XMLObject)
+ return "xml";
+ return (value instanceof Callable) ? "function" : "object";
+ }
+ if (value instanceof String)
+ return "string";
+ if (value instanceof Number)
+ return "number";
+ if (value instanceof Boolean)
+ return "boolean";
+ throw errorWithClassName("msg.invalid.type", value);
+ }
+
+ /**
+ * The typeof operator that correctly handles the undefined case
+ */
+ public static String typeofName(Scriptable scope, String id)
+ {
+ Context cx = Context.getContext();
+ Scriptable val = bind(cx, scope, id);
+ if (val == null)
+ return "undefined";
+ return typeof(getObjectProp(val, id, cx));
+ }
+
+ // neg:
+ // implement the '-' operator inline in the caller
+ // as "-toNumber(val)"
+
+ // not:
+ // implement the '!' operator inline in the caller
+ // as "!toBoolean(val)"
+
+ // bitnot:
+ // implement the '~' operator inline in the caller
+ // as "~toInt32(val)"
+
+ public static Object add(Object val1, Object val2, Context cx)
+ {
+ if(val1 instanceof Number && val2 instanceof Number) {
+ return wrapNumber(((Number)val1).doubleValue() +
+ ((Number)val2).doubleValue());
+ }
+ if (val1 instanceof XMLObject) {
+ Object test = ((XMLObject)val1).addValues(cx, true, val2);
+ if (test != Scriptable.NOT_FOUND) {
+ return test;
+ }
+ }
+ if (val2 instanceof XMLObject) {
+ Object test = ((XMLObject)val2).addValues(cx, false, val1);
+ if (test != Scriptable.NOT_FOUND) {
+ return test;
+ }
+ }
+ if (val1 instanceof Scriptable)
+ val1 = ((Scriptable) val1).getDefaultValue(null);
+ if (val2 instanceof Scriptable)
+ val2 = ((Scriptable) val2).getDefaultValue(null);
+ if (!(val1 instanceof String) && !(val2 instanceof String))
+ if ((val1 instanceof Number) && (val2 instanceof Number))
+ return wrapNumber(((Number)val1).doubleValue() +
+ ((Number)val2).doubleValue());
+ else
+ return wrapNumber(toNumber(val1) + toNumber(val2));
+ return toString(val1).concat(toString(val2));
+ }
+
+ public static String add(String val1, Object val2) {
+ return val1.concat(toString(val2));
+ }
+
+ public static String add(Object val1, String val2) {
+ return toString(val1).concat(val2);
+ }
+
+ /**
+ * @deprecated The method is only present for compatibility.
+ */
+ public static Object nameIncrDecr(Scriptable scopeChain, String id,
+ int incrDecrMask)
+ {
+ return nameIncrDecr(scopeChain, id, Context.getContext(), incrDecrMask);
+ }
+
+ public static Object nameIncrDecr(Scriptable scopeChain, String id,
+ Context cx, int incrDecrMask)
+ {
+ Scriptable target;
+ Object value;
+ search: {
+ do {
+ if (cx.useDynamicScope && scopeChain.getParentScope() == null) {
+ scopeChain = checkDynamicScope(cx.topCallScope, scopeChain);
+ }
+ target = scopeChain;
+ do {
+ value = target.get(id, scopeChain);
+ if (value != Scriptable.NOT_FOUND) {
+ break search;
+ }
+ target = target.getPrototype();
+ } while (target != null);
+ scopeChain = scopeChain.getParentScope();
+ } while (scopeChain != null);
+ throw notFoundError(scopeChain, id);
+ }
+ return doScriptableIncrDecr(target, id, scopeChain, value,
+ incrDecrMask);
+ }
+
+ public static Object propIncrDecr(Object obj, String id,
+ Context cx, int incrDecrMask)
+ {
+ Scriptable start = toObjectOrNull(cx, obj);
+ if (start == null) {
+ throw undefReadError(obj, id);
+ }
+
+ Scriptable target = start;
+ Object value;
+ search: {
+ do {
+ value = target.get(id, start);
+ if (value != Scriptable.NOT_FOUND) {
+ break search;
+ }
+ target = target.getPrototype();
+ } while (target != null);
+ start.put(id, start, NaNobj);
+ return NaNobj;
+ }
+ return doScriptableIncrDecr(target, id, start, value,
+ incrDecrMask);
+ }
+
+ private static Object doScriptableIncrDecr(Scriptable target,
+ String id,
+ Scriptable protoChainStart,
+ Object value,
+ int incrDecrMask)
+ {
+ boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
+ double number;
+ if (value instanceof Number) {
+ number = ((Number)value).doubleValue();
+ } else {
+ number = toNumber(value);
+ if (post) {
+ // convert result to number
+ value = wrapNumber(number);
+ }
+ }
+ if ((incrDecrMask & Node.DECR_FLAG) == 0) {
+ ++number;
+ } else {
+ --number;
+ }
+ Number result = wrapNumber(number);
+ target.put(id, protoChainStart, result);
+ if (post) {
+ return value;
+ } else {
+ return result;
+ }
+ }
+
+ public static Object elemIncrDecr(Object obj, Object index,
+ Context cx, int incrDecrMask)
+ {
+ Object value = getObjectElem(obj, index, cx);
+ boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
+ double number;
+ if (value instanceof Number) {
+ number = ((Number)value).doubleValue();
+ } else {
+ number = toNumber(value);
+ if (post) {
+ // convert result to number
+ value = wrapNumber(number);
+ }
+ }
+ if ((incrDecrMask & Node.DECR_FLAG) == 0) {
+ ++number;
+ } else {
+ --number;
+ }
+ Number result = wrapNumber(number);
+ setObjectElem(obj, index, result, cx);
+ if (post) {
+ return value;
+ } else {
+ return result;
+ }
+ }
+
+ public static Object refIncrDecr(Ref ref, Context cx, int incrDecrMask)
+ {
+ Object value = ref.get(cx);
+ boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
+ double number;
+ if (value instanceof Number) {
+ number = ((Number)value).doubleValue();
+ } else {
+ number = toNumber(value);
+ if (post) {
+ // convert result to number
+ value = wrapNumber(number);
+ }
+ }
+ if ((incrDecrMask & Node.DECR_FLAG) == 0) {
+ ++number;
+ } else {
+ --number;
+ }
+ Number result = wrapNumber(number);
+ ref.set(cx, result);
+ if (post) {
+ return value;
+ } else {
+ return result;
+ }
+ }
+
+ private static Object toPrimitive(Object val)
+ {
+ if (!(val instanceof Scriptable)) {
+ return val;
+ }
+ Scriptable s = (Scriptable)val;
+ Object result = s.getDefaultValue(null);
+ if (result instanceof Scriptable)
+ throw typeError0("msg.bad.default.value");
+ return result;
+ }
+
+ /**
+ * Equality
+ *
+ * See ECMA 11.9
+ */
+ public static boolean eq(Object x, Object y)
+ {
+ if (x == null || x == Undefined.instance) {
+ if (y == null || y == Undefined.instance) {
+ return true;
+ }
+ if (y instanceof ScriptableObject) {
+ Object test = ((ScriptableObject)y).equivalentValues(x);
+ if (test != Scriptable.NOT_FOUND) {
+ return ((Boolean)test).booleanValue();
+ }
+ }
+ return false;
+ } else if (x instanceof Number) {
+ return eqNumber(((Number)x).doubleValue(), y);
+ } else if (x instanceof String) {
+ return eqString((String)x, y);
+ } else if (x instanceof Boolean) {
+ boolean b = ((Boolean)x).booleanValue();
+ if (y instanceof Boolean) {
+ return b == ((Boolean)y).booleanValue();
+ }
+ if (y instanceof ScriptableObject) {
+ Object test = ((ScriptableObject)y).equivalentValues(x);
+ if (test != Scriptable.NOT_FOUND) {
+ return ((Boolean)test).booleanValue();
+ }
+ }
+ return eqNumber(b ? 1.0 : 0.0, y);
+ } else if (x instanceof Scriptable) {
+ if (y instanceof Scriptable) {
+ if (x == y) {
+ return true;
+ }
+ if (x instanceof ScriptableObject) {
+ Object test = ((ScriptableObject)x).equivalentValues(y);
+ if (test != Scriptable.NOT_FOUND) {
+ return ((Boolean)test).booleanValue();
+ }
+ }
+ if (y instanceof ScriptableObject) {
+ Object test = ((ScriptableObject)y).equivalentValues(x);
+ if (test != Scriptable.NOT_FOUND) {
+ return ((Boolean)test).booleanValue();
+ }
+ }
+ if (x instanceof Wrapper && y instanceof Wrapper) {
+ // See bug 413838. Effectively an extension to ECMA for
+ // the LiveConnect case.
+ Object unwrappedX = ((Wrapper)x).unwrap();
+ Object unwrappedY = ((Wrapper)y).unwrap();
+ return unwrappedX == unwrappedY ||
+ (isPrimitive(unwrappedX) &&
+ isPrimitive(unwrappedY) &&
+ eq(unwrappedX, unwrappedY));
+ }
+ return false;
+ } else if (y instanceof Boolean) {
+ if (x instanceof ScriptableObject) {
+ Object test = ((ScriptableObject)x).equivalentValues(y);
+ if (test != Scriptable.NOT_FOUND) {
+ return ((Boolean)test).booleanValue();
+ }
+ }
+ double d = ((Boolean)y).booleanValue() ? 1.0 : 0.0;
+ return eqNumber(d, x);
+ } else if (y instanceof Number) {
+ return eqNumber(((Number)y).doubleValue(), x);
+ } else if (y instanceof String) {
+ return eqString((String)y, x);
+ }
+ // covers the case when y == Undefined.instance as well
+ return false;
+ } else {
+ warnAboutNonJSObject(x);
+ return x == y;
+ }
+ }
+
+ private static boolean isPrimitive(Object obj) {
+ return (obj instanceof Number) || (obj instanceof String) ||
+ (obj instanceof Boolean);
+ }
+
+ static boolean eqNumber(double x, Object y)
+ {
+ for (;;) {
+ if (y == null || y == Undefined.instance) {
+ return false;
+ } else if (y instanceof Number) {
+ return x == ((Number)y).doubleValue();
+ } else if (y instanceof String) {
+ return x == toNumber(y);
+ } else if (y instanceof Boolean) {
+ return x == (((Boolean)y).booleanValue() ? 1.0 : +0.0);
+ } else if (y instanceof Scriptable) {
+ if (y instanceof ScriptableObject) {
+ Object xval = wrapNumber(x);
+ Object test = ((ScriptableObject)y).equivalentValues(xval);
+ if (test != Scriptable.NOT_FOUND) {
+ return ((Boolean)test).booleanValue();
+ }
+ }
+ y = toPrimitive(y);
+ } else {
+ warnAboutNonJSObject(y);
+ return false;
+ }
+ }
+ }
+
+ private static boolean eqString(String x, Object y)
+ {
+ for (;;) {
+ if (y == null || y == Undefined.instance) {
+ return false;
+ } else if (y instanceof String) {
+ return x.equals(y);
+ } else if (y instanceof Number) {
+ return toNumber(x) == ((Number)y).doubleValue();
+ } else if (y instanceof Boolean) {
+ return toNumber(x) == (((Boolean)y).booleanValue() ? 1.0 : 0.0);
+ } else if (y instanceof Scriptable) {
+ if (y instanceof ScriptableObject) {
+ Object test = ((ScriptableObject)y).equivalentValues(x);
+ if (test != Scriptable.NOT_FOUND) {
+ return ((Boolean)test).booleanValue();
+ }
+ }
+ y = toPrimitive(y);
+ continue;
+ } else {
+ warnAboutNonJSObject(y);
+ return false;
+ }
+ }
+ }
+ public static boolean shallowEq(Object x, Object y)
+ {
+ if (x == y) {
+ if (!(x instanceof Number)) {
+ return true;
+ }
+ // NaN check
+ double d = ((Number)x).doubleValue();
+ return d == d;
+ }
+ if (x == null || x == Undefined.instance) {
+ return false;
+ } else if (x instanceof Number) {
+ if (y instanceof Number) {
+ return ((Number)x).doubleValue() == ((Number)y).doubleValue();
+ }
+ } else if (x instanceof String) {
+ if (y instanceof String) {
+ return x.equals(y);
+ }
+ } else if (x instanceof Boolean) {
+ if (y instanceof Boolean) {
+ return x.equals(y);
+ }
+ } else if (x instanceof Scriptable) {
+ if (x instanceof Wrapper && y instanceof Wrapper) {
+ return ((Wrapper)x).unwrap() == ((Wrapper)y).unwrap();
+ }
+ } else {
+ warnAboutNonJSObject(x);
+ return x == y;
+ }
+ return false;
+ }
+
+ /**
+ * The instanceof operator.
+ *
+ * @return a instanceof b
+ */
+ public static boolean instanceOf(Object a, Object b, Context cx)
+ {
+ // Check RHS is an object
+ if (! (b instanceof Scriptable)) {
+ throw typeError0("msg.instanceof.not.object");
+ }
+
+ // for primitive values on LHS, return false
+ // XXX we may want to change this so that
+ // 5 instanceof Number == true
+ if (! (a instanceof Scriptable))
+ return false;
+
+ return ((Scriptable)b).hasInstance((Scriptable)a);
+ }
+
+ /**
+ * Delegates to
+ *
+ * @return true iff rhs appears in lhs' proto chain
+ */
+ public static boolean jsDelegatesTo(Scriptable lhs, Scriptable rhs) {
+ Scriptable proto = lhs.getPrototype();
+
+ while (proto != null) {
+ if (proto.equals(rhs)) return true;
+ proto = proto.getPrototype();
+ }
+
+ return false;
+ }
+
+ /**
+ * The in operator.
+ *
+ * This is a new JS 1.3 language feature. The in operator mirrors
+ * the operation of the for .. in construct, and tests whether the
+ * rhs has the property given by the lhs. It is different from the
+ * for .. in construct in that:
+ * <BR> - it doesn't perform ToObject on the right hand side
+ * <BR> - it returns true for DontEnum properties.
+ * @param a the left hand operand
+ * @param b the right hand operand
+ *
+ * @return true if property name or element number a is a property of b
+ */
+ public static boolean in(Object a, Object b, Context cx)
+ {
+ if (!(b instanceof Scriptable)) {
+ throw typeError0("msg.instanceof.not.object");
+ }
+
+ return hasObjectElem((Scriptable)b, a, cx);
+ }
+
+ public static boolean cmp_LT(Object val1, Object val2)
+ {
+ double d1, d2;
+ if (val1 instanceof Number && val2 instanceof Number) {
+ d1 = ((Number)val1).doubleValue();
+ d2 = ((Number)val2).doubleValue();
+ } else {
+ if (val1 instanceof Scriptable)
+ val1 = ((Scriptable) val1).getDefaultValue(NumberClass);
+ if (val2 instanceof Scriptable)
+ val2 = ((Scriptable) val2).getDefaultValue(NumberClass);
+ if (val1 instanceof String && val2 instanceof String) {
+ return ((String)val1).compareTo((String)val2) < 0;
+ }
+ d1 = toNumber(val1);
+ d2 = toNumber(val2);
+ }
+ return d1 < d2;
+ }
+
+ public static boolean cmp_LE(Object val1, Object val2)
+ {
+ double d1, d2;
+ if (val1 instanceof Number && val2 instanceof Number) {
+ d1 = ((Number)val1).doubleValue();
+ d2 = ((Number)val2).doubleValue();
+ } else {
+ if (val1 instanceof Scriptable)
+ val1 = ((Scriptable) val1).getDefaultValue(NumberClass);
+ if (val2 instanceof Scriptable)
+ val2 = ((Scriptable) val2).getDefaultValue(NumberClass);
+ if (val1 instanceof String && val2 instanceof String) {
+ return ((String)val1).compareTo((String)val2) <= 0;
+ }
+ d1 = toNumber(val1);
+ d2 = toNumber(val2);
+ }
+ return d1 <= d2;
+ }
+
+ // ------------------
+ // Statements
+ // ------------------
+
+ public static ScriptableObject getGlobal(Context cx) {
+ final String GLOBAL_CLASS = "org.mozilla.javascript.tools.shell.Global";
+ Class globalClass = Kit.classOrNull(GLOBAL_CLASS);
+ if (globalClass != null) {
+ try {
+ Class[] parm = { ScriptRuntime.ContextClass };
+ Constructor globalClassCtor = globalClass.getConstructor(parm);
+ Object[] arg = { cx };
+ return (ScriptableObject) globalClassCtor.newInstance(arg);
+ } catch (Exception e) {
+ // fall through...
+ }
+ }
+ return new ImporterTopLevel(cx);
+ }
+
+ public static boolean hasTopCall(Context cx)
+ {
+ return (cx.topCallScope != null);
+ }
+
+ public static Scriptable getTopCallScope(Context cx)
+ {
+ Scriptable scope = cx.topCallScope;
+ if (scope == null) {
+ throw new IllegalStateException();
+ }
+ return scope;
+ }
+
+ public static Object doTopCall(Callable callable,
+ Context cx, Scriptable scope,
+ Scriptable thisObj, Object[] args)
+ {
+ if (scope == null) throw new IllegalArgumentException();
+ if (cx.topCallScope != null) throw new IllegalStateException();
+
+ Object result;
+ cx.topCallScope = ScriptableObject.getTopLevelScope(scope);
+ cx.useDynamicScope = cx.hasFeature(Context.FEATURE_DYNAMIC_SCOPE);
+ ContextFactory f = cx.getFactory();
+ try {
+ result = f.doTopCall(callable, cx, scope, thisObj, args);
+ } finally {
+ cx.topCallScope = null;
+ // Cleanup cached references
+ cx.cachedXMLLib = null;
+
+ if (cx.currentActivationCall != null) {
+ // Function should always call exitActivationFunction
+ // if it creates activation record
+ throw new IllegalStateException();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Return <tt>possibleDynamicScope</tt> if <tt>staticTopScope</tt>
+ * is present on its prototype chain and return <tt>staticTopScope</tt>
+ * otherwise.
+ * Should only be called when <tt>staticTopScope</tt> is top scope.
+ */
+ static Scriptable checkDynamicScope(Scriptable possibleDynamicScope,
+ Scriptable staticTopScope)
+ {
+ // Return cx.topCallScope if scope
+ if (possibleDynamicScope == staticTopScope) {
+ return possibleDynamicScope;
+ }
+ Scriptable proto = possibleDynamicScope;
+ for (;;) {
+ proto = proto.getPrototype();
+ if (proto == staticTopScope) {
+ return possibleDynamicScope;
+ }
+ if (proto == null) {
+ return staticTopScope;
+ }
+ }
+ }
+
+ public static void addInstructionCount(Context cx, int instructionsToAdd)
+ {
+ cx.instructionCount += instructionsToAdd;
+ if (cx.instructionCount > cx.instructionThreshold)
+ {
+ cx.observeInstructionCount(cx.instructionCount);
+ cx.instructionCount = 0;
+ }
+ }
+
+ public static void initScript(NativeFunction funObj, Scriptable thisObj,
+ Context cx, Scriptable scope,
+ boolean evalScript)
+ {
+ if (cx.topCallScope == null)
+ throw new IllegalStateException();
+
+ int varCount = funObj.getParamAndVarCount();
+ if (varCount != 0) {
+
+ Scriptable varScope = scope;
+ // Never define any variables from var statements inside with
+ // object. See bug 38590.
+ while (varScope instanceof NativeWith) {
+ varScope = varScope.getParentScope();
+ }
+
+ for (int i = varCount; i-- != 0;) {
+ String name = funObj.getParamOrVarName(i);
+ boolean isConst = funObj.getParamOrVarConst(i);
+ // Don't overwrite existing def if already defined in object
+ // or prototypes of object.
+ if (!ScriptableObject.hasProperty(scope, name)) {
+ if (!evalScript) {
+ // Global var definitions are supposed to be DONTDELETE
+ if (isConst)
+ ScriptableObject.defineConstProperty(varScope, name);
+ else
+ ScriptableObject.defineProperty(
+ varScope, name, Undefined.instance,
+ ScriptableObject.PERMANENT);
+ } else {
+ varScope.put(name, varScope, Undefined.instance);
+ }
+ } else {
+ ScriptableObject.redefineProperty(scope, name, isConst);
+ }
+ }
+ }
+ }
+
+ public static Scriptable createFunctionActivation(NativeFunction funObj,
+ Scriptable scope,
+ Object[] args)
+ {
+ return new NativeCall(funObj, scope, args);
+ }
+
+
+ public static void enterActivationFunction(Context cx,
+ Scriptable scope)
+ {
+ if (cx.topCallScope == null)
+ throw new IllegalStateException();
+ NativeCall call = (NativeCall)scope;
+ call.parentActivationCall = cx.currentActivationCall;
+ cx.currentActivationCall = call;
+ }
+
+ public static void exitActivationFunction(Context cx)
+ {
+ NativeCall call = cx.currentActivationCall;
+ cx.currentActivationCall = call.parentActivationCall;
+ call.parentActivationCall = null;
+ }
+
+ static NativeCall findFunctionActivation(Context cx, Function f)
+ {
+ NativeCall call = cx.currentActivationCall;
+ while (call != null) {
+ if (call.function == f)
+ return call;
+ call = call.parentActivationCall;
+ }
+ return null;
+ }
+
+ public static Scriptable newCatchScope(Throwable t,
+ Scriptable lastCatchScope,
+ String exceptionName,
+ Context cx, Scriptable scope)
+ {
+ Object obj;
+ boolean cacheObj;
+
+ getObj:
+ if (t instanceof JavaScriptException) {
+ cacheObj = false;
+ obj = ((JavaScriptException)t).getValue();
+ } else {
+ cacheObj = true;
+
+ // Create wrapper object unless it was associated with
+ // the previous scope object
+
+ if (lastCatchScope != null) {
+ NativeObject last = (NativeObject)lastCatchScope;
+ obj = last.getAssociatedValue(t);
+ if (obj == null) Kit.codeBug();
+ break getObj;
+ }
+
+ RhinoException re;
+ String errorName;
+ String errorMsg;
+ Throwable javaException = null;
+
+ if (t instanceof EcmaError) {
+ EcmaError ee = (EcmaError)t;
+ re = ee;
+ errorName = ee.getName();
+ errorMsg = ee.getErrorMessage();
+ } else if (t instanceof WrappedException) {
+ WrappedException we = (WrappedException)t;
+ re = we;
+ javaException = we.getWrappedException();
+ errorName = "JavaException";
+ errorMsg = javaException.getClass().getName()
+ +": "+javaException.getMessage();
+ } else if (t instanceof EvaluatorException) {
+ // Pure evaluator exception, nor WrappedException instance
+ EvaluatorException ee = (EvaluatorException)t;
+ re = ee;
+ errorName = "InternalError";
+ errorMsg = ee.getMessage();
+ } else if (cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)) {
+ // With FEATURE_ENHANCED_JAVA_ACCESS, scripts can catch
+ // all exception types
+ re = new WrappedException(t);
+ errorName = "JavaException";
+ errorMsg = t.toString();
+ } else {
+ // Script can catch only instances of JavaScriptException,
+ // EcmaError and EvaluatorException
+ throw Kit.codeBug();
+ }
+
+ String sourceUri = re.sourceName();
+ if (sourceUri == null) {
+ sourceUri = "";
+ }
+ int line = re.lineNumber();
+ Object args[];
+ if (line > 0) {
+ args = new Object[] { errorMsg, sourceUri, new Integer(line) };
+ } else {
+ args = new Object[] { errorMsg, sourceUri };
+ }
+
+ Scriptable errorObject = cx.newObject(scope, errorName, args);
+ ScriptableObject.putProperty(errorObject, "name", errorName);
+
+ if (javaException != null) {
+ Object wrap = cx.getWrapFactory().wrap(cx, scope, javaException,
+ null);
+ ScriptableObject.defineProperty(
+ errorObject, "javaException", wrap,
+ ScriptableObject.PERMANENT | ScriptableObject.READONLY);
+ }
+ Object wrap = cx.getWrapFactory().wrap(cx, scope, re, null);
+ ScriptableObject.defineProperty(
+ errorObject, "rhinoException", wrap,
+ ScriptableObject.PERMANENT | ScriptableObject.READONLY);
+
+ obj = errorObject;
+ }
+
+ NativeObject catchScopeObject = new NativeObject();
+ // See ECMA 12.4
+ catchScopeObject.defineProperty(
+ exceptionName, obj, ScriptableObject.PERMANENT);
+
+ // Add special Rhino object __exception__ defined in the catch
+ // scope that can be used to retrieve the Java exception associated
+ // with the JavaScript exception (to get stack trace info, etc.)
+ /*APPJET NOJAVA*/
+ /*catchScopeObject.defineProperty(
+ "__exception__", Context.javaToJS(t, scope),
+ ScriptableObject.PERMANENT|ScriptableObject.DONTENUM);*/
+
+ if (cacheObj) {
+ catchScopeObject.associateValue(t, obj);
+ }
+ return catchScopeObject;
+ }
+
+ public static Scriptable enterWith(Object obj, Context cx,
+ Scriptable scope)
+ {
+ Scriptable sobj = toObjectOrNull(cx, obj);
+ if (sobj == null) {
+ throw typeError1("msg.undef.with", toString(obj));
+ }
+ if (sobj instanceof XMLObject) {
+ XMLObject xmlObject = (XMLObject)sobj;
+ return xmlObject.enterWith(scope);
+ }
+ return new NativeWith(scope, sobj);
+ }
+
+ public static Scriptable leaveWith(Scriptable scope)
+ {
+ NativeWith nw = (NativeWith)scope;
+ return nw.getParentScope();
+ }
+
+ public static Scriptable enterDotQuery(Object value, Scriptable scope)
+ {
+ if (!(value instanceof XMLObject)) {
+ throw notXmlError(value);
+ }
+ XMLObject object = (XMLObject)value;
+ return object.enterDotQuery(scope);
+ }
+
+ public static Object updateDotQuery(boolean value, Scriptable scope)
+ {
+ // Return null to continue looping
+ NativeWith nw = (NativeWith)scope;
+ return nw.updateDotQuery(value);
+ }
+
+ public static Scriptable leaveDotQuery(Scriptable scope)
+ {
+ NativeWith nw = (NativeWith)scope;
+ return nw.getParentScope();
+ }
+
+ public static void setFunctionProtoAndParent(BaseFunction fn,
+ Scriptable scope)
+ {
+ fn.setParentScope(scope);
+ fn.setPrototype(ScriptableObject.getFunctionPrototype(scope));
+ }
+
+ public static void setObjectProtoAndParent(ScriptableObject object,
+ Scriptable scope)
+ {
+ // Compared with function it always sets the scope to top scope
+ scope = ScriptableObject.getVeryTopLevelScope(scope); // APPJET
+ object.setParentScope(scope);
+ Scriptable proto
+ = ScriptableObject.getClassPrototype(scope, object.getClassName());
+ object.setPrototype(proto);
+ }
+
+ public static void initFunction(Context cx, Scriptable scope,
+ NativeFunction function, int type,
+ boolean fromEvalCode)
+ {
+ if (type == FunctionNode.FUNCTION_STATEMENT) {
+ String name = function.getFunctionName();
+ if (name != null && name.length() != 0) {
+ if (!fromEvalCode) {
+ // ECMA specifies that functions defined in global and
+ // function scope outside eval should have DONTDELETE set.
+ ScriptableObject.defineProperty
+ (scope, name, function, ScriptableObject.PERMANENT);
+ } else {
+ scope.put(name, scope, function);
+ }
+ }
+ } else if (type == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) {
+ String name = function.getFunctionName();
+ if (name != null && name.length() != 0) {
+ // Always put function expression statements into initial
+ // activation object ignoring the with statement to follow
+ // SpiderMonkey
+ while (scope instanceof NativeWith) {
+ scope = scope.getParentScope();
+ }
+ scope.put(name, scope, function);
+ }
+ } else {
+ throw Kit.codeBug();
+ }
+ }
+
+ public static Scriptable newArrayLiteral(Object[] objects,
+ int[] skipIndices,
+ Context cx, Scriptable scope)
+ {
+ final int SKIP_DENSITY = 2;
+ int count = objects.length;
+ int skipCount = 0;
+ if (skipIndices != null) {
+ skipCount = skipIndices.length;
+ }
+ int length = count + skipCount;
+ if (length > 1 && skipCount * SKIP_DENSITY < length) {
+ // If not too sparse, create whole array for constructor
+ Object[] sparse;
+ if (skipCount == 0) {
+ sparse = objects;
+ } else {
+ sparse = new Object[length];
+ int skip = 0;
+ for (int i = 0, j = 0; i != length; ++i) {
+ if (skip != skipCount && skipIndices[skip] == i) {
+ sparse[i] = Scriptable.NOT_FOUND;
+ ++skip;
+ continue;
+ }
+ sparse[i] = objects[j];
+ ++j;
+ }
+ }
+ return cx.newObject(scope, "Array", sparse);
+ }
+
+ Scriptable arrayObj = cx.newObject(scope, "Array",
+ ScriptRuntime.emptyArgs);
+ int skip = 0;
+ for (int i = 0, j = 0; i != length; ++i) {
+ if (skip != skipCount && skipIndices[skip] == i) {
+ ++skip;
+ continue;
+ }
+ ScriptableObject.putProperty(arrayObj, i, objects[j]);
+ ++j;
+ }
+ return arrayObj;
+ }
+
+ /**
+ * This method is here for backward compat with existing compiled code. It
+ * is called when an object literal is compiled. The next instance will be
+ * the version called from new code.
+ * @deprecated This method only present for compatibility.
+ */
+ public static Scriptable newObjectLiteral(Object[] propertyIds,
+ Object[] propertyValues,
+ Context cx, Scriptable scope)
+ {
+ // This will initialize to all zeros, exactly what we need for old-style
+ // getterSetters values (no getters or setters in the list)
+ int [] getterSetters = new int[propertyIds.length];
+ return newObjectLiteral(propertyIds, propertyValues, getterSetters,
+ cx, scope);
+ }
+
+ public static Scriptable newObjectLiteral(Object[] propertyIds,
+ Object[] propertyValues,
+ int [] getterSetters,
+ Context cx, Scriptable scope)
+ {
+ Scriptable object = cx.newObject(scope);
+ for (int i = 0, end = propertyIds.length; i != end; ++i) {
+ Object id = propertyIds[i];
+ int getterSetter = getterSetters[i];
+ Object value = propertyValues[i];
+ if (id instanceof String) {
+ if (getterSetter == 0)
+ ScriptableObject.putProperty(object, (String)id, value);
+ else {
+ Callable fun;
+ String definer;
+ if (getterSetter < 0) // < 0 means get foo() ...
+ definer = "__defineGetter__";
+ else
+ definer = "__defineSetter__";
+ fun = getPropFunctionAndThis(object, definer, cx);
+ // Must consume the last scriptable object in cx
+ lastStoredScriptable(cx);
+ Object[] outArgs = new Object[2];
+ outArgs[0] = id;
+ outArgs[1] = value;
+ fun.call(cx, scope, object, outArgs);
+ }
+ } else {
+ int index = ((Integer)id).intValue();
+ ScriptableObject.putProperty(object, index, value);
+ }
+ }
+ return object;
+ }
+
+ public static boolean isArrayObject(Object obj)
+ {
+ return obj instanceof NativeArray || obj instanceof Arguments;
+ }
+
+ public static Object[] getArrayElements(Scriptable object)
+ {
+ Context cx = Context.getContext();
+ long longLen = NativeArray.getLengthProperty(cx, object);
+ if (longLen > Integer.MAX_VALUE) {
+ // arrays beyond MAX_INT is not in Java in any case
+ throw new IllegalArgumentException();
+ }
+ int len = (int) longLen;
+ if (len == 0) {
+ return ScriptRuntime.emptyArgs;
+ } else {
+ Object[] result = new Object[len];
+ for (int i=0; i < len; i++) {
+ Object elem = ScriptableObject.getProperty(object, i);
+ result[i] = (elem == Scriptable.NOT_FOUND) ? Undefined.instance
+ : elem;
+ }
+ return result;
+ }
+ }
+
+ static void checkDeprecated(Context cx, String name) {
+ int version = cx.getLanguageVersion();
+ if (version >= Context.VERSION_1_4 || version == Context.VERSION_DEFAULT) {
+ String msg = getMessage1("msg.deprec.ctor", name);
+ if (version == Context.VERSION_DEFAULT)
+ Context.reportWarning(msg);
+ else
+ throw Context.reportRuntimeError(msg);
+ }
+ }
+
+ public static String getMessage0(String messageId)
+ {
+ return getMessage(messageId, null);
+ }
+
+ public static String getMessage1(String messageId, Object arg1)
+ {
+ Object[] arguments = {arg1};
+ return getMessage(messageId, arguments);
+ }
+
+ public static String getMessage2(
+ String messageId, Object arg1, Object arg2)
+ {
+ Object[] arguments = {arg1, arg2};
+ return getMessage(messageId, arguments);
+ }
+
+ public static String getMessage3(
+ String messageId, Object arg1, Object arg2, Object arg3)
+ {
+ Object[] arguments = {arg1, arg2, arg3};
+ return getMessage(messageId, arguments);
+ }
+
+ public static String getMessage4(
+ String messageId, Object arg1, Object arg2, Object arg3, Object arg4)
+ {
+ Object[] arguments = {arg1, arg2, arg3, arg4};
+ return getMessage(messageId, arguments);
+ }
+
+ /* OPT there's a noticable delay for the first error! Maybe it'd
+ * make sense to use a ListResourceBundle instead of a properties
+ * file to avoid (synchronized) text parsing.
+ */
+ public static String getMessage(String messageId, Object[] arguments)
+ {
+ final String defaultResource
+ = "org.mozilla.javascript.resources.Messages";
+
+ Context cx = Context.getCurrentContext();
+ Locale locale = cx != null ? cx.getLocale() : Locale.getDefault();
+
+ // ResourceBundle does cacheing.
+ ResourceBundle rb = ResourceBundle.getBundle(defaultResource, locale);
+
+ String formatString;
+ try {
+ formatString = rb.getString(messageId);
+ } catch (java.util.MissingResourceException mre) {
+ throw new RuntimeException
+ ("no message resource found for message property "+ messageId);
+ }
+
+ /*
+ * It's OK to format the string, even if 'arguments' is null;
+ * we need to format it anyway, to make double ''s collapse to
+ * single 's.
+ */
+ MessageFormat formatter = new MessageFormat(formatString);
+ return formatter.format(arguments);
+ }
+
+ public static EcmaError constructError(String error, String message)
+ {
+ int[] linep = new int[1];
+ String filename = Context.getSourcePositionFromStack(linep);
+ return constructError(error, message, filename, linep[0], null, 0);
+ }
+
+ public static EcmaError constructError(String error,
+ String message,
+ int lineNumberDelta)
+ {
+ int[] linep = new int[1];
+ String filename = Context.getSourcePositionFromStack(linep);
+ if (linep[0] != 0) {
+ linep[0] += lineNumberDelta;
+ }
+ return constructError(error, message, filename, linep[0], null, 0);
+ }
+
+ public static EcmaError constructError(String error,
+ String message,
+ String sourceName,
+ int lineNumber,
+ String lineSource,
+ int columnNumber)
+ {
+ return new EcmaError(error, message, sourceName,
+ lineNumber, lineSource, columnNumber);
+ }
+
+ public static EcmaError typeError(String message)
+ {
+ return constructError("TypeError", message);
+ }
+
+ public static EcmaError typeError0(String messageId)
+ {
+ String msg = getMessage0(messageId);
+ return typeError(msg);
+ }
+
+ public static EcmaError typeError1(String messageId, String arg1)
+ {
+ String msg = getMessage1(messageId, arg1);
+ return typeError(msg);
+ }
+
+ public static EcmaError typeError2(String messageId, String arg1,
+ String arg2)
+ {
+ String msg = getMessage2(messageId, arg1, arg2);
+ return typeError(msg);
+ }
+
+ public static EcmaError typeError3(String messageId, String arg1,
+ String arg2, String arg3)
+ {
+ String msg = getMessage3(messageId, arg1, arg2, arg3);
+ return typeError(msg);
+ }
+
+ public static RuntimeException undefReadError(Object object, Object id)
+ {
+ String idStr = (id == null) ? "null" : id.toString();
+ return typeError2("msg.undef.prop.read", toString(object), idStr);
+ }
+
+ public static RuntimeException undefCallError(Object object, Object id)
+ {
+ String idStr = (id == null) ? "null" : id.toString();
+ return typeError2("msg.undef.method.call", toString(object), idStr);
+ }
+
+ public static RuntimeException undefWriteError(Object object,
+ Object id,
+ Object value)
+ {
+ String idStr = (id == null) ? "null" : id.toString();
+ String valueStr = (value instanceof Scriptable)
+ ? value.toString() : toString(value);
+ return typeError3("msg.undef.prop.write", toString(object), idStr,
+ valueStr);
+ }
+
+ public static RuntimeException notFoundError(Scriptable object,
+ String property)
+ {
+ // XXX: use object to improve the error message
+ String msg = getMessage1("msg.is.not.defined", property);
+ throw constructError("ReferenceError", msg);
+ }
+
+ public static RuntimeException notFunctionError(Object value)
+ {
+ return notFunctionError(value, value);
+ }
+
+ public static RuntimeException notFunctionError(Object value,
+ Object messageHelper)
+ {
+ // Use value for better error reporting
+ String msg = (messageHelper == null)
+ ? "null" : messageHelper.toString();
+ if (value == Scriptable.NOT_FOUND) {
+ return typeError1("msg.function.not.found", msg);
+ }
+ return typeError2("msg.isnt.function", msg, typeof(value));
+ }
+
+ public static RuntimeException notFunctionError(Object obj, Object value,
+ String propertyName)
+ {
+ // Use obj and value for better error reporting
+ String objString = toString(obj);
+ if (value == Scriptable.NOT_FOUND) {
+ return typeError2("msg.function.not.found.in", propertyName,
+ objString);
+ }
+ return typeError3("msg.isnt.function.in", propertyName, objString,
+ typeof(value));
+ }
+
+ private static RuntimeException notXmlError(Object value)
+ {
+ throw typeError1("msg.isnt.xml.object", toString(value));
+ }
+
+ private static void warnAboutNonJSObject(Object nonJSObject)
+ {
+ String message =
+"RHINO USAGE WARNING: Missed Context.javaToJS() conversion:\n"
++"Rhino runtime detected object "+nonJSObject+" of class "+nonJSObject.getClass().getName()+" where it expected String, Number, Boolean or Scriptable instance. Please check your code for missing Context.javaToJS() call.";
+ Context.reportWarning(message);
+ // Just to be sure that it would be noticed
+ System.err.println(message);
+ }
+
+ public static RegExpProxy getRegExpProxy(Context cx)
+ {
+ return cx.getRegExpProxy();
+ }
+
+ public static void setRegExpProxy(Context cx, RegExpProxy proxy)
+ {
+ if (proxy == null) throw new IllegalArgumentException();
+ cx.regExpProxy = proxy;
+ }
+
+ public static RegExpProxy checkRegExpProxy(Context cx)
+ {
+ RegExpProxy result = getRegExpProxy(cx);
+ if (result == null) {
+ throw Context.reportRuntimeError0("msg.no.regexp");
+ }
+ return result;
+ }
+
+ private static XMLLib currentXMLLib(Context cx)
+ {
+ // Scripts should be running to access this
+ if (cx.topCallScope == null)
+ throw new IllegalStateException();
+
+ XMLLib xmlLib = cx.cachedXMLLib;
+ if (xmlLib == null) {
+ xmlLib = XMLLib.extractFromScope(cx.topCallScope);
+ if (xmlLib == null)
+ throw new IllegalStateException();
+ cx.cachedXMLLib = xmlLib;
+ }
+
+ return xmlLib;
+ }
+
+ /**
+ * Escapes the reserved characters in a value of an attribute
+ *
+ * @param value Unescaped text
+ * @return The escaped text
+ */
+ public static String escapeAttributeValue(Object value, Context cx)
+ {
+ XMLLib xmlLib = currentXMLLib(cx);
+ return xmlLib.escapeAttributeValue(value);
+ }
+
+ /**
+ * Escapes the reserved characters in a value of a text node
+ *
+ * @param value Unescaped text
+ * @return The escaped text
+ */
+ public static String escapeTextValue(Object value, Context cx)
+ {
+ XMLLib xmlLib = currentXMLLib(cx);
+ return xmlLib.escapeTextValue(value);
+ }
+
+ public static Ref memberRef(Object obj, Object elem,
+ Context cx, int memberTypeFlags)
+ {
+ if (!(obj instanceof XMLObject)) {
+ throw notXmlError(obj);
+ }
+ XMLObject xmlObject = (XMLObject)obj;
+ return xmlObject.memberRef(cx, elem, memberTypeFlags);
+ }
+
+ public static Ref memberRef(Object obj, Object namespace, Object elem,
+ Context cx, int memberTypeFlags)
+ {
+ if (!(obj instanceof XMLObject)) {
+ throw notXmlError(obj);
+ }
+ XMLObject xmlObject = (XMLObject)obj;
+ return xmlObject.memberRef(cx, namespace, elem, memberTypeFlags);
+ }
+
+ public static Ref nameRef(Object name, Context cx,
+ Scriptable scope, int memberTypeFlags)
+ {
+ XMLLib xmlLib = currentXMLLib(cx);
+ return xmlLib.nameRef(cx, name, scope, memberTypeFlags);
+ }
+
+ public static Ref nameRef(Object namespace, Object name, Context cx,
+ Scriptable scope, int memberTypeFlags)
+ {
+ XMLLib xmlLib = currentXMLLib(cx);
+ return xmlLib.nameRef(cx, namespace, name, scope, memberTypeFlags);
+ }
+
+ private static void storeIndexResult(Context cx, int index)
+ {
+ cx.scratchIndex = index;
+ }
+
+ static int lastIndexResult(Context cx)
+ {
+ return cx.scratchIndex;
+ }
+
+ public static void storeUint32Result(Context cx, long value)
+ {
+ if ((value >>> 32) != 0)
+ throw new IllegalArgumentException();
+ cx.scratchUint32 = value;
+ }
+
+ public static long lastUint32Result(Context cx)
+ {
+ long value = cx.scratchUint32;
+ if ((value >>> 32) != 0)
+ throw new IllegalStateException();
+ return value;
+ }
+
+ private static void storeScriptable(Context cx, Scriptable value)
+ {
+ // The previosly stored scratchScriptable should be consumed
+ if (cx.scratchScriptable != null)
+ throw new IllegalStateException();
+ cx.scratchScriptable = value;
+ }
+
+ public static Scriptable lastStoredScriptable(Context cx)
+ {
+ Scriptable result = cx.scratchScriptable;
+ cx.scratchScriptable = null;
+ return result;
+ }
+
+ static String makeUrlForGeneratedScript
+ (boolean isEval, String masterScriptUrl, int masterScriptLine)
+ {
+ if (isEval) {
+ return masterScriptUrl+'#'+masterScriptLine+"(eval)";
+ } else {
+ return masterScriptUrl+'#'+masterScriptLine+"(Function)";
+ }
+ }
+
+ static boolean isGeneratedScript(String sourceUrl) {
+ // ALERT: this may clash with a valid URL containing (eval) or
+ // (Function)
+ return sourceUrl.indexOf("(eval)") >= 0
+ || sourceUrl.indexOf("(Function)") >= 0;
+ }
+
+ private static RuntimeException errorWithClassName(String msg, Object val)
+ {
+ return Context.reportRuntimeError1(msg, val.getClass().getName());
+ }
+
+ public static final Object[] emptyArgs = new Object[0];
+ public static final String[] emptyStrings = new String[0];
+
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Scriptable.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Scriptable.java
new file mode 100644
index 0000000..74e5ba7
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Scriptable.java
@@ -0,0 +1,342 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// API class
+
+package org.mozilla.javascript;
+
+/**
+ * This is interface that all objects in JavaScript must implement.
+ * The interface provides for the management of properties and for
+ * performing conversions.
+ * <p>
+ * Host system implementors may find it easier to extend the ScriptableObject
+ * class rather than implementing Scriptable when writing host objects.
+ * <p>
+ * There are many static methods defined in ScriptableObject that perform
+ * the multiple calls to the Scriptable interface needed in order to
+ * manipulate properties in prototype chains.
+ * <p>
+ *
+ * @see org.mozilla.javascript.ScriptableObject
+ * @author Norris Boyd
+ * @author Nick Thompson
+ * @author Brendan Eich
+ */
+
+public interface Scriptable {
+
+ /**
+ * Get the name of the set of objects implemented by this Java class.
+ * This corresponds to the [[Class]] operation in ECMA and is used
+ * by Object.prototype.toString() in ECMA.<p>
+ * See ECMA 8.6.2 and 15.2.4.2.
+ */
+ public String getClassName();
+
+ /**
+ * Value returned from <code>get</code> if the property is not
+ * found.
+ */
+ public static final Object NOT_FOUND = UniqueTag.NOT_FOUND;
+
+ /**
+ * Get a named property from the object.
+ *
+ * Looks property up in this object and returns the associated value
+ * if found. Returns NOT_FOUND if not found.
+ * Note that this method is not expected to traverse the prototype
+ * chain. This is different from the ECMA [[Get]] operation.
+ *
+ * Depending on the property selector, the runtime will call
+ * this method or the form of <code>get</code> that takes an
+ * integer:
+ * <table>
+ * <tr><th>JavaScript code</th><th>Java code</th></tr>
+ * <tr><td>a.b </td><td>a.get("b", a)</td></tr>
+ * <tr><td>a["foo"] </td><td>a.get("foo", a)</td></tr>
+ * <tr><td>a[3] </td><td>a.get(3, a)</td></tr>
+ * <tr><td>a["3"] </td><td>a.get(3, a)</td></tr>
+ * <tr><td>a[3.0] </td><td>a.get(3, a)</td></tr>
+ * <tr><td>a["3.0"] </td><td>a.get("3.0", a)</td></tr>
+ * <tr><td>a[1.1] </td><td>a.get("1.1", a)</td></tr>
+ * <tr><td>a[-4] </td><td>a.get(-4, a)</td></tr>
+ * </table>
+ * <p>
+ * The values that may be returned are limited to the following:
+ * <UL>
+ * <LI>java.lang.Boolean objects</LI>
+ * <LI>java.lang.String objects</LI>
+ * <LI>java.lang.Number objects</LI>
+ * <LI>org.mozilla.javascript.Scriptable objects</LI>
+ * <LI>null</LI>
+ * <LI>The value returned by Context.getUndefinedValue()</LI>
+ * <LI>NOT_FOUND</LI>
+ * </UL>
+ * @param name the name of the property
+ * @param start the object in which the lookup began
+ * @return the value of the property (may be null), or NOT_FOUND
+ * @see org.mozilla.javascript.Context#getUndefinedValue
+ */
+ public Object get(String name, Scriptable start);
+
+ /**
+ * Get a property from the object selected by an integral index.
+ *
+ * Identical to <code>get(String, Scriptable)</code> except that
+ * an integral index is used to select the property.
+ *
+ * @param index the numeric index for the property
+ * @param start the object in which the lookup began
+ * @return the value of the property (may be null), or NOT_FOUND
+ * @see org.mozilla.javascript.Scriptable#get(String,Scriptable)
+ */
+ public Object get(int index, Scriptable start);
+
+ /**
+ * Indicates whether or not a named property is defined in an object.
+ *
+ * Does not traverse the prototype chain.<p>
+ *
+ * The property is specified by a String name
+ * as defined for the <code>get</code> method.<p>
+ *
+ * @param name the name of the property
+ * @param start the object in which the lookup began
+ * @return true if and only if the named property is found in the object
+ * @see org.mozilla.javascript.Scriptable#get(String, Scriptable)
+ * @see org.mozilla.javascript.ScriptableObject#getProperty(Scriptable, String)
+ */
+ public boolean has(String name, Scriptable start);
+
+ /**
+ * Indicates whether or not an indexed property is defined in an object.
+ *
+ * Does not traverse the prototype chain.<p>
+ *
+ * The property is specified by an integral index
+ * as defined for the <code>get</code> method.<p>
+ *
+ * @param index the numeric index for the property
+ * @param start the object in which the lookup began
+ * @return true if and only if the indexed property is found in the object
+ * @see org.mozilla.javascript.Scriptable#get(int, Scriptable)
+ * @see org.mozilla.javascript.ScriptableObject#getProperty(Scriptable, int)
+ */
+ public boolean has(int index, Scriptable start);
+
+ /**
+ * Sets a named property in this object.
+ * <p>
+ * The property is specified by a string name
+ * as defined for <code>get</code>.
+ * <p>
+ * The possible values that may be passed in are as defined for
+ * <code>get</code>. A class that implements this method may choose
+ * to ignore calls to set certain properties, in which case those
+ * properties are effectively read-only.<p>
+ * For properties defined in a prototype chain,
+ * use <code>putProperty</code> in ScriptableObject. <p>
+ * Note that if a property <i>a</i> is defined in the prototype <i>p</i>
+ * of an object <i>o</i>, then evaluating <code>o.a = 23</code> will cause
+ * <code>set</code> to be called on the prototype <i>p</i> with
+ * <i>o</i> as the <i>start</i> parameter.
+ * To preserve JavaScript semantics, it is the Scriptable
+ * object's responsibility to modify <i>o</i>. <p>
+ * 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.<p>
+ * <p>
+ * The values that may be set are limited to the following:
+ * <UL>
+ * <LI>java.lang.Boolean objects</LI>
+ * <LI>java.lang.String objects</LI>
+ * <LI>java.lang.Number objects</LI>
+ * <LI>org.mozilla.javascript.Scriptable objects</LI>
+ * <LI>null</LI>
+ * <LI>The value returned by Context.getUndefinedValue()</LI>
+ * </UL><p>
+ * Arbitrary Java objects may be wrapped in a Scriptable by first calling
+ * <code>Context.toObject</code>. This allows the property of a JavaScript
+ * object to contain an arbitrary Java object as a value.<p>
+ * Note that <code>has</code> will be called by the runtime first before
+ * <code>set</code> 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 put(String name, Scriptable start, Object value);
+
+ /**
+ * Sets an indexed property in this object.
+ * <p>
+ * The property is specified by an integral index
+ * as defined for <code>get</code>.<p>
+ *
+ * Identical to <code>put(String, Scriptable, Object)</code> except that
+ * an integral index is used to select the property.
+ *
+ * @param index the numeric index for 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(int, Scriptable)
+ * @see org.mozilla.javascript.Scriptable#get(int, Scriptable)
+ * @see org.mozilla.javascript.ScriptableObject#putProperty(Scriptable, int, Object)
+ * @see org.mozilla.javascript.Context#toObject(Object, Scriptable)
+ */
+ public void put(int index, Scriptable start, Object value);
+
+ /**
+ * Removes a property from this object.
+ * This operation corresponds to the ECMA [[Delete]] except that
+ * the no result is returned. The runtime will guarantee that this
+ * method is called only if the property exists. After this method
+ * is called, the runtime will call Scriptable.has to see if the
+ * property has been removed in order to determine the boolean
+ * result of the delete operator as defined by ECMA 11.4.1.
+ * <p>
+ * A property can be made permanent by ignoring calls to remove
+ * it.<p>
+ * The property is specified by a String name
+ * as defined for <code>get</code>.
+ * <p>
+ * To delete properties defined in a prototype chain,
+ * see deleteProperty in ScriptableObject.
+ * @param name the identifier for the property
+ * @see org.mozilla.javascript.Scriptable#get(String, Scriptable)
+ * @see org.mozilla.javascript.ScriptableObject#deleteProperty(Scriptable, String)
+ */
+ public void delete(String name);
+
+ /**
+ * Removes a property from this object.
+ *
+ * The property is specified by an integral index
+ * as defined for <code>get</code>.
+ * <p>
+ * To delete properties defined in a prototype chain,
+ * see deleteProperty in ScriptableObject.
+ *
+ * Identical to <code>delete(String)</code> except that
+ * an integral index is used to select the property.
+ *
+ * @param index the numeric index for the property
+ * @see org.mozilla.javascript.Scriptable#get(int, Scriptable)
+ * @see org.mozilla.javascript.ScriptableObject#deleteProperty(Scriptable, int)
+ */
+ public void delete(int index);
+
+ /**
+ * Get the prototype of the object.
+ * @return the prototype
+ */
+ public Scriptable getPrototype();
+
+ /**
+ * Set the prototype of the object.
+ * @param prototype the prototype to set
+ */
+ public void setPrototype(Scriptable prototype);
+
+ /**
+ * Get the parent scope of the object.
+ * @return the parent scope
+ */
+ public Scriptable getParentScope();
+
+ /**
+ * Set the parent scope of the object.
+ * @param parent the parent scope to set
+ */
+ public void setParentScope(Scriptable parent);
+
+ /**
+ * Get an array of property ids.
+ *
+ * Not all property ids need be returned. Those properties
+ * whose ids are not returned are considered non-enumerable.
+ *
+ * @return an array of Objects. Each entry in the array is either
+ * a java.lang.String or a java.lang.Number
+ */
+ public Object[] getIds();
+
+ /**
+ * Get the default value of the object with a given hint.
+ * The hints are String.class for type String, Number.class for type
+ * Number, Scriptable.class for type Object, and Boolean.class for
+ * type Boolean. <p>
+ *
+ * A <code>hint</code> of null means "no hint".
+ *
+ * See ECMA 8.6.2.6.
+ *
+ * @param hint the type hint
+ * @return the default value
+ */
+ public Object getDefaultValue(Class hint);
+
+ /**
+ * The instanceof operator.
+ *
+ * <p>
+ * The JavaScript code "lhs instanceof rhs" causes rhs.hasInstance(lhs) to
+ * be called.
+ *
+ * <p>
+ * The return value is implementation dependent so that embedded host objects can
+ * return an appropriate value. See the JS 1.3 language documentation for more
+ * detail.
+ *
+ * <p>This operator corresponds to the proposed EMCA [[HasInstance]] operator.
+ *
+ * @param instance The value that appeared on the LHS of the instanceof
+ * operator
+ *
+ * @return an implementation dependent value
+ */
+ public boolean hasInstance(Scriptable instance);
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptableObject.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptableObject.java
new file mode 100644
index 0000000..53de1fc
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptableObject.java
@@ -0,0 +1,2428 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ * Bob Jervis
+ * Roger Lawrence
+ * Steve Weiss
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// API class
+
+package org.mozilla.javascript;
+
+import java.lang.reflect.*;
+import java.util.Hashtable;
+import java.io.*;
+import org.mozilla.javascript.debug.DebuggableObject;
+
+/**
+ * This is the default implementation of the Scriptable interface. This
+ * class provides convenient default behavior that makes it easier to
+ * define host objects.
+ * <p>
+ * Various properties and methods of JavaScript objects can be conveniently
+ * defined using methods of ScriptableObject.
+ * <p>
+ * Classes extending ScriptableObject must define the getClassName method.
+ *
+ * @see org.mozilla.javascript.Scriptable
+ * @author Norris Boyd
+ */
+
+public abstract class ScriptableObject implements Scriptable, Serializable,
+ DebuggableObject,
+ ConstProperties
+{
+
+ /**
+ * The empty property attribute.
+ *
+ * Used by getAttributes() and setAttributes().
+ *
+ * @see org.mozilla.javascript.ScriptableObject#getAttributes(String)
+ * @see org.mozilla.javascript.ScriptableObject#setAttributes(String, int)
+ */
+ public static final int EMPTY = 0x00;
+
+ /**
+ * Property attribute indicating assignment to this property is ignored.
+ *
+ * @see org.mozilla.javascript.ScriptableObject
+ * #put(String, Scriptable, Object)
+ * @see org.mozilla.javascript.ScriptableObject#getAttributes(String)
+ * @see org.mozilla.javascript.ScriptableObject#setAttributes(String, int)
+ */
+ public static final int READONLY = 0x01;
+
+ /**
+ * Property attribute indicating property is not enumerated.
+ *
+ * Only enumerated properties will be returned by getIds().
+ *
+ * @see org.mozilla.javascript.ScriptableObject#getIds()
+ * @see org.mozilla.javascript.ScriptableObject#getAttributes(String)
+ * @see org.mozilla.javascript.ScriptableObject#setAttributes(String, int)
+ */
+ public static final int DONTENUM = 0x02;
+
+ /**
+ * Property attribute indicating property cannot be deleted.
+ *
+ * @see org.mozilla.javascript.ScriptableObject#delete(String)
+ * @see org.mozilla.javascript.ScriptableObject#getAttributes(String)
+ * @see org.mozilla.javascript.ScriptableObject#setAttributes(String, int)
+ */
+ public static final int PERMANENT = 0x04;
+
+ /**
+ * Property attribute indicating that this is a const property that has not
+ * been assigned yet. The first 'const' assignment to the property will
+ * clear this bit.
+ */
+ public static final int UNINITIALIZED_CONST = 0x08;
+
+ public static final int CONST = PERMANENT|READONLY|UNINITIALIZED_CONST;
+ /**
+ * The prototype of this object.
+ */
+ private Scriptable prototypeObject;
+
+ /**
+ * The parent scope of this object.
+ */
+ private Scriptable parentScopeObject;
+
+ private static final Slot REMOVED = new Slot(null, 0, READONLY);
+
+ static {
+ REMOVED.wasDeleted = 1;
+ }
+
+ private transient Slot[] slots;
+ // If count >= 0, it gives number of keys or if count < 0,
+ // it indicates sealed object where ~count gives number of keys
+ private int count;
+
+ // cache; may be removed for smaller memory footprint
+ private transient Slot lastAccess = REMOVED;
+
+ // associated values are not serialized
+ private transient volatile Hashtable associatedValues;
+
+ private static final int SLOT_QUERY = 1;
+ private static final int SLOT_MODIFY = 2;
+ private static final int SLOT_REMOVE = 3;
+ private static final int SLOT_MODIFY_GETTER_SETTER = 4;
+ private static final int SLOT_MODIFY_CONST = 5;
+
+ private static class Slot implements Serializable
+ {
+ static final long serialVersionUID = -3539051633409902634L;
+
+ String name; // This can change due to caching
+ int indexOrHash;
+ private volatile short attributes;
+ transient volatile byte wasDeleted;
+ volatile Object value;
+ transient volatile Slot next;
+
+ Slot(String name, int indexOrHash, int attributes)
+ {
+ this.name = name;
+ this.indexOrHash = indexOrHash;
+ this.attributes = (short)attributes;
+ }
+
+ private void readObject(ObjectInputStream in)
+ throws IOException, ClassNotFoundException
+ {
+ in.defaultReadObject();
+ if (name != null) {
+ indexOrHash = name.hashCode();
+ }
+ }
+
+ final int getAttributes()
+ {
+ return attributes;
+ }
+
+ final synchronized void setAttributes(int value)
+ {
+ checkValidAttributes(value);
+ attributes = (short)value;
+ }
+
+ final void checkNotReadonly()
+ {
+ if ((attributes & READONLY) != 0) {
+ String str = (name != null ? name
+ : Integer.toString(indexOrHash));
+ throw Context.reportRuntimeError1("msg.modify.readonly", str);
+ }
+ }
+
+ }
+
+ private static final class GetterSlot extends Slot
+ {
+ static final long serialVersionUID = -4900574849788797588L;
+
+ Object getter;
+ Object setter;
+
+ GetterSlot(String name, int indexOrHash, int attributes)
+ {
+ super(name, indexOrHash, attributes);
+ }
+ }
+
+ static void checkValidAttributes(int attributes)
+ {
+ final int mask = READONLY | DONTENUM | PERMANENT | UNINITIALIZED_CONST;
+ if ((attributes & ~mask) != 0) {
+ throw new IllegalArgumentException(String.valueOf(attributes));
+ }
+ }
+
+ public ScriptableObject()
+ {
+ }
+
+ public ScriptableObject(Scriptable scope, Scriptable prototype)
+ {
+ if (scope == null)
+ throw new IllegalArgumentException();
+
+ parentScopeObject = scope;
+ prototypeObject = prototype;
+ }
+
+ /**
+ * Return the name of the class.
+ *
+ * This is typically the same name as the constructor.
+ * Classes extending ScriptableObject must implement this abstract
+ * method.
+ */
+ public abstract String getClassName();
+
+ /**
+ * Returns true if the named property is defined.
+ *
+ * @param name the name of the property
+ * @param start the object in which the lookup began
+ * @return true if and only if the property was found in the object
+ */
+ public boolean has(String name, Scriptable start)
+ {
+ return null != getSlot(name, 0, SLOT_QUERY);
+ }
+
+ /**
+ * Returns true if the property index is defined.
+ *
+ * @param index the numeric index for the property
+ * @param start the object in which the lookup began
+ * @return true if and only if the property was found in the object
+ */
+ public boolean has(int index, Scriptable start)
+ {
+ return null != getSlot(null, index, SLOT_QUERY);
+ }
+
+ /**
+ * Returns the value of the named property or NOT_FOUND.
+ *
+ * If the property was created using defineProperty, the
+ * appropriate getter method is called.
+ *
+ * @param name the name of the property
+ * @param start the object in which the lookup began
+ * @return the value of the property (may be null), or NOT_FOUND
+ */
+ public Object get(String name, Scriptable start)
+ {
+ return getImpl(name, 0, start);
+ }
+
+ /**
+ * Returns the value of the indexed property or NOT_FOUND.
+ *
+ * @param index the numeric index for the property
+ * @param start the object in which the lookup began
+ * @return the value of the property (may be null), or NOT_FOUND
+ */
+ public Object get(int index, Scriptable start)
+ {
+ return getImpl(null, index, start);
+ }
+
+ /**
+ * Sets the value of the named property, creating it if need be.
+ *
+ * If the property was created using defineProperty, the
+ * appropriate setter method is called. <p>
+ *
+ * If the property's attributes include READONLY, no action is
+ * taken.
+ * This method will actually set the property in the start
+ * object.
+ *
+ * @param name the name of the property
+ * @param start the object whose property is being set
+ * @param value value to set the property to
+ */
+ public void put(String name, Scriptable start, Object value)
+ {
+ if (putImpl(name, 0, start, value, EMPTY))
+ return;
+
+ if (start == this) throw Kit.codeBug();
+ start.put(name, start, value);
+ }
+
+ /**
+ * Sets the value of the indexed property, creating it if need be.
+ *
+ * @param index the numeric index for the property
+ * @param start the object whose property is being set
+ * @param value value to set the property to
+ */
+ public void put(int index, Scriptable start, Object value)
+ {
+ if (putImpl(null, index, start, value, EMPTY))
+ return;
+
+ if (start == this) throw Kit.codeBug();
+ start.put(index, start, value);
+ }
+
+ /**
+ * Removes a named property from the object.
+ *
+ * If the property is not found, or it has the PERMANENT attribute,
+ * no action is taken.
+ *
+ * @param name the name of the property
+ */
+ public void delete(String name)
+ {
+ checkNotSealed(name, 0);
+ accessSlot(name, 0, SLOT_REMOVE);
+ }
+
+ /**
+ * Removes the indexed property from the object.
+ *
+ * If the property is not found, or it has the PERMANENT attribute,
+ * no action is taken.
+ *
+ * @param index the numeric index for the property
+ */
+ public void delete(int index)
+ {
+ checkNotSealed(null, index);
+ accessSlot(null, index, SLOT_REMOVE);
+ }
+
+ /**
+ * Sets the value of the named const property, creating it if need be.
+ *
+ * If the property was created using defineProperty, the
+ * appropriate setter method is called. <p>
+ *
+ * If the property's attributes include READONLY, no action is
+ * taken.
+ * This method will actually set the property in the start
+ * object.
+ *
+ * @param name the name of the property
+ * @param start the object whose property is being set
+ * @param value value to set the property to
+ */
+ public void putConst(String name, Scriptable start, Object value)
+ {
+ if (putImpl(name, 0, start, value, READONLY))
+ return;
+
+ if (start == this) throw Kit.codeBug();
+ if (start instanceof ConstProperties)
+ ((ConstProperties)start).putConst(name, start, value);
+ else
+ start.put(name, start, value);
+ }
+
+ public void defineConst(String name, Scriptable start)
+ {
+ if (putImpl(name, 0, start, Undefined.instance, UNINITIALIZED_CONST))
+ return;
+
+ if (start == this) throw Kit.codeBug();
+ if (start instanceof ConstProperties)
+ ((ConstProperties)start).defineConst(name, 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)
+ {
+ Slot slot = getSlot(name, 0, SLOT_QUERY);
+ if (slot == null) {
+ return false;
+ }
+ return (slot.getAttributes() & (PERMANENT|READONLY)) ==
+ (PERMANENT|READONLY);
+
+ }
+ /**
+ * @deprecated Use {@link #getAttributes(String name)}. The engine always
+ * ignored the start argument.
+ */
+ public final int getAttributes(String name, Scriptable start)
+ {
+ return getAttributes(name);
+ }
+
+ /**
+ * @deprecated Use {@link #getAttributes(int index)}. The engine always
+ * ignored the start argument.
+ */
+ public final int getAttributes(int index, Scriptable start)
+ {
+ return getAttributes(index);
+ }
+
+ /**
+ * @deprecated Use {@link #setAttributes(String name, int attributes)}.
+ * The engine always ignored the start argument.
+ */
+ public final void setAttributes(String name, Scriptable start,
+ int attributes)
+ {
+ setAttributes(name, attributes);
+ }
+
+ /**
+ * @deprecated Use {@link #setAttributes(int index, int attributes)}.
+ * The engine always ignored the start argument.
+ */
+ public void setAttributes(int index, Scriptable start,
+ int attributes)
+ {
+ setAttributes(index, attributes);
+ }
+
+ /**
+ * Get the attributes of a named property.
+ *
+ * The property is specified by <code>name</code>
+ * as defined for <code>has</code>.<p>
+ *
+ * @param name the identifier for the property
+ * @return the bitset of attributes
+ * @exception EvaluatorException if the named property is not found
+ * @see org.mozilla.javascript.ScriptableObject#has(String, Scriptable)
+ * @see org.mozilla.javascript.ScriptableObject#READONLY
+ * @see org.mozilla.javascript.ScriptableObject#DONTENUM
+ * @see org.mozilla.javascript.ScriptableObject#PERMANENT
+ * @see org.mozilla.javascript.ScriptableObject#EMPTY
+ */
+ public int getAttributes(String name)
+ {
+ return findAttributeSlot(name, 0, SLOT_QUERY).getAttributes();
+ }
+
+ /**
+ * Get the attributes of an indexed property.
+ *
+ * @param index the numeric index for the property
+ * @exception EvaluatorException if the named property is not found
+ * is not found
+ * @return the bitset of attributes
+ * @see org.mozilla.javascript.ScriptableObject#has(String, Scriptable)
+ * @see org.mozilla.javascript.ScriptableObject#READONLY
+ * @see org.mozilla.javascript.ScriptableObject#DONTENUM
+ * @see org.mozilla.javascript.ScriptableObject#PERMANENT
+ * @see org.mozilla.javascript.ScriptableObject#EMPTY
+ */
+ public int getAttributes(int index)
+ {
+ return findAttributeSlot(null, index, SLOT_QUERY).getAttributes();
+ }
+
+ /**
+ * Set the attributes of a named property.
+ *
+ * The property is specified by <code>name</code>
+ * as defined for <code>has</code>.<p>
+ *
+ * The possible attributes are READONLY, DONTENUM,
+ * and PERMANENT. Combinations of attributes
+ * are expressed by the bitwise OR of attributes.
+ * EMPTY is the state of no attributes set. Any unused
+ * bits are reserved for future use.
+ *
+ * @param name the name of the property
+ * @param attributes the bitset of attributes
+ * @exception EvaluatorException if the named property is not found
+ * @see org.mozilla.javascript.Scriptable#has(String, Scriptable)
+ * @see org.mozilla.javascript.ScriptableObject#READONLY
+ * @see org.mozilla.javascript.ScriptableObject#DONTENUM
+ * @see org.mozilla.javascript.ScriptableObject#PERMANENT
+ * @see org.mozilla.javascript.ScriptableObject#EMPTY
+ */
+ public void setAttributes(String name, int attributes)
+ {
+ checkNotSealed(name, 0);
+ findAttributeSlot(name, 0, SLOT_MODIFY).setAttributes(attributes);
+ }
+
+ /**
+ * Set the attributes of an indexed property.
+ *
+ * @param index the numeric index for the property
+ * @param attributes the bitset of attributes
+ * @exception EvaluatorException if the named property is not found
+ * @see org.mozilla.javascript.Scriptable#has(String, Scriptable)
+ * @see org.mozilla.javascript.ScriptableObject#READONLY
+ * @see org.mozilla.javascript.ScriptableObject#DONTENUM
+ * @see org.mozilla.javascript.ScriptableObject#PERMANENT
+ * @see org.mozilla.javascript.ScriptableObject#EMPTY
+ */
+ public void setAttributes(int index, int attributes)
+ {
+ checkNotSealed(null, index);
+ findAttributeSlot(null, index, SLOT_MODIFY).setAttributes(attributes);
+ }
+
+ /**
+ * XXX: write docs.
+ */
+ public void setGetterOrSetter(String name, int index,
+ Callable getterOrSeter, boolean isSetter)
+ {
+ if (name != null && index != 0)
+ throw new IllegalArgumentException(name);
+
+ checkNotSealed(name, index);
+ GetterSlot gslot = (GetterSlot)getSlot(name, index,
+ SLOT_MODIFY_GETTER_SETTER);
+ gslot.checkNotReadonly();
+ if (isSetter) {
+ gslot.setter = getterOrSeter;
+ } else {
+ gslot.getter = getterOrSeter;
+ }
+ gslot.value = Undefined.instance;
+ }
+
+ /**
+ * Get the getter or setter for a given property. Used by __lookupGetter__
+ * and __lookupSetter__.
+ *
+ * @param name Name of the object. If nonnull, index must be 0.
+ * @param index Index of the object. If nonzero, name must be null.
+ * @param isSetter If true, return the setter, otherwise return the getter.
+ * @exception IllegalArgumentException if both name and index are nonnull
+ * and nonzero respectively.
+ * @return Null if the property does not exist. Otherwise returns either
+ * the getter or the setter for the property, depending on
+ * the value of isSetter (may be undefined if unset).
+ */
+ public Object getGetterOrSetter(String name, int index, boolean isSetter)
+ {
+ if (name != null && index != 0)
+ throw new IllegalArgumentException(name);
+ Slot slot = getSlot(name, index, SLOT_QUERY);
+ if (slot == null)
+ return null;
+ if (slot instanceof GetterSlot) {
+ GetterSlot gslot = (GetterSlot)slot;
+ Object result = isSetter ? gslot.setter : gslot.getter;
+ return result != null ? result : Undefined.instance;
+ } else
+ return Undefined.instance;
+ }
+
+ /**
+ * Returns whether a property is a getter or a setter
+ * @param name property name
+ * @param index property index
+ * @param setter true to check for a setter, false for a getter
+ * @return whether the property is a getter or a setter
+ */
+ protected boolean isGetterOrSetter(String name, int index, boolean setter) {
+ Slot slot = getSlot(name, index, SLOT_QUERY);
+ if (slot instanceof GetterSlot) {
+ if (setter && ((GetterSlot)slot).setter != null) return true;
+ if (!setter && ((GetterSlot)slot).getter != null) return true;
+ }
+ return false;
+ }
+
+ void addLazilyInitializedValue(String name, int index,
+ LazilyLoadedCtor init, int attributes)
+ {
+ if (name != null && index != 0)
+ throw new IllegalArgumentException(name);
+ checkNotSealed(name, index);
+ GetterSlot gslot = (GetterSlot)getSlot(name, index,
+ SLOT_MODIFY_GETTER_SETTER);
+ gslot.setAttributes(attributes);
+ gslot.getter = null;
+ gslot.setter = null;
+ gslot.value = init;
+ }
+
+ /**
+ * Returns the prototype of the object.
+ */
+ public Scriptable getPrototype()
+ {
+ return prototypeObject;
+ }
+
+ /**
+ * Sets the prototype of the object.
+ */
+ public void setPrototype(Scriptable m)
+ {
+ prototypeObject = m;
+ }
+
+ /**
+ * Returns the parent (enclosing) scope of the object.
+ */
+ public Scriptable getParentScope()
+ {
+ return parentScopeObject;
+ }
+
+ /**
+ * Sets the parent (enclosing) scope of the object.
+ */
+ public void setParentScope(Scriptable m)
+ {
+ parentScopeObject = m;
+ }
+
+ /**
+ * Returns an array of ids for the properties of the object.
+ *
+ * <p>Any properties with the attribute DONTENUM are not listed. <p>
+ *
+ * @return an array of java.lang.Objects with an entry for every
+ * listed property. Properties accessed via an integer index will
+ * have a corresponding
+ * Integer entry in the returned array. Properties accessed by
+ * a String will have a String entry in the returned array.
+ */
+ public Object[] getIds() {
+ return getIds(false);
+ }
+
+ /**
+ * Returns an array of ids for the properties of the object.
+ *
+ * <p>All properties, even those with attribute DONTENUM, are listed. <p>
+ *
+ * @return an array of java.lang.Objects with an entry for every
+ * listed property. Properties accessed via an integer index will
+ * have a corresponding
+ * Integer entry in the returned array. Properties accessed by
+ * a String will have a String entry in the returned array.
+ */
+ public Object[] getAllIds() {
+ return getIds(true);
+ }
+
+ /**
+ * Implements the [[DefaultValue]] internal method.
+ *
+ * <p>Note that the toPrimitive conversion is a no-op for
+ * every type other than Object, for which [[DefaultValue]]
+ * is called. See ECMA 9.1.<p>
+ *
+ * A <code>hint</code> of null means "no hint".
+ *
+ * @param typeHint the type hint
+ * @return the default value for the object
+ *
+ * See ECMA 8.6.2.6.
+ */
+ public Object getDefaultValue(Class typeHint)
+ {
+ return getDefaultValue(this, typeHint);
+ }
+
+ public static Object getDefaultValue(Scriptable object, Class typeHint)
+ {
+ Context cx = null;
+ for (int i=0; i < 2; i++) {
+ boolean tryToString;
+ if (typeHint == ScriptRuntime.StringClass) {
+ tryToString = (i == 0);
+ } else {
+ tryToString = (i == 1);
+ }
+
+ String methodName;
+ Object[] args;
+ if (tryToString) {
+ methodName = "toString";
+ args = ScriptRuntime.emptyArgs;
+ } else {
+ methodName = "valueOf";
+ args = new Object[1];
+ String hint;
+ if (typeHint == null) {
+ hint = "undefined";
+ } else if (typeHint == ScriptRuntime.StringClass) {
+ hint = "string";
+ } else if (typeHint == ScriptRuntime.ScriptableClass) {
+ hint = "object";
+ } else if (typeHint == ScriptRuntime.FunctionClass) {
+ hint = "function";
+ } else if (typeHint == ScriptRuntime.BooleanClass
+ || typeHint == Boolean.TYPE)
+ {
+ hint = "boolean";
+ } else if (typeHint == ScriptRuntime.NumberClass ||
+ typeHint == ScriptRuntime.ByteClass ||
+ typeHint == Byte.TYPE ||
+ typeHint == ScriptRuntime.ShortClass ||
+ typeHint == Short.TYPE ||
+ typeHint == ScriptRuntime.IntegerClass ||
+ typeHint == Integer.TYPE ||
+ typeHint == ScriptRuntime.FloatClass ||
+ typeHint == Float.TYPE ||
+ typeHint == ScriptRuntime.DoubleClass ||
+ typeHint == Double.TYPE)
+ {
+ hint = "number";
+ } else {
+ throw Context.reportRuntimeError1(
+ "msg.invalid.type", typeHint.toString());
+ }
+ args[0] = hint;
+ }
+ Object v = getProperty(object, methodName);
+ if (!(v instanceof Function))
+ continue;
+ Function fun = (Function) v;
+ if (cx == null)
+ cx = Context.getContext();
+ v = fun.call(cx, fun.getParentScope(), object, args);
+ if (v != null) {
+ if (!(v instanceof Scriptable)) {
+ return v;
+ }
+ if (typeHint == ScriptRuntime.ScriptableClass
+ || typeHint == ScriptRuntime.FunctionClass)
+ {
+ return v;
+ }
+ if (tryToString && v instanceof Wrapper) {
+ // Let a wrapped java.lang.String pass for a primitive
+ // string.
+ Object u = ((Wrapper)v).unwrap();
+ if (u instanceof String)
+ return u;
+ }
+ }
+ }
+ // fall through to error
+ String arg = (typeHint == null) ? "undefined" : typeHint.getName();
+ throw ScriptRuntime.typeError1("msg.default.value", arg);
+ }
+
+ /**
+ * Implements the instanceof operator.
+ *
+ * <p>This operator has been proposed to ECMA.
+ *
+ * @param instance The value that appeared on the LHS of the instanceof
+ * operator
+ * @return true if "this" appears in value's prototype chain
+ *
+ */
+ public boolean hasInstance(Scriptable instance) {
+ // Default for JS objects (other than Function) is to do prototype
+ // chasing. This will be overridden in NativeFunction and non-JS
+ // objects.
+
+ return ScriptRuntime.jsDelegatesTo(instance, this);
+ }
+
+ /**
+ * Emulate the SpiderMonkey (and Firefox) feature of allowing
+ * custom objects to avoid detection by normal "object detection"
+ * code patterns. This is used to implement document.all.
+ * See https://bugzilla.mozilla.org/show_bug.cgi?id=412247.
+ * This is an analog to JOF_DETECTING from SpiderMonkey; see
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=248549.
+ * Other than this special case, embeddings should return false.
+ * @return true if this object should avoid object detection
+ * @since 1.7R1
+ */
+ public boolean avoidObjectDetection() {
+ return false;
+ }
+
+ /**
+ * Custom <tt>==</tt> operator.
+ * Must return {@link Scriptable#NOT_FOUND} if this object does not
+ * have custom equality operator for the given value,
+ * <tt>Boolean.TRUE</tt> if this object is equivalent to <tt>value</tt>,
+ * <tt>Boolean.FALSE</tt> if this object is not equivalent to
+ * <tt>value</tt>.
+ * <p>
+ * The default implementation returns Boolean.TRUE
+ * if <tt>this == value</tt> or {@link Scriptable#NOT_FOUND} otherwise.
+ * It indicates that by default custom equality is available only if
+ * <tt>value</tt> is <tt>this</tt> in which case true is returned.
+ */
+ protected Object equivalentValues(Object value)
+ {
+ return (this == value) ? Boolean.TRUE : Scriptable.NOT_FOUND;
+ }
+
+ /**
+ * Defines JavaScript objects from a Java class that implements Scriptable.
+ *
+ * If the given class has a method
+ * <pre>
+ * static void init(Context cx, Scriptable scope, boolean sealed);</pre>
+ *
+ * or its compatibility form
+ * <pre>
+ * static void init(Scriptable scope);</pre>
+ *
+ * then it is invoked and no further initialization is done.<p>
+ *
+ * However, if no such a method is found, then the class's constructors and
+ * methods are used to initialize a class in the following manner.<p>
+ *
+ * First, the zero-parameter constructor of the class is called to
+ * create the prototype. If no such constructor exists,
+ * a {@link EvaluatorException} is thrown. <p>
+ *
+ * Next, all methods are scanned for special prefixes that indicate that they
+ * have special meaning for defining JavaScript objects.
+ * These special prefixes are
+ * <ul>
+ * <li><code>jsFunction_</code> for a JavaScript function
+ * <li><code>jsStaticFunction_</code> for a JavaScript function that
+ * is a property of the constructor
+ * <li><code>jsGet_</code> for a getter of a JavaScript property
+ * <li><code>jsSet_</code> for a setter of a JavaScript property
+ * <li><code>jsConstructor</code> for a JavaScript function that
+ * is the constructor
+ * </ul><p>
+ *
+ * If the method's name begins with "jsFunction_", a JavaScript function
+ * is created with a name formed from the rest of the Java method name
+ * following "jsFunction_". So a Java method named "jsFunction_foo" will
+ * define a JavaScript method "foo". Calling this JavaScript function
+ * will cause the Java method to be called. The parameters of the method
+ * must be of number and types as defined by the FunctionObject class.
+ * The JavaScript function is then added as a property
+ * of the prototype. <p>
+ *
+ * If the method's name begins with "jsStaticFunction_", it is handled
+ * similarly except that the resulting JavaScript function is added as a
+ * property of the constructor object. The Java method must be static.
+ *
+ * If the method's name begins with "jsGet_" or "jsSet_", the method is
+ * considered to define a property. Accesses to the defined property
+ * will result in calls to these getter and setter methods. If no
+ * setter is defined, the property is defined as READONLY.<p>
+ *
+ * If the method's name is "jsConstructor", the method is
+ * considered to define the body of the constructor. Only one
+ * method of this name may be defined.
+ * If no method is found that can serve as constructor, a Java
+ * constructor will be selected to serve as the JavaScript
+ * constructor in the following manner. If the class has only one
+ * Java constructor, that constructor is used to define
+ * the JavaScript constructor. If the the class has two constructors,
+ * one must be the zero-argument constructor (otherwise an
+ * {@link EvaluatorException} would have already been thrown
+ * when the prototype was to be created). In this case
+ * the Java constructor with one or more parameters will be used
+ * to define the JavaScript constructor. If the class has three
+ * or more constructors, an {@link EvaluatorException}
+ * will be thrown.<p>
+ *
+ * Finally, if there is a method
+ * <pre>
+ * static void finishInit(Scriptable scope, FunctionObject constructor,
+ * Scriptable prototype)</pre>
+ *
+ * it will be called to finish any initialization. The <code>scope</code>
+ * argument will be passed, along with the newly created constructor and
+ * the newly created prototype.<p>
+ *
+ * @param scope The scope in which to define the constructor.
+ * @param clazz The Java class to use to define the JavaScript objects
+ * and properties.
+ * @exception IllegalAccessException if access is not available
+ * to a reflected class member
+ * @exception InstantiationException if unable to instantiate
+ * the named class
+ * @exception InvocationTargetException if an exception is thrown
+ * during execution of methods of the named class
+ * @see org.mozilla.javascript.Function
+ * @see org.mozilla.javascript.FunctionObject
+ * @see org.mozilla.javascript.ScriptableObject#READONLY
+ * @see org.mozilla.javascript.ScriptableObject
+ * #defineProperty(String, Class, int)
+ */
+ public static void defineClass(Scriptable scope, Class clazz)
+ throws IllegalAccessException, InstantiationException,
+ InvocationTargetException
+ {
+ defineClass(scope, clazz, false, false);
+ }
+
+ /**
+ * Defines JavaScript objects from a Java class, optionally
+ * allowing sealing.
+ *
+ * Similar to <code>defineClass(Scriptable scope, Class clazz)</code>
+ * except that sealing is allowed. An object that is sealed cannot have
+ * properties added or removed. Note that sealing is not allowed in
+ * the current ECMA/ISO language specification, but is likely for
+ * the next version.
+ *
+ * @param scope The scope in which to define the constructor.
+ * @param clazz The Java class to use to define the JavaScript objects
+ * and properties. The class must implement Scriptable.
+ * @param sealed Whether or not to create sealed standard objects that
+ * cannot be modified.
+ * @exception IllegalAccessException if access is not available
+ * to a reflected class member
+ * @exception InstantiationException if unable to instantiate
+ * the named class
+ * @exception InvocationTargetException if an exception is thrown
+ * during execution of methods of the named class
+ * @since 1.4R3
+ */
+ public static void defineClass(Scriptable scope, Class clazz,
+ boolean sealed)
+ throws IllegalAccessException, InstantiationException,
+ InvocationTargetException
+ {
+ defineClass(scope, clazz, sealed, false);
+ }
+
+ /**
+ * Defines JavaScript objects from a Java class, optionally
+ * allowing sealing and mapping of Java inheritance to JavaScript
+ * prototype-based inheritance.
+ *
+ * Similar to <code>defineClass(Scriptable scope, Class clazz)</code>
+ * except that sealing and inheritance mapping are allowed. An object
+ * that is sealed cannot have properties added or removed. Note that
+ * sealing is not allowed in the current ECMA/ISO language specification,
+ * but is likely for the next version.
+ *
+ * @param scope The scope in which to define the constructor.
+ * @param clazz The Java class to use to define the JavaScript objects
+ * and properties. The class must implement Scriptable.
+ * @param sealed Whether or not to create sealed standard objects that
+ * cannot be modified.
+ * @param mapInheritance Whether or not to map Java inheritance to
+ * JavaScript prototype-based inheritance.
+ * @return the class name for the prototype of the specified class
+ * @exception IllegalAccessException if access is not available
+ * to a reflected class member
+ * @exception InstantiationException if unable to instantiate
+ * the named class
+ * @exception InvocationTargetException if an exception is thrown
+ * during execution of methods of the named class
+ * @since 1.6R2
+ */
+ public static String defineClass(Scriptable scope, Class clazz,
+ boolean sealed, boolean mapInheritance)
+ throws IllegalAccessException, InstantiationException,
+ InvocationTargetException
+ {
+ BaseFunction ctor = buildClassCtor(scope, clazz, sealed,
+ mapInheritance);
+ if (ctor == null)
+ return null;
+ String name = ctor.getClassPrototype().getClassName();
+ defineProperty(scope, name, ctor, ScriptableObject.DONTENUM);
+ return name;
+ }
+
+ static BaseFunction buildClassCtor(Scriptable scope, Class clazz,
+ boolean sealed,
+ boolean mapInheritance)
+ throws IllegalAccessException, InstantiationException,
+ InvocationTargetException
+ {
+ Method[] methods = FunctionObject.getMethodList(clazz);
+ for (int i=0; i < methods.length; i++) {
+ Method method = methods[i];
+ if (!method.getName().equals("init"))
+ continue;
+ Class[] parmTypes = method.getParameterTypes();
+ if (parmTypes.length == 3 &&
+ parmTypes[0] == ScriptRuntime.ContextClass &&
+ parmTypes[1] == ScriptRuntime.ScriptableClass &&
+ parmTypes[2] == Boolean.TYPE &&
+ Modifier.isStatic(method.getModifiers()))
+ {
+ Object args[] = { Context.getContext(), scope,
+ sealed ? Boolean.TRUE : Boolean.FALSE };
+ method.invoke(null, args);
+ return null;
+ }
+ if (parmTypes.length == 1 &&
+ parmTypes[0] == ScriptRuntime.ScriptableClass &&
+ Modifier.isStatic(method.getModifiers()))
+ {
+ Object args[] = { scope };
+ method.invoke(null, args);
+ return null;
+ }
+
+ }
+
+ // If we got here, there isn't an "init" method with the right
+ // parameter types.
+
+ Constructor[] ctors = clazz.getConstructors();
+ Constructor protoCtor = null;
+ for (int i=0; i < ctors.length; i++) {
+ if (ctors[i].getParameterTypes().length == 0) {
+ protoCtor = ctors[i];
+ break;
+ }
+ }
+ if (protoCtor == null) {
+ throw Context.reportRuntimeError1(
+ "msg.zero.arg.ctor", clazz.getName());
+ }
+
+ Scriptable proto = (Scriptable) protoCtor.newInstance(ScriptRuntime.emptyArgs);
+ String className = proto.getClassName();
+
+ // Set the prototype's prototype, trying to map Java inheritance to JS
+ // prototype-based inheritance if requested to do so.
+ Scriptable superProto = null;
+ if (mapInheritance) {
+ Class superClass = clazz.getSuperclass();
+ if (ScriptRuntime.ScriptableClass.isAssignableFrom(superClass)
+ && !Modifier.isAbstract(superClass.getModifiers())) {
+ String name = ScriptableObject.defineClass(scope, superClass, sealed, mapInheritance);
+ if (name != null) {
+ superProto = ScriptableObject.getClassPrototype(scope, name);
+ }
+ }
+ }
+ if (superProto == null) {
+ superProto = ScriptableObject.getObjectPrototype(scope);
+ }
+ proto.setPrototype(superProto);
+
+ // Find out whether there are any methods that begin with
+ // "js". If so, then only methods that begin with special
+ // prefixes will be defined as JavaScript entities.
+ final String functionPrefix = "jsFunction_";
+ final String staticFunctionPrefix = "jsStaticFunction_";
+ final String getterPrefix = "jsGet_";
+ final String setterPrefix = "jsSet_";
+ final String ctorName = "jsConstructor";
+
+ Member ctorMember = FunctionObject.findSingleMethod(methods, ctorName);
+
+ if (ctorMember == null) {
+ if (ctors.length == 1) {
+ ctorMember = ctors[0];
+ } else if (ctors.length == 2) {
+ if (ctors[0].getParameterTypes().length == 0)
+ ctorMember = ctors[1];
+ else if (ctors[1].getParameterTypes().length == 0)
+ ctorMember = ctors[0];
+ }
+ if (ctorMember == null) {
+ throw Context.reportRuntimeError1(
+ "msg.ctor.multiple.parms", clazz.getName());
+ }
+ }
+
+ FunctionObject ctor = new FunctionObject(className, ctorMember, scope);
+ if (ctor.isVarArgsMethod()) {
+ throw Context.reportRuntimeError1
+ ("msg.varargs.ctor", ctorMember.getName());
+ }
+ ctor.initAsConstructor(scope, proto);
+
+ Method finishInit = null;
+ for (int i=0; i < methods.length; i++) {
+ if (methods[i] == ctorMember) {
+ continue;
+ }
+ String name = methods[i].getName();
+ if (name.equals("finishInit")) {
+ Class[] parmTypes = methods[i].getParameterTypes();
+ if (parmTypes.length == 3 &&
+ parmTypes[0] == ScriptRuntime.ScriptableClass &&
+ parmTypes[1] == FunctionObject.class &&
+ parmTypes[2] == ScriptRuntime.ScriptableClass &&
+ Modifier.isStatic(methods[i].getModifiers()))
+ {
+ finishInit = methods[i];
+ continue;
+ }
+ }
+ // ignore any compiler generated methods.
+ if (name.indexOf('$') != -1)
+ continue;
+ if (name.equals(ctorName))
+ continue;
+
+ String prefix = null;
+ if (name.startsWith(functionPrefix)) {
+ prefix = functionPrefix;
+ } else if (name.startsWith(staticFunctionPrefix)) {
+ prefix = staticFunctionPrefix;
+ if (!Modifier.isStatic(methods[i].getModifiers())) {
+ throw Context.reportRuntimeError(
+ "jsStaticFunction must be used with static method.");
+ }
+ } else if (name.startsWith(getterPrefix)) {
+ prefix = getterPrefix;
+ } else if (name.startsWith(setterPrefix)) {
+ prefix = setterPrefix;
+ } else {
+ continue;
+ }
+ name = name.substring(prefix.length());
+ if (prefix == setterPrefix)
+ continue; // deal with set when we see get
+ if (prefix == getterPrefix) {
+ if (!(proto instanceof ScriptableObject)) {
+ throw Context.reportRuntimeError2(
+ "msg.extend.scriptable",
+ proto.getClass().toString(), name);
+ }
+ Method setter = FunctionObject.findSingleMethod(
+ methods,
+ setterPrefix + name);
+ int attr = ScriptableObject.PERMANENT |
+ ScriptableObject.DONTENUM |
+ (setter != null ? 0
+ : ScriptableObject.READONLY);
+ ((ScriptableObject) proto).defineProperty(name, null,
+ methods[i], setter,
+ attr);
+ continue;
+ }
+
+ FunctionObject f = new FunctionObject(name, methods[i], proto);
+ if (f.isVarArgsConstructor()) {
+ throw Context.reportRuntimeError1
+ ("msg.varargs.fun", ctorMember.getName());
+ }
+ Scriptable dest = prefix == staticFunctionPrefix
+ ? ctor
+ : proto;
+ defineProperty(dest, name, f, DONTENUM);
+ if (sealed) {
+ f.sealObject();
+ }
+ }
+
+ // Call user code to complete initialization if necessary.
+ if (finishInit != null) {
+ Object[] finishArgs = { scope, ctor, proto };
+ finishInit.invoke(null, finishArgs);
+ }
+
+ // Seal the object if necessary.
+ if (sealed) {
+ ctor.sealObject();
+ if (proto instanceof ScriptableObject) {
+ ((ScriptableObject) proto).sealObject();
+ }
+ }
+
+ return ctor;
+ }
+
+ /**
+ * Define a JavaScript property.
+ *
+ * Creates the property with an initial value and sets its attributes.
+ *
+ * @param propertyName the name of the property to define.
+ * @param value the initial value of the property
+ * @param attributes the attributes of the JavaScript property
+ * @see org.mozilla.javascript.Scriptable#put(String, Scriptable, Object)
+ */
+ public void defineProperty(String propertyName, Object value,
+ int attributes)
+ {
+ checkNotSealed(propertyName, 0);
+ put(propertyName, this, value);
+ setAttributes(propertyName, attributes);
+ }
+
+ /**
+ * Utility method to add properties to arbitrary Scriptable object.
+ * If destination is instance of ScriptableObject, calls
+ * defineProperty there, otherwise calls put in destination
+ * ignoring attributes
+ */
+ public static void defineProperty(Scriptable destination,
+ String propertyName, Object value,
+ int attributes)
+ {
+ if (!(destination instanceof ScriptableObject)) {
+ destination.put(propertyName, destination, value);
+ return;
+ }
+ ScriptableObject so = (ScriptableObject)destination;
+ so.defineProperty(propertyName, value, attributes);
+ }
+
+ /**
+ * Utility method to add properties to arbitrary Scriptable object.
+ * If destination is instance of ScriptableObject, calls
+ * defineProperty there, otherwise calls put in destination
+ * ignoring attributes
+ */
+ public static void defineConstProperty(Scriptable destination,
+ String propertyName)
+ {
+ if (destination instanceof ConstProperties) {
+ ConstProperties cp = (ConstProperties)destination;
+ cp.defineConst(propertyName, destination);
+ } else
+ defineProperty(destination, propertyName, Undefined.instance, CONST);
+ }
+
+ /**
+ * Define a JavaScript property with getter and setter side effects.
+ *
+ * If the setter is not found, the attribute READONLY is added to
+ * the given attributes. <p>
+ *
+ * The getter must be a method with zero parameters, and the setter, if
+ * found, must be a method with one parameter.<p>
+ *
+ * @param propertyName the name of the property to define. This name
+ * also affects the name of the setter and getter
+ * to search for. If the propertyId is "foo", then
+ * <code>clazz</code> will be searched for "getFoo"
+ * and "setFoo" methods.
+ * @param clazz the Java class to search for the getter and setter
+ * @param attributes the attributes of the JavaScript property
+ * @see org.mozilla.javascript.Scriptable#put(String, Scriptable, Object)
+ */
+ public void defineProperty(String propertyName, Class clazz,
+ int attributes)
+ {
+ int length = propertyName.length();
+ if (length == 0) throw new IllegalArgumentException();
+ char[] buf = new char[3 + length];
+ propertyName.getChars(0, length, buf, 3);
+ buf[3] = Character.toUpperCase(buf[3]);
+ buf[0] = 'g';
+ buf[1] = 'e';
+ buf[2] = 't';
+ String getterName = new String(buf);
+ buf[0] = 's';
+ String setterName = new String(buf);
+
+ Method[] methods = FunctionObject.getMethodList(clazz);
+ Method getter = FunctionObject.findSingleMethod(methods, getterName);
+ Method setter = FunctionObject.findSingleMethod(methods, setterName);
+ if (setter == null)
+ attributes |= ScriptableObject.READONLY;
+ defineProperty(propertyName, null, getter,
+ setter == null ? null : setter, attributes);
+ }
+
+ /**
+ * Define a JavaScript property.
+ *
+ * Use this method only if you wish to define getters and setters for
+ * a given property in a ScriptableObject. To create a property without
+ * special getter or setter side effects, use
+ * <code>defineProperty(String,int)</code>.
+ *
+ * If <code>setter</code> is null, the attribute READONLY is added to
+ * the given attributes.<p>
+ *
+ * Several forms of getters or setters are allowed. In all cases the
+ * type of the value parameter can be any one of the following types:
+ * Object, String, boolean, Scriptable, byte, short, int, long, float,
+ * or double. The runtime will perform appropriate conversions based
+ * upon the type of the parameter (see description in FunctionObject).
+ * The first forms are nonstatic methods of the class referred to
+ * by 'this':
+ * <pre>
+ * Object getFoo();
+ * void setFoo(SomeType value);</pre>
+ * Next are static methods that may be of any class; the object whose
+ * property is being accessed is passed in as an extra argument:
+ * <pre>
+ * static Object getFoo(Scriptable obj);
+ * static void setFoo(Scriptable obj, SomeType value);</pre>
+ * Finally, it is possible to delegate to another object entirely using
+ * the <code>delegateTo</code> parameter. In this case the methods are
+ * nonstatic methods of the class delegated to, and the object whose
+ * property is being accessed is passed in as an extra argument:
+ * <pre>
+ * Object getFoo(Scriptable obj);
+ * void setFoo(Scriptable obj, SomeType value);</pre>
+ *
+ * @param propertyName the name of the property to define.
+ * @param delegateTo an object to call the getter and setter methods on,
+ * or null, depending on the form used above.
+ * @param getter the method to invoke to get the value of the property
+ * @param setter the method to invoke to set the value of the property
+ * @param attributes the attributes of the JavaScript property
+ */
+ public void defineProperty(String propertyName, Object delegateTo,
+ Method getter, Method setter, int attributes)
+ {
+ MemberBox getterBox = null;
+ if (getter != null) {
+ getterBox = new MemberBox(getter);
+
+ boolean delegatedForm;
+ if (!Modifier.isStatic(getter.getModifiers())) {
+ delegatedForm = (delegateTo != null);
+ getterBox.delegateTo = delegateTo;
+ } else {
+ delegatedForm = true;
+ // Ignore delegateTo for static getter but store
+ // non-null delegateTo indicator.
+ getterBox.delegateTo = Void.TYPE;
+ }
+
+ String errorId = null;
+ Class[] parmTypes = getter.getParameterTypes();
+ if (parmTypes.length == 0) {
+ if (delegatedForm) {
+ errorId = "msg.obj.getter.parms";
+ }
+ } else if (parmTypes.length == 1) {
+ Object argType = parmTypes[0];
+ // Allow ScriptableObject for compatibility
+ if (!(argType == ScriptRuntime.ScriptableClass ||
+ argType == ScriptRuntime.ScriptableObjectClass))
+ {
+ errorId = "msg.bad.getter.parms";
+ } else if (!delegatedForm) {
+ errorId = "msg.bad.getter.parms";
+ }
+ } else {
+ errorId = "msg.bad.getter.parms";
+ }
+ if (errorId != null) {
+ throw Context.reportRuntimeError1(errorId, getter.toString());
+ }
+ }
+
+ MemberBox setterBox = null;
+ if (setter != null) {
+ if (setter.getReturnType() != Void.TYPE)
+ throw Context.reportRuntimeError1("msg.setter.return",
+ setter.toString());
+
+ setterBox = new MemberBox(setter);
+
+ boolean delegatedForm;
+ if (!Modifier.isStatic(setter.getModifiers())) {
+ delegatedForm = (delegateTo != null);
+ setterBox.delegateTo = delegateTo;
+ } else {
+ delegatedForm = true;
+ // Ignore delegateTo for static setter but store
+ // non-null delegateTo indicator.
+ setterBox.delegateTo = Void.TYPE;
+ }
+
+ String errorId = null;
+ Class[] parmTypes = setter.getParameterTypes();
+ if (parmTypes.length == 1) {
+ if (delegatedForm) {
+ errorId = "msg.setter2.expected";
+ }
+ } else if (parmTypes.length == 2) {
+ Object argType = parmTypes[0];
+ // Allow ScriptableObject for compatibility
+ if (!(argType == ScriptRuntime.ScriptableClass ||
+ argType == ScriptRuntime.ScriptableObjectClass))
+ {
+ errorId = "msg.setter2.parms";
+ } else if (!delegatedForm) {
+ errorId = "msg.setter1.parms";
+ }
+ } else {
+ errorId = "msg.setter.parms";
+ }
+ if (errorId != null) {
+ throw Context.reportRuntimeError1(errorId, setter.toString());
+ }
+ }
+
+ GetterSlot gslot = (GetterSlot)getSlot(propertyName, 0,
+ SLOT_MODIFY_GETTER_SETTER);
+ gslot.setAttributes(attributes);
+ gslot.getter = getterBox;
+ gslot.setter = setterBox;
+ }
+
+ /**
+ * Search for names in a class, adding the resulting methods
+ * as properties.
+ *
+ * <p> Uses reflection to find the methods of the given names. Then
+ * FunctionObjects are constructed from the methods found, and
+ * are added to this object as properties with the given names.
+ *
+ * @param names the names of the Methods to add as function properties
+ * @param clazz the class to search for the Methods
+ * @param attributes the attributes of the new properties
+ * @see org.mozilla.javascript.FunctionObject
+ */
+ public void defineFunctionProperties(String[] names, Class clazz,
+ int attributes)
+ {
+ Method[] methods = FunctionObject.getMethodList(clazz);
+ for (int i=0; i < names.length; i++) {
+ String name = names[i];
+ Method m = FunctionObject.findSingleMethod(methods, name);
+ if (m == null) {
+ throw Context.reportRuntimeError2(
+ "msg.method.not.found", name, clazz.getName());
+ }
+ FunctionObject f = new FunctionObject(name, m, this);
+ defineProperty(name, f, attributes);
+ }
+ }
+
+ /**
+ * Get the Object.prototype property.
+ * See ECMA 15.2.4.
+ */
+ public static Scriptable getObjectPrototype(Scriptable scope) {
+ return getClassPrototype(scope, "Object");
+ }
+
+ /**
+ * Get the Function.prototype property.
+ * See ECMA 15.3.4.
+ */
+ public static Scriptable getFunctionPrototype(Scriptable scope) {
+ return getClassPrototype(scope, "Function");
+ }
+
+ /**
+ * Get the prototype for the named class.
+ *
+ * For example, <code>getClassPrototype(s, "Date")</code> will first
+ * walk up the parent chain to find the outermost scope, then will
+ * search that scope for the Date constructor, and then will
+ * return Date.prototype. If any of the lookups fail, or
+ * the prototype is not a JavaScript object, then null will
+ * be returned.
+ *
+ * @param scope an object in the scope chain
+ * @param className the name of the constructor
+ * @return the prototype for the named class, or null if it
+ * cannot be found.
+ */
+ public static Scriptable getClassPrototype(Scriptable scope,
+ String className)
+ {
+ scope = getTopLevelScope(scope);
+ Object ctor = getProperty(scope, className);
+ Object proto;
+ if (ctor instanceof BaseFunction) {
+ proto = ((BaseFunction)ctor).getPrototypeProperty();
+ } else if (ctor instanceof Scriptable) {
+ Scriptable ctorObj = (Scriptable)ctor;
+ proto = ctorObj.get("prototype", ctorObj);
+ } else {
+ return null;
+ }
+ if (proto instanceof Scriptable) {
+ return (Scriptable)proto;
+ }
+ return null;
+ }
+
+ /**
+ * Get the global scope.
+ *
+ * <p>Walks the parent scope chain to find an object with a null
+ * parent scope (the global object).
+ *
+ * @param obj a JavaScript object
+ * @return the corresponding global scope
+ */
+ public static Scriptable getTopLevelScope(Scriptable obj)
+ {
+ for (;;) {
+ Scriptable parent = obj.getParentScope();
+ if (parent == null) {
+ return obj;
+ }
+ obj = parent;
+ }
+ }
+
+ // APPJET
+ public static Scriptable getVeryTopLevelScope(Scriptable obj) {
+ return ScriptRuntime.getLibraryScopeOrNull(obj);
+ }
+
+ /**
+ * Seal this object.
+ *
+ * A sealed object may not have properties added or removed. Once
+ * an object is sealed it may not be unsealed.
+ *
+ * @since 1.4R3
+ */
+ public synchronized void sealObject() {
+ if (count >= 0) {
+ count = ~count;
+ }
+ }
+
+ /**
+ * Return true if this object is sealed.
+ *
+ * It is an error to attempt to add or remove properties to
+ * a sealed object.
+ *
+ * @return true if sealed, false otherwise.
+ * @since 1.4R3
+ */
+ public final boolean isSealed() {
+ return count < 0;
+ }
+
+ private void checkNotSealed(String name, int index)
+ {
+ if (!isSealed())
+ return;
+
+ String str = (name != null) ? name : Integer.toString(index);
+ throw Context.reportRuntimeError1("msg.modify.sealed", str);
+ }
+
+ /**
+ * Gets a named property from an object or any object in its prototype chain.
+ * <p>
+ * Searches the prototype chain for a property named <code>name</code>.
+ * <p>
+ * @param obj a JavaScript object
+ * @param name a property name
+ * @return the value of a property with name <code>name</code> found in
+ * <code>obj</code> or any object in its prototype chain, or
+ * <code>Scriptable.NOT_FOUND</code> if not found
+ * @since 1.5R2
+ */
+ public static Object getProperty(Scriptable obj, String name)
+ {
+ Scriptable start = obj;
+ Object result;
+ do {
+ result = obj.get(name, start);
+ if (result != Scriptable.NOT_FOUND)
+ break;
+ obj = obj.getPrototype();
+ } while (obj != null);
+ return result;
+ }
+
+ /**
+ * Gets an indexed property from an object or any object in its prototype chain.
+ * <p>
+ * Searches the prototype chain for a property with integral index
+ * <code>index</code>. Note that if you wish to look for properties with numerical
+ * but non-integral indicies, you should use getProperty(Scriptable,String) with
+ * the string value of the index.
+ * <p>
+ * @param obj a JavaScript object
+ * @param index an integral index
+ * @return the value of a property with index <code>index</code> found in
+ * <code>obj</code> or any object in its prototype chain, or
+ * <code>Scriptable.NOT_FOUND</code> if not found
+ * @since 1.5R2
+ */
+ public static Object getProperty(Scriptable obj, int index)
+ {
+ Scriptable start = obj;
+ Object result;
+ do {
+ result = obj.get(index, start);
+ if (result != Scriptable.NOT_FOUND)
+ break;
+ obj = obj.getPrototype();
+ } while (obj != null);
+ return result;
+ }
+
+ /**
+ * Returns whether a named property is defined in an object or any object
+ * in its prototype chain.
+ * <p>
+ * Searches the prototype chain for a property named <code>name</code>.
+ * <p>
+ * @param obj a JavaScript object
+ * @param name a property name
+ * @return the true if property was found
+ * @since 1.5R2
+ */
+ public static boolean hasProperty(Scriptable obj, String name)
+ {
+ return null != getBase(obj, name);
+ }
+
+ /**
+ * If hasProperty(obj, name) would return true, then if the property that
+ * was found is compatible with the new property, this method just returns.
+ * If the property is not compatible, then an exception is thrown.
+ *
+ * A property redefinition is incompatible if the first definition was a
+ * const declaration or if this one is. They are compatible only if neither
+ * was const.
+ */
+ public static void redefineProperty(Scriptable obj, String name,
+ boolean isConst)
+ {
+ Scriptable base = getBase(obj, name);
+ if (base == null)
+ return;
+ if (base instanceof ConstProperties) {
+ ConstProperties cp = (ConstProperties)base;
+
+ if (cp.isConst(name))
+ throw Context.reportRuntimeError1("msg.const.redecl", name);
+ }
+ if (isConst)
+ throw Context.reportRuntimeError1("msg.var.redecl", name);
+ }
+ /**
+ * Returns whether an indexed property is defined in an object or any object
+ * in its prototype chain.
+ * <p>
+ * Searches the prototype chain for a property with index <code>index</code>.
+ * <p>
+ * @param obj a JavaScript object
+ * @param index a property index
+ * @return the true if property was found
+ * @since 1.5R2
+ */
+ public static boolean hasProperty(Scriptable obj, int index)
+ {
+ return null != getBase(obj, index);
+ }
+
+ /**
+ * Puts a named property in an object or in an object in its prototype chain.
+ * <p>
+ * Searches for the named property in the prototype chain. If it is found,
+ * the value of the property in <code>obj</code> is changed through a call
+ * to {@link Scriptable#put(String, Scriptable, Object)} on the
+ * prototype passing <code>obj</code> as the <code>start</code> argument.
+ * This allows the prototype to veto the property setting in case the
+ * prototype defines the property with [[ReadOnly]] attribute. If the
+ * property is not found, it is added in <code>obj</code>.
+ * @param obj a JavaScript object
+ * @param name a property name
+ * @param value any JavaScript value accepted by Scriptable.put
+ * @since 1.5R2
+ */
+ public static void putProperty(Scriptable obj, String name, Object value)
+ {
+ Scriptable base = getBase(obj, name);
+ if (base == null)
+ base = obj;
+ base.put(name, obj, value);
+ }
+
+ /**
+ * Puts a named property in an object or in an object in its prototype chain.
+ * <p>
+ * Searches for the named property in the prototype chain. If it is found,
+ * the value of the property in <code>obj</code> is changed through a call
+ * to {@link Scriptable#put(String, Scriptable, Object)} on the
+ * prototype passing <code>obj</code> as the <code>start</code> argument.
+ * This allows the prototype to veto the property setting in case the
+ * prototype defines the property with [[ReadOnly]] attribute. If the
+ * property is not found, it is added in <code>obj</code>.
+ * @param obj a JavaScript object
+ * @param name a property name
+ * @param value any JavaScript value accepted by Scriptable.put
+ * @since 1.5R2
+ */
+ public static void putConstProperty(Scriptable obj, String name, Object value)
+ {
+ Scriptable base = getBase(obj, name);
+ if (base == null)
+ base = obj;
+ if (base instanceof ConstProperties)
+ ((ConstProperties)base).putConst(name, obj, value);
+ }
+
+ /**
+ * Puts an indexed property in an object or in an object in its prototype chain.
+ * <p>
+ * Searches for the indexed property in the prototype chain. If it is found,
+ * the value of the property in <code>obj</code> is changed through a call
+ * to {@link Scriptable#put(int, Scriptable, Object)} on the prototype
+ * passing <code>obj</code> as the <code>start</code> argument. This allows
+ * the prototype to veto the property setting in case the prototype defines
+ * the property with [[ReadOnly]] attribute. If the property is not found,
+ * it is added in <code>obj</code>.
+ * @param obj a JavaScript object
+ * @param index a property index
+ * @param value any JavaScript value accepted by Scriptable.put
+ * @since 1.5R2
+ */
+ public static void putProperty(Scriptable obj, int index, Object value)
+ {
+ Scriptable base = getBase(obj, index);
+ if (base == null)
+ base = obj;
+ base.put(index, obj, value);
+ }
+
+ /**
+ * Removes the property from an object or its prototype chain.
+ * <p>
+ * Searches for a property with <code>name</code> in obj or
+ * its prototype chain. If it is found, the object's delete
+ * method is called.
+ * @param obj a JavaScript object
+ * @param name a property name
+ * @return true if the property doesn't exist or was successfully removed
+ * @since 1.5R2
+ */
+ public static boolean deleteProperty(Scriptable obj, String name)
+ {
+ Scriptable base = getBase(obj, name);
+ if (base == null)
+ return true;
+ base.delete(name);
+ return !base.has(name, obj);
+ }
+
+ /**
+ * Removes the property from an object or its prototype chain.
+ * <p>
+ * Searches for a property with <code>index</code> in obj or
+ * its prototype chain. If it is found, the object's delete
+ * method is called.
+ * @param obj a JavaScript object
+ * @param index a property index
+ * @return true if the property doesn't exist or was successfully removed
+ * @since 1.5R2
+ */
+ public static boolean deleteProperty(Scriptable obj, int index)
+ {
+ Scriptable base = getBase(obj, index);
+ if (base == null)
+ return true;
+ base.delete(index);
+ return !base.has(index, obj);
+ }
+
+ /**
+ * Returns an array of all ids from an object and its prototypes.
+ * <p>
+ * @param obj a JavaScript object
+ * @return an array of all ids from all object in the prototype chain.
+ * If a given id occurs multiple times in the prototype chain,
+ * it will occur only once in this list.
+ * @since 1.5R2
+ */
+ public static Object[] getPropertyIds(Scriptable obj)
+ {
+ if (obj == null) {
+ return ScriptRuntime.emptyArgs;
+ }
+ Object[] result = obj.getIds();
+ ObjToIntMap map = null;
+ for (;;) {
+ obj = obj.getPrototype();
+ if (obj == null) {
+ break;
+ }
+ Object[] ids = obj.getIds();
+ if (ids.length == 0) {
+ continue;
+ }
+ if (map == null) {
+ if (result.length == 0) {
+ result = ids;
+ continue;
+ }
+ map = new ObjToIntMap(result.length + ids.length);
+ for (int i = 0; i != result.length; ++i) {
+ map.intern(result[i]);
+ }
+ result = null; // Allow to GC the result
+ }
+ for (int i = 0; i != ids.length; ++i) {
+ map.intern(ids[i]);
+ }
+ }
+ if (map != null) {
+ result = map.getKeys();
+ }
+ return result;
+ }
+
+ /**
+ * Call a method of an object.
+ * @param obj the JavaScript object
+ * @param methodName the name of the function property
+ * @param args the arguments for the call
+ *
+ * @see Context#getCurrentContext()
+ */
+ public static Object callMethod(Scriptable obj, String methodName,
+ Object[] args)
+ {
+ return callMethod(null, obj, methodName, args);
+ }
+
+ /**
+ * Call a method of an object.
+ * @param cx the Context object associated with the current thread.
+ * @param obj the JavaScript object
+ * @param methodName the name of the function property
+ * @param args the arguments for the call
+ */
+ public static Object callMethod(Context cx, Scriptable obj,
+ String methodName,
+ Object[] args)
+ {
+ Object funObj = getProperty(obj, methodName);
+ if (!(funObj instanceof Function)) {
+ throw ScriptRuntime.notFunctionError(obj, methodName);
+ }
+ Function fun = (Function)funObj;
+ // XXX: What should be the scope when calling funObj?
+ // The following favor scope stored in the object on the assumption
+ // that is more useful especially under dynamic scope setup.
+ // An alternative is to check for dynamic scope flag
+ // and use ScriptableObject.getTopLevelScope(fun) if the flag is not
+ // set. But that require access to Context and messy code
+ // so for now it is not checked.
+ Scriptable scope = ScriptableObject.getTopLevelScope(obj);
+ if (cx != null) {
+ return fun.call(cx, scope, obj, args);
+ } else {
+ return Context.call(null, fun, scope, obj, args);
+ }
+ }
+
+ private static Scriptable getBase(Scriptable obj, String name)
+ {
+ do {
+ if (obj.has(name, obj))
+ break;
+ obj = obj.getPrototype();
+ } while(obj != null);
+ return obj;
+ }
+
+ private static Scriptable getBase(Scriptable obj, int index)
+ {
+ do {
+ if (obj.has(index, obj))
+ break;
+ obj = obj.getPrototype();
+ } while(obj != null);
+ return obj;
+ }
+
+ /**
+ * Get arbitrary application-specific value associated with this object.
+ * @param key key object to select particular value.
+ * @see #associateValue(Object key, Object value)
+ */
+ public final Object getAssociatedValue(Object key)
+ {
+ Hashtable h = associatedValues;
+ if (h == null)
+ return null;
+ return h.get(key);
+ }
+
+ /**
+ * Get arbitrary application-specific value associated with the top scope
+ * of the given scope.
+ * The method first calls {@link #getTopLevelScope(Scriptable scope)}
+ * and then searches the prototype chain of the top scope for the first
+ * object containing the associated value with the given key.
+ *
+ * @param scope the starting scope.
+ * @param key key object to select particular value.
+ * @see #getAssociatedValue(Object key)
+ */
+ public static Object getTopScopeValue(Scriptable scope, Object key)
+ {
+ scope = ScriptableObject.getTopLevelScope(scope);
+ for (;;) {
+ if (scope instanceof ScriptableObject) {
+ ScriptableObject so = (ScriptableObject)scope;
+ Object value = so.getAssociatedValue(key);
+ if (value != null) {
+ return value;
+ }
+ }
+ scope = scope.getPrototype();
+ if (scope == null) {
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Associate arbitrary application-specific value with this object.
+ * Value can only be associated with the given object and key only once.
+ * The method ignores any subsequent attempts to change the already
+ * associated value.
+ * <p> The associated values are not serialized.
+ * @param key key object to select particular value.
+ * @param value the value to associate
+ * @return the passed value if the method is called first time for the
+ * given key or old value for any subsequent calls.
+ * @see #getAssociatedValue(Object key)
+ */
+ public final Object associateValue(Object key, Object value)
+ {
+ if (value == null) throw new IllegalArgumentException();
+ Hashtable h = associatedValues;
+ if (h == null) {
+ synchronized (this) {
+ h = associatedValues;
+ if (h == null) {
+ h = new Hashtable();
+ associatedValues = h;
+ }
+ }
+ }
+ return Kit.initHash(h, key, value);
+ }
+
+ private Object getImpl(String name, int index, Scriptable start)
+ {
+ Slot slot = getSlot(name, index, SLOT_QUERY);
+ if (slot == null) {
+ return Scriptable.NOT_FOUND;
+ }
+ if (!(slot instanceof GetterSlot)) {
+ return slot.value;
+ }
+ Object getterObj = ((GetterSlot)slot).getter;
+ if (getterObj != null) {
+ if (getterObj instanceof MemberBox) {
+ MemberBox nativeGetter = (MemberBox)getterObj;
+ Object getterThis;
+ Object[] args;
+ if (nativeGetter.delegateTo == null) {
+ getterThis = start;
+ args = ScriptRuntime.emptyArgs;
+ } else {
+ getterThis = nativeGetter.delegateTo;
+ args = new Object[] { start };
+ }
+ return nativeGetter.invoke(getterThis, args);
+ } else {
+ Function f = (Function)getterObj;
+ Context cx = Context.getContext();
+ return f.call(cx, f.getParentScope(), start,
+ ScriptRuntime.emptyArgs);
+ }
+ }
+ Object value = slot.value;
+ if (value instanceof LazilyLoadedCtor) {
+ LazilyLoadedCtor initializer = (LazilyLoadedCtor)value;
+ try {
+ initializer.init();
+ } finally {
+ value = initializer.getValue();
+ slot.value = value;
+ }
+ }
+ return value;
+ }
+
+ /**
+ *
+ * @param name
+ * @param index
+ * @param start
+ * @param value
+ * @param constFlag EMPTY means normal put. UNINITIALIZED_CONST means
+ * defineConstProperty. READONLY means const initialization expression.
+ * @return false if this != start and no slot was found. true if this == start
+ * or this != start and a READONLY slot was found.
+ */
+ private boolean putImpl(String name, int index, Scriptable start,
+ Object value, int constFlag)
+ {
+ Slot slot;
+ if (this != start) {
+ slot = getSlot(name, index, SLOT_QUERY);
+ if (slot == null) {
+ return false;
+ }
+ } else {
+ checkNotSealed(name, index);
+ // either const hoisted declaration or initialization
+ if (constFlag != EMPTY) {
+ slot = getSlot(name, index, SLOT_MODIFY_CONST);
+ int attr = slot.getAttributes();
+ if ((attr & READONLY) == 0)
+ throw Context.reportRuntimeError1("msg.var.redecl", name);
+ if ((attr & UNINITIALIZED_CONST) != 0) {
+ slot.value = value;
+ // clear the bit on const initialization
+ if (constFlag != UNINITIALIZED_CONST)
+ slot.setAttributes(attr & ~UNINITIALIZED_CONST);
+ }
+ return true;
+ }
+ slot = getSlot(name, index, SLOT_MODIFY);
+ }
+ if ((slot.getAttributes() & READONLY) != 0)
+ return true;
+ if (slot instanceof GetterSlot) {
+ Object setterObj = ((GetterSlot)slot).setter;
+ if (setterObj != null) {
+ Context cx = Context.getContext();
+ if (setterObj instanceof MemberBox) {
+ MemberBox nativeSetter = (MemberBox)setterObj;
+ Class pTypes[] = nativeSetter.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);
+ Object setterThis;
+ Object[] args;
+ if (nativeSetter.delegateTo == null) {
+ setterThis = start;
+ args = new Object[] { actualArg };
+ } else {
+ setterThis = nativeSetter.delegateTo;
+ args = new Object[] { start, actualArg };
+ }
+ nativeSetter.invoke(setterThis, args);
+ } else {
+ Function f = (Function)setterObj;
+ f.call(cx, f.getParentScope(), start,
+ new Object[] { value });
+ }
+ return true;
+ }
+ }
+ if (this == start) {
+ slot.value = value;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private Slot findAttributeSlot(String name, int index, int accessType)
+ {
+ Slot slot = getSlot(name, index, accessType);
+ if (slot == null) {
+ String str = (name != null ? name : Integer.toString(index));
+ throw Context.reportRuntimeError1("msg.prop.not.found", str);
+ }
+ return slot;
+ }
+
+ /**
+ * Locate the slot with given name or index.
+ *
+ * @param name property name or null if slot holds spare array index.
+ * @param index index or 0 if slot holds property name.
+ */
+ private Slot getSlot(String name, int index, int accessType)
+ {
+ Slot slot;
+
+ // Query last access cache and check that it was not deleted.
+ lastAccessCheck:
+ {
+ slot = lastAccess;
+ if (name != null) {
+ if (name != slot.name)
+ break lastAccessCheck;
+ // No String.equals here as successful slot search update
+ // name object with fresh reference of the same string.
+ } else {
+ if (slot.name != null || index != slot.indexOrHash)
+ break lastAccessCheck;
+ }
+
+ if (slot.wasDeleted != 0)
+ break lastAccessCheck;
+
+ if (accessType == SLOT_MODIFY_GETTER_SETTER &&
+ !(slot instanceof GetterSlot))
+ break lastAccessCheck;
+
+ return slot;
+ }
+
+ slot = accessSlot(name, index, accessType);
+ if (slot != null) {
+ // Update the cache
+ lastAccess = slot;
+ }
+ return slot;
+ }
+
+ private Slot accessSlot(String name, int index, int accessType)
+ {
+ int indexOrHash = (name != null ? name.hashCode() : index);
+
+ if (accessType == SLOT_QUERY ||
+ accessType == SLOT_MODIFY ||
+ accessType == SLOT_MODIFY_CONST ||
+ accessType == SLOT_MODIFY_GETTER_SETTER)
+ {
+ // Check the hashtable without using synchronization
+
+ Slot[] slotsLocalRef = slots; // Get stable local reference
+ if (slotsLocalRef == null) {
+ if (accessType == SLOT_QUERY)
+ return null;
+ } else {
+ int tableSize = slotsLocalRef.length;
+ int slotIndex = getSlotIndex(tableSize, indexOrHash);
+ Slot slot = slotsLocalRef[slotIndex];
+ while (slot != null) {
+ String sname = slot.name;
+ if (sname != null) {
+ if (sname == name)
+ break;
+ if (name != null && indexOrHash == slot.indexOrHash) {
+ if (name.equals(sname)) {
+ // This will avoid calling String.equals when
+ // slot is accessed with same string object
+ // next time.
+ slot.name = name;
+ break;
+ }
+ }
+ } else if (name == null &&
+ indexOrHash == slot.indexOrHash) {
+ break;
+ }
+ slot = slot.next;
+ }
+ if (accessType == SLOT_QUERY) {
+ return slot;
+ } else if (accessType == SLOT_MODIFY) {
+ if (slot != null)
+ return slot;
+ } else if (accessType == SLOT_MODIFY_GETTER_SETTER) {
+ if (slot instanceof GetterSlot)
+ return slot;
+ } else if (accessType == SLOT_MODIFY_CONST) {
+ if (slot != null)
+ return slot;
+ }
+ }
+
+ // A new slot has to be inserted or the old has to be replaced
+ // by GetterSlot. Time to synchronize.
+
+ synchronized (this) {
+ // Refresh local ref if another thread triggered grow
+ slotsLocalRef = slots;
+ int insertPos;
+ if (count == 0) {
+ // Always throw away old slots if any on empty insert
+ slotsLocalRef = new Slot[5];
+ slots = slotsLocalRef;
+ insertPos = getSlotIndex(slotsLocalRef.length, indexOrHash);
+ } else {
+ int tableSize = slotsLocalRef.length;
+ insertPos = getSlotIndex(tableSize, indexOrHash);
+ Slot prev = slotsLocalRef[insertPos];
+ Slot slot = prev;
+ while (slot != null) {
+ if (slot.indexOrHash == indexOrHash &&
+ (slot.name == name ||
+ (name != null && name.equals(slot.name))))
+ {
+ break;
+ }
+ prev = slot;
+ slot = slot.next;
+ }
+
+ if (slot != null) {
+ // Another thread just added a slot with same
+ // name/index before this one entered synchronized
+ // block. This is a race in application code and
+ // probably indicates bug there. But for the hashtable
+ // implementation it is harmless with the only
+ // complication is the need to replace the added slot
+ // if we need GetterSlot and the old one is not.
+ if (accessType == SLOT_MODIFY_GETTER_SETTER &&
+ !(slot instanceof GetterSlot))
+ {
+ GetterSlot newSlot = new GetterSlot(name, indexOrHash,
+ slot.getAttributes());
+ newSlot.value = slot.value;
+ newSlot.next = slot.next;
+ if (prev == slot) {
+ slotsLocalRef[insertPos] = newSlot;
+ } else {
+ prev.next = newSlot;
+ }
+ slot.wasDeleted = (byte)1;
+ if (slot == lastAccess) {
+ lastAccess = REMOVED;
+ }
+ slot = newSlot;
+ } else if (accessType == SLOT_MODIFY_CONST) {
+ return null;
+ }
+ return slot;
+ }
+
+ // Check if the table is not too full before inserting.
+ if (4 * (count + 1) > 3 * slotsLocalRef.length) {
+ slotsLocalRef = new Slot[slotsLocalRef.length * 2 + 1];
+ copyTable(slots, slotsLocalRef, count);
+ slots = slotsLocalRef;
+ insertPos = getSlotIndex(slotsLocalRef.length,
+ indexOrHash);
+ }
+ }
+
+ Slot newSlot = (accessType == SLOT_MODIFY_GETTER_SETTER
+ ? new GetterSlot(name, indexOrHash, 0)
+ : new Slot(name, indexOrHash, 0));
+ if (accessType == SLOT_MODIFY_CONST)
+ newSlot.setAttributes(CONST);
+ ++count;
+ addKnownAbsentSlot(slotsLocalRef, newSlot, insertPos);
+ return newSlot;
+ }
+
+ } else if (accessType == SLOT_REMOVE) {
+ synchronized (this) {
+ Slot[] slotsLocalRef = slots;
+ if (count != 0) {
+ int tableSize = slots.length;
+ int slotIndex = getSlotIndex(tableSize, indexOrHash);
+ Slot prev = slotsLocalRef[slotIndex];
+ Slot slot = prev;
+ while (slot != null) {
+ if (slot.indexOrHash == indexOrHash &&
+ (slot.name == name ||
+ (name != null && name.equals(slot.name))))
+ {
+ break;
+ }
+ prev = slot;
+ slot = slot.next;
+ }
+ if (slot != null && (slot.getAttributes() & PERMANENT) == 0) {
+ count--;
+ if (prev == slot) {
+ slotsLocalRef[slotIndex] = slot.next;
+ } else {
+ prev.next = slot.next;
+ }
+ // Mark the slot as removed to handle a case when
+ // another thread manages to put just removed slot
+ // into lastAccess cache.
+ slot.wasDeleted = (byte)1;
+ if (slot == lastAccess) {
+ lastAccess = REMOVED;
+ }
+ }
+ }
+ }
+ return null;
+
+ } else {
+ throw Kit.codeBug();
+ }
+ }
+
+ private static int getSlotIndex(int tableSize, int indexOrHash)
+ {
+ return (indexOrHash & 0x7fffffff) % tableSize;
+ }
+
+ // Must be inside synchronized (this)
+ private static void copyTable(Slot[] slots, Slot[] newSlots, int count)
+ {
+ if (count == 0) throw Kit.codeBug();
+
+ int tableSize = newSlots.length;
+ int i = slots.length;
+ for (;;) {
+ --i;
+ Slot slot = slots[i];
+ while (slot != null) {
+ int insertPos = getSlotIndex(tableSize, slot.indexOrHash);
+ Slot next = slot.next;
+ addKnownAbsentSlot(newSlots, slot, insertPos);
+ slot.next = null;
+ slot = next;
+ if (--count == 0)
+ return;
+ }
+ }
+ }
+
+ /**
+ * 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[] slots, Slot slot, int insertPos)
+ {
+ if (slots[insertPos] == null) {
+ slots[insertPos] = slot;
+ } else {
+ Slot prev = slots[insertPos];
+ while (prev.next != null) {
+ prev = prev.next;
+ }
+ prev.next = slot;
+ }
+ }
+
+ Object[] getIds(boolean getAll) {
+ Slot[] s = slots;
+ Object[] a = ScriptRuntime.emptyArgs;
+ if (s == null)
+ return a;
+ int c = 0;
+ for (int i=0; i < s.length; i++) {
+ Slot slot = s[i];
+ while (slot != null) {
+ if (getAll || (slot.getAttributes() & DONTENUM) == 0) {
+ if (c == 0)
+ a = new Object[s.length];
+ a[c++] = (slot.name != null ? (Object) slot.name
+ : new Integer(slot.indexOrHash));
+ }
+ slot = slot.next;
+ }
+ }
+ if (c == a.length)
+ return a;
+ Object[] result = new Object[c];
+ System.arraycopy(a, 0, result, 0, c);
+ return result;
+ }
+
+ private synchronized void writeObject(ObjectOutputStream out)
+ throws IOException
+ {
+ out.defaultWriteObject();
+ int objectsCount = count;
+ if (objectsCount < 0) {
+ // "this" was sealed
+ objectsCount = ~objectsCount;
+ }
+ if (objectsCount == 0) {
+ out.writeInt(0);
+ } else {
+ out.writeInt(slots.length);
+ for (int i = 0; i < slots.length; ++i) {
+ Slot slot = slots[i];
+ while (slot != null) {
+ out.writeObject(slot);
+ slot = slot.next;
+ if (--objectsCount == 0)
+ return;
+ }
+ }
+ }
+ }
+
+ private void readObject(ObjectInputStream in)
+ throws IOException, ClassNotFoundException
+ {
+ in.defaultReadObject();
+ lastAccess = REMOVED;
+
+ int tableSize = in.readInt();
+ if (tableSize != 0) {
+ slots = new Slot[tableSize];
+ int objectsCount = count;
+ if (objectsCount < 0) {
+ // "this" was sealed
+ objectsCount = ~objectsCount;
+ }
+ for (int i = 0; i != objectsCount; ++i) {
+ Slot slot = (Slot)in.readObject();
+ int slotIndex = getSlotIndex(tableSize, slot.indexOrHash);
+ addKnownAbsentSlot(slots, slot, slotIndex);
+ }
+ }
+ }
+
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/SecureCaller.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/SecureCaller.java
new file mode 100644
index 0000000..bc8ed86
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/SecureCaller.java
@@ -0,0 +1,198 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.ref.SoftReference;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.SecureClassLoader;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+/**
+ * @author Attila Szegedi
+ */
+public abstract class SecureCaller
+{
+ private static final byte[] secureCallerImplBytecode = loadBytecode();
+
+ // We're storing a CodeSource -> (ClassLoader -> SecureRenderer), since we
+ // need to have one renderer per class loader. We're using weak hash maps
+ // and soft references all the way, since we don't want to interfere with
+ // cleanup of either CodeSource or ClassLoader objects.
+ private static final Map callers = new WeakHashMap();
+
+ public abstract Object call(Callable callable, Context cx,
+ Scriptable scope, Scriptable thisObj, Object[] args);
+
+ /**
+ * Call the specified callable using a protection domain belonging to the
+ * specified code source.
+ */
+ static Object callSecurely(final CodeSource codeSource, Callable callable,
+ Context cx, Scriptable scope, Scriptable thisObj, Object[] args)
+ {
+ final Thread thread = Thread.currentThread();
+ // Run in doPrivileged as we might be checked for "getClassLoader"
+ // runtime permission
+ final ClassLoader classLoader = (ClassLoader)AccessController.doPrivileged(
+ new PrivilegedAction() {
+ public Object run() {
+ return thread.getContextClassLoader();
+ }
+ });
+ Map classLoaderMap;
+ synchronized(callers)
+ {
+ classLoaderMap = (Map)callers.get(codeSource);
+ if(classLoaderMap == null)
+ {
+ classLoaderMap = new WeakHashMap();
+ callers.put(codeSource, classLoaderMap);
+ }
+ }
+ SecureCaller caller;
+ synchronized(classLoaderMap)
+ {
+ SoftReference ref = (SoftReference)classLoaderMap.get(classLoader);
+ if(ref != null)
+ {
+ caller = (SecureCaller)ref.get();
+ }
+ else
+ {
+ caller = null;
+ }
+ if(caller == null)
+ {
+ try
+ {
+ // Run in doPrivileged as we'll be checked for
+ // "createClassLoader" runtime permission
+ caller = (SecureCaller)AccessController.doPrivileged(
+ new PrivilegedExceptionAction()
+ {
+ public Object run() throws Exception
+ {
+ ClassLoader effectiveClassLoader;
+ Class thisClass = getClass();
+ if(classLoader.loadClass(thisClass.getName()) != thisClass) {
+ effectiveClassLoader = thisClass.getClassLoader();
+ } else {
+ effectiveClassLoader = classLoader;
+ }
+ SecureClassLoaderImpl secCl =
+ new SecureClassLoaderImpl(effectiveClassLoader);
+ Class c = secCl.defineAndLinkClass(
+ SecureCaller.class.getName() + "Impl",
+ secureCallerImplBytecode, codeSource);
+ return c.newInstance();
+ }
+ });
+ classLoaderMap.put(classLoader, new SoftReference(caller));
+ }
+ catch(PrivilegedActionException ex)
+ {
+ throw new UndeclaredThrowableException(ex.getCause());
+ }
+ }
+ }
+ return caller.call(callable, cx, scope, thisObj, args);
+ }
+
+ private static class SecureClassLoaderImpl extends SecureClassLoader
+ {
+ SecureClassLoaderImpl(ClassLoader parent)
+ {
+ super(parent);
+ }
+
+ Class defineAndLinkClass(String name, byte[] bytes, CodeSource cs)
+ {
+ Class cl = defineClass(name, bytes, 0, bytes.length, cs);
+ resolveClass(cl);
+ return cl;
+ }
+ }
+
+ private static byte[] loadBytecode()
+ {
+ return (byte[])AccessController.doPrivileged(new PrivilegedAction()
+ {
+ public Object run()
+ {
+ return loadBytecodePrivileged();
+ }
+ });
+ }
+
+ private static byte[] loadBytecodePrivileged()
+ {
+ URL url = SecureCaller.class.getResource("SecureCallerImpl.clazz");
+ try
+ {
+ InputStream in = url.openStream();
+ try
+ {
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ for(;;)
+ {
+ int r = in.read();
+ if(r == -1)
+ {
+ return bout.toByteArray();
+ }
+ bout.write(r);
+ }
+ }
+ finally
+ {
+ in.close();
+ }
+ }
+ catch(IOException e)
+ {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/SecurityController.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/SecurityController.java
new file mode 100644
index 0000000..ed85dbf
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/SecurityController.java
@@ -0,0 +1,211 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// API class
+
+package org.mozilla.javascript;
+
+/**
+ * This class describes the support needed to implement security.
+ * <p>
+ * Three main pieces of functionality are required to implement
+ * security for JavaScript. First, it must be possible to define
+ * classes with an associated security domain. (This security
+ * domain may be any object incorporating notion of access
+ * restrictions that has meaning to an embedding; for a client-side
+ * JavaScript embedding this would typically be
+ * java.security.ProtectionDomain or similar object depending on an
+ * origin URL and/or a digital certificate.)
+ * Next it must be possible to get a security domain object that
+ * allows a particular action only if all security domains
+ * associated with code on the current Java stack allows it. And
+ * finally, it must be possible to execute script code with
+ * associated security domain injected into Java stack.
+ * <p>
+ * These three pieces of functionality are encapsulated in the
+ * SecurityController class.
+ *
+ * @see org.mozilla.javascript.Context#setSecurityController(SecurityController)
+ * @see java.lang.ClassLoader
+ * @since 1.5 Release 4
+ */
+public abstract class SecurityController
+{
+ private static SecurityController global;
+
+// The method must NOT be public or protected
+ static SecurityController global()
+ {
+ return global;
+ }
+
+ /**
+ * Check if global {@link SecurityController} was already installed.
+ * @see #initGlobal(SecurityController controller)
+ */
+ public static boolean hasGlobal()
+ {
+ return global != null;
+ }
+
+ /**
+ * Initialize global controller that will be used for all
+ * security-related operations. The global controller takes precedence
+ * over already installed {@link Context}-specific controllers and cause
+ * any subsequent call to
+ * {@link Context#setSecurityController(SecurityController)}
+ * to throw an exception.
+ * <p>
+ * The method can only be called once.
+ *
+ * @see #hasGlobal()
+ */
+ public static void initGlobal(SecurityController controller)
+ {
+ if (controller == null) throw new IllegalArgumentException();
+ if (global != null) {
+ throw new SecurityException("Cannot overwrite already installed global SecurityController");
+ }
+ global = controller;
+ }
+
+ /**
+ * Get class loader-like object that can be used
+ * to define classes with the given security context.
+ * @param parentLoader parent class loader to delegate search for classes
+ * not defined by the class loader itself
+ * @param securityDomain some object specifying the security
+ * context of the code that is defined by the returned class loader.
+ */
+ public abstract GeneratedClassLoader createClassLoader(
+ ClassLoader parentLoader, Object securityDomain);
+
+ /**
+ * Create {@link GeneratedClassLoader} with restrictions imposed by
+ * staticDomain and all current stack frames.
+ * The method uses the SecurityController instance associated with the
+ * current {@link Context} to construct proper dynamic domain and create
+ * corresponding class loader.
+ * <par>
+ * If no SecurityController is associated with the current {@link Context} ,
+ * the method calls {@link Context#createClassLoader(ClassLoader parent)}.
+ *
+ * @param parent parent class loader. If null,
+ * {@link Context#getApplicationClassLoader()} will be used.
+ * @param staticDomain static security domain.
+ */
+ public static GeneratedClassLoader createLoader(
+ ClassLoader parent, Object staticDomain)
+ {
+ Context cx = Context.getContext();
+ if (parent == null) {
+ parent = cx.getApplicationClassLoader();
+ }
+ SecurityController sc = cx.getSecurityController();
+ GeneratedClassLoader loader;
+ if (sc == null) {
+ loader = cx.createClassLoader(parent);
+ } else {
+ Object dynamicDomain = sc.getDynamicSecurityDomain(staticDomain);
+ loader = sc.createClassLoader(parent, dynamicDomain);
+ }
+ return loader;
+ }
+
+ public static Class getStaticSecurityDomainClass() {
+ SecurityController sc = Context.getContext().getSecurityController();
+ return sc == null ? null : sc.getStaticSecurityDomainClassInternal();
+ }
+
+ public Class getStaticSecurityDomainClassInternal()
+ {
+ return null;
+ }
+
+ /**
+ * Get dynamic security domain that allows an action only if it is allowed
+ * by the current Java stack and <i>securityDomain</i>. If
+ * <i>securityDomain</i> is null, return domain representing permissions
+ * allowed by the current stack.
+ */
+ public abstract Object getDynamicSecurityDomain(Object securityDomain);
+
+ /**
+ * Call {@link
+ * Callable#call(Context cx, Scriptable scope, Scriptable thisObj,
+ * Object[] args)}
+ * of <i>callable</i> under restricted security domain where an action is
+ * allowed only if it is allowed according to the Java stack on the
+ * moment of the <i>execWithDomain</i> call and <i>securityDomain</i>.
+ * Any call to {@link #getDynamicSecurityDomain(Object)} during
+ * execution of <tt>callable.call(cx, scope, thisObj, args)</tt>
+ * should return a domain incorporate restrictions imposed by
+ * <i>securityDomain</i> and Java stack on the moment of callWithDomain
+ * invocation.
+ * <p>
+ * The method should always be overridden, it is not declared abstract
+ * for compatibility reasons.
+ */
+ public Object callWithDomain(Object securityDomain, Context cx,
+ final Callable callable, Scriptable scope,
+ final Scriptable thisObj, final Object[] args)
+ {
+ return execWithDomain(cx, scope, new Script()
+ {
+ public Object exec(Context cx, Scriptable scope)
+ {
+ return callable.call(cx, scope, thisObj, args);
+ }
+
+ }, securityDomain);
+ }
+
+ /**
+ * @deprecated The application should not override this method and instead
+ * override
+ * {@link #callWithDomain(Object securityDomain, Context cx, Callable callable, Scriptable scope, Scriptable thisObj, Object[] args)}.
+ */
+ public Object execWithDomain(Context cx, Scriptable scope,
+ Script script, Object securityDomain)
+ {
+ throw new IllegalStateException("callWithDomain should be overridden");
+ }
+
+
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/SecurityUtilities.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/SecurityUtilities.java
new file mode 100644
index 0000000..275ad92
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/SecurityUtilities.java
@@ -0,0 +1,80 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+
+/**
+ * @author Attila Szegedi
+ */
+public class SecurityUtilities
+{
+ /**
+ * Retrieves a system property within a privileged block. Use it only when
+ * the property is used from within Rhino code and is not passed out of it.
+ * @param name the name of the system property
+ * @return the value of the system property
+ */
+ public static String getSystemProperty(final String name)
+ {
+ return (String)AccessController.doPrivileged(
+ new PrivilegedAction()
+ {
+ public Object run()
+ {
+ return System.getProperty(name);
+ }
+ });
+ }
+
+ public static ProtectionDomain getProtectionDomain(final Class clazz)
+ {
+ return (ProtectionDomain)AccessController.doPrivileged(
+ new PrivilegedAction()
+ {
+ public Object run()
+ {
+ return clazz.getProtectionDomain();
+ }
+ });
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/SpecialRef.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/SpecialRef.java
new file mode 100644
index 0000000..b037eaf
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/SpecialRef.java
@@ -0,0 +1,151 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov, igor@fastmail.fm
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+class SpecialRef extends Ref
+{
+ static final long serialVersionUID = -7521596632456797847L;
+
+ private static final int SPECIAL_NONE = 0;
+ private static final int SPECIAL_PROTO = 1;
+ private static final int SPECIAL_PARENT = 2;
+
+ private Scriptable target;
+ private int type;
+ private String name;
+
+ private SpecialRef(Scriptable target, int type, String name)
+ {
+ this.target = target;
+ this.type = type;
+ this.name = name;
+ }
+
+ static Ref createSpecial(Context cx, Object object, String name)
+ {
+ Scriptable target = ScriptRuntime.toObjectOrNull(cx, object);
+ if (target == null) {
+ throw ScriptRuntime.undefReadError(object, name);
+ }
+
+ int type;
+ if (name.equals("__proto__")) {
+ type = SPECIAL_PROTO;
+ } else if (name.equals("__parent__")) {
+ type = SPECIAL_PARENT;
+ } else {
+ throw new IllegalArgumentException(name);
+ }
+
+ if (!cx.hasFeature(Context.FEATURE_PARENT_PROTO_PROPERTIES)) {
+ // Clear special after checking for valid name!
+ type = SPECIAL_NONE;
+ }
+
+ return new SpecialRef(target, type, name);
+ }
+
+ public Object get(Context cx)
+ {
+ switch (type) {
+ case SPECIAL_NONE:
+ return ScriptRuntime.getObjectProp(target, name, cx);
+ case SPECIAL_PROTO:
+ return target.getPrototype();
+ case SPECIAL_PARENT:
+ return target.getParentScope();
+ default:
+ throw Kit.codeBug();
+ }
+ }
+
+ public Object set(Context cx, Object value)
+ {
+ switch (type) {
+ case SPECIAL_NONE:
+ return ScriptRuntime.setObjectProp(target, name, value, cx);
+ case SPECIAL_PROTO:
+ case SPECIAL_PARENT:
+ {
+ Scriptable obj = ScriptRuntime.toObjectOrNull(cx, value);
+ if (obj != null) {
+ // Check that obj does not contain on its prototype/scope
+ // chain to prevent cycles
+ Scriptable search = obj;
+ do {
+ if (search == target) {
+ throw Context.reportRuntimeError1(
+ "msg.cyclic.value", name);
+ }
+ if (type == SPECIAL_PROTO) {
+ search = search.getPrototype();
+ } else {
+ search = search.getParentScope();
+ }
+ } while (search != null);
+ }
+ if (type == SPECIAL_PROTO) {
+ target.setPrototype(obj);
+ } else {
+ target.setParentScope(obj);
+ }
+ return obj;
+ }
+ default:
+ throw Kit.codeBug();
+ }
+ }
+
+ public boolean has(Context cx)
+ {
+ if (type == SPECIAL_NONE) {
+ return ScriptRuntime.hasObjectElem(target, name, cx);
+ }
+ return true;
+ }
+
+ public boolean delete(Context cx)
+ {
+ if (type == SPECIAL_NONE) {
+ return ScriptRuntime.deleteObjectElem(target, name, cx);
+ }
+ return false;
+ }
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Synchronizer.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Synchronizer.java
new file mode 100644
index 0000000..f2fca52
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Synchronizer.java
@@ -0,0 +1,81 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Delegator.java, released
+ * Sep 27, 2000.
+ *
+ * The Initial Developer of the Original Code is
+ * Matthias Radestock. <matthias@sorted.org>.
+ * Portions created by the Initial Developer are Copyright (C) 2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// API class
+
+package org.mozilla.javascript;
+
+/**
+ * This class provides support for implementing Java-style synchronized
+ * methods in Javascript.
+ *
+ * Synchronized functions are created from ordinary Javascript
+ * functions by the <code>Synchronizer</code> constructor, e.g.
+ * <code>new Packages.org.mozilla.javascript.Synchronizer(fun)</code>.
+ * The resulting object is a function that establishes an exclusive
+ * lock on the <code>this</code> object of its invocation.
+ *
+ * The Rhino shell provides a short-cut for the creation of
+ * synchronized methods: <code>sync(fun)</code> has the same effect as
+ * calling the above constructor.
+ *
+ * @see org.mozilla.javascript.Delegator
+ * @author Matthias Radestock
+ */
+
+public class Synchronizer extends Delegator {
+
+ /**
+ * Create a new synchronized function from an existing one.
+ *
+ * @param obj the existing function
+ */
+ public Synchronizer(Scriptable obj) {
+ super(obj);
+ }
+
+ /**
+ * @see org.mozilla.javascript.Function#call
+ */
+ public Object call(Context cx, Scriptable scope, Scriptable thisObj,
+ Object[] args)
+ {
+ synchronized(thisObj instanceof Wrapper ? ((Wrapper)thisObj).unwrap() : thisObj) {
+ return ((Function)obj).call(cx,scope,thisObj,args);
+ }
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Token.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Token.java
new file mode 100644
index 0000000..be96487
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Token.java
@@ -0,0 +1,436 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Roger Lawrence
+ * Mike McCabe
+ * Igor Bukanov
+ * Bob Jervis
+ * Milen Nankov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+/**
+ * This class implements the JavaScript scanner.
+ *
+ * It is based on the C source files jsscan.c and jsscan.h
+ * in the jsref package.
+ *
+ * @see org.mozilla.javascript.Parser
+ *
+ * @author Mike McCabe
+ * @author Brendan Eich
+ */
+
+public class Token
+{
+
+ // debug flags
+ public static final boolean printTrees =
+ /*APPJET: some info not generated with this off; we disable
+ actual printing in Interpreter and CodeGen */true;
+ static final boolean printICode = false;
+ static final boolean printNames = printTrees || printICode;
+
+ /**
+ * Token types. These values correspond to JSTokenType values in
+ * jsscan.c.
+ */
+
+ public final static int
+ // start enum
+ ERROR = -1, // well-known as the only code < EOF
+ EOF = 0, // end of file token - (not EOF_CHAR)
+ EOL = 1, // end of line
+
+ // Interpreter reuses the following as bytecodes
+ FIRST_BYTECODE_TOKEN = 2,
+
+ ENTERWITH = 2,
+ LEAVEWITH = 3,
+ RETURN = 4,
+ GOTO = 5,
+ IFEQ = 6,
+ IFNE = 7,
+ SETNAME = 8,
+ BITOR = 9,
+ BITXOR = 10,
+ BITAND = 11,
+ EQ = 12,
+ NE = 13,
+ LT = 14,
+ LE = 15,
+ GT = 16,
+ GE = 17,
+ LSH = 18,
+ RSH = 19,
+ URSH = 20,
+ ADD = 21,
+ SUB = 22,
+ MUL = 23,
+ DIV = 24,
+ MOD = 25,
+ NOT = 26,
+ BITNOT = 27,
+ POS = 28,
+ NEG = 29,
+ NEW = 30,
+ DELPROP = 31,
+ TYPEOF = 32,
+ GETPROP = 33,
+ GETPROPNOWARN = 34,
+ SETPROP = 35,
+ GETELEM = 36,
+ SETELEM = 37,
+ CALL = 38,
+ NAME = 39,
+ NUMBER = 40,
+ STRING = 41,
+ NULL = 42,
+ THIS = 43,
+ FALSE = 44,
+ TRUE = 45,
+ SHEQ = 46, // shallow equality (===)
+ SHNE = 47, // shallow inequality (!==)
+ REGEXP = 48,
+ BINDNAME = 49,
+ THROW = 50,
+ RETHROW = 51, // rethrow caught exception: catch (e if ) use it
+ IN = 52,
+ INSTANCEOF = 53,
+ LOCAL_LOAD = 54,
+ GETVAR = 55,
+ SETVAR = 56,
+ CATCH_SCOPE = 57,
+ ENUM_INIT_KEYS = 58,
+ ENUM_INIT_VALUES = 59,
+ ENUM_INIT_ARRAY= 60,
+ ENUM_NEXT = 61,
+ ENUM_ID = 62,
+ THISFN = 63,
+ RETURN_RESULT = 64, // to return previously stored return result
+ ARRAYLIT = 65, // array literal
+ OBJECTLIT = 66, // object literal
+ GET_REF = 67, // *reference
+ SET_REF = 68, // *reference = something
+ DEL_REF = 69, // delete reference
+ REF_CALL = 70, // f(args) = something or f(args)++
+ REF_SPECIAL = 71, // reference for special properties like __proto
+ YIELD = 72, // JS 1.7 yield pseudo keyword
+
+ // For XML support:
+ DEFAULTNAMESPACE = 73, // default xml namespace =
+ ESCXMLATTR = 74,
+ ESCXMLTEXT = 75,
+ REF_MEMBER = 76, // Reference for x.@y, x..y etc.
+ REF_NS_MEMBER = 77, // Reference for x.ns::y, x..ns::y etc.
+ REF_NAME = 78, // Reference for @y, @[y] etc.
+ REF_NS_NAME = 79; // Reference for ns::y, @ns::y@[y] etc.
+
+ // End of interpreter bytecodes
+ public final static int
+ LAST_BYTECODE_TOKEN = REF_NS_NAME,
+
+ TRY = 80,
+ SEMI = 81, // semicolon
+ LB = 82, // left and right brackets
+ RB = 83,
+ LC = 84, // left and right curlies (braces)
+ RC = 85,
+ LP = 86, // left and right parentheses
+ RP = 87,
+ COMMA = 88, // comma operator
+
+ ASSIGN = 89, // simple assignment (=)
+ ASSIGN_BITOR = 90, // |=
+ ASSIGN_BITXOR = 91, // ^=
+ ASSIGN_BITAND = 92, // |=
+ ASSIGN_LSH = 93, // <<=
+ ASSIGN_RSH = 94, // >>=
+ ASSIGN_URSH = 95, // >>>=
+ ASSIGN_ADD = 96, // +=
+ ASSIGN_SUB = 97, // -=
+ ASSIGN_MUL = 98, // *=
+ ASSIGN_DIV = 99, // /=
+ ASSIGN_MOD = 100; // %=
+
+ public final static int
+ FIRST_ASSIGN = ASSIGN,
+ LAST_ASSIGN = ASSIGN_MOD,
+
+ HOOK = 101, // conditional (?:)
+ COLON = 102,
+ OR = 103, // logical or (||)
+ AND = 104, // logical and (&&)
+ INC = 105, // increment/decrement (++ --)
+ DEC = 106,
+ DOT = 107, // member operator (.)
+ FUNCTION = 108, // function keyword
+ EXPORT = 109, // export keyword
+ IMPORT = 110, // import keyword
+ IF = 111, // if keyword
+ ELSE = 112, // else keyword
+ SWITCH = 113, // switch keyword
+ CASE = 114, // case keyword
+ DEFAULT = 115, // default keyword
+ WHILE = 116, // while keyword
+ DO = 117, // do keyword
+ FOR = 118, // for keyword
+ BREAK = 119, // break keyword
+ CONTINUE = 120, // continue keyword
+ VAR = 121, // var keyword
+ WITH = 122, // with keyword
+ CATCH = 123, // catch keyword
+ FINALLY = 124, // finally keyword
+ VOID = 125, // void keyword
+ RESERVED = 126, // reserved keywords
+
+ EMPTY = 127,
+
+ /* types used for the parse tree - these never get returned
+ * by the scanner.
+ */
+
+ BLOCK = 128, // statement block
+ LABEL = 129, // label
+ TARGET = 130,
+ LOOP = 131,
+ EXPR_VOID = 132, // expression statement in functions
+ EXPR_RESULT = 133, // expression statement in scripts
+ JSR = 134,
+ SCRIPT = 135, // top-level node for entire script
+ TYPEOFNAME = 136, // for typeof(simple-name)
+ USE_STACK = 137,
+ SETPROP_OP = 138, // x.y op= something
+ SETELEM_OP = 139, // x[y] op= something
+ LOCAL_BLOCK = 140,
+ SET_REF_OP = 141, // *reference op= something
+
+ // For XML support:
+ DOTDOT = 142, // member operator (..)
+ COLONCOLON = 143, // namespace::name
+ XML = 144, // XML type
+ DOTQUERY = 145, // .() -- e.g., x.emps.emp.(name == "terry")
+ XMLATTR = 146, // @
+ XMLEND = 147,
+
+ // Optimizer-only-tokens
+ TO_OBJECT = 148,
+ TO_DOUBLE = 149,
+
+ GET = 150, // JS 1.5 get pseudo keyword
+ SET = 151, // JS 1.5 set pseudo keyword
+ LET = 152, // JS 1.7 let pseudo keyword
+ CONST = 153,
+ SETCONST = 154,
+ SETCONSTVAR = 155,
+ ARRAYCOMP = 156, // array comprehension
+ LETEXPR = 157,
+ WITHEXPR = 158,
+ DEBUGGER = 159,
+ LAST_TOKEN = 159;
+
+ public static String name(int token)
+ {
+ if (!printNames) {
+ return String.valueOf(token);
+ }
+ switch (token) {
+ case ERROR: return "ERROR";
+ case EOF: return "EOF";
+ case EOL: return "EOL";
+ case ENTERWITH: return "ENTERWITH";
+ case LEAVEWITH: return "LEAVEWITH";
+ case RETURN: return "RETURN";
+ case GOTO: return "GOTO";
+ case IFEQ: return "IFEQ";
+ case IFNE: return "IFNE";
+ case SETNAME: return "SETNAME";
+ case BITOR: return "BITOR";
+ case BITXOR: return "BITXOR";
+ case BITAND: return "BITAND";
+ case EQ: return "EQ";
+ case NE: return "NE";
+ case LT: return "LT";
+ case LE: return "LE";
+ case GT: return "GT";
+ case GE: return "GE";
+ case LSH: return "LSH";
+ case RSH: return "RSH";
+ case URSH: return "URSH";
+ case ADD: return "ADD";
+ case SUB: return "SUB";
+ case MUL: return "MUL";
+ case DIV: return "DIV";
+ case MOD: return "MOD";
+ case NOT: return "NOT";
+ case BITNOT: return "BITNOT";
+ case POS: return "POS";
+ case NEG: return "NEG";
+ case NEW: return "NEW";
+ case DELPROP: return "DELPROP";
+ case TYPEOF: return "TYPEOF";
+ case GETPROP: return "GETPROP";
+ case GETPROPNOWARN: return "GETPROPNOWARN";
+ case SETPROP: return "SETPROP";
+ case GETELEM: return "GETELEM";
+ case SETELEM: return "SETELEM";
+ case CALL: return "CALL";
+ case NAME: return "NAME";
+ case NUMBER: return "NUMBER";
+ case STRING: return "STRING";
+ case NULL: return "NULL";
+ case THIS: return "THIS";
+ case FALSE: return "FALSE";
+ case TRUE: return "TRUE";
+ case SHEQ: return "SHEQ";
+ case SHNE: return "SHNE";
+ case REGEXP: return "OBJECT";
+ case BINDNAME: return "BINDNAME";
+ case THROW: return "THROW";
+ case RETHROW: return "RETHROW";
+ case IN: return "IN";
+ case INSTANCEOF: return "INSTANCEOF";
+ case LOCAL_LOAD: return "LOCAL_LOAD";
+ case GETVAR: return "GETVAR";
+ case SETVAR: return "SETVAR";
+ case CATCH_SCOPE: return "CATCH_SCOPE";
+ case ENUM_INIT_KEYS: return "ENUM_INIT_KEYS";
+ case ENUM_INIT_VALUES:return "ENUM_INIT_VALUES";
+ case ENUM_INIT_ARRAY: return "ENUM_INIT_ARRAY";
+ case ENUM_NEXT: return "ENUM_NEXT";
+ case ENUM_ID: return "ENUM_ID";
+ case THISFN: return "THISFN";
+ case RETURN_RESULT: return "RETURN_RESULT";
+ case ARRAYLIT: return "ARRAYLIT";
+ case OBJECTLIT: return "OBJECTLIT";
+ case GET_REF: return "GET_REF";
+ case SET_REF: return "SET_REF";
+ case DEL_REF: return "DEL_REF";
+ case REF_CALL: return "REF_CALL";
+ case REF_SPECIAL: return "REF_SPECIAL";
+ case DEFAULTNAMESPACE:return "DEFAULTNAMESPACE";
+ case ESCXMLTEXT: return "ESCXMLTEXT";
+ case ESCXMLATTR: return "ESCXMLATTR";
+ case REF_MEMBER: return "REF_MEMBER";
+ case REF_NS_MEMBER: return "REF_NS_MEMBER";
+ case REF_NAME: return "REF_NAME";
+ case REF_NS_NAME: return "REF_NS_NAME";
+ case TRY: return "TRY";
+ case SEMI: return "SEMI";
+ case LB: return "LB";
+ case RB: return "RB";
+ case LC: return "LC";
+ case RC: return "RC";
+ case LP: return "LP";
+ case RP: return "RP";
+ case COMMA: return "COMMA";
+ case ASSIGN: return "ASSIGN";
+ case ASSIGN_BITOR: return "ASSIGN_BITOR";
+ case ASSIGN_BITXOR: return "ASSIGN_BITXOR";
+ case ASSIGN_BITAND: return "ASSIGN_BITAND";
+ case ASSIGN_LSH: return "ASSIGN_LSH";
+ case ASSIGN_RSH: return "ASSIGN_RSH";
+ case ASSIGN_URSH: return "ASSIGN_URSH";
+ case ASSIGN_ADD: return "ASSIGN_ADD";
+ case ASSIGN_SUB: return "ASSIGN_SUB";
+ case ASSIGN_MUL: return "ASSIGN_MUL";
+ case ASSIGN_DIV: return "ASSIGN_DIV";
+ case ASSIGN_MOD: return "ASSIGN_MOD";
+ case HOOK: return "HOOK";
+ case COLON: return "COLON";
+ case OR: return "OR";
+ case AND: return "AND";
+ case INC: return "INC";
+ case DEC: return "DEC";
+ case DOT: return "DOT";
+ case FUNCTION: return "FUNCTION";
+ case EXPORT: return "EXPORT";
+ case IMPORT: return "IMPORT";
+ case IF: return "IF";
+ case ELSE: return "ELSE";
+ case SWITCH: return "SWITCH";
+ case CASE: return "CASE";
+ case DEFAULT: return "DEFAULT";
+ case WHILE: return "WHILE";
+ case DO: return "DO";
+ case FOR: return "FOR";
+ case BREAK: return "BREAK";
+ case CONTINUE: return "CONTINUE";
+ case VAR: return "VAR";
+ case WITH: return "WITH";
+ case CATCH: return "CATCH";
+ case FINALLY: return "FINALLY";
+ case VOID: return "VOID";
+ case RESERVED: return "RESERVED";
+ case EMPTY: return "EMPTY";
+ case BLOCK: return "BLOCK";
+ case LABEL: return "LABEL";
+ case TARGET: return "TARGET";
+ case LOOP: return "LOOP";
+ case EXPR_VOID: return "EXPR_VOID";
+ case EXPR_RESULT: return "EXPR_RESULT";
+ case JSR: return "JSR";
+ case SCRIPT: return "SCRIPT";
+ case TYPEOFNAME: return "TYPEOFNAME";
+ case USE_STACK: return "USE_STACK";
+ case SETPROP_OP: return "SETPROP_OP";
+ case SETELEM_OP: return "SETELEM_OP";
+ case LOCAL_BLOCK: return "LOCAL_BLOCK";
+ case SET_REF_OP: return "SET_REF_OP";
+ case DOTDOT: return "DOTDOT";
+ case COLONCOLON: return "COLONCOLON";
+ case XML: return "XML";
+ case DOTQUERY: return "DOTQUERY";
+ case XMLATTR: return "XMLATTR";
+ case XMLEND: return "XMLEND";
+ case TO_OBJECT: return "TO_OBJECT";
+ case TO_DOUBLE: return "TO_DOUBLE";
+ case GET: return "GET";
+ case SET: return "SET";
+ case LET: return "LET";
+ case YIELD: return "YIELD";
+ case CONST: return "CONST";
+ case SETCONST: return "SETCONST";
+ case ARRAYCOMP: return "ARRAYCOMP";
+ case WITHEXPR: return "WITHEXPR";
+ case LETEXPR: return "LETEXPR";
+ case DEBUGGER: return "DEBUGGER";
+ }
+
+ // Token without name
+ throw new IllegalStateException(String.valueOf(token));
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/TokenStream.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/TokenStream.java
new file mode 100644
index 0000000..c8c3045
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/TokenStream.java
@@ -0,0 +1,1500 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Roger Lawrence
+ * Mike McCabe
+ * Igor Bukanov
+ * Ethan Hugg
+ * Bob Jervis
+ * Terry Lucas
+ * Milen Nankov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.io.*;
+
+/**
+ * This class implements the JavaScript scanner.
+ *
+ * It is based on the C source files jsscan.c and jsscan.h
+ * in the jsref package.
+ *
+ * @see org.mozilla.javascript.Parser
+ *
+ * @author Mike McCabe
+ * @author Brendan Eich
+ */
+
+class TokenStream
+{
+ /*
+ * For chars - because we need something out-of-range
+ * to check. (And checking EOF by exception is annoying.)
+ * Note distinction from EOF token type!
+ */
+ private final static int
+ EOF_CHAR = -1;
+
+ TokenStream(Parser parser, Reader sourceReader, String sourceString,
+ int lineno)
+ {
+ this.parser = parser;
+ this.lineno = lineno;
+ if (sourceReader != null) {
+ if (sourceString != null) Kit.codeBug();
+ this.sourceReader = sourceReader;
+ this.sourceBuffer = new char[512];
+ this.sourceEnd = 0;
+ } else {
+ if (sourceString == null) Kit.codeBug();
+ this.sourceString = sourceString;
+ this.sourceEnd = sourceString.length();
+ }
+ this.sourceCursor = 0;
+ }
+
+ /* This function uses the cached op, string and number fields in
+ * TokenStream; if getToken has been called since the passed token
+ * was scanned, the op or string printed may be incorrect.
+ */
+ String tokenToString(int token)
+ {
+ if (Token.printTrees) {
+ String name = Token.name(token);
+
+ switch (token) {
+ case Token.STRING:
+ case Token.REGEXP:
+ case Token.NAME:
+ return name + " `" + this.string + "'";
+
+ case Token.NUMBER:
+ return "NUMBER " + this.number;
+ }
+
+ return name;
+ }
+ return "";
+ }
+
+ static boolean isKeyword(String s)
+ {
+ return Token.EOF != stringToKeyword(s);
+ }
+
+ private static int stringToKeyword(String name)
+ {
+// #string_id_map#
+// The following assumes that Token.EOF == 0
+ final int
+ Id_break = Token.BREAK,
+ Id_case = Token.CASE,
+ Id_continue = Token.CONTINUE,
+ Id_default = Token.DEFAULT,
+ Id_delete = Token.DELPROP,
+ Id_do = Token.DO,
+ Id_else = Token.ELSE,
+ Id_export = Token.EXPORT,
+ Id_false = Token.FALSE,
+ Id_for = Token.FOR,
+ Id_function = Token.FUNCTION,
+ Id_if = Token.IF,
+ Id_in = Token.IN,
+ Id_let = Token.LET,
+ Id_new = Token.NEW,
+ Id_null = Token.NULL,
+ Id_return = Token.RETURN,
+ Id_switch = Token.SWITCH,
+ Id_this = Token.THIS,
+ Id_true = Token.TRUE,
+ Id_typeof = Token.TYPEOF,
+ Id_var = Token.VAR,
+ Id_void = Token.VOID,
+ Id_while = Token.WHILE,
+ Id_with = Token.WITH,
+ Id_yield = Token.YIELD,
+
+ // the following are #ifdef RESERVE_JAVA_KEYWORDS in jsscan.c
+ Id_abstract = Token.RESERVED,
+ Id_boolean = Token.RESERVED,
+ Id_byte = Token.RESERVED,
+ Id_catch = Token.CATCH,
+ Id_char = Token.RESERVED,
+ Id_class = Token.RESERVED,
+ Id_const = Token.CONST,
+ Id_debugger = Token.DEBUGGER,
+ Id_double = Token.RESERVED,
+ Id_enum = Token.RESERVED,
+ Id_extends = Token.RESERVED,
+ Id_final = Token.RESERVED,
+ Id_finally = Token.FINALLY,
+ Id_float = Token.RESERVED,
+ Id_goto = Token.RESERVED,
+ Id_implements = Token.RESERVED,
+ Id_import = Token.IMPORT,
+ Id_instanceof = Token.INSTANCEOF,
+ Id_int = Token.RESERVED,
+ Id_interface = Token.RESERVED,
+ Id_long = Token.RESERVED,
+ Id_native = Token.RESERVED,
+ Id_package = Token.RESERVED,
+ Id_private = Token.RESERVED,
+ Id_protected = Token.RESERVED,
+ Id_public = Token.RESERVED,
+ Id_short = Token.RESERVED,
+ Id_static = Token.RESERVED,
+ Id_super = Token.RESERVED,
+ Id_synchronized = Token.RESERVED,
+ Id_throw = Token.THROW,
+ Id_throws = Token.RESERVED,
+ Id_transient = Token.RESERVED,
+ Id_try = Token.TRY,
+ Id_volatile = Token.RESERVED;
+
+ int id;
+ String s = name;
+// #generated# Last update: 2007-04-18 13:53:30 PDT
+ L0: { id = 0; String X = null; int c;
+ L: switch (s.length()) {
+ case 2: c=s.charAt(1);
+ if (c=='f') { if (s.charAt(0)=='i') {id=Id_if; break L0;} }
+ else if (c=='n') { if (s.charAt(0)=='i') {id=Id_in; break L0;} }
+ else if (c=='o') { if (s.charAt(0)=='d') {id=Id_do; break L0;} }
+ break L;
+ case 3: switch (s.charAt(0)) {
+ case 'f': if (s.charAt(2)=='r' && s.charAt(1)=='o') {id=Id_for; break L0;} break L;
+ case 'i': if (s.charAt(2)=='t' && s.charAt(1)=='n') {id=Id_int; break L0;} break L;
+ case 'l': if (s.charAt(2)=='t' && s.charAt(1)=='e') {id=Id_let; break L0;} break L;
+ case 'n': if (s.charAt(2)=='w' && s.charAt(1)=='e') {id=Id_new; break L0;} break L;
+ case 't': if (s.charAt(2)=='y' && s.charAt(1)=='r') {id=Id_try; break L0;} break L;
+ case 'v': if (s.charAt(2)=='r' && s.charAt(1)=='a') {id=Id_var; break L0;} break L;
+ } break L;
+ case 4: switch (s.charAt(0)) {
+ case 'b': X="byte";id=Id_byte; break L;
+ case 'c': c=s.charAt(3);
+ if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='a') {id=Id_case; break L0;} }
+ else if (c=='r') { if (s.charAt(2)=='a' && s.charAt(1)=='h') {id=Id_char; break L0;} }
+ break L;
+ case 'e': c=s.charAt(3);
+ if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='l') {id=Id_else; break L0;} }
+ else if (c=='m') { if (s.charAt(2)=='u' && s.charAt(1)=='n') {id=Id_enum; break L0;} }
+ break L;
+ case 'g': X="goto";id=Id_goto; break L;
+ case 'l': X="long";id=Id_long; break L;
+ case 'n': X="null";id=Id_null; break L;
+ case 't': c=s.charAt(3);
+ if (c=='e') { if (s.charAt(2)=='u' && s.charAt(1)=='r') {id=Id_true; break L0;} }
+ else if (c=='s') { if (s.charAt(2)=='i' && s.charAt(1)=='h') {id=Id_this; break L0;} }
+ break L;
+ case 'v': X="void";id=Id_void; break L;
+ case 'w': X="with";id=Id_with; break L;
+ } break L;
+ case 5: switch (s.charAt(2)) {
+ case 'a': X="class";id=Id_class; break L;
+ case 'e': c=s.charAt(0);
+ if (c=='b') { X="break";id=Id_break; }
+ else if (c=='y') { X="yield";id=Id_yield; }
+ break L;
+ case 'i': X="while";id=Id_while; break L;
+ case 'l': X="false";id=Id_false; break L;
+ case 'n': c=s.charAt(0);
+ if (c=='c') { X="const";id=Id_const; }
+ else if (c=='f') { X="final";id=Id_final; }
+ break L;
+ case 'o': c=s.charAt(0);
+ if (c=='f') { X="float";id=Id_float; }
+ else if (c=='s') { X="short";id=Id_short; }
+ break L;
+ case 'p': X="super";id=Id_super; break L;
+ case 'r': X="throw";id=Id_throw; break L;
+ case 't': X="catch";id=Id_catch; break L;
+ } break L;
+ case 6: switch (s.charAt(1)) {
+ case 'a': X="native";id=Id_native; break L;
+ case 'e': c=s.charAt(0);
+ if (c=='d') { X="delete";id=Id_delete; }
+ else if (c=='r') { X="return";id=Id_return; }
+ break L;
+ case 'h': X="throws";id=Id_throws; break L;
+ /*APPJET*//* case 'm': X="import";id=Id_import; break L;*/
+ case 'o': X="double";id=Id_double; break L;
+ case 't': X="static";id=Id_static; break L;
+ case 'u': X="public";id=Id_public; break L;
+ case 'w': X="switch";id=Id_switch; break L;
+ case 'x': X="export";id=Id_export; break L;
+ case 'y': X="typeof";id=Id_typeof; break L;
+ } break L;
+ case 7: switch (s.charAt(1)) {
+ case 'a': X="package";id=Id_package; break L;
+ case 'e': X="default";id=Id_default; break L;
+ case 'i': X="finally";id=Id_finally; break L;
+ case 'o': X="boolean";id=Id_boolean; break L;
+ case 'r': X="private";id=Id_private; break L;
+ case 'x': X="extends";id=Id_extends; break L;
+ } break L;
+ case 8: switch (s.charAt(0)) {
+ case 'a': X="abstract";id=Id_abstract; break L;
+ case 'c': X="continue";id=Id_continue; break L;
+ case 'd': X="debugger";id=Id_debugger; break L;
+ case 'f': X="function";id=Id_function; break L;
+ case 'v': X="volatile";id=Id_volatile; break L;
+ } break L;
+ case 9: c=s.charAt(0);
+ if (c=='i') { X="interface";id=Id_interface; }
+ else if (c=='p') { X="protected";id=Id_protected; }
+ else if (c=='t') { X="transient";id=Id_transient; }
+ break L;
+ case 10: c=s.charAt(1);
+ if (c=='m') { X="implements";id=Id_implements; }
+ else if (c=='n') { X="instanceof";id=Id_instanceof; }
+ break L;
+ case 12: X="synchronized";id=Id_synchronized; break L;
+ }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ }
+// #/generated#
+// #/string_id_map#
+ if (id == 0) { return Token.EOF; }
+ return id & 0xff;
+ }
+
+ final int getLineno() { return lineno; }
+
+ final String getString() { return string; }
+
+ final double getNumber() { return number; }
+
+ final boolean eof() { return hitEOF; }
+
+ final int getToken() throws IOException
+ {
+ int c;
+
+ retry:
+ for (;;) {
+ // Eat whitespace, possibly sensitive to newlines.
+ for (;;) {
+ c = getChar();
+ if (c == EOF_CHAR) {
+ return Token.EOF;
+ } else if (c == '\n') {
+ dirtyLine = false;
+ return Token.EOL;
+ } else if (!isJSSpace(c)) {
+ if (c != '-') {
+ dirtyLine = true;
+ }
+ break;
+ }
+ }
+
+ if (c == '@') return Token.XMLATTR;
+
+ // identifier/keyword/instanceof?
+ // watch out for starting with a <backslash>
+ boolean identifierStart;
+ boolean isUnicodeEscapeStart = false;
+ if (c == '\\') {
+ c = getChar();
+ if (c == 'u') {
+ identifierStart = true;
+ isUnicodeEscapeStart = true;
+ stringBufferTop = 0;
+ } else {
+ identifierStart = false;
+ ungetChar(c);
+ c = '\\';
+ }
+ } else {
+ identifierStart = Character.isJavaIdentifierStart((char)c);
+ if (identifierStart) {
+ stringBufferTop = 0;
+ addToString(c);
+ }
+ }
+
+ if (identifierStart) {
+ boolean containsEscape = isUnicodeEscapeStart;
+ for (;;) {
+ if (isUnicodeEscapeStart) {
+ // strictly speaking we should probably push-back
+ // all the bad characters if the <backslash>uXXXX
+ // sequence is malformed. But since there isn't a
+ // correct context(is there?) for a bad Unicode
+ // escape sequence in an identifier, we can report
+ // an error here.
+ int escapeVal = 0;
+ for (int i = 0; i != 4; ++i) {
+ c = getChar();
+ escapeVal = Kit.xDigitToInt(c, escapeVal);
+ // Next check takes care about c < 0 and bad escape
+ if (escapeVal < 0) { break; }
+ }
+ if (escapeVal < 0) {
+ parser.addError("msg.invalid.escape");
+ return Token.ERROR;
+ }
+ addToString(escapeVal);
+ isUnicodeEscapeStart = false;
+ } else {
+ c = getChar();
+ if (c == '\\') {
+ c = getChar();
+ if (c == 'u') {
+ isUnicodeEscapeStart = true;
+ containsEscape = true;
+ } else {
+ /*APPJET*/
+ parser.addError("msg.illegal.character.appjet",
+ "\\"+Character.toString((char)c));
+ return Token.ERROR;
+ }
+ } else {
+ if (c == EOF_CHAR
+ || !Character.isJavaIdentifierPart((char)c))
+ {
+ break;
+ }
+ addToString(c);
+ }
+ }
+ }
+ ungetChar(c);
+
+ String str = getStringFromBuffer();
+ /*APPJET*//*MOVED*/this.string = (String)allStrings.intern(str);
+ /*APPJET this move lets the names of RESERVED tokens and other
+ tokens be determined, and also fixes broken yield/let parsing
+ under pre-1.7 JS */
+ if (!containsEscape) {
+ // OPT we shouldn't have to make a string (object!) to
+ // check if it's a keyword.
+
+ // Return the corresponding token if it's a keyword
+ int result = stringToKeyword(str);
+ if (result != Token.EOF) {
+ if ((result == Token.LET || result == Token.YIELD) &&
+ parser.compilerEnv.getLanguageVersion()
+ < Context.VERSION_1_7)
+ {
+ // LET and YIELD are tokens only in 1.7 and later
+ result = Token.NAME;
+ }
+ if (result != Token.RESERVED) {
+ return result;
+ } else if (!parser.compilerEnv.
+ isReservedKeywordAsIdentifier())
+ {
+ return result;
+ } else {
+ // If implementation permits to use future reserved
+ // keywords in violation with the EcmaScript,
+ // treat it as name but issue warning
+ parser.addWarning("msg.reserved.keyword", str);
+ }
+ }
+ }
+ return Token.NAME;
+ }
+
+ // is it a number?
+ if (isDigit(c) || (c == '.' && isDigit(peekChar()))) {
+
+ stringBufferTop = 0;
+ int base = 10;
+
+ if (c == '0') {
+ c = getChar();
+ if (c == 'x' || c == 'X') {
+ base = 16;
+ c = getChar();
+ } else if (isDigit(c)) {
+ base = 8;
+ } else {
+ addToString('0');
+ }
+ }
+
+ if (base == 16) {
+ while (0 <= Kit.xDigitToInt(c, 0)) {
+ addToString(c);
+ c = getChar();
+ }
+ } else {
+ while ('0' <= c && c <= '9') {
+ /*
+ * We permit 08 and 09 as decimal numbers, which
+ * makes our behavior a superset of the ECMA
+ * numeric grammar. We might not always be so
+ * permissive, so we warn about it.
+ */
+ if (base == 8 && c >= '8') {
+ parser.addWarning("msg.bad.octal.literal",
+ c == '8' ? "8" : "9");
+ base = 10;
+ }
+ addToString(c);
+ c = getChar();
+ }
+ }
+
+ boolean isInteger = true;
+
+ if (base == 10 && (c == '.' || c == 'e' || c == 'E')) {
+ isInteger = false;
+ if (c == '.') {
+ do {
+ addToString(c);
+ c = getChar();
+ } while (isDigit(c));
+ }
+ if (c == 'e' || c == 'E') {
+ addToString(c);
+ c = getChar();
+ if (c == '+' || c == '-') {
+ addToString(c);
+ c = getChar();
+ }
+ if (!isDigit(c)) {
+ parser.addError("msg.missing.exponent");
+ return Token.ERROR;
+ }
+ do {
+ addToString(c);
+ c = getChar();
+ } while (isDigit(c));
+ }
+ }
+ ungetChar(c);
+ String numString = getStringFromBuffer();
+
+ double dval;
+ if (base == 10 && !isInteger) {
+ try {
+ // Use Java conversion to number from string...
+ dval = Double.valueOf(numString).doubleValue();
+ }
+ catch (NumberFormatException ex) {
+ parser.addError("msg.caught.nfe");
+ return Token.ERROR;
+ }
+ } else {
+ dval = ScriptRuntime.stringToNumber(numString, 0, base);
+ }
+
+ this.number = dval;
+ return Token.NUMBER;
+ }
+
+ // is it a string?
+ if (c == '"' || c == '\'') {
+ // We attempt to accumulate a string the fast way, by
+ // building it directly out of the reader. But if there
+ // are any escaped characters in the string, we revert to
+ // building it out of a StringBuffer.
+
+ /*APPJET*/
+ if (c == '"' && tryReadTripleQuotedString()) {
+ return Token.STRING;
+ }
+
+ int quoteChar = c;
+ stringBufferTop = 0;
+
+ c = getChar();
+ strLoop: while (c != quoteChar) {
+ if (c == '\n' || c == EOF_CHAR) {
+ ungetChar(c);
+ /*APPJET*/
+ parser.addError("msg.unterminated.string.lit",
+ Character.toString((char)quoteChar));
+ return Token.ERROR;
+ }
+
+ if (c == '\\') {
+ // We've hit an escaped character
+ int escapeVal;
+
+ c = getChar();
+ switch (c) {
+ case 'b': c = '\b'; break;
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 't': c = '\t'; break;
+
+ // \v a late addition to the ECMA spec,
+ // it is not in Java, so use 0xb
+ case 'v': c = 0xb; break;
+
+ case 'u':
+ // Get 4 hex digits; if the u escape is not
+ // followed by 4 hex digits, use 'u' + the
+ // literal character sequence that follows.
+ int escapeStart = stringBufferTop;
+ addToString('u');
+ escapeVal = 0;
+ for (int i = 0; i != 4; ++i) {
+ c = getChar();
+ escapeVal = Kit.xDigitToInt(c, escapeVal);
+ if (escapeVal < 0) {
+ continue strLoop;
+ }
+ addToString(c);
+ }
+ // prepare for replace of stored 'u' sequence
+ // by escape value
+ stringBufferTop = escapeStart;
+ c = escapeVal;
+ break;
+ case 'x':
+ // Get 2 hex digits, defaulting to 'x'+literal
+ // sequence, as above.
+ c = getChar();
+ escapeVal = Kit.xDigitToInt(c, 0);
+ if (escapeVal < 0) {
+ addToString('x');
+ continue strLoop;
+ } else {
+ int c1 = c;
+ c = getChar();
+ escapeVal = Kit.xDigitToInt(c, escapeVal);
+ if (escapeVal < 0) {
+ addToString('x');
+ addToString(c1);
+ continue strLoop;
+ } else {
+ // got 2 hex digits
+ c = escapeVal;
+ }
+ }
+ break;
+
+ case '\n':
+ // Remove line terminator after escape to follow
+ // SpiderMonkey and C/C++
+ c = getChar();
+ continue strLoop;
+
+ default:
+ if ('0' <= c && c < '8') {
+ int val = c - '0';
+ c = getChar();
+ if ('0' <= c && c < '8') {
+ val = 8 * val + c - '0';
+ c = getChar();
+ if ('0' <= c && c < '8' && val <= 037) {
+ // c is 3rd char of octal sequence only
+ // if the resulting val <= 0377
+ val = 8 * val + c - '0';
+ c = getChar();
+ }
+ }
+ ungetChar(c);
+ c = val;
+ }
+ }
+ }
+ addToString(c);
+ c = getChar();
+ }
+
+ String str = getStringFromBuffer();
+ this.string = (String)allStrings.intern(str);
+ return Token.STRING;
+ }
+
+ switch (c) {
+ case ';': return Token.SEMI;
+ case '[': return Token.LB;
+ case ']': return Token.RB;
+ case '{': return Token.LC;
+ case '}': return Token.RC;
+ case '(': return Token.LP;
+ case ')': return Token.RP;
+ case ',': return Token.COMMA;
+ case '?': return Token.HOOK;
+ case ':':
+ if (matchChar(':')) {
+ return Token.COLONCOLON;
+ } else {
+ return Token.COLON;
+ }
+ case '.':
+ if (matchChar('.')) {
+ return Token.DOTDOT;
+ } else if (matchChar('(')) {
+ return Token.DOTQUERY;
+ } else {
+ return Token.DOT;
+ }
+
+ case '|':
+ if (matchChar('|')) {
+ return Token.OR;
+ } else if (matchChar('=')) {
+ return Token.ASSIGN_BITOR;
+ } else {
+ return Token.BITOR;
+ }
+
+ case '^':
+ if (matchChar('=')) {
+ return Token.ASSIGN_BITXOR;
+ } else {
+ return Token.BITXOR;
+ }
+
+ case '&':
+ if (matchChar('&')) {
+ return Token.AND;
+ } else if (matchChar('=')) {
+ return Token.ASSIGN_BITAND;
+ } else {
+ return Token.BITAND;
+ }
+
+ case '=':
+ if (matchChar('=')) {
+ if (matchChar('='))
+ return Token.SHEQ;
+ else
+ return Token.EQ;
+ } else {
+ return Token.ASSIGN;
+ }
+
+ case '!':
+ if (matchChar('=')) {
+ if (matchChar('='))
+ return Token.SHNE;
+ else
+ return Token.NE;
+ } else {
+ return Token.NOT;
+ }
+
+ case '<':
+ /* NB:treat HTML begin-comment as comment-till-eol */
+ if (matchChar('!')) {
+ if (matchChar('-')) {
+ if (matchChar('-')) {
+ skipLine();
+ continue retry;
+ }
+ ungetChar('-');
+ }
+ ungetChar('!');
+ }
+ if (matchChar('<')) {
+ if (matchChar('=')) {
+ return Token.ASSIGN_LSH;
+ } else {
+ return Token.LSH;
+ }
+ } else {
+ if (matchChar('=')) {
+ return Token.LE;
+ } else {
+ return Token.LT;
+ }
+ }
+
+ case '>':
+ if (matchChar('>')) {
+ if (matchChar('>')) {
+ if (matchChar('=')) {
+ return Token.ASSIGN_URSH;
+ } else {
+ return Token.URSH;
+ }
+ } else {
+ if (matchChar('=')) {
+ return Token.ASSIGN_RSH;
+ } else {
+ return Token.RSH;
+ }
+ }
+ } else {
+ if (matchChar('=')) {
+ return Token.GE;
+ } else {
+ return Token.GT;
+ }
+ }
+
+ case '*':
+ if (matchChar('=')) {
+ return Token.ASSIGN_MUL;
+ } else {
+ return Token.MUL;
+ }
+
+ case '/':
+ // is it a // comment?
+ if (matchChar('/')) {
+ skipLine();
+ continue retry;
+ }
+ if (matchChar('*')) {
+ boolean lookForSlash = false;
+ for (;;) {
+ c = getChar();
+ if (c == EOF_CHAR) {
+ parser.addError("msg.unterminated.comment");
+ return Token.ERROR;
+ } else if (c == '*') {
+ lookForSlash = true;
+ } else if (c == '/') {
+ if (lookForSlash) {
+ continue retry;
+ }
+ } else {
+ lookForSlash = false;
+ }
+ }
+ }
+
+ if (matchChar('=')) {
+ return Token.ASSIGN_DIV;
+ } else {
+ return Token.DIV;
+ }
+
+ case '%':
+ if (matchChar('=')) {
+ return Token.ASSIGN_MOD;
+ } else {
+ return Token.MOD;
+ }
+
+ case '~':
+ return Token.BITNOT;
+
+ case '+':
+ if (matchChar('=')) {
+ return Token.ASSIGN_ADD;
+ } else if (matchChar('+')) {
+ return Token.INC;
+ } else {
+ return Token.ADD;
+ }
+
+ case '-':
+ if (matchChar('=')) {
+ c = Token.ASSIGN_SUB;
+ } else if (matchChar('-')) {
+ if (!dirtyLine) {
+ // treat HTML end-comment after possible whitespace
+ // after line start as comment-utill-eol
+ if (matchChar('>')) {
+ skipLine();
+ continue retry;
+ }
+ }
+ c = Token.DEC;
+ } else {
+ c = Token.SUB;
+ }
+ dirtyLine = true;
+ return c;
+
+ default:
+ /*APPJET*/
+ parser.addError("msg.illegal.character.appjet",
+ Character.toString((char)c));
+ return Token.ERROR;
+ }
+ }
+ }
+
+ private static boolean isAlpha(int c)
+ {
+ // Use 'Z' < 'a'
+ if (c <= 'Z') {
+ return 'A' <= c;
+ } else {
+ return 'a' <= c && c <= 'z';
+ }
+ }
+
+ static boolean isDigit(int c)
+ {
+ return '0' <= c && c <= '9';
+ }
+
+ /* As defined in ECMA. jsscan.c uses C isspace() (which allows
+ * \v, I think.) note that code in getChar() implicitly accepts
+ * '\r' == \u000D as well.
+ */
+ static boolean isJSSpace(int c)
+ {
+ if (c <= 127) {
+ return c == 0x20 || c == 0x9 || c == 0xC || c == 0xB;
+ } else {
+ return c == 0xA0
+ || Character.getType((char)c) == Character.SPACE_SEPARATOR;
+ }
+ }
+
+ private static boolean isJSFormatChar(int c)
+ {
+ return c > 127 && Character.getType((char)c) == Character.FORMAT;
+ }
+
+ /**
+ * Parser calls the method when it gets / or /= in literal context.
+ */
+ void readRegExp(int startToken)
+ throws IOException
+ {
+ stringBufferTop = 0;
+ if (startToken == Token.ASSIGN_DIV) {
+ // Miss-scanned /=
+ addToString('=');
+ } else {
+ if (startToken != Token.DIV) Kit.codeBug();
+ }
+
+ int c;
+ while ((c = getChar()) != '/') {
+ if (c == '\n' || c == EOF_CHAR) {
+ ungetChar(c);
+ throw parser.reportError("msg.unterminated.re.lit");
+ }
+ if (c == '\\') {
+ addToString(c);
+ c = getChar();
+ }
+
+ addToString(c);
+ }
+ int reEnd = stringBufferTop;
+
+ while (true) {
+ if (matchChar('g'))
+ addToString('g');
+ else if (matchChar('i'))
+ addToString('i');
+ else if (matchChar('m'))
+ addToString('m');
+ else
+ break;
+ }
+
+ if (isAlpha(peekChar())) {
+ throw parser.reportError("msg.invalid.re.flag");
+ }
+
+ this.string = new String(stringBuffer, 0, reEnd);
+ this.regExpFlags = new String(stringBuffer, reEnd,
+ stringBufferTop - reEnd);
+ }
+
+ boolean isXMLAttribute()
+ {
+ return xmlIsAttribute;
+ }
+
+ int getFirstXMLToken() throws IOException
+ {
+ xmlOpenTagsCount = 0;
+ xmlIsAttribute = false;
+ xmlIsTagContent = false;
+ ungetChar('<');
+ return getNextXMLToken();
+ }
+
+ int getNextXMLToken() throws IOException
+ {
+ stringBufferTop = 0; // remember the XML
+
+ for (int c = getChar(); c != EOF_CHAR; c = getChar()) {
+ if (xmlIsTagContent) {
+ switch (c) {
+ case '>':
+ addToString(c);
+ xmlIsTagContent = false;
+ xmlIsAttribute = false;
+ break;
+ case '/':
+ addToString(c);
+ if (peekChar() == '>') {
+ c = getChar();
+ addToString(c);
+ xmlIsTagContent = false;
+ xmlOpenTagsCount--;
+ }
+ break;
+ case '{':
+ ungetChar(c);
+ this.string = getStringFromBuffer();
+ return Token.XML;
+ case '\'':
+ case '"':
+ addToString(c);
+ if (!readQuotedString(c)) return Token.ERROR;
+ break;
+ case '=':
+ addToString(c);
+ xmlIsAttribute = true;
+ break;
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ addToString(c);
+ break;
+ default:
+ addToString(c);
+ xmlIsAttribute = false;
+ break;
+ }
+
+ if (!xmlIsTagContent && xmlOpenTagsCount == 0) {
+ this.string = getStringFromBuffer();
+ return Token.XMLEND;
+ }
+ } else {
+ switch (c) {
+ case '<':
+ addToString(c);
+ c = peekChar();
+ switch (c) {
+ case '!':
+ c = getChar(); // Skip !
+ addToString(c);
+ c = peekChar();
+ switch (c) {
+ case '-':
+ c = getChar(); // Skip -
+ addToString(c);
+ c = getChar();
+ if (c == '-') {
+ addToString(c);
+ if(!readXmlComment()) return Token.ERROR;
+ } else {
+ // throw away the string in progress
+ stringBufferTop = 0;
+ this.string = null;
+ parser.addError("msg.XML.bad.form");
+ return Token.ERROR;
+ }
+ break;
+ case '[':
+ c = getChar(); // Skip [
+ addToString(c);
+ if (getChar() == 'C' &&
+ getChar() == 'D' &&
+ getChar() == 'A' &&
+ getChar() == 'T' &&
+ getChar() == 'A' &&
+ getChar() == '[')
+ {
+ addToString('C');
+ addToString('D');
+ addToString('A');
+ addToString('T');
+ addToString('A');
+ addToString('[');
+ if (!readCDATA()) return Token.ERROR;
+
+ } else {
+ // throw away the string in progress
+ stringBufferTop = 0;
+ this.string = null;
+ parser.addError("msg.XML.bad.form");
+ return Token.ERROR;
+ }
+ break;
+ default:
+ if(!readEntity()) return Token.ERROR;
+ break;
+ }
+ break;
+ case '?':
+ c = getChar(); // Skip ?
+ addToString(c);
+ if (!readPI()) return Token.ERROR;
+ break;
+ case '/':
+ // End tag
+ c = getChar(); // Skip /
+ addToString(c);
+ if (xmlOpenTagsCount == 0) {
+ // throw away the string in progress
+ stringBufferTop = 0;
+ this.string = null;
+ parser.addError("msg.XML.bad.form");
+ return Token.ERROR;
+ }
+ xmlIsTagContent = true;
+ xmlOpenTagsCount--;
+ break;
+ default:
+ // Start tag
+ xmlIsTagContent = true;
+ xmlOpenTagsCount++;
+ break;
+ }
+ break;
+ case '{':
+ ungetChar(c);
+ this.string = getStringFromBuffer();
+ return Token.XML;
+ default:
+ addToString(c);
+ break;
+ }
+ }
+ }
+
+ stringBufferTop = 0; // throw away the string in progress
+ this.string = null;
+ parser.addError("msg.XML.bad.form");
+ return Token.ERROR;
+ }
+
+ /**
+ *
+ */
+ private boolean readQuotedString(int quote) throws IOException
+ {
+ for (int c = getChar(); c != EOF_CHAR; c = getChar()) {
+ addToString(c);
+ if (c == quote) return true;
+ }
+
+ stringBufferTop = 0; // throw away the string in progress
+ this.string = null;
+ parser.addError("msg.XML.bad.form");
+ return false;
+ }
+
+ /**
+ *
+ */
+ private boolean readXmlComment() throws IOException
+ {
+ for (int c = getChar(); c != EOF_CHAR;) {
+ addToString(c);
+ if (c == '-' && peekChar() == '-') {
+ c = getChar();
+ addToString(c);
+ if (peekChar() == '>') {
+ c = getChar(); // Skip >
+ addToString(c);
+ return true;
+ } else {
+ continue;
+ }
+ }
+ c = getChar();
+ }
+
+ stringBufferTop = 0; // throw away the string in progress
+ this.string = null;
+ parser.addError("msg.XML.bad.form");
+ return false;
+ }
+
+ /**
+ *
+ */
+ private boolean readCDATA() throws IOException
+ {
+ for (int c = getChar(); c != EOF_CHAR;) {
+ addToString(c);
+ if (c == ']' && peekChar() == ']') {
+ c = getChar();
+ addToString(c);
+ if (peekChar() == '>') {
+ c = getChar(); // Skip >
+ addToString(c);
+ return true;
+ } else {
+ continue;
+ }
+ }
+ c = getChar();
+ }
+
+ stringBufferTop = 0; // throw away the string in progress
+ this.string = null;
+ parser.addError("msg.XML.bad.form");
+ return false;
+ }
+
+ /**
+ *
+ */
+ private boolean readEntity() throws IOException
+ {
+ int declTags = 1;
+ for (int c = getChar(); c != EOF_CHAR; c = getChar()) {
+ addToString(c);
+ switch (c) {
+ case '<':
+ declTags++;
+ break;
+ case '>':
+ declTags--;
+ if (declTags == 0) return true;
+ break;
+ }
+ }
+
+ stringBufferTop = 0; // throw away the string in progress
+ this.string = null;
+ parser.addError("msg.XML.bad.form");
+ return false;
+ }
+
+ /**
+ *
+ */
+ private boolean readPI() throws IOException
+ {
+ for (int c = getChar(); c != EOF_CHAR; c = getChar()) {
+ addToString(c);
+ if (c == '?' && peekChar() == '>') {
+ c = getChar(); // Skip >
+ addToString(c);
+ return true;
+ }
+ }
+
+ stringBufferTop = 0; // throw away the string in progress
+ this.string = null;
+ parser.addError("msg.XML.bad.form");
+ return false;
+ }
+
+ private String getStringFromBuffer()
+ {
+ return new String(stringBuffer, 0, stringBufferTop);
+ }
+
+ private void addToString(int c)
+ {
+ int N = stringBufferTop;
+ if (N == stringBuffer.length) {
+ char[] tmp = new char[stringBuffer.length * 2];
+ System.arraycopy(stringBuffer, 0, tmp, 0, N);
+ stringBuffer = tmp;
+ }
+ stringBuffer[N] = (char)c;
+ stringBufferTop = N + 1;
+ }
+
+ private void ungetChar(int c)
+ {
+ // can not unread past across line boundary
+ if (ungetCursor != 0 && ungetBuffer[ungetCursor - 1] == '\n')
+ Kit.codeBug();
+ ungetBuffer[ungetCursor++] = c;
+ }
+
+ private boolean matchChar(int test) throws IOException
+ {
+ int c = getChar();
+ if (c == test) {
+ return true;
+ } else {
+ ungetChar(c);
+ return false;
+ }
+ }
+
+ private int peekChar() throws IOException
+ {
+ int c = getChar();
+ ungetChar(c);
+ return c;
+ }
+
+ private int getChar() throws IOException
+ {
+ if (ungetCursor != 0) {
+ return ungetBuffer[--ungetCursor];
+ }
+
+ for(;;) {
+ int c;
+ if (sourceString != null) {
+ if (sourceCursor == sourceEnd) {
+ hitEOF = true;
+ return EOF_CHAR;
+ }
+ c = sourceString.charAt(sourceCursor++);
+ } else {
+ if (sourceCursor == sourceEnd) {
+ if (!fillSourceBuffer()) {
+ hitEOF = true;
+ return EOF_CHAR;
+ }
+ }
+ c = sourceBuffer[sourceCursor++];
+ }
+
+ if (lineEndChar >= 0) {
+ if (lineEndChar == '\r' && c == '\n') {
+ lineEndChar = '\n';
+ continue;
+ }
+ lineEndChar = -1;
+ lineStart = sourceCursor - 1;
+ lineno++;
+ }
+
+ if (c <= 127) {
+ if (c == '\n' || c == '\r') {
+ lineEndChar = c;
+ c = '\n';
+ }
+ } else {
+ if (isJSFormatChar(c)) {
+ continue;
+ }
+ if (ScriptRuntime.isJSLineTerminator(c)) {
+ lineEndChar = c;
+ c = '\n';
+ }
+ }
+ return c;
+ }
+ }
+
+ private void skipLine() throws IOException
+ {
+ // skip to end of line
+ int c;
+ while ((c = getChar()) != EOF_CHAR && c != '\n') { }
+ ungetChar(c);
+ }
+
+ final int getOffset()
+ {
+ int n = sourceCursor - lineStart;
+ if (lineEndChar >= 0) { --n; }
+ return n;
+ }
+
+ final String getLine()
+ {
+ if (sourceString != null) {
+ // String case
+ int lineEnd = sourceCursor;
+ if (lineEndChar >= 0) {
+ --lineEnd;
+ } else {
+ for (; lineEnd != sourceEnd; ++lineEnd) {
+ int c = sourceString.charAt(lineEnd);
+ if (ScriptRuntime.isJSLineTerminator(c)) {
+ break;
+ }
+ }
+ }
+ return sourceString.substring(lineStart, lineEnd);
+ } else {
+ // Reader case
+ int lineLength = sourceCursor - lineStart;
+ if (lineEndChar >= 0) {
+ --lineLength;
+ } else {
+ // Read until the end of line
+ for (;; ++lineLength) {
+ int i = lineStart + lineLength;
+ if (i == sourceEnd) {
+ try {
+ if (!fillSourceBuffer()) { break; }
+ } catch (IOException ioe) {
+ // ignore it, we're already displaying an error...
+ break;
+ }
+ // i recalculuation as fillSourceBuffer can move saved
+ // line buffer and change lineStart
+ i = lineStart + lineLength;
+ }
+ int c = sourceBuffer[i];
+ if (ScriptRuntime.isJSLineTerminator(c)) {
+ break;
+ }
+ }
+ }
+ return new String(sourceBuffer, lineStart, lineLength);
+ }
+ }
+
+ private boolean fillSourceBuffer() throws IOException
+ {
+ if (sourceString != null) Kit.codeBug();
+ if (sourceEnd == sourceBuffer.length) {
+ if (lineStart != 0) {
+ System.arraycopy(sourceBuffer, lineStart, sourceBuffer, 0,
+ sourceEnd - lineStart);
+ sourceEnd -= lineStart;
+ sourceCursor -= lineStart;
+ lineStart = 0;
+ } else {
+ char[] tmp = new char[sourceBuffer.length * 2];
+ System.arraycopy(sourceBuffer, 0, tmp, 0, sourceEnd);
+ sourceBuffer = tmp;
+ }
+ }
+ int n = sourceReader.read(sourceBuffer, sourceEnd,
+ sourceBuffer.length - sourceEnd);
+ if (n < 0) {
+ return false;
+ }
+ sourceEnd += n;
+ return true;
+ }
+
+ // stuff other than whitespace since start of line
+ private boolean dirtyLine;
+
+ String regExpFlags;
+
+ // Set this to an inital non-null value so that the Parser has
+ // something to retrieve even if an error has occured and no
+ // string is found. Fosters one class of error, but saves lots of
+ // code.
+ private String string = "";
+ private double number;
+
+ private char[] stringBuffer = new char[128];
+ private int stringBufferTop;
+ private ObjToIntMap allStrings = new ObjToIntMap(50);
+
+ // Room to backtrace from to < on failed match of the last - in <!--
+ private final int[] ungetBuffer = new int[3];
+ private int ungetCursor;
+
+ private boolean hitEOF = false;
+
+ private int lineStart = 0;
+ private int lineno;
+ private int lineEndChar = -1;
+
+ private String sourceString;
+ private Reader sourceReader;
+ private char[] sourceBuffer;
+ private int sourceEnd;
+ private int sourceCursor;
+
+ // for xml tokenizer
+ private boolean xmlIsAttribute;
+ private boolean xmlIsTagContent;
+ private int xmlOpenTagsCount;
+
+ private Parser parser;
+
+ /*APPJET*//* added method */
+ private boolean tryReadTripleQuotedString() throws IOException {
+ // have to be kind of clever, because we don't want to
+ // add to the overhead of tokenizing, but there are constraints
+ // like "once you've gotten a newline and put it back (ungotten it),
+ // you can't put anything else back"
+
+ // have seen one quote
+ int c = getChar();
+ if (c != '"') { ungetChar(c); return false; }
+ // have seen two quotes
+ c = getChar();
+ if (c != '"') {
+ ungetChar(c);
+ this.string = (String)allStrings.intern("");
+ return true;
+ }
+ // have seen three quotes
+
+ final boolean newPolicy = false;
+
+ stringBufferTop = 0;
+
+ boolean afterBackslash = false;
+ boolean done = false;
+ while (! done) {
+ c = getChar();
+ if (c == EOF_CHAR) {
+ ungetChar(c);
+ throw parser.reportError("msg.unterminated.mstring.appjet");
+ }
+ if (c == '"') {
+ int quoteCount = 0;
+ int quoteLimit = 3;
+ if (newPolicy && ! afterBackslash)
+ quoteLimit = 5;
+ while (c == '"' && quoteCount < quoteLimit) {
+ quoteCount++;
+ c = getChar();
+ }
+ ungetChar(c);
+
+ if (afterBackslash) {
+ if (quoteCount < 3) addToString('\\');
+ }
+ else if (quoteCount >= 3) {
+ quoteCount -= 3;
+ done = true;
+ }
+
+ while (quoteCount > 0) {
+ addToString('"'); quoteCount--;
+ }
+ afterBackslash = false;
+ }
+ else {
+ if (afterBackslash) {
+ addToString('\\');
+ afterBackslash = false;
+ }
+ if (c == '\\') {
+ afterBackslash = true;
+ }
+ else {
+ addToString(c);
+ }
+ }
+ }
+
+ String str = getStringFromBuffer();
+ this.string = (String)allStrings.intern(str);
+
+ return true;
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/UintMap.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/UintMap.java
new file mode 100644
index 0000000..0027819
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/UintMap.java
@@ -0,0 +1,659 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.io.Serializable;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+/**
+ * Map to associate non-negative integers to objects or integers.
+ * The map does not synchronize any of its operation, so either use
+ * it from a single thread or do own synchronization or perform all mutation
+ * operations on one thread before passing the map to others.
+ *
+ * @author Igor Bukanov
+ *
+ */
+
+public class UintMap implements Serializable
+{
+ static final long serialVersionUID = 4242698212885848444L;
+
+// Map implementation via hashtable,
+// follows "The Art of Computer Programming" by Donald E. Knuth
+
+ public UintMap() {
+ this(4);
+ }
+
+ public UintMap(int initialCapacity) {
+ if (initialCapacity < 0) Kit.codeBug();
+ // Table grow when number of stored keys >= 3/4 of max capacity
+ int minimalCapacity = initialCapacity * 4 / 3;
+ int i;
+ for (i = 2; (1 << i) < minimalCapacity; ++i) { }
+ power = i;
+ if (check && power < 2) Kit.codeBug();
+ }
+
+ public boolean isEmpty() {
+ return keyCount == 0;
+ }
+
+ public int size() {
+ return keyCount;
+ }
+
+ public boolean has(int key) {
+ if (key < 0) Kit.codeBug();
+ return 0 <= findIndex(key);
+ }
+
+ /**
+ * Get object value assigned with key.
+ * @return key object value or null if key is absent
+ */
+ public Object getObject(int key) {
+ if (key < 0) Kit.codeBug();
+ if (values != null) {
+ int index = findIndex(key);
+ if (0 <= index) {
+ return values[index];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get integer value assigned with key.
+ * @return key integer value or defaultValue if key is absent
+ */
+ public int getInt(int key, int defaultValue) {
+ if (key < 0) Kit.codeBug();
+ int index = findIndex(key);
+ if (0 <= index) {
+ if (ivaluesShift != 0) {
+ return keys[ivaluesShift + index];
+ }
+ return 0;
+ }
+ return defaultValue;
+ }
+
+ /**
+ * Get integer value assigned with key.
+ * @return key integer value or defaultValue if key does not exist or does
+ * not have int value
+ * @throws RuntimeException if key does not exist
+ */
+ public int getExistingInt(int key) {
+ if (key < 0) Kit.codeBug();
+ int index = findIndex(key);
+ if (0 <= index) {
+ if (ivaluesShift != 0) {
+ return keys[ivaluesShift + index];
+ }
+ return 0;
+ }
+ // Key must exist
+ Kit.codeBug();
+ return 0;
+ }
+
+ /**
+ * Set object value of the key.
+ * If key does not exist, also set its int value to 0.
+ */
+ public void put(int key, Object value) {
+ if (key < 0) Kit.codeBug();
+ int index = ensureIndex(key, false);
+ if (values == null) {
+ values = new Object[1 << power];
+ }
+ values[index] = value;
+ }
+
+ /**
+ * Set int value of the key.
+ * If key does not exist, also set its object value to null.
+ */
+ public void put(int key, int value) {
+ if (key < 0) Kit.codeBug();
+ int index = ensureIndex(key, true);
+ if (ivaluesShift == 0) {
+ int N = 1 << power;
+ // keys.length can be N * 2 after clear which set ivaluesShift to 0
+ if (keys.length != N * 2) {
+ int[] tmp = new int[N * 2];
+ System.arraycopy(keys, 0, tmp, 0, N);
+ keys = tmp;
+ }
+ ivaluesShift = N;
+ }
+ keys[ivaluesShift + index] = value;
+ }
+
+ public void remove(int key) {
+ if (key < 0) Kit.codeBug();
+ int index = findIndex(key);
+ if (0 <= index) {
+ keys[index] = DELETED;
+ --keyCount;
+ // Allow to GC value and make sure that new key with the deleted
+ // slot shall get proper default values
+ if (values != null) { values[index] = null; }
+ if (ivaluesShift != 0) { keys[ivaluesShift + index] = 0; }
+ }
+ }
+
+ public void clear() {
+ int N = 1 << power;
+ if (keys != null) {
+ for (int i = 0; i != N; ++i) {
+ keys[i] = EMPTY;
+ }
+ if (values != null) {
+ for (int i = 0; i != N; ++i) {
+ values[i] = null;
+ }
+ }
+ }
+ ivaluesShift = 0;
+ keyCount = 0;
+ occupiedCount = 0;
+ }
+
+ /** Return array of present keys */
+ public int[] getKeys() {
+ int[] keys = this.keys;
+ int n = keyCount;
+ int[] result = new int[n];
+ for (int i = 0; n != 0; ++i) {
+ int entry = keys[i];
+ if (entry != EMPTY && entry != DELETED) {
+ result[--n] = entry;
+ }
+ }
+ return result;
+ }
+
+ private static int tableLookupStep(int fraction, int mask, int power) {
+ int shift = 32 - 2 * power;
+ if (shift >= 0) {
+ return ((fraction >>> shift) & mask) | 1;
+ }
+ else {
+ return (fraction & (mask >>> -shift)) | 1;
+ }
+ }
+
+ private int findIndex(int key) {
+ int[] keys = this.keys;
+ if (keys != null) {
+ int fraction = key * A;
+ int index = fraction >>> (32 - power);
+ int entry = keys[index];
+ if (entry == key) { return index; }
+ if (entry != EMPTY) {
+ // Search in table after first failed attempt
+ int mask = (1 << power) - 1;
+ int step = tableLookupStep(fraction, mask, power);
+ int n = 0;
+ do {
+ if (check) {
+ if (n >= occupiedCount) Kit.codeBug();
+ ++n;
+ }
+ index = (index + step) & mask;
+ entry = keys[index];
+ if (entry == key) { return index; }
+ } while (entry != EMPTY);
+ }
+ }
+ return -1;
+ }
+
+// Insert key that is not present to table without deleted entries
+// and enough free space
+ private int insertNewKey(int key) {
+ if (check && occupiedCount != keyCount) Kit.codeBug();
+ if (check && keyCount == 1 << power) Kit.codeBug();
+ int[] keys = this.keys;
+ int fraction = key * A;
+ int index = fraction >>> (32 - power);
+ if (keys[index] != EMPTY) {
+ int mask = (1 << power) - 1;
+ int step = tableLookupStep(fraction, mask, power);
+ int firstIndex = index;
+ do {
+ if (check && keys[index] == DELETED) Kit.codeBug();
+ index = (index + step) & mask;
+ if (check && firstIndex == index) Kit.codeBug();
+ } while (keys[index] != EMPTY);
+ }
+ keys[index] = key;
+ ++occupiedCount;
+ ++keyCount;
+ return index;
+ }
+
+ private void rehashTable(boolean ensureIntSpace) {
+ if (keys != null) {
+ // Check if removing deleted entries would free enough space
+ if (keyCount * 2 >= occupiedCount) {
+ // Need to grow: less then half of deleted entries
+ ++power;
+ }
+ }
+ int N = 1 << power;
+ int[] old = keys;
+ int oldShift = ivaluesShift;
+ if (oldShift == 0 && !ensureIntSpace) {
+ keys = new int[N];
+ }
+ else {
+ ivaluesShift = N; keys = new int[N * 2];
+ }
+ for (int i = 0; i != N; ++i) { keys[i] = EMPTY; }
+
+ Object[] oldValues = values;
+ if (oldValues != null) { values = new Object[N]; }
+
+ int oldCount = keyCount;
+ occupiedCount = 0;
+ if (oldCount != 0) {
+ keyCount = 0;
+ for (int i = 0, remaining = oldCount; remaining != 0; ++i) {
+ int key = old[i];
+ if (key != EMPTY && key != DELETED) {
+ int index = insertNewKey(key);
+ if (oldValues != null) {
+ values[index] = oldValues[i];
+ }
+ if (oldShift != 0) {
+ keys[ivaluesShift + index] = old[oldShift + i];
+ }
+ --remaining;
+ }
+ }
+ }
+ }
+
+// Ensure key index creating one if necessary
+ private int ensureIndex(int key, boolean intType) {
+ int index = -1;
+ int firstDeleted = -1;
+ int[] keys = this.keys;
+ if (keys != null) {
+ int fraction = key * A;
+ index = fraction >>> (32 - power);
+ int entry = keys[index];
+ if (entry == key) { return index; }
+ if (entry != EMPTY) {
+ if (entry == DELETED) { firstDeleted = index; }
+ // Search in table after first failed attempt
+ int mask = (1 << power) - 1;
+ int step = tableLookupStep(fraction, mask, power);
+ int n = 0;
+ do {
+ if (check) {
+ if (n >= occupiedCount) Kit.codeBug();
+ ++n;
+ }
+ index = (index + step) & mask;
+ entry = keys[index];
+ if (entry == key) { return index; }
+ if (entry == DELETED && firstDeleted < 0) {
+ firstDeleted = index;
+ }
+ } while (entry != EMPTY);
+ }
+ }
+ // Inserting of new key
+ if (check && keys != null && keys[index] != EMPTY)
+ Kit.codeBug();
+ if (firstDeleted >= 0) {
+ index = firstDeleted;
+ }
+ else {
+ // Need to consume empty entry: check occupation level
+ if (keys == null || occupiedCount * 4 >= (1 << power) * 3) {
+ // Too litle unused entries: rehash
+ rehashTable(intType);
+ keys = this.keys;
+ return insertNewKey(key);
+ }
+ ++occupiedCount;
+ }
+ keys[index] = key;
+ ++keyCount;
+ return index;
+ }
+
+ private void writeObject(ObjectOutputStream out)
+ throws IOException
+ {
+ out.defaultWriteObject();
+
+ int count = keyCount;
+ if (count != 0) {
+ boolean hasIntValues = (ivaluesShift != 0);
+ boolean hasObjectValues = (values != null);
+ out.writeBoolean(hasIntValues);
+ out.writeBoolean(hasObjectValues);
+
+ for (int i = 0; count != 0; ++i) {
+ int key = keys[i];
+ if (key != EMPTY && key != DELETED) {
+ --count;
+ out.writeInt(key);
+ if (hasIntValues) {
+ out.writeInt(keys[ivaluesShift + i]);
+ }
+ if (hasObjectValues) {
+ out.writeObject(values[i]);
+ }
+ }
+ }
+ }
+ }
+
+ private void readObject(ObjectInputStream in)
+ throws IOException, ClassNotFoundException
+ {
+ in.defaultReadObject();
+
+ int writtenKeyCount = keyCount;
+ if (writtenKeyCount != 0) {
+ keyCount = 0;
+ boolean hasIntValues = in.readBoolean();
+ boolean hasObjectValues = in.readBoolean();
+
+ int N = 1 << power;
+ if (hasIntValues) {
+ keys = new int[2 * N];
+ ivaluesShift = N;
+ }else {
+ keys = new int[N];
+ }
+ for (int i = 0; i != N; ++i) {
+ keys[i] = EMPTY;
+ }
+ if (hasObjectValues) {
+ values = new Object[N];
+ }
+ for (int i = 0; i != writtenKeyCount; ++i) {
+ int key = in.readInt();
+ int index = insertNewKey(key);
+ if (hasIntValues) {
+ int ivalue = in.readInt();
+ keys[ivaluesShift + index] = ivalue;
+ }
+ if (hasObjectValues) {
+ values[index] = in.readObject();
+ }
+ }
+ }
+ }
+
+// A == golden_ratio * (1 << 32) = ((sqrt(5) - 1) / 2) * (1 << 32)
+// See Knuth etc.
+ private static final int A = 0x9e3779b9;
+
+ private static final int EMPTY = -1;
+ private static final int DELETED = -2;
+
+// Structure of kyes and values arrays (N == 1 << power):
+// keys[0 <= i < N]: key value or EMPTY or DELETED mark
+// values[0 <= i < N]: value of key at keys[i]
+// keys[N <= i < 2N]: int values of keys at keys[i - N]
+
+ private transient int[] keys;
+ private transient Object[] values;
+
+ private int power;
+ private int keyCount;
+ private transient int occupiedCount; // == keyCount + deleted_count
+
+ // If ivaluesShift != 0, keys[ivaluesShift + index] contains integer
+ // values associated with keys
+ private transient int ivaluesShift;
+
+// If true, enables consitency checks
+ private static final boolean check = false;
+
+/* TEST START
+
+ public static void main(String[] args) {
+ if (!check) {
+ System.err.println("Set check to true and re-run");
+ throw new RuntimeException("Set check to true and re-run");
+ }
+
+ UintMap map;
+ map = new UintMap();
+ testHash(map, 2);
+ map = new UintMap();
+ testHash(map, 10 * 1000);
+ map = new UintMap(30 * 1000);
+ testHash(map, 10 * 100);
+ map.clear();
+ testHash(map, 4);
+ map = new UintMap(0);
+ testHash(map, 10 * 100);
+ }
+
+ private static void testHash(UintMap map, int N) {
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ map.put(i, i);
+ check(i == map.getInt(i, -1));
+ }
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ map.put(i, i);
+ check(i == map.getInt(i, -1));
+ }
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ map.put(i, new Integer(i));
+ check(-1 == map.getInt(i, -1));
+ Integer obj = (Integer)map.getObject(i);
+ check(obj != null && i == obj.intValue());
+ }
+
+ check(map.size() == N);
+
+ System.out.print("."); System.out.flush();
+ int[] keys = map.getKeys();
+ check(keys.length == N);
+ for (int i = 0; i != N; ++i) {
+ int key = keys[i];
+ check(map.has(key));
+ check(!map.isIntType(key));
+ check(map.isObjectType(key));
+ Integer obj = (Integer) map.getObject(key);
+ check(obj != null && key == obj.intValue());
+ }
+
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ check(-1 == map.getInt(i, -1));
+ }
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ map.put(i * i, i);
+ check(i == map.getInt(i * i, -1));
+ }
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ check(i == map.getInt(i * i, -1));
+ }
+
+ System.out.print("."); System.out.flush();
+ for (int i = 0; i != N; ++i) {
+ map.put(i * i, new Integer(i));
+ check(-1 == map.getInt(i * i, -1));
+ map.remove(i * i);
+ check(!map.has(i * i));
+ map.put(i * i, i);
+ check(map.isIntType(i * i));
+ check(null == map.getObject(i * i));
+ map.remove(i * i);
+ check(!map.isObjectType(i * i));
+ check(!map.isIntType(i * i));
+ }
+
+ int old_size = map.size();
+ for (int i = 0; i != N; ++i) {
+ map.remove(i * i);
+ check(map.size() == old_size);
+ }
+
+ System.out.print("."); System.out.flush();
+ map.clear();
+ check(map.size() == 0);
+ for (int i = 0; i != N; ++i) {
+ map.put(i * i, i);
+ map.put(i * i + 1, new Double(i+0.5));
+ }
+ checkSameMaps(map, (UintMap)writeAndRead(map));
+
+ System.out.print("."); System.out.flush();
+ map = new UintMap(0);
+ checkSameMaps(map, (UintMap)writeAndRead(map));
+ map = new UintMap(1);
+ checkSameMaps(map, (UintMap)writeAndRead(map));
+ map = new UintMap(1000);
+ checkSameMaps(map, (UintMap)writeAndRead(map));
+
+ System.out.print("."); System.out.flush();
+ map = new UintMap(N / 10);
+ for (int i = 0; i != N; ++i) {
+ map.put(2*i+1, i);
+ }
+ checkSameMaps(map, (UintMap)writeAndRead(map));
+
+ System.out.print("."); System.out.flush();
+ map = new UintMap(N / 10);
+ for (int i = 0; i != N; ++i) {
+ map.put(2*i+1, i);
+ }
+ for (int i = 0; i != N / 2; ++i) {
+ map.remove(2*i+1);
+ }
+ checkSameMaps(map, (UintMap)writeAndRead(map));
+
+ System.out.print("."); System.out.flush();
+ map = new UintMap();
+ for (int i = 0; i != N; ++i) {
+ map.put(2*i+1, new Double(i + 10));
+ }
+ for (int i = 0; i != N / 2; ++i) {
+ map.remove(2*i+1);
+ }
+ checkSameMaps(map, (UintMap)writeAndRead(map));
+
+ System.out.println(); System.out.flush();
+
+ }
+
+ private static void checkSameMaps(UintMap map1, UintMap map2) {
+ check(map1.size() == map2.size());
+ int[] keys = map1.getKeys();
+ check(keys.length == map1.size());
+ for (int i = 0; i != keys.length; ++i) {
+ int key = keys[i];
+ check(map2.has(key));
+ check(map1.isObjectType(key) == map2.isObjectType(key));
+ check(map1.isIntType(key) == map2.isIntType(key));
+ Object o1 = map1.getObject(key);
+ Object o2 = map2.getObject(key);
+ if (map1.isObjectType(key)) {
+ check(o1.equals(o2));
+ }else {
+ check(map1.getObject(key) == null);
+ check(map2.getObject(key) == null);
+ }
+ if (map1.isIntType(key)) {
+ check(map1.getExistingInt(key) == map2.getExistingInt(key));
+ }else {
+ check(map1.getInt(key, -10) == -10);
+ check(map1.getInt(key, -11) == -11);
+ check(map2.getInt(key, -10) == -10);
+ check(map2.getInt(key, -11) == -11);
+ }
+ }
+ }
+
+ private static void check(boolean condition) {
+ if (!condition) Kit.codeBug();
+ }
+
+ private static Object writeAndRead(Object obj) {
+ try {
+ java.io.ByteArrayOutputStream
+ bos = new java.io.ByteArrayOutputStream();
+ java.io.ObjectOutputStream
+ out = new java.io.ObjectOutputStream(bos);
+ out.writeObject(obj);
+ out.close();
+ byte[] data = bos.toByteArray();
+ java.io.ByteArrayInputStream
+ bis = new java.io.ByteArrayInputStream(data);
+ java.io.ObjectInputStream
+ in = new java.io.ObjectInputStream(bis);
+ Object result = in.readObject();
+ in.close();
+ return result;
+ }catch (Exception ex) {
+ ex.printStackTrace();
+ throw new RuntimeException("Unexpected");
+ }
+ }
+
+// TEST END */
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Undefined.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Undefined.java
new file mode 100644
index 0000000..472f26c
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Undefined.java
@@ -0,0 +1,60 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.io.Serializable;
+
+/**
+ * This class implements the Undefined value in JavaScript.
+ */
+public class Undefined implements Serializable
+{
+ static final long serialVersionUID = 9195680630202616767L;
+
+ public static final Object instance = new Undefined();
+
+ private Undefined()
+ {
+ }
+
+ public Object readResolve()
+ {
+ return instance;
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/UniqueTag.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/UniqueTag.java
new file mode 100644
index 0000000..33f96eb
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/UniqueTag.java
@@ -0,0 +1,120 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.io.Serializable;
+
+/**
+ * Class instances represent serializable tags to mark special Object values.
+ * <p>
+ * Compatibility note: under jdk 1.1 use
+ * org.mozilla.javascript.serialize.ScriptableInputStream to read serialized
+ * instances of UniqueTag as under this JDK version the default
+ * ObjectInputStream would not restore them correctly as it lacks support
+ * for readResolve method
+ */
+public final class UniqueTag implements Serializable
+{
+ static final long serialVersionUID = -4320556826714577259L;
+
+ private static final int ID_NOT_FOUND = 1;
+ private static final int ID_NULL_VALUE = 2;
+ private static final int ID_DOUBLE_MARK = 3;
+
+ /**
+ * Tag to mark non-existing values.
+ */
+ public static final UniqueTag
+ NOT_FOUND = new UniqueTag(ID_NOT_FOUND);
+
+ /**
+ * Tag to distinguish between uninitialized and null values.
+ */
+ public static final UniqueTag
+ NULL_VALUE = new UniqueTag(ID_NULL_VALUE);
+
+ /**
+ * Tag to indicate that a object represents "double" with the real value
+ * stored somewhere else.
+ */
+ public static final UniqueTag
+ DOUBLE_MARK = new UniqueTag(ID_DOUBLE_MARK);
+
+ private final int tagId;
+
+ private UniqueTag(int tagId)
+ {
+ this.tagId = tagId;
+ }
+
+ public Object readResolve()
+ {
+ switch (tagId) {
+ case ID_NOT_FOUND:
+ return NOT_FOUND;
+ case ID_NULL_VALUE:
+ return NULL_VALUE;
+ case ID_DOUBLE_MARK:
+ return DOUBLE_MARK;
+ }
+ throw new IllegalStateException(String.valueOf(tagId));
+ }
+
+// Overridden for better debug printouts
+ public String toString()
+ {
+ String name;
+ switch (tagId) {
+ case ID_NOT_FOUND:
+ name = "NOT_FOUND";
+ break;
+ case ID_NULL_VALUE:
+ name = "NULL_VALUE";
+ break;
+ case ID_DOUBLE_MARK:
+ name = "DOUBLE_MARK";
+ break;
+ default:
+ throw Kit.codeBug();
+ }
+ return super.toString()+": "+name;
+ }
+
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/VMBridge.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/VMBridge.java
new file mode 100644
index 0000000..5fba4a5
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/VMBridge.java
@@ -0,0 +1,183 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// API class
+
+package org.mozilla.javascript;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Member;
+import java.util.Iterator;
+
+public abstract class VMBridge
+{
+
+ static final VMBridge instance = makeInstance();
+
+ private static VMBridge makeInstance()
+ {
+ String[] classNames = {
+ "org.mozilla.javascript.VMBridge_custom",
+ "org.mozilla.javascript.jdk15.VMBridge_jdk15",
+ "org.mozilla.javascript.jdk13.VMBridge_jdk13",
+ "org.mozilla.javascript.jdk11.VMBridge_jdk11",
+ };
+ for (int i = 0; i != classNames.length; ++i) {
+ String className = classNames[i];
+ Class cl = Kit.classOrNull(className);
+ if (cl != null) {
+ VMBridge bridge = (VMBridge)Kit.newInstanceOrNull(cl);
+ if (bridge != null) {
+ return bridge;
+ }
+ }
+ }
+ throw new IllegalStateException("Failed to create VMBridge instance");
+ }
+
+ /**
+ * Return a helper object to optimize {@link Context} access.
+ * <p>
+ * The runtime will pass the resulting helper object to the subsequent
+ * calls to {@link #getContext(Object contextHelper)} and
+ * {@link #setContext(Object contextHelper, Context cx)} methods.
+ * In this way the implementation can use the helper to cache
+ * information about current thread to make {@link Context} access faster.
+ */
+ protected abstract Object getThreadContextHelper();
+
+ /**
+ * Get {@link Context} instance associated with the current thread
+ * or null if none.
+ *
+ * @param contextHelper The result of {@link #getThreadContextHelper()}
+ * called from the current thread.
+ */
+ protected abstract Context getContext(Object contextHelper);
+
+ /**
+ * Associate {@link Context} instance with the current thread or remove
+ * the current association if <tt>cx</tt> is null.
+ *
+ * @param contextHelper The result of {@link #getThreadContextHelper()}
+ * called from the current thread.
+ */
+ protected abstract void setContext(Object contextHelper, Context cx);
+
+ /**
+ * Return the ClassLoader instance associated with the current thread.
+ */
+ protected abstract ClassLoader getCurrentThreadClassLoader();
+
+ /**
+ * In many JVMSs, public methods in private
+ * classes are not accessible by default (Sun Bug #4071593).
+ * VMBridge instance should try to workaround that via, for example,
+ * calling method.setAccessible(true) when it is available.
+ * The implementation is responsible to catch all possible exceptions
+ * like SecurityException if the workaround is not available.
+ *
+ * @return true if it was possible to make method accessible
+ * or false otherwise.
+ */
+ protected abstract boolean tryToMakeAccessible(Object accessibleObject);
+
+ /**
+ * Create helper object to create later proxies implementing the specified
+ * interfaces later. Under JDK 1.3 the implementation can look like:
+ * <pre>
+ * return java.lang.reflect.Proxy.getProxyClass(..., interfaces).
+ * getConstructor(new Class[] {
+ * java.lang.reflect.InvocationHandler.class });
+ * </pre>
+ *
+ * @param interfaces Array with one or more interface class objects.
+ */
+ protected Object getInterfaceProxyHelper(ContextFactory cf,
+ Class[] interfaces)
+ {
+ throw Context.reportRuntimeError(
+ "VMBridge.getInterfaceProxyHelper is not supported");
+ }
+
+ /**
+ * Create proxy object for {@link InterfaceAdapter}. The proxy should call
+ * {@link InterfaceAdapter#invoke(ContextFactory cf,
+ * Object target,
+ * Scriptable topScope,
+ * Method method,
+ * Object[] args)}
+ * as implementation of interface methods associated with
+ * <tt>proxyHelper</tt>.
+ *
+ * @param proxyHelper The result of the previous call to
+ * {@link #getInterfaceProxyHelper(ContextFactory, Class[])}.
+ */
+ protected Object newInterfaceProxy(Object proxyHelper,
+ ContextFactory cf,
+ InterfaceAdapter adapter,
+ Object target,
+ Scriptable topScope)
+ {
+ throw Context.reportRuntimeError(
+ "VMBridge.newInterfaceProxy is not supported");
+ }
+
+ /**
+ * Returns whether or not a given member (method or constructor)
+ * has variable arguments.
+ * Variable argument methods have only been supported in Java since
+ * JDK 1.5.
+ */
+ protected abstract boolean isVarArgs(Member member);
+
+ /**
+ * 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.
+ */
+ public Iterator getJavaIterator(Context cx, Scriptable scope, Object obj) {
+ if (obj instanceof Wrapper) {
+ Object unwrapped = ((Wrapper) obj).unwrap();
+ Iterator iterator = null;
+ if (unwrapped instanceof Iterator)
+ iterator = (Iterator) unwrapped;
+ return iterator;
+ }
+ return null;
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/WrapFactory.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/WrapFactory.java
new file mode 100644
index 0000000..3edc203
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/WrapFactory.java
@@ -0,0 +1,183 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// API class
+
+package org.mozilla.javascript;
+
+/**
+ * Embeddings that wish to provide their own custom wrappings for Java
+ * objects may extend this class and call
+ * {@link Context#setWrapFactory(WrapFactory)}
+ * Once an instance of this class or an extension of this class is enabled
+ * for a given context (by calling setWrapFactory on that context), Rhino
+ * will call the methods of this class whenever it needs to wrap a value
+ * resulting from a call to a Java method or an access to a Java field.
+ *
+ * @see org.mozilla.javascript.Context#setWrapFactory(WrapFactory)
+ * @since 1.5 Release 4
+ */
+public class WrapFactory
+{
+ /**
+ * Wrap the object.
+ * <p>
+ * The value returned must be one of
+ * <UL>
+ * <LI>java.lang.Boolean</LI>
+ * <LI>java.lang.String</LI>
+ * <LI>java.lang.Number</LI>
+ * <LI>org.mozilla.javascript.Scriptable objects</LI>
+ * <LI>The value returned by Context.getUndefinedValue()</LI>
+ * <LI>null</LI>
+ * </UL>
+ * @param cx the current Context for this thread
+ * @param scope the scope of the executing script
+ * @param obj the object to be wrapped. Note it can be null.
+ * @param staticType type hint. If security restrictions prevent to wrap
+ object based on its class, staticType will be used instead.
+ * @return the wrapped value.
+ */
+ public Object wrap(Context cx, Scriptable scope,
+ Object obj, Class staticType)
+ {
+ if (obj == null || obj == Undefined.instance
+ || obj instanceof Scriptable)
+ {
+ return obj;
+ }
+ if (staticType != null && staticType.isPrimitive()) {
+ if (staticType == Void.TYPE)
+ return Undefined.instance;
+ if (staticType == Character.TYPE)
+ return new Integer(((Character) obj).charValue());
+ return obj;
+ }
+ if (!isJavaPrimitiveWrap()) {
+ if (obj instanceof String || obj instanceof Number
+ || obj instanceof Boolean)
+ {
+ return obj;
+ } else if (obj instanceof Character) {
+ return String.valueOf(((Character)obj).charValue());
+ }
+ }
+ Class cls = obj.getClass();
+ if (cls.isArray()) {
+ return NativeJavaArray.wrap(scope, obj);
+ }
+ return wrapAsJavaObject(cx, scope, obj, staticType);
+ }
+
+ /**
+ * Wrap an object newly created by a constructor call.
+ * @param cx the current Context for this thread
+ * @param scope the scope of the executing script
+ * @param obj the object to be wrapped
+ * @return the wrapped value.
+ */
+ public Scriptable wrapNewObject(Context cx, Scriptable scope, Object obj)
+ {
+ if (obj instanceof Scriptable) {
+ return (Scriptable)obj;
+ }
+ Class cls = obj.getClass();
+ if (cls.isArray()) {
+ return NativeJavaArray.wrap(scope, obj);
+ }
+ return wrapAsJavaObject(cx, scope, obj, null);
+ }
+
+ /**
+ * Wrap Java object as Scriptable instance to allow full access to its
+ * methods and fields from JavaScript.
+ * <p>
+ * {@link #wrap(Context, Scriptable, Object, Class)} and
+ * {@link #wrapNewObject(Context, Scriptable, Object)} call this method
+ * when they can not convert <tt>javaObject</tt> to JavaScript primitive
+ * value or JavaScript array.
+ * <p>
+ * Subclasses can override the method to provide custom wrappers
+ * for Java objects.
+ * @param cx the current Context for this thread
+ * @param scope the scope of the executing script
+ * @param javaObject the object to be wrapped
+ * @param staticType type hint. If security restrictions prevent to wrap
+ object based on its class, staticType will be used instead.
+ * @return the wrapped value which shall not be null
+ */
+ public Scriptable wrapAsJavaObject(Context cx, Scriptable scope,
+ Object javaObject, Class staticType)
+ {
+ Scriptable wrap;
+ wrap = new NativeJavaObject(scope, javaObject, staticType);
+ return wrap;
+ }
+
+ /**
+ * Return <code>false</code> if result of Java method, which is instance of
+ * <code>String</code>, <code>Number</code>, <code>Boolean</code> and
+ * <code>Character</code>, should be used directly as JavaScript primitive
+ * type.
+ * By default the method returns true to indicate that instances of
+ * <code>String</code>, <code>Number</code>, <code>Boolean</code> and
+ * <code>Character</code> should be wrapped as any other Java object and
+ * scripts can access any Java method available in these objects.
+ * Use {@link #setJavaPrimitiveWrap(boolean)} to change this.
+ */
+ public final boolean isJavaPrimitiveWrap()
+ {
+ return javaPrimitiveWrap;
+ }
+
+ /**
+ * @see #isJavaPrimitiveWrap()
+ */
+ public final void setJavaPrimitiveWrap(boolean value)
+ {
+ Context cx = Context.getCurrentContext();
+ if (cx != null && cx.isSealed()) {
+ Context.onSealedMutation();
+ }
+ javaPrimitiveWrap = value;
+ }
+
+ private boolean javaPrimitiveWrap = true;
+
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/WrappedException.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/WrappedException.java
new file mode 100644
index 0000000..c749f74
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/WrappedException.java
@@ -0,0 +1,93 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+/**
+ * A wrapper for runtime exceptions.
+ *
+ * Used by the JavaScript runtime to wrap and propagate exceptions that occur
+ * during runtime.
+ *
+ * @author Norris Boyd
+ */
+public class WrappedException extends EvaluatorException
+{
+ static final long serialVersionUID = -1551979216966520648L;
+
+ /**
+ * @see Context#throwAsScriptRuntimeEx(Throwable e)
+ */
+ public WrappedException(Throwable exception)
+ {
+ super("Wrapped "+exception.toString());
+ this.exception = exception;
+ Kit.initCause(this, exception);
+
+ int[] linep = { 0 };
+ String sourceName = Context.getSourcePositionFromStack(linep);
+ int lineNumber = linep[0];
+ if (sourceName != null) {
+ initSourceName(sourceName);
+ }
+ if (lineNumber != 0) {
+ initLineNumber(lineNumber);
+ }
+ }
+
+ /**
+ * Get the wrapped exception.
+ *
+ * @return the exception that was presented as a argument to the
+ * constructor when this object was created
+ */
+ public Throwable getWrappedException()
+ {
+ return exception;
+ }
+
+ /**
+ * @deprecated Use {@link #getWrappedException()} instead.
+ */
+ public Object unwrap()
+ {
+ return getWrappedException();
+ }
+
+ private Throwable exception;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Wrapper.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Wrapper.java
new file mode 100644
index 0000000..cb2d2f5
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Wrapper.java
@@ -0,0 +1,58 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// API class
+
+package org.mozilla.javascript;
+
+/**
+ * Objects that can wrap other values for reflection in the JS environment
+ * will implement Wrapper.
+ *
+ * Wrapper defines a single method that can be called to unwrap the object.
+ */
+
+public interface Wrapper {
+
+ /**
+ * Unwrap the object by returning the wrapped value.
+ *
+ * @return a wrapped value
+ */
+ public Object unwrap();
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/continuations/Continuation.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/continuations/Continuation.java
new file mode 100644
index 0000000..c6d3966
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/continuations/Continuation.java
@@ -0,0 +1,136 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript.continuations;
+
+import org.mozilla.javascript.*;
+
+public final class Continuation extends IdScriptableObject implements Function
+{
+ static final long serialVersionUID = 1794167133757605367L;
+
+ private static final Object FTAG = new Object();
+
+ private Object implementation;
+
+ public static void init(Context cx, Scriptable scope, boolean sealed)
+ {
+ Continuation obj = new Continuation();
+ obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
+ }
+
+ public Object getImplementation()
+ {
+ return implementation;
+ }
+
+ public void initImplementation(Object implementation)
+ {
+ this.implementation = implementation;
+ }
+
+ public String getClassName()
+ {
+ return "Continuation";
+ }
+
+ public Scriptable construct(Context cx, Scriptable scope, Object[] args)
+ {
+ throw Context.reportRuntimeError("Direct call is not supported");
+ }
+
+ 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;
+ }
+
+ 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);
+ }
+
+ 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));
+ }
+
+// #string_id_map#
+
+ protected int findPrototypeId(String s)
+ {
+ int id;
+// #generated# Last update: 2007-05-09 08:16:40 EDT
+ L0: { id = 0; String X = null;
+ if (s.length()==11) { X="constructor";id=Id_constructor; }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ break L0;
+ }
+// #/generated#
+ return id;
+ }
+
+ private static final int
+ Id_constructor = 1,
+ MAX_PROTOTYPE_ID = 1;
+
+// #/string_id_map#
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/debug/DebugFrame.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/debug/DebugFrame.java
new file mode 100644
index 0000000..ef15710
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/debug/DebugFrame.java
@@ -0,0 +1,91 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// API class
+
+package org.mozilla.javascript.debug;
+
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Scriptable;
+
+/**
+Interface to implement if the application is interested in receiving debug
+information during execution of a particular script or function.
+*/
+public interface DebugFrame {
+
+/**
+Called when execution is ready to start bytecode interpretation for entered a particular function or script.
+
+@param cx current Context for this thread
+@param activation the activation scope for the function or script.
+@param thisObj value of the JavaScript <code>this</code> object
+@param args the array of arguments
+*/
+ public void onEnter(Context cx, Scriptable activation,
+ Scriptable thisObj, Object[] args);
+/**
+Called when executed code reaches new line in the source.
+@param cx current Context for this thread
+@param lineNumber current line number in the script source
+*/
+ public void onLineChange(Context cx, int lineNumber);
+
+/**
+Called when thrown exception is handled by the function or script.
+@param cx current Context for this thread
+@param ex exception object
+*/
+ public void onExceptionThrown(Context cx, Throwable ex);
+
+/**
+Called when the function or script for this frame is about to return.
+@param cx current Context for this thread
+@param byThrow if true function will leave by throwing exception, otherwise it
+ will execute normal return
+@param resultOrException function result in case of normal return or
+ exception object if about to throw exception
+*/
+ public void onExit(Context cx, boolean byThrow, Object resultOrException);
+
+/**
+Called when the function or script executes a 'debugger' statement.
+@param cx current Context for this thread
+*/
+ public void onDebuggerStatement(Context cx);
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/debug/DebuggableObject.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/debug/DebuggableObject.java
new file mode 100644
index 0000000..23e7421
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/debug/DebuggableObject.java
@@ -0,0 +1,61 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// API class
+
+package org.mozilla.javascript.debug;
+
+/**
+ * This interface exposes debugging information from objects.
+ */
+public interface DebuggableObject {
+
+ /**
+ * Returns an array of ids for the properties of the object.
+ *
+ * <p>All properties, even those with attribute {DontEnum}, are listed.
+ * This allows the debugger to display all properties of the object.<p>
+ *
+ * @return an array of java.lang.Objects with an entry for every
+ * listed property. Properties accessed via an integer index will
+ * have a corresponding
+ * Integer entry in the returned array. Properties accessed by
+ * a String will have a String entry in the returned array.
+ */
+ public Object[] getAllIds();
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/debug/DebuggableScript.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/debug/DebuggableScript.java
new file mode 100644
index 0000000..705e442
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/debug/DebuggableScript.java
@@ -0,0 +1,119 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// API class
+
+package org.mozilla.javascript.debug;
+
+/**
+ * This interface exposes debugging information from executable
+ * code (either functions or top-level scripts).
+ */
+public interface DebuggableScript
+{
+ public boolean isTopLevel();
+
+ /**
+ * Returns true if this is a function, false if it is a script.
+ */
+ public boolean isFunction();
+
+ /**
+ * Get name of the function described by this script.
+ * Return null or an empty string if this script is not a function.
+ */
+ public String getFunctionName();
+
+ /**
+ * Get number of declared parameters in the function.
+ * Return 0 if this script is not a function.
+ *
+ * @see #getParamAndVarCount()
+ * @see #getParamOrVarName(int index)
+ */
+ public int getParamCount();
+
+ /**
+ * Get number of declared parameters and local variables.
+ * Return number of declared global variables if this script is not a
+ * function.
+ *
+ * @see #getParamCount()
+ * @see #getParamOrVarName(int index)
+ */
+ public int getParamAndVarCount();
+
+ /**
+ * Get name of a declared parameter or local variable.
+ * <tt>index</tt> should be less then {@link #getParamAndVarCount()}.
+ * If <tt>index&nbsp;&lt;&nbsp;{@link #getParamCount()}</tt>, return
+ * the name of the corresponding parameter, otherwise return the name
+ * of variable.
+ * If this script is not function, return the name of the declared
+ * global variable.
+ */
+ public String getParamOrVarName(int index);
+
+ /**
+ * Get the name of the source (usually filename or URL)
+ * of the script.
+ */
+ public String getSourceName();
+
+ /**
+ * Returns true if this script or function were runtime-generated
+ * from JavaScript using <tt>eval</tt> function or <tt>Function</tt>
+ * or <tt>Script</tt> constructors.
+ */
+ public boolean isGeneratedScript();
+
+ /**
+ * Get array containing the line numbers that
+ * that can be passed to <code>DebugFrame.onLineChange()<code>.
+ * Note that line order in the resulting array is arbitrary
+ */
+ public int[] getLineNumbers();
+
+ public int getFunctionCount();
+
+ public DebuggableScript getFunction(int index);
+
+ public DebuggableScript getParent();
+
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/debug/Debugger.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/debug/Debugger.java
new file mode 100644
index 0000000..bfac153
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/debug/Debugger.java
@@ -0,0 +1,69 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// API class
+
+package org.mozilla.javascript.debug;
+
+import org.mozilla.javascript.Context;
+
+/**
+Interface to implement if the application is interested in receiving debug
+information.
+*/
+public interface Debugger {
+
+/**
+Called when compilation of a particular function or script into internal
+bytecode is done.
+
+@param cx current Context for this thread
+@param fnOrScript object describing the function or script
+@param source the function or script source
+*/
+ void handleCompilationDone(Context cx, DebuggableScript fnOrScript,
+ String source);
+
+/**
+Called when execution entered a particular function or script.
+
+@return implementation of DebugFrame which receives debug information during
+ the function or script execution or null otherwise
+*/
+ DebugFrame getFrame(Context cx, DebuggableScript fnOrScript);
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/jdk11/VMBridge_jdk11.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/jdk11/VMBridge_jdk11.java
new file mode 100644
index 0000000..f5d1522
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/jdk11/VMBridge_jdk11.java
@@ -0,0 +1,84 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript.jdk11;
+
+import java.lang.reflect.Member;
+import java.util.Hashtable;
+
+import org.mozilla.javascript.*;
+
+public class VMBridge_jdk11 extends VMBridge
+{
+ private Hashtable threadsWithContext = new Hashtable();
+
+ protected Object getThreadContextHelper()
+ {
+ return Thread.currentThread();
+ }
+
+ protected Context getContext(Object contextHelper)
+ {
+ Thread t = (Thread)contextHelper;
+ return (Context)threadsWithContext.get(t);
+ }
+
+ protected void setContext(Object contextHelper, Context cx)
+ {
+ Thread t = (Thread)contextHelper;
+ if (cx == null) {
+ // Allow to garbage collect thread reference
+ threadsWithContext.remove(t);
+ } else {
+ threadsWithContext.put(t, cx);
+ }
+ }
+
+ protected ClassLoader getCurrentThreadClassLoader()
+ {
+ return null;
+ }
+
+ protected boolean tryToMakeAccessible(Object accessibleObject)
+ {
+ return false;
+ }
+
+ protected boolean isVarArgs(Member member) {
+ return false;
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/jdk13/VMBridge_jdk13.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/jdk13/VMBridge_jdk13.java
new file mode 100644
index 0000000..c33e9b4
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/jdk13/VMBridge_jdk13.java
@@ -0,0 +1,157 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript.jdk13;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Member;
+import java.lang.reflect.Proxy;
+
+import org.mozilla.javascript.*;
+
+public class VMBridge_jdk13 extends VMBridge
+{
+ private ThreadLocal contextLocal = new ThreadLocal();
+
+ protected Object getThreadContextHelper()
+ {
+ // To make subsequent batch calls to getContext/setContext faster
+ // associate permanently one element array with contextLocal
+ // so getContext/setContext would need just to read/write the first
+ // array element.
+ // Note that it is necessary to use Object[], not Context[] to allow
+ // garbage collection of Rhino classes. For details see comments
+ // by Attila Szegedi in
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=281067#c5
+
+ Object[] storage = (Object[])contextLocal.get();
+ if (storage == null) {
+ storage = new Object[1];
+ contextLocal.set(storage);
+ }
+ return storage;
+ }
+
+ protected Context getContext(Object contextHelper)
+ {
+ Object[] storage = (Object[])contextHelper;
+ return (Context)storage[0];
+ }
+
+ protected void setContext(Object contextHelper, Context cx)
+ {
+ Object[] storage = (Object[])contextHelper;
+ storage[0] = cx;
+ }
+
+ protected ClassLoader getCurrentThreadClassLoader()
+ {
+ return Thread.currentThread().getContextClassLoader();
+ }
+
+ protected boolean tryToMakeAccessible(Object accessibleObject)
+ {
+ if (!(accessibleObject instanceof AccessibleObject)) {
+ return false;
+ }
+ AccessibleObject accessible = (AccessibleObject)accessibleObject;
+ if (accessible.isAccessible()) {
+ return true;
+ }
+ try {
+ accessible.setAccessible(true);
+ } catch (Exception ex) { }
+
+ return accessible.isAccessible();
+ }
+
+ protected Object getInterfaceProxyHelper(ContextFactory cf,
+ Class[] interfaces)
+ {
+ // XXX: How to handle interfaces array withclasses from different
+ // class loaders? Using cf.getApplicationClassLoader() ?
+ ClassLoader loader = interfaces[0].getClassLoader();
+ Class cl = Proxy.getProxyClass(loader, interfaces);
+ Constructor c;
+ try {
+ c = cl.getConstructor(new Class[] { InvocationHandler.class });
+ } catch (NoSuchMethodException ex) {
+ // Should not happen
+ throw Kit.initCause(new IllegalStateException(), ex);
+ }
+ return c;
+ }
+
+ protected Object newInterfaceProxy(Object proxyHelper,
+ final ContextFactory cf,
+ final InterfaceAdapter adapter,
+ final Object target,
+ final Scriptable topScope)
+ {
+ Constructor c = (Constructor)proxyHelper;
+
+ InvocationHandler handler = new InvocationHandler() {
+ public Object invoke(Object proxy,
+ Method method,
+ Object[] args)
+ {
+ return adapter.invoke(cf, target, topScope, method, args);
+ }
+ };
+ Object proxy;
+ try {
+ proxy = c.newInstance(new Object[] { handler });
+ } catch (InvocationTargetException ex) {
+ throw Context.throwAsScriptRuntimeEx(ex);
+ } catch (IllegalAccessException ex) {
+ // Shouls not happen
+ throw Kit.initCause(new IllegalStateException(), ex);
+ } catch (InstantiationException ex) {
+ // Shouls not happen
+ throw Kit.initCause(new IllegalStateException(), ex);
+ }
+ return proxy;
+ }
+
+ protected boolean isVarArgs(Member member) {
+ return false;
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/jdk15/VMBridge_jdk15.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/jdk15/VMBridge_jdk15.java
new file mode 100644
index 0000000..0ffaf9d
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/jdk15/VMBridge_jdk15.java
@@ -0,0 +1,87 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript.jdk15;
+
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
+import java.util.Iterator;
+import org.mozilla.javascript.*;
+
+public class VMBridge_jdk15 extends org.mozilla.javascript.jdk13.VMBridge_jdk13
+{
+ public VMBridge_jdk15() throws SecurityException, InstantiationException {
+ try {
+ // Just try and see if we can access the isVarArgs method.
+ // We want to fail loading if the method does not exist
+ // so that we can load a bridge to an older JDK instead.
+ Method.class.getMethod("isVarArgs", (Class[]) null);
+ } catch (NoSuchMethodException e) {
+ // Throw a fitting exception that is handled by
+ // org.mozilla.javascript.Kit.newInstanceOrNull:
+ throw new InstantiationException(e.getMessage());
+ }
+ }
+
+ public boolean isVarArgs(Member member) {
+ if (member instanceof Method)
+ return ((Method) member).isVarArgs();
+ else if (member instanceof Constructor)
+ return ((Constructor) member).isVarArgs();
+ else
+ return false;
+ }
+
+ /**
+ * 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.
+ */
+ public Iterator getJavaIterator(Context cx, Scriptable scope, 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;
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Block.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Block.java
new file mode 100644
index 0000000..bd56714
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Block.java
@@ -0,0 +1,615 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ * Roger Lawrence
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript.optimizer;
+
+import org.mozilla.javascript.*;
+
+import java.util.Hashtable;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+class Block
+{
+
+ private static class FatBlock
+ {
+
+ private static Block[] reduceToArray(ObjToIntMap map)
+ {
+ Block[] result = null;
+ if (!map.isEmpty()) {
+ result = new Block[map.size()];
+ int i = 0;
+ ObjToIntMap.Iterator iter = map.newIterator();
+ for (iter.start(); !iter.done(); iter.next()) {
+ FatBlock fb = (FatBlock)(iter.getKey());
+ result[i++] = fb.realBlock;
+ }
+ }
+ return result;
+ }
+
+ void addSuccessor(FatBlock b) { successors.put(b, 0); }
+ void addPredecessor(FatBlock b) { predecessors.put(b, 0); }
+
+ Block[] getSuccessors() { return reduceToArray(successors); }
+ Block[] getPredecessors() { return reduceToArray(predecessors); }
+
+ // all the Blocks that come immediately after this
+ private ObjToIntMap successors = new ObjToIntMap();
+ // all the Blocks that come immediately before this
+ private ObjToIntMap predecessors = new ObjToIntMap();
+
+ Block realBlock;
+ }
+
+ Block(int startNodeIndex, int endNodeIndex)
+ {
+ itsStartNodeIndex = startNodeIndex;
+ itsEndNodeIndex = endNodeIndex;
+ }
+
+ static void runFlowAnalyzes(OptFunctionNode fn, Node[] statementNodes)
+ {
+ int paramCount = fn.fnode.getParamCount();
+ int varCount = fn.fnode.getParamAndVarCount();
+ int[] varTypes = new int[varCount];
+ // If the variable is a parameter, it could have any type.
+ for (int i = 0; i != paramCount; ++i) {
+ varTypes[i] = Optimizer.AnyType;
+ }
+ // If the variable is from a "var" statement, its typeEvent will be set
+ // when we see the setVar node.
+ for (int i = paramCount; i != varCount; ++i) {
+ varTypes[i] = Optimizer.NoType;
+ }
+
+ Block[] theBlocks = buildBlocks(statementNodes);
+
+ if (DEBUG) {
+ ++debug_blockCount;
+ System.out.println("-------------------"+fn.fnode.getFunctionName()+" "+debug_blockCount+"--------");
+ System.out.println(toString(theBlocks, statementNodes));
+ }
+
+ reachingDefDataFlow(fn, statementNodes, theBlocks, varTypes);
+ typeFlow(fn, statementNodes, theBlocks, varTypes);
+
+ if (DEBUG) {
+ for (int i = 0; i < theBlocks.length; i++) {
+ System.out.println("For block " + theBlocks[i].itsBlockID);
+ theBlocks[i].printLiveOnEntrySet(fn);
+ }
+ System.out.println("Variable Table, size = " + varCount);
+ for (int i = 0; i != varCount; i++) {
+ System.out.println("["+i+"] type: "+varTypes[i]);
+ }
+ }
+
+ for (int i = paramCount; i != varCount; i++) {
+ if (varTypes[i] == Optimizer.NumberType) {
+ fn.setIsNumberVar(i);
+ }
+ }
+
+ }
+
+ private static Block[] buildBlocks(Node[] statementNodes)
+ {
+ // a mapping from each target node to the block it begins
+ Hashtable theTargetBlocks = new Hashtable();
+ ObjArray theBlocks = new ObjArray();
+
+ // there's a block that starts at index 0
+ int beginNodeIndex = 0;
+
+ for (int i = 0; i < statementNodes.length; i++) {
+ switch (statementNodes[i].getType()) {
+ case Token.TARGET :
+ {
+ if (i != beginNodeIndex) {
+ FatBlock fb = newFatBlock(beginNodeIndex, i - 1);
+ if (statementNodes[beginNodeIndex].getType()
+ == Token.TARGET)
+ theTargetBlocks.put(statementNodes[beginNodeIndex], fb);
+ theBlocks.add(fb);
+ // start the next block at this node
+ beginNodeIndex = i;
+ }
+ }
+ break;
+ case Token.IFNE :
+ case Token.IFEQ :
+ case Token.GOTO :
+ {
+ FatBlock fb = newFatBlock(beginNodeIndex, i);
+ if (statementNodes[beginNodeIndex].getType()
+ == Token.TARGET)
+ theTargetBlocks.put(statementNodes[beginNodeIndex], fb);
+ theBlocks.add(fb);
+ // start the next block at the next node
+ beginNodeIndex = i + 1;
+ }
+ break;
+ }
+ }
+
+ if (beginNodeIndex != statementNodes.length) {
+ FatBlock fb = newFatBlock(beginNodeIndex, statementNodes.length - 1);
+ if (statementNodes[beginNodeIndex].getType() == Token.TARGET)
+ theTargetBlocks.put(statementNodes[beginNodeIndex], fb);
+ theBlocks.add(fb);
+ }
+
+ // build successor and predecessor links
+
+ for (int i = 0; i < theBlocks.size(); i++) {
+ FatBlock fb = (FatBlock)(theBlocks.get(i));
+
+ Node blockEndNode = statementNodes[fb.realBlock.itsEndNodeIndex];
+ int blockEndNodeType = blockEndNode.getType();
+
+ if ((blockEndNodeType != Token.GOTO)
+ && (i < (theBlocks.size() - 1))) {
+ FatBlock fallThruTarget = (FatBlock)(theBlocks.get(i + 1));
+ fb.addSuccessor(fallThruTarget);
+ fallThruTarget.addPredecessor(fb);
+ }
+
+
+ if ( (blockEndNodeType == Token.IFNE)
+ || (blockEndNodeType == Token.IFEQ)
+ || (blockEndNodeType == Token.GOTO) ) {
+ Node target = ((Node.Jump)blockEndNode).target;
+ FatBlock branchTargetBlock
+ = (FatBlock)(theTargetBlocks.get(target));
+ target.putProp(Node.TARGETBLOCK_PROP,
+ branchTargetBlock.realBlock);
+ fb.addSuccessor(branchTargetBlock);
+ branchTargetBlock.addPredecessor(fb);
+ }
+ }
+
+ Block[] result = new Block[theBlocks.size()];
+
+ for (int i = 0; i < theBlocks.size(); i++) {
+ FatBlock fb = (FatBlock)(theBlocks.get(i));
+ Block b = fb.realBlock;
+ b.itsSuccessors = fb.getSuccessors();
+ b.itsPredecessors = fb.getPredecessors();
+ b.itsBlockID = i;
+ result[i] = b;
+ }
+
+ return result;
+ }
+
+ private static FatBlock newFatBlock(int startNodeIndex, int endNodeIndex)
+ {
+ FatBlock fb = new FatBlock();
+ fb.realBlock = new Block(startNodeIndex, endNodeIndex);
+ return fb;
+ }
+
+ private static String toString(Block[] blockList, Node[] statementNodes)
+ {
+ if (!DEBUG) return null;
+
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+
+ pw.println(blockList.length + " Blocks");
+ for (int i = 0; i < blockList.length; i++) {
+ Block b = blockList[i];
+ pw.println("#" + b.itsBlockID);
+ pw.println("from " + b.itsStartNodeIndex
+ + " "
+ + statementNodes[b.itsStartNodeIndex].toString());
+ pw.println("thru " + b.itsEndNodeIndex
+ + " "
+ + statementNodes[b.itsEndNodeIndex].toString());
+ pw.print("Predecessors ");
+ if (b.itsPredecessors != null) {
+ for (int j = 0; j < b.itsPredecessors.length; j++)
+ pw.print(b.itsPredecessors[j].itsBlockID + " ");
+ pw.println();
+ }
+ else
+ pw.println("none");
+ pw.print("Successors ");
+ if (b.itsSuccessors != null) {
+ for (int j = 0; j < b.itsSuccessors.length; j++)
+ pw.print(b.itsSuccessors[j].itsBlockID + " ");
+ pw.println();
+ }
+ else
+ pw.println("none");
+ }
+ return sw.toString();
+ }
+
+ private static void reachingDefDataFlow(OptFunctionNode fn, Node[] statementNodes, Block theBlocks[], int[] varTypes)
+ {
+/*
+ initialize the liveOnEntry and liveOnExit sets, then discover the variables
+ that are def'd by each function, and those that are used before being def'd
+ (hence liveOnEntry)
+*/
+ for (int i = 0; i < theBlocks.length; i++) {
+ theBlocks[i].initLiveOnEntrySets(fn, statementNodes);
+ }
+/*
+ this visits every block starting at the last, re-adding the predecessors of
+ any block whose inputs change as a result of the dataflow.
+ REMIND, better would be to visit in CFG postorder
+*/
+ boolean visit[] = new boolean[theBlocks.length];
+ boolean doneOnce[] = new boolean[theBlocks.length];
+ int vIndex = theBlocks.length - 1;
+ boolean needRescan = false;
+ visit[vIndex] = true;
+ while (true) {
+ if (visit[vIndex] || !doneOnce[vIndex]) {
+ doneOnce[vIndex] = true;
+ visit[vIndex] = false;
+ if (theBlocks[vIndex].doReachedUseDataFlow()) {
+ Block pred[] = theBlocks[vIndex].itsPredecessors;
+ if (pred != null) {
+ for (int i = 0; i < pred.length; i++) {
+ int index = pred[i].itsBlockID;
+ visit[index] = true;
+ needRescan |= (index > vIndex);
+ }
+ }
+ }
+ }
+ if (vIndex == 0) {
+ if (needRescan) {
+ vIndex = theBlocks.length - 1;
+ needRescan = false;
+ }
+ else
+ break;
+ }
+ else
+ vIndex--;
+ }
+/*
+ if any variable is live on entry to block 0, we have to mark it as
+ not jRegable - since it means that someone is trying to access the
+ 'undefined'-ness of that variable.
+*/
+
+ theBlocks[0].markAnyTypeVariables(varTypes);
+ }
+
+ private static void typeFlow(OptFunctionNode fn, Node[] statementNodes, Block theBlocks[], int[] varTypes)
+ {
+ boolean visit[] = new boolean[theBlocks.length];
+ boolean doneOnce[] = new boolean[theBlocks.length];
+ int vIndex = 0;
+ boolean needRescan = false;
+ visit[vIndex] = true;
+ while (true) {
+ if (visit[vIndex] || !doneOnce[vIndex]) {
+ doneOnce[vIndex] = true;
+ visit[vIndex] = false;
+ if (theBlocks[vIndex].doTypeFlow(fn, statementNodes, varTypes))
+ {
+ Block succ[] = theBlocks[vIndex].itsSuccessors;
+ if (succ != null) {
+ for (int i = 0; i < succ.length; i++) {
+ int index = succ[i].itsBlockID;
+ visit[index] = true;
+ needRescan |= (index < vIndex);
+ }
+ }
+ }
+ }
+ if (vIndex == (theBlocks.length - 1)) {
+ if (needRescan) {
+ vIndex = 0;
+ needRescan = false;
+ }
+ else
+ break;
+ }
+ else
+ vIndex++;
+ }
+ }
+
+ private static boolean assignType(int[] varTypes, int index, int type)
+ {
+ return type != (varTypes[index] |= type);
+ }
+
+ private void markAnyTypeVariables(int[] varTypes)
+ {
+ for (int i = 0; i != varTypes.length; i++) {
+ if (itsLiveOnEntrySet.test(i)) {
+ assignType(varTypes, i, Optimizer.AnyType);
+ }
+ }
+
+ }
+
+ /*
+ We're tracking uses and defs - in order to
+ build the def set and to identify the last use
+ nodes.
+
+ The itsNotDefSet is built reversed then flipped later.
+
+ */
+ private void lookForVariableAccess(OptFunctionNode fn, Node n)
+ {
+ switch (n.getType()) {
+ case Token.DEC :
+ case Token.INC :
+ {
+ Node child = n.getFirstChild();
+ if (child.getType() == Token.GETVAR) {
+ int varIndex = fn.getVarIndex(child);
+ if (!itsNotDefSet.test(varIndex))
+ itsUseBeforeDefSet.set(varIndex);
+ itsNotDefSet.set(varIndex);
+ }
+ }
+ break;
+ case Token.SETVAR :
+ {
+ Node lhs = n.getFirstChild();
+ Node rhs = lhs.getNext();
+ lookForVariableAccess(fn, rhs);
+ itsNotDefSet.set(fn.getVarIndex(n));
+ }
+ break;
+ case Token.GETVAR :
+ {
+ int varIndex = fn.getVarIndex(n);
+ if (!itsNotDefSet.test(varIndex))
+ itsUseBeforeDefSet.set(varIndex);
+ }
+ break;
+ default :
+ Node child = n.getFirstChild();
+ while (child != null) {
+ lookForVariableAccess(fn, child);
+ child = child.getNext();
+ }
+ break;
+ }
+ }
+
+ /*
+ build the live on entry/exit sets.
+ Then walk the trees looking for defs/uses of variables
+ and build the def and useBeforeDef sets.
+ */
+ private void initLiveOnEntrySets(OptFunctionNode fn, Node[] statementNodes)
+ {
+ int listLength = fn.getVarCount();
+ itsUseBeforeDefSet = new DataFlowBitSet(listLength);
+ itsNotDefSet = new DataFlowBitSet(listLength);
+ itsLiveOnEntrySet = new DataFlowBitSet(listLength);
+ itsLiveOnExitSet = new DataFlowBitSet(listLength);
+ for (int i = itsStartNodeIndex; i <= itsEndNodeIndex; i++) {
+ Node n = statementNodes[i];
+ lookForVariableAccess(fn, n);
+ }
+ itsNotDefSet.not(); // truth in advertising
+ }
+
+ /*
+ the liveOnEntry of each successor is the liveOnExit for this block.
+ The liveOnEntry for this block is -
+ liveOnEntry = liveOnExit - defsInThisBlock + useBeforeDefsInThisBlock
+
+ */
+ private boolean doReachedUseDataFlow()
+ {
+ itsLiveOnExitSet.clear();
+ if (itsSuccessors != null)
+ for (int i = 0; i < itsSuccessors.length; i++)
+ itsLiveOnExitSet.or(itsSuccessors[i].itsLiveOnEntrySet);
+ return itsLiveOnEntrySet.df2(itsLiveOnExitSet,
+ itsUseBeforeDefSet, itsNotDefSet);
+ }
+
+ /*
+ the type of an expression is relatively unknown. Cases we can be sure
+ about are -
+ Literals,
+ Arithmetic operations - always return a Number
+ */
+ private static int findExpressionType(OptFunctionNode fn, Node n,
+ int[] varTypes)
+ {
+ switch (n.getType()) {
+ case Token.NUMBER :
+ return Optimizer.NumberType;
+
+ case Token.CALL :
+ case Token.NEW :
+ case Token.REF_CALL :
+ return Optimizer.AnyType;
+
+ case Token.GETELEM :
+ return Optimizer.AnyType;
+
+ case Token.GETVAR :
+ return varTypes[fn.getVarIndex(n)];
+
+ case Token.INC :
+ case Token.DEC :
+ case Token.DIV:
+ case Token.MOD:
+ case Token.BITOR:
+ case Token.BITXOR:
+ case Token.BITAND:
+ case Token.LSH:
+ case Token.RSH:
+ case Token.URSH:
+ case Token.SUB :
+ return Optimizer.NumberType;
+
+ case Token.ARRAYLIT:
+ case Token.OBJECTLIT:
+ return Optimizer.AnyType; // XXX: actually, we know it's not
+ // number, but no type yet for that
+
+ case Token.ADD : {
+ // if the lhs & rhs are known to be numbers, we can be sure that's
+ // the result, otherwise it could be a string.
+ Node child = n.getFirstChild();
+ int lType = findExpressionType(fn, child, varTypes);
+ int rType = findExpressionType(fn, child.getNext(), varTypes);
+ return lType | rType; // we're not distinguishing strings yet
+ }
+ }
+
+ Node child = n.getFirstChild();
+ if (child == null) {
+ return Optimizer.AnyType;
+ } else {
+ int result = Optimizer.NoType;
+ while (child != null) {
+ result |= findExpressionType(fn, child, varTypes);
+ child = child.getNext();
+ }
+ return result;
+ }
+ }
+
+ private static boolean findDefPoints(OptFunctionNode fn, Node n,
+ int[] varTypes)
+ {
+ boolean result = false;
+ Node child = n.getFirstChild();
+ switch (n.getType()) {
+ default :
+ while (child != null) {
+ result |= findDefPoints(fn, child, varTypes);
+ child = child.getNext();
+ }
+ break;
+ case Token.DEC :
+ case Token.INC :
+ if (child.getType() == Token.GETVAR) {
+ // theVar is a Number now
+ int i = fn.getVarIndex(child);
+ result |= assignType(varTypes, i, Optimizer.NumberType);
+ }
+ break;
+ case Token.SETPROP :
+ case Token.SETPROP_OP :
+ if (child.getType() == Token.GETVAR) {
+ int i = fn.getVarIndex(child);
+ assignType(varTypes, i, Optimizer.AnyType);
+ }
+ while (child != null) {
+ result |= findDefPoints(fn, child, varTypes);
+ child = child.getNext();
+ }
+ break;
+ case Token.SETVAR : {
+ Node rValue = child.getNext();
+ int theType = findExpressionType(fn, rValue, varTypes);
+ int i = fn.getVarIndex(n);
+ result |= assignType(varTypes, i, theType);
+ break;
+ }
+ }
+ return result;
+ }
+
+ private boolean doTypeFlow(OptFunctionNode fn, Node[] statementNodes,
+ int[] varTypes)
+ {
+ boolean changed = false;
+
+ for (int i = itsStartNodeIndex; i <= itsEndNodeIndex; i++) {
+ Node n = statementNodes[i];
+ if (n != null)
+ changed |= findDefPoints(fn, n, varTypes);
+ }
+
+ return changed;
+ }
+
+ private void printLiveOnEntrySet(OptFunctionNode fn)
+ {
+ if (DEBUG) {
+ for (int i = 0; i < fn.getVarCount(); i++) {
+ String name = fn.fnode.getParamOrVarName(i);
+ if (itsUseBeforeDefSet.test(i))
+ System.out.println(name + " is used before def'd");
+ if (itsNotDefSet.test(i))
+ System.out.println(name + " is not def'd");
+ if (itsLiveOnEntrySet.test(i))
+ System.out.println(name + " is live on entry");
+ if (itsLiveOnExitSet.test(i))
+ System.out.println(name + " is live on exit");
+ }
+ }
+ }
+
+ // all the Blocks that come immediately after this
+ private Block[] itsSuccessors;
+ // all the Blocks that come immediately before this
+ private Block[] itsPredecessors;
+
+ private int itsStartNodeIndex; // the Node at the start of the block
+ private int itsEndNodeIndex; // the Node at the end of the block
+
+ private int itsBlockID; // a unique index for each block
+
+// reaching def bit sets -
+ private DataFlowBitSet itsLiveOnEntrySet;
+ private DataFlowBitSet itsLiveOnExitSet;
+ private DataFlowBitSet itsUseBeforeDefSet;
+ private DataFlowBitSet itsNotDefSet;
+
+ static final boolean DEBUG = false;
+ private static int debug_blockCount;
+
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/ClassCompiler.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/ClassCompiler.java
new file mode 100644
index 0000000..4a69c6a
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/ClassCompiler.java
@@ -0,0 +1,214 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript.optimizer;
+
+import org.mozilla.javascript.*;
+
+/**
+ * Generates class files from script sources.
+ *
+ * since 1.5 Release 5
+ * @author Igor Bukanov
+ */
+
+public class ClassCompiler
+{
+ /**
+ * Construct ClassCompiler that uses the specified compiler environment
+ * when generating classes.
+ */
+ public ClassCompiler(CompilerEnvirons compilerEnv)
+ {
+ if (compilerEnv == null) throw new IllegalArgumentException();
+ this.compilerEnv = compilerEnv;
+ this.mainMethodClassName = Codegen.DEFAULT_MAIN_METHOD_CLASS;
+ }
+
+ /**
+ * Set the class name to use for main method implementation.
+ * The class must have a method matching
+ * <tt>public static void main(Script sc, String[] args)</tt>, it will be
+ * called when <tt>main(String[] args)</tt> is called in the generated
+ * class. The class name should be fully qulified name and include the
+ * package name like in <tt>org.foo.Bar<tt>.
+ */
+ public void setMainMethodClass(String className)
+ {
+ // XXX Should this check for a valid class name?
+ mainMethodClassName = className;
+ }
+
+ /**
+ * Get the name of the class for main method implementation.
+ * @see #setMainMethodClass(String)
+ */
+ public String getMainMethodClass()
+ {
+ return mainMethodClassName;
+ }
+
+ /**
+ * Get the compiler environment the compiler uses.
+ */
+ public CompilerEnvirons getCompilerEnv()
+ {
+ return compilerEnv;
+ }
+
+ /**
+ * Get the class that the generated target will extend.
+ */
+ public Class getTargetExtends()
+ {
+ return targetExtends;
+ }
+
+ /**
+ * Set the class that the generated target will extend.
+ *
+ * @param extendsClass the class it extends
+ */
+ public void setTargetExtends(Class extendsClass)
+ {
+ targetExtends = extendsClass;
+ }
+
+ /**
+ * Get the interfaces that the generated target will implement.
+ */
+ public Class[] getTargetImplements()
+ {
+ return targetImplements == null ? null : (Class[])targetImplements.clone();
+ }
+
+ /**
+ * Set the interfaces that the generated target will implement.
+ *
+ * @param implementsClasses an array of Class objects, one for each
+ * interface the target will extend
+ */
+ public void setTargetImplements(Class[] implementsClasses)
+ {
+ targetImplements = implementsClasses == null ? null : (Class[])implementsClasses.clone();
+ }
+
+ /**
+ * Build class name for a auxiliary class generated by compiler.
+ * If the compiler needs to generate extra classes beyond the main class,
+ * it will call this function to build the auxiliary class name.
+ * The default implementation simply appends auxMarker to mainClassName
+ * but this can be overridden.
+ */
+ protected String makeAuxiliaryClassName(String mainClassName,
+ String auxMarker)
+ {
+ return mainClassName+auxMarker;
+ }
+
+ /**
+ * Compile JavaScript source into one or more Java class files.
+ * The first compiled class will have name mainClassName.
+ * If the results of {@link #getTargetExtends()} or
+ * {@link #getTargetImplements()} are not null, then the first compiled
+ * class will extend the specified super class and implement
+ * specified interfaces.
+ *
+ * @return array where elements with even indexes specifies class name
+ * and the following odd index gives class file body as byte[]
+ * array. The initial element of the array always holds
+ * mainClassName and array[1] holds its byte code.
+ */
+ public Object[] compileToClassFiles(String source,
+ String sourceLocation,
+ int lineno,
+ String mainClassName)
+ {
+ /*APPJET*/
+ Parser p =
+ InformativeParser.makeParser(compilerEnv,
+ compilerEnv.getErrorReporter());
+ ScriptOrFnNode tree = p.parse(source, sourceLocation, lineno);
+ String encodedSource = p.getEncodedSource();
+
+ Class superClass = getTargetExtends();
+ Class[] interfaces = getTargetImplements();
+ String scriptClassName;
+ boolean isPrimary = (interfaces == null && superClass == null);
+ if (isPrimary) {
+ scriptClassName = mainClassName;
+ } else {
+ scriptClassName = makeAuxiliaryClassName(mainClassName, "1");
+ }
+
+ Codegen codegen = new Codegen();
+ codegen.setMainMethodClass(mainMethodClassName);
+ byte[] scriptClassBytes
+ = codegen.compileToClassFile(compilerEnv, scriptClassName,
+ tree, encodedSource,
+ false);
+
+ if (isPrimary) {
+ return new Object[] { scriptClassName, scriptClassBytes };
+ }
+ int functionCount = tree.getFunctionCount();
+ ObjToIntMap functionNames = new ObjToIntMap(functionCount);
+ for (int i = 0; i != functionCount; ++i) {
+ FunctionNode ofn = tree.getFunctionNode(i);
+ String name = ofn.getFunctionName();
+ if (name != null && name.length() != 0) {
+ functionNames.put(name, ofn.getParamCount());
+ }
+ }
+ if (superClass == null) {
+ superClass = ScriptRuntime.ObjectClass;
+ }
+ byte[] mainClassBytes
+ = JavaAdapter.createAdapterCode(
+ functionNames, mainClassName,
+ superClass, interfaces, scriptClassName);
+
+ return new Object[] { mainClassName, mainClassBytes,
+ scriptClassName, scriptClassBytes };
+ }
+
+ private String mainMethodClassName;
+ private CompilerEnvirons compilerEnv;
+ private Class targetExtends;
+ private Class[] targetImplements;
+
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Codegen.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Codegen.java
new file mode 100644
index 0000000..64952bf
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Codegen.java
@@ -0,0 +1,5031 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Kemal Bayram
+ * Igor Bukanov
+ * Bob Jervis
+ * Roger Lawrence
+ * Andi Vajda
+ * Hannes Wallnoefer
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+package org.mozilla.javascript.optimizer;
+
+import org.mozilla.javascript.*;
+import org.mozilla.classfile.*;
+import java.util.*;
+import java.lang.reflect.Constructor;
+import java.util.Hashtable;
+
+/**
+ * This class generates code for a given IR tree.
+ *
+ * @author Norris Boyd
+ * @author Roger Lawrence
+ */
+
+public class Codegen implements Evaluator
+{
+ public void captureStackInfo(RhinoException ex) {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getSourcePositionFromStack(Context cx, int[] linep) {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getPatchedStack(RhinoException ex, String nativeStackTrace) {
+ throw new UnsupportedOperationException();
+ }
+
+ public List getScriptStack(RhinoException ex) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setEvalScriptFlag(Script script) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object compile(CompilerEnvirons compilerEnv,
+ ScriptOrFnNode tree,
+ String encodedSource,
+ boolean returnFunction)
+ {
+ int serial;
+ synchronized (globalLock) {
+ serial = ++globalSerialClassCounter;
+ }
+ String mainClassName = "org.mozilla.javascript.gen.c"+serial;
+
+ byte[] mainClassBytes = compileToClassFile(compilerEnv, mainClassName,
+ tree, encodedSource,
+ returnFunction);
+
+ return new Object[] { mainClassName, mainClassBytes };
+ }
+
+ public Script createScriptObject(Object bytecode,
+ Object staticSecurityDomain)
+ {
+ Class cl = defineClass(bytecode, staticSecurityDomain);
+
+ Script script;
+ try {
+ script = (Script)cl.newInstance();
+ } catch (Exception ex) {
+ throw new RuntimeException
+ ("Unable to instantiate compiled class:"+ex.toString());
+ }
+ return script;
+ }
+
+ public Function createFunctionObject(Context cx, Scriptable scope,
+ Object bytecode,
+ Object staticSecurityDomain)
+ {
+ Class cl = defineClass(bytecode, staticSecurityDomain);
+
+ NativeFunction f;
+ try {
+ Constructor ctor = cl.getConstructors()[0];
+ Object[] initArgs = { scope, cx, new Integer(0) };
+ f = (NativeFunction)ctor.newInstance(initArgs);
+ } catch (Exception ex) {
+ throw new RuntimeException
+ ("Unable to instantiate compiled class:"+ex.toString());
+ }
+ return f;
+ }
+
+ private Class defineClass(Object bytecode,
+ Object staticSecurityDomain)
+ {
+ Object[] nameBytesPair = (Object[])bytecode;
+ String className = (String)nameBytesPair[0];
+ byte[] classBytes = (byte[])nameBytesPair[1];
+
+ // The generated classes in this case refer only to Rhino classes
+ // which must be accessible through this class loader
+ ClassLoader rhinoLoader = getClass().getClassLoader();
+ GeneratedClassLoader loader;
+ loader = SecurityController.createLoader(rhinoLoader,
+ staticSecurityDomain);
+ Exception e;
+ try {
+ Class cl = loader.defineClass(className, classBytes);
+ loader.linkClass(cl);
+ return cl;
+ } catch (SecurityException x) {
+ e = x;
+ } catch (IllegalArgumentException x) {
+ e = x;
+ }
+ throw new RuntimeException("Malformed optimizer package " + e);
+ }
+
+ byte[] compileToClassFile(CompilerEnvirons compilerEnv,
+ String mainClassName,
+ ScriptOrFnNode scriptOrFn,
+ String encodedSource,
+ boolean returnFunction)
+ {
+ this.compilerEnv = compilerEnv;
+
+ transform(scriptOrFn);
+
+ if (Token.printTrees) {
+ /*APPJET*///System.out.println(scriptOrFn.toStringTree(scriptOrFn));
+ }
+
+ if (returnFunction) {
+ scriptOrFn = scriptOrFn.getFunctionNode(0);
+ }
+
+ initScriptOrFnNodesData(scriptOrFn);
+
+ this.mainClassName = mainClassName;
+ this.mainClassSignature
+ = ClassFileWriter.classNameToSignature(mainClassName);
+
+ try {
+ return generateCode(encodedSource);
+ } catch (ClassFileWriter.ClassFileFormatException e) {
+ throw reportClassFileFormatException(scriptOrFn, e.getMessage());
+ }
+ }
+
+ private RuntimeException reportClassFileFormatException(
+ ScriptOrFnNode scriptOrFn,
+ String message)
+ {
+ String msg = scriptOrFn instanceof FunctionNode
+ ? ScriptRuntime.getMessage2("msg.while.compiling.fn",
+ ((FunctionNode)scriptOrFn).getFunctionName(), message)
+ : ScriptRuntime.getMessage1("msg.while.compiling.script", message);
+ return Context.reportRuntimeError(msg, scriptOrFn.getSourceName(),
+ scriptOrFn.getLineno(), null, 0);
+ }
+
+ private void transform(ScriptOrFnNode tree)
+ {
+ initOptFunctions_r(tree);
+
+ int optLevel = compilerEnv.getOptimizationLevel();
+
+ Hashtable possibleDirectCalls = null;
+ if (optLevel > 0) {
+ /*
+ * Collect all of the contained functions into a hashtable
+ * so that the call optimizer can access the class name & parameter
+ * count for any call it encounters
+ */
+ if (tree.getType() == Token.SCRIPT) {
+ int functionCount = tree.getFunctionCount();
+ for (int i = 0; i != functionCount; ++i) {
+ OptFunctionNode ofn = OptFunctionNode.get(tree, i);
+ if (ofn.fnode.getFunctionType()
+ == FunctionNode.FUNCTION_STATEMENT)
+ {
+ String name = ofn.fnode.getFunctionName();
+ if (name.length() != 0) {
+ if (possibleDirectCalls == null) {
+ possibleDirectCalls = new Hashtable();
+ }
+ possibleDirectCalls.put(name, ofn);
+ }
+ }
+ }
+ }
+ }
+
+ if (possibleDirectCalls != null) {
+ directCallTargets = new ObjArray();
+ }
+
+ OptTransformer ot = new OptTransformer(possibleDirectCalls,
+ directCallTargets);
+ ot.transform(tree);
+
+ if (optLevel > 0) {
+ (new Optimizer()).optimize(tree);
+ }
+ }
+
+ private static void initOptFunctions_r(ScriptOrFnNode scriptOrFn)
+ {
+ for (int i = 0, N = scriptOrFn.getFunctionCount(); i != N; ++i) {
+ FunctionNode fn = scriptOrFn.getFunctionNode(i);
+ new OptFunctionNode(fn);
+ initOptFunctions_r(fn);
+ }
+ }
+
+ private void initScriptOrFnNodesData(ScriptOrFnNode scriptOrFn)
+ {
+ ObjArray x = new ObjArray();
+ collectScriptOrFnNodes_r(scriptOrFn, x);
+
+ int count = x.size();
+ scriptOrFnNodes = new ScriptOrFnNode[count];
+ x.toArray(scriptOrFnNodes);
+
+ scriptOrFnIndexes = new ObjToIntMap(count);
+ for (int i = 0; i != count; ++i) {
+ scriptOrFnIndexes.put(scriptOrFnNodes[i], i);
+ }
+ }
+
+ private static void collectScriptOrFnNodes_r(ScriptOrFnNode n,
+ ObjArray x)
+ {
+ x.add(n);
+ int nestedCount = n.getFunctionCount();
+ for (int i = 0; i != nestedCount; ++i) {
+ collectScriptOrFnNodes_r(n.getFunctionNode(i), x);
+ }
+ }
+
+ private byte[] generateCode(String encodedSource)
+ {
+ boolean hasScript = (scriptOrFnNodes[0].getType() == Token.SCRIPT);
+ boolean hasFunctions = (scriptOrFnNodes.length > 1 || !hasScript);
+
+ String sourceFile = null;
+ if (compilerEnv.isGenerateDebugInfo()) {
+ sourceFile = scriptOrFnNodes[0].getSourceName();
+ }
+
+ ClassFileWriter cfw = new ClassFileWriter(mainClassName,
+ SUPER_CLASS_NAME,
+ sourceFile);
+ cfw.addField(ID_FIELD_NAME, "I",
+ ClassFileWriter.ACC_PRIVATE);
+ cfw.addField(DIRECT_CALL_PARENT_FIELD, mainClassSignature,
+ ClassFileWriter.ACC_PRIVATE);
+ cfw.addField(REGEXP_ARRAY_FIELD_NAME, REGEXP_ARRAY_FIELD_TYPE,
+ ClassFileWriter.ACC_PRIVATE);
+
+ if (hasFunctions) {
+ generateFunctionConstructor(cfw);
+ }
+
+ if (hasScript) {
+ cfw.addInterface("org/mozilla/javascript/Script");
+ generateScriptCtor(cfw);
+ generateMain(cfw);
+ generateExecute(cfw);
+ }
+
+ generateCallMethod(cfw);
+ generateResumeGenerator(cfw);
+
+ generateNativeFunctionOverrides(cfw, encodedSource);
+
+ int count = scriptOrFnNodes.length;
+ for (int i = 0; i != count; ++i) {
+ ScriptOrFnNode n = scriptOrFnNodes[i];
+
+ BodyCodegen bodygen = new BodyCodegen();
+ bodygen.cfw = cfw;
+ bodygen.codegen = this;
+ bodygen.compilerEnv = compilerEnv;
+ bodygen.scriptOrFn = n;
+ bodygen.scriptOrFnIndex = i;
+
+ try {
+ bodygen.generateBodyCode();
+ } catch (ClassFileWriter.ClassFileFormatException e) {
+ throw reportClassFileFormatException(n, e.getMessage());
+ }
+
+ if (n.getType() == Token.FUNCTION) {
+ OptFunctionNode ofn = OptFunctionNode.get(n);
+ generateFunctionInit(cfw, ofn);
+ if (ofn.isTargetOfDirectCall()) {
+ emitDirectConstructor(cfw, ofn);
+ }
+ }
+ }
+
+ if (directCallTargets != null) {
+ int N = directCallTargets.size();
+ for (int j = 0; j != N; ++j) {
+ cfw.addField(getDirectTargetFieldName(j),
+ mainClassSignature,
+ ClassFileWriter.ACC_PRIVATE);
+ }
+ }
+
+ emitRegExpInit(cfw);
+ emitConstantDudeInitializers(cfw);
+
+ return cfw.toByteArray();
+ }
+
+ private void emitDirectConstructor(ClassFileWriter cfw,
+ OptFunctionNode ofn)
+ {
+/*
+ we generate ..
+ Scriptable directConstruct(<directCallArgs>) {
+ Scriptable newInstance = createObject(cx, scope);
+ Object val = <body-name>(cx, scope, newInstance, <directCallArgs>);
+ if (val instanceof Scriptable) {
+ return (Scriptable) val;
+ }
+ return newInstance;
+ }
+*/
+ cfw.startMethod(getDirectCtorName(ofn.fnode),
+ getBodyMethodSignature(ofn.fnode),
+ (short)(ClassFileWriter.ACC_STATIC
+ | ClassFileWriter.ACC_PRIVATE));
+
+ int argCount = ofn.fnode.getParamCount();
+ int firstLocal = (4 + argCount * 3) + 1;
+
+ cfw.addALoad(0); // this
+ cfw.addALoad(1); // cx
+ cfw.addALoad(2); // scope
+ cfw.addInvoke(ByteCode.INVOKEVIRTUAL,
+ "org/mozilla/javascript/BaseFunction",
+ "createObject",
+ "(Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +")Lorg/mozilla/javascript/Scriptable;");
+ cfw.addAStore(firstLocal);
+
+ cfw.addALoad(0);
+ cfw.addALoad(1);
+ cfw.addALoad(2);
+ cfw.addALoad(firstLocal);
+ for (int i = 0; i < argCount; i++) {
+ cfw.addALoad(4 + (i * 3));
+ cfw.addDLoad(5 + (i * 3));
+ }
+ cfw.addALoad(4 + argCount * 3);
+ cfw.addInvoke(ByteCode.INVOKESTATIC,
+ mainClassName,
+ getBodyMethodName(ofn.fnode),
+ getBodyMethodSignature(ofn.fnode));
+ int exitLabel = cfw.acquireLabel();
+ cfw.add(ByteCode.DUP); // make a copy of direct call result
+ cfw.add(ByteCode.INSTANCEOF, "org/mozilla/javascript/Scriptable");
+ cfw.add(ByteCode.IFEQ, exitLabel);
+ // cast direct call result
+ cfw.add(ByteCode.CHECKCAST, "org/mozilla/javascript/Scriptable");
+ cfw.add(ByteCode.ARETURN);
+ cfw.markLabel(exitLabel);
+
+ cfw.addALoad(firstLocal);
+ cfw.add(ByteCode.ARETURN);
+
+ cfw.stopMethod((short)(firstLocal + 1));
+ }
+
+ static boolean isGenerator(ScriptOrFnNode node)
+ {
+ return (node.getType() == Token.FUNCTION ) &&
+ ((FunctionNode)node).isGenerator();
+ }
+
+ // How dispatch to generators works:
+ // Two methods are generated corresponding to a user-written generator.
+ // One of these creates a generator object (NativeGenerator), which is
+ // returned to the user. The other method contains all of the body code
+ // of the generator.
+ // When a user calls a generator, the call() method dispatches control to
+ // to the method that creates the NativeGenerator object. Subsequently when
+ // the user invokes .next(), .send() or any such method on the generator
+ // object, the resumeGenerator() below dispatches the call to the
+ // method corresponding to the generator body. As a matter of convention
+ // the generator body is given the name of the generator activation function
+ // appended by "_gen".
+ private void generateResumeGenerator(ClassFileWriter cfw)
+ {
+ boolean hasGenerators = false;
+ for (int i=0; i < scriptOrFnNodes.length; i++) {
+ if (isGenerator(scriptOrFnNodes[i]))
+ hasGenerators = true;
+ }
+
+ // if there are no generators defined, we don't implement a
+ // resumeGenerator(). The base class provides a default implementation.
+ if (!hasGenerators)
+ return;
+
+ cfw.startMethod("resumeGenerator",
+ "(Lorg/mozilla/javascript/Context;" +
+ "Lorg/mozilla/javascript/Scriptable;" +
+ "ILjava/lang/Object;" +
+ "Ljava/lang/Object;)Ljava/lang/Object;",
+ (short)(ClassFileWriter.ACC_PUBLIC
+ | ClassFileWriter.ACC_FINAL));
+
+ // load arguments for dispatch to the corresponding *_gen method
+ cfw.addALoad(0);
+ cfw.addALoad(1);
+ cfw.addALoad(2);
+ cfw.addALoad(4);
+ cfw.addALoad(5);
+ cfw.addILoad(3);
+
+ cfw.addLoadThis();
+ cfw.add(ByteCode.GETFIELD, cfw.getClassName(), ID_FIELD_NAME, "I");
+
+ int startSwitch = cfw.addTableSwitch(0, scriptOrFnNodes.length - 1);
+ cfw.markTableSwitchDefault(startSwitch);
+ int endlabel = cfw.acquireLabel();
+
+ for (int i = 0; i < scriptOrFnNodes.length; i++) {
+ ScriptOrFnNode n = scriptOrFnNodes[i];
+ cfw.markTableSwitchCase(startSwitch, i, (short)6);
+ if (isGenerator(n)) {
+ String type = "(" +
+ mainClassSignature +
+ "Lorg/mozilla/javascript/Context;" +
+ "Lorg/mozilla/javascript/Scriptable;" +
+ "Ljava/lang/Object;" +
+ "Ljava/lang/Object;I)Ljava/lang/Object;";
+ cfw.addInvoke(ByteCode.INVOKESTATIC,
+ mainClassName,
+ getBodyMethodName(n) + "_gen",
+ type);
+ cfw.add(ByteCode.ARETURN);
+ } else {
+ cfw.add(ByteCode.GOTO, endlabel);
+ }
+ }
+
+ cfw.markLabel(endlabel);
+ pushUndefined(cfw);
+ cfw.add(ByteCode.ARETURN);
+
+
+ // this method uses as many locals as there are arguments (hence 6)
+ cfw.stopMethod((short)6);
+ }
+
+ private void generateCallMethod(ClassFileWriter cfw)
+ {
+ cfw.startMethod("call",
+ "(Lorg/mozilla/javascript/Context;" +
+ "Lorg/mozilla/javascript/Scriptable;" +
+ "Lorg/mozilla/javascript/Scriptable;" +
+ "[Ljava/lang/Object;)Ljava/lang/Object;",
+ (short)(ClassFileWriter.ACC_PUBLIC
+ | ClassFileWriter.ACC_FINAL));
+
+ // Generate code for:
+ // if (!ScriptRuntime.hasTopCall(cx)) {
+ // return ScriptRuntime.doTopCall(this, cx, scope, thisObj, args);
+ // }
+
+ int nonTopCallLabel = cfw.acquireLabel();
+ cfw.addALoad(1); //cx
+ cfw.addInvoke(ByteCode.INVOKESTATIC,
+ "org/mozilla/javascript/ScriptRuntime",
+ "hasTopCall",
+ "(Lorg/mozilla/javascript/Context;"
+ +")Z");
+ cfw.add(ByteCode.IFNE, nonTopCallLabel);
+ cfw.addALoad(0);
+ cfw.addALoad(1);
+ cfw.addALoad(2);
+ cfw.addALoad(3);
+ cfw.addALoad(4);
+ cfw.addInvoke(ByteCode.INVOKESTATIC,
+ "org/mozilla/javascript/ScriptRuntime",
+ "doTopCall",
+ "(Lorg/mozilla/javascript/Callable;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"[Ljava/lang/Object;"
+ +")Ljava/lang/Object;");
+ cfw.add(ByteCode.ARETURN);
+ cfw.markLabel(nonTopCallLabel);
+
+ // Now generate switch to call the real methods
+ cfw.addALoad(0);
+ cfw.addALoad(1);
+ cfw.addALoad(2);
+ cfw.addALoad(3);
+ cfw.addALoad(4);
+
+ int end = scriptOrFnNodes.length;
+ boolean generateSwitch = (2 <= end);
+
+ int switchStart = 0;
+ int switchStackTop = 0;
+ if (generateSwitch) {
+ cfw.addLoadThis();
+ cfw.add(ByteCode.GETFIELD, cfw.getClassName(), ID_FIELD_NAME, "I");
+ // do switch from (1, end - 1) mapping 0 to
+ // the default case
+ switchStart = cfw.addTableSwitch(1, end - 1);
+ }
+
+ for (int i = 0; i != end; ++i) {
+ ScriptOrFnNode n = scriptOrFnNodes[i];
+ if (generateSwitch) {
+ if (i == 0) {
+ cfw.markTableSwitchDefault(switchStart);
+ switchStackTop = cfw.getStackTop();
+ } else {
+ cfw.markTableSwitchCase(switchStart, i - 1,
+ switchStackTop);
+ }
+ }
+ if (n.getType() == Token.FUNCTION) {
+ OptFunctionNode ofn = OptFunctionNode.get(n);
+ if (ofn.isTargetOfDirectCall()) {
+ int pcount = ofn.fnode.getParamCount();
+ if (pcount != 0) {
+ // loop invariant:
+ // stack top == arguments array from addALoad4()
+ for (int p = 0; p != pcount; ++p) {
+ cfw.add(ByteCode.ARRAYLENGTH);
+ cfw.addPush(p);
+ int undefArg = cfw.acquireLabel();
+ int beyond = cfw.acquireLabel();
+ cfw.add(ByteCode.IF_ICMPLE, undefArg);
+ // get array[p]
+ cfw.addALoad(4);
+ cfw.addPush(p);
+ cfw.add(ByteCode.AALOAD);
+ cfw.add(ByteCode.GOTO, beyond);
+ cfw.markLabel(undefArg);
+ pushUndefined(cfw);
+ cfw.markLabel(beyond);
+ // Only one push
+ cfw.adjustStackTop(-1);
+ cfw.addPush(0.0);
+ // restore invariant
+ cfw.addALoad(4);
+ }
+ }
+ }
+ }
+ cfw.addInvoke(ByteCode.INVOKESTATIC,
+ mainClassName,
+ getBodyMethodName(n),
+ getBodyMethodSignature(n));
+ cfw.add(ByteCode.ARETURN);
+ }
+ cfw.stopMethod((short)5);
+ // 5: this, cx, scope, js this, args[]
+ }
+
+ private void generateMain(ClassFileWriter cfw)
+ {
+ cfw.startMethod("main", "([Ljava/lang/String;)V",
+ (short)(ClassFileWriter.ACC_PUBLIC
+ | ClassFileWriter.ACC_STATIC));
+
+ // load new ScriptImpl()
+ cfw.add(ByteCode.NEW, cfw.getClassName());
+ cfw.add(ByteCode.DUP);
+ cfw.addInvoke(ByteCode.INVOKESPECIAL, cfw.getClassName(),
+ "<init>", "()V");
+ // load 'args'
+ cfw.add(ByteCode.ALOAD_0);
+ // Call mainMethodClass.main(Script script, String[] args)
+ cfw.addInvoke(ByteCode.INVOKESTATIC,
+ mainMethodClass,
+ "main",
+ "(Lorg/mozilla/javascript/Script;[Ljava/lang/String;)V");
+ cfw.add(ByteCode.RETURN);
+ // 1 = String[] args
+ cfw.stopMethod((short)1);
+ }
+
+ private void generateExecute(ClassFileWriter cfw)
+ {
+ cfw.startMethod("exec",
+ "(Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +")Ljava/lang/Object;",
+ (short)(ClassFileWriter.ACC_PUBLIC
+ | ClassFileWriter.ACC_FINAL));
+
+ final int CONTEXT_ARG = 1;
+ final int SCOPE_ARG = 2;
+
+ cfw.addLoadThis();
+ cfw.addALoad(CONTEXT_ARG);
+ cfw.addALoad(SCOPE_ARG);
+ cfw.add(ByteCode.DUP);
+ cfw.add(ByteCode.ACONST_NULL);
+ cfw.addInvoke(ByteCode.INVOKEVIRTUAL,
+ cfw.getClassName(),
+ "call",
+ "(Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"[Ljava/lang/Object;"
+ +")Ljava/lang/Object;");
+
+ cfw.add(ByteCode.ARETURN);
+ // 3 = this + context + scope
+ cfw.stopMethod((short)3);
+ }
+
+ private void generateScriptCtor(ClassFileWriter cfw)
+ {
+ cfw.startMethod("<init>", "()V", ClassFileWriter.ACC_PUBLIC);
+
+ cfw.addLoadThis();
+ cfw.addInvoke(ByteCode.INVOKESPECIAL, SUPER_CLASS_NAME,
+ "<init>", "()V");
+ // set id to 0
+ cfw.addLoadThis();
+ cfw.addPush(0);
+ cfw.add(ByteCode.PUTFIELD, cfw.getClassName(), ID_FIELD_NAME, "I");
+
+ cfw.add(ByteCode.RETURN);
+ // 1 parameter = this
+ cfw.stopMethod((short)1);
+ }
+
+ private void generateFunctionConstructor(ClassFileWriter cfw)
+ {
+ final int SCOPE_ARG = 1;
+ final int CONTEXT_ARG = 2;
+ final int ID_ARG = 3;
+
+ cfw.startMethod("<init>", FUNCTION_CONSTRUCTOR_SIGNATURE,
+ ClassFileWriter.ACC_PUBLIC);
+ cfw.addALoad(0);
+ cfw.addInvoke(ByteCode.INVOKESPECIAL, SUPER_CLASS_NAME,
+ "<init>", "()V");
+
+ cfw.addLoadThis();
+ cfw.addILoad(ID_ARG);
+ cfw.add(ByteCode.PUTFIELD, cfw.getClassName(), ID_FIELD_NAME, "I");
+
+ cfw.addLoadThis();
+ cfw.addALoad(CONTEXT_ARG);
+ cfw.addALoad(SCOPE_ARG);
+
+ int start = (scriptOrFnNodes[0].getType() == Token.SCRIPT) ? 1 : 0;
+ int end = scriptOrFnNodes.length;
+ if (start == end) throw badTree();
+ boolean generateSwitch = (2 <= end - start);
+
+ int switchStart = 0;
+ int switchStackTop = 0;
+ if (generateSwitch) {
+ cfw.addILoad(ID_ARG);
+ // do switch from (start + 1, end - 1) mapping start to
+ // the default case
+ switchStart = cfw.addTableSwitch(start + 1, end - 1);
+ }
+
+ for (int i = start; i != end; ++i) {
+ if (generateSwitch) {
+ if (i == start) {
+ cfw.markTableSwitchDefault(switchStart);
+ switchStackTop = cfw.getStackTop();
+ } else {
+ cfw.markTableSwitchCase(switchStart, i - 1 - start,
+ switchStackTop);
+ }
+ }
+ OptFunctionNode ofn = OptFunctionNode.get(scriptOrFnNodes[i]);
+ cfw.addInvoke(ByteCode.INVOKEVIRTUAL,
+ mainClassName,
+ getFunctionInitMethodName(ofn),
+ FUNCTION_INIT_SIGNATURE);
+ cfw.add(ByteCode.RETURN);
+ }
+
+ // 4 = this + scope + context + id
+ cfw.stopMethod((short)4);
+ }
+
+ private void generateFunctionInit(ClassFileWriter cfw,
+ OptFunctionNode ofn)
+ {
+ final int CONTEXT_ARG = 1;
+ final int SCOPE_ARG = 2;
+ cfw.startMethod(getFunctionInitMethodName(ofn),
+ FUNCTION_INIT_SIGNATURE,
+ (short)(ClassFileWriter.ACC_PRIVATE
+ | ClassFileWriter.ACC_FINAL));
+
+ // Call NativeFunction.initScriptFunction
+ cfw.addLoadThis();
+ cfw.addALoad(CONTEXT_ARG);
+ cfw.addALoad(SCOPE_ARG);
+ cfw.addInvoke(ByteCode.INVOKEVIRTUAL,
+ "org/mozilla/javascript/NativeFunction",
+ "initScriptFunction",
+ "(Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +")V");
+
+ // precompile all regexp literals
+ int regexpCount = ofn.fnode.getRegexpCount();
+ if (regexpCount != 0) {
+ cfw.addLoadThis();
+ pushRegExpArray(cfw, ofn.fnode, CONTEXT_ARG, SCOPE_ARG);
+ cfw.add(ByteCode.PUTFIELD, mainClassName,
+ REGEXP_ARRAY_FIELD_NAME, REGEXP_ARRAY_FIELD_TYPE);
+ }
+
+ cfw.add(ByteCode.RETURN);
+ // 3 = (scriptThis/functionRef) + scope + context
+ cfw.stopMethod((short)3);
+ }
+
+ private void generateNativeFunctionOverrides(ClassFileWriter cfw,
+ String encodedSource)
+ {
+ // Override NativeFunction.getLanguageVersion() with
+ // public int getLanguageVersion() { return <version-constant>; }
+
+ cfw.startMethod("getLanguageVersion", "()I",
+ ClassFileWriter.ACC_PUBLIC);
+
+ cfw.addPush(compilerEnv.getLanguageVersion());
+ cfw.add(ByteCode.IRETURN);
+
+ // 1: this and no argument or locals
+ cfw.stopMethod((short)1);
+
+ // The rest of NativeFunction overrides require specific code for each
+ // script/function id
+
+ final int Do_getFunctionName = 0;
+ final int Do_getParamCount = 1;
+ final int Do_getParamAndVarCount = 2;
+ final int Do_getParamOrVarName = 3;
+ final int Do_getEncodedSource = 4;
+ final int Do_getParamOrVarConst = 5;
+ final int SWITCH_COUNT = 6;
+
+ for (int methodIndex = 0; methodIndex != SWITCH_COUNT; ++methodIndex) {
+ if (methodIndex == Do_getEncodedSource && encodedSource == null) {
+ continue;
+ }
+
+ // Generate:
+ // prologue;
+ // switch over function id to implement function-specific action
+ // epilogue
+
+ short methodLocals;
+ switch (methodIndex) {
+ case Do_getFunctionName:
+ methodLocals = 1; // Only this
+ cfw.startMethod("getFunctionName", "()Ljava/lang/String;",
+ ClassFileWriter.ACC_PUBLIC);
+ break;
+ case Do_getParamCount:
+ methodLocals = 1; // Only this
+ cfw.startMethod("getParamCount", "()I",
+ ClassFileWriter.ACC_PUBLIC);
+ break;
+ case Do_getParamAndVarCount:
+ methodLocals = 1; // Only this
+ cfw.startMethod("getParamAndVarCount", "()I",
+ ClassFileWriter.ACC_PUBLIC);
+ break;
+ case Do_getParamOrVarName:
+ methodLocals = 1 + 1; // this + paramOrVarIndex
+ cfw.startMethod("getParamOrVarName", "(I)Ljava/lang/String;",
+ ClassFileWriter.ACC_PUBLIC);
+ break;
+ case Do_getParamOrVarConst:
+ methodLocals = 1 + 1 + 1; // this + paramOrVarName
+ cfw.startMethod("getParamOrVarConst", "(I)Z",
+ ClassFileWriter.ACC_PUBLIC);
+ break;
+ case Do_getEncodedSource:
+ methodLocals = 1; // Only this
+ cfw.startMethod("getEncodedSource", "()Ljava/lang/String;",
+ ClassFileWriter.ACC_PUBLIC);
+ cfw.addPush(encodedSource);
+ break;
+ default:
+ throw Kit.codeBug();
+ }
+
+ int count = scriptOrFnNodes.length;
+
+ int switchStart = 0;
+ int switchStackTop = 0;
+ if (count > 1) {
+ // Generate switch but only if there is more then one
+ // script/function
+ cfw.addLoadThis();
+ cfw.add(ByteCode.GETFIELD, cfw.getClassName(),
+ ID_FIELD_NAME, "I");
+
+ // do switch from 1 .. count - 1 mapping 0 to the default case
+ switchStart = cfw.addTableSwitch(1, count - 1);
+ }
+
+ for (int i = 0; i != count; ++i) {
+ ScriptOrFnNode n = scriptOrFnNodes[i];
+ if (i == 0) {
+ if (count > 1) {
+ cfw.markTableSwitchDefault(switchStart);
+ switchStackTop = cfw.getStackTop();
+ }
+ } else {
+ cfw.markTableSwitchCase(switchStart, i - 1,
+ switchStackTop);
+ }
+
+ // Impelemnet method-specific switch code
+ switch (methodIndex) {
+ case Do_getFunctionName:
+ // Push function name
+ if (n.getType() == Token.SCRIPT) {
+ cfw.addPush("");
+ } else {
+ String name = ((FunctionNode)n).getFunctionName();
+ cfw.addPush(name);
+ }
+ cfw.add(ByteCode.ARETURN);
+ break;
+
+ case Do_getParamCount:
+ // Push number of defined parameters
+ cfw.addPush(n.getParamCount());
+ cfw.add(ByteCode.IRETURN);
+ break;
+
+ case Do_getParamAndVarCount:
+ // Push number of defined parameters and declared variables
+ cfw.addPush(n.getParamAndVarCount());
+ cfw.add(ByteCode.IRETURN);
+ break;
+
+ case Do_getParamOrVarName:
+ // Push name of parameter using another switch
+ // over paramAndVarCount
+ int paramAndVarCount = n.getParamAndVarCount();
+ if (paramAndVarCount == 0) {
+ // The runtime should never call the method in this
+ // case but to make bytecode verifier happy return null
+ // as throwing execption takes more code
+ cfw.add(ByteCode.ACONST_NULL);
+ cfw.add(ByteCode.ARETURN);
+ } else if (paramAndVarCount == 1) {
+ // As above do not check for valid index but always
+ // return the name of the first param
+ cfw.addPush(n.getParamOrVarName(0));
+ cfw.add(ByteCode.ARETURN);
+ } else {
+ // Do switch over getParamOrVarName
+ cfw.addILoad(1); // param or var index
+ // do switch from 1 .. paramAndVarCount - 1 mapping 0
+ // to the default case
+ int paramSwitchStart = cfw.addTableSwitch(
+ 1, paramAndVarCount - 1);
+ for (int j = 0; j != paramAndVarCount; ++j) {
+ if (cfw.getStackTop() != 0) Kit.codeBug();
+ String s = n.getParamOrVarName(j);
+ if (j == 0) {
+ cfw.markTableSwitchDefault(paramSwitchStart);
+ } else {
+ cfw.markTableSwitchCase(paramSwitchStart, j - 1,
+ 0);
+ }
+ cfw.addPush(s);
+ cfw.add(ByteCode.ARETURN);
+ }
+ }
+ break;
+
+ case Do_getParamOrVarConst:
+ // Push name of parameter using another switch
+ // over paramAndVarCount
+ paramAndVarCount = n.getParamAndVarCount();
+ boolean [] constness = n.getParamAndVarConst();
+ if (paramAndVarCount == 0) {
+ // The runtime should never call the method in this
+ // case but to make bytecode verifier happy return null
+ // as throwing execption takes more code
+ cfw.add(ByteCode.ICONST_0);
+ cfw.add(ByteCode.IRETURN);
+ } else if (paramAndVarCount == 1) {
+ // As above do not check for valid index but always
+ // return the name of the first param
+ cfw.addPush(constness[0]);
+ cfw.add(ByteCode.IRETURN);
+ } else {
+ // Do switch over getParamOrVarName
+ cfw.addILoad(1); // param or var index
+ // do switch from 1 .. paramAndVarCount - 1 mapping 0
+ // to the default case
+ int paramSwitchStart = cfw.addTableSwitch(
+ 1, paramAndVarCount - 1);
+ for (int j = 0; j != paramAndVarCount; ++j) {
+ if (cfw.getStackTop() != 0) Kit.codeBug();
+ if (j == 0) {
+ cfw.markTableSwitchDefault(paramSwitchStart);
+ } else {
+ cfw.markTableSwitchCase(paramSwitchStart, j - 1,
+ 0);
+ }
+ cfw.addPush(constness[j]);
+ cfw.add(ByteCode.IRETURN);
+ }
+ }
+ break;
+
+ case Do_getEncodedSource:
+ // Push number encoded source start and end
+ // to prepare for encodedSource.substring(start, end)
+ cfw.addPush(n.getEncodedSourceStart());
+ cfw.addPush(n.getEncodedSourceEnd());
+ cfw.addInvoke(ByteCode.INVOKEVIRTUAL,
+ "java/lang/String",
+ "substring",
+ "(II)Ljava/lang/String;");
+ cfw.add(ByteCode.ARETURN);
+ break;
+
+ default:
+ throw Kit.codeBug();
+ }
+ }
+
+ cfw.stopMethod(methodLocals);
+ }
+ }
+
+ private void emitRegExpInit(ClassFileWriter cfw)
+ {
+ // precompile all regexp literals
+
+ int totalRegCount = 0;
+ for (int i = 0; i != scriptOrFnNodes.length; ++i) {
+ totalRegCount += scriptOrFnNodes[i].getRegexpCount();
+ }
+ if (totalRegCount == 0) {
+ return;
+ }
+
+ cfw.startMethod(REGEXP_INIT_METHOD_NAME, REGEXP_INIT_METHOD_SIGNATURE,
+ (short)(ClassFileWriter.ACC_STATIC | ClassFileWriter.ACC_PRIVATE
+ | ClassFileWriter.ACC_SYNCHRONIZED));
+ cfw.addField("_reInitDone", "Z",
+ (short)(ClassFileWriter.ACC_STATIC
+ | ClassFileWriter.ACC_PRIVATE));
+ cfw.add(ByteCode.GETSTATIC, mainClassName, "_reInitDone", "Z");
+ int doInit = cfw.acquireLabel();
+ cfw.add(ByteCode.IFEQ, doInit);
+ cfw.add(ByteCode.RETURN);
+ cfw.markLabel(doInit);
+
+ for (int i = 0; i != scriptOrFnNodes.length; ++i) {
+ ScriptOrFnNode n = scriptOrFnNodes[i];
+ int regCount = n.getRegexpCount();
+ for (int j = 0; j != regCount; ++j) {
+ String reFieldName = getCompiledRegexpName(n, j);
+ String reFieldType = "Ljava/lang/Object;";
+ String reString = n.getRegexpString(j);
+ String reFlags = n.getRegexpFlags(j);
+ cfw.addField(reFieldName, reFieldType,
+ (short)(ClassFileWriter.ACC_STATIC
+ | ClassFileWriter.ACC_PRIVATE));
+ cfw.addALoad(0); // proxy
+ cfw.addALoad(1); // context
+ cfw.addPush(reString);
+ if (reFlags == null) {
+ cfw.add(ByteCode.ACONST_NULL);
+ } else {
+ cfw.addPush(reFlags);
+ }
+ /*APPJET*/cfw.addLineNumberEntry((short)n.getRegexpLineno(j));
+ cfw.addInvoke(ByteCode.INVOKEINTERFACE,
+ "org/mozilla/javascript/RegExpProxy",
+ "compileRegExp",
+ "(Lorg/mozilla/javascript/Context;"
+ +"Ljava/lang/String;Ljava/lang/String;"
+ +")Ljava/lang/Object;");
+ cfw.add(ByteCode.PUTSTATIC, mainClassName,
+ reFieldName, reFieldType);
+ }
+ }
+
+ cfw.addPush(1);
+ cfw.add(ByteCode.PUTSTATIC, mainClassName, "_reInitDone", "Z");
+ cfw.add(ByteCode.RETURN);
+ cfw.stopMethod((short)2);
+ }
+
+ private void emitConstantDudeInitializers(ClassFileWriter cfw)
+ {
+ int N = itsConstantListSize;
+ if (N == 0)
+ return;
+
+ cfw.startMethod("<clinit>", "()V",
+ (short)(ClassFileWriter.ACC_STATIC | ClassFileWriter.ACC_FINAL));
+
+ double[] array = itsConstantList;
+ for (int i = 0; i != N; ++i) {
+ double num = array[i];
+ String constantName = "_k" + i;
+ String constantType = getStaticConstantWrapperType(num);
+ cfw.addField(constantName, constantType,
+ (short)(ClassFileWriter.ACC_STATIC
+ | ClassFileWriter.ACC_PRIVATE));
+ int inum = (int)num;
+ if (inum == num) {
+ cfw.add(ByteCode.NEW, "java/lang/Integer");
+ cfw.add(ByteCode.DUP);
+ cfw.addPush(inum);
+ cfw.addInvoke(ByteCode.INVOKESPECIAL, "java/lang/Integer",
+ "<init>", "(I)V");
+ } else {
+ cfw.addPush(num);
+ addDoubleWrap(cfw);
+ }
+ cfw.add(ByteCode.PUTSTATIC, mainClassName,
+ constantName, constantType);
+ }
+
+ cfw.add(ByteCode.RETURN);
+ cfw.stopMethod((short)0);
+ }
+
+ void pushRegExpArray(ClassFileWriter cfw, ScriptOrFnNode n,
+ int contextArg, int scopeArg)
+ {
+ int regexpCount = n.getRegexpCount();
+ if (regexpCount == 0) throw badTree();
+
+ cfw.addPush(regexpCount);
+ cfw.add(ByteCode.ANEWARRAY, "java/lang/Object");
+
+ cfw.addALoad(contextArg);
+ cfw.addInvoke(ByteCode.INVOKESTATIC,
+ "org/mozilla/javascript/ScriptRuntime",
+ "checkRegExpProxy",
+ "(Lorg/mozilla/javascript/Context;"
+ +")Lorg/mozilla/javascript/RegExpProxy;");
+ // Stack: proxy, array
+ cfw.add(ByteCode.DUP);
+ cfw.addALoad(contextArg);
+ cfw.addInvoke(ByteCode.INVOKESTATIC, mainClassName,
+ REGEXP_INIT_METHOD_NAME, REGEXP_INIT_METHOD_SIGNATURE);
+ for (int i = 0; i != regexpCount; ++i) {
+ // Stack: proxy, array
+ cfw.add(ByteCode.DUP2);
+ cfw.addALoad(contextArg);
+ cfw.addALoad(scopeArg);
+ cfw.add(ByteCode.GETSTATIC, mainClassName,
+ getCompiledRegexpName(n, i), "Ljava/lang/Object;");
+ // Stack: compiledRegExp, scope, cx, proxy, array, proxy, array
+ cfw.addInvoke(ByteCode.INVOKEINTERFACE,
+ "org/mozilla/javascript/RegExpProxy",
+ "wrapRegExp",
+ "(Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"Ljava/lang/Object;"
+ +")Lorg/mozilla/javascript/Scriptable;");
+ // Stack: wrappedRegExp, array, proxy, array
+ cfw.addPush(i);
+ cfw.add(ByteCode.SWAP);
+ cfw.add(ByteCode.AASTORE);
+ // Stack: proxy, array
+ }
+ // remove proxy
+ cfw.add(ByteCode.POP);
+ }
+
+ void pushNumberAsObject(ClassFileWriter cfw, double num)
+ {
+ if (num == 0.0) {
+ if (1 / num > 0) {
+ // +0.0
+ cfw.add(ByteCode.GETSTATIC,
+ "org/mozilla/javascript/optimizer/OptRuntime",
+ "zeroObj", "Ljava/lang/Double;");
+ } else {
+ cfw.addPush(num);
+ addDoubleWrap(cfw);
+ }
+
+ } else if (num == 1.0) {
+ cfw.add(ByteCode.GETSTATIC,
+ "org/mozilla/javascript/optimizer/OptRuntime",
+ "oneObj", "Ljava/lang/Double;");
+ return;
+
+ } else if (num == -1.0) {
+ cfw.add(ByteCode.GETSTATIC,
+ "org/mozilla/javascript/optimizer/OptRuntime",
+ "minusOneObj", "Ljava/lang/Double;");
+
+ } else if (num != num) {
+ cfw.add(ByteCode.GETSTATIC,
+ "org/mozilla/javascript/ScriptRuntime",
+ "NaNobj", "Ljava/lang/Double;");
+
+ } else if (itsConstantListSize >= 2000) {
+ // There appears to be a limit in the JVM on either the number
+ // of static fields in a class or the size of the class
+ // initializer. Either way, we can't have any more than 2000
+ // statically init'd constants.
+ cfw.addPush(num);
+ addDoubleWrap(cfw);
+
+ } else {
+ int N = itsConstantListSize;
+ int index = 0;
+ if (N == 0) {
+ itsConstantList = new double[64];
+ } else {
+ double[] array = itsConstantList;
+ while (index != N && array[index] != num) {
+ ++index;
+ }
+ if (N == array.length) {
+ array = new double[N * 2];
+ System.arraycopy(itsConstantList, 0, array, 0, N);
+ itsConstantList = array;
+ }
+ }
+ if (index == N) {
+ itsConstantList[N] = num;
+ itsConstantListSize = N + 1;
+ }
+ String constantName = "_k" + index;
+ String constantType = getStaticConstantWrapperType(num);
+ cfw.add(ByteCode.GETSTATIC, mainClassName,
+ constantName, constantType);
+ }
+ }
+
+ private static void addDoubleWrap(ClassFileWriter cfw)
+ {
+ cfw.addInvoke(ByteCode.INVOKESTATIC,
+ "org/mozilla/javascript/optimizer/OptRuntime",
+ "wrapDouble", "(D)Ljava/lang/Double;");
+ }
+
+ private static String getStaticConstantWrapperType(double num)
+ {
+ int inum = (int)num;
+ if (inum == num) {
+ return "Ljava/lang/Integer;";
+ } else {
+ return "Ljava/lang/Double;";
+ }
+ }
+ static void pushUndefined(ClassFileWriter cfw)
+ {
+ cfw.add(ByteCode.GETSTATIC, "org/mozilla/javascript/Undefined",
+ "instance", "Ljava/lang/Object;");
+ }
+
+ int getIndex(ScriptOrFnNode n)
+ {
+ return scriptOrFnIndexes.getExisting(n);
+ }
+
+ static String getDirectTargetFieldName(int i)
+ {
+ return "_dt" + i;
+ }
+
+ String getDirectCtorName(ScriptOrFnNode n)
+ {
+ return "_n"+getIndex(n);
+ }
+
+ String getBodyMethodName(ScriptOrFnNode n)
+ {
+ return "_c"+getIndex(n);
+ }
+
+ String getBodyMethodSignature(ScriptOrFnNode n)
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append('(');
+ sb.append(mainClassSignature);
+ sb.append("Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"Lorg/mozilla/javascript/Scriptable;");
+ if (n.getType() == Token.FUNCTION) {
+ OptFunctionNode ofn = OptFunctionNode.get(n);
+ if (ofn.isTargetOfDirectCall()) {
+ int pCount = ofn.fnode.getParamCount();
+ for (int i = 0; i != pCount; i++) {
+ sb.append("Ljava/lang/Object;D");
+ }
+ }
+ }
+ sb.append("[Ljava/lang/Object;)Ljava/lang/Object;");
+ return sb.toString();
+ }
+
+ String getFunctionInitMethodName(OptFunctionNode ofn)
+ {
+ return "_i"+getIndex(ofn.fnode);
+ }
+
+ String getCompiledRegexpName(ScriptOrFnNode n, int regexpIndex)
+ {
+ return "_re"+getIndex(n)+"_"+regexpIndex;
+ }
+
+ static RuntimeException badTree()
+ {
+ throw new RuntimeException("Bad tree in codegen");
+ }
+
+ void setMainMethodClass(String className)
+ {
+ mainMethodClass = className;
+ }
+
+ static final String DEFAULT_MAIN_METHOD_CLASS
+ = "org.mozilla.javascript.optimizer.OptRuntime";
+
+ private static final String SUPER_CLASS_NAME
+ = "org.mozilla.javascript.NativeFunction";
+
+ static final String DIRECT_CALL_PARENT_FIELD = "_dcp";
+ private static final String ID_FIELD_NAME = "_id";
+
+ private static final String REGEXP_INIT_METHOD_NAME = "_reInit";
+ private static final String REGEXP_INIT_METHOD_SIGNATURE
+ = "(Lorg/mozilla/javascript/RegExpProxy;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")V";
+ static final String REGEXP_ARRAY_FIELD_NAME = "_re";
+ static final String REGEXP_ARRAY_FIELD_TYPE = "[Ljava/lang/Object;";
+
+ static final String FUNCTION_INIT_SIGNATURE
+ = "(Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +")V";
+
+ static final String FUNCTION_CONSTRUCTOR_SIGNATURE
+ = "(Lorg/mozilla/javascript/Scriptable;"
+ +"Lorg/mozilla/javascript/Context;I)V";
+
+ private static final Object globalLock = new Object();
+ private static int globalSerialClassCounter;
+
+ private CompilerEnvirons compilerEnv;
+
+ private ObjArray directCallTargets;
+ ScriptOrFnNode[] scriptOrFnNodes;
+ private ObjToIntMap scriptOrFnIndexes;
+
+ private String mainMethodClass = DEFAULT_MAIN_METHOD_CLASS;
+
+ String mainClassName;
+ String mainClassSignature;
+
+ private double[] itsConstantList;
+ private int itsConstantListSize;
+}
+
+
+class BodyCodegen
+{
+ void generateBodyCode()
+ {
+ isGenerator = Codegen.isGenerator(scriptOrFn);
+
+ // generate the body of the current function or script object
+ initBodyGeneration();
+
+ if (isGenerator) {
+
+ // All functions in the generated bytecode have a unique name. Every
+ // generator has a unique prefix followed by _gen
+ String type = "(" +
+ codegen.mainClassSignature +
+ "Lorg/mozilla/javascript/Context;" +
+ "Lorg/mozilla/javascript/Scriptable;" +
+ "Ljava/lang/Object;" +
+ "Ljava/lang/Object;I)Ljava/lang/Object;";
+ cfw.startMethod(codegen.getBodyMethodName(scriptOrFn) + "_gen",
+ type,
+ (short)(ClassFileWriter.ACC_STATIC
+ | ClassFileWriter.ACC_PRIVATE));
+ } else {
+ cfw.startMethod(codegen.getBodyMethodName(scriptOrFn),
+ codegen.getBodyMethodSignature(scriptOrFn),
+ (short)(ClassFileWriter.ACC_STATIC
+ | ClassFileWriter.ACC_PRIVATE));
+ }
+
+ generatePrologue();
+ Node treeTop;
+ if (fnCurrent != null) {
+ treeTop = scriptOrFn.getLastChild();
+ } else {
+ treeTop = scriptOrFn;
+ }
+ generateStatement(treeTop);
+ generateEpilogue();
+
+ cfw.stopMethod((short)(localsMax + 1));
+
+ if (isGenerator) {
+ // generate the user visible method which when invoked will
+ // return a generator object
+ generateGenerator();
+ }
+ }
+
+ // This creates a the user-facing function that returns a NativeGenerator
+ // object.
+ private void generateGenerator()
+ {
+ cfw.startMethod(codegen.getBodyMethodName(scriptOrFn),
+ codegen.getBodyMethodSignature(scriptOrFn),
+ (short)(ClassFileWriter.ACC_STATIC
+ | ClassFileWriter.ACC_PRIVATE));
+
+ initBodyGeneration();
+ argsLocal = firstFreeLocal++;
+ localsMax = firstFreeLocal;
+
+ // get top level scope
+ if (fnCurrent != null && !inDirectCallFunction
+ && (!compilerEnv.isUseDynamicScope()
+ || fnCurrent.fnode.getIgnoreDynamicScope()))
+ {
+ // Unless we're either in a direct call or using dynamic scope,
+ // use the enclosing scope of the function as our variable object.
+ cfw.addALoad(funObjLocal);
+ cfw.addInvoke(ByteCode.INVOKEINTERFACE,
+ "org/mozilla/javascript/Scriptable",
+ "getParentScope",
+ "()Lorg/mozilla/javascript/Scriptable;");
+ cfw.addAStore(variableObjectLocal);
+ }
+
+ // generators are forced to have an activation record
+ cfw.addALoad(funObjLocal);
+ cfw.addALoad(variableObjectLocal);
+ cfw.addALoad(argsLocal);
+ addScriptRuntimeInvoke("createFunctionActivation",
+ "(Lorg/mozilla/javascript/NativeFunction;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"[Ljava/lang/Object;"
+ +")Lorg/mozilla/javascript/Scriptable;");
+ cfw.addAStore(variableObjectLocal);
+
+ // create a function object
+ cfw.add(ByteCode.NEW, codegen.mainClassName);
+ // Call function constructor
+ cfw.add(ByteCode.DUP);
+ cfw.addALoad(variableObjectLocal);
+ cfw.addALoad(contextLocal); // load 'cx'
+ cfw.addPush(scriptOrFnIndex);
+ cfw.addInvoke(ByteCode.INVOKESPECIAL, codegen.mainClassName,
+ "<init>", Codegen.FUNCTION_CONSTRUCTOR_SIGNATURE);
+
+ // Init mainScript field
+ cfw.add(ByteCode.DUP);
+ if (isTopLevel) Kit.codeBug(); // Only functions can be generators
+ cfw.add(ByteCode.ALOAD_0);
+ cfw.add(ByteCode.GETFIELD,
+ codegen.mainClassName,
+ Codegen.DIRECT_CALL_PARENT_FIELD,
+ codegen.mainClassSignature);
+ cfw.add(ByteCode.PUTFIELD,
+ codegen.mainClassName,
+ Codegen.DIRECT_CALL_PARENT_FIELD,
+ codegen.mainClassSignature);
+
+ generateNestedFunctionInits();
+
+ // create the NativeGenerator object that we return
+ cfw.addALoad(variableObjectLocal);
+ cfw.addALoad(thisObjLocal);
+ cfw.addLoadConstant(maxLocals);
+ cfw.addLoadConstant(maxStack);
+ addOptRuntimeInvoke("createNativeGenerator",
+ "(Lorg/mozilla/javascript/NativeFunction;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"Lorg/mozilla/javascript/Scriptable;II"
+ +")Lorg/mozilla/javascript/Scriptable;");
+
+ cfw.add(ByteCode.ARETURN);
+ cfw.stopMethod((short)(localsMax + 1));
+ }
+
+ private void generateNestedFunctionInits()
+ {
+ int functionCount = scriptOrFn.getFunctionCount();
+ for (int i = 0; i != functionCount; i++) {
+ OptFunctionNode ofn = OptFunctionNode.get(scriptOrFn, i);
+ if (ofn.fnode.getFunctionType()
+ == FunctionNode.FUNCTION_STATEMENT)
+ {
+ visitFunction(ofn, FunctionNode.FUNCTION_STATEMENT);
+ }
+ }
+ }
+
+ private void initBodyGeneration()
+ {
+ isTopLevel = (scriptOrFn == codegen.scriptOrFnNodes[0]);
+
+ varRegisters = null;
+ if (scriptOrFn.getType() == Token.FUNCTION) {
+ fnCurrent = OptFunctionNode.get(scriptOrFn);
+ hasVarsInRegs = !fnCurrent.fnode.requiresActivation();
+ if (hasVarsInRegs) {
+ int n = fnCurrent.fnode.getParamAndVarCount();
+ if (n != 0) {
+ varRegisters = new short[n];
+ }
+ }
+ inDirectCallFunction = fnCurrent.isTargetOfDirectCall();
+ if (inDirectCallFunction && !hasVarsInRegs) Codegen.badTree();
+ } else {
+ fnCurrent = null;
+ hasVarsInRegs = false;
+ inDirectCallFunction = false;
+ }
+
+ locals = new int[MAX_LOCALS];
+
+ funObjLocal = 0;
+ contextLocal = 1;
+ variableObjectLocal = 2;
+ thisObjLocal = 3;
+ localsMax = (short) 4; // number of parms + "this"
+ firstFreeLocal = 4;
+
+ popvLocal = -1;
+ argsLocal = -1;
+ itsZeroArgArray = -1;
+ itsOneArgArray = -1;
+ scriptRegexpLocal = -1;
+ epilogueLabel = -1;
+ enterAreaStartLabel = -1;
+ generatorStateLocal = -1;
+ }
+
+ /**
+ * Generate the prologue for a function or script.
+ */
+ private void generatePrologue()
+ {
+ if (inDirectCallFunction) {
+ int directParameterCount = scriptOrFn.getParamCount();
+ // 0 is reserved for function Object 'this'
+ // 1 is reserved for context
+ // 2 is reserved for parentScope
+ // 3 is reserved for script 'this'
+ if (firstFreeLocal != 4) Kit.codeBug();
+ for (int i = 0; i != directParameterCount; ++i) {
+ varRegisters[i] = firstFreeLocal;
+ // 3 is 1 for Object parm and 2 for double parm
+ firstFreeLocal += 3;
+ }
+ if (!fnCurrent.getParameterNumberContext()) {
+ // make sure that all parameters are objects
+ itsForcedObjectParameters = true;
+ for (int i = 0; i != directParameterCount; ++i) {
+ short reg = varRegisters[i];
+ cfw.addALoad(reg);
+ cfw.add(ByteCode.GETSTATIC,
+ "java/lang/Void",
+ "TYPE",
+ "Ljava/lang/Class;");
+ int isObjectLabel = cfw.acquireLabel();
+ cfw.add(ByteCode.IF_ACMPNE, isObjectLabel);
+ cfw.addDLoad(reg + 1);
+ addDoubleWrap();
+ cfw.addAStore(reg);
+ cfw.markLabel(isObjectLabel);
+ }
+ }
+ }
+
+ if (fnCurrent != null && !inDirectCallFunction
+ && (!compilerEnv.isUseDynamicScope()
+ || fnCurrent.fnode.getIgnoreDynamicScope()))
+ {
+ // Unless we're either in a direct call or using dynamic scope,
+ // use the enclosing scope of the function as our variable object.
+ cfw.addALoad(funObjLocal);
+ cfw.addInvoke(ByteCode.INVOKEINTERFACE,
+ "org/mozilla/javascript/Scriptable",
+ "getParentScope",
+ "()Lorg/mozilla/javascript/Scriptable;");
+ cfw.addAStore(variableObjectLocal);
+ }
+
+ // reserve 'args[]'
+ argsLocal = firstFreeLocal++;
+ localsMax = firstFreeLocal;
+
+ // Generate Generator specific prelude
+ if (isGenerator) {
+
+ // reserve 'args[]'
+ operationLocal = firstFreeLocal++;
+ localsMax = firstFreeLocal;
+
+ // Local 3 is a reference to a GeneratorState object. The rest
+ // of codegen expects local 3 to be a reference to the thisObj.
+ // So move the value in local 3 to generatorStateLocal, and load
+ // the saved thisObj from the GeneratorState object.
+ cfw.addALoad(thisObjLocal);
+ generatorStateLocal = firstFreeLocal++;
+ localsMax = firstFreeLocal;
+ cfw.add(ByteCode.CHECKCAST, OptRuntime.GeneratorState.CLASS_NAME);
+ cfw.add(ByteCode.DUP);
+ cfw.addAStore(generatorStateLocal);
+ cfw.add(ByteCode.GETFIELD,
+ OptRuntime.GeneratorState.CLASS_NAME,
+ OptRuntime.GeneratorState.thisObj_NAME,
+ OptRuntime.GeneratorState.thisObj_TYPE);
+ cfw.addAStore(thisObjLocal);
+
+ if (epilogueLabel == -1) {
+ epilogueLabel = cfw.acquireLabel();
+ }
+
+ ArrayList targets = ((FunctionNode)scriptOrFn).getResumptionPoints();
+ if (targets != null) {
+ // get resumption point
+ generateGetGeneratorResumptionPoint();
+
+ // generate dispatch table
+ generatorSwitch = cfw.addTableSwitch(0,
+ targets.size() + GENERATOR_START);
+ generateCheckForThrowOrClose(-1, false, GENERATOR_START);
+ }
+ }
+
+ if (fnCurrent == null) {
+ // See comments in case Token.REGEXP
+ if (scriptOrFn.getRegexpCount() != 0) {
+ scriptRegexpLocal = getNewWordLocal();
+ codegen.pushRegExpArray(cfw, scriptOrFn, contextLocal,
+ variableObjectLocal);
+ cfw.addAStore(scriptRegexpLocal);
+ }
+ }
+
+ if (compilerEnv.isGenerateObserverCount())
+ saveCurrentCodeOffset();
+
+ if (hasVarsInRegs) {
+ // No need to create activation. Pad arguments if need be.
+ int parmCount = scriptOrFn.getParamCount();
+ if (parmCount > 0 && !inDirectCallFunction) {
+ // Set up args array
+ // check length of arguments, pad if need be
+ cfw.addALoad(argsLocal);
+ cfw.add(ByteCode.ARRAYLENGTH);
+ cfw.addPush(parmCount);
+ int label = cfw.acquireLabel();
+ cfw.add(ByteCode.IF_ICMPGE, label);
+ cfw.addALoad(argsLocal);
+ cfw.addPush(parmCount);
+ addScriptRuntimeInvoke("padArguments",
+ "([Ljava/lang/Object;I"
+ +")[Ljava/lang/Object;");
+ cfw.addAStore(argsLocal);
+ cfw.markLabel(label);
+ }
+
+ int paramCount = fnCurrent.fnode.getParamCount();
+ int varCount = fnCurrent.fnode.getParamAndVarCount();
+ boolean [] constDeclarations = fnCurrent.fnode.getParamAndVarConst();
+
+ // REMIND - only need to initialize the vars that don't get a value
+ // before the next call and are used in the function
+ short firstUndefVar = -1;
+ for (int i = 0; i != varCount; ++i) {
+ short reg = -1;
+ if (i < paramCount) {
+ if (!inDirectCallFunction) {
+ reg = getNewWordLocal();
+ cfw.addALoad(argsLocal);
+ cfw.addPush(i);
+ cfw.add(ByteCode.AALOAD);
+ cfw.addAStore(reg);
+ }
+ } else if (fnCurrent.isNumberVar(i)) {
+ reg = getNewWordPairLocal(constDeclarations[i]);
+ cfw.addPush(0.0);
+ cfw.addDStore(reg);
+ } else {
+ reg = getNewWordLocal(constDeclarations[i]);
+ if (firstUndefVar == -1) {
+ Codegen.pushUndefined(cfw);
+ firstUndefVar = reg;
+ } else {
+ cfw.addALoad(firstUndefVar);
+ }
+ cfw.addAStore(reg);
+ }
+ if (reg >= 0) {
+ if (constDeclarations[i]) {
+ cfw.addPush(0);
+ cfw.addIStore(reg + (fnCurrent.isNumberVar(i) ? 2 : 1));
+ }
+ varRegisters[i] = reg;
+ }
+
+ // Add debug table entry if we're generating debug info
+ if (compilerEnv.isGenerateDebugInfo()) {
+ String name = fnCurrent.fnode.getParamOrVarName(i);
+ String type = fnCurrent.isNumberVar(i)
+ ? "D" : "Ljava/lang/Object;";
+ int startPC = cfw.getCurrentCodeOffset();
+ if (reg < 0) {
+ reg = varRegisters[i];
+ }
+ cfw.addVariableDescriptor(name, type, startPC, reg);
+ }
+ }
+
+ // Skip creating activation object.
+ return;
+ }
+
+ // skip creating activation object for the body of a generator. The
+ // activation record required by a generator has already been created
+ // in generateGenerator().
+ if (isGenerator)
+ return;
+
+
+ String debugVariableName;
+ if (fnCurrent != null) {
+ debugVariableName = "activation";
+ cfw.addALoad(funObjLocal);
+ cfw.addALoad(variableObjectLocal);
+ cfw.addALoad(argsLocal);
+ addScriptRuntimeInvoke("createFunctionActivation",
+ "(Lorg/mozilla/javascript/NativeFunction;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"[Ljava/lang/Object;"
+ +")Lorg/mozilla/javascript/Scriptable;");
+ cfw.addAStore(variableObjectLocal);
+ cfw.addALoad(contextLocal);
+ cfw.addALoad(variableObjectLocal);
+ addScriptRuntimeInvoke("enterActivationFunction",
+ "(Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +")V");
+ } else {
+ debugVariableName = "global";
+ cfw.addALoad(funObjLocal);
+ cfw.addALoad(thisObjLocal);
+ cfw.addALoad(contextLocal);
+ cfw.addALoad(variableObjectLocal);
+ cfw.addPush(0); // false to indicate it is not eval script
+ addScriptRuntimeInvoke("initScript",
+ "(Lorg/mozilla/javascript/NativeFunction;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"Z"
+ +")V");
+ }
+
+ enterAreaStartLabel = cfw.acquireLabel();
+ epilogueLabel = cfw.acquireLabel();
+ cfw.markLabel(enterAreaStartLabel);
+
+ generateNestedFunctionInits();
+
+ // default is to generate debug info
+ if (compilerEnv.isGenerateDebugInfo()) {
+ cfw.addVariableDescriptor(debugVariableName,
+ "Lorg/mozilla/javascript/Scriptable;",
+ cfw.getCurrentCodeOffset(), variableObjectLocal);
+ }
+
+ if (fnCurrent == null) {
+ // OPT: use dataflow to prove that this assignment is dead
+ popvLocal = getNewWordLocal();
+ Codegen.pushUndefined(cfw);
+ cfw.addAStore(popvLocal);
+
+ int linenum = scriptOrFn.getEndLineno();
+ if (linenum != -1)
+ cfw.addLineNumberEntry((short)linenum);
+
+ } else {
+ if (fnCurrent.itsContainsCalls0) {
+ itsZeroArgArray = getNewWordLocal();
+ cfw.add(ByteCode.GETSTATIC,
+ "org/mozilla/javascript/ScriptRuntime",
+ "emptyArgs", "[Ljava/lang/Object;");
+ cfw.addAStore(itsZeroArgArray);
+ }
+ if (fnCurrent.itsContainsCalls1) {
+ itsOneArgArray = getNewWordLocal();
+ cfw.addPush(1);
+ cfw.add(ByteCode.ANEWARRAY, "java/lang/Object");
+ cfw.addAStore(itsOneArgArray);
+ }
+ }
+ }
+
+ private void generateGetGeneratorResumptionPoint()
+ {
+ cfw.addALoad(generatorStateLocal);
+ cfw.add(ByteCode.GETFIELD,
+ OptRuntime.GeneratorState.CLASS_NAME,
+ OptRuntime.GeneratorState.resumptionPoint_NAME,
+ OptRuntime.GeneratorState.resumptionPoint_TYPE);
+ }
+
+ private void generateSetGeneratorResumptionPoint(int nextState)
+ {
+ cfw.addALoad(generatorStateLocal);
+ cfw.addLoadConstant(nextState);
+ cfw.add(ByteCode.PUTFIELD,
+ OptRuntime.GeneratorState.CLASS_NAME,
+ OptRuntime.GeneratorState.resumptionPoint_NAME,
+ OptRuntime.GeneratorState.resumptionPoint_TYPE);
+ }
+
+ private void generateGetGeneratorStackState()
+ {
+ cfw.addALoad(generatorStateLocal);
+ addOptRuntimeInvoke("getGeneratorStackState",
+ "(Ljava/lang/Object;)[Ljava/lang/Object;");
+ }
+
+ private void generateEpilogue()
+ {
+ if (compilerEnv.isGenerateObserverCount())
+ addInstructionCount();
+ if (isGenerator) {
+ // generate locals initialization
+ HashMap liveLocals = ((FunctionNode)scriptOrFn).getLiveLocals();
+ if (liveLocals != null) {
+ ArrayList nodes = ((FunctionNode)scriptOrFn).getResumptionPoints();
+ for (int i = 0; i < nodes.size(); i++) {
+ Node node = (Node) nodes.get(i);
+ int[] live = (int [])liveLocals.get(node);
+ if (live != null) {
+ cfw.markTableSwitchCase(generatorSwitch,
+ getNextGeneratorState(node));
+ generateGetGeneratorLocalsState();
+ for (int j = 0; j < live.length; j++) {
+ cfw.add(ByteCode.DUP);
+ cfw.addLoadConstant(j);
+ cfw.add(ByteCode.AALOAD);
+ cfw.addAStore(live[j]);
+ }
+ cfw.add(ByteCode.POP);
+ cfw.add(ByteCode.GOTO, getTargetLabel(node));
+ }
+ }
+ }
+
+ // generate dispatch tables for finally
+ if (finallys != null) {
+ Enumeration en = finallys.keys();
+ while(en.hasMoreElements()) {
+ Node n = (Node) en.nextElement();
+ if (n.getType() == Token.FINALLY) {
+ FinallyReturnPoint ret =
+ (FinallyReturnPoint)finallys.get(n);
+ // the finally will jump here
+ cfw.markLabel(ret.tableLabel, (short)1);
+
+ // start generating a dispatch table
+ int startSwitch = cfw.addTableSwitch(0,
+ ret.jsrPoints.size() - 1);
+ int c = 0;
+ cfw.markTableSwitchDefault(startSwitch);
+ for (int i = 0; i < ret.jsrPoints.size(); i++) {
+ // generate gotos back to the JSR location
+ cfw.markTableSwitchCase(startSwitch, c);
+ cfw.add(ByteCode.GOTO,
+ ((Integer)ret.jsrPoints.get(i)).intValue());
+ c++;
+ }
+ }
+ }
+ }
+ }
+
+ if (epilogueLabel != -1) {
+ cfw.markLabel(epilogueLabel);
+ }
+
+ if (hasVarsInRegs) {
+ cfw.add(ByteCode.ARETURN);
+ return;
+ } else if (isGenerator) {
+ if (((FunctionNode)scriptOrFn).getResumptionPoints() != null) {
+ cfw.markTableSwitchDefault(generatorSwitch);
+ }
+
+ // change state for re-entry
+ generateSetGeneratorResumptionPoint(GENERATOR_TERMINATE);
+
+ // throw StopIteration
+ cfw.addALoad(variableObjectLocal);
+ addOptRuntimeInvoke("throwStopIteration",
+ "(Ljava/lang/Object;)V");
+
+ Codegen.pushUndefined(cfw);
+ cfw.add(ByteCode.ARETURN);
+
+ } else if (fnCurrent == null) {
+ cfw.addALoad(popvLocal);
+ cfw.add(ByteCode.ARETURN);
+ } else {
+ generateActivationExit();
+ cfw.add(ByteCode.ARETURN);
+
+ // Generate catch block to catch all and rethrow to call exit code
+ // under exception propagation as well.
+
+ int finallyHandler = cfw.acquireLabel();
+ cfw.markHandler(finallyHandler);
+ short exceptionObject = getNewWordLocal();
+ cfw.addAStore(exceptionObject);
+
+ // Duplicate generateActivationExit() in the catch block since it
+ // takes less space then full-featured ByteCode.JSR/ByteCode.RET
+ generateActivationExit();
+
+ cfw.addALoad(exceptionObject);
+ releaseWordLocal(exceptionObject);
+ // rethrow
+ cfw.add(ByteCode.ATHROW);
+
+ // mark the handler
+ cfw.addExceptionHandler(enterAreaStartLabel, epilogueLabel,
+ finallyHandler, null); // catch any
+ }
+ }
+
+ private void generateGetGeneratorLocalsState() {
+ cfw.addALoad(generatorStateLocal);
+ addOptRuntimeInvoke("getGeneratorLocalsState",
+ "(Ljava/lang/Object;)[Ljava/lang/Object;");
+ }
+
+ private void generateActivationExit()
+ {
+ if (fnCurrent == null || hasVarsInRegs) throw Kit.codeBug();
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke("exitActivationFunction",
+ "(Lorg/mozilla/javascript/Context;)V");
+ }
+
+ private void generateStatement(Node node)
+ {
+ updateLineNumber(node);
+ int type = node.getType();
+ Node child = node.getFirstChild();
+ switch (type) {
+ case Token.LOOP:
+ case Token.LABEL:
+ case Token.WITH:
+ case Token.SCRIPT:
+ case Token.BLOCK:
+ case Token.EMPTY:
+ // no-ops.
+ while (child != null) {
+ generateStatement(child);
+ child = child.getNext();
+ }
+ break;
+
+ case Token.LOCAL_BLOCK: {
+ int local = getNewWordLocal();
+ if (isGenerator) {
+ cfw.add(ByteCode.ACONST_NULL);
+ cfw.addAStore(local);
+ }
+ node.putIntProp(Node.LOCAL_PROP, local);
+ while (child != null) {
+ generateStatement(child);
+ child = child.getNext();
+ }
+ releaseWordLocal((short)local);
+ node.removeProp(Node.LOCAL_PROP);
+ break;
+ }
+
+ case Token.FUNCTION: {
+ int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
+ OptFunctionNode ofn = OptFunctionNode.get(scriptOrFn, fnIndex);
+ int t = ofn.fnode.getFunctionType();
+ if (t == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) {
+ visitFunction(ofn, t);
+ } else {
+ if (t != FunctionNode.FUNCTION_STATEMENT) {
+ throw Codegen.badTree();
+ }
+ }
+ break;
+ }
+
+ case Token.TRY:
+ visitTryCatchFinally((Node.Jump)node, child);
+ break;
+
+ case Token.CATCH_SCOPE:
+ {
+ // nothing stays on the stack on entry into a catch scope
+ cfw.setStackTop((short) 0);
+
+ int local = getLocalBlockRegister(node);
+ int scopeIndex
+ = node.getExistingIntProp(Node.CATCH_SCOPE_PROP);
+
+ String name = child.getString(); // name of exception
+ child = child.getNext();
+ generateExpression(child, node); // load expression object
+ if (scopeIndex == 0) {
+ cfw.add(ByteCode.ACONST_NULL);
+ } else {
+ // Load previous catch scope object
+ cfw.addALoad(local);
+ }
+ cfw.addPush(name);
+ cfw.addALoad(contextLocal);
+ cfw.addALoad(variableObjectLocal);
+
+ addScriptRuntimeInvoke(
+ "newCatchScope",
+ "(Ljava/lang/Throwable;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"Ljava/lang/String;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +")Lorg/mozilla/javascript/Scriptable;");
+ cfw.addAStore(local);
+ }
+ break;
+
+ case Token.THROW:
+ generateExpression(child, node);
+ if (compilerEnv.isGenerateObserverCount())
+ addInstructionCount();
+ generateThrowJavaScriptException();
+ break;
+
+ case Token.RETHROW:
+ if (compilerEnv.isGenerateObserverCount())
+ addInstructionCount();
+ cfw.addALoad(getLocalBlockRegister(node));
+ cfw.add(ByteCode.ATHROW);
+ break;
+
+ case Token.RETURN_RESULT:
+ case Token.RETURN:
+ if (!isGenerator) {
+ if (child != null) {
+ generateExpression(child, node);
+ } else if (type == Token.RETURN) {
+ Codegen.pushUndefined(cfw);
+ } else {
+ if (popvLocal < 0) throw Codegen.badTree();
+ cfw.addALoad(popvLocal);
+ }
+ }
+ if (compilerEnv.isGenerateObserverCount())
+ addInstructionCount();
+ if (epilogueLabel == -1) {
+ if (!hasVarsInRegs) throw Codegen.badTree();
+ epilogueLabel = cfw.acquireLabel();
+ }
+ cfw.add(ByteCode.GOTO, epilogueLabel);
+ break;
+
+ case Token.SWITCH:
+ if (compilerEnv.isGenerateObserverCount())
+ addInstructionCount();
+ visitSwitch((Node.Jump)node, child);
+ break;
+
+ case Token.ENTERWITH:
+ generateExpression(child, node);
+ cfw.addALoad(contextLocal);
+ cfw.addALoad(variableObjectLocal);
+ addScriptRuntimeInvoke(
+ "enterWith",
+ "(Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +")Lorg/mozilla/javascript/Scriptable;");
+ cfw.addAStore(variableObjectLocal);
+ incReferenceWordLocal(variableObjectLocal);
+ break;
+
+ case Token.LEAVEWITH:
+ cfw.addALoad(variableObjectLocal);
+ addScriptRuntimeInvoke(
+ "leaveWith",
+ "(Lorg/mozilla/javascript/Scriptable;"
+ +")Lorg/mozilla/javascript/Scriptable;");
+ cfw.addAStore(variableObjectLocal);
+ decReferenceWordLocal(variableObjectLocal);
+ break;
+
+ case Token.ENUM_INIT_KEYS:
+ case Token.ENUM_INIT_VALUES:
+ case Token.ENUM_INIT_ARRAY:
+ generateExpression(child, node);
+ cfw.addALoad(contextLocal);
+ int enumType = type == Token.ENUM_INIT_KEYS
+ ? ScriptRuntime.ENUMERATE_KEYS :
+ type == Token.ENUM_INIT_VALUES
+ ? ScriptRuntime.ENUMERATE_VALUES :
+ ScriptRuntime.ENUMERATE_ARRAY;
+ cfw.addPush(enumType);
+ addScriptRuntimeInvoke("enumInit",
+ "(Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"I"
+ +")Ljava/lang/Object;");
+ cfw.addAStore(getLocalBlockRegister(node));
+ break;
+
+ case Token.EXPR_VOID:
+ if (child.getType() == Token.SETVAR) {
+ /* special case this so as to avoid unnecessary
+ load's & pop's */
+ visitSetVar(child, child.getFirstChild(), false);
+ }
+ else if (child.getType() == Token.SETCONSTVAR) {
+ /* special case this so as to avoid unnecessary
+ load's & pop's */
+ visitSetConstVar(child, child.getFirstChild(), false);
+ }
+ else if (child.getType() == Token.YIELD) {
+ generateYieldPoint(child, false);
+ }
+ else {
+ generateExpression(child, node);
+ if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1)
+ cfw.add(ByteCode.POP2);
+ else
+ cfw.add(ByteCode.POP);
+ }
+ break;
+
+ case Token.EXPR_RESULT:
+ generateExpression(child, node);
+ if (popvLocal < 0) {
+ popvLocal = getNewWordLocal();
+ }
+ cfw.addAStore(popvLocal);
+ break;
+
+ case Token.TARGET:
+ {
+ if (compilerEnv.isGenerateObserverCount())
+ addInstructionCount();
+ int label = getTargetLabel(node);
+ cfw.markLabel(label);
+ if (compilerEnv.isGenerateObserverCount())
+ saveCurrentCodeOffset();
+ }
+ break;
+
+ case Token.JSR:
+ case Token.GOTO:
+ case Token.IFEQ:
+ case Token.IFNE:
+ if (compilerEnv.isGenerateObserverCount())
+ addInstructionCount();
+ visitGoto((Node.Jump)node, type, child);
+ break;
+
+ case Token.FINALLY:
+ {
+ if (compilerEnv.isGenerateObserverCount())
+ saveCurrentCodeOffset();
+ // there is exactly one value on the stack when enterring
+ // finally blocks: the return address (or its int encoding)
+ cfw.setStackTop((short)1);
+
+ // Save return address in a new local
+ int finallyRegister = getNewWordLocal();
+ if (isGenerator)
+ generateIntegerWrap();
+ cfw.addAStore(finallyRegister);
+
+ while (child != null) {
+ generateStatement(child);
+ child = child.getNext();
+ }
+ if (isGenerator) {
+ cfw.addALoad(finallyRegister);
+ cfw.add(ByteCode.CHECKCAST, "java/lang/Integer");
+ generateIntegerUnwrap();
+ FinallyReturnPoint ret =
+ (FinallyReturnPoint)finallys.get(node);
+ ret.tableLabel = cfw.acquireLabel();
+ cfw.add(ByteCode.GOTO, ret.tableLabel);
+ } else {
+ cfw.add(ByteCode.RET, finallyRegister);
+ }
+ releaseWordLocal((short)finallyRegister);
+ }
+ break;
+
+ case Token.DEBUGGER:
+ break;
+
+ default:
+ throw Codegen.badTree();
+ }
+
+ }
+
+ private void generateIntegerWrap()
+ {
+ cfw.addInvoke(ByteCode.INVOKESTATIC, "java/lang/Integer", "valueOf",
+ "(I)Ljava/lang/Integer;");
+ }
+
+
+ private void generateIntegerUnwrap()
+ {
+ cfw.addInvoke(ByteCode.INVOKEVIRTUAL, "java/lang/Integer",
+ "intValue", "()I");
+ }
+
+
+ private void generateThrowJavaScriptException()
+ {
+ cfw.add(ByteCode.NEW,
+ "org/mozilla/javascript/JavaScriptException");
+ cfw.add(ByteCode.DUP_X1);
+ cfw.add(ByteCode.SWAP);
+ cfw.addPush(scriptOrFn.getSourceName());
+ cfw.addPush(itsLineNumber);
+ cfw.addInvoke(
+ ByteCode.INVOKESPECIAL,
+ "org/mozilla/javascript/JavaScriptException",
+ "<init>",
+ "(Ljava/lang/Object;Ljava/lang/String;I)V");
+ cfw.add(ByteCode.ATHROW);
+ }
+
+ private int getNextGeneratorState(Node node)
+ {
+ int nodeIndex = ((FunctionNode)scriptOrFn).getResumptionPoints()
+ .indexOf(node);
+ return nodeIndex + GENERATOR_YIELD_START;
+ }
+
+ private void generateExpression(Node node, Node parent)
+ {
+ int type = node.getType();
+ Node child = node.getFirstChild();
+ switch (type) {
+ case Token.USE_STACK:
+ break;
+
+ case Token.FUNCTION:
+ if (fnCurrent != null || parent.getType() != Token.SCRIPT) {
+ int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
+ OptFunctionNode ofn = OptFunctionNode.get(scriptOrFn,
+ fnIndex);
+ int t = ofn.fnode.getFunctionType();
+ if (t != FunctionNode.FUNCTION_EXPRESSION) {
+ throw Codegen.badTree();
+ }
+ visitFunction(ofn, t);
+ }
+ break;
+
+ case Token.NAME:
+ {
+ cfw.addALoad(contextLocal);
+ cfw.addALoad(variableObjectLocal);
+ cfw.addPush(node.getString());
+ addScriptRuntimeInvoke(
+ "name",
+ "(Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"Ljava/lang/String;"
+ +")Ljava/lang/Object;");
+ }
+ break;
+
+ case Token.CALL:
+ case Token.NEW:
+ {
+ int specialType = node.getIntProp(Node.SPECIALCALL_PROP,
+ Node.NON_SPECIALCALL);
+ if (specialType == Node.NON_SPECIALCALL) {
+ OptFunctionNode target;
+ target = (OptFunctionNode)node.getProp(
+ Node.DIRECTCALL_PROP);
+
+ if (target != null) {
+ visitOptimizedCall(node, target, type, child);
+ } else if (type == Token.CALL) {
+ visitStandardCall(node, child);
+ } else {
+ visitStandardNew(node, child);
+ }
+ } else {
+ visitSpecialCall(node, type, specialType, child);
+ }
+ }
+ break;
+
+ case Token.REF_CALL:
+ generateFunctionAndThisObj(child, node);
+ // stack: ... functionObj thisObj
+ child = child.getNext();
+ generateCallArgArray(node, child, false);
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke(
+ "callRef",
+ "(Lorg/mozilla/javascript/Callable;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"[Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Lorg/mozilla/javascript/Ref;");
+ break;
+
+ case Token.NUMBER:
+ {
+ double num = node.getDouble();
+ if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
+ cfw.addPush(num);
+ } else {
+ codegen.pushNumberAsObject(cfw, num);
+ }
+ }
+ break;
+
+ case Token.STRING:
+ cfw.addPush(node.getString());
+ break;
+
+ case Token.THIS:
+ cfw.addALoad(thisObjLocal);
+ break;
+
+ case Token.THISFN:
+ cfw.add(ByteCode.ALOAD_0);
+ break;
+
+ case Token.NULL:
+ cfw.add(ByteCode.ACONST_NULL);
+ break;
+
+ case Token.TRUE:
+ cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean",
+ "TRUE", "Ljava/lang/Boolean;");
+ break;
+
+ case Token.FALSE:
+ cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean",
+ "FALSE", "Ljava/lang/Boolean;");
+ break;
+
+ case Token.REGEXP:
+ {
+ int i = node.getExistingIntProp(Node.REGEXP_PROP);
+ // Scripts can not use REGEXP_ARRAY_FIELD_NAME since
+ // it it will make script.exec non-reentrant so they
+ // store regexp array in a local variable while
+ // functions always access precomputed
+ // REGEXP_ARRAY_FIELD_NAME not to consume locals
+ if (fnCurrent == null) {
+ cfw.addALoad(scriptRegexpLocal);
+ } else {
+ cfw.addALoad(funObjLocal);
+ cfw.add(ByteCode.GETFIELD, codegen.mainClassName,
+ Codegen.REGEXP_ARRAY_FIELD_NAME,
+ Codegen.REGEXP_ARRAY_FIELD_TYPE);
+ }
+ cfw.addPush(i);
+ cfw.add(ByteCode.AALOAD);
+ }
+ break;
+
+ case Token.COMMA: {
+ Node next = child.getNext();
+ while (next != null) {
+ generateExpression(child, node);
+ cfw.add(ByteCode.POP);
+ child = next;
+ next = next.getNext();
+ }
+ generateExpression(child, node);
+ break;
+ }
+
+ case Token.ENUM_NEXT:
+ case Token.ENUM_ID: {
+ int local = getLocalBlockRegister(node);
+ cfw.addALoad(local);
+ if (type == Token.ENUM_NEXT) {
+ addScriptRuntimeInvoke(
+ "enumNext", "(Ljava/lang/Object;)Ljava/lang/Boolean;");
+ } else {
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke("enumId",
+ "(Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Ljava/lang/Object;");
+ }
+ break;
+ }
+
+ case Token.ARRAYLIT:
+ visitArrayLiteral(node, child);
+ break;
+
+ case Token.OBJECTLIT:
+ visitObjectLiteral(node, child);
+ break;
+
+ case Token.NOT: {
+ int trueTarget = cfw.acquireLabel();
+ int falseTarget = cfw.acquireLabel();
+ int beyond = cfw.acquireLabel();
+ generateIfJump(child, node, trueTarget, falseTarget);
+
+ cfw.markLabel(trueTarget);
+ cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean",
+ "FALSE", "Ljava/lang/Boolean;");
+ cfw.add(ByteCode.GOTO, beyond);
+ cfw.markLabel(falseTarget);
+ cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean",
+ "TRUE", "Ljava/lang/Boolean;");
+ cfw.markLabel(beyond);
+ cfw.adjustStackTop(-1);
+ break;
+ }
+
+ case Token.BITNOT:
+ generateExpression(child, node);
+ addScriptRuntimeInvoke("toInt32", "(Ljava/lang/Object;)I");
+ cfw.addPush(-1); // implement ~a as (a ^ -1)
+ cfw.add(ByteCode.IXOR);
+ cfw.add(ByteCode.I2D);
+ addDoubleWrap();
+ break;
+
+ case Token.VOID:
+ generateExpression(child, node);
+ cfw.add(ByteCode.POP);
+ Codegen.pushUndefined(cfw);
+ break;
+
+ case Token.TYPEOF:
+ generateExpression(child, node);
+ addScriptRuntimeInvoke("typeof",
+ "(Ljava/lang/Object;"
+ +")Ljava/lang/String;");
+ break;
+
+ case Token.TYPEOFNAME:
+ visitTypeofname(node);
+ break;
+
+ case Token.INC:
+ case Token.DEC:
+ visitIncDec(node);
+ break;
+
+ case Token.OR:
+ case Token.AND: {
+ generateExpression(child, node);
+ cfw.add(ByteCode.DUP);
+ addScriptRuntimeInvoke("toBoolean",
+ "(Ljava/lang/Object;)Z");
+ int falseTarget = cfw.acquireLabel();
+ if (type == Token.AND)
+ cfw.add(ByteCode.IFEQ, falseTarget);
+ else
+ cfw.add(ByteCode.IFNE, falseTarget);
+ cfw.add(ByteCode.POP);
+ generateExpression(child.getNext(), node);
+ cfw.markLabel(falseTarget);
+ }
+ break;
+
+ case Token.HOOK : {
+ Node ifThen = child.getNext();
+ Node ifElse = ifThen.getNext();
+ generateExpression(child, node);
+ addScriptRuntimeInvoke("toBoolean",
+ "(Ljava/lang/Object;)Z");
+ int elseTarget = cfw.acquireLabel();
+ cfw.add(ByteCode.IFEQ, elseTarget);
+ short stack = cfw.getStackTop();
+ generateExpression(ifThen, node);
+ int afterHook = cfw.acquireLabel();
+ cfw.add(ByteCode.GOTO, afterHook);
+ cfw.markLabel(elseTarget, stack);
+ generateExpression(ifElse, node);
+ cfw.markLabel(afterHook);
+ }
+ break;
+
+ case Token.ADD: {
+ generateExpression(child, node);
+ generateExpression(child.getNext(), node);
+ switch (node.getIntProp(Node.ISNUMBER_PROP, -1)) {
+ case Node.BOTH:
+ cfw.add(ByteCode.DADD);
+ break;
+ case Node.LEFT:
+ addOptRuntimeInvoke("add",
+ "(DLjava/lang/Object;)Ljava/lang/Object;");
+ break;
+ case Node.RIGHT:
+ addOptRuntimeInvoke("add",
+ "(Ljava/lang/Object;D)Ljava/lang/Object;");
+ break;
+ default:
+ if (child.getType() == Token.STRING) {
+ addScriptRuntimeInvoke("add",
+ "(Ljava/lang/String;"
+ +"Ljava/lang/Object;"
+ +")Ljava/lang/String;");
+ } else if (child.getNext().getType() == Token.STRING) {
+ addScriptRuntimeInvoke("add",
+ "(Ljava/lang/Object;"
+ +"Ljava/lang/String;"
+ +")Ljava/lang/String;");
+ } else {
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke("add",
+ "(Ljava/lang/Object;"
+ +"Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Ljava/lang/Object;");
+ }
+ }
+ }
+ break;
+
+ case Token.MUL:
+ visitArithmetic(node, ByteCode.DMUL, child, parent);
+ break;
+
+ case Token.SUB:
+ visitArithmetic(node, ByteCode.DSUB, child, parent);
+ break;
+
+ case Token.DIV:
+ case Token.MOD:
+ visitArithmetic(node, type == Token.DIV
+ ? ByteCode.DDIV
+ : ByteCode.DREM, child, parent);
+ break;
+
+ case Token.BITOR:
+ case Token.BITXOR:
+ case Token.BITAND:
+ case Token.LSH:
+ case Token.RSH:
+ case Token.URSH:
+ visitBitOp(node, type, child);
+ break;
+
+ case Token.POS:
+ case Token.NEG:
+ generateExpression(child, node);
+ addObjectToDouble();
+ if (type == Token.NEG) {
+ cfw.add(ByteCode.DNEG);
+ }
+ addDoubleWrap();
+ break;
+
+ case Token.TO_DOUBLE:
+ // cnvt to double (not Double)
+ generateExpression(child, node);
+ addObjectToDouble();
+ break;
+
+ case Token.TO_OBJECT: {
+ // convert from double
+ int prop = -1;
+ if (child.getType() == Token.NUMBER) {
+ prop = child.getIntProp(Node.ISNUMBER_PROP, -1);
+ }
+ if (prop != -1) {
+ child.removeProp(Node.ISNUMBER_PROP);
+ generateExpression(child, node);
+ child.putIntProp(Node.ISNUMBER_PROP, prop);
+ } else {
+ generateExpression(child, node);
+ addDoubleWrap();
+ }
+ break;
+ }
+
+ case Token.IN:
+ case Token.INSTANCEOF:
+ case Token.LE:
+ case Token.LT:
+ case Token.GE:
+ case Token.GT: {
+ int trueGOTO = cfw.acquireLabel();
+ int falseGOTO = cfw.acquireLabel();
+ visitIfJumpRelOp(node, child, trueGOTO, falseGOTO);
+ addJumpedBooleanWrap(trueGOTO, falseGOTO);
+ break;
+ }
+
+ case Token.EQ:
+ case Token.NE:
+ case Token.SHEQ:
+ case Token.SHNE: {
+ int trueGOTO = cfw.acquireLabel();
+ int falseGOTO = cfw.acquireLabel();
+ visitIfJumpEqOp(node, child, trueGOTO, falseGOTO);
+ addJumpedBooleanWrap(trueGOTO, falseGOTO);
+ break;
+ }
+
+ case Token.GETPROP:
+ case Token.GETPROPNOWARN:
+ visitGetProp(node, child);
+ break;
+
+ case Token.GETELEM:
+ generateExpression(child, node); // object
+ generateExpression(child.getNext(), node); // id
+ cfw.addALoad(contextLocal);
+ if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
+ addScriptRuntimeInvoke(
+ "getObjectIndex",
+ "(Ljava/lang/Object;D"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Ljava/lang/Object;");
+ }
+ else {
+ addScriptRuntimeInvoke(
+ "getObjectElem",
+ "(Ljava/lang/Object;"
+ +"Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Ljava/lang/Object;");
+ }
+ break;
+
+ case Token.GET_REF:
+ generateExpression(child, node); // reference
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke(
+ "refGet",
+ "(Lorg/mozilla/javascript/Ref;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Ljava/lang/Object;");
+ break;
+
+ case Token.GETVAR:
+ visitGetVar(node);
+ break;
+
+ case Token.SETVAR:
+ visitSetVar(node, child, true);
+ break;
+
+ case Token.SETNAME:
+ visitSetName(node, child);
+ break;
+
+ case Token.SETCONST:
+ visitSetConst(node, child);
+ break;
+
+ case Token.SETCONSTVAR:
+ visitSetConstVar(node, child, true);
+ break;
+
+ case Token.SETPROP:
+ case Token.SETPROP_OP:
+ visitSetProp(type, node, child);
+ break;
+
+ case Token.SETELEM:
+ case Token.SETELEM_OP:
+ visitSetElem(type, node, child);
+ break;
+
+ case Token.SET_REF:
+ case Token.SET_REF_OP:
+ {
+ generateExpression(child, node);
+ child = child.getNext();
+ if (type == Token.SET_REF_OP) {
+ cfw.add(ByteCode.DUP);
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke(
+ "refGet",
+ "(Lorg/mozilla/javascript/Ref;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Ljava/lang/Object;");
+ }
+ generateExpression(child, node);
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke(
+ "refSet",
+ "(Lorg/mozilla/javascript/Ref;"
+ +"Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Ljava/lang/Object;");
+ }
+ break;
+
+ case Token.DEL_REF:
+ generateExpression(child, node);
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke("refDel",
+ "(Lorg/mozilla/javascript/Ref;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Ljava/lang/Object;");
+ break;
+
+ case Token.DELPROP:
+ generateExpression(child, node);
+ child = child.getNext();
+ generateExpression(child, node);
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke("delete",
+ "(Ljava/lang/Object;"
+ +"Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Ljava/lang/Object;");
+ break;
+
+ case Token.BINDNAME:
+ {
+ while (child != null) {
+ generateExpression(child, node);
+ child = child.getNext();
+ }
+ // Generate code for "ScriptRuntime.bind(varObj, "s")"
+ cfw.addALoad(contextLocal);
+ cfw.addALoad(variableObjectLocal);
+ cfw.addPush(node.getString());
+ addScriptRuntimeInvoke(
+ "bind",
+ "(Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"Ljava/lang/String;"
+ +")Lorg/mozilla/javascript/Scriptable;");
+ }
+ break;
+
+ case Token.LOCAL_LOAD:
+ cfw.addALoad(getLocalBlockRegister(node));
+ break;
+
+ case Token.REF_SPECIAL:
+ {
+ String special = (String)node.getProp(Node.NAME_PROP);
+ generateExpression(child, node);
+ cfw.addPush(special);
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke(
+ "specialRef",
+ "(Ljava/lang/Object;"
+ +"Ljava/lang/String;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Lorg/mozilla/javascript/Ref;");
+ }
+ 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
+ do {
+ generateExpression(child, node);
+ child = child.getNext();
+ } while (child != null);
+ cfw.addALoad(contextLocal);
+ String methodName, signature;
+ switch (type) {
+ case Token.REF_MEMBER:
+ methodName = "memberRef";
+ signature = "(Ljava/lang/Object;"
+ +"Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"I"
+ +")Lorg/mozilla/javascript/Ref;";
+ break;
+ case Token.REF_NS_MEMBER:
+ methodName = "memberRef";
+ signature = "(Ljava/lang/Object;"
+ +"Ljava/lang/Object;"
+ +"Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"I"
+ +")Lorg/mozilla/javascript/Ref;";
+ break;
+ case Token.REF_NAME:
+ methodName = "nameRef";
+ signature = "(Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"I"
+ +")Lorg/mozilla/javascript/Ref;";
+ cfw.addALoad(variableObjectLocal);
+ break;
+ case Token.REF_NS_NAME:
+ methodName = "nameRef";
+ signature = "(Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"I"
+ +")Lorg/mozilla/javascript/Ref;";
+ cfw.addALoad(variableObjectLocal);
+ break;
+ default:
+ throw Kit.codeBug();
+ }
+ cfw.addPush(memberTypeFlags);
+ addScriptRuntimeInvoke(methodName, signature);
+ }
+ break;
+
+ case Token.DOTQUERY:
+ visitDotQuery(node, child);
+ break;
+
+ case Token.ESCXMLATTR:
+ generateExpression(child, node);
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke("escapeAttributeValue",
+ "(Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Ljava/lang/String;");
+ break;
+
+ case Token.ESCXMLTEXT:
+ generateExpression(child, node);
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke("escapeTextValue",
+ "(Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Ljava/lang/String;");
+ break;
+
+ case Token.DEFAULTNAMESPACE:
+ generateExpression(child, node);
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke("setDefaultNamespace",
+ "(Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Ljava/lang/Object;");
+ break;
+
+ case Token.YIELD:
+ generateYieldPoint(node, true);
+ break;
+
+ case Token.WITHEXPR: {
+ Node enterWith = child;
+ Node with = enterWith.getNext();
+ Node leaveWith = with.getNext();
+ generateStatement(enterWith);
+ generateExpression(with.getFirstChild(), with);
+ generateStatement(leaveWith);
+ break;
+ }
+
+ case Token.ARRAYCOMP: {
+ Node initStmt = child;
+ Node expr = child.getNext();
+ generateStatement(initStmt);
+ generateExpression(expr, node);
+ break;
+ }
+
+ default:
+ throw new RuntimeException("Unexpected node type "+type);
+ }
+
+ }
+
+ private void generateYieldPoint(Node node, boolean exprContext) {
+ // save stack state
+ int top = cfw.getStackTop();
+ maxStack = maxStack > top ? maxStack : top;
+ if (cfw.getStackTop() != 0) {
+ generateGetGeneratorStackState();
+ for (int i = 0; i < top; i++) {
+ cfw.add(ByteCode.DUP_X1);
+ cfw.add(ByteCode.SWAP);
+ cfw.addLoadConstant(i);
+ cfw.add(ByteCode.SWAP);
+ cfw.add(ByteCode.AASTORE);
+ }
+ // pop the array object
+ cfw.add(ByteCode.POP);
+ }
+
+ // generate the yield argument
+ Node child = node.getFirstChild();
+ if (child != null)
+ generateExpression(child, node);
+ else
+ Codegen.pushUndefined(cfw);
+
+ // change the resumption state
+ int nextState = getNextGeneratorState(node);
+ generateSetGeneratorResumptionPoint(nextState);
+
+ boolean hasLocals = generateSaveLocals(node);
+
+ cfw.add(ByteCode.ARETURN);
+
+ generateCheckForThrowOrClose(getTargetLabel(node),
+ hasLocals, nextState);
+
+ // reconstruct the stack
+ if (top != 0) {
+ generateGetGeneratorStackState();
+ for (int i = 0; i < top; i++) {
+ cfw.add(ByteCode.DUP);
+ cfw.addLoadConstant(top - i - 1);
+ cfw.add(ByteCode.AALOAD);
+ cfw.add(ByteCode.SWAP);
+ }
+ cfw.add(ByteCode.POP);
+ }
+
+ // load return value from yield
+ if (exprContext) {
+ cfw.addALoad(argsLocal);
+ }
+ }
+
+ private void generateCheckForThrowOrClose(int label,
+ boolean hasLocals,
+ int nextState) {
+ int throwLabel = cfw.acquireLabel();
+ int closeLabel = cfw.acquireLabel();
+
+ // throw the user provided object, if the operation is .throw()
+ cfw.markLabel(throwLabel);
+ cfw.addALoad(argsLocal);
+ generateThrowJavaScriptException();
+
+ // throw our special internal exception if the generator is being closed
+ cfw.markLabel(closeLabel);
+ cfw.addALoad(argsLocal);
+ cfw.add(ByteCode.CHECKCAST, "java/lang/Throwable");
+ cfw.add(ByteCode.ATHROW);
+
+ // mark the re-entry point
+ // jump here after initializing the locals
+ if (label != -1)
+ cfw.markLabel(label);
+ if (!hasLocals) {
+ // jump here directly if there are no locals
+ cfw.markTableSwitchCase(generatorSwitch, nextState);
+ }
+
+ // see if we need to dispatch for .close() or .throw()
+ cfw.addILoad(operationLocal);
+ cfw.addLoadConstant(NativeGenerator.GENERATOR_CLOSE);
+ cfw.add(ByteCode.IF_ICMPEQ, closeLabel);
+ cfw.addILoad(operationLocal);
+ cfw.addLoadConstant(NativeGenerator.GENERATOR_THROW);
+ cfw.add(ByteCode.IF_ICMPEQ, throwLabel);
+ }
+
+ private void generateIfJump(Node node, Node parent,
+ int trueLabel, int falseLabel)
+ {
+ // System.out.println("gen code for " + node.toString());
+
+ int type = node.getType();
+ Node child = node.getFirstChild();
+
+ switch (type) {
+ case Token.NOT:
+ generateIfJump(child, node, falseLabel, trueLabel);
+ break;
+
+ case Token.OR:
+ case Token.AND: {
+ int interLabel = cfw.acquireLabel();
+ if (type == Token.AND) {
+ generateIfJump(child, node, interLabel, falseLabel);
+ }
+ else {
+ generateIfJump(child, node, trueLabel, interLabel);
+ }
+ cfw.markLabel(interLabel);
+ child = child.getNext();
+ generateIfJump(child, node, trueLabel, falseLabel);
+ break;
+ }
+
+ case Token.IN:
+ case Token.INSTANCEOF:
+ case Token.LE:
+ case Token.LT:
+ case Token.GE:
+ case Token.GT:
+ visitIfJumpRelOp(node, child, trueLabel, falseLabel);
+ break;
+
+ case Token.EQ:
+ case Token.NE:
+ case Token.SHEQ:
+ case Token.SHNE:
+ visitIfJumpEqOp(node, child, trueLabel, falseLabel);
+ break;
+
+ default:
+ // Generate generic code for non-optimized jump
+ generateExpression(node, parent);
+ addScriptRuntimeInvoke("toBoolean", "(Ljava/lang/Object;)Z");
+ cfw.add(ByteCode.IFNE, trueLabel);
+ cfw.add(ByteCode.GOTO, falseLabel);
+ }
+ }
+
+ private void visitFunction(OptFunctionNode ofn, int functionType)
+ {
+ int fnIndex = codegen.getIndex(ofn.fnode);
+ cfw.add(ByteCode.NEW, codegen.mainClassName);
+ // Call function constructor
+ cfw.add(ByteCode.DUP);
+ cfw.addALoad(variableObjectLocal);
+ cfw.addALoad(contextLocal); // load 'cx'
+ cfw.addPush(fnIndex);
+ cfw.addInvoke(ByteCode.INVOKESPECIAL, codegen.mainClassName,
+ "<init>", Codegen.FUNCTION_CONSTRUCTOR_SIGNATURE);
+
+ // Init mainScript field;
+ cfw.add(ByteCode.DUP);
+ if (isTopLevel) {
+ cfw.add(ByteCode.ALOAD_0);
+ } else {
+ cfw.add(ByteCode.ALOAD_0);
+ cfw.add(ByteCode.GETFIELD,
+ codegen.mainClassName,
+ Codegen.DIRECT_CALL_PARENT_FIELD,
+ codegen.mainClassSignature);
+ }
+ cfw.add(ByteCode.PUTFIELD,
+ codegen.mainClassName,
+ Codegen.DIRECT_CALL_PARENT_FIELD,
+ codegen.mainClassSignature);
+
+ int directTargetIndex = ofn.getDirectTargetIndex();
+ if (directTargetIndex >= 0) {
+ cfw.add(ByteCode.DUP);
+ if (isTopLevel) {
+ cfw.add(ByteCode.ALOAD_0);
+ } else {
+ cfw.add(ByteCode.ALOAD_0);
+ cfw.add(ByteCode.GETFIELD,
+ codegen.mainClassName,
+ Codegen.DIRECT_CALL_PARENT_FIELD,
+ codegen.mainClassSignature);
+ }
+ cfw.add(ByteCode.SWAP);
+ cfw.add(ByteCode.PUTFIELD,
+ codegen.mainClassName,
+ Codegen.getDirectTargetFieldName(directTargetIndex),
+ codegen.mainClassSignature);
+ }
+
+ if (functionType == FunctionNode.FUNCTION_EXPRESSION) {
+ // Leave closure object on stack and do not pass it to
+ // initFunction which suppose to connect statements to scope
+ return;
+ }
+ cfw.addPush(functionType);
+ cfw.addALoad(variableObjectLocal);
+ cfw.addALoad(contextLocal); // load 'cx'
+ addOptRuntimeInvoke("initFunction",
+ "(Lorg/mozilla/javascript/NativeFunction;"
+ +"I"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")V");
+ }
+
+ private int getTargetLabel(Node target)
+ {
+ int labelId = target.labelId();
+ if (labelId == -1) {
+ labelId = cfw.acquireLabel();
+ target.labelId(labelId);
+ }
+ return labelId;
+ }
+
+ private void visitGoto(Node.Jump node, int type, Node child)
+ {
+ Node target = node.target;
+ if (type == Token.IFEQ || type == Token.IFNE) {
+ if (child == null) throw Codegen.badTree();
+ int targetLabel = getTargetLabel(target);
+ int fallThruLabel = cfw.acquireLabel();
+ if (type == Token.IFEQ)
+ generateIfJump(child, node, targetLabel, fallThruLabel);
+ else
+ generateIfJump(child, node, fallThruLabel, targetLabel);
+ cfw.markLabel(fallThruLabel);
+ } else {
+ if (type == Token.JSR) {
+ if (isGenerator) {
+ addGotoWithReturn(target);
+ } else {
+ addGoto(target, ByteCode.JSR);
+ }
+ } else {
+ addGoto(target, ByteCode.GOTO);
+ }
+ }
+ }
+
+ private void addGotoWithReturn(Node target) {
+ FinallyReturnPoint ret =
+ (FinallyReturnPoint)finallys.get(target);
+ cfw.addLoadConstant(ret.jsrPoints.size());
+ addGoto(target, ByteCode.GOTO);
+ int retLabel = cfw.acquireLabel();
+ cfw.markLabel(retLabel);
+ ret.jsrPoints.add(Integer.valueOf(retLabel));
+ }
+
+ private void visitArrayLiteral(Node node, Node child)
+ {
+ int count = 0;
+ for (Node cursor = child; cursor != null; cursor = cursor.getNext()) {
+ ++count;
+ }
+ // load array to store array literal objects
+ addNewObjectArray(count);
+ for (int i = 0; i != count; ++i) {
+ cfw.add(ByteCode.DUP);
+ cfw.addPush(i);
+ generateExpression(child, node);
+ cfw.add(ByteCode.AASTORE);
+ child = child.getNext();
+ }
+ int[] skipIndexes = (int[])node.getProp(Node.SKIP_INDEXES_PROP);
+ if (skipIndexes == null) {
+ cfw.add(ByteCode.ACONST_NULL);
+ cfw.add(ByteCode.ICONST_0);
+ } else {
+ cfw.addPush(OptRuntime.encodeIntArray(skipIndexes));
+ cfw.addPush(skipIndexes.length);
+ }
+ cfw.addALoad(contextLocal);
+ cfw.addALoad(variableObjectLocal);
+ addOptRuntimeInvoke("newArrayLiteral",
+ "([Ljava/lang/Object;"
+ +"Ljava/lang/String;"
+ +"I"
+ +"Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +")Lorg/mozilla/javascript/Scriptable;");
+ }
+
+ private void visitObjectLiteral(Node node, Node child)
+ {
+ Object[] properties = (Object[])node.getProp(Node.OBJECT_IDS_PROP);
+ int count = properties.length;
+
+ // load array with property ids
+ addNewObjectArray(count);
+ for (int i = 0; i != count; ++i) {
+ cfw.add(ByteCode.DUP);
+ cfw.addPush(i);
+ Object id = properties[i];
+ if (id instanceof String) {
+ cfw.addPush((String)id);
+ } else {
+ cfw.addPush(((Integer)id).intValue());
+ addScriptRuntimeInvoke("wrapInt", "(I)Ljava/lang/Integer;");
+ }
+ cfw.add(ByteCode.AASTORE);
+ }
+ // load array with property values
+ addNewObjectArray(count);
+ Node child2 = child;
+ for (int i = 0; i != count; ++i) {
+ cfw.add(ByteCode.DUP);
+ cfw.addPush(i);
+ int childType = child.getType();
+ if (childType == Token.GET) {
+ generateExpression(child.getFirstChild(), node);
+ } else if (childType == Token.SET) {
+ generateExpression(child.getFirstChild(), node);
+ } else {
+ generateExpression(child, node);
+ }
+ cfw.add(ByteCode.AASTORE);
+ child = child.getNext();
+ }
+ // load array with getterSetter values
+ cfw.addPush(count);
+ cfw.add(ByteCode.NEWARRAY, ByteCode.T_INT);
+ for (int i = 0; i != count; ++i) {
+ cfw.add(ByteCode.DUP);
+ cfw.addPush(i);
+ int childType = child2.getType();
+ if (childType == Token.GET) {
+ cfw.add(ByteCode.ICONST_M1);
+ } else if (childType == Token.SET) {
+ cfw.add(ByteCode.ICONST_1);
+ } else {
+ cfw.add(ByteCode.ICONST_0);
+ }
+ cfw.add(ByteCode.IASTORE);
+ child2 = child2.getNext();
+ }
+
+ cfw.addALoad(contextLocal);
+ cfw.addALoad(variableObjectLocal);
+ addScriptRuntimeInvoke("newObjectLiteral",
+ "([Ljava/lang/Object;"
+ +"[Ljava/lang/Object;"
+ +"[I"
+ +"Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +")Lorg/mozilla/javascript/Scriptable;");
+ }
+
+ private void visitSpecialCall(Node node, int type, int specialType,
+ Node child)
+ {
+ cfw.addALoad(contextLocal);
+
+ if (type == Token.NEW) {
+ generateExpression(child, node);
+ // stack: ... cx functionObj
+ } else {
+ generateFunctionAndThisObj(child, node);
+ // stack: ... cx functionObj thisObj
+ }
+ child = child.getNext();
+
+ generateCallArgArray(node, child, false);
+
+ String methodName;
+ String callSignature;
+
+ if (type == Token.NEW) {
+ methodName = "newObjectSpecial";
+ callSignature = "(Lorg/mozilla/javascript/Context;"
+ +"Ljava/lang/Object;"
+ +"[Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"I" // call type
+ +")Ljava/lang/Object;";
+ cfw.addALoad(variableObjectLocal);
+ cfw.addALoad(thisObjLocal);
+ cfw.addPush(specialType);
+ } else {
+ methodName = "callSpecial";
+ callSignature = "(Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Callable;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"[Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"I" // call type
+ +"Ljava/lang/String;I" // filename, linenumber
+ +")Ljava/lang/Object;";
+ cfw.addALoad(variableObjectLocal);
+ cfw.addALoad(thisObjLocal);
+ cfw.addPush(specialType);
+ String sourceName = scriptOrFn.getSourceName();
+ cfw.addPush(sourceName == null ? "" : sourceName);
+ cfw.addPush(itsLineNumber);
+ }
+
+ addOptRuntimeInvoke(methodName, callSignature);
+ }
+
+ private void visitStandardCall(Node node, Node child)
+ {
+ if (node.getType() != Token.CALL) throw Codegen.badTree();
+
+ Node firstArgChild = child.getNext();
+ int childType = child.getType();
+
+ String methodName;
+ String signature;
+
+ if (firstArgChild == null) {
+ if (childType == Token.NAME) {
+ // name() call
+ String name = child.getString();
+ cfw.addPush(name);
+ methodName = "callName0";
+ signature = "(Ljava/lang/String;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +")Ljava/lang/Object;";
+ } else if (childType == Token.GETPROP) {
+ // x.name() call
+ Node propTarget = child.getFirstChild();
+ generateExpression(propTarget, node);
+ Node id = propTarget.getNext();
+ String property = id.getString();
+ cfw.addPush(property);
+ methodName = "callProp0";
+ signature = "(Ljava/lang/Object;"
+ +"Ljava/lang/String;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +")Ljava/lang/Object;";
+ } else if (childType == Token.GETPROPNOWARN) {
+ throw Kit.codeBug();
+ } else {
+ generateFunctionAndThisObj(child, node);
+ methodName = "call0";
+ signature = "(Lorg/mozilla/javascript/Callable;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +")Ljava/lang/Object;";
+ }
+
+ } else if (childType == Token.NAME) {
+ // XXX: this optimization is only possible if name
+ // resolution
+ // is not affected by arguments evaluation and currently
+ // there are no checks for it
+ String name = child.getString();
+ generateCallArgArray(node, firstArgChild, false);
+ cfw.addPush(name);
+ methodName = "callName";
+ signature = "([Ljava/lang/Object;"
+ +"Ljava/lang/String;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +")Ljava/lang/Object;";
+ } else {
+ int argCount = 0;
+ for (Node arg = firstArgChild; arg != null; arg = arg.getNext()) {
+ ++argCount;
+ }
+ generateFunctionAndThisObj(child, node);
+ // stack: ... functionObj thisObj
+ if (argCount == 1) {
+ generateExpression(firstArgChild, node);
+ methodName = "call1";
+ signature = "(Lorg/mozilla/javascript/Callable;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +")Ljava/lang/Object;";
+ } else if (argCount == 2) {
+ generateExpression(firstArgChild, node);
+ generateExpression(firstArgChild.getNext(), node);
+ methodName = "call2";
+ signature = "(Lorg/mozilla/javascript/Callable;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"Ljava/lang/Object;"
+ +"Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +")Ljava/lang/Object;";
+ } else {
+ generateCallArgArray(node, firstArgChild, false);
+ methodName = "callN";
+ signature = "(Lorg/mozilla/javascript/Callable;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"[Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +")Ljava/lang/Object;";
+ }
+ }
+
+ cfw.addALoad(contextLocal);
+ cfw.addALoad(variableObjectLocal);
+ addOptRuntimeInvoke(methodName, signature);
+ }
+
+ private void visitStandardNew(Node node, Node child)
+ {
+ if (node.getType() != Token.NEW) throw Codegen.badTree();
+
+ Node firstArgChild = child.getNext();
+
+ generateExpression(child, node);
+ // stack: ... functionObj
+ cfw.addALoad(contextLocal);
+ cfw.addALoad(variableObjectLocal);
+ // stack: ... functionObj cx scope
+ generateCallArgArray(node, firstArgChild, false);
+ addScriptRuntimeInvoke(
+ "newObject",
+ "(Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"[Ljava/lang/Object;"
+ +")Lorg/mozilla/javascript/Scriptable;");
+ }
+
+ private void visitOptimizedCall(Node node, OptFunctionNode target,
+ int type, Node child)
+ {
+ Node firstArgChild = child.getNext();
+
+ short thisObjLocal = 0;
+ if (type == Token.NEW) {
+ generateExpression(child, node);
+ } else {
+ generateFunctionAndThisObj(child, node);
+ thisObjLocal = getNewWordLocal();
+ cfw.addAStore(thisObjLocal);
+ }
+ // stack: ... functionObj
+
+ int beyond = cfw.acquireLabel();
+
+ int directTargetIndex = target.getDirectTargetIndex();
+ if (isTopLevel) {
+ cfw.add(ByteCode.ALOAD_0);
+ } else {
+ cfw.add(ByteCode.ALOAD_0);
+ cfw.add(ByteCode.GETFIELD, codegen.mainClassName,
+ Codegen.DIRECT_CALL_PARENT_FIELD,
+ codegen.mainClassSignature);
+ }
+ cfw.add(ByteCode.GETFIELD, codegen.mainClassName,
+ Codegen.getDirectTargetFieldName(directTargetIndex),
+ codegen.mainClassSignature);
+
+ cfw.add(ByteCode.DUP2);
+ // stack: ... functionObj directFunct functionObj directFunct
+
+ int regularCall = cfw.acquireLabel();
+ cfw.add(ByteCode.IF_ACMPNE, regularCall);
+
+ // stack: ... functionObj directFunct
+ short stackHeight = cfw.getStackTop();
+ cfw.add(ByteCode.SWAP);
+ cfw.add(ByteCode.POP);
+ // stack: ... directFunct
+ if (compilerEnv.isUseDynamicScope()) {
+ cfw.addALoad(contextLocal);
+ cfw.addALoad(variableObjectLocal);
+ } else {
+ cfw.add(ByteCode.DUP);
+ // stack: ... directFunct directFunct
+ cfw.addInvoke(ByteCode.INVOKEINTERFACE,
+ "org/mozilla/javascript/Scriptable",
+ "getParentScope",
+ "()Lorg/mozilla/javascript/Scriptable;");
+ // stack: ... directFunct scope
+ cfw.addALoad(contextLocal);
+ // stack: ... directFunct scope cx
+ cfw.add(ByteCode.SWAP);
+ }
+ // stack: ... directFunc cx scope
+
+ if (type == Token.NEW) {
+ cfw.add(ByteCode.ACONST_NULL);
+ } else {
+ cfw.addALoad(thisObjLocal);
+ }
+ // stack: ... directFunc cx scope thisObj
+/*
+Remember that directCall parameters are paired in 1 aReg and 1 dReg
+If the argument is an incoming arg, just pass the orginal pair thru.
+Else, if the argument is known to be typed 'Number', pass Void.TYPE
+in the aReg and the number is the dReg
+Else pass the JS object in the aReg and 0.0 in the dReg.
+*/
+ Node argChild = firstArgChild;
+ while (argChild != null) {
+ int dcp_register = nodeIsDirectCallParameter(argChild);
+ if (dcp_register >= 0) {
+ cfw.addALoad(dcp_register);
+ cfw.addDLoad(dcp_register + 1);
+ } else if (argChild.getIntProp(Node.ISNUMBER_PROP, -1)
+ == Node.BOTH)
+ {
+ cfw.add(ByteCode.GETSTATIC,
+ "java/lang/Void",
+ "TYPE",
+ "Ljava/lang/Class;");
+ generateExpression(argChild, node);
+ } else {
+ generateExpression(argChild, node);
+ cfw.addPush(0.0);
+ }
+ argChild = argChild.getNext();
+ }
+
+ cfw.add(ByteCode.GETSTATIC,
+ "org/mozilla/javascript/ScriptRuntime",
+ "emptyArgs", "[Ljava/lang/Object;");
+ cfw.addInvoke(ByteCode.INVOKESTATIC,
+ codegen.mainClassName,
+ (type == Token.NEW)
+ ? codegen.getDirectCtorName(target.fnode)
+ : codegen.getBodyMethodName(target.fnode),
+ codegen.getBodyMethodSignature(target.fnode));
+
+ cfw.add(ByteCode.GOTO, beyond);
+
+ cfw.markLabel(regularCall, stackHeight);
+ // stack: ... functionObj directFunct
+ cfw.add(ByteCode.POP);
+ cfw.addALoad(contextLocal);
+ cfw.addALoad(variableObjectLocal);
+ // stack: ... functionObj cx scope
+ if (type != Token.NEW) {
+ cfw.addALoad(thisObjLocal);
+ releaseWordLocal(thisObjLocal);
+ // stack: ... functionObj cx scope thisObj
+ }
+ // XXX: this will generate code for the child array the second time,
+ // so expression code generation better not to alter tree structure...
+ generateCallArgArray(node, firstArgChild, true);
+
+ if (type == Token.NEW) {
+ addScriptRuntimeInvoke(
+ "newObject",
+ "(Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"[Ljava/lang/Object;"
+ +")Lorg/mozilla/javascript/Scriptable;");
+ } else {
+ cfw.addInvoke(ByteCode.INVOKEINTERFACE,
+ "org/mozilla/javascript/Callable",
+ "call",
+ "(Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"[Ljava/lang/Object;"
+ +")Ljava/lang/Object;");
+ }
+
+ cfw.markLabel(beyond);
+ }
+
+ private void generateCallArgArray(Node node, Node argChild, boolean directCall)
+ {
+ int argCount = 0;
+ for (Node child = argChild; child != null; child = child.getNext()) {
+ ++argCount;
+ }
+ // load array object to set arguments
+ if (argCount == 1 && itsOneArgArray >= 0) {
+ cfw.addALoad(itsOneArgArray);
+ } else {
+ addNewObjectArray(argCount);
+ }
+ // Copy arguments into it
+ for (int i = 0; i != argCount; ++i) {
+ // If we are compiling a generator an argument could be the result
+ // of a yield. In that case we will have an immediate on the stack
+ // which we need to avoid
+ if (!isGenerator) {
+ cfw.add(ByteCode.DUP);
+ cfw.addPush(i);
+ }
+
+ if (!directCall) {
+ generateExpression(argChild, node);
+ } else {
+ // If this has also been a directCall sequence, the Number
+ // flag will have remained set for any parameter so that
+ // the values could be copied directly into the outgoing
+ // args. Here we want to force it to be treated as not in
+ // a Number context, so we set the flag off.
+ int dcp_register = nodeIsDirectCallParameter(argChild);
+ if (dcp_register >= 0) {
+ dcpLoadAsObject(dcp_register);
+ } else {
+ generateExpression(argChild, node);
+ int childNumberFlag
+ = argChild.getIntProp(Node.ISNUMBER_PROP, -1);
+ if (childNumberFlag == Node.BOTH) {
+ addDoubleWrap();
+ }
+ }
+ }
+
+ // When compiling generators, any argument to a method may be a
+ // yield expression. Hence we compile the argument first and then
+ // load the argument index and assign the value to the args array.
+ if (isGenerator) {
+ short tempLocal = getNewWordLocal();
+ cfw.addAStore(tempLocal);
+ cfw.add(ByteCode.CHECKCAST, "[Ljava/lang/Object;");
+ cfw.add(ByteCode.DUP);
+ cfw.addPush(i);
+ cfw.addALoad(tempLocal);
+ releaseWordLocal(tempLocal);
+ }
+
+ cfw.add(ByteCode.AASTORE);
+
+ argChild = argChild.getNext();
+ }
+ }
+
+ private void generateFunctionAndThisObj(Node node, Node parent)
+ {
+ // Place on stack (function object, function this) pair
+ int type = node.getType();
+ switch (node.getType()) {
+ case Token.GETPROPNOWARN:
+ throw Kit.codeBug();
+
+ case Token.GETPROP:
+ case Token.GETELEM: {
+ Node target = node.getFirstChild();
+ generateExpression(target, node);
+ Node id = target.getNext();
+ if (type == Token.GETPROP) {
+ String property = id.getString();
+ cfw.addPush(property);
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke(
+ "getPropFunctionAndThis",
+ "(Ljava/lang/Object;"
+ +"Ljava/lang/String;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Lorg/mozilla/javascript/Callable;");
+ } else {
+ // Optimizer do not optimize this case for now
+ if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1)
+ throw Codegen.badTree();
+ generateExpression(id, node); // id
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke(
+ "getElemFunctionAndThis",
+ "(Ljava/lang/Object;"
+ +"Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Lorg/mozilla/javascript/Callable;");
+ }
+ break;
+ }
+
+ case Token.NAME: {
+ String name = node.getString();
+ cfw.addPush(name);
+ cfw.addALoad(contextLocal);
+ cfw.addALoad(variableObjectLocal);
+ addScriptRuntimeInvoke(
+ "getNameFunctionAndThis",
+ "(Ljava/lang/String;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +")Lorg/mozilla/javascript/Callable;");
+ break;
+ }
+
+ default: // including GETVAR
+ generateExpression(node, parent);
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke(
+ "getValueFunctionAndThis",
+ "(Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Lorg/mozilla/javascript/Callable;");
+ break;
+ }
+ // Get thisObj prepared by get(Name|Prop|Elem|Value)FunctionAndThis
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke(
+ "lastStoredScriptable",
+ "(Lorg/mozilla/javascript/Context;"
+ +")Lorg/mozilla/javascript/Scriptable;");
+ }
+
+ private void updateLineNumber(Node node)
+ {
+ itsLineNumber = node.getLineno();
+ if (itsLineNumber == -1)
+ return;
+ cfw.addLineNumberEntry((short)itsLineNumber);
+ }
+
+ private void visitTryCatchFinally(Node.Jump node, Node child)
+ {
+ /* Save the variable object, in case there are with statements
+ * enclosed by the try block and we catch some exception.
+ * We'll restore it for the catch block so that catch block
+ * statements get the right scope.
+ */
+
+ // OPT we only need to do this if there are enclosed WITH
+ // statements; could statically check and omit this if there aren't any.
+
+ // XXX OPT Maybe instead do syntactic transforms to associate
+ // each 'with' with a try/finally block that does the exitwith.
+
+ short savedVariableObject = getNewWordLocal();
+ cfw.addALoad(variableObjectLocal);
+ cfw.addAStore(savedVariableObject);
+
+ /*
+ * Generate the code for the tree; most of the work is done in IRFactory
+ * and NodeTransformer; Codegen just adds the java handlers for the
+ * javascript catch and finally clauses. */
+
+ int startLabel = cfw.acquireLabel();
+ cfw.markLabel(startLabel, (short)0);
+
+ Node catchTarget = node.target;
+ Node finallyTarget = node.getFinally();
+
+ // create a table for the equivalent of JSR returns
+ if (isGenerator && finallyTarget != null) {
+ FinallyReturnPoint ret = new FinallyReturnPoint();
+ if (finallys == null) {
+ finallys = new Hashtable();
+ }
+ // add the finally target to hashtable
+ finallys.put(finallyTarget, ret);
+ // add the finally node as well to the hash table
+ finallys.put(finallyTarget.getNext(), ret);
+ }
+
+ while (child != null) {
+ generateStatement(child);
+ child = child.getNext();
+ }
+
+ // control flow skips the handlers
+ int realEnd = cfw.acquireLabel();
+ cfw.add(ByteCode.GOTO, realEnd);
+
+ int exceptionLocal = getLocalBlockRegister(node);
+ // javascript handler; unwrap exception and GOTO to javascript
+ // catch area.
+ if (catchTarget != null) {
+ // get the label to goto
+ int catchLabel = catchTarget.labelId();
+
+ generateCatchBlock(JAVASCRIPT_EXCEPTION, savedVariableObject,
+ catchLabel, startLabel, exceptionLocal);
+ /*
+ * catch WrappedExceptions, see if they are wrapped
+ * JavaScriptExceptions. Otherwise, rethrow.
+ */
+ generateCatchBlock(EVALUATOR_EXCEPTION, savedVariableObject,
+ catchLabel, startLabel, exceptionLocal);
+
+ /*
+ we also need to catch EcmaErrors and feed the
+ associated error object to the handler
+ */
+ generateCatchBlock(ECMAERROR_EXCEPTION, savedVariableObject,
+ catchLabel, startLabel, exceptionLocal);
+
+ Context cx = Context.getCurrentContext();
+ if (cx != null &&
+ cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS))
+ {
+ generateCatchBlock(THROWABLE_EXCEPTION, savedVariableObject,
+ catchLabel, startLabel, exceptionLocal);
+ }
+ }
+
+ // finally handler; catch all exceptions, store to a local; JSR to
+ // the finally, then re-throw.
+ if (finallyTarget != null) {
+ int finallyHandler = cfw.acquireLabel();
+ cfw.markHandler(finallyHandler);
+ cfw.addAStore(exceptionLocal);
+
+ // reset the variable object local
+ cfw.addALoad(savedVariableObject);
+ cfw.addAStore(variableObjectLocal);
+
+ // get the label to JSR to
+ int finallyLabel = finallyTarget.labelId();
+ if (isGenerator)
+ addGotoWithReturn(finallyTarget);
+ else
+ cfw.add(ByteCode.JSR, finallyLabel);
+
+ // rethrow
+ cfw.addALoad(exceptionLocal);
+ if (isGenerator)
+ cfw.add(ByteCode.CHECKCAST, "java/lang/Throwable");
+ cfw.add(ByteCode.ATHROW);
+
+ // mark the handler
+ cfw.addExceptionHandler(startLabel, finallyLabel,
+ finallyHandler, null); // catch any
+ }
+ releaseWordLocal(savedVariableObject);
+ cfw.markLabel(realEnd);
+ }
+
+ private static final int JAVASCRIPT_EXCEPTION = 0;
+ private static final int EVALUATOR_EXCEPTION = 1;
+ private static final int ECMAERROR_EXCEPTION = 2;
+ private static final int THROWABLE_EXCEPTION = 3;
+
+ private void generateCatchBlock(int exceptionType,
+ short savedVariableObject,
+ int catchLabel, int startLabel,
+ int exceptionLocal)
+ {
+ int handler = cfw.acquireLabel();
+ cfw.markHandler(handler);
+
+ // MS JVM gets cranky if the exception object is left on the stack
+ cfw.addAStore(exceptionLocal);
+
+ // reset the variable object local
+ cfw.addALoad(savedVariableObject);
+ cfw.addAStore(variableObjectLocal);
+
+ String exceptionName;
+ if (exceptionType == JAVASCRIPT_EXCEPTION) {
+ exceptionName = "org/mozilla/javascript/JavaScriptException";
+ } else if (exceptionType == EVALUATOR_EXCEPTION) {
+ exceptionName = "org/mozilla/javascript/EvaluatorException";
+ } else if (exceptionType == ECMAERROR_EXCEPTION) {
+ exceptionName = "org/mozilla/javascript/EcmaError";
+ } else if (exceptionType == THROWABLE_EXCEPTION) {
+ exceptionName = "java/lang/Throwable";
+ } else {
+ throw Kit.codeBug();
+ }
+
+ // mark the handler
+ cfw.addExceptionHandler(startLabel, catchLabel, handler,
+ exceptionName);
+
+ cfw.add(ByteCode.GOTO, catchLabel);
+ }
+
+
+ private boolean generateSaveLocals(Node node)
+ {
+ int count = 0;
+ for (int i = 0; i < firstFreeLocal; i++) {
+ if (locals[i] != 0)
+ count++;
+ }
+
+ if (count == 0) {
+ ((FunctionNode)scriptOrFn).addLiveLocals(node, null);
+ return false;
+ }
+
+ // calculate the max locals
+ maxLocals = maxLocals > count ? maxLocals : count;
+
+ // create a locals list
+ int[] ls = new int[count];
+ int s = 0;
+ for (int i = 0; i < firstFreeLocal; i++) {
+ if (locals[i] != 0) {
+ ls[s] = i;
+ s++;
+ }
+ }
+
+ // save the locals
+ ((FunctionNode)scriptOrFn).addLiveLocals(node, ls);
+
+ // save locals
+ generateGetGeneratorLocalsState();
+ for (int i = 0; i < count; i++) {
+ cfw.add(ByteCode.DUP);
+ cfw.addLoadConstant(i);
+ cfw.addALoad(ls[i]);
+ cfw.add(ByteCode.AASTORE);
+ }
+ // pop the array off the stack
+ cfw.add(ByteCode.POP);
+
+ return true;
+ }
+
+ private void visitSwitch(Node.Jump switchNode, Node child)
+ {
+ // See comments in IRFactory.createSwitch() for description
+ // of SWITCH node
+
+ generateExpression(child, switchNode);
+ // save selector value
+ short selector = getNewWordLocal();
+ cfw.addAStore(selector);
+
+ for (Node.Jump caseNode = (Node.Jump)child.getNext();
+ caseNode != null;
+ caseNode = (Node.Jump)caseNode.getNext())
+ {
+ if (caseNode.getType() != Token.CASE)
+ throw Codegen.badTree();
+ Node test = caseNode.getFirstChild();
+ generateExpression(test, caseNode);
+ cfw.addALoad(selector);
+ addScriptRuntimeInvoke("shallowEq",
+ "(Ljava/lang/Object;"
+ +"Ljava/lang/Object;"
+ +")Z");
+ addGoto(caseNode.target, ByteCode.IFNE);
+ }
+ releaseWordLocal(selector);
+ }
+
+ private void visitTypeofname(Node node)
+ {
+ if (hasVarsInRegs) {
+ int varIndex = fnCurrent.fnode.getIndexForNameNode(node);
+ if (varIndex >= 0) {
+ if (fnCurrent.isNumberVar(varIndex)) {
+ cfw.addPush("number");
+ } else if (varIsDirectCallParameter(varIndex)) {
+ int dcp_register = varRegisters[varIndex];
+ cfw.addALoad(dcp_register);
+ cfw.add(ByteCode.GETSTATIC, "java/lang/Void", "TYPE",
+ "Ljava/lang/Class;");
+ int isNumberLabel = cfw.acquireLabel();
+ cfw.add(ByteCode.IF_ACMPEQ, isNumberLabel);
+ short stack = cfw.getStackTop();
+ cfw.addALoad(dcp_register);
+ addScriptRuntimeInvoke("typeof",
+ "(Ljava/lang/Object;"
+ +")Ljava/lang/String;");
+ int beyond = cfw.acquireLabel();
+ cfw.add(ByteCode.GOTO, beyond);
+ cfw.markLabel(isNumberLabel, stack);
+ cfw.addPush("number");
+ cfw.markLabel(beyond);
+ } else {
+ cfw.addALoad(varRegisters[varIndex]);
+ addScriptRuntimeInvoke("typeof",
+ "(Ljava/lang/Object;"
+ +")Ljava/lang/String;");
+ }
+ return;
+ }
+ }
+ cfw.addALoad(variableObjectLocal);
+ cfw.addPush(node.getString());
+ addScriptRuntimeInvoke("typeofName",
+ "(Lorg/mozilla/javascript/Scriptable;"
+ +"Ljava/lang/String;"
+ +")Ljava/lang/String;");
+ }
+
+ /**
+ * Save the current code offset. This saved code offset is used to
+ * compute instruction counts in subsequent calls to
+ * {@link #addInstructionCount}.
+ */
+ private void saveCurrentCodeOffset() {
+ savedCodeOffset = cfw.getCurrentCodeOffset();
+ }
+
+ /**
+ * Generate calls to ScriptRuntime.addInstructionCount to keep track of
+ * executed instructions and call <code>observeInstructionCount()</code>
+ * if a threshold is exceeded.
+ */
+ private void addInstructionCount() {
+ int count = cfw.getCurrentCodeOffset() - savedCodeOffset;
+ if (count == 0)
+ return;
+ cfw.addALoad(contextLocal);
+ cfw.addPush(count);
+ addScriptRuntimeInvoke("addInstructionCount",
+ "(Lorg/mozilla/javascript/Context;"
+ +"I)V");
+ }
+
+ private void visitIncDec(Node node)
+ {
+ int incrDecrMask = node.getExistingIntProp(Node.INCRDECR_PROP);
+ Node child = node.getFirstChild();
+ switch (child.getType()) {
+ case Token.GETVAR:
+ if (!hasVarsInRegs) Kit.codeBug();
+ if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
+ boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
+ int varIndex = fnCurrent.getVarIndex(child);
+ short reg = varRegisters[varIndex];
+ int offset = varIsDirectCallParameter(varIndex) ? 1 : 0;
+ cfw.addDLoad(reg + offset);
+ if (post) {
+ cfw.add(ByteCode.DUP2);
+ }
+ cfw.addPush(1.0);
+ if ((incrDecrMask & Node.DECR_FLAG) == 0) {
+ cfw.add(ByteCode.DADD);
+ } else {
+ cfw.add(ByteCode.DSUB);
+ }
+ if (!post) {
+ cfw.add(ByteCode.DUP2);
+ }
+ cfw.addDStore(reg + offset);
+ } else {
+ boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
+ int varIndex = fnCurrent.getVarIndex(child);
+ short reg = varRegisters[varIndex];
+ cfw.addALoad(reg);
+ if (post) {
+ cfw.add(ByteCode.DUP);
+ }
+ addObjectToDouble();
+ cfw.addPush(1.0);
+ if ((incrDecrMask & Node.DECR_FLAG) == 0) {
+ cfw.add(ByteCode.DADD);
+ } else {
+ cfw.add(ByteCode.DSUB);
+ }
+ addDoubleWrap();
+ if (!post) {
+ cfw.add(ByteCode.DUP);
+ }
+ cfw.addAStore(reg);
+ break;
+ }
+ break;
+ case Token.NAME:
+ cfw.addALoad(variableObjectLocal);
+ cfw.addPush(child.getString()); // push name
+ cfw.addALoad(contextLocal);
+ cfw.addPush(incrDecrMask);
+ addScriptRuntimeInvoke("nameIncrDecr",
+ "(Lorg/mozilla/javascript/Scriptable;"
+ +"Ljava/lang/String;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"I)Ljava/lang/Object;");
+ break;
+ case Token.GETPROPNOWARN:
+ throw Kit.codeBug();
+ case Token.GETPROP: {
+ Node getPropChild = child.getFirstChild();
+ generateExpression(getPropChild, node);
+ generateExpression(getPropChild.getNext(), node);
+ cfw.addALoad(contextLocal);
+ cfw.addPush(incrDecrMask);
+ addScriptRuntimeInvoke("propIncrDecr",
+ "(Ljava/lang/Object;"
+ +"Ljava/lang/String;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"I)Ljava/lang/Object;");
+ break;
+ }
+ case Token.GETELEM: {
+ Node elemChild = child.getFirstChild();
+ generateExpression(elemChild, node);
+ generateExpression(elemChild.getNext(), node);
+ cfw.addALoad(contextLocal);
+ cfw.addPush(incrDecrMask);
+ if (elemChild.getNext().getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
+ addOptRuntimeInvoke("elemIncrDecr",
+ "(Ljava/lang/Object;"
+ +"D"
+ +"Lorg/mozilla/javascript/Context;"
+ +"I"
+ +")Ljava/lang/Object;");
+ } else {
+ addScriptRuntimeInvoke("elemIncrDecr",
+ "(Ljava/lang/Object;"
+ +"Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"I"
+ +")Ljava/lang/Object;");
+ }
+ break;
+ }
+ case Token.GET_REF: {
+ Node refChild = child.getFirstChild();
+ generateExpression(refChild, node);
+ cfw.addALoad(contextLocal);
+ cfw.addPush(incrDecrMask);
+ addScriptRuntimeInvoke(
+ "refIncrDecr",
+ "(Lorg/mozilla/javascript/Ref;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"I)Ljava/lang/Object;");
+ break;
+ }
+ default:
+ Codegen.badTree();
+ }
+ }
+
+ private static boolean isArithmeticNode(Node node)
+ {
+ int type = node.getType();
+ return (type == Token.SUB)
+ || (type == Token.MOD)
+ || (type == Token.DIV)
+ || (type == Token.MUL);
+ }
+
+ private void visitArithmetic(Node node, int opCode, Node child,
+ Node parent)
+ {
+ int childNumberFlag = node.getIntProp(Node.ISNUMBER_PROP, -1);
+ if (childNumberFlag != -1) {
+ generateExpression(child, node);
+ generateExpression(child.getNext(), node);
+ cfw.add(opCode);
+ }
+ else {
+ boolean childOfArithmetic = isArithmeticNode(parent);
+ generateExpression(child, node);
+ if (!isArithmeticNode(child))
+ addObjectToDouble();
+ generateExpression(child.getNext(), node);
+ if (!isArithmeticNode(child.getNext()))
+ addObjectToDouble();
+ cfw.add(opCode);
+ if (!childOfArithmetic) {
+ addDoubleWrap();
+ }
+ }
+ }
+
+ private void visitBitOp(Node node, int type, Node child)
+ {
+ int childNumberFlag = node.getIntProp(Node.ISNUMBER_PROP, -1);
+ generateExpression(child, node);
+
+ // special-case URSH; work with the target arg as a long, so
+ // that we can return a 32-bit unsigned value, and call
+ // toUint32 instead of toInt32.
+ if (type == Token.URSH) {
+ addScriptRuntimeInvoke("toUint32", "(Ljava/lang/Object;)J");
+ generateExpression(child.getNext(), node);
+ addScriptRuntimeInvoke("toInt32", "(Ljava/lang/Object;)I");
+ // Looks like we need to explicitly mask the shift to 5 bits -
+ // LUSHR takes 6 bits.
+ cfw.addPush(31);
+ cfw.add(ByteCode.IAND);
+ cfw.add(ByteCode.LUSHR);
+ cfw.add(ByteCode.L2D);
+ addDoubleWrap();
+ return;
+ }
+ if (childNumberFlag == -1) {
+ addScriptRuntimeInvoke("toInt32", "(Ljava/lang/Object;)I");
+ generateExpression(child.getNext(), node);
+ addScriptRuntimeInvoke("toInt32", "(Ljava/lang/Object;)I");
+ }
+ else {
+ addScriptRuntimeInvoke("toInt32", "(D)I");
+ generateExpression(child.getNext(), node);
+ addScriptRuntimeInvoke("toInt32", "(D)I");
+ }
+ switch (type) {
+ case Token.BITOR:
+ cfw.add(ByteCode.IOR);
+ break;
+ case Token.BITXOR:
+ cfw.add(ByteCode.IXOR);
+ break;
+ case Token.BITAND:
+ cfw.add(ByteCode.IAND);
+ break;
+ case Token.RSH:
+ cfw.add(ByteCode.ISHR);
+ break;
+ case Token.LSH:
+ cfw.add(ByteCode.ISHL);
+ break;
+ default:
+ throw Codegen.badTree();
+ }
+ cfw.add(ByteCode.I2D);
+ if (childNumberFlag == -1) {
+ addDoubleWrap();
+ }
+ }
+
+ private int nodeIsDirectCallParameter(Node node)
+ {
+ if (node.getType() == Token.GETVAR
+ && inDirectCallFunction && !itsForcedObjectParameters)
+ {
+ int varIndex = fnCurrent.getVarIndex(node);
+ if (fnCurrent.isParameter(varIndex)) {
+ return varRegisters[varIndex];
+ }
+ }
+ return -1;
+ }
+
+ private boolean varIsDirectCallParameter(int varIndex)
+ {
+ return fnCurrent.isParameter(varIndex)
+ && inDirectCallFunction && !itsForcedObjectParameters;
+ }
+
+ private void genSimpleCompare(int type, int trueGOTO, int falseGOTO)
+ {
+ if (trueGOTO == -1) throw Codegen.badTree();
+ switch (type) {
+ case Token.LE :
+ cfw.add(ByteCode.DCMPG);
+ cfw.add(ByteCode.IFLE, trueGOTO);
+ break;
+ case Token.GE :
+ cfw.add(ByteCode.DCMPL);
+ cfw.add(ByteCode.IFGE, trueGOTO);
+ break;
+ case Token.LT :
+ cfw.add(ByteCode.DCMPG);
+ cfw.add(ByteCode.IFLT, trueGOTO);
+ break;
+ case Token.GT :
+ cfw.add(ByteCode.DCMPL);
+ cfw.add(ByteCode.IFGT, trueGOTO);
+ break;
+ default :
+ throw Codegen.badTree();
+
+ }
+ if (falseGOTO != -1)
+ cfw.add(ByteCode.GOTO, falseGOTO);
+ }
+
+ private void visitIfJumpRelOp(Node node, Node child,
+ int trueGOTO, int falseGOTO)
+ {
+ if (trueGOTO == -1 || falseGOTO == -1) throw Codegen.badTree();
+ int type = node.getType();
+ Node rChild = child.getNext();
+ if (type == Token.INSTANCEOF || type == Token.IN) {
+ generateExpression(child, node);
+ generateExpression(rChild, node);
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke(
+ (type == Token.INSTANCEOF) ? "instanceOf" : "in",
+ "(Ljava/lang/Object;"
+ +"Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Z");
+ cfw.add(ByteCode.IFNE, trueGOTO);
+ cfw.add(ByteCode.GOTO, falseGOTO);
+ return;
+ }
+ int childNumberFlag = node.getIntProp(Node.ISNUMBER_PROP, -1);
+ int left_dcp_register = nodeIsDirectCallParameter(child);
+ int right_dcp_register = nodeIsDirectCallParameter(rChild);
+ if (childNumberFlag != -1) {
+ // Force numeric context on both parameters and optimize
+ // direct call case as Optimizer currently does not handle it
+
+ if (childNumberFlag != Node.RIGHT) {
+ // Left already has number content
+ generateExpression(child, node);
+ } else if (left_dcp_register != -1) {
+ dcpLoadAsNumber(left_dcp_register);
+ } else {
+ generateExpression(child, node);
+ addObjectToDouble();
+ }
+
+ if (childNumberFlag != Node.LEFT) {
+ // Right already has number content
+ generateExpression(rChild, node);
+ } else if (right_dcp_register != -1) {
+ dcpLoadAsNumber(right_dcp_register);
+ } else {
+ generateExpression(rChild, node);
+ addObjectToDouble();
+ }
+
+ genSimpleCompare(type, trueGOTO, falseGOTO);
+
+ } else {
+ if (left_dcp_register != -1 && right_dcp_register != -1) {
+ // Generate code to dynamically check for number content
+ // if both operands are dcp
+ short stack = cfw.getStackTop();
+ int leftIsNotNumber = cfw.acquireLabel();
+ cfw.addALoad(left_dcp_register);
+ cfw.add(ByteCode.GETSTATIC,
+ "java/lang/Void",
+ "TYPE",
+ "Ljava/lang/Class;");
+ cfw.add(ByteCode.IF_ACMPNE, leftIsNotNumber);
+ cfw.addDLoad(left_dcp_register + 1);
+ dcpLoadAsNumber(right_dcp_register);
+ genSimpleCompare(type, trueGOTO, falseGOTO);
+ if (stack != cfw.getStackTop()) throw Codegen.badTree();
+
+ cfw.markLabel(leftIsNotNumber);
+ int rightIsNotNumber = cfw.acquireLabel();
+ cfw.addALoad(right_dcp_register);
+ cfw.add(ByteCode.GETSTATIC,
+ "java/lang/Void",
+ "TYPE",
+ "Ljava/lang/Class;");
+ cfw.add(ByteCode.IF_ACMPNE, rightIsNotNumber);
+ cfw.addALoad(left_dcp_register);
+ addObjectToDouble();
+ cfw.addDLoad(right_dcp_register + 1);
+ genSimpleCompare(type, trueGOTO, falseGOTO);
+ if (stack != cfw.getStackTop()) throw Codegen.badTree();
+
+ cfw.markLabel(rightIsNotNumber);
+ // Load both register as objects to call generic cmp_*
+ cfw.addALoad(left_dcp_register);
+ cfw.addALoad(right_dcp_register);
+
+ } else {
+ generateExpression(child, node);
+ generateExpression(rChild, node);
+ }
+
+ if (type == Token.GE || type == Token.GT) {
+ cfw.add(ByteCode.SWAP);
+ }
+ String routine = ((type == Token.LT)
+ || (type == Token.GT)) ? "cmp_LT" : "cmp_LE";
+ addScriptRuntimeInvoke(routine,
+ "(Ljava/lang/Object;"
+ +"Ljava/lang/Object;"
+ +")Z");
+ cfw.add(ByteCode.IFNE, trueGOTO);
+ cfw.add(ByteCode.GOTO, falseGOTO);
+ }
+ }
+
+ private void visitIfJumpEqOp(Node node, Node child,
+ int trueGOTO, int falseGOTO)
+ {
+ if (trueGOTO == -1 || falseGOTO == -1) throw Codegen.badTree();
+
+ short stackInitial = cfw.getStackTop();
+ int type = node.getType();
+ Node rChild = child.getNext();
+
+ // Optimize if one of operands is null
+ if (child.getType() == Token.NULL || rChild.getType() == Token.NULL) {
+ // eq is symmetric in this case
+ if (child.getType() == Token.NULL) {
+ child = rChild;
+ }
+ generateExpression(child, node);
+ if (type == Token.SHEQ || type == Token.SHNE) {
+ int testCode = (type == Token.SHEQ)
+ ? ByteCode.IFNULL : ByteCode.IFNONNULL;
+ cfw.add(testCode, trueGOTO);
+ } else {
+ if (type != Token.EQ) {
+ // swap false/true targets for !=
+ if (type != Token.NE) throw Codegen.badTree();
+ int tmp = trueGOTO;
+ trueGOTO = falseGOTO;
+ falseGOTO = tmp;
+ }
+ cfw.add(ByteCode.DUP);
+ int undefCheckLabel = cfw.acquireLabel();
+ cfw.add(ByteCode.IFNONNULL, undefCheckLabel);
+ short stack = cfw.getStackTop();
+ cfw.add(ByteCode.POP);
+ cfw.add(ByteCode.GOTO, trueGOTO);
+ cfw.markLabel(undefCheckLabel, stack);
+ Codegen.pushUndefined(cfw);
+ cfw.add(ByteCode.IF_ACMPEQ, trueGOTO);
+ }
+ cfw.add(ByteCode.GOTO, falseGOTO);
+ } else {
+ int child_dcp_register = nodeIsDirectCallParameter(child);
+ if (child_dcp_register != -1
+ && rChild.getType() == Token.TO_OBJECT)
+ {
+ Node convertChild = rChild.getFirstChild();
+ if (convertChild.getType() == Token.NUMBER) {
+ cfw.addALoad(child_dcp_register);
+ cfw.add(ByteCode.GETSTATIC,
+ "java/lang/Void",
+ "TYPE",
+ "Ljava/lang/Class;");
+ int notNumbersLabel = cfw.acquireLabel();
+ cfw.add(ByteCode.IF_ACMPNE, notNumbersLabel);
+ cfw.addDLoad(child_dcp_register + 1);
+ cfw.addPush(convertChild.getDouble());
+ cfw.add(ByteCode.DCMPL);
+ if (type == Token.EQ)
+ cfw.add(ByteCode.IFEQ, trueGOTO);
+ else
+ cfw.add(ByteCode.IFNE, trueGOTO);
+ cfw.add(ByteCode.GOTO, falseGOTO);
+ cfw.markLabel(notNumbersLabel);
+ // fall thru into generic handling
+ }
+ }
+
+ generateExpression(child, node);
+ generateExpression(rChild, node);
+
+ String name;
+ int testCode;
+ switch (type) {
+ case Token.EQ:
+ name = "eq";
+ testCode = ByteCode.IFNE;
+ break;
+ case Token.NE:
+ name = "eq";
+ testCode = ByteCode.IFEQ;
+ break;
+ case Token.SHEQ:
+ name = "shallowEq";
+ testCode = ByteCode.IFNE;
+ break;
+ case Token.SHNE:
+ name = "shallowEq";
+ testCode = ByteCode.IFEQ;
+ break;
+ default:
+ throw Codegen.badTree();
+ }
+ addScriptRuntimeInvoke(name,
+ "(Ljava/lang/Object;"
+ +"Ljava/lang/Object;"
+ +")Z");
+ cfw.add(testCode, trueGOTO);
+ cfw.add(ByteCode.GOTO, falseGOTO);
+ }
+ if (stackInitial != cfw.getStackTop()) throw Codegen.badTree();
+ }
+
+ private void visitSetName(Node node, Node child)
+ {
+ String name = node.getFirstChild().getString();
+ while (child != null) {
+ generateExpression(child, node);
+ child = child.getNext();
+ }
+ cfw.addALoad(contextLocal);
+ cfw.addALoad(variableObjectLocal);
+ cfw.addPush(name);
+ addScriptRuntimeInvoke(
+ "setName",
+ "(Lorg/mozilla/javascript/Scriptable;"
+ +"Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +"Ljava/lang/String;"
+ +")Ljava/lang/Object;");
+ }
+
+ private void visitSetConst(Node node, Node child)
+ {
+ String name = node.getFirstChild().getString();
+ while (child != null) {
+ generateExpression(child, node);
+ child = child.getNext();
+ }
+ cfw.addALoad(contextLocal);
+ cfw.addPush(name);
+ addScriptRuntimeInvoke(
+ "setConst",
+ "(Lorg/mozilla/javascript/Scriptable;"
+ +"Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +"Ljava/lang/String;"
+ +")Ljava/lang/Object;");
+ }
+
+ private void visitGetVar(Node node)
+ {
+ if (!hasVarsInRegs) Kit.codeBug();
+ int varIndex = fnCurrent.getVarIndex(node);
+ short reg = varRegisters[varIndex];
+ if (varIsDirectCallParameter(varIndex)) {
+ // Remember that here the isNumber flag means that we
+ // want to use the incoming parameter in a Number
+ // context, so test the object type and convert the
+ // value as necessary.
+ if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
+ dcpLoadAsNumber(reg);
+ } else {
+ dcpLoadAsObject(reg);
+ }
+ } else if (fnCurrent.isNumberVar(varIndex)) {
+ cfw.addDLoad(reg);
+ } else {
+ cfw.addALoad(reg);
+ }
+ }
+
+ private void visitSetVar(Node node, Node child, boolean needValue)
+ {
+ if (!hasVarsInRegs) Kit.codeBug();
+ int varIndex = fnCurrent.getVarIndex(node);
+ generateExpression(child.getNext(), node);
+ boolean isNumber = (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1);
+ short reg = varRegisters[varIndex];
+ boolean [] constDeclarations = fnCurrent.fnode.getParamAndVarConst();
+ if (constDeclarations[varIndex]) {
+ if (!needValue) {
+ if (isNumber)
+ cfw.add(ByteCode.POP2);
+ else
+ cfw.add(ByteCode.POP);
+ }
+ }
+ else if (varIsDirectCallParameter(varIndex)) {
+ if (isNumber) {
+ if (needValue) cfw.add(ByteCode.DUP2);
+ cfw.addALoad(reg);
+ cfw.add(ByteCode.GETSTATIC,
+ "java/lang/Void",
+ "TYPE",
+ "Ljava/lang/Class;");
+ int isNumberLabel = cfw.acquireLabel();
+ int beyond = cfw.acquireLabel();
+ cfw.add(ByteCode.IF_ACMPEQ, isNumberLabel);
+ short stack = cfw.getStackTop();
+ addDoubleWrap();
+ cfw.addAStore(reg);
+ cfw.add(ByteCode.GOTO, beyond);
+ cfw.markLabel(isNumberLabel, stack);
+ cfw.addDStore(reg + 1);
+ cfw.markLabel(beyond);
+ }
+ else {
+ if (needValue) cfw.add(ByteCode.DUP);
+ cfw.addAStore(reg);
+ }
+ } else {
+ boolean isNumberVar = fnCurrent.isNumberVar(varIndex);
+ if (isNumber) {
+ if (isNumberVar) {
+ cfw.addDStore(reg);
+ if (needValue) cfw.addDLoad(reg);
+ } else {
+ if (needValue) cfw.add(ByteCode.DUP2);
+ // Cannot save number in variable since !isNumberVar,
+ // so convert to object
+ addDoubleWrap();
+ cfw.addAStore(reg);
+ }
+ } else {
+ if (isNumberVar) Kit.codeBug();
+ cfw.addAStore(reg);
+ if (needValue) cfw.addALoad(reg);
+ }
+ }
+ }
+
+ private void visitSetConstVar(Node node, Node child, boolean needValue)
+ {
+ if (!hasVarsInRegs) Kit.codeBug();
+ int varIndex = fnCurrent.getVarIndex(node);
+ generateExpression(child.getNext(), node);
+ boolean isNumber = (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1);
+ short reg = varRegisters[varIndex];
+ int beyond = cfw.acquireLabel();
+ int noAssign = cfw.acquireLabel();
+ if (isNumber) {
+ cfw.addILoad(reg + 2);
+ cfw.add(ByteCode.IFNE, noAssign);
+ short stack = cfw.getStackTop();
+ cfw.addPush(1);
+ cfw.addIStore(reg + 2);
+ cfw.addDStore(reg);
+ if (needValue) {
+ cfw.addDLoad(reg);
+ cfw.markLabel(noAssign, stack);
+ } else {
+ cfw.add(ByteCode.GOTO, beyond);
+ cfw.markLabel(noAssign, stack);
+ cfw.add(ByteCode.POP2);
+ }
+ }
+ else {
+ cfw.addILoad(reg + 1);
+ cfw.add(ByteCode.IFNE, noAssign);
+ short stack = cfw.getStackTop();
+ cfw.addPush(1);
+ cfw.addIStore(reg + 1);
+ cfw.addAStore(reg);
+ if (needValue) {
+ cfw.addALoad(reg);
+ cfw.markLabel(noAssign, stack);
+ } else {
+ cfw.add(ByteCode.GOTO, beyond);
+ cfw.markLabel(noAssign, stack);
+ cfw.add(ByteCode.POP);
+ }
+ }
+ cfw.markLabel(beyond);
+ }
+
+ private void visitGetProp(Node node, Node child)
+ {
+ generateExpression(child, node); // object
+ Node nameChild = child.getNext();
+ generateExpression(nameChild, node); // the name
+ if (node.getType() == Token.GETPROPNOWARN) {
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke(
+ "getObjectPropNoWarn",
+ "(Ljava/lang/Object;"
+ +"Ljava/lang/String;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Ljava/lang/Object;");
+ return;
+ }
+ /*
+ for 'this.foo' we call getObjectProp(Scriptable...) which can
+ skip some casting overhead.
+ */
+ int childType = child.getType();
+ if (childType == Token.THIS && nameChild.getType() == Token.STRING) {
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke(
+ "getObjectProp",
+ "(Lorg/mozilla/javascript/Scriptable;"
+ +"Ljava/lang/String;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Ljava/lang/Object;");
+ } else {
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke(
+ "getObjectProp",
+ "(Ljava/lang/Object;"
+ +"Ljava/lang/String;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Ljava/lang/Object;");
+ }
+ }
+
+ private void visitSetProp(int type, Node node, Node child)
+ {
+ Node objectChild = child;
+ generateExpression(child, node);
+ child = child.getNext();
+ if (type == Token.SETPROP_OP) {
+ cfw.add(ByteCode.DUP);
+ }
+ Node nameChild = child;
+ generateExpression(child, node);
+ child = child.getNext();
+ if (type == Token.SETPROP_OP) {
+ // stack: ... object object name -> ... object name object name
+ cfw.add(ByteCode.DUP_X1);
+ //for 'this.foo += ...' we call thisGet which can skip some
+ //casting overhead.
+ if (objectChild.getType() == Token.THIS
+ && nameChild.getType() == Token.STRING)
+ {
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke(
+ "getObjectProp",
+ "(Lorg/mozilla/javascript/Scriptable;"
+ +"Ljava/lang/String;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Ljava/lang/Object;");
+ } else {
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke(
+ "getObjectProp",
+ "(Ljava/lang/Object;"
+ +"Ljava/lang/String;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Ljava/lang/Object;");
+ }
+ }
+ generateExpression(child, node);
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke(
+ "setObjectProp",
+ "(Ljava/lang/Object;"
+ +"Ljava/lang/String;"
+ +"Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Ljava/lang/Object;");
+ }
+
+ private void visitSetElem(int type, Node node, Node child)
+ {
+ generateExpression(child, node);
+ child = child.getNext();
+ if (type == Token.SETELEM_OP) {
+ cfw.add(ByteCode.DUP);
+ }
+ generateExpression(child, node);
+ child = child.getNext();
+ boolean indexIsNumber = (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1);
+ if (type == Token.SETELEM_OP) {
+ if (indexIsNumber) {
+ // stack: ... object object number
+ // -> ... object number object number
+ cfw.add(ByteCode.DUP2_X1);
+ cfw.addALoad(contextLocal);
+ addOptRuntimeInvoke(
+ "getObjectIndex",
+ "(Ljava/lang/Object;D"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Ljava/lang/Object;");
+ } else {
+ // stack: ... object object indexObject
+ // -> ... object indexObject object indexObject
+ cfw.add(ByteCode.DUP_X1);
+ cfw.addALoad(contextLocal);
+ addScriptRuntimeInvoke(
+ "getObjectElem",
+ "(Ljava/lang/Object;"
+ +"Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Ljava/lang/Object;");
+ }
+ }
+ generateExpression(child, node);
+ cfw.addALoad(contextLocal);
+ if (indexIsNumber) {
+ addScriptRuntimeInvoke(
+ "setObjectIndex",
+ "(Ljava/lang/Object;"
+ +"D"
+ +"Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Ljava/lang/Object;");
+ } else {
+ addScriptRuntimeInvoke(
+ "setObjectElem",
+ "(Ljava/lang/Object;"
+ +"Ljava/lang/Object;"
+ +"Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Context;"
+ +")Ljava/lang/Object;");
+ }
+ }
+
+ private void visitDotQuery(Node node, Node child)
+ {
+ updateLineNumber(node);
+ generateExpression(child, node);
+ cfw.addALoad(variableObjectLocal);
+ addScriptRuntimeInvoke("enterDotQuery",
+ "(Ljava/lang/Object;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +")Lorg/mozilla/javascript/Scriptable;");
+ cfw.addAStore(variableObjectLocal);
+
+ // add push null/pop with label in between to simplify code for loop
+ // continue when it is necessary to pop the null result from
+ // updateDotQuery
+ cfw.add(ByteCode.ACONST_NULL);
+ int queryLoopStart = cfw.acquireLabel();
+ cfw.markLabel(queryLoopStart); // loop continue jumps here
+ cfw.add(ByteCode.POP);
+
+ generateExpression(child.getNext(), node);
+ addScriptRuntimeInvoke("toBoolean", "(Ljava/lang/Object;)Z");
+ cfw.addALoad(variableObjectLocal);
+ addScriptRuntimeInvoke("updateDotQuery",
+ "(Z"
+ +"Lorg/mozilla/javascript/Scriptable;"
+ +")Ljava/lang/Object;");
+ cfw.add(ByteCode.DUP);
+ cfw.add(ByteCode.IFNULL, queryLoopStart);
+ // stack: ... non_null_result_of_updateDotQuery
+ cfw.addALoad(variableObjectLocal);
+ addScriptRuntimeInvoke("leaveDotQuery",
+ "(Lorg/mozilla/javascript/Scriptable;"
+ +")Lorg/mozilla/javascript/Scriptable;");
+ cfw.addAStore(variableObjectLocal);
+ }
+
+ private int getLocalBlockRegister(Node node)
+ {
+ Node localBlock = (Node)node.getProp(Node.LOCAL_BLOCK_PROP);
+ int localSlot = localBlock.getExistingIntProp(Node.LOCAL_PROP);
+ return localSlot;
+ }
+
+ private void dcpLoadAsNumber(int dcp_register)
+ {
+ cfw.addALoad(dcp_register);
+ cfw.add(ByteCode.GETSTATIC,
+ "java/lang/Void",
+ "TYPE",
+ "Ljava/lang/Class;");
+ int isNumberLabel = cfw.acquireLabel();
+ cfw.add(ByteCode.IF_ACMPEQ, isNumberLabel);
+ short stack = cfw.getStackTop();
+ cfw.addALoad(dcp_register);
+ addObjectToDouble();
+ int beyond = cfw.acquireLabel();
+ cfw.add(ByteCode.GOTO, beyond);
+ cfw.markLabel(isNumberLabel, stack);
+ cfw.addDLoad(dcp_register + 1);
+ cfw.markLabel(beyond);
+ }
+
+ private void dcpLoadAsObject(int dcp_register)
+ {
+ cfw.addALoad(dcp_register);
+ cfw.add(ByteCode.GETSTATIC,
+ "java/lang/Void",
+ "TYPE",
+ "Ljava/lang/Class;");
+ int isNumberLabel = cfw.acquireLabel();
+ cfw.add(ByteCode.IF_ACMPEQ, isNumberLabel);
+ short stack = cfw.getStackTop();
+ cfw.addALoad(dcp_register);
+ int beyond = cfw.acquireLabel();
+ cfw.add(ByteCode.GOTO, beyond);
+ cfw.markLabel(isNumberLabel, stack);
+ cfw.addDLoad(dcp_register + 1);
+ addDoubleWrap();
+ cfw.markLabel(beyond);
+ }
+
+ private void addGoto(Node target, int jumpcode)
+ {
+ int targetLabel = getTargetLabel(target);
+ cfw.add(jumpcode, targetLabel);
+ }
+
+ private void addObjectToDouble()
+ {
+ addScriptRuntimeInvoke("toNumber", "(Ljava/lang/Object;)D");
+ }
+
+ private void addNewObjectArray(int size)
+ {
+ if (size == 0) {
+ if (itsZeroArgArray >= 0) {
+ cfw.addALoad(itsZeroArgArray);
+ } else {
+ cfw.add(ByteCode.GETSTATIC,
+ "org/mozilla/javascript/ScriptRuntime",
+ "emptyArgs", "[Ljava/lang/Object;");
+ }
+ } else {
+ cfw.addPush(size);
+ cfw.add(ByteCode.ANEWARRAY, "java/lang/Object");
+ }
+ }
+
+ private void addScriptRuntimeInvoke(String methodName,
+ String methodSignature)
+ {
+ cfw.addInvoke(ByteCode.INVOKESTATIC,
+ "org.mozilla.javascript.ScriptRuntime",
+ methodName,
+ methodSignature);
+ }
+
+ private void addOptRuntimeInvoke(String methodName,
+ String methodSignature)
+ {
+ cfw.addInvoke(ByteCode.INVOKESTATIC,
+ "org/mozilla/javascript/optimizer/OptRuntime",
+ methodName,
+ methodSignature);
+ }
+
+ private void addJumpedBooleanWrap(int trueLabel, int falseLabel)
+ {
+ cfw.markLabel(falseLabel);
+ int skip = cfw.acquireLabel();
+ cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean",
+ "FALSE", "Ljava/lang/Boolean;");
+ cfw.add(ByteCode.GOTO, skip);
+ cfw.markLabel(trueLabel);
+ cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean",
+ "TRUE", "Ljava/lang/Boolean;");
+ cfw.markLabel(skip);
+ cfw.adjustStackTop(-1); // only have 1 of true/false
+ }
+
+ private void addDoubleWrap()
+ {
+ addOptRuntimeInvoke("wrapDouble", "(D)Ljava/lang/Double;");
+ }
+
+ /**
+ * Const locals use an extra slot to hold the has-been-assigned-once flag at
+ * runtime.
+ * @param isConst true iff the variable is const
+ * @return the register for the word pair (double/long)
+ */
+ private short getNewWordPairLocal(boolean isConst)
+ {
+ short result = getConsecutiveSlots(2, isConst);
+ if (result < (MAX_LOCALS - 1)) {
+ locals[result] = 1;
+ locals[result + 1] = 1;
+ if (isConst)
+ locals[result + 2] = 1;
+ if (result == firstFreeLocal) {
+ for (int i = firstFreeLocal + 2; i < MAX_LOCALS; i++) {
+ if (locals[i] == 0) {
+ firstFreeLocal = (short) i;
+ if (localsMax < firstFreeLocal)
+ localsMax = firstFreeLocal;
+ return result;
+ }
+ }
+ }
+ else {
+ return result;
+ }
+ }
+ throw Context.reportRuntimeError("Program too complex " +
+ "(out of locals)");
+ }
+
+ private short getNewWordLocal(boolean isConst)
+ {
+ short result = getConsecutiveSlots(1, isConst);
+ if (result < (MAX_LOCALS - 1)) {
+ locals[result] = 1;
+ if (isConst)
+ locals[result + 1] = 1;
+ if (result == firstFreeLocal) {
+ for (int i = firstFreeLocal + 2; i < MAX_LOCALS; i++) {
+ if (locals[i] == 0) {
+ firstFreeLocal = (short) i;
+ if (localsMax < firstFreeLocal)
+ localsMax = firstFreeLocal;
+ return result;
+ }
+ }
+ }
+ else {
+ return result;
+ }
+ }
+ throw Context.reportRuntimeError("Program too complex " +
+ "(out of locals)");
+ }
+
+ private short getNewWordLocal()
+ {
+ short result = firstFreeLocal;
+ locals[result] = 1;
+ for (int i = firstFreeLocal + 1; i < MAX_LOCALS; i++) {
+ if (locals[i] == 0) {
+ firstFreeLocal = (short) i;
+ if (localsMax < firstFreeLocal)
+ localsMax = firstFreeLocal;
+ return result;
+ }
+ }
+ throw Context.reportRuntimeError("Program too complex " +
+ "(out of locals)");
+ }
+
+ private short getConsecutiveSlots(int count, boolean isConst) {
+ if (isConst)
+ count++;
+ short result = firstFreeLocal;
+ while (true) {
+ if (result >= (MAX_LOCALS - 1))
+ break;
+ int i;
+ for (i = 0; i < count; i++)
+ if (locals[result + i] != 0)
+ break;
+ if (i >= count)
+ break;
+ result++;
+ }
+ return result;
+ }
+
+ // This is a valid call only for a local that is allocated by default.
+ private void incReferenceWordLocal(short local)
+ {
+ locals[local]++;
+ }
+
+ // This is a valid call only for a local that is allocated by default.
+ private void decReferenceWordLocal(short local)
+ {
+ locals[local]--;
+ }
+
+ private void releaseWordLocal(short local)
+ {
+ if (local < firstFreeLocal)
+ firstFreeLocal = local;
+ locals[local] = 0;
+ }
+
+
+ static final int GENERATOR_TERMINATE = -1;
+ static final int GENERATOR_START = 0;
+ static final int GENERATOR_YIELD_START = 1;
+
+ ClassFileWriter cfw;
+ Codegen codegen;
+ CompilerEnvirons compilerEnv;
+ ScriptOrFnNode scriptOrFn;
+ public int scriptOrFnIndex;
+ private int savedCodeOffset;
+
+ private OptFunctionNode fnCurrent;
+ private boolean isTopLevel;
+
+ private static final int MAX_LOCALS = 256;
+ private int[] locals;
+ private short firstFreeLocal;
+ private short localsMax;
+
+ private int itsLineNumber;
+
+ private boolean hasVarsInRegs;
+ private short[] varRegisters;
+ private boolean inDirectCallFunction;
+ private boolean itsForcedObjectParameters;
+ private int enterAreaStartLabel;
+ private int epilogueLabel;
+
+ // special known locals. If you add a new local here, be sure
+ // to initialize it to -1 in initBodyGeneration
+ private short variableObjectLocal;
+ private short popvLocal;
+ private short contextLocal;
+ private short argsLocal;
+ private short operationLocal;
+ private short thisObjLocal;
+ private short funObjLocal;
+ private short itsZeroArgArray;
+ private short itsOneArgArray;
+ private short scriptRegexpLocal;
+ private short generatorStateLocal;
+
+ private boolean isGenerator;
+ private int generatorSwitch;
+ private int maxLocals = 0;
+ private int maxStack = 0;
+
+ private Hashtable finallys;
+
+ class FinallyReturnPoint {
+ public ArrayList jsrPoints = new ArrayList();
+ public int tableLabel = 0;
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/DataFlowBitSet.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/DataFlowBitSet.java
new file mode 100644
index 0000000..607e649
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/DataFlowBitSet.java
@@ -0,0 +1,134 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Roger Lawrence
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+
+package org.mozilla.javascript.optimizer;
+
+class DataFlowBitSet {
+
+ private int itsBits[];
+ private int itsSize;
+
+ DataFlowBitSet(int size)
+ {
+ itsSize = size;
+ itsBits = new int[(size + 31) >> 5];
+ }
+
+ void set(int n)
+ {
+ if (!(0 <= n && n < itsSize)) badIndex(n);
+ itsBits[n >> 5] |= 1 << (n & 31);
+ }
+
+ boolean test(int n)
+ {
+ if (!(0 <= n && n < itsSize)) badIndex(n);
+ return ((itsBits[n >> 5] & (1 << (n & 31))) != 0);
+ }
+
+ void not()
+ {
+ int bitsLength = itsBits.length;
+ for (int i = 0; i < bitsLength; i++)
+ itsBits[i] = ~itsBits[i];
+ }
+
+ void clear(int n)
+ {
+ if (!(0 <= n && n < itsSize)) badIndex(n);
+ itsBits[n >> 5] &= ~(1 << (n & 31));
+ }
+
+ void clear()
+ {
+ int bitsLength = itsBits.length;
+ for (int i = 0; i < bitsLength; i++)
+ itsBits[i] = 0;
+ }
+
+ void or(DataFlowBitSet b)
+ {
+ int bitsLength = itsBits.length;
+ for (int i = 0; i < bitsLength; i++)
+ itsBits[i] |= b.itsBits[i];
+ }
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("DataFlowBitSet, size = ");
+ sb.append(itsSize);
+ sb.append('\n');
+ int bitsLength = itsBits.length;
+ for (int i = 0; i < bitsLength; i++) {
+ sb.append(Integer.toHexString(itsBits[i]));
+ sb.append(' ');
+ }
+ return sb.toString();
+ }
+
+ boolean df(DataFlowBitSet in, DataFlowBitSet gen, DataFlowBitSet notKill)
+ {
+ int bitsLength = itsBits.length;
+ boolean changed = false;
+ for (int i = 0; i < bitsLength; i++) {
+ int oldBits = itsBits[i];
+ itsBits[i] = (in.itsBits[i] | gen.itsBits[i]) & notKill.itsBits[i];
+ changed |= (oldBits != itsBits[i]);
+ }
+ return changed;
+ }
+
+ boolean df2(DataFlowBitSet in, DataFlowBitSet gen, DataFlowBitSet notKill)
+ {
+ int bitsLength = itsBits.length;
+ boolean changed = false;
+ for (int i = 0; i < bitsLength; i++) {
+ int oldBits = itsBits[i];
+ itsBits[i] = (in.itsBits[i] & notKill.itsBits[i]) | gen.itsBits[i];
+ changed |= (oldBits != itsBits[i]);
+ }
+ return changed;
+ }
+
+ private void badIndex(int n)
+ {
+ throw new RuntimeException("DataFlowBitSet bad index " + n);
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/OptFunctionNode.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/OptFunctionNode.java
new file mode 100644
index 0000000..e043165
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/OptFunctionNode.java
@@ -0,0 +1,149 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Bob Jervis
+ * Roger Lawrence
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+package org.mozilla.javascript.optimizer;
+
+import org.mozilla.javascript.*;
+
+final class OptFunctionNode
+{
+ OptFunctionNode(FunctionNode fnode)
+ {
+ this.fnode = fnode;
+ fnode.setCompilerData(this);
+ }
+
+ static OptFunctionNode get(ScriptOrFnNode scriptOrFn, int i)
+ {
+ FunctionNode fnode = scriptOrFn.getFunctionNode(i);
+ return (OptFunctionNode)fnode.getCompilerData();
+ }
+
+ static OptFunctionNode get(ScriptOrFnNode scriptOrFn)
+ {
+ return (OptFunctionNode)scriptOrFn.getCompilerData();
+ }
+
+ boolean isTargetOfDirectCall()
+ {
+ return directTargetIndex >= 0;
+ }
+
+ int getDirectTargetIndex()
+ {
+ return directTargetIndex;
+ }
+
+ void setDirectTargetIndex(int directTargetIndex)
+ {
+ // One time action
+ if (directTargetIndex < 0 || this.directTargetIndex >= 0)
+ Kit.codeBug();
+ this.directTargetIndex = directTargetIndex;
+ }
+
+ void setParameterNumberContext(boolean b)
+ {
+ itsParameterNumberContext = b;
+ }
+
+ boolean getParameterNumberContext()
+ {
+ return itsParameterNumberContext;
+ }
+
+ int getVarCount()
+ {
+ return fnode.getParamAndVarCount();
+ }
+
+ boolean isParameter(int varIndex)
+ {
+ return varIndex < fnode.getParamCount();
+ }
+
+ boolean isNumberVar(int varIndex)
+ {
+ varIndex -= fnode.getParamCount();
+ if (varIndex >= 0 && numberVarFlags != null) {
+ return numberVarFlags[varIndex];
+ }
+ return false;
+ }
+
+ void setIsNumberVar(int varIndex)
+ {
+ varIndex -= fnode.getParamCount();
+ // Can only be used with non-parameters
+ if (varIndex < 0) Kit.codeBug();
+ if (numberVarFlags == null) {
+ int size = fnode.getParamAndVarCount() - fnode.getParamCount();
+ numberVarFlags = new boolean[size];
+ }
+ numberVarFlags[varIndex] = true;
+ }
+
+ int getVarIndex(Node n)
+ {
+ int index = n.getIntProp(Node.VARIABLE_PROP, -1);
+ if (index == -1) {
+ Node node;
+ int type = n.getType();
+ if (type == Token.GETVAR) {
+ node = n;
+ } else if (type == Token.SETVAR ||
+ type == Token.SETCONSTVAR) {
+ node = n.getFirstChild();
+ } else {
+ throw Kit.codeBug();
+ }
+ index = fnode.getIndexForNameNode(node);
+ if (index < 0) throw Kit.codeBug();
+ n.putIntProp(Node.VARIABLE_PROP, index);
+ }
+ return index;
+ }
+
+ FunctionNode fnode;
+ private boolean[] numberVarFlags;
+ private int directTargetIndex = -1;
+ private boolean itsParameterNumberContext;
+ boolean itsContainsCalls0;
+ boolean itsContainsCalls1;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/OptRuntime.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/OptRuntime.java
new file mode 100644
index 0000000..ba8ca03
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/OptRuntime.java
@@ -0,0 +1,311 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Roger Lawrence
+ * Hannes Wallnoefer
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+package org.mozilla.javascript.optimizer;
+
+import org.mozilla.javascript.*;
+
+public final class OptRuntime extends ScriptRuntime
+{
+
+ public static final Double zeroObj = new Double(0.0);
+ public static final Double oneObj = new Double(1.0);
+ public static final Double minusOneObj = new Double(-1.0);
+
+ /**
+ * Implement ....() call shrinking optimizer code.
+ */
+ public static Object call0(Callable fun, Scriptable thisObj,
+ Context cx, Scriptable scope)
+ {
+ return fun.call(cx, scope, thisObj, ScriptRuntime.emptyArgs);
+ }
+
+ /**
+ * Implement ....(arg) call shrinking optimizer code.
+ */
+ public static Object call1(Callable fun, Scriptable thisObj, Object arg0,
+ Context cx, Scriptable scope)
+ {
+ return fun.call(cx, scope, thisObj, new Object[] { arg0 } );
+ }
+
+ /**
+ * Implement ....(arg0, arg1) call shrinking optimizer code.
+ */
+ public static Object call2(Callable fun, Scriptable thisObj,
+ Object arg0, Object arg1,
+ Context cx, Scriptable scope)
+ {
+ return fun.call(cx, scope, thisObj, new Object[] { arg0, arg1 });
+ }
+
+ /**
+ * Implement ....(arg0, arg1, ...) call shrinking optimizer code.
+ */
+ public static Object callN(Callable fun, Scriptable thisObj,
+ Object[] args,
+ Context cx, Scriptable scope)
+ {
+ return fun.call(cx, scope, thisObj, args);
+ }
+
+ /**
+ * Implement name(args) call shrinking optimizer code.
+ */
+ public static Object callName(Object[] args, String name,
+ Context cx, Scriptable scope)
+ {
+ Callable f = getNameFunctionAndThis(name, cx, scope);
+ Scriptable thisObj = lastStoredScriptable(cx);
+ return f.call(cx, scope, thisObj, args);
+ }
+
+ /**
+ * Implement name() call shrinking optimizer code.
+ */
+ public static Object callName0(String name,
+ Context cx, Scriptable scope)
+ {
+ Callable f = getNameFunctionAndThis(name, cx, scope);
+ Scriptable thisObj = lastStoredScriptable(cx);
+ return f.call(cx, scope, thisObj, ScriptRuntime.emptyArgs);
+ }
+
+ /**
+ * Implement x.property() call shrinking optimizer code.
+ */
+ public static Object callProp0(Object value, String property,
+ Context cx, Scriptable scope)
+ {
+ Callable f = getPropFunctionAndThis(value, property, cx);
+ Scriptable thisObj = lastStoredScriptable(cx);
+ return f.call(cx, scope, thisObj, ScriptRuntime.emptyArgs);
+ }
+
+ public static Object add(Object val1, double val2)
+ {
+ if (val1 instanceof Scriptable)
+ val1 = ((Scriptable) val1).getDefaultValue(null);
+ if (!(val1 instanceof String))
+ return wrapDouble(toNumber(val1) + val2);
+ return ((String)val1).concat(toString(val2));
+ }
+
+ public static Object add(double val1, Object val2)
+ {
+ if (val2 instanceof Scriptable)
+ val2 = ((Scriptable) val2).getDefaultValue(null);
+ if (!(val2 instanceof String))
+ return wrapDouble(toNumber(val2) + val1);
+ return toString(val1).concat((String)val2);
+ }
+
+ public static Object elemIncrDecr(Object obj, double index,
+ Context cx, int incrDecrMask)
+ {
+ return ScriptRuntime.elemIncrDecr(obj, new Double(index), cx,
+ incrDecrMask);
+ }
+
+ public static Object[] padStart(Object[] currentArgs, int count) {
+ Object[] result = new Object[currentArgs.length + count];
+ System.arraycopy(currentArgs, 0, result, count, currentArgs.length);
+ return result;
+ }
+
+ public static void initFunction(NativeFunction fn, int functionType,
+ Scriptable scope, Context cx)
+ {
+ ScriptRuntime.initFunction(cx, scope, fn, functionType, false);
+ }
+
+ public static Object callSpecial(Context cx, Callable fun,
+ Scriptable thisObj, Object[] args,
+ Scriptable scope,
+ Scriptable callerThis, int callType,
+ String fileName, int lineNumber)
+ {
+ return ScriptRuntime.callSpecial(cx, fun, thisObj, args, scope,
+ callerThis, callType,
+ fileName, lineNumber);
+ }
+
+ public static Object newObjectSpecial(Context cx, Object fun,
+ Object[] args, Scriptable scope,
+ Scriptable callerThis, int callType)
+ {
+ return ScriptRuntime.newSpecial(cx, fun, args, scope, callType);
+ }
+
+ public static Double wrapDouble(double num)
+ {
+ if (num == 0.0) {
+ if (1 / num > 0) {
+ // +0.0
+ return zeroObj;
+ }
+ } else if (num == 1.0) {
+ return oneObj;
+ } else if (num == -1.0) {
+ return minusOneObj;
+ } else if (num != num) {
+ return NaNobj;
+ }
+ return new Double(num);
+ }
+
+ static String encodeIntArray(int[] array)
+ {
+ // XXX: this extremely inefficient for small integers
+ if (array == null) { return null; }
+ int n = array.length;
+ char[] buffer = new char[1 + n * 2];
+ buffer[0] = 1;
+ for (int i = 0; i != n; ++i) {
+ int value = array[i];
+ int shift = 1 + i * 2;
+ buffer[shift] = (char)(value >>> 16);
+ buffer[shift + 1] = (char)value;
+ }
+ return new String(buffer);
+ }
+
+ private static int[] decodeIntArray(String str, int arraySize)
+ {
+ // XXX: this extremely inefficient for small integers
+ if (arraySize == 0) {
+ if (str != null) throw new IllegalArgumentException();
+ return null;
+ }
+ if (str.length() != 1 + arraySize * 2 && str.charAt(0) != 1) {
+ throw new IllegalArgumentException();
+ }
+ int[] array = new int[arraySize];
+ for (int i = 0; i != arraySize; ++i) {
+ int shift = 1 + i * 2;
+ array[i] = (str.charAt(shift) << 16) | str.charAt(shift + 1);
+ }
+ return array;
+ }
+
+ public static Scriptable newArrayLiteral(Object[] objects,
+ String encodedInts,
+ int skipCount,
+ Context cx,
+ Scriptable scope)
+ {
+ int[] skipIndexces = decodeIntArray(encodedInts, skipCount);
+ return newArrayLiteral(objects, skipIndexces, cx, scope);
+ }
+
+ public static void main(final Script script, final String[] args)
+ {
+ Context.call(new ContextAction() {
+ public Object run(Context cx)
+ {
+ ScriptableObject global = getGlobal(cx);
+
+ // get the command line arguments and define "arguments"
+ // array in the top-level object
+ Object[] argsCopy = new Object[args.length];
+ System.arraycopy(args, 0, argsCopy, 0, args.length);
+ Scriptable argsObj = cx.newArray(global, argsCopy);
+ global.defineProperty("arguments", argsObj,
+ ScriptableObject.DONTENUM);
+ script.exec(cx, global);
+ return null;
+ }
+ });
+ }
+
+ public static void throwStopIteration(Object obj) {
+ throw new JavaScriptException(
+ NativeIterator.getStopIterationObject((Scriptable)obj), "", 0);
+ }
+
+ public static Scriptable createNativeGenerator(NativeFunction funObj,
+ Scriptable scope,
+ Scriptable thisObj,
+ int maxLocals,
+ int maxStack)
+ {
+ return new NativeGenerator(scope, funObj,
+ new GeneratorState(thisObj, maxLocals, maxStack));
+ }
+
+ public static Object[] getGeneratorStackState(Object obj) {
+ GeneratorState rgs = (GeneratorState) obj;
+ if (rgs.stackState == null)
+ rgs.stackState = new Object[rgs.maxStack];
+ return rgs.stackState;
+ }
+
+ public static Object[] getGeneratorLocalsState(Object obj) {
+ GeneratorState rgs = (GeneratorState) obj;
+ if (rgs.localsState == null)
+ rgs.localsState = new Object[rgs.maxLocals];
+ return rgs.localsState;
+ }
+
+ public static class GeneratorState {
+ static final String CLASS_NAME =
+ "org/mozilla/javascript/optimizer/OptRuntime$GeneratorState";
+
+ public int resumptionPoint;
+ static final String resumptionPoint_NAME = "resumptionPoint";
+ static final String resumptionPoint_TYPE = "I";
+
+ public Scriptable thisObj;
+ static final String thisObj_NAME = "thisObj";
+ static final String thisObj_TYPE =
+ "Lorg/mozilla/javascript/Scriptable;";
+
+ Object[] stackState;
+ Object[] localsState;
+ int maxLocals;
+ int maxStack;
+
+ GeneratorState(Scriptable thisObj, int maxLocals, int maxStack) {
+ this.thisObj = thisObj;
+ this.maxLocals = maxLocals;
+ this.maxStack = maxStack;
+ }
+ }
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/OptTransformer.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/OptTransformer.java
new file mode 100644
index 0000000..7cf679f
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/OptTransformer.java
@@ -0,0 +1,133 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Roger Lawrence
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+package org.mozilla.javascript.optimizer;
+
+import org.mozilla.javascript.*;
+import java.util.Hashtable;
+
+/**
+ * This class performs node transforms to prepare for optimization.
+ *
+ * @see NodeTransformer
+ * @author Norris Boyd
+ */
+
+class OptTransformer extends NodeTransformer {
+
+ OptTransformer(Hashtable possibleDirectCalls, ObjArray directCallTargets)
+ {
+ this.possibleDirectCalls = possibleDirectCalls;
+ this.directCallTargets = directCallTargets;
+ }
+
+ protected void visitNew(Node node, ScriptOrFnNode tree) {
+ detectDirectCall(node, tree);
+ super.visitNew(node, tree);
+ }
+
+ protected void visitCall(Node node, ScriptOrFnNode tree) {
+ detectDirectCall(node, tree);
+ super.visitCall(node, tree);
+ }
+
+ private void detectDirectCall(Node node, ScriptOrFnNode tree)
+ {
+ if (tree.getType() == Token.FUNCTION) {
+ Node left = node.getFirstChild();
+
+ // count the arguments
+ int argCount = 0;
+ Node arg = left.getNext();
+ while (arg != null) {
+ arg = arg.getNext();
+ argCount++;
+ }
+
+ if (argCount == 0) {
+ OptFunctionNode.get(tree).itsContainsCalls0 = true;
+ }
+
+ /*
+ * Optimize a call site by converting call("a", b, c) into :
+ *
+ * FunctionObjectFor"a" <-- instance variable init'd by constructor
+ *
+ * // this is a DIRECTCALL node
+ * fn = GetProp(tmp = GetBase("a"), "a");
+ * if (fn == FunctionObjectFor"a")
+ * fn.call(tmp, b, c)
+ * else
+ * ScriptRuntime.Call(fn, tmp, b, c)
+ */
+ if (possibleDirectCalls != null) {
+ String targetName = null;
+ if (left.getType() == Token.NAME) {
+ targetName = left.getString();
+ } else if (left.getType() == Token.GETPROP) {
+ targetName = left.getFirstChild().getNext().getString();
+ } else if (left.getType() == Token.GETPROPNOWARN) {
+ throw Kit.codeBug();
+ }
+ if (targetName != null) {
+ OptFunctionNode ofn;
+ ofn = (OptFunctionNode)possibleDirectCalls.get(targetName);
+ if (ofn != null
+ && argCount == ofn.fnode.getParamCount()
+ && !ofn.fnode.requiresActivation())
+ {
+ // Refuse to directCall any function with more
+ // than 32 parameters - prevent code explosion
+ // for wacky test cases
+ if (argCount <= 32) {
+ node.putProp(Node.DIRECTCALL_PROP, ofn);
+ if (!ofn.isTargetOfDirectCall()) {
+ int index = directCallTargets.size();
+ directCallTargets.add(ofn);
+ ofn.setDirectTargetIndex(index);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private Hashtable possibleDirectCalls;
+ private ObjArray directCallTargets;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Optimizer.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Optimizer.java
new file mode 100644
index 0000000..575c7e7
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Optimizer.java
@@ -0,0 +1,510 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Roger Lawrence
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+
+package org.mozilla.javascript.optimizer;
+
+import org.mozilla.javascript.*;
+
+class Optimizer
+{
+
+ static final int NoType = 0;
+ static final int NumberType = 1;
+ static final int AnyType = 3;
+
+ // It is assumed that (NumberType | AnyType) == AnyType
+
+ void optimize(ScriptOrFnNode scriptOrFn)
+ {
+ // run on one function at a time for now
+ int functionCount = scriptOrFn.getFunctionCount();
+ for (int i = 0; i != functionCount; ++i) {
+ OptFunctionNode f = OptFunctionNode.get(scriptOrFn, i);
+ optimizeFunction(f);
+ }
+ }
+
+ private void optimizeFunction(OptFunctionNode theFunction)
+ {
+ if (theFunction.fnode.requiresActivation()) return;
+
+ inDirectCallFunction = theFunction.isTargetOfDirectCall();
+ this.theFunction = theFunction;
+
+ ObjArray statementsArray = new ObjArray();
+ buildStatementList_r(theFunction.fnode, statementsArray);
+ Node[] theStatementNodes = new Node[statementsArray.size()];
+ statementsArray.toArray(theStatementNodes);
+
+ Block.runFlowAnalyzes(theFunction, theStatementNodes);
+
+ if (!theFunction.fnode.requiresActivation()) {
+ /*
+ * Now that we know which local vars are in fact always
+ * Numbers, we re-write the tree to take advantage of
+ * that. Any arithmetic or assignment op involving just
+ * Number typed vars is marked so that the codegen will
+ * generate non-object code.
+ */
+ parameterUsedInNumberContext = false;
+ for (int i = 0; i < theStatementNodes.length; i++) {
+ rewriteForNumberVariables(theStatementNodes[i]);
+ }
+ theFunction.setParameterNumberContext(parameterUsedInNumberContext);
+ }
+
+ }
+
+
+/*
+ Each directCall parameter is passed as a pair of values - an object
+ and a double. The value passed depends on the type of value available at
+ the call site. If a double is available, the object in java/lang/Void.TYPE
+ is passed as the object value, and if an object value is available, then
+ 0.0 is passed as the double value.
+
+ The receiving routine always tests the object value before proceeding.
+ If the parameter is being accessed in a 'Number Context' then the code
+ sequence is :
+ if ("parameter_objectValue" == java/lang/Void.TYPE)
+ ...fine..., use the parameter_doubleValue
+ else
+ toNumber(parameter_objectValue)
+
+ and if the parameter is being referenced in an Object context, the code is
+ if ("parameter_objectValue" == java/lang/Void.TYPE)
+ new Double(parameter_doubleValue)
+ else
+ ...fine..., use the parameter_objectValue
+
+ If the receiving code never uses the doubleValue, it is converted on
+ entry to a Double instead.
+*/
+
+
+/*
+ We're referencing a node in a Number context (i.e. we'd prefer it
+ was a double value). If the node is a parameter in a directCall
+ function, mark it as being referenced in this context.
+*/
+ private void markDCPNumberContext(Node n)
+ {
+ if (inDirectCallFunction && n.getType() == Token.GETVAR) {
+ int varIndex = theFunction.getVarIndex(n);
+ if (theFunction.isParameter(varIndex)) {
+ parameterUsedInNumberContext = true;
+ }
+ }
+ }
+
+ private boolean convertParameter(Node n)
+ {
+ if (inDirectCallFunction && n.getType() == Token.GETVAR) {
+ int varIndex = theFunction.getVarIndex(n);
+ if (theFunction.isParameter(varIndex)) {
+ n.removeProp(Node.ISNUMBER_PROP);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private int rewriteForNumberVariables(Node n)
+ {
+ switch (n.getType()) {
+ case Token.EXPR_VOID : {
+ Node child = n.getFirstChild();
+ int type = rewriteForNumberVariables(child);
+ if (type == NumberType)
+ n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
+ return NoType;
+ }
+ case Token.NUMBER :
+ n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
+ return NumberType;
+
+ case Token.GETVAR :
+ {
+ int varIndex = theFunction.getVarIndex(n);
+ if (inDirectCallFunction
+ && theFunction.isParameter(varIndex))
+ {
+ n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
+ return NumberType;
+ }
+ else if (theFunction.isNumberVar(varIndex)) {
+ n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
+ return NumberType;
+ }
+ return NoType;
+ }
+
+ case Token.INC :
+ case Token.DEC : {
+ Node child = n.getFirstChild();
+ // "child" will be GETVAR or GETPROP or GETELEM
+ if (child.getType() == Token.GETVAR) {
+ if (rewriteForNumberVariables(child) == NumberType) {
+ n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
+ markDCPNumberContext(child);
+ return NumberType;
+ }
+ return NoType;
+ }
+ else if (child.getType() == Token.GETELEM) {
+ return rewriteForNumberVariables(child);
+ }
+ return NoType;
+ }
+ case Token.SETVAR : {
+ Node lChild = n.getFirstChild();
+ Node rChild = lChild.getNext();
+ int rType = rewriteForNumberVariables(rChild);
+ int varIndex = theFunction.getVarIndex(n);
+ if (inDirectCallFunction
+ && theFunction.isParameter(varIndex))
+ {
+ if (rType == NumberType) {
+ if (!convertParameter(rChild)) {
+ n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
+ return NumberType;
+ }
+ markDCPNumberContext(rChild);
+ return NoType;
+ }
+ else
+ return rType;
+ }
+ else if (theFunction.isNumberVar(varIndex)) {
+ if (rType != NumberType) {
+ n.removeChild(rChild);
+ n.addChildToBack(
+ new Node(Token.TO_DOUBLE, rChild));
+ }
+ n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
+ markDCPNumberContext(rChild);
+ return NumberType;
+ }
+ else {
+ if (rType == NumberType) {
+ if (!convertParameter(rChild)) {
+ n.removeChild(rChild);
+ n.addChildToBack(
+ new Node(Token.TO_OBJECT, rChild));
+ }
+ }
+ return NoType;
+ }
+ }
+ case Token.LE :
+ case Token.LT :
+ case Token.GE :
+ case Token.GT : {
+ Node lChild = n.getFirstChild();
+ Node rChild = lChild.getNext();
+ int lType = rewriteForNumberVariables(lChild);
+ int rType = rewriteForNumberVariables(rChild);
+ markDCPNumberContext(lChild);
+ markDCPNumberContext(rChild);
+
+ if (convertParameter(lChild)) {
+ if (convertParameter(rChild)) {
+ return NoType;
+ } else if (rType == NumberType) {
+ n.putIntProp(Node.ISNUMBER_PROP, Node.RIGHT);
+ }
+ }
+ else if (convertParameter(rChild)) {
+ if (lType == NumberType) {
+ n.putIntProp(Node.ISNUMBER_PROP, Node.LEFT);
+ }
+ }
+ else {
+ if (lType == NumberType) {
+ if (rType == NumberType) {
+ n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
+ }
+ else {
+ n.putIntProp(Node.ISNUMBER_PROP, Node.LEFT);
+ }
+ }
+ else {
+ if (rType == NumberType) {
+ n.putIntProp(Node.ISNUMBER_PROP, Node.RIGHT);
+ }
+ }
+ }
+ // we actually build a boolean value
+ return NoType;
+ }
+
+ case Token.ADD : {
+ Node lChild = n.getFirstChild();
+ Node rChild = lChild.getNext();
+ int lType = rewriteForNumberVariables(lChild);
+ int rType = rewriteForNumberVariables(rChild);
+
+
+ if (convertParameter(lChild)) {
+ if (convertParameter(rChild)) {
+ return NoType;
+ }
+ else {
+ if (rType == NumberType) {
+ n.putIntProp(Node.ISNUMBER_PROP, Node.RIGHT);
+ }
+ }
+ }
+ else {
+ if (convertParameter(rChild)) {
+ if (lType == NumberType) {
+ n.putIntProp(Node.ISNUMBER_PROP, Node.LEFT);
+ }
+ }
+ else {
+ if (lType == NumberType) {
+ if (rType == NumberType) {
+ n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
+ return NumberType;
+ }
+ else {
+ n.putIntProp(Node.ISNUMBER_PROP, Node.LEFT);
+ }
+ }
+ else {
+ if (rType == NumberType) {
+ n.putIntProp(Node.ISNUMBER_PROP,
+ Node.RIGHT);
+ }
+ }
+ }
+ }
+ return NoType;
+ }
+
+ case Token.BITXOR :
+ case Token.BITOR :
+ case Token.BITAND :
+ case Token.RSH :
+ case Token.LSH :
+ case Token.SUB :
+ case Token.MUL :
+ case Token.DIV :
+ case Token.MOD : {
+ Node lChild = n.getFirstChild();
+ Node rChild = lChild.getNext();
+ int lType = rewriteForNumberVariables(lChild);
+ int rType = rewriteForNumberVariables(rChild);
+ markDCPNumberContext(lChild);
+ markDCPNumberContext(rChild);
+ if (lType == NumberType) {
+ if (rType == NumberType) {
+ n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
+ return NumberType;
+ }
+ else {
+ if (!convertParameter(rChild)) {
+ n.removeChild(rChild);
+ n.addChildToBack(
+ new Node(Token.TO_DOUBLE, rChild));
+ n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
+ }
+ return NumberType;
+ }
+ }
+ else {
+ if (rType == NumberType) {
+ if (!convertParameter(lChild)) {
+ n.removeChild(lChild);
+ n.addChildToFront(
+ new Node(Token.TO_DOUBLE, lChild));
+ n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
+ }
+ return NumberType;
+ }
+ else {
+ if (!convertParameter(lChild)) {
+ n.removeChild(lChild);
+ n.addChildToFront(
+ new Node(Token.TO_DOUBLE, lChild));
+ }
+ if (!convertParameter(rChild)) {
+ n.removeChild(rChild);
+ n.addChildToBack(
+ new Node(Token.TO_DOUBLE, rChild));
+ }
+ n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
+ return NumberType;
+ }
+ }
+ }
+ case Token.SETELEM :
+ case Token.SETELEM_OP : {
+ Node arrayBase = n.getFirstChild();
+ Node arrayIndex = arrayBase.getNext();
+ Node rValue = arrayIndex.getNext();
+ int baseType = rewriteForNumberVariables(arrayBase);
+ if (baseType == NumberType) {// can never happen ???
+ if (!convertParameter(arrayBase)) {
+ n.removeChild(arrayBase);
+ n.addChildToFront(
+ new Node(Token.TO_OBJECT, arrayBase));
+ }
+ }
+ int indexType = rewriteForNumberVariables(arrayIndex);
+ if (indexType == NumberType) {
+ // setting the ISNUMBER_PROP signals the codegen
+ // to use the OptRuntime.setObjectIndex that takes
+ // a double index
+ n.putIntProp(Node.ISNUMBER_PROP, Node.LEFT);
+ markDCPNumberContext(arrayIndex);
+ }
+ int rValueType = rewriteForNumberVariables(rValue);
+ if (rValueType == NumberType) {
+ if (!convertParameter(rValue)) {
+ n.removeChild(rValue);
+ n.addChildToBack(
+ new Node(Token.TO_OBJECT, rValue));
+ }
+ }
+ return NoType;
+ }
+ case Token.GETELEM : {
+ Node arrayBase = n.getFirstChild();
+ Node arrayIndex = arrayBase.getNext();
+ int baseType = rewriteForNumberVariables(arrayBase);
+ if (baseType == NumberType) {// can never happen ???
+ if (!convertParameter(arrayBase)) {
+ n.removeChild(arrayBase);
+ n.addChildToFront(
+ new Node(Token.TO_OBJECT, arrayBase));
+ }
+ }
+ int indexType = rewriteForNumberVariables(arrayIndex);
+ if (indexType == NumberType) {
+ if (!convertParameter(arrayIndex)) {
+ // setting the ISNUMBER_PROP signals the codegen
+ // to use the OptRuntime.getObjectIndex that takes
+ // a double index
+ n.putIntProp(Node.ISNUMBER_PROP, Node.RIGHT);
+ }
+ }
+ return NoType;
+ }
+ case Token.CALL :
+ {
+ Node child = n.getFirstChild(); // the function node
+ if (child.getType() == Token.GETELEM) {
+ // Optimization of x[0]() is not supported
+ // so bypass GETELEM optimization that
+ // rewriteForNumberVariables would trigger
+ rewriteAsObjectChildren(child, child.getFirstChild());
+ } else {
+ rewriteForNumberVariables(child);
+ }
+ child = child.getNext(); // the first arg
+
+ OptFunctionNode target
+ = (OptFunctionNode)n.getProp(Node.DIRECTCALL_PROP);
+ if (target != null) {
+/*
+ we leave each child as a Number if it can be. The codegen will
+ handle moving the pairs of parameters.
+*/
+ while (child != null) {
+ int type = rewriteForNumberVariables(child);
+ if (type == NumberType) {
+ markDCPNumberContext(child);
+ }
+ child = child.getNext();
+ }
+ } else {
+ rewriteAsObjectChildren(n, child);
+ }
+ return NoType;
+ }
+ default : {
+ rewriteAsObjectChildren(n, n.getFirstChild());
+ return NoType;
+ }
+ }
+ }
+
+ private void rewriteAsObjectChildren(Node n, Node child)
+ {
+ // Force optimized children to be objects
+ while (child != null) {
+ Node nextChild = child.getNext();
+ int type = rewriteForNumberVariables(child);
+ if (type == NumberType) {
+ if (!convertParameter(child)) {
+ n.removeChild(child);
+ Node nuChild = new Node(Token.TO_OBJECT, child);
+ if (nextChild == null)
+ n.addChildToBack(nuChild);
+ else
+ n.addChildBefore(nuChild, nextChild);
+ }
+ }
+ child = nextChild;
+ }
+ }
+
+ private static void buildStatementList_r(Node node, ObjArray statements)
+ {
+ int type = node.getType();
+ if (type == Token.BLOCK
+ || type == Token.LOCAL_BLOCK
+ || type == Token.LOOP
+ || type == Token.FUNCTION)
+ {
+ Node child = node.getFirstChild();
+ while (child != null) {
+ buildStatementList_r(child, statements);
+ child = child.getNext();
+ }
+ } else {
+ statements.add(node);
+ }
+ }
+
+ private boolean inDirectCallFunction;
+ OptFunctionNode theFunction;
+ private boolean parameterUsedInNumberContext;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/regexp/NativeRegExp.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/regexp/NativeRegExp.java
new file mode 100644
index 0000000..a893841
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/regexp/NativeRegExp.java
@@ -0,0 +1,2782 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ * Brendan Eich
+ * Matthias Radestock
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript.regexp;
+
+import java.io.Serializable;
+
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Function;
+import org.mozilla.javascript.IdFunctionObject;
+import org.mozilla.javascript.IdScriptableObject;
+import org.mozilla.javascript.Kit;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+import org.mozilla.javascript.Undefined;
+
+/**
+ * This class implements the RegExp native object.
+ *
+ * Revision History:
+ * Implementation in C by Brendan Eich
+ * Initial port to Java by Norris Boyd from jsregexp.c version 1.36
+ * Merged up to version 1.38, which included Unicode support.
+ * Merged bug fixes in version 1.39.
+ * Merged JSFUN13_BRANCH changes up to 1.32.2.13
+ *
+ * @author Brendan Eich
+ * @author Norris Boyd
+ */
+
+
+
+public class NativeRegExp extends IdScriptableObject implements Function
+{
+ static final long serialVersionUID = 4965263491464903264L;
+
+ private static final Object REGEXP_TAG = new Object();
+
+ public static final int JSREG_GLOB = 0x1; // 'g' flag: global
+ public static final int JSREG_FOLD = 0x2; // 'i' flag: fold
+ public static final int JSREG_MULTILINE = 0x4; // 'm' flag: multiline
+
+ //type of match to perform
+ public static final int TEST = 0;
+ public static final int MATCH = 1;
+ public static final int PREFIX = 2;
+
+ private static final boolean debug = false;
+
+ private static final byte REOP_EMPTY = 0; /* match rest of input against rest of r.e. */
+ private static final byte REOP_ALT = 1; /* alternative subexpressions in kid and next */
+ private static final byte REOP_BOL = 2; /* beginning of input (or line if multiline) */
+ private static final byte REOP_EOL = 3; /* end of input (or line if multiline) */
+ private static final byte REOP_WBDRY = 4; /* match "" at word boundary */
+ private static final byte REOP_WNONBDRY = 5; /* match "" at word non-boundary */
+ private static final byte REOP_QUANT = 6; /* quantified atom: atom{1,2} */
+ private static final byte REOP_STAR = 7; /* zero or more occurrences of kid */
+ private static final byte REOP_PLUS = 8; /* one or more occurrences of kid */
+ private static final byte REOP_OPT = 9; /* optional subexpression in kid */
+ private static final byte REOP_LPAREN = 10; /* left paren bytecode: kid is u.num'th sub-regexp */
+ private static final byte REOP_RPAREN = 11; /* right paren bytecode */
+ private static final byte REOP_DOT = 12; /* stands for any character */
+// private static final byte REOP_CCLASS = 13; /* character class: [a-f] */
+ private static final byte REOP_DIGIT = 14; /* match a digit char: [0-9] */
+ private static final byte REOP_NONDIGIT = 15; /* match a non-digit char: [^0-9] */
+ private static final byte REOP_ALNUM = 16; /* match an alphanumeric char: [0-9a-z_A-Z] */
+ private static final byte REOP_NONALNUM = 17; /* match a non-alphanumeric char: [^0-9a-z_A-Z] */
+ private static final byte REOP_SPACE = 18; /* match a whitespace char */
+ private static final byte REOP_NONSPACE = 19; /* match a non-whitespace char */
+ private static final byte REOP_BACKREF = 20; /* back-reference (e.g., \1) to a parenthetical */
+ private static final byte REOP_FLAT = 21; /* match a flat string */
+ private static final byte REOP_FLAT1 = 22; /* match a single char */
+ private static final byte REOP_JUMP = 23; /* for deoptimized closure loops */
+// private static final byte REOP_DOTSTAR = 24; /* optimize .* to use a single opcode */
+// private static final byte REOP_ANCHOR = 25; /* like .* but skips left context to unanchored r.e. */
+// private static final byte REOP_EOLONLY = 26; /* $ not preceded by any pattern */
+// private static final byte REOP_UCFLAT = 27; /* flat Unicode string; len immediate counts chars */
+ private static final byte REOP_UCFLAT1 = 28; /* single Unicode char */
+// private static final byte REOP_UCCLASS = 29; /* Unicode character class, vector of chars to match */
+// private static final byte REOP_NUCCLASS = 30; /* negated Unicode character class */
+// private static final byte REOP_BACKREFi = 31; /* case-independent REOP_BACKREF */
+ private static final byte REOP_FLATi = 32; /* case-independent REOP_FLAT */
+ private static final byte REOP_FLAT1i = 33; /* case-independent REOP_FLAT1 */
+// private static final byte REOP_UCFLATi = 34; /* case-independent REOP_UCFLAT */
+ private static final byte REOP_UCFLAT1i = 35; /* case-independent REOP_UCFLAT1 */
+// private static final byte REOP_ANCHOR1 = 36; /* first-char discriminating REOP_ANCHOR */
+// private static final byte REOP_NCCLASS = 37; /* negated 8-bit character class */
+// private static final byte REOP_DOTSTARMIN = 38; /* ungreedy version of REOP_DOTSTAR */
+// private static final byte REOP_LPARENNON = 39; /* non-capturing version of REOP_LPAREN */
+// private static final byte REOP_RPARENNON = 40; /* non-capturing version of REOP_RPAREN */
+ private static final byte REOP_ASSERT = 41; /* zero width positive lookahead assertion */
+ private static final byte REOP_ASSERT_NOT = 42; /* zero width negative lookahead assertion */
+ private static final byte REOP_ASSERTTEST = 43; /* sentinel at end of assertion child */
+ private static final byte REOP_ASSERTNOTTEST = 44; /* sentinel at end of !assertion child */
+ private static final byte REOP_MINIMALSTAR = 45; /* non-greedy version of * */
+ private static final byte REOP_MINIMALPLUS = 46; /* non-greedy version of + */
+ private static final byte REOP_MINIMALOPT = 47; /* non-greedy version of ? */
+ private static final byte REOP_MINIMALQUANT = 48; /* non-greedy version of {} */
+ private static final byte REOP_ENDCHILD = 49; /* sentinel at end of quantifier child */
+ private static final byte REOP_CLASS = 50; /* character class with index */
+ private static final byte REOP_REPEAT = 51; /* directs execution of greedy quantifier */
+ private static final byte REOP_MINIMALREPEAT = 52; /* directs execution of non-greedy quantifier */
+ private static final byte REOP_END = 53;
+
+
+
+ public static void init(Context cx, Scriptable scope, boolean sealed)
+ {
+
+ NativeRegExp proto = new NativeRegExp();
+ proto.re = (RECompiled)compileRE(cx, "", null, false);
+ proto.activatePrototypeMap(MAX_PROTOTYPE_ID);
+ proto.setParentScope(scope);
+ proto.setPrototype(getObjectPrototype(scope));
+
+ NativeRegExpCtor ctor = new NativeRegExpCtor();
+ // Bug #324006: ECMA-262 15.10.6.1 says "The initial value of
+ // RegExp.prototype.constructor is the builtin RegExp constructor."
+ proto.put("constructor", proto, ctor);
+
+ ScriptRuntime.setFunctionProtoAndParent(ctor, scope);
+
+ ctor.setImmunePrototypeProperty(proto);
+
+ if (sealed) {
+ proto.sealObject();
+ ctor.sealObject();
+ }
+
+ defineProperty(scope, "RegExp", ctor, ScriptableObject.DONTENUM);
+ }
+
+ NativeRegExp(Scriptable scope, Object regexpCompiled)
+ {
+ this.re = (RECompiled)regexpCompiled;
+ this.lastIndex = 0;
+ ScriptRuntime.setObjectProtoAndParent(this, scope);
+ }
+
+ public String getClassName()
+ {
+ return "RegExp";
+ }
+
+ public Object call(Context cx, Scriptable scope, Scriptable thisObj,
+ Object[] args)
+ {
+ return execSub(cx, scope, args, MATCH);
+ }
+
+ public Scriptable construct(Context cx, Scriptable scope, Object[] args)
+ {
+ return (Scriptable)execSub(cx, scope, args, MATCH);
+ }
+
+ Scriptable compile(Context cx, Scriptable scope, Object[] args)
+ {
+ if (args.length > 0 && args[0] instanceof NativeRegExp) {
+ if (args.length > 1 && args[1] != Undefined.instance) {
+ // report error
+ throw ScriptRuntime.typeError0("msg.bad.regexp.compile");
+ }
+ NativeRegExp thatObj = (NativeRegExp) args[0];
+ this.re = thatObj.re;
+ this.lastIndex = thatObj.lastIndex;
+ return this;
+ }
+ String s = args.length == 0 ? "" : ScriptRuntime.toString(args[0]);
+ String global = args.length > 1 && args[1] != Undefined.instance
+ ? ScriptRuntime.toString(args[1])
+ : null;
+ this.re = (RECompiled)compileRE(cx, s, global, false);
+ this.lastIndex = 0;
+ return this;
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ buf.append('/');
+ if (re.source.length != 0) {
+ buf.append(re.source);
+ } else {
+ // See bugzilla 226045
+ buf.append("(?:)");
+ }
+ buf.append('/');
+ if ((re.flags & JSREG_GLOB) != 0)
+ buf.append('g');
+ if ((re.flags & JSREG_FOLD) != 0)
+ buf.append('i');
+ if ((re.flags & JSREG_MULTILINE) != 0)
+ buf.append('m');
+ return buf.toString();
+ }
+
+ NativeRegExp() { }
+
+ private static RegExpImpl getImpl(Context cx)
+ {
+ return (RegExpImpl) ScriptRuntime.getRegExpProxy(cx);
+ }
+
+ private Object execSub(Context cx, Scriptable scopeObj,
+ Object[] args, int matchType)
+ {
+ RegExpImpl reImpl = getImpl(cx);
+ String str;
+ if (args.length == 0) {
+ str = reImpl.input;
+ if (str == null) {
+ reportError("msg.no.re.input.for", toString());
+ }
+ } else {
+ str = ScriptRuntime.toString(args[0]);
+ }
+ double d = ((re.flags & JSREG_GLOB) != 0) ? lastIndex : 0;
+
+ Object rval;
+ if (d < 0 || str.length() < d) {
+ lastIndex = 0;
+ rval = null;
+ }
+ else {
+ int indexp[] = { (int)d };
+ rval = executeRegExp(cx, scopeObj, reImpl, str, indexp, matchType);
+ if ((re.flags & JSREG_GLOB) != 0) {
+ lastIndex = (rval == null || rval == Undefined.instance)
+ ? 0 : indexp[0];
+ }
+ }
+ return rval;
+ }
+
+ static Object compileRE(Context cx, String str, String global, boolean flat)
+ {
+ RECompiled regexp = new RECompiled();
+ regexp.source = str.toCharArray();
+ int length = str.length();
+
+ int flags = 0;
+ if (global != null) {
+ for (int i = 0; i < global.length(); i++) {
+ char c = global.charAt(i);
+ if (c == 'g') {
+ flags |= JSREG_GLOB;
+ } else if (c == 'i') {
+ flags |= JSREG_FOLD;
+ } else if (c == 'm') {
+ flags |= JSREG_MULTILINE;
+ } else {
+ reportError("msg.invalid.re.flag", String.valueOf(c));
+ }
+ }
+ }
+ regexp.flags = flags;
+
+ CompilerState state = new CompilerState(cx, regexp.source, length, flags);
+ if (flat && length > 0) {
+if (debug) {
+System.out.println("flat = \"" + str + "\"");
+}
+ state.result = new RENode(REOP_FLAT);
+ state.result.chr = state.cpbegin[0];
+ state.result.length = length;
+ state.result.flatIndex = 0;
+ state.progLength += 5;
+ }
+ else
+ if (!parseDisjunction(state))
+ return null;
+
+ regexp.program = new byte[state.progLength + 1];
+ if (state.classCount != 0) {
+ regexp.classList = new RECharSet[state.classCount];
+ regexp.classCount = state.classCount;
+ }
+ int endPC = emitREBytecode(state, regexp, 0, state.result);
+ regexp.program[endPC++] = REOP_END;
+
+if (debug) {
+System.out.println("Prog. length = " + endPC);
+for (int i = 0; i < endPC; i++) {
+ System.out.print(regexp.program[i]);
+ if (i < (endPC - 1)) System.out.print(", ");
+}
+System.out.println();
+}
+ regexp.parenCount = state.parenCount;
+
+ // If re starts with literal, init anchorCh accordingly
+ switch (regexp.program[0]) {
+ case REOP_UCFLAT1:
+ case REOP_UCFLAT1i:
+ regexp.anchorCh = (char)getIndex(regexp.program, 1);
+ break;
+ case REOP_FLAT1:
+ case REOP_FLAT1i:
+ regexp.anchorCh = (char)(regexp.program[1] & 0xFF);
+ break;
+ case REOP_FLAT:
+ case REOP_FLATi:
+ int k = getIndex(regexp.program, 1);
+ regexp.anchorCh = regexp.source[k];
+ break;
+ }
+
+if (debug) {
+if (regexp.anchorCh >= 0) {
+ System.out.println("Anchor ch = '" + (char)regexp.anchorCh + "'");
+}
+}
+ return regexp;
+ }
+
+ static boolean isDigit(char c)
+ {
+ return '0' <= c && c <= '9';
+ }
+
+ private static boolean isWord(char c)
+ {
+ return Character.isLetter(c) || isDigit(c) || c == '_';
+ }
+
+ private static boolean isLineTerm(char c)
+ {
+ return ScriptRuntime.isJSLineTerminator(c);
+ }
+
+ private static boolean isREWhiteSpace(int c)
+ {
+ return (c == '\u0020' || c == '\u0009'
+ || c == '\n' || c == '\r'
+ || c == 0x2028 || c == 0x2029
+ || c == '\u000C' || c == '\u000B'
+ || c == '\u00A0'
+ || Character.getType((char)c) == Character.SPACE_SEPARATOR);
+ }
+
+ /*
+ *
+ * 1. If IgnoreCase is false, return ch.
+ * 2. Let u be ch converted to upper case as if by calling
+ * String.prototype.toUpperCase on the one-character string ch.
+ * 3. If u does not consist of a single character, return ch.
+ * 4. Let cu be u's character.
+ * 5. If ch's code point value is greater than or equal to decimal 128 and cu's
+ * code point value is less than decimal 128, then return ch.
+ * 6. Return cu.
+ */
+ private static char upcase(char ch)
+ {
+ if (ch < 128) {
+ if ('a' <= ch && ch <= 'z') {
+ return (char)(ch + ('A' - 'a'));
+ }
+ return ch;
+ }
+ char cu = Character.toUpperCase(ch);
+ if ((ch >= 128) && (cu < 128)) return ch;
+ return cu;
+ }
+
+ private static char downcase(char ch)
+ {
+ if (ch < 128) {
+ if ('A' <= ch && ch <= 'Z') {
+ return (char)(ch + ('a' - 'A'));
+ }
+ return ch;
+ }
+ char cl = Character.toLowerCase(ch);
+ if ((ch >= 128) && (cl < 128)) return ch;
+ return cl;
+ }
+
+/*
+ * Validates and converts hex ascii value.
+ */
+ private static int toASCIIHexDigit(int c)
+ {
+ if (c < '0')
+ return -1;
+ if (c <= '9') {
+ return c - '0';
+ }
+ c |= 0x20;
+ if ('a' <= c && c <= 'f') {
+ return c - 'a' + 10;
+ }
+ return -1;
+ }
+
+/*
+ * Top-down regular expression grammar, based closely on Perl4.
+ *
+ * regexp: altern A regular expression is one or more
+ * altern '|' regexp alternatives separated by vertical bar.
+ */
+ private static boolean parseDisjunction(CompilerState state)
+ {
+ if (!parseAlternative(state))
+ return false;
+ char[] source = state.cpbegin;
+ int index = state.cp;
+ if (index != source.length && source[index] == '|') {
+ RENode altResult;
+ ++state.cp;
+ altResult = new RENode(REOP_ALT);
+ altResult.kid = state.result;
+ if (!parseDisjunction(state))
+ return false;
+ altResult.kid2 = state.result;
+ state.result = altResult;
+ /* ALT, <next>, ..., JUMP, <end> ... JUMP <end> */
+ state.progLength += 9;
+ }
+ return true;
+ }
+
+/*
+ * altern: item An alternative is one or more items,
+ * item altern concatenated together.
+ */
+ private static boolean parseAlternative(CompilerState state)
+ {
+ RENode headTerm = null;
+ RENode tailTerm = null;
+ char[] source = state.cpbegin;
+ while (true) {
+ if (state.cp == state.cpend || source[state.cp] == '|'
+ || (state.parenNesting != 0 && source[state.cp] == ')'))
+ {
+ if (headTerm == null) {
+ state.result = new RENode(REOP_EMPTY);
+ }
+ else
+ state.result = headTerm;
+ return true;
+ }
+ if (!parseTerm(state))
+ return false;
+ if (headTerm == null)
+ headTerm = state.result;
+ else {
+ if (tailTerm == null) {
+ headTerm.next = state.result;
+ tailTerm = state.result;
+ while (tailTerm.next != null) tailTerm = tailTerm.next;
+ }
+ else {
+ tailTerm.next = state.result;
+ tailTerm = tailTerm.next;
+ while (tailTerm.next != null) tailTerm = tailTerm.next;
+ }
+ }
+ }
+ }
+
+ /* calculate the total size of the bitmap required for a class expression */
+ private static boolean
+ calculateBitmapSize(CompilerState state, RENode target, char[] src,
+ int index, int end)
+ {
+ char rangeStart = 0;
+ char c;
+ int n;
+ int nDigits;
+ int i;
+ int max = 0;
+ boolean inRange = false;
+
+ target.bmsize = 0;
+
+ if (index == end)
+ return true;
+
+ if (src[index] == '^')
+ ++index;
+
+ while (index != end) {
+ int localMax = 0;
+ nDigits = 2;
+ switch (src[index]) {
+ case '\\':
+ ++index;
+ c = src[index++];
+ switch (c) {
+ case 'b':
+ localMax = 0x8;
+ break;
+ case 'f':
+ localMax = 0xC;
+ break;
+ case 'n':
+ localMax = 0xA;
+ break;
+ case 'r':
+ localMax = 0xD;
+ break;
+ case 't':
+ localMax = 0x9;
+ break;
+ case 'v':
+ localMax = 0xB;
+ break;
+ case 'c':
+ if (((index + 1) < end) && Character.isLetter(src[index + 1]))
+ localMax = (char)(src[index++] & 0x1F);
+ else
+ localMax = '\\';
+ break;
+ case 'u':
+ nDigits += 2;
+ // fall thru...
+ case 'x':
+ n = 0;
+ for (i = 0; (i < nDigits) && (index < end); i++) {
+ c = src[index++];
+ n = Kit.xDigitToInt(c, n);
+ if (n < 0) {
+ // Back off to accepting the original
+ // '\' as a literal
+ index -= (i + 1);
+ n = '\\';
+ break;
+ }
+ }
+ localMax = n;
+ break;
+ case 'd':
+ if (inRange) {
+ reportError("msg.bad.range", "");
+ return false;
+ }
+ localMax = '9';
+ break;
+ case 'D':
+ case 's':
+ case 'S':
+ case 'w':
+ case 'W':
+ if (inRange) {
+ reportError("msg.bad.range", "");
+ return false;
+ }
+ target.bmsize = 65535;
+ return true;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ /*
+ * This is a non-ECMA extension - decimal escapes (in this
+ * case, octal!) are supposed to be an error inside class
+ * ranges, but supported here for backwards compatibility.
+ *
+ */
+ n = (c - '0');
+ c = src[index];
+ if ('0' <= c && c <= '7') {
+ index++;
+ n = 8 * n + (c - '0');
+ c = src[index];
+ if ('0' <= c && c <= '7') {
+ index++;
+ i = 8 * n + (c - '0');
+ if (i <= 0377)
+ n = i;
+ else
+ index--;
+ }
+ }
+ localMax = n;
+ break;
+
+ default:
+ localMax = c;
+ break;
+ }
+ break;
+ default:
+ localMax = src[index++];
+ break;
+ }
+ if (inRange) {
+ if (rangeStart > localMax) {
+ reportError("msg.bad.range", "");
+ return false;
+ }
+ inRange = false;
+ }
+ else {
+ if (index < (end - 1)) {
+ if (src[index] == '-') {
+ ++index;
+ inRange = true;
+ rangeStart = (char)localMax;
+ continue;
+ }
+ }
+ }
+ if ((state.flags & JSREG_FOLD) != 0){
+ char cu = upcase((char)localMax);
+ char cd = downcase((char)localMax);
+ localMax = (cu >= cd) ? cu : cd;
+ }
+ if (localMax > max)
+ max = localMax;
+ }
+ target.bmsize = max;
+ return true;
+ }
+
+ /*
+ * item: assertion An item is either an assertion or
+ * quantatom a quantified atom.
+ *
+ * assertion: '^' Assertions match beginning of string
+ * (or line if the class static property
+ * RegExp.multiline is true).
+ * '$' End of string (or line if the class
+ * static property RegExp.multiline is
+ * true).
+ * '\b' Word boundary (between \w and \W).
+ * '\B' Word non-boundary.
+ *
+ * quantatom: atom An unquantified atom.
+ * quantatom '{' n ',' m '}'
+ * Atom must occur between n and m times.
+ * quantatom '{' n ',' '}' Atom must occur at least n times.
+ * quantatom '{' n '}' Atom must occur exactly n times.
+ * quantatom '*' Zero or more times (same as {0,}).
+ * quantatom '+' One or more times (same as {1,}).
+ * quantatom '?' Zero or one time (same as {0,1}).
+ *
+ * any of which can be optionally followed by '?' for ungreedy
+ *
+ * atom: '(' regexp ')' A parenthesized regexp (what matched
+ * can be addressed using a backreference,
+ * see '\' n below).
+ * '.' Matches any char except '\n'.
+ * '[' classlist ']' A character class.
+ * '[' '^' classlist ']' A negated character class.
+ * '\f' Form Feed.
+ * '\n' Newline (Line Feed).
+ * '\r' Carriage Return.
+ * '\t' Horizontal Tab.
+ * '\v' Vertical Tab.
+ * '\d' A digit (same as [0-9]).
+ * '\D' A non-digit.
+ * '\w' A word character, [0-9a-z_A-Z].
+ * '\W' A non-word character.
+ * '\s' A whitespace character, [ \b\f\n\r\t\v].
+ * '\S' A non-whitespace character.
+ * '\' n A backreference to the nth (n decimal
+ * and positive) parenthesized expression.
+ * '\' octal An octal escape sequence (octal must be
+ * two or three digits long, unless it is
+ * 0 for the null character).
+ * '\x' hex A hex escape (hex must be two digits).
+ * '\c' ctrl A control character, ctrl is a letter.
+ * '\' literalatomchar Any character except one of the above
+ * that follow '\' in an atom.
+ * otheratomchar Any character not first among the other
+ * atom right-hand sides.
+ */
+
+ private static void doFlat(CompilerState state, char c)
+ {
+ state.result = new RENode(REOP_FLAT);
+ state.result.chr = c;
+ state.result.length = 1;
+ state.result.flatIndex = -1;
+ state.progLength += 3;
+ }
+
+ private static int
+ getDecimalValue(char c, CompilerState state, int maxValue,
+ String overflowMessageId)
+ {
+ boolean overflow = false;
+ int start = state.cp;
+ char[] src = state.cpbegin;
+ int value = c - '0';
+ for (; state.cp != state.cpend; ++state.cp) {
+ c = src[state.cp];
+ if (!isDigit(c)) {
+ break;
+ }
+ if (!overflow) {
+ int digit = c - '0';
+ if (value < (maxValue - digit) / 10) {
+ value = value * 10 + digit;
+ } else {
+ overflow = true;
+ value = maxValue;
+ }
+ }
+ }
+ if (overflow) {
+ reportError(overflowMessageId,
+ String.valueOf(src, start, state.cp - start));
+ }
+ return value;
+ }
+
+ private static boolean
+ parseTerm(CompilerState state)
+ {
+ char[] src = state.cpbegin;
+ char c = src[state.cp++];
+ int nDigits = 2;
+ int parenBaseCount = state.parenCount;
+ int num, tmp;
+ RENode term;
+ int termStart;
+
+ switch (c) {
+ /* assertions and atoms */
+ case '^':
+ state.result = new RENode(REOP_BOL);
+ state.progLength++;
+ return true;
+ case '$':
+ state.result = new RENode(REOP_EOL);
+ state.progLength++;
+ return true;
+ case '\\':
+ if (state.cp < state.cpend) {
+ c = src[state.cp++];
+ switch (c) {
+ /* assertion escapes */
+ case 'b' :
+ state.result = new RENode(REOP_WBDRY);
+ state.progLength++;
+ return true;
+ case 'B':
+ state.result = new RENode(REOP_WNONBDRY);
+ state.progLength++;
+ return true;
+ /* Decimal escape */
+ case '0':
+/*
+ * Under 'strict' ECMA 3, we interpret \0 as NUL and don't accept octal.
+ * However, (XXX and since Rhino doesn't have a 'strict' mode) we'll just
+ * behave the old way for compatibility reasons.
+ * (see http://bugzilla.mozilla.org/show_bug.cgi?id=141078)
+ *
+ */
+ reportWarning(state.cx, "msg.bad.backref", "");
+ /* octal escape */
+ num = 0;
+ while (state.cp < state.cpend) {
+ c = src[state.cp];
+ if ((c >= '0') && (c <= '7')) {
+ state.cp++;
+ tmp = 8 * num + (c - '0');
+ if (tmp > 0377)
+ break;
+ num = tmp;
+ }
+ else
+ break;
+ }
+ c = (char)(num);
+ doFlat(state, c);
+ break;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ termStart = state.cp - 1;
+ num = getDecimalValue(c, state, 0xFFFF,
+ "msg.overlarge.backref");
+ if (num > state.parenCount)
+ reportWarning(state.cx, "msg.bad.backref", "");
+ /*
+ * n > 9 or > count of parentheses,
+ * then treat as octal instead.
+ */
+ if ((num > 9) && (num > state.parenCount)) {
+ state.cp = termStart;
+ num = 0;
+ while (state.cp < state.cpend) {
+ c = src[state.cp];
+ if ((c >= '0') && (c <= '7')) {
+ state.cp++;
+ tmp = 8 * num + (c - '0');
+ if (tmp > 0377)
+ break;
+ num = tmp;
+ }
+ else
+ break;
+ }
+ c = (char)(num);
+ doFlat(state, c);
+ break;
+ }
+ /* otherwise, it's a back-reference */
+ state.result = new RENode(REOP_BACKREF);
+ state.result.parenIndex = num - 1;
+ state.progLength += 3;
+ break;
+ /* Control escape */
+ case 'f':
+ c = 0xC;
+ doFlat(state, c);
+ break;
+ case 'n':
+ c = 0xA;
+ doFlat(state, c);
+ break;
+ case 'r':
+ c = 0xD;
+ doFlat(state, c);
+ break;
+ case 't':
+ c = 0x9;
+ doFlat(state, c);
+ break;
+ case 'v':
+ c = 0xB;
+ doFlat(state, c);
+ break;
+ /* Control letter */
+ case 'c':
+ if (((state.cp + 1) < state.cpend) &&
+ Character.isLetter(src[state.cp + 1]))
+ c = (char)(src[state.cp++] & 0x1F);
+ else {
+ /* back off to accepting the original '\' as a literal */
+ --state.cp;
+ c = '\\';
+ }
+ doFlat(state, c);
+ break;
+ /* UnicodeEscapeSequence */
+ case 'u':
+ nDigits += 2;
+ // fall thru...
+ /* HexEscapeSequence */
+ case 'x':
+ {
+ int n = 0;
+ int i;
+ for (i = 0; (i < nDigits)
+ && (state.cp < state.cpend); i++) {
+ c = src[state.cp++];
+ n = Kit.xDigitToInt(c, n);
+ if (n < 0) {
+ // Back off to accepting the original
+ // 'u' or 'x' as a literal
+ state.cp -= (i + 2);
+ n = src[state.cp++];
+ break;
+ }
+ }
+ c = (char)(n);
+ }
+ doFlat(state, c);
+ break;
+ /* Character class escapes */
+ case 'd':
+ state.result = new RENode(REOP_DIGIT);
+ state.progLength++;
+ break;
+ case 'D':
+ state.result = new RENode(REOP_NONDIGIT);
+ state.progLength++;
+ break;
+ case 's':
+ state.result = new RENode(REOP_SPACE);
+ state.progLength++;
+ break;
+ case 'S':
+ state.result = new RENode(REOP_NONSPACE);
+ state.progLength++;
+ break;
+ case 'w':
+ state.result = new RENode(REOP_ALNUM);
+ state.progLength++;
+ break;
+ case 'W':
+ state.result = new RENode(REOP_NONALNUM);
+ state.progLength++;
+ break;
+ /* IdentityEscape */
+ default:
+ state.result = new RENode(REOP_FLAT);
+ state.result.chr = c;
+ state.result.length = 1;
+ state.result.flatIndex = state.cp - 1;
+ state.progLength += 3;
+ break;
+ }
+ break;
+ }
+ else {
+ /* a trailing '\' is an error */
+ reportError("msg.trail.backslash", "");
+ return false;
+ }
+ case '(': {
+ RENode result = null;
+ termStart = state.cp;
+ if (state.cp + 1 < state.cpend && src[state.cp] == '?'
+ && ((c = src[state.cp + 1]) == '=' || c == '!' || c == ':'))
+ {
+ state.cp += 2;
+ if (c == '=') {
+ result = new RENode(REOP_ASSERT);
+ /* ASSERT, <next>, ... ASSERTTEST */
+ state.progLength += 4;
+ } else if (c == '!') {
+ result = new RENode(REOP_ASSERT_NOT);
+ /* ASSERTNOT, <next>, ... ASSERTNOTTEST */
+ state.progLength += 4;
+ }
+ } else {
+ result = new RENode(REOP_LPAREN);
+ /* LPAREN, <index>, ... RPAREN, <index> */
+ state.progLength += 6;
+ result.parenIndex = state.parenCount++;
+ }
+ ++state.parenNesting;
+ if (!parseDisjunction(state))
+ return false;
+ if (state.cp == state.cpend || src[state.cp] != ')') {
+ reportError("msg.unterm.paren", "in regular expression"/*APPJET*/);
+ return false;
+ }
+ ++state.cp;
+ --state.parenNesting;
+ if (result != null) {
+ result.kid = state.result;
+ state.result = result;
+ }
+ break;
+ }
+ case ')':
+ reportError("msg.re.unmatched.right.paren", "");
+ return false;
+ case '[':
+ state.result = new RENode(REOP_CLASS);
+ termStart = state.cp;
+ state.result.startIndex = termStart;
+ while (true) {
+ if (state.cp == state.cpend) {
+ reportError("msg.unterm.class", "");
+ return false;
+ }
+ if (src[state.cp] == '\\')
+ state.cp++;
+ else {
+ if (src[state.cp] == ']') {
+ state.result.kidlen = state.cp - termStart;
+ break;
+ }
+ }
+ state.cp++;
+ }
+ state.result.index = state.classCount++;
+ /*
+ * Call calculateBitmapSize now as we want any errors it finds
+ * to be reported during the parse phase, not at execution.
+ */
+ if (!calculateBitmapSize(state, state.result, src, termStart, state.cp++))
+ return false;
+ state.progLength += 3; /* CLASS, <index> */
+ break;
+
+ case '.':
+ state.result = new RENode(REOP_DOT);
+ state.progLength++;
+ break;
+ case '*':
+ case '+':
+ case '?':
+ reportError("msg.bad.quant", String.valueOf(src[state.cp - 1]));
+ return false;
+ default:
+ state.result = new RENode(REOP_FLAT);
+ state.result.chr = c;
+ state.result.length = 1;
+ state.result.flatIndex = state.cp - 1;
+ state.progLength += 3;
+ break;
+ }
+
+ term = state.result;
+ if (state.cp == state.cpend) {
+ return true;
+ }
+ boolean hasQ = false;
+ switch (src[state.cp]) {
+ case '+':
+ state.result = new RENode(REOP_QUANT);
+ state.result.min = 1;
+ state.result.max = -1;
+ /* <PLUS>, <parencount>, <parenindex>, <next> ... <ENDCHILD> */
+ state.progLength += 8;
+ hasQ = true;
+ break;
+ case '*':
+ state.result = new RENode(REOP_QUANT);
+ state.result.min = 0;
+ state.result.max = -1;
+ /* <STAR>, <parencount>, <parenindex>, <next> ... <ENDCHILD> */
+ state.progLength += 8;
+ hasQ = true;
+ break;
+ case '?':
+ state.result = new RENode(REOP_QUANT);
+ state.result.min = 0;
+ state.result.max = 1;
+ /* <OPT>, <parencount>, <parenindex>, <next> ... <ENDCHILD> */
+ state.progLength += 8;
+ hasQ = true;
+ break;
+ case '{': /* balance '}' */
+ {
+ int min = 0;
+ int max = -1;
+ int leftCurl = state.cp;
+
+ /* For Perl etc. compatibility, if quntifier does not match
+ * \{\d+(,\d*)?\} exactly back off from it
+ * being a quantifier, and chew it up as a literal
+ * atom next time instead.
+ */
+
+ c = src[++state.cp];
+ if (isDigit(c)) {
+ ++state.cp;
+ min = getDecimalValue(c, state, 0xFFFF,
+ "msg.overlarge.min");
+ c = src[state.cp];
+ if (c == ',') {
+ c = src[++state.cp];
+ if (isDigit(c)) {
+ ++state.cp;
+ max = getDecimalValue(c, state, 0xFFFF,
+ "msg.overlarge.max");
+ c = src[state.cp];
+ if (min > max) {
+ reportError("msg.max.lt.min",
+ String.valueOf(src[state.cp]));
+ return false;
+ }
+ }
+ } else {
+ max = min;
+ }
+ /* balance '{' */
+ if (c == '}') {
+ state.result = new RENode(REOP_QUANT);
+ state.result.min = min;
+ state.result.max = max;
+ // QUANT, <min>, <max>, <parencount>,
+ // <parenindex>, <next> ... <ENDCHILD>
+ state.progLength += 12;
+ hasQ = true;
+ }
+ }
+ if (!hasQ) {
+ state.cp = leftCurl;
+ }
+ break;
+ }
+ }
+ if (!hasQ)
+ return true;
+
+ ++state.cp;
+ state.result.kid = term;
+ state.result.parenIndex = parenBaseCount;
+ state.result.parenCount = state.parenCount - parenBaseCount;
+ if ((state.cp < state.cpend) && (src[state.cp] == '?')) {
+ ++state.cp;
+ state.result.greedy = false;
+ }
+ else
+ state.result.greedy = true;
+ return true;
+ }
+
+ private static void resolveForwardJump(byte[] array, int from, int pc)
+ {
+ if (from > pc) throw Kit.codeBug();
+ addIndex(array, from, pc - from);
+ }
+
+ private static int getOffset(byte[] array, int pc)
+ {
+ return getIndex(array, pc);
+ }
+
+ private static int addIndex(byte[] array, int pc, int index)
+ {
+ if (index < 0) throw Kit.codeBug();
+ if (index > 0xFFFF)
+ throw Context.reportRuntimeError("Too complex regexp");
+ array[pc] = (byte)(index >> 8);
+ array[pc + 1] = (byte)(index);
+ return pc + 2;
+ }
+
+ private static int getIndex(byte[] array, int pc)
+ {
+ return ((array[pc] & 0xFF) << 8) | (array[pc + 1] & 0xFF);
+ }
+
+ private static final int OFFSET_LEN = 2;
+ private static final int INDEX_LEN = 2;
+
+ private static int
+ emitREBytecode(CompilerState state, RECompiled re, int pc, RENode t)
+ {
+ RENode nextAlt;
+ int nextAltFixup, nextTermFixup;
+ byte[] program = re.program;
+
+ while (t != null) {
+ program[pc++] = t.op;
+ switch (t.op) {
+ case REOP_EMPTY:
+ --pc;
+ break;
+ case REOP_ALT:
+ nextAlt = t.kid2;
+ nextAltFixup = pc; /* address of next alternate */
+ pc += OFFSET_LEN;
+ pc = emitREBytecode(state, re, pc, t.kid);
+ program[pc++] = REOP_JUMP;
+ nextTermFixup = pc; /* address of following term */
+ pc += OFFSET_LEN;
+ resolveForwardJump(program, nextAltFixup, pc);
+ pc = emitREBytecode(state, re, pc, nextAlt);
+
+ program[pc++] = REOP_JUMP;
+ nextAltFixup = pc;
+ pc += OFFSET_LEN;
+
+ resolveForwardJump(program, nextTermFixup, pc);
+ resolveForwardJump(program, nextAltFixup, pc);
+ break;
+ case REOP_FLAT:
+ /*
+ * Consecutize FLAT's if possible.
+ */
+ if (t.flatIndex != -1) {
+ while ((t.next != null) && (t.next.op == REOP_FLAT)
+ && ((t.flatIndex + t.length)
+ == t.next.flatIndex)) {
+ t.length += t.next.length;
+ t.next = t.next.next;
+ }
+ }
+ if ((t.flatIndex != -1) && (t.length > 1)) {
+ if ((state.flags & JSREG_FOLD) != 0)
+ program[pc - 1] = REOP_FLATi;
+ else
+ program[pc - 1] = REOP_FLAT;
+ pc = addIndex(program, pc, t.flatIndex);
+ pc = addIndex(program, pc, t.length);
+ }
+ else {
+ if (t.chr < 256) {
+ if ((state.flags & JSREG_FOLD) != 0)
+ program[pc - 1] = REOP_FLAT1i;
+ else
+ program[pc - 1] = REOP_FLAT1;
+ program[pc++] = (byte)(t.chr);
+ }
+ else {
+ if ((state.flags & JSREG_FOLD) != 0)
+ program[pc - 1] = REOP_UCFLAT1i;
+ else
+ program[pc - 1] = REOP_UCFLAT1;
+ pc = addIndex(program, pc, t.chr);
+ }
+ }
+ break;
+ case REOP_LPAREN:
+ pc = addIndex(program, pc, t.parenIndex);
+ pc = emitREBytecode(state, re, pc, t.kid);
+ program[pc++] = REOP_RPAREN;
+ pc = addIndex(program, pc, t.parenIndex);
+ break;
+ case REOP_BACKREF:
+ pc = addIndex(program, pc, t.parenIndex);
+ break;
+ case REOP_ASSERT:
+ nextTermFixup = pc;
+ pc += OFFSET_LEN;
+ pc = emitREBytecode(state, re, pc, t.kid);
+ program[pc++] = REOP_ASSERTTEST;
+ resolveForwardJump(program, nextTermFixup, pc);
+ break;
+ case REOP_ASSERT_NOT:
+ nextTermFixup = pc;
+ pc += OFFSET_LEN;
+ pc = emitREBytecode(state, re, pc, t.kid);
+ program[pc++] = REOP_ASSERTNOTTEST;
+ resolveForwardJump(program, nextTermFixup, pc);
+ break;
+ case REOP_QUANT:
+ if ((t.min == 0) && (t.max == -1))
+ program[pc - 1] = (t.greedy) ? REOP_STAR : REOP_MINIMALSTAR;
+ else
+ if ((t.min == 0) && (t.max == 1))
+ program[pc - 1] = (t.greedy) ? REOP_OPT : REOP_MINIMALOPT;
+ else
+ if ((t.min == 1) && (t.max == -1))
+ program[pc - 1] = (t.greedy) ? REOP_PLUS : REOP_MINIMALPLUS;
+ else {
+ if (!t.greedy) program[pc - 1] = REOP_MINIMALQUANT;
+ pc = addIndex(program, pc, t.min);
+ // max can be -1 which addIndex does not accept
+ pc = addIndex(program, pc, t.max + 1);
+ }
+ pc = addIndex(program, pc, t.parenCount);
+ pc = addIndex(program, pc, t.parenIndex);
+ nextTermFixup = pc;
+ pc += OFFSET_LEN;
+ pc = emitREBytecode(state, re, pc, t.kid);
+ program[pc++] = REOP_ENDCHILD;
+ resolveForwardJump(program, nextTermFixup, pc);
+ break;
+ case REOP_CLASS:
+ pc = addIndex(program, pc, t.index);
+ re.classList[t.index] = new RECharSet(t.bmsize, t.startIndex,
+ t.kidlen);
+ break;
+ default:
+ break;
+ }
+ t = t.next;
+ }
+ return pc;
+ }
+
+ private static void
+ pushProgState(REGlobalData gData, int min, int max,
+ REBackTrackData backTrackLastToSave,
+ int continuation_pc, int continuation_op)
+ {
+ gData.stateStackTop = new REProgState(gData.stateStackTop, min, max,
+ gData.cp, backTrackLastToSave,
+ continuation_pc,
+ continuation_op);
+ }
+
+ private static REProgState
+ popProgState(REGlobalData gData)
+ {
+ REProgState state = gData.stateStackTop;
+ gData.stateStackTop = state.previous;
+ return state;
+ }
+
+ private static void
+ pushBackTrackState(REGlobalData gData, byte op, int target)
+ {
+ gData.backTrackStackTop = new REBackTrackData(gData, op, target);
+ }
+
+ /*
+ * Consecutive literal characters.
+ */
+ private static boolean
+ flatNMatcher(REGlobalData gData, int matchChars,
+ int length, char[] chars, int end)
+ {
+ if ((gData.cp + length) > end)
+ return false;
+ for (int i = 0; i < length; i++) {
+ if (gData.regexp.source[matchChars + i] != chars[gData.cp + i]) {
+ return false;
+ }
+ }
+ gData.cp += length;
+ return true;
+ }
+
+ private static boolean
+ flatNIMatcher(REGlobalData gData, int matchChars,
+ int length, char[] chars, int end)
+ {
+ if ((gData.cp + length) > end)
+ return false;
+ for (int i = 0; i < length; i++) {
+ if (upcase(gData.regexp.source[matchChars + i])
+ != upcase(chars[gData.cp + i]))
+ {
+ return false;
+ }
+ }
+ gData.cp += length;
+ return true;
+ }
+
+ /*
+ 1. Evaluate DecimalEscape to obtain an EscapeValue E.
+ 2. If E is not a character then go to step 6.
+ 3. Let ch be E's character.
+ 4. Let A be a one-element RECharSet containing the character ch.
+ 5. Call CharacterSetMatcher(A, false) and return its Matcher result.
+ 6. E must be an integer. Let n be that integer.
+ 7. If n=0 or n>NCapturingParens then throw a SyntaxError exception.
+ 8. Return an internal Matcher closure that takes two arguments, a State x
+ and a Continuation c, and performs the following:
+ 1. Let cap be x's captures internal array.
+ 2. Let s be cap[n].
+ 3. If s is undefined, then call c(x) and return its result.
+ 4. Let e be x's endIndex.
+ 5. Let len be s's length.
+ 6. Let f be e+len.
+ 7. If f>InputLength, return failure.
+ 8. If there exists an integer i between 0 (inclusive) and len (exclusive)
+ such that Canonicalize(s[i]) is not the same character as
+ Canonicalize(Input [e+i]), then return failure.
+ 9. Let y be the State (f, cap).
+ 10. Call c(y) and return its result.
+ */
+ private static boolean
+ backrefMatcher(REGlobalData gData, int parenIndex,
+ char[] chars, int end)
+ {
+ int len;
+ int i;
+ int parenContent = gData.parens_index(parenIndex);
+ if (parenContent == -1)
+ return true;
+
+ len = gData.parens_length(parenIndex);
+ if ((gData.cp + len) > end)
+ return false;
+
+ if ((gData.regexp.flags & JSREG_FOLD) != 0) {
+ for (i = 0; i < len; i++) {
+ if (upcase(chars[parenContent + i]) != upcase(chars[gData.cp + i]))
+ return false;
+ }
+ }
+ else {
+ for (i = 0; i < len; i++) {
+ if (chars[parenContent + i] != chars[gData.cp + i])
+ return false;
+ }
+ }
+ gData.cp += len;
+ return true;
+ }
+
+
+ /* Add a single character to the RECharSet */
+ private static void
+ addCharacterToCharSet(RECharSet cs, char c)
+ {
+ int byteIndex = (c / 8);
+ if (c > cs.length)
+ throw new RuntimeException();
+ cs.bits[byteIndex] |= 1 << (c & 0x7);
+ }
+
+
+ /* Add a character range, c1 to c2 (inclusive) to the RECharSet */
+ private static void
+ addCharacterRangeToCharSet(RECharSet cs, char c1, char c2)
+ {
+ int i;
+
+ int byteIndex1 = (c1 / 8);
+ int byteIndex2 = (c2 / 8);
+
+ if ((c2 > cs.length) || (c1 > c2))
+ throw new RuntimeException();
+
+ c1 &= 0x7;
+ c2 &= 0x7;
+
+ if (byteIndex1 == byteIndex2) {
+ cs.bits[byteIndex1] |= ((0xFF) >> (7 - (c2 - c1))) << c1;
+ }
+ else {
+ cs.bits[byteIndex1] |= 0xFF << c1;
+ for (i = byteIndex1 + 1; i < byteIndex2; i++)
+ cs.bits[i] = (byte)0xFF;
+ cs.bits[byteIndex2] |= (0xFF) >> (7 - c2);
+ }
+ }
+
+ /* Compile the source of the class into a RECharSet */
+ private static void
+ processCharSet(REGlobalData gData, RECharSet charSet)
+ {
+ synchronized (charSet) {
+ if (!charSet.converted) {
+ processCharSetImpl(gData, charSet);
+ charSet.converted = true;
+ }
+ }
+ }
+
+
+ private static void
+ processCharSetImpl(REGlobalData gData, RECharSet charSet)
+ {
+ int src = charSet.startIndex;
+ int end = src + charSet.strlength;
+
+ char rangeStart = 0, thisCh;
+ int byteLength;
+ char c;
+ int n;
+ int nDigits;
+ int i;
+ boolean inRange = false;
+
+ charSet.sense = true;
+ byteLength = (charSet.length / 8) + 1;
+ charSet.bits = new byte[byteLength];
+
+ if (src == end)
+ return;
+
+ if (gData.regexp.source[src] == '^') {
+ charSet.sense = false;
+ ++src;
+ }
+
+ while (src != end) {
+ nDigits = 2;
+ switch (gData.regexp.source[src]) {
+ case '\\':
+ ++src;
+ c = gData.regexp.source[src++];
+ switch (c) {
+ case 'b':
+ thisCh = 0x8;
+ break;
+ case 'f':
+ thisCh = 0xC;
+ break;
+ case 'n':
+ thisCh = 0xA;
+ break;
+ case 'r':
+ thisCh = 0xD;
+ break;
+ case 't':
+ thisCh = 0x9;
+ break;
+ case 'v':
+ thisCh = 0xB;
+ break;
+ case 'c':
+ if (((src + 1) < end) && isWord(gData.regexp.source[src + 1]))
+ thisCh = (char)(gData.regexp.source[src++] & 0x1F);
+ else {
+ --src;
+ thisCh = '\\';
+ }
+ break;
+ case 'u':
+ nDigits += 2;
+ // fall thru
+ case 'x':
+ n = 0;
+ for (i = 0; (i < nDigits) && (src < end); i++) {
+ c = gData.regexp.source[src++];
+ int digit = toASCIIHexDigit(c);
+ if (digit < 0) {
+ /* back off to accepting the original '\'
+ * as a literal
+ */
+ src -= (i + 1);
+ n = '\\';
+ break;
+ }
+ n = (n << 4) | digit;
+ }
+ thisCh = (char)(n);
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ /*
+ * This is a non-ECMA extension - decimal escapes (in this
+ * case, octal!) are supposed to be an error inside class
+ * ranges, but supported here for backwards compatibility.
+ *
+ */
+ n = (c - '0');
+ c = gData.regexp.source[src];
+ if ('0' <= c && c <= '7') {
+ src++;
+ n = 8 * n + (c - '0');
+ c = gData.regexp.source[src];
+ if ('0' <= c && c <= '7') {
+ src++;
+ i = 8 * n + (c - '0');
+ if (i <= 0377)
+ n = i;
+ else
+ src--;
+ }
+ }
+ thisCh = (char)(n);
+ break;
+
+ case 'd':
+ addCharacterRangeToCharSet(charSet, '0', '9');
+ continue; /* don't need range processing */
+ case 'D':
+ addCharacterRangeToCharSet(charSet, (char)0, (char)('0' - 1));
+ addCharacterRangeToCharSet(charSet, (char)('9' + 1),
+ (char)(charSet.length));
+ continue;
+ case 's':
+ for (i = charSet.length; i >= 0; i--)
+ if (isREWhiteSpace(i))
+ addCharacterToCharSet(charSet, (char)(i));
+ continue;
+ case 'S':
+ for (i = charSet.length; i >= 0; i--)
+ if (!isREWhiteSpace(i))
+ addCharacterToCharSet(charSet, (char)(i));
+ continue;
+ case 'w':
+ for (i = charSet.length; i >= 0; i--)
+ if (isWord((char)i))
+ addCharacterToCharSet(charSet, (char)(i));
+ continue;
+ case 'W':
+ for (i = charSet.length; i >= 0; i--)
+ if (!isWord((char)i))
+ addCharacterToCharSet(charSet, (char)(i));
+ continue;
+ default:
+ thisCh = c;
+ break;
+
+ }
+ break;
+
+ default:
+ thisCh = gData.regexp.source[src++];
+ break;
+
+ }
+ if (inRange) {
+ if ((gData.regexp.flags & JSREG_FOLD) != 0) {
+ addCharacterRangeToCharSet(charSet,
+ upcase(rangeStart),
+ upcase(thisCh));
+ addCharacterRangeToCharSet(charSet,
+ downcase(rangeStart),
+ downcase(thisCh));
+ } else {
+ addCharacterRangeToCharSet(charSet, rangeStart, thisCh);
+ }
+ inRange = false;
+ }
+ else {
+ if ((gData.regexp.flags & JSREG_FOLD) != 0) {
+ addCharacterToCharSet(charSet, upcase(thisCh));
+ addCharacterToCharSet(charSet, downcase(thisCh));
+ } else {
+ addCharacterToCharSet(charSet, thisCh);
+ }
+ if (src < (end - 1)) {
+ if (gData.regexp.source[src] == '-') {
+ ++src;
+ inRange = true;
+ rangeStart = thisCh;
+ }
+ }
+ }
+ }
+ }
+
+
+ /*
+ * Initialize the character set if it this is the first call.
+ * Test the bit - if the ^ flag was specified, non-inclusion is a success
+ */
+ private static boolean
+ classMatcher(REGlobalData gData, RECharSet charSet, char ch)
+ {
+ if (!charSet.converted) {
+ processCharSet(gData, charSet);
+ }
+
+ int byteIndex = ch / 8;
+ if (charSet.sense) {
+ if ((charSet.length == 0) ||
+ ( (ch > charSet.length)
+ || ((charSet.bits[byteIndex] & (1 << (ch & 0x7))) == 0) ))
+ return false;
+ } else {
+ if (! ((charSet.length == 0) ||
+ ( (ch > charSet.length)
+ || ((charSet.bits[byteIndex] & (1 << (ch & 0x7))) == 0) )))
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean
+ executeREBytecode(REGlobalData gData, char[] chars, int end)
+ {
+ int pc = 0;
+ byte program[] = gData.regexp.program;
+ int currentContinuation_op;
+ int currentContinuation_pc;
+ boolean result = false;
+
+ currentContinuation_pc = 0;
+ currentContinuation_op = REOP_END;
+if (debug) {
+System.out.println("Input = \"" + new String(chars) + "\", start at " + gData.cp);
+}
+ int op = program[pc++];
+ for (;;) {
+if (debug) {
+System.out.println("Testing at " + gData.cp + ", op = " + op);
+}
+ switch (op) {
+ case REOP_EMPTY:
+ result = true;
+ break;
+ case REOP_BOL:
+ if (gData.cp != 0) {
+ if (gData.multiline ||
+ ((gData.regexp.flags & JSREG_MULTILINE) != 0)) {
+ if (!isLineTerm(chars[gData.cp - 1])) {
+ result = false;
+ break;
+ }
+ }
+ else {
+ result = false;
+ break;
+ }
+ }
+ result = true;
+ break;
+ case REOP_EOL:
+ if (gData.cp != end) {
+ if (gData.multiline ||
+ ((gData.regexp.flags & JSREG_MULTILINE) != 0)) {
+ if (!isLineTerm(chars[gData.cp])) {
+ result = false;
+ break;
+ }
+ }
+ else {
+ result = false;
+ break;
+ }
+ }
+ result = true;
+ break;
+ case REOP_WBDRY:
+ result = ((gData.cp == 0 || !isWord(chars[gData.cp - 1]))
+ ^ !((gData.cp < end) && isWord(chars[gData.cp])));
+ break;
+ case REOP_WNONBDRY:
+ result = ((gData.cp == 0 || !isWord(chars[gData.cp - 1]))
+ ^ ((gData.cp < end) && isWord(chars[gData.cp])));
+ break;
+ case REOP_DOT:
+ result = (gData.cp != end && !isLineTerm(chars[gData.cp]));
+ if (result) {
+ gData.cp++;
+ }
+ break;
+ case REOP_DIGIT:
+ result = (gData.cp != end && isDigit(chars[gData.cp]));
+ if (result) {
+ gData.cp++;
+ }
+ break;
+ case REOP_NONDIGIT:
+ result = (gData.cp != end && !isDigit(chars[gData.cp]));
+ if (result) {
+ gData.cp++;
+ }
+ break;
+ case REOP_SPACE:
+ result = (gData.cp != end && isREWhiteSpace(chars[gData.cp]));
+ if (result) {
+ gData.cp++;
+ }
+ break;
+ case REOP_NONSPACE:
+ result = (gData.cp != end && !isREWhiteSpace(chars[gData.cp]));
+ if (result) {
+ gData.cp++;
+ }
+ break;
+ case REOP_ALNUM:
+ result = (gData.cp != end && isWord(chars[gData.cp]));
+ if (result) {
+ gData.cp++;
+ }
+ break;
+ case REOP_NONALNUM:
+ result = (gData.cp != end && !isWord(chars[gData.cp]));
+ if (result) {
+ gData.cp++;
+ }
+ break;
+ case REOP_FLAT:
+ {
+ int offset = getIndex(program, pc);
+ pc += INDEX_LEN;
+ int length = getIndex(program, pc);
+ pc += INDEX_LEN;
+ result = flatNMatcher(gData, offset, length, chars, end);
+ }
+ break;
+ case REOP_FLATi:
+ {
+ int offset = getIndex(program, pc);
+ pc += INDEX_LEN;
+ int length = getIndex(program, pc);
+ pc += INDEX_LEN;
+ result = flatNIMatcher(gData, offset, length, chars, end);
+ }
+ break;
+ case REOP_FLAT1:
+ {
+ char matchCh = (char)(program[pc++] & 0xFF);
+ result = (gData.cp != end && chars[gData.cp] == matchCh);
+ if (result) {
+ gData.cp++;
+ }
+ }
+ break;
+ case REOP_FLAT1i:
+ {
+ char matchCh = (char)(program[pc++] & 0xFF);
+ result = (gData.cp != end
+ && upcase(chars[gData.cp]) == upcase(matchCh));
+ if (result) {
+ gData.cp++;
+ }
+ }
+ break;
+ case REOP_UCFLAT1:
+ {
+ char matchCh = (char)getIndex(program, pc);
+ pc += INDEX_LEN;
+ result = (gData.cp != end && chars[gData.cp] == matchCh);
+ if (result) {
+ gData.cp++;
+ }
+ }
+ break;
+ case REOP_UCFLAT1i:
+ {
+ char matchCh = (char)getIndex(program, pc);
+ pc += INDEX_LEN;
+ result = (gData.cp != end
+ && upcase(chars[gData.cp]) == upcase(matchCh));
+ if (result) {
+ gData.cp++;
+ }
+ }
+ break;
+ case REOP_ALT:
+ {
+ int nextpc;
+ byte nextop;
+ pushProgState(gData, 0, 0, null,
+ currentContinuation_pc,
+ currentContinuation_op);
+ nextpc = pc + getOffset(program, pc);
+ nextop = program[nextpc++];
+ pushBackTrackState(gData, nextop, nextpc);
+ pc += INDEX_LEN;
+ op = program[pc++];
+ }
+ continue;
+
+ case REOP_JUMP:
+ {
+ int offset;
+ REProgState state = popProgState(gData);
+ currentContinuation_pc = state.continuation_pc;
+ currentContinuation_op = state.continuation_op;
+ offset = getOffset(program, pc);
+ pc += offset;
+ op = program[pc++];
+ }
+ continue;
+
+
+ case REOP_LPAREN:
+ {
+ int parenIndex = getIndex(program, pc);
+ pc += INDEX_LEN;
+ gData.set_parens(parenIndex, gData.cp, 0);
+ op = program[pc++];
+ }
+ continue;
+ case REOP_RPAREN:
+ {
+ int cap_index;
+ int parenIndex = getIndex(program, pc);
+ pc += INDEX_LEN;
+ cap_index = gData.parens_index(parenIndex);
+ gData.set_parens(parenIndex, cap_index,
+ gData.cp - cap_index);
+ if (parenIndex > gData.lastParen)
+ gData.lastParen = parenIndex;
+ op = program[pc++];
+ }
+ continue;
+ case REOP_BACKREF:
+ {
+ int parenIndex = getIndex(program, pc);
+ pc += INDEX_LEN;
+ result = backrefMatcher(gData, parenIndex, chars, end);
+ }
+ break;
+
+ case REOP_CLASS:
+ {
+ int index = getIndex(program, pc);
+ pc += INDEX_LEN;
+ if (gData.cp != end) {
+ if (classMatcher(gData, gData.regexp.classList[index],
+ chars[gData.cp]))
+ {
+ gData.cp++;
+ result = true;
+ break;
+ }
+ }
+ result = false;
+ }
+ break;
+
+ case REOP_ASSERT:
+ case REOP_ASSERT_NOT:
+ {
+ byte testOp;
+ pushProgState(gData, 0, 0, gData.backTrackStackTop,
+ currentContinuation_pc,
+ currentContinuation_op);
+ if (op == REOP_ASSERT) {
+ testOp = REOP_ASSERTTEST;
+ } else {
+ testOp = REOP_ASSERTNOTTEST;
+ }
+ pushBackTrackState(gData, testOp,
+ pc + getOffset(program, pc));
+ pc += INDEX_LEN;
+ op = program[pc++];
+ }
+ continue;
+
+ case REOP_ASSERTTEST:
+ case REOP_ASSERTNOTTEST:
+ {
+ REProgState state = popProgState(gData);
+ gData.cp = state.index;
+ gData.backTrackStackTop = state.backTrack;
+ currentContinuation_pc = state.continuation_pc;
+ currentContinuation_op = state.continuation_op;
+ if (result) {
+ if (op == REOP_ASSERTTEST) {
+ result = true;
+ } else {
+ result = false;
+ }
+ } else {
+ if (op == REOP_ASSERTTEST) {
+ // Do nothing
+ } else {
+ result = true;
+ }
+ }
+ }
+ break;
+
+ case REOP_STAR:
+ case REOP_PLUS:
+ case REOP_OPT:
+ case REOP_QUANT:
+ case REOP_MINIMALSTAR:
+ case REOP_MINIMALPLUS:
+ case REOP_MINIMALOPT:
+ case REOP_MINIMALQUANT:
+ {
+ int min, max;
+ boolean greedy = false;
+ switch (op) {
+ case REOP_STAR:
+ greedy = true;
+ // fallthrough
+ case REOP_MINIMALSTAR:
+ min = 0;
+ max = -1;
+ break;
+ case REOP_PLUS:
+ greedy = true;
+ // fallthrough
+ case REOP_MINIMALPLUS:
+ min = 1;
+ max = -1;
+ break;
+ case REOP_OPT:
+ greedy = true;
+ // fallthrough
+ case REOP_MINIMALOPT:
+ min = 0;
+ max = 1;
+ break;
+ case REOP_QUANT:
+ greedy = true;
+ // fallthrough
+ case REOP_MINIMALQUANT:
+ min = getOffset(program, pc);
+ pc += INDEX_LEN;
+ // See comments in emitREBytecode for " - 1" reason
+ max = getOffset(program, pc) - 1;
+ pc += INDEX_LEN;
+ break;
+ default:
+ throw Kit.codeBug();
+ }
+ pushProgState(gData, min, max, null,
+ currentContinuation_pc,
+ currentContinuation_op);
+ if (greedy) {
+ currentContinuation_op = REOP_REPEAT;
+ currentContinuation_pc = pc;
+ pushBackTrackState(gData, REOP_REPEAT, pc);
+ /* Step over <parencount>, <parenindex> & <next> */
+ pc += 3 * INDEX_LEN;
+ op = program[pc++];
+ } else {
+ if (min != 0) {
+ currentContinuation_op = REOP_MINIMALREPEAT;
+ currentContinuation_pc = pc;
+ /* <parencount> <parenindex> & <next> */
+ pc += 3 * INDEX_LEN;
+ op = program[pc++];
+ } else {
+ pushBackTrackState(gData, REOP_MINIMALREPEAT, pc);
+ popProgState(gData);
+ pc += 2 * INDEX_LEN; // <parencount> & <parenindex>
+ pc = pc + getOffset(program, pc);
+ op = program[pc++];
+ }
+ }
+ }
+ continue;
+
+ case REOP_ENDCHILD:
+ // Use the current continuation.
+ pc = currentContinuation_pc;
+ op = currentContinuation_op;
+ continue;
+
+ case REOP_REPEAT:
+ {
+ REProgState state = popProgState(gData);
+ if (!result) {
+ //
+ // There's been a failure, see if we have enough
+ // children.
+ //
+ if (state.min == 0)
+ result = true;
+ currentContinuation_pc = state.continuation_pc;
+ currentContinuation_op = state.continuation_op;
+ pc += 2 * INDEX_LEN; /* <parencount> & <parenindex> */
+ pc = pc + getOffset(program, pc);
+ break;
+ }
+ else {
+ if (state.min == 0 && gData.cp == state.index) {
+ // matched an empty string, that'll get us nowhere
+ result = false;
+ currentContinuation_pc = state.continuation_pc;
+ currentContinuation_op = state.continuation_op;
+ pc += 2 * INDEX_LEN;
+ pc = pc + getOffset(program, pc);
+ break;
+ }
+ int new_min = state.min, new_max = state.max;
+ if (new_min != 0) new_min--;
+ if (new_max != -1) new_max--;
+ if (new_max == 0) {
+ result = true;
+ currentContinuation_pc = state.continuation_pc;
+ currentContinuation_op = state.continuation_op;
+ pc += 2 * INDEX_LEN;
+ pc = pc + getOffset(program, pc);
+ break;
+ }
+ pushProgState(gData, new_min, new_max, null,
+ state.continuation_pc,
+ state.continuation_op);
+ currentContinuation_op = REOP_REPEAT;
+ currentContinuation_pc = pc;
+ pushBackTrackState(gData, REOP_REPEAT, pc);
+ int parenCount = getIndex(program, pc);
+ pc += INDEX_LEN;
+ int parenIndex = getIndex(program, pc);
+ pc += 2 * INDEX_LEN;
+ op = program[pc++];
+ for (int k = 0; k < parenCount; k++) {
+ gData.set_parens(parenIndex + k, -1, 0);
+ }
+ }
+ }
+ continue;
+
+ case REOP_MINIMALREPEAT:
+ {
+ REProgState state = popProgState(gData);
+ if (!result) {
+ //
+ // Non-greedy failure - try to consume another child.
+ //
+ if (state.max == -1 || state.max > 0) {
+ pushProgState(gData, state.min, state.max, null,
+ state.continuation_pc,
+ state.continuation_op);
+ currentContinuation_op = REOP_MINIMALREPEAT;
+ currentContinuation_pc = pc;
+ int parenCount = getIndex(program, pc);
+ pc += INDEX_LEN;
+ int parenIndex = getIndex(program, pc);
+ pc += 2 * INDEX_LEN;
+ for (int k = 0; k < parenCount; k++) {
+ gData.set_parens(parenIndex + k, -1, 0);
+ }
+ op = program[pc++];
+ continue;
+ } else {
+ // Don't need to adjust pc since we're going to pop.
+ currentContinuation_pc = state.continuation_pc;
+ currentContinuation_op = state.continuation_op;
+ break;
+ }
+ } else {
+ if (state.min == 0 && gData.cp == state.index) {
+ // Matched an empty string, that'll get us nowhere.
+ result = false;
+ currentContinuation_pc = state.continuation_pc;
+ currentContinuation_op = state.continuation_op;
+ break;
+ }
+ int new_min = state.min, new_max = state.max;
+ if (new_min != 0) new_min--;
+ if (new_max != -1) new_max--;
+ pushProgState(gData, new_min, new_max, null,
+ state.continuation_pc,
+ state.continuation_op);
+ if (new_min != 0) {
+ currentContinuation_op = REOP_MINIMALREPEAT;
+ currentContinuation_pc = pc;
+ int parenCount = getIndex(program, pc);
+ pc += INDEX_LEN;
+ int parenIndex = getIndex(program, pc);
+ pc += 2 * INDEX_LEN;
+ for (int k = 0; k < parenCount; k++) {
+ gData.set_parens(parenIndex + k, -1, 0);
+ }
+ op = program[pc++];
+ } else {
+ currentContinuation_pc = state.continuation_pc;
+ currentContinuation_op = state.continuation_op;
+ pushBackTrackState(gData, REOP_MINIMALREPEAT, pc);
+ popProgState(gData);
+ pc += 2 * INDEX_LEN;
+ pc = pc + getOffset(program, pc);
+ op = program[pc++];
+ }
+ continue;
+ }
+ }
+
+ case REOP_END:
+ return true;
+
+ default:
+ throw Kit.codeBug();
+
+ }
+ /*
+ * If the match failed and there's a backtrack option, take it.
+ * Otherwise this is a complete and utter failure.
+ */
+ if (!result) {
+ REBackTrackData backTrackData = gData.backTrackStackTop;
+ if (backTrackData != null) {
+ gData.backTrackStackTop = backTrackData.previous;
+
+ gData.lastParen = backTrackData.lastParen;
+
+ // XXX: If backTrackData will no longer be used, then
+ // there is no need to clone backTrackData.parens
+ if (backTrackData.parens != null) {
+ gData.parens = backTrackData.parens.clone();
+ }
+
+ gData.cp = backTrackData.cp;
+
+ gData.stateStackTop = backTrackData.stateStackTop;
+
+ currentContinuation_op
+ = gData.stateStackTop.continuation_op;
+ currentContinuation_pc
+ = gData.stateStackTop.continuation_pc;
+ pc = backTrackData.continuation_pc;
+ op = backTrackData.continuation_op;
+ continue;
+ }
+ else
+ return false;
+ }
+
+ op = program[pc++];
+ }
+
+ }
+
+ private static boolean
+ matchRegExp(REGlobalData gData, RECompiled re,
+ char[] chars, int start, int end, boolean multiline)
+ {
+ if (re.parenCount != 0) {
+ gData.parens = new long[re.parenCount];
+ } else {
+ gData.parens = null;
+ }
+
+ gData.backTrackStackTop = null;
+
+ gData.stateStackTop = null;
+
+ gData.multiline = multiline;
+ gData.regexp = re;
+ gData.lastParen = 0;
+
+ int anchorCh = gData.regexp.anchorCh;
+ //
+ // have to include the position beyond the last character
+ // in order to detect end-of-input/line condition
+ //
+ for (int i = start; i <= end; ++i) {
+ //
+ // If the first node is a literal match, step the index into
+ // the string until that match is made, or fail if it can't be
+ // found at all.
+ //
+ if (anchorCh >= 0) {
+ for (;;) {
+ if (i == end) {
+ return false;
+ }
+ char matchCh = chars[i];
+ if (matchCh == anchorCh ||
+ ((gData.regexp.flags & JSREG_FOLD) != 0
+ && upcase(matchCh) == upcase((char)anchorCh)))
+ {
+ break;
+ }
+ ++i;
+ }
+ }
+ gData.cp = i;
+ for (int j = 0; j < re.parenCount; j++) {
+ gData.set_parens(j, -1, 0);
+ }
+ boolean result = executeREBytecode(gData, chars, end);
+
+ gData.backTrackStackTop = null;
+ gData.stateStackTop = null;
+ if (result) {
+ gData.skipped = i - start;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /*
+ * indexp is assumed to be an array of length 1
+ */
+ Object executeRegExp(Context cx, Scriptable scopeObj, RegExpImpl res,
+ String str, int indexp[], int matchType)
+ {
+ REGlobalData gData = new REGlobalData();
+
+ int start = indexp[0];
+ char[] charArray = str.toCharArray();
+ int end = charArray.length;
+ if (start > end)
+ start = end;
+ //
+ // Call the recursive matcher to do the real work.
+ //
+ boolean matches = matchRegExp(gData, re, charArray, start, end,
+ res.multiline);
+ if (!matches) {
+ if (matchType != PREFIX) return null;
+ return Undefined.instance;
+ }
+ int index = gData.cp;
+ int i = index;
+ indexp[0] = i;
+ int matchlen = i - (start + gData.skipped);
+ int ep = index;
+ index -= matchlen;
+ Object result;
+ Scriptable obj;
+
+ if (matchType == TEST) {
+ /*
+ * Testing for a match and updating cx.regExpImpl: don't allocate
+ * an array object, do return true.
+ */
+ result = Boolean.TRUE;
+ obj = null;
+ }
+ else {
+ /*
+ * The array returned on match has element 0 bound to the matched
+ * string, elements 1 through re.parenCount bound to the paren
+ * matches, an index property telling the length of the left context,
+ * and an input property referring to the input string.
+ */
+ Scriptable scope = getTopLevelScope(scopeObj);
+ result = ScriptRuntime.newObject(cx, scope, "Array", null);
+ obj = (Scriptable) result;
+
+ String matchstr = new String(charArray, index, matchlen);
+ obj.put(0, obj, matchstr);
+ }
+
+ if (re.parenCount == 0) {
+ res.parens = null;
+ res.lastParen = SubString.emptySubString;
+ } else {
+ SubString parsub = null;
+ int num;
+ res.parens = new SubString[re.parenCount];
+ for (num = 0; num < re.parenCount; num++) {
+ int cap_index = gData.parens_index(num);
+ String parstr;
+ if (cap_index != -1) {
+ int cap_length = gData.parens_length(num);
+ parsub = new SubString(charArray, cap_index, cap_length);
+ res.parens[num] = parsub;
+ if (matchType == TEST) continue;
+ parstr = parsub.toString();
+ obj.put(num+1, obj, parstr);
+ }
+ else {
+ if (matchType != TEST)
+ obj.put(num+1, obj, Undefined.instance);
+ }
+ }
+ res.lastParen = parsub;
+ }
+
+ if (! (matchType == TEST)) {
+ /*
+ * Define the index and input properties last for better for/in loop
+ * order (so they come after the elements).
+ */
+ obj.put("index", obj, new Integer(start + gData.skipped));
+ obj.put("input", obj, str);
+ }
+
+ if (res.lastMatch == null) {
+ res.lastMatch = new SubString();
+ res.leftContext = new SubString();
+ res.rightContext = new SubString();
+ }
+ res.lastMatch.charArray = charArray;
+ res.lastMatch.index = index;
+ res.lastMatch.length = matchlen;
+
+ res.leftContext.charArray = charArray;
+ if (cx.getLanguageVersion() == Context.VERSION_1_2) {
+ /*
+ * JS1.2 emulated Perl4.0.1.8 (patch level 36) for global regexps used
+ * in scalar contexts, and unintentionally for the string.match "list"
+ * psuedo-context. On "hi there bye", the following would result:
+ *
+ * Language while(/ /g){print("$`");} s/ /$`/g
+ * perl4.036 "hi", "there" "hihitherehi therebye"
+ * perl5 "hi", "hi there" "hihitherehi therebye"
+ * js1.2 "hi", "there" "hihitheretherebye"
+ *
+ * Insofar as JS1.2 always defined $` as "left context from the last
+ * match" for global regexps, it was more consistent than perl4.
+ */
+ res.leftContext.index = start;
+ res.leftContext.length = gData.skipped;
+ } else {
+ /*
+ * For JS1.3 and ECMAv2, emulate Perl5 exactly:
+ *
+ * js1.3 "hi", "hi there" "hihitherehi therebye"
+ */
+ res.leftContext.index = 0;
+ res.leftContext.length = start + gData.skipped;
+ }
+
+ res.rightContext.charArray = charArray;
+ res.rightContext.index = ep;
+ res.rightContext.length = end - ep;
+
+ return result;
+ }
+
+ int getFlags()
+ {
+ return re.flags;
+ }
+
+ private static void reportWarning(Context cx, String messageId, String arg)
+ {
+ if (cx.hasFeature(Context.FEATURE_STRICT_MODE)) {
+ String msg = ScriptRuntime.getMessage1(messageId, arg);
+ Context.reportWarning(msg);
+ }
+ }
+
+ private static void reportError(String messageId, String arg)
+ {
+ String msg = ScriptRuntime.getMessage1(messageId, arg);
+ throw ScriptRuntime.constructError("SyntaxError", msg);
+ }
+
+// #string_id_map#
+
+ private static final int
+ Id_lastIndex = 1,
+ Id_source = 2,
+ Id_global = 3,
+ Id_ignoreCase = 4,
+ Id_multiline = 5,
+
+ MAX_INSTANCE_ID = 5;
+
+ protected int getMaxInstanceId()
+ {
+ return MAX_INSTANCE_ID;
+ }
+
+ protected int findInstanceIdInfo(String s)
+ {
+ int id;
+// #generated# Last update: 2007-05-09 08:16:24 EDT
+ L0: { id = 0; String X = null; int c;
+ int s_length = s.length();
+ if (s_length==6) {
+ c=s.charAt(0);
+ if (c=='g') { X="global";id=Id_global; }
+ else if (c=='s') { X="source";id=Id_source; }
+ }
+ else if (s_length==9) {
+ c=s.charAt(0);
+ if (c=='l') { X="lastIndex";id=Id_lastIndex; }
+ else if (c=='m') { X="multiline";id=Id_multiline; }
+ }
+ else if (s_length==10) { X="ignoreCase";id=Id_ignoreCase; }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ break L0;
+ }
+// #/generated#
+// #/string_id_map#
+
+ if (id == 0) return super.findInstanceIdInfo(s);
+
+ int attr;
+ switch (id) {
+ case Id_lastIndex:
+ attr = PERMANENT | DONTENUM;
+ break;
+ case Id_source:
+ case Id_global:
+ case Id_ignoreCase:
+ case Id_multiline:
+ attr = PERMANENT | READONLY | DONTENUM;
+ break;
+ default:
+ throw new IllegalStateException();
+ }
+ return instanceIdInfo(attr, id);
+ }
+
+ protected String getInstanceIdName(int id)
+ {
+ switch (id) {
+ case Id_lastIndex: return "lastIndex";
+ case Id_source: return "source";
+ case Id_global: return "global";
+ case Id_ignoreCase: return "ignoreCase";
+ case Id_multiline: return "multiline";
+ }
+ return super.getInstanceIdName(id);
+ }
+
+ protected Object getInstanceIdValue(int id)
+ {
+ switch (id) {
+ case Id_lastIndex:
+ return ScriptRuntime.wrapNumber(lastIndex);
+ case Id_source:
+ return new String(re.source);
+ case Id_global:
+ return ScriptRuntime.wrapBoolean((re.flags & JSREG_GLOB) != 0);
+ case Id_ignoreCase:
+ return ScriptRuntime.wrapBoolean((re.flags & JSREG_FOLD) != 0);
+ case Id_multiline:
+ return ScriptRuntime.wrapBoolean((re.flags & JSREG_MULTILINE) != 0);
+ }
+ return super.getInstanceIdValue(id);
+ }
+
+ protected void setInstanceIdValue(int id, Object value)
+ {
+ if (id == Id_lastIndex) {
+ lastIndex = ScriptRuntime.toNumber(value);
+ return;
+ }
+ super.setInstanceIdValue(id, value);
+ }
+
+ protected void initPrototypeId(int id)
+ {
+ String s;
+ int arity;
+ switch (id) {
+ case Id_compile: arity=1; s="compile"; break;
+ case Id_toString: arity=0; s="toString"; break;
+ case Id_toSource: arity=0; s="toSource"; break;
+ case Id_exec: arity=1; s="exec"; break;
+ case Id_test: arity=1; s="test"; break;
+ case Id_prefix: arity=1; s="prefix"; break;
+ default: throw new IllegalArgumentException(String.valueOf(id));
+ }
+ initPrototypeMethod(REGEXP_TAG, id, s, arity);
+ }
+
+ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
+ Scriptable thisObj, Object[] args)
+ {
+ if (!f.hasTag(REGEXP_TAG)) {
+ return super.execIdCall(f, cx, scope, thisObj, args);
+ }
+ int id = f.methodId();
+ switch (id) {
+ case Id_compile:
+ return realThis(thisObj, f).compile(cx, scope, args);
+
+ case Id_toString:
+ case Id_toSource:
+ return realThis(thisObj, f).toString();
+
+ case Id_exec:
+ return realThis(thisObj, f).execSub(cx, scope, args, MATCH);
+
+ case Id_test: {
+ Object x = realThis(thisObj, f).execSub(cx, scope, args, TEST);
+ return Boolean.TRUE.equals(x) ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ case Id_prefix:
+ return realThis(thisObj, f).execSub(cx, scope, args, PREFIX);
+ }
+ throw new IllegalArgumentException(String.valueOf(id));
+ }
+
+ private static NativeRegExp realThis(Scriptable thisObj, IdFunctionObject f)
+ {
+ if (!(thisObj instanceof NativeRegExp))
+ throw incompatibleCallError(f);
+ return (NativeRegExp)thisObj;
+ }
+
+// #string_id_map#
+ protected int findPrototypeId(String s)
+ {
+ int id;
+// #generated# Last update: 2007-05-09 08:16:24 EDT
+ L0: { id = 0; String X = null; int c;
+ L: switch (s.length()) {
+ case 4: c=s.charAt(0);
+ if (c=='e') { X="exec";id=Id_exec; }
+ else if (c=='t') { X="test";id=Id_test; }
+ break L;
+ case 6: X="prefix";id=Id_prefix; break L;
+ case 7: X="compile";id=Id_compile; break L;
+ case 8: c=s.charAt(3);
+ if (c=='o') { X="toSource";id=Id_toSource; }
+ else if (c=='t') { X="toString";id=Id_toString; }
+ break L;
+ }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ break L0;
+ }
+// #/generated#
+ return id;
+ }
+
+ private static final int
+ Id_compile = 1,
+ Id_toString = 2,
+ Id_toSource = 3,
+ Id_exec = 4,
+ Id_test = 5,
+ Id_prefix = 6,
+
+ MAX_PROTOTYPE_ID = 6;
+
+// #/string_id_map#
+
+ private RECompiled re;
+ double lastIndex; /* index after last match, for //g iterator */
+
+} // class NativeRegExp
+
+class RECompiled implements Serializable
+{
+ static final long serialVersionUID = -6144956577595844213L;
+
+ char []source; /* locked source string, sans // */
+ int parenCount; /* number of parenthesized submatches */
+ int flags; /* flags */
+ byte[] program; /* regular expression bytecode */
+ int classCount; /* count [...] bitmaps */
+ RECharSet[] classList; /* list of [...] bitmaps */
+ int anchorCh = -1; /* if >= 0, then re starts with this literal char */
+}
+
+class RENode {
+
+ RENode(byte op)
+ {
+ this.op = op;
+ }
+
+ byte op; /* r.e. op bytecode */
+ RENode next; /* next in concatenation order */
+ RENode kid; /* first operand */
+
+ RENode kid2; /* second operand */
+ int num; /* could be a number */
+ int parenIndex; /* or a parenthesis index */
+
+ /* or a range */
+ int min;
+ int max;
+ int parenCount;
+ boolean greedy;
+
+ /* or a character class */
+ int startIndex;
+ int kidlen; /* length of string at kid, in chars */
+ int bmsize; /* bitmap size, based on max char code */
+ int index; /* index into class list */
+
+ /* or a literal sequence */
+ char chr; /* of one character */
+ int length; /* or many (via the index) */
+ int flatIndex; /* which is -1 if not sourced */
+
+}
+
+class CompilerState {
+
+ CompilerState(Context cx, char[] source, int length, int flags)
+ {
+ this.cx = cx;
+ this.cpbegin = source;
+ this.cp = 0;
+ this.cpend = length;
+ this.flags = flags;
+ this.parenCount = 0;
+ this.classCount = 0;
+ this.progLength = 0;
+ }
+
+ Context cx;
+ char cpbegin[];
+ int cpend;
+ int cp;
+ int flags;
+ int parenCount;
+ int parenNesting;
+ int classCount; /* number of [] encountered */
+ int progLength; /* estimated bytecode length */
+ RENode result;
+}
+
+class REProgState
+{
+ REProgState(REProgState previous, int min, int max, int index,
+ REBackTrackData backTrack,
+ int continuation_pc, int continuation_op)
+ {
+ this.previous = previous;
+ this.min = min;
+ this.max = max;
+ this.index = index;
+ this.continuation_op = continuation_op;
+ this.continuation_pc = continuation_pc;
+ this.backTrack = backTrack;
+ }
+
+ REProgState previous; // previous state in stack
+
+ int min; /* current quantifier min */
+ int max; /* current quantifier max */
+ int index; /* progress in text */
+ int continuation_op;
+ int continuation_pc;
+ REBackTrackData backTrack; // used by ASSERT_ to recover state
+}
+
+class REBackTrackData {
+
+ REBackTrackData(REGlobalData gData, int op, int pc)
+ {
+ previous = gData.backTrackStackTop;
+ continuation_op = op;
+ continuation_pc = pc;
+ lastParen = gData.lastParen;
+ if (gData.parens != null) {
+ parens = gData.parens.clone();
+ }
+ cp = gData.cp;
+ stateStackTop = gData.stateStackTop;
+ }
+
+ REBackTrackData previous;
+
+ int continuation_op; /* where to backtrack to */
+ int continuation_pc;
+ int lastParen;
+ long[] parens; /* parenthesis captures */
+ int cp; /* char buffer index */
+ REProgState stateStackTop; /* state of op that backtracked */
+}
+
+class REGlobalData {
+ boolean multiline;
+ RECompiled regexp; /* the RE in execution */
+ int lastParen; /* highest paren set so far */
+ int skipped; /* chars skipped anchoring this r.e. */
+
+ int cp; /* char buffer index */
+ long[] parens; /* parens captures */
+
+ REProgState stateStackTop; /* stack of state of current ancestors */
+
+ REBackTrackData backTrackStackTop; /* last matched-so-far position */
+
+
+ /**
+ * Get start of parenthesis capture contents, -1 for empty.
+ */
+ int parens_index(int i)
+ {
+ return (int)(parens[i]);
+ }
+
+ /**
+ * Get length of parenthesis capture contents.
+ */
+ int parens_length(int i)
+ {
+ return (int)(parens[i] >>> 32);
+ }
+
+ void set_parens(int i, int index, int length)
+ {
+ parens[i] = (index & 0xffffffffL) | ((long)length << 32);
+ }
+
+}
+
+/*
+ * This struct holds a bitmap representation of a class from a regexp.
+ * There's a list of these referenced by the classList field in the NativeRegExp
+ * struct below. The initial state has startIndex set to the offset in the
+ * original regexp source of the beginning of the class contents. The first
+ * use of the class converts the source representation into a bitmap.
+ *
+ */
+final class RECharSet implements Serializable
+{
+ static final long serialVersionUID = 7931787979395898394L;
+
+ RECharSet(int length, int startIndex, int strlength)
+ {
+ this.length = length;
+ this.startIndex = startIndex;
+ this.strlength = strlength;
+ }
+
+ int length;
+ int startIndex;
+ int strlength;
+
+ volatile transient boolean converted;
+ volatile transient boolean sense;
+ volatile transient byte[] bits;
+}
+
+
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java
new file mode 100644
index 0000000..808d62d
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java
@@ -0,0 +1,289 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ * Brendan Eich
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript.regexp;
+
+import org.mozilla.javascript.*;
+
+/**
+ * This class implements the RegExp constructor native object.
+ *
+ * Revision History:
+ * Implementation in C by Brendan Eich
+ * Initial port to Java by Norris Boyd from jsregexp.c version 1.36
+ * Merged up to version 1.38, which included Unicode support.
+ * Merged bug fixes in version 1.39.
+ * Merged JSFUN13_BRANCH changes up to 1.32.2.11
+ *
+ * @author Brendan Eich
+ * @author Norris Boyd
+ */
+class NativeRegExpCtor extends BaseFunction
+{
+ static final long serialVersionUID = -5733330028285400526L;
+
+ NativeRegExpCtor()
+ {
+ }
+
+ public String getFunctionName()
+ {
+ return "RegExp";
+ }
+
+ public Object call(Context cx, Scriptable scope, Scriptable thisObj,
+ Object[] args)
+ {
+ if (args.length > 0 && args[0] instanceof NativeRegExp &&
+ (args.length == 1 || args[1] == Undefined.instance))
+ {
+ return args[0];
+ }
+ return construct(cx, scope, args);
+ }
+
+ public Scriptable construct(Context cx, Scriptable scope, Object[] args)
+ {
+ NativeRegExp re = new NativeRegExp();
+ re.compile(cx, scope, args);
+ ScriptRuntime.setObjectProtoAndParent(re, scope);
+ return re;
+ }
+
+ private static RegExpImpl getImpl()
+ {
+ Context cx = Context.getCurrentContext();
+ return (RegExpImpl) ScriptRuntime.getRegExpProxy(cx);
+ }
+
+// #string_id_map#
+
+ private static final int
+ Id_multiline = 1,
+ Id_STAR = 2, // #string=$*#
+
+ Id_input = 3,
+ Id_UNDERSCORE = 4, // #string=$_#
+
+ Id_lastMatch = 5,
+ Id_AMPERSAND = 6, // #string=$&#
+
+ Id_lastParen = 7,
+ Id_PLUS = 8, // #string=$+#
+
+ Id_leftContext = 9,
+ Id_BACK_QUOTE = 10, // #string=$`#
+
+ Id_rightContext = 11,
+ Id_QUOTE = 12, // #string=$'#
+
+ DOLLAR_ID_BASE = 12;
+
+ private static final int
+ Id_DOLLAR_1 = DOLLAR_ID_BASE + 1, // #string=$1#
+ Id_DOLLAR_2 = DOLLAR_ID_BASE + 2, // #string=$2#
+ Id_DOLLAR_3 = DOLLAR_ID_BASE + 3, // #string=$3#
+ Id_DOLLAR_4 = DOLLAR_ID_BASE + 4, // #string=$4#
+ Id_DOLLAR_5 = DOLLAR_ID_BASE + 5, // #string=$5#
+ Id_DOLLAR_6 = DOLLAR_ID_BASE + 6, // #string=$6#
+ Id_DOLLAR_7 = DOLLAR_ID_BASE + 7, // #string=$7#
+ Id_DOLLAR_8 = DOLLAR_ID_BASE + 8, // #string=$8#
+ Id_DOLLAR_9 = DOLLAR_ID_BASE + 9, // #string=$9#
+
+ MAX_INSTANCE_ID = DOLLAR_ID_BASE + 9;
+
+ protected int getMaxInstanceId()
+ {
+ return super.getMaxInstanceId() + MAX_INSTANCE_ID;
+ }
+
+ protected int findInstanceIdInfo(String s) {
+ int id;
+// #generated# Last update: 2001-05-24 16:09:31 GMT+02:00
+ L0: { id = 0; String X = null; int c;
+ L: switch (s.length()) {
+ case 2: switch (s.charAt(1)) {
+ case '&': if (s.charAt(0)=='$') {id=Id_AMPERSAND; break L0;} break L;
+ case '\'': if (s.charAt(0)=='$') {id=Id_QUOTE; break L0;} break L;
+ case '*': if (s.charAt(0)=='$') {id=Id_STAR; break L0;} break L;
+ case '+': if (s.charAt(0)=='$') {id=Id_PLUS; break L0;} break L;
+ case '1': if (s.charAt(0)=='$') {id=Id_DOLLAR_1; break L0;} break L;
+ case '2': if (s.charAt(0)=='$') {id=Id_DOLLAR_2; break L0;} break L;
+ case '3': if (s.charAt(0)=='$') {id=Id_DOLLAR_3; break L0;} break L;
+ case '4': if (s.charAt(0)=='$') {id=Id_DOLLAR_4; break L0;} break L;
+ case '5': if (s.charAt(0)=='$') {id=Id_DOLLAR_5; break L0;} break L;
+ case '6': if (s.charAt(0)=='$') {id=Id_DOLLAR_6; break L0;} break L;
+ case '7': if (s.charAt(0)=='$') {id=Id_DOLLAR_7; break L0;} break L;
+ case '8': if (s.charAt(0)=='$') {id=Id_DOLLAR_8; break L0;} break L;
+ case '9': if (s.charAt(0)=='$') {id=Id_DOLLAR_9; break L0;} break L;
+ case '_': if (s.charAt(0)=='$') {id=Id_UNDERSCORE; break L0;} break L;
+ case '`': if (s.charAt(0)=='$') {id=Id_BACK_QUOTE; break L0;} break L;
+ } break L;
+ case 5: X="input";id=Id_input; break L;
+ case 9: c=s.charAt(4);
+ if (c=='M') { X="lastMatch";id=Id_lastMatch; }
+ else if (c=='P') { X="lastParen";id=Id_lastParen; }
+ else if (c=='i') { X="multiline";id=Id_multiline; }
+ break L;
+ case 11: X="leftContext";id=Id_leftContext; break L;
+ case 12: X="rightContext";id=Id_rightContext; break L;
+ }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ }
+// #/generated#
+
+ if (id == 0) return super.findInstanceIdInfo(s);
+
+ int attr;
+ switch (id) {
+ case Id_multiline:
+ case Id_STAR:
+ case Id_input:
+ case Id_UNDERSCORE:
+ attr = PERMANENT;
+ break;
+ default:
+ attr = PERMANENT | READONLY;
+ break;
+ }
+
+ return instanceIdInfo(attr, super.getMaxInstanceId() + id);
+ }
+
+// #/string_id_map#
+
+ protected String getInstanceIdName(int id)
+ {
+ int shifted = id - super.getMaxInstanceId();
+ if (1 <= shifted && shifted <= MAX_INSTANCE_ID) {
+ switch (shifted) {
+ case Id_multiline: return "multiline";
+ case Id_STAR: return "$*";
+
+ case Id_input: return "input";
+ case Id_UNDERSCORE: return "$_";
+
+ case Id_lastMatch: return "lastMatch";
+ case Id_AMPERSAND: return "$&";
+
+ case Id_lastParen: return "lastParen";
+ case Id_PLUS: return "$+";
+
+ case Id_leftContext: return "leftContext";
+ case Id_BACK_QUOTE: return "$`";
+
+ case Id_rightContext: return "rightContext";
+ case Id_QUOTE: return "$'";
+ }
+ // Must be one of $1..$9, convert to 0..8
+ int substring_number = shifted - DOLLAR_ID_BASE - 1;
+ char[] buf = { '$', (char)('1' + substring_number) };
+ return new String(buf);
+ }
+ return super.getInstanceIdName(id);
+ }
+
+ protected Object getInstanceIdValue(int id)
+ {
+ int shifted = id - super.getMaxInstanceId();
+ if (1 <= shifted && shifted <= MAX_INSTANCE_ID) {
+ RegExpImpl impl = getImpl();
+ Object stringResult;
+ switch (shifted) {
+ case Id_multiline:
+ case Id_STAR:
+ return ScriptRuntime.wrapBoolean(impl.multiline);
+
+ case Id_input:
+ case Id_UNDERSCORE:
+ stringResult = impl.input;
+ break;
+
+ case Id_lastMatch:
+ case Id_AMPERSAND:
+ stringResult = impl.lastMatch;
+ break;
+
+ case Id_lastParen:
+ case Id_PLUS:
+ stringResult = impl.lastParen;
+ break;
+
+ case Id_leftContext:
+ case Id_BACK_QUOTE:
+ stringResult = impl.leftContext;
+ break;
+
+ case Id_rightContext:
+ case Id_QUOTE:
+ stringResult = impl.rightContext;
+ break;
+
+ default:
+ {
+ // Must be one of $1..$9, convert to 0..8
+ int substring_number = shifted - DOLLAR_ID_BASE - 1;
+ stringResult = impl.getParenSubString(substring_number);
+ break;
+ }
+ }
+ return (stringResult == null) ? "" : stringResult.toString();
+ }
+ return super.getInstanceIdValue(id);
+ }
+
+ protected void setInstanceIdValue(int id, Object value)
+ {
+ int shifted = id - super.getMaxInstanceId();
+ switch (shifted) {
+ case Id_multiline:
+ case Id_STAR:
+ getImpl().multiline = ScriptRuntime.toBoolean(value);
+ return;
+
+ case Id_input:
+ case Id_UNDERSCORE:
+ getImpl().input = ScriptRuntime.toString(value);
+ return;
+ }
+ super.setInstanceIdValue(id, value);
+ }
+
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/regexp/RegExpImpl.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/regexp/RegExpImpl.java
new file mode 100644
index 0000000..4b0a303
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/regexp/RegExpImpl.java
@@ -0,0 +1,541 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript.regexp;
+
+import org.mozilla.javascript.*;
+
+/**
+ *
+ */
+public class RegExpImpl implements RegExpProxy {
+
+ public boolean isRegExp(Scriptable obj) {
+ return obj instanceof NativeRegExp;
+ }
+
+ public Object compileRegExp(Context cx, String source, String flags)
+ {
+ return NativeRegExp.compileRE(cx, source, flags, false);
+ }
+
+ public Scriptable wrapRegExp(Context cx, Scriptable scope,
+ Object compiled)
+ {
+ return new NativeRegExp(scope, compiled);
+ }
+
+ public Object action(Context cx, Scriptable scope,
+ Scriptable thisObj, Object[] args,
+ int actionType)
+ {
+ GlobData data = new GlobData();
+ data.mode = actionType;
+
+ switch (actionType) {
+ case RA_MATCH:
+ {
+ Object rval;
+ data.optarg = 1;
+ rval = matchOrReplace(cx, scope, thisObj, args,
+ this, data, false);
+ return data.arrayobj == null ? rval : data.arrayobj;
+ }
+
+ case RA_SEARCH:
+ data.optarg = 1;
+ return matchOrReplace(cx, scope, thisObj, args,
+ this, data, false);
+
+ case RA_REPLACE:
+ {
+ Object arg1 = args.length < 2 ? Undefined.instance : args[1];
+ String repstr = null;
+ Function lambda = null;
+ if (arg1 instanceof Function) {
+ lambda = (Function) arg1;
+ } else {
+ repstr = ScriptRuntime.toString(arg1);
+ }
+
+ data.optarg = 2;
+ data.lambda = lambda;
+ data.repstr = repstr;
+ data.dollar = repstr == null ? -1 : repstr.indexOf('$');
+ data.charBuf = null;
+ data.leftIndex = 0;
+ Object val = matchOrReplace(cx, scope, thisObj, args,
+ this, data, true);
+ SubString rc = this.rightContext;
+
+ if (data.charBuf == null) {
+ if (data.global || val == null
+ || !val.equals(Boolean.TRUE))
+ {
+ /* Didn't match even once. */
+ return data.str;
+ }
+ SubString lc = this.leftContext;
+ replace_glob(data, cx, scope, this, lc.index, lc.length);
+ }
+ data.charBuf.append(rc.charArray, rc.index, rc.length);
+ return data.charBuf.toString();
+ }
+
+ default:
+ throw Kit.codeBug();
+ }
+ }
+
+ /**
+ * Analog of C match_or_replace.
+ */
+ private static Object matchOrReplace(Context cx, Scriptable scope,
+ Scriptable thisObj, Object[] args,
+ RegExpImpl reImpl,
+ GlobData data, boolean forceFlat)
+ {
+ NativeRegExp re;
+
+ String str = ScriptRuntime.toString(thisObj);
+ data.str = str;
+ Scriptable topScope = ScriptableObject.getTopLevelScope(scope);
+
+ if (args.length == 0) {
+ Object compiled = NativeRegExp.compileRE(cx, "", "", false);
+ re = new NativeRegExp(topScope, compiled);
+ } else if (args[0] instanceof NativeRegExp) {
+ re = (NativeRegExp) args[0];
+ } else {
+ String src = ScriptRuntime.toString(args[0]);
+ String opt;
+ if (data.optarg < args.length) {
+ args[0] = src;
+ opt = ScriptRuntime.toString(args[data.optarg]);
+ } else {
+ opt = null;
+ }
+ Object compiled = NativeRegExp.compileRE(cx, src, opt, forceFlat);
+ re = new NativeRegExp(topScope, compiled);
+ }
+ data.regexp = re;
+
+ data.global = (re.getFlags() & NativeRegExp.JSREG_GLOB) != 0;
+ int[] indexp = { 0 };
+ Object result = null;
+ if (data.mode == RA_SEARCH) {
+ result = re.executeRegExp(cx, scope, reImpl,
+ str, indexp, NativeRegExp.TEST);
+ if (result != null && result.equals(Boolean.TRUE))
+ result = new Integer(reImpl.leftContext.length);
+ else
+ result = new Integer(-1);
+ } else if (data.global) {
+ re.lastIndex = 0;
+ for (int count = 0; indexp[0] <= str.length(); count++) {
+ result = re.executeRegExp(cx, scope, reImpl,
+ str, indexp, NativeRegExp.TEST);
+ if (result == null || !result.equals(Boolean.TRUE))
+ break;
+ if (data.mode == RA_MATCH) {
+ match_glob(data, cx, scope, count, reImpl);
+ } else {
+ if (data.mode != RA_REPLACE) Kit.codeBug();
+ SubString lastMatch = reImpl.lastMatch;
+ int leftIndex = data.leftIndex;
+ int leftlen = lastMatch.index - leftIndex;
+ data.leftIndex = lastMatch.index + lastMatch.length;
+ replace_glob(data, cx, scope, reImpl, leftIndex, leftlen);
+ }
+ if (reImpl.lastMatch.length == 0) {
+ if (indexp[0] == str.length())
+ break;
+ indexp[0]++;
+ }
+ }
+ } else {
+ result = re.executeRegExp(cx, scope, reImpl, str, indexp,
+ ((data.mode == RA_REPLACE)
+ ? NativeRegExp.TEST
+ : NativeRegExp.MATCH));
+ }
+
+ return result;
+ }
+
+
+
+ public int find_split(Context cx, Scriptable scope, String target,
+ String separator, Scriptable reObj,
+ int[] ip, int[] matchlen,
+ boolean[] matched, String[][] parensp)
+ {
+ int i = ip[0];
+ int length = target.length();
+ int result;
+
+ int version = cx.getLanguageVersion();
+ NativeRegExp re = (NativeRegExp) reObj;
+ again:
+ while (true) { // imitating C label
+ /* JS1.2 deviated from Perl by never matching at end of string. */
+ int ipsave = ip[0]; // reuse ip to save object creation
+ ip[0] = i;
+ Object ret = re.executeRegExp(cx, scope, this, target, ip,
+ NativeRegExp.TEST);
+ if (ret != Boolean.TRUE) {
+ // Mismatch: ensure our caller advances i past end of string.
+ ip[0] = ipsave;
+ matchlen[0] = 1;
+ matched[0] = false;
+ return length;
+ }
+ i = ip[0];
+ ip[0] = ipsave;
+ matched[0] = true;
+
+ SubString sep = this.lastMatch;
+ matchlen[0] = sep.length;
+ if (matchlen[0] == 0) {
+ /*
+ * Empty string match: never split on an empty
+ * match at the start of a find_split cycle. Same
+ * rule as for an empty global match in
+ * match_or_replace.
+ */
+ if (i == ip[0]) {
+ /*
+ * "Bump-along" to avoid sticking at an empty
+ * match, but don't bump past end of string --
+ * our caller must do that by adding
+ * sep->length to our return value.
+ */
+ if (i == length) {
+ if (version == Context.VERSION_1_2) {
+ matchlen[0] = 1;
+ result = i;
+ }
+ else
+ result = -1;
+ break;
+ }
+ i++;
+ continue again; // imitating C goto
+ }
+ }
+ // PR_ASSERT((size_t)i >= sep->length);
+ result = i - matchlen[0];
+ break;
+ }
+ int size = (parens == null) ? 0 : parens.length;
+ parensp[0] = new String[size];
+ for (int num = 0; num < size; num++) {
+ SubString parsub = getParenSubString(num);
+ parensp[0][num] = parsub.toString();
+ }
+ return result;
+ }
+
+ /**
+ * Analog of REGEXP_PAREN_SUBSTRING in C jsregexp.h.
+ * Assumes zero-based; i.e., for $3, i==2
+ */
+ SubString getParenSubString(int i)
+ {
+ if (parens != null && i < parens.length) {
+ SubString parsub = parens[i];
+ if (parsub != null) {
+ return parsub;
+ }
+ }
+ return SubString.emptySubString;
+ }
+
+ /*
+ * Analog of match_glob() in jsstr.c
+ */
+ private static void match_glob(GlobData mdata, Context cx,
+ Scriptable scope, int count,
+ RegExpImpl reImpl)
+ {
+ if (mdata.arrayobj == null) {
+ Scriptable s = ScriptableObject.getTopLevelScope(scope);
+ mdata.arrayobj = ScriptRuntime.newObject(cx, s, "Array", null);
+ }
+ SubString matchsub = reImpl.lastMatch;
+ String matchstr = matchsub.toString();
+ mdata.arrayobj.put(count, mdata.arrayobj, matchstr);
+ }
+
+ /*
+ * Analog of replace_glob() in jsstr.c
+ */
+ private static void replace_glob(GlobData rdata, Context cx,
+ Scriptable scope, RegExpImpl reImpl,
+ int leftIndex, int leftlen)
+ {
+ int replen;
+ String lambdaStr;
+ if (rdata.lambda != null) {
+ // invoke lambda function with args lastMatch, $1, $2, ... $n,
+ // leftContext.length, whole string.
+ SubString[] parens = reImpl.parens;
+ int parenCount = (parens == null) ? 0 : parens.length;
+ Object[] args = new Object[parenCount + 3];
+ args[0] = reImpl.lastMatch.toString();
+ for (int i=0; i < parenCount; i++) {
+ SubString sub = parens[i];
+ if (sub != null) {
+ args[i+1] = sub.toString();
+ } else {
+ args[i+1] = Undefined.instance;
+ }
+ }
+ args[parenCount+1] = new Integer(reImpl.leftContext.length);
+ args[parenCount+2] = rdata.str;
+ // This is a hack to prevent expose of reImpl data to
+ // JS function which can run new regexps modifing
+ // regexp that are used later by the engine.
+ // TODO: redesign is necessary
+ if (reImpl != ScriptRuntime.getRegExpProxy(cx)) Kit.codeBug();
+ RegExpImpl re2 = new RegExpImpl();
+ re2.multiline = reImpl.multiline;
+ re2.input = reImpl.input;
+ ScriptRuntime.setRegExpProxy(cx, re2);
+ try {
+ Scriptable parent = ScriptableObject.getTopLevelScope(scope);
+ Object result = rdata.lambda.call(cx, parent, parent, args);
+ lambdaStr = ScriptRuntime.toString(result);
+ } finally {
+ ScriptRuntime.setRegExpProxy(cx, reImpl);
+ }
+ replen = lambdaStr.length();
+ } else {
+ lambdaStr = null;
+ replen = rdata.repstr.length();
+ if (rdata.dollar >= 0) {
+ int[] skip = new int[1];
+ int dp = rdata.dollar;
+ do {
+ SubString sub = interpretDollar(cx, reImpl, rdata.repstr,
+ dp, skip);
+ if (sub != null) {
+ replen += sub.length - skip[0];
+ dp += skip[0];
+ } else {
+ ++dp;
+ }
+ dp = rdata.repstr.indexOf('$', dp);
+ } while (dp >= 0);
+ }
+ }
+
+ int growth = leftlen + replen + reImpl.rightContext.length;
+ StringBuffer charBuf = rdata.charBuf;
+ if (charBuf == null) {
+ charBuf = new StringBuffer(growth);
+ rdata.charBuf = charBuf;
+ } else {
+ charBuf.ensureCapacity(rdata.charBuf.length() + growth);
+ }
+
+ charBuf.append(reImpl.leftContext.charArray, leftIndex, leftlen);
+ if (rdata.lambda != null) {
+ charBuf.append(lambdaStr);
+ } else {
+ do_replace(rdata, cx, reImpl);
+ }
+ }
+
+ private static SubString interpretDollar(Context cx, RegExpImpl res,
+ String da, int dp, int[] skip)
+ {
+ char dc;
+ int num, tmp;
+
+ if (da.charAt(dp) != '$') Kit.codeBug();
+
+ /* Allow a real backslash (literal "\\") to escape "$1" etc. */
+ int version = cx.getLanguageVersion();
+ if (version != Context.VERSION_DEFAULT
+ && version <= Context.VERSION_1_4)
+ {
+ if (dp > 0 && da.charAt(dp - 1) == '\\')
+ return null;
+ }
+ int daL = da.length();
+ if (dp + 1 >= daL)
+ return null;
+ /* Interpret all Perl match-induced dollar variables. */
+ dc = da.charAt(dp + 1);
+ if (NativeRegExp.isDigit(dc)) {
+ int cp;
+ if (version != Context.VERSION_DEFAULT
+ && version <= Context.VERSION_1_4)
+ {
+ if (dc == '0')
+ return null;
+ /* Check for overflow to avoid gobbling arbitrary decimal digits. */
+ num = 0;
+ cp = dp;
+ while (++cp < daL && NativeRegExp.isDigit(dc = da.charAt(cp)))
+ {
+ tmp = 10 * num + (dc - '0');
+ if (tmp < num)
+ break;
+ num = tmp;
+ }
+ }
+ else { /* ECMA 3, 1-9 or 01-99 */
+ int parenCount = (res.parens == null) ? 0 : res.parens.length;
+ num = dc - '0';
+ if (num > parenCount)
+ return null;
+ cp = dp + 2;
+ if ((dp + 2) < daL) {
+ dc = da.charAt(dp + 2);
+ if (NativeRegExp.isDigit(dc)) {
+ tmp = 10 * num + (dc - '0');
+ if (tmp <= parenCount) {
+ cp++;
+ num = tmp;
+ }
+ }
+ }
+ if (num == 0) return null; /* $0 or $00 is not valid */
+ }
+ /* Adjust num from 1 $n-origin to 0 array-index-origin. */
+ num--;
+ skip[0] = cp - dp;
+ return res.getParenSubString(num);
+ }
+
+ skip[0] = 2;
+ switch (dc) {
+ case '$':
+ return new SubString("$");
+ case '&':
+ return res.lastMatch;
+ case '+':
+ return res.lastParen;
+ case '`':
+ if (version == Context.VERSION_1_2) {
+ /*
+ * JS1.2 imitated the Perl4 bug where left context at each step
+ * in an iterative use of a global regexp started from last match,
+ * not from the start of the target string. But Perl4 does start
+ * $` at the beginning of the target string when it is used in a
+ * substitution, so we emulate that special case here.
+ */
+ res.leftContext.index = 0;
+ res.leftContext.length = res.lastMatch.index;
+ }
+ return res.leftContext;
+ case '\'':
+ return res.rightContext;
+ }
+ return null;
+ }
+
+ /**
+ * Analog of do_replace in jsstr.c
+ */
+ private static void do_replace(GlobData rdata, Context cx,
+ RegExpImpl regExpImpl)
+ {
+ StringBuffer charBuf = rdata.charBuf;
+ int cp = 0;
+ String da = rdata.repstr;
+ int dp = rdata.dollar;
+ if (dp != -1) {
+ int[] skip = new int[1];
+ do {
+ int len = dp - cp;
+ charBuf.append(da.substring(cp, dp));
+ cp = dp;
+ SubString sub = interpretDollar(cx, regExpImpl, da,
+ dp, skip);
+ if (sub != null) {
+ len = sub.length;
+ if (len > 0) {
+ charBuf.append(sub.charArray, sub.index, len);
+ }
+ cp += skip[0];
+ dp += skip[0];
+ } else {
+ ++dp;
+ }
+ dp = da.indexOf('$', dp);
+ } while (dp >= 0);
+ }
+ int daL = da.length();
+ if (daL > cp) {
+ charBuf.append(da.substring(cp, daL));
+ }
+ }
+
+ String input; /* input string to match (perl $_, GC root) */
+ boolean multiline; /* whether input contains newlines (perl $*) */
+ SubString[] parens; /* Vector of SubString; last set of parens
+ matched (perl $1, $2) */
+ SubString lastMatch; /* last string matched (perl $&) */
+ SubString lastParen; /* last paren matched (perl $+) */
+ SubString leftContext; /* input to left of last match (perl $`) */
+ SubString rightContext; /* input to right of last match (perl $') */
+}
+
+
+final class GlobData
+{
+ int mode; /* input: return index, match object, or void */
+ int optarg; /* input: index of optional flags argument */
+ boolean global; /* output: whether regexp was global */
+ String str; /* output: 'this' parameter object as string */
+ NativeRegExp regexp;/* output: regexp parameter object private data */
+
+ // match-specific data
+
+ Scriptable arrayobj;
+
+ // replace-specific data
+
+ Function lambda; /* replacement function object or null */
+ String repstr; /* replacement string */
+ int dollar = -1; /* -1 or index of first $ in repstr */
+ StringBuffer charBuf; /* result characters, null initially */
+ int leftIndex; /* leftContext index, always 0 for JS1.2 */
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/regexp/SubString.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/regexp/SubString.java
new file mode 100644
index 0000000..00905ca
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/regexp/SubString.java
@@ -0,0 +1,75 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript.regexp;
+
+class SubString {
+
+ public SubString()
+ {
+ }
+
+ public SubString(String str)
+ {
+ index = 0;
+ charArray = str.toCharArray();
+ length = str.length();
+ }
+
+ public SubString(char[] source, int start, int len)
+ {
+ // there must be a better way of doing this??
+ index = 0;
+ length = len;
+ charArray = new char[len];
+ for (int j = 0; j < len; j++)
+ charArray[j] = source[start + j];
+ }
+
+ public String toString() {
+ return charArray == null
+ ? ""
+ : new String(charArray, index, length);
+ }
+
+ static final SubString emptySubString = new SubString();
+
+ char[] charArray;
+ int index;
+ int length;
+}
+
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/resources/Messages.properties b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/resources/Messages.properties
new file mode 100644
index 0000000..fd869c1
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/resources/Messages.properties
@@ -0,0 +1,778 @@
+#
+# Default JavaScript messages file.
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Rhino code, released
+# May 6, 1999.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 1997-1999
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Norris Boyd
+# Bob Jervis
+#
+# Alternatively, the contents of this file may be used under the terms of
+# the GNU General Public License Version 2 or later (the "GPL"), in which
+# case the provisions of the GPL are applicable instead of those above. If
+# you wish to allow use of your version of this file only under the terms of
+# the GPL and not to allow others to use your version of this file under the
+# MPL, indicate your decision by deleting the provisions above and replacing
+# them with the notice and other provisions required by the GPL. If you do
+# not delete the provisions above, a recipient may use your version of this
+# file under either the MPL or the GPL.
+#
+# ***** END LICENSE BLOCK *****
+
+# This is replaced during jar assembly from property string
+# and should not be translated
+implementation.version = @IMPLEMENTATION.VERSION@
+
+#
+# To add JavaScript error messages for a particular locale, create a
+# new Messages_[locale].properties file, where [locale] is the Java
+# string abbreviation for that locale. For example, JavaScript
+# messages for the Polish locale should be located in
+# Messages_pl.properties, and messages for the Italian Swiss locale
+# should be located in Messages_it_CH.properties. Message properties
+# files should be accessible through the classpath under
+# org.mozilla.javascript.resources
+#
+# See:
+# java.util.ResourceBundle
+# java.text.MessageFormat
+#
+
+# SomeJavaClassWhereUsed
+
+# Codegen
+msg.dup.parms =\
+ Duplicate parameter name "{0}".
+
+msg.too.big.jump =\
+ Program too complex: too big jump offset.
+
+msg.too.big.index =\
+ Program too complex: internal index exceeds 64K limit.
+
+msg.while.compiling.fn =\
+ Encountered code generation error while compiling function "{0}": {1}
+
+msg.while.compiling.script =\
+ Encountered code generation error while compiling script: {0}
+
+# Context
+msg.ctor.not.found =\
+ Constructor for "{0}" not found.
+
+msg.not.ctor =\
+ "{0}" is not a constructor.
+
+# FunctionObject
+msg.varargs.ctor =\
+ Method or constructor "{0}" must be static with the signature \
+ "(Context cx, Object[] args, Function ctorObj, boolean inNewExpr)" \
+ to define a variable arguments constructor.
+
+msg.varargs.fun =\
+ Method "{0}" must be static with the signature \
+ "(Context cx, Scriptable thisObj, Object[] args, Function funObj)" \
+ to define a variable arguments function.
+
+msg.incompat.call =\
+ Method "{0}" called on incompatible object.
+
+msg.bad.parms =\
+ Unsupported parameter type "{0}" in method "{1}".
+
+msg.bad.method.return =\
+ Unsupported return type "{0}" in method "{1}".
+
+msg.bad.ctor.return =\
+ Construction of objects of type "{0}" is not supported.
+
+msg.no.overload =\
+ Method "{0}" occurs multiple times in class "{1}".
+
+msg.method.not.found =\
+ Method "{0}" not found in "{1}".
+
+# IRFactory
+
+msg.bad.for.in.lhs =\
+ Invalid left-hand side of for..in loop.
+
+msg.mult.index =\
+ Only one variable allowed in for..in loop.
+
+msg.bad.for.in.destruct =\
+ Left hand side of for..in loop must be an array of length 2 to accept \
+ key/value pair.
+
+msg.cant.convert =\
+ Can''t convert to type "{0}".
+
+msg.bad.assign.left =\
+ Invalid assignment left-hand side.
+
+msg.bad.decr =\
+ Invalid decerement operand.
+
+msg.bad.incr =\
+ Invalid increment operand.
+
+msg.bad.yield =\
+ yield must be in a function.
+
+msg.yield.parenthesized =\
+ yield expression must be parenthesized.
+
+# NativeGlobal
+msg.cant.call.indirect =\
+ Function "{0}" must be called directly, and not by way of a \
+ function of another name.
+
+msg.eval.nonstring =\
+ Calling eval() with anything other than a primitive string value will \
+ simply return the value. Is this what you intended?
+
+msg.eval.nonstring.strict =\
+ Calling eval() with anything other than a primitive string value is not \
+ allowed in strict mode.
+
+msg.bad.destruct.op =\
+ Invalid destructuring assignment operator
+
+# NativeCall
+msg.only.from.new =\
+ "{0}" may only be invoked from a "new" expression.
+
+msg.deprec.ctor =\
+ The "{0}" constructor is deprecated.
+
+# NativeFunction
+msg.no.function.ref.found =\
+ no source found to decompile function reference {0}
+
+msg.arg.isnt.array =\
+ second argument to Function.prototype.apply must be an array
+
+# NativeGlobal
+msg.bad.esc.mask =\
+ invalid string escape mask
+
+# NativeJavaClass
+msg.cant.instantiate =\
+ error instantiating ({0}): class {1} is interface or abstract
+
+msg.bad.ctor.sig =\
+ Found constructor with wrong signature: \
+ {0} calling {1} with signature {2}
+
+msg.not.java.obj =\
+ Expected argument to getClass() to be a Java object.
+
+msg.no.java.ctor =\
+ Java constructor for "{0}" with arguments "{1}" not found.
+
+# NativeJavaMethod
+msg.method.ambiguous =\
+ The choice of Java method {0}.{1} matching JavaScript argument types ({2}) is ambiguous; \
+ candidate methods are: {3}
+
+msg.constructor.ambiguous =\
+ The choice of Java constructor {0} matching JavaScript argument types ({1}) is ambiguous; \
+ candidate constructors are: {2}
+
+# NativeJavaObject
+msg.conversion.not.allowed =\
+ Cannot convert {0} to {1}
+
+msg.no.empty.interface.conversion =\
+ Cannot convert {0} to interface {1} with no methods
+
+msg.no.function.interface.conversion =\
+ Cannot convert function {0} to interface since it contains methods with \
+ different signatures
+
+# NativeJavaPackage
+msg.not.classloader =\
+ Constructor for "Packages" expects argument of type java.lang.Classloader
+
+# NativeRegExp
+msg.bad.quant =\
+ Invalid quantifier {0}
+
+msg.overlarge.backref =\
+ Overly large back reference {0}
+
+msg.overlarge.min =\
+ Overly large minimum {0}
+
+msg.overlarge.max =\
+ Overly large maximum {0}
+
+msg.zero.quant =\
+ Zero quantifier {0}
+
+msg.max.lt.min =\
+ Maximum {0} less than minimum
+
+msg.unterm.quant =\
+ Unterminated quantifier {0}
+
+msg.unterm.paren =\
+ Unterminated parenthetical {0}
+
+msg.unterm.class =\
+ Unterminated character class {0}
+
+msg.bad.range =\
+ Invalid range in character class.
+
+msg.trail.backslash =\
+ Trailing \\ in regular expression.
+
+msg.re.unmatched.right.paren =\
+ unmatched ) in regular expression.
+
+msg.no.regexp =\
+ Regular expressions are not available.
+
+msg.bad.backref =\
+ back-reference exceeds number of capturing parentheses.
+
+msg.bad.regexp.compile =\
+ Only one argument may be specified if the first argument to \
+ RegExp.prototype.compile is a RegExp object.
+
+# Parser
+msg.got.syntax.errors = \
+ Compilation produced {0} syntax errors.
+
+msg.var.redecl =\
+ TypeError: redeclaration of var {0}.
+
+msg.const.redecl =\
+ TypeError: redeclaration of const {0}.
+
+msg.let.redecl =\
+ TypeError: redeclaration of variable {0}.
+
+msg.parm.redecl =\
+ TypeError: redeclaration of formal parameter {0}.
+
+msg.fn.redecl =\
+ TypeError: redeclaration of function {0}.
+
+# NodeTransformer
+msg.dup.label =\
+ duplicated label
+
+msg.undef.label =\
+ undefined label
+
+msg.bad.break =\
+ unlabelled break must be inside loop or switch
+
+msg.continue.outside =\
+ continue must be inside loop
+
+msg.continue.nonloop =\
+ continue can only use labeles of iteration statements
+
+msg.bad.throw.eol =\
+ Line terminator is not allowed between the throw keyword and throw \
+ expression.
+
+msg.no.paren.parms =\
+ missing ( before function parameters.
+
+msg.no.parm =\
+ missing formal parameter
+
+msg.no.paren.after.parms =\
+ missing ) after formal parameters
+
+msg.no.brace.body =\
+ missing '{' before function body
+
+msg.no.brace.after.body =\
+ missing } after function body
+
+msg.no.paren.cond =\
+ missing ( before condition
+
+msg.no.paren.after.cond =\
+ missing ) after condition
+
+msg.no.semi.stmt =\
+ missing ; before statement
+
+msg.no.name.after.dot =\
+ missing name after . operator
+
+msg.no.name.after.coloncolon =\
+ missing name after :: operator
+
+msg.no.name.after.dotdot =\
+ missing name after .. operator
+
+msg.no.name.after.xmlAttr =\
+ missing name after .@
+
+msg.no.bracket.index =\
+ missing ] in index expression
+
+msg.no.paren.switch =\
+ missing ( before switch expression
+
+msg.no.paren.after.switch =\
+ missing ) after switch expression
+
+msg.no.brace.switch =\
+ missing '{' before switch body
+
+msg.bad.switch =\
+ invalid switch statement
+
+msg.no.colon.case =\
+ missing : after case expression
+
+msg.double.switch.default =\
+ double default label in the switch statement
+
+msg.no.while.do =\
+ missing while after do-loop body
+
+msg.no.paren.for =\
+ missing ( after for
+
+msg.no.semi.for =\
+ missing ; after for-loop initializer
+
+msg.no.semi.for.cond =\
+ missing ; after for-loop condition
+
+msg.in.after.for.name =\
+ missing in after for
+
+msg.no.paren.for.ctrl =\
+ missing ) after for-loop control
+
+msg.no.paren.with =\
+ missing ( before with-statement object
+
+msg.no.paren.after.with =\
+ missing ) after with-statement object
+
+msg.no.paren.after.let =\
+ missing ( after let
+
+msg.no.paren.let =\
+ missing ) after variable list
+
+msg.no.curly.let =\
+ missing } after let statement
+
+msg.bad.return =\
+ invalid return
+
+msg.no.brace.block =\
+ missing } in compound statement
+
+msg.bad.label =\
+ invalid label
+
+msg.bad.var =\
+ missing variable name
+
+msg.bad.var.init =\
+ invalid variable initialization
+
+msg.no.colon.cond =\
+ missing : in conditional expression
+
+msg.no.paren.arg =\
+ missing ) after argument list
+
+msg.no.bracket.arg =\
+ missing ] after element list
+
+msg.bad.prop =\
+ invalid property id
+
+msg.no.colon.prop =\
+ missing : after property id
+
+msg.no.brace.prop =\
+ missing } after property list
+
+msg.no.paren =\
+ missing ) in parenthetical
+
+msg.reserved.id =\
+ identifier is a reserved word
+
+msg.no.paren.catch =\
+ missing ( before catch-block condition
+
+msg.bad.catchcond =\
+ invalid catch block condition
+
+msg.catch.unreachable =\
+ any catch clauses following an unqualified catch are unreachable
+
+msg.no.brace.try =\
+ missing '{' before try block
+
+msg.no.brace.catchblock =\
+ missing '{' before catch-block body
+
+msg.try.no.catchfinally =\
+ ''try'' without ''catch'' or ''finally''
+
+msg.no.return.value =\
+ function {0} does not always return a value
+
+msg.anon.no.return.value =\
+ anonymous function does not always return a value
+
+msg.return.inconsistent =\
+ return statement is inconsistent with previous usage
+
+msg.generator.returns =\
+ TypeError: generator function {0} returns a value
+
+msg.anon.generator.returns =\
+ TypeError: anonymous generator function returns a value
+
+msg.syntax =\
+ syntax error
+
+msg.unexpected.eof =\
+ Unexpected end of file
+
+msg.XML.bad.form =\
+ illegally formed XML syntax
+
+msg.XML.not.available =\
+ XML runtime not available
+
+msg.too.deep.parser.recursion =\
+ Too deep recursion while parsing
+
+msg.no.side.effects =\
+ Code has no side effects
+
+msg.extra.trailing.comma =\
+ Trailing comma is not legal in an ECMA-262 object initializer
+
+msg.equal.as.assign =\
+ Test for equality (==) mistyped as assignment (=)?
+
+msg.var.hides.arg =\
+ Variable {0} hides argument
+
+msg.destruct.assign.no.init =\
+ Missing = in destructuring declaration
+
+# ScriptRuntime
+msg.no.properties =\
+ {0} has no properties.
+
+msg.invalid.iterator =\
+ Invalid iterator value
+
+msg.iterator.primitive =\
+ __iterator__ returned a primitive value
+
+msg.assn.create.strict =\
+ Assignment to undeclared variable {0}
+
+msg.ref.undefined.prop =\
+ Reference to undefined property "{0}"
+
+msg.prop.not.found =\
+ Property {0} not found.
+
+msg.invalid.type =\
+ Invalid JavaScript value of type {0}
+
+msg.primitive.expected =\
+ Primitive type expected (had {0} instead)
+
+msg.namespace.expected =\
+ Namespace object expected to left of :: (found {0} instead)
+
+msg.null.to.object =\
+ Cannot convert null to an object.
+
+msg.undef.to.object =\
+ Cannot convert undefined to an object.
+
+msg.cyclic.value =\
+ Cyclic {0} value not allowed.
+
+msg.is.not.defined =\
+ "{0}" is not defined.
+
+msg.undef.prop.read =\
+ Cannot read property "{1}" from {0}
+
+msg.undef.prop.write =\
+ Cannot set property "{1}" of {0} to "{2}"
+
+msg.undef.prop.delete =\
+ Cannot delete property "{1}" of {0}
+
+msg.undef.method.call =\
+ Cannot call method "{1}" of {0}
+
+msg.undef.with =\
+ Cannot apply "with" to {0}
+
+msg.isnt.function =\
+ {0} is not a function, it is {1}.
+
+msg.isnt.function.in =\
+ Cannot call property {0} in object {1}. It is not a function, it is "{2}".
+
+msg.function.not.found =\
+ Cannot find function {0}.
+
+msg.function.not.found.in =\
+ Cannot find function {0} in object {1}.
+
+msg.isnt.xml.object =\
+ {0} is not an xml object.
+
+msg.no.ref.to.get =\
+ {0} is not a reference to read reference value.
+
+msg.no.ref.to.set =\
+ {0} is not a reference to set reference value to {1}.
+
+msg.no.ref.from.function =\
+ Function {0} can not be used as the left-hand side of assignment \
+ or as an operand of ++ or -- operator.
+
+msg.bad.default.value =\
+ Object''s getDefaultValue() method returned an object.
+
+msg.instanceof.not.object = \
+ Can''t use instanceof on a non-object.
+
+msg.instanceof.bad.prototype = \
+ ''prototype'' property of {0} is not an object.
+
+msg.bad.radix = \
+ illegal radix {0}.
+
+# ScriptableObject
+msg.default.value =\
+ Cannot find default value for object.
+
+msg.zero.arg.ctor =\
+ Cannot load class "{0}" which has no zero-parameter constructor.
+
+msg.ctor.multiple.parms =\
+ Can''t define constructor or class {0} since more than one \
+ constructor has multiple parameters.
+
+msg.extend.scriptable =\
+ {0} must extend ScriptableObject in order to define property {1}.
+
+msg.bad.getter.parms =\
+ In order to define a property, getter {0} must have zero parameters \
+ or a single ScriptableObject parameter.
+
+msg.obj.getter.parms =\
+ Expected static or delegated getter {0} to take a ScriptableObject parameter.
+
+msg.getter.static =\
+ Getter and setter must both be static or neither be static.
+
+msg.setter.return =\
+ Setter must have void return type: {0}
+
+msg.setter2.parms =\
+ Two-parameter setter must take a ScriptableObject as its first parameter.
+
+msg.setter1.parms =\
+ Expected single parameter setter for {0}
+
+msg.setter2.expected =\
+ Expected static or delegated setter {0} to take two parameters.
+
+msg.setter.parms =\
+ Expected either one or two parameters for setter.
+
+msg.setter.bad.type =\
+ Unsupported parameter type "{0}" in setter "{1}".
+
+msg.add.sealed =\
+ Cannot add a property to a sealed object: {0}.
+
+msg.remove.sealed =\
+ Cannot remove a property from a sealed object: {0}.
+
+msg.modify.sealed =\
+ Cannot modify a property of a sealed object: {0}.
+
+msg.modify.readonly =\
+ Cannot modify readonly property: {0}.
+
+# TokenStream
+msg.missing.exponent =\
+ missing exponent
+
+msg.caught.nfe =\
+ number format error
+
+msg.unterminated.string.lit =\
+ unterminated string literal
+
+msg.unterminated.mstring.appjet =\
+ unterminated multi-line string literal
+
+msg.unterminated.comment =\
+ unterminated comment
+
+msg.unterminated.re.lit =\
+ unterminated regular expression literal
+
+msg.invalid.re.flag =\
+ invalid flag after regular expression
+
+msg.no.re.input.for =\
+ no input for {0}
+
+msg.illegal.character =\
+ illegal character
+
+msg.illegal.character.appjet =\
+ illegal character: {0}
+
+msg.invalid.escape =\
+ invalid Unicode escape sequence
+
+msg.bad.namespace =\
+ not a valid default namespace statement. \
+ Syntax is: default xml namespace = EXPRESSION;
+
+# TokensStream warnings
+msg.bad.octal.literal =\
+ illegal octal literal digit {0}; interpreting it as a decimal digit
+
+msg.reserved.keyword =\
+ illegal usage of future reserved keyword {0}; interpreting it as ordinary identifier
+
+# LiveConnect errors
+msg.java.internal.field.type =\
+ Internal error: type conversion of {0} to assign to {1} on {2} failed.
+
+msg.java.conversion.implicit_method =\
+ Can''t find converter method "{0}" on class {1}.
+
+msg.java.method.assign =\
+ Java method "{0}" cannot be assigned to.
+
+msg.java.internal.private =\
+ Internal error: attempt to access private/protected field "{0}".
+
+msg.java.no_such_method =\
+ Can''t find method {0}.
+
+msg.script.is.not.constructor =\
+ Script objects are not constructors.
+
+msg.nonjava.method =\
+ Java method "{0}" was invoked with {1} as "this" value that can not be converted to Java type {2}.
+
+msg.java.member.not.found =\
+ Java class "{0}" has no public instance field or method named "{1}".
+
+msg.java.array.index.out.of.bounds =\
+ Array index {0} is out of bounds [0..{1}].
+
+msg.java.array.member.not.found =\
+ Java arrays have no public instance fields or methods named "{0}".
+
+msg.pkg.int =\
+ Java package names may not be numbers.
+
+msg.access.prohibited =\
+ Access to Java class "{0}" is prohibited.
+
+# ImporterTopLevel
+msg.ambig.import =\
+ Ambiguous import: "{0}" and and "{1}".
+
+msg.not.pkg =\
+ Function importPackage must be called with a package; had "{0}" instead.
+
+msg.not.class =\
+ Function importClass must be called with a class; had "{0}" instead.
+
+msg.not.class.not.pkg =\
+ "{0}" is neither a class nor a package.
+
+msg.prop.defined =\
+ Cannot import "{0}" since a property by that name is already defined.
+
+#JavaAdapter
+msg.adapter.zero.args =\
+ JavaAdapter requires at least one argument.
+
+msg.not.java.class.arg = \
+Argument {0} is not Java class: {1}.
+
+#JavaAdapter
+msg.only.one.super = \
+Only one class may be extended by a JavaAdapter. Had {0} and {1}.
+
+
+# Arrays
+msg.arraylength.bad =\
+ Inappropriate array length.
+
+# Arrays
+msg.arraylength.too.big =\
+ Array length {0} exceeds supported capacity limit.
+
+# URI
+msg.bad.uri =\
+ Malformed URI sequence.
+
+# Number
+msg.bad.precision =\
+ Precision {0} out of range.
+
+# NativeGenerator
+msg.send.newborn =\
+ Attempt to send value to newborn generator
+
+msg.already.exec.gen =\
+ Already executing generator
+
+msg.StopIteration.invalid =\
+ StopIteration may not be changed to an arbitrary object.
+
+# Interpreter
+msg.yield.closing =\
+ Yield from closing generator
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/resources/Messages_fr.properties b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/resources/Messages_fr.properties
new file mode 100644
index 0000000..fc87c97
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/resources/Messages_fr.properties
@@ -0,0 +1,329 @@
+#
+# French JavaScript messages file.
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Aviva Inc. code, released
+# March 5, 2004.
+#
+# The Initial Developer of the Original Code is
+# Aviva Inc.
+# Portions created by the Initial Developer are Copyright (C) 2004
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Eugene Aresteanu
+#
+# Alternatively, the contents of this file may be used under the terms of
+# the GNU General Public License Version 2 or later (the "GPL"), in which
+# case the provisions of the GPL are applicable instead of those above. If
+# you wish to allow use of your version of this file only under the terms of
+# the GPL and not to allow others to use your version of this file under the
+# MPL, indicate your decision by deleting the provisions above and replacing
+# them with the notice and other provisions required by the GPL. If you do
+# not delete the provisions above, a recipient may use your version of this
+# file under either the MPL or the GPL.
+#
+# ***** END LICENSE BLOCK *****
+
+msg.dup.parms =\
+ Le nom de param\u00E8tre "{0}" existe d\u00E9j\u00E0.
+msg.too.big.jump =\
+ Programme trop complexe : d\u00E9calage de saut trop important
+msg.too.big.index =\
+ Programme trop complexe : l''indice interne d\u00E9passe la limite de 64 ko
+msg.ctor.not.found =\
+ Le constructeur de "{0}" est introuvable
+msg.not.ctor =\
+ {0} n''est pas un constructeur
+msg.varargs.ctor =\
+ La m\u00E9thode ou le constructeur "{0}" doit \u00EAtre statique avec la signature "(Context cx, arguments Object[], Function ctorObj, boolean inNewExpr)" pour d\u00E9finir un constructeur d''arguments de variable.
+msg.varargs.fun =\
+ La m\u00E9thode "{0}" doit \u00EAtre statique avec la signature "(Context cx, Scriptable thisObj, arguments Object[], Function funObj)" pour d\u00E9finir une fonction d''arguments de variable
+msg.incompat.call =\
+ La m\u00E9thode "{0}" a \u00E9t\u00E9 appel\u00E9e dans un objet non compatible
+msg.bad.parms =\
+ Les param\u00E8tres de la m\u00E9thode sont incorrects pour "{0}"
+msg.no.overload =\
+ La m\u00E9thode "{0}" appara\u00EEt plusieurs fois dans la classe "{1}"
+msg.method.not.found =\
+ La m\u00E9thode "{0}" est introuvable dans "{1}"
+msg.bad.for.in.lhs =\
+ La partie gauche de la boucle for..in est incorrecte
+msg.bad.lhs.assign =\
+ La partie gauche de l''affectation est incorrecte
+msg.mult.index =\
+ Une seule variable est autoris\u00E9e dans la boucle for..in
+msg.cant.convert =\
+ La conversion en type "{0}" est impossible
+msg.cant.call.indirect =\
+ La fonction "{0}" doit \u00EAtre appel\u00E9e directement et non par l''interm\u00E9diaire d''une fonction portant un autre nom
+msg.eval.nonstring =\
+ Si vous appelez la fonction eval() avec une valeur qui n''appartient pas \u00E0 une cha\u00EEne primitive, c''est la valeur en question qui est renvoy\u00E9e. \u00E9tait-ce votre intention ?
+msg.only.from.new =\
+ {0} ne peut \u00EAtre appel\u00E9e qu''\u00E0 partir d''une "nouvelle" expression.
+msg.deprec.ctor =\
+ Le constructeur "{0}" est d\u00E9conseill\u00E9
+msg.no.function.ref.found =\
+ aucune source n''a \u00E9t\u00E9 trouv\u00E9e pour d\u00E9compiler la r\u00E9f\u00E9rence de fonction {0}
+msg.arg.isnt.array =\
+ le second argument de la m\u00E9thode Function.prototype.apply doit \u00EAtre un tableau
+msg.bad.esc.mask =\
+ le masque d''\u00E9chappement de cha\u00EEne est incorrect
+msg.cant.instantiate =\
+ erreur lors de l''instanciation ({0}) : la classe {1} est une classe interface ou abstract
+msg.bad.ctor.sig =\
+ Un constructeur avec une signature incorrecte a \u00E9t\u00E9 d\u00E9tect\u00E9 : {0} qui appelle {1} avec la signature {2}
+msg.not.java.obj =\
+ L''argument attendu pour la fonction getClass() doit \u00EAtre un objet Java
+msg.no.java.ctor =\
+ Le constructeur Java de "{0}" avec les arguments "{1}" est introuvable
+msg.method.ambiguous =\
+ Le choix de la m\u00E9thode Java {0}.{1} correspondant aux types d''argument JavaScript ({2}) est ambigu. Les m\u00E9thodes propos\u00E9es sont les suivantes : {3}
+msg.constructor.ambiguous =\
+ Le choix du constructeur Java {0} correspondant aux types d''argument JavaScript ({1}) est ambigu. Les constructeurs propos\u00E9s sont les suivants : {2}
+msg.conversion.not.allowed =\
+ Impossible de convertir {0} en {1}
+msg.not.classloader =\
+ Le constructeur de "Packages" attend un argument de type java.lang.Classloader
+msg.bad.quant =\
+ Le quantificateur {0} est incorrect
+msg.overlarge.max =\
+ Le maximum {0} est trop important
+msg.zero.quant =\
+ Le quantificateur {0} est nul
+msg.max.lt.min =\
+ Le maximum {0} est inf\u00E9rieur au minimum
+msg.unterm.quant =\
+ Le quantificateur {0} n''a pas de limite
+msg.unterm.paren =\
+ Les parenth\u00E8ses {0} n''ont pas de limite
+msg.unterm.class =\
+ La classe de caract\u00E8res {0} n''a pas de limite
+msg.bad.range =\
+ La classe de caract\u00E8res contient une plage de valeurs incorrecte
+msg.trail.backslash =\
+ \\ au d\u00E9but d''une expression r\u00E9guli\u00E8re
+msg.no.regexp =\
+ Les expressions r\u00E9guli\u00E8res ne sont pas disponibles
+msg.bad.backref =\
+ la r\u00E9f\u00E9rence ant\u00E9rieure d\u00E9passe le nombre de parenth\u00E8ses de capture
+msg.dup.label =\
+ Le libell\u00E9 {0} existe d\u00E9j\u00E0
+msg.undef.label =\
+ Le libell\u00E9 {0} n''est pas d\u00E9fini
+msg.bad.break =\
+ Le saut non libell\u00E9 doit se trouver dans la boucle ou dans l''aiguillage
+msg.continue.outside =\
+ continue doit se trouver dans la boucle
+msg.continue.nonloop =\
+ Il n''est possible de continuer que dans l''instruction d''it\u00E9ration libell\u00E9e
+msg.fn.redecl =\
+ La fonction "{0}" a \u00E9t\u00E9 de nouveau d\u00E9clar\u00E9e. La d\u00E9finition pr\u00E9c\u00E9dente sera ignor\u00E9e
+msg.no.paren.parms =\
+ il manque ''('' avant les param\u00E8tres de la fonction
+msg.no.parm =\
+ il manque un param\u00E8tre de forme
+msg.no.paren.after.parms =\
+ il manque '')'' apr\u00E8s les param\u00E8tres de forme
+msg.no.brace.body =\
+ il manque '{' avant le corps d''une fonction
+msg.no.brace.after.body =\
+ il manque ''}'' apr\u00E8s le corps d''une fonction
+msg.no.paren.cond =\
+ il manque ''('' avant une condition
+msg.no.paren.after.cond =\
+ il manque '')'' apr\u00E8s une condition
+msg.no.semi.stmt =\
+ il manque '';'' avant une instruction
+msg.no.name.after.dot =\
+ il manque un nom apr\u00E8s un op\u00E9rateur ''.''
+msg.no.bracket.index =\
+ il manque '']'' dans l''expression de l''indice
+msg.no.paren.switch =\
+ il manque ''('' avant l''expression d''un aiguillage
+msg.no.paren.after.switch =\
+ il manque '')'' apr\u00E8s l''expression d''un aiguillage
+msg.no.brace.switch =\
+ il manque '{' avant le corps d''un aiguillage
+msg.bad.switch =\
+ l''instruction d''aiguillage est incorrecte
+msg.no.colon.case =\
+ il manque '':'' apr\u00E8s l''expression d''un cas
+msg.no.while.do =\
+ il manque ''while'' apr\u00E8s le corps d''une boucle do-loop
+msg.no.paren.for =\
+ il manque ''('' apr\u00E8s for
+msg.no.semi.for =\
+ Il manque '';'' apr\u00E8s l''initialiseur for-loop
+msg.no.semi.for.cond =\
+ il manque '';'' apr\u00E8s la condition for-loop
+msg.no.paren.for.ctrl =\
+ il manque '')'' apr\u00E8s le contrôle for-loop
+msg.no.paren.with =\
+ il manque ''('' avant un objet with-statement
+msg.no.paren.after.with =\
+ il manque '')'' apr\u00E8s un objet with-statement
+msg.bad.return =\
+ la valeur renvoy\u00E9e est incorrecte
+msg.no.brace.block =\
+ il manque ''}'' dans une instruction compos\u00E9e
+msg.bad.label =\
+ le libell\u00E9 est incorrect
+msg.bad.var =\
+ il manque un nom de variable
+msg.bad.var.init =\
+ l''initialisation de la variable est incorrecte
+msg.no.colon.cond =\
+ il manque '':'' dans une expression conditionnelle
+msg.no.paren.arg =\
+ il manque '')'' apr\u00E8s une liste d''arguments
+msg.no.bracket.arg =\
+ il manque '']'' apr\u00E8s une liste d''\u00E9l\u00E9ments
+msg.bad.prop =\
+ l''identifiant de propri\u00E9t\u00E9 est incorrect
+msg.no.colon.prop =\
+ il manque '':'' apr\u00E8s un identifiant de propri\u00E9t\u00E9
+msg.no.brace.prop =\
+ il manque ''}'' apr\u00E8s une liste de propri\u00E9t\u00E9s
+msg.no.paren =\
+ il manque '')'' dans des parenth\u00E8ses
+msg.reserved.id =\
+ l''identifiant est un mot r\u00E9serv\u00E9
+msg.no.paren.catch =\
+ il manque ''('' avant une condition catch-block
+msg.bad.catchcond =\
+ la condition catch-block est incorrecte
+msg.catch.unreachable =\
+ aucune clause catch suivant une interception non qualifi\u00E9e ne peut \u00EAtre atteinte
+msg.no.brace.catchblock =\
+ il manque '{' avant le corps catch-block
+msg.try.no.catchfinally =\
+ ''try'' a \u00E9t\u00E9 d\u00E9tect\u00E9 sans ''catch'' ni ''finally''
+msg.syntax =\
+ erreur de syntaxe
+msg.assn.create =\
+ Une variable va \u00EAtre cr\u00E9\u00E9e en raison de l''affectation \u00E0 un ''{0}'' non d\u00E9fini. Ajoutez une instruction de variable \u00E0 la port\u00E9e sup\u00E9rieure pour que cet avertissement ne soit plus affich\u00E9
+msg.prop.not.found =\
+ La propri\u00E9t\u00E9 est introuvable
+msg.invalid.type =\
+ Valeur JavaScript de type {0} incorrecte
+msg.primitive.expected =\
+ Un type primitif \u00E9tait attendu (et non {0})
+msg.null.to.object =\
+ Il est impossible de convertir la valeur null en objet
+msg.undef.to.object =\
+ Il est impossible de convertir une valeur non d\u00E9finie en objet
+msg.cyclic.value =\
+ La valeur cyclique {0} n''est pas autoris\u00E9e
+msg.is.not.defined =\
+ "{0}" n''est pas d\u00E9fini
+msg.isnt.function =\
+ {0} n''est pas une fonction, est un {1}
+msg.bad.default.value =\
+ La m\u00E9thode getDefaultValue() de l''objet a renvoy\u00E9 un objet
+msg.instanceof.not.object =\
+ Il est impossible d''utiliser une instance d''un \u00E9l\u00E9ment autre qu''un objet
+msg.instanceof.bad.prototype =\
+ La propri\u00E9t\u00E9 ''prototype'' de {0} n''est pas un objet
+msg.bad.radix =\
+ la base {0} n''est pas autoris\u00E9e
+msg.default.value =\
+ La valeur par d\u00E9faut de l''objet est introuvable
+msg.zero.arg.ctor =\
+ Il est impossible de charger la classe "{0}", qui ne poss\u00E8de pas de constructeur de param\u00E8tre z\u00E9ro
+msg.multiple.ctors =\
+ Les m\u00E9thodes {0} et {1} ont \u00E9t\u00E9 d\u00E9tect\u00E9es alors qu''il est impossible d''utiliser plusieurs m\u00E9thodes constructor
+msg.ctor.multiple.parms =\
+ Il est impossible de d\u00E9finir le constructeur ou la classe {0} car plusieurs constructeurs poss\u00E8dent plusieurs param\u00E8tres
+msg.extend.scriptable =\
+ {0} doit \u00E9tendre ScriptableObject afin de d\u00E9finir la propri\u00E9t\u00E9 {1}
+msg.bad.getter.parms =\
+ Pour d\u00E9finir une propri\u00E9t\u00E9, la m\u00E9thode d''obtention {0} doit avoir des param\u00E8tres z\u00E9ro ou un seul param\u00E8tre ScriptableObject
+msg.obj.getter.parms =\
+ La m\u00E9thode d''obtention statique ou d\u00E9l\u00E9gu\u00E9e {0} doit utiliser un param\u00E8tre ScriptableObject
+msg.getter.static =\
+ La m\u00E9thode d''obtention et la m\u00E9thode de d\u00E9finition doivent toutes deux avoir le m\u00EAme \u00E9tat (statique ou non)
+msg.setter2.parms =\
+ La m\u00E9thode de d\u00E9finition \u00E0 deux param\u00E8tres doit utiliser un param\u00E8tre ScriptableObject comme premier param\u00E8tre
+msg.setter1.parms =\
+ Une m\u00E9thode d''obtention \u00E0 param\u00E8tre unique est attendue pour {0}
+msg.setter2.expected =\
+ La m\u00E9thode de d\u00E9finition statique ou d\u00E9l\u00E9gu\u00E9e {0} doit utiliser deux param\u00E8tres
+msg.setter.parms =\
+ Un ou deux param\u00E8tres sont attendus pour la m\u00E9thode de d\u00E9finition
+msg.add.sealed =\
+ Il est impossible d''ajouter une propri\u00E9t\u00E9 \u00E0 un objet ferm\u00E9
+msg.remove.sealed =\
+ Il est impossible de supprimer une propri\u00E9t\u00E9 d''un objet ferm\u00E9
+msg.token.replaces.pushback =\
+ le jeton de non-obtention {0} remplace le jeton de renvoi {1}
+msg.missing.exponent =\
+ il manque un exposant
+msg.caught.nfe =\
+ erreur de format de nombre : {0}
+msg.unterminated.string.lit =\
+ le litt\u00E9ral de la cha\u00EEne n''a pas de limite
+msg.unterminated.comment =\
+ le commentaire n''a pas de limite
+msg.unterminated.re.lit =\
+ le litt\u00E9ral de l''expression r\u00E9guli\u00E8re n''a pas de limite
+msg.invalid.re.flag =\
+ une expression r\u00E9guli\u00E8re est suivie d''un indicateur incorrect
+msg.no.re.input.for =\
+ il n''y a pas d''entr\u00E9e pour {0}
+msg.illegal.character =\
+ caract\u00E8re non autoris\u00E9
+msg.invalid.escape =\
+ la s\u00E9quence d''\u00E9chappement Unicode est incorrecte
+msg.bad.octal.literal =\
+ le chiffre octal du litt\u00E9ral, {0}, n''est pas autoris\u00E9 et sera interpr\u00E9t\u00E9 comme un chiffre d\u00E9cimal
+msg.reserved.keyword =\
+ l''utilisation du futur mot-cl\u00E9 r\u00E9serv\u00E9 {0} n''est pas autoris\u00E9e et celui-ci sera interpr\u00E9t\u00E9 comme un identifiant ordinaire
+msg.undefined =\
+ La valeur non d\u00E9finie ne poss\u00E8de pas de propri\u00E9t\u00E9
+msg.java.internal.field.type =\
+ Erreur interne : la conversion de type de {0} afin d''affecter {1} \u00E0 {2} a \u00E9chou\u00E9
+msg.java.conversion.implicit_method =\
+ La m\u00E9thode de conversion "{0}" est introuvable dans la classe {1}
+sg.java.method.assign =\
+ La m\u00E9thode Java "{0}" ne peut pas \u00EAtre affect\u00E9e \u00E0
+msg.java.internal.private =\
+ Erreur interne : une tentative d''acc\u00E9der \u00E0 un champ "{0}" priv\u00E9/prot\u00E9g\u00E9 a \u00E9t\u00E9 d\u00E9tect\u00E9e
+msg.java.no_such_method =\
+ La m\u00E9thode ''{0}'' est introuvable
+msg.script.is.not.constructor =\
+ Les objets Script ne sont pas des constructeurs
+msg.nonjava.method =\
+ La m\u00E9thode Java "{0}" a \u00E9t\u00E9 appel\u00E9e avec une valeur ''this'' qui n''est pas un objet Java
+msg.java.member.not.found =\
+ La classe Java "{0}" ne poss\u00E8de aucun champ ou aucune m\u00E9thode d''instance publique appel\u00E9 "{1}"
+msg.java.array.index.out.of.bounds =\
+ Array index {0} is out of bounds [0..{1}].
+msg.pkg.int =\
+ Les noms de package Java ne peuvent pas \u00EAtre des nombres
+msg.ambig.import =\
+ Importation ambigu\u00EB : "{0}" et "{1}"
+msg.not.pkg =\
+ La fonction importPackage doit \u00EAtre appel\u00E9e avec un package et non avec "{0}"
+msg.not.class =\
+ La fonction importClass doit \u00EAtre appel\u00E9e avec une classe et non avec "{0}"
+msg.prop.defined =\
+ Il est impossible d''importer "{0}" car une propri\u00E9t\u00E9 portant le m\u00EAme nom a d\u00E9j\u00E0 \u00E9t\u00E9 d\u00E9finie
+sg.arraylength.bad =\
+ La longueur du tableau n''est pas appropri\u00E9e
+msg.bad.uri =\
+ La s\u00E9quence URI n''est pas form\u00E9e correctement
+msg.bad.precision =\
+ La pr\u00E9cision {0} ne se trouve pas dans la plage de valeurs
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/serialize/ScriptableInputStream.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/serialize/ScriptableInputStream.java
new file mode 100644
index 0000000..476ff69
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/serialize/ScriptableInputStream.java
@@ -0,0 +1,112 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino serialization code, released
+ * Sept. 25, 2001.
+ *
+ * The Initial Developer of the Original Code is
+ * Norris Boyd.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ * Attila Szegedi
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// API class
+
+package org.mozilla.javascript.serialize;
+
+import java.io.*;
+
+import org.mozilla.javascript.*;
+
+/**
+ * Class ScriptableInputStream is used to read in a JavaScript
+ * object or function previously serialized with a ScriptableOutputStream.
+ * References to names in the exclusion list
+ * replaced with references to the top-level scope specified during
+ * creation of the ScriptableInputStream.
+ *
+ * @author Norris Boyd
+ */
+
+public class ScriptableInputStream extends ObjectInputStream {
+
+ /**
+ * Create a ScriptableInputStream.
+ * @param in the InputStream to read from.
+ * @param scope the top-level scope to create the object in.
+ */
+ public ScriptableInputStream(InputStream in, Scriptable scope)
+ throws IOException
+ {
+ super(in);
+ this.scope = scope;
+ enableResolveObject(true);
+ Context cx = Context.getCurrentContext();
+ if (cx != null) {
+ this.classLoader = cx.getApplicationClassLoader();
+ }
+ }
+
+ protected Class resolveClass(ObjectStreamClass desc)
+ throws IOException, ClassNotFoundException
+ {
+ String name = desc.getName();
+ if (classLoader != null) {
+ try {
+ return classLoader.loadClass(name);
+ } catch (ClassNotFoundException ex) {
+ // fall through to default loading
+ }
+ }
+ return super.resolveClass(desc);
+ }
+
+ protected Object resolveObject(Object obj)
+ throws IOException
+ {
+ if (obj instanceof ScriptableOutputStream.PendingLookup) {
+ String name = ((ScriptableOutputStream.PendingLookup)obj).getName();
+ obj = ScriptableOutputStream.lookupQualifiedName(scope, name);
+ if (obj == Scriptable.NOT_FOUND) {
+ throw new IOException("Object " + name + " not found upon " +
+ "deserialization.");
+ }
+ }else if (obj instanceof UniqueTag) {
+ obj = ((UniqueTag)obj).readResolve();
+ }else if (obj instanceof Undefined) {
+ obj = ((Undefined)obj).readResolve();
+ }
+ return obj;
+ }
+
+ private Scriptable scope;
+ private ClassLoader classLoader;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/serialize/ScriptableOutputStream.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/serialize/ScriptableOutputStream.java
new file mode 100644
index 0000000..5ba0d74
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/serialize/ScriptableOutputStream.java
@@ -0,0 +1,207 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino serialization code, released
+ * Sept. 25, 2001.
+ *
+ * The Initial Developer of the Original Code is
+ * Norris Boyd.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Attila Szegedi
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript.serialize;
+
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+import java.io.*;
+
+import org.mozilla.javascript.*;
+
+/**
+ * Class ScriptableOutputStream is an ObjectOutputStream used
+ * to serialize JavaScript objects and functions. Note that
+ * compiled functions currently cannot be serialized, only
+ * interpreted functions. The top-level scope containing the
+ * object is not written out, but is instead replaced with
+ * another top-level object when the ScriptableInputStream
+ * reads in this object. Also, object corresponding to names
+ * added to the exclude list are not written out but instead
+ * are looked up during deserialization. This approach avoids
+ * the creation of duplicate copies of standard objects
+ * during deserialization.
+ *
+ * @author Norris Boyd
+ */
+
+// API class
+
+public class ScriptableOutputStream extends ObjectOutputStream {
+
+ /**
+ * ScriptableOutputStream constructor.
+ * Creates a ScriptableOutputStream for use in serializing
+ * JavaScript objects. Calls excludeStandardObjectNames.
+ *
+ * @param out the OutputStream to write to.
+ * @param scope the scope containing the object.
+ */
+ public ScriptableOutputStream(OutputStream out, Scriptable scope)
+ throws IOException
+ {
+ super(out);
+ this.scope = scope;
+ table = new Hashtable(31);
+ table.put(scope, "");
+ enableReplaceObject(true);
+ excludeStandardObjectNames();
+ }
+
+ /**
+ * Adds a qualified name to the list of object to be excluded from
+ * serialization. Names excluded from serialization are looked up
+ * in the new scope and replaced upon deserialization.
+ * @param name a fully qualified name (of the form "a.b.c", where
+ * "a" must be a property of the top-level object). The object
+ * need not exist, in which case the name is ignored.
+ * @throws IllegalArgumentException if the object is not a
+ * {@link Scriptable}.
+ */
+ public void addOptionalExcludedName(String name) {
+ Object obj = lookupQualifiedName(scope, name);
+ if(obj != null && obj != UniqueTag.NOT_FOUND) {
+ if (!(obj instanceof Scriptable)) {
+ throw new IllegalArgumentException(
+ "Object for excluded name " + name +
+ " is not a Scriptable, it is " +
+ obj.getClass().getName());
+ }
+ table.put(obj, name);
+ }
+ }
+
+ /**
+ * Adds a qualified name to the list of object to be excluded from
+ * serialization. Names excluded from serialization are looked up
+ * in the new scope and replaced upon deserialization.
+ * @param name a fully qualified name (of the form "a.b.c", where
+ * "a" must be a property of the top-level object)
+ * @throws IllegalArgumentException if the object is not found or is not
+ * a {@link Scriptable}.
+ */
+ public void addExcludedName(String name) {
+ Object obj = lookupQualifiedName(scope, name);
+ if (!(obj instanceof Scriptable)) {
+ throw new IllegalArgumentException("Object for excluded name " +
+ name + " not found.");
+ }
+ table.put(obj, name);
+ }
+
+ /**
+ * Returns true if the name is excluded from serialization.
+ */
+ public boolean hasExcludedName(String name) {
+ return table.get(name) != null;
+ }
+
+ /**
+ * Removes a name from the list of names to exclude.
+ */
+ public void removeExcludedName(String name) {
+ table.remove(name);
+ }
+
+ /**
+ * Adds the names of the standard objects and their
+ * prototypes to the list of excluded names.
+ */
+ public void excludeStandardObjectNames() {
+ String[] names = { "Object", "Object.prototype",
+ "Function", "Function.prototype",
+ "String", "String.prototype",
+ "Math", // no Math.prototype
+ "Array", "Array.prototype",
+ "Error", "Error.prototype",
+ "Number", "Number.prototype",
+ "Date", "Date.prototype",
+ "RegExp", "RegExp.prototype",
+ "Script", "Script.prototype",
+ "Continuation", "Continuation.prototype",
+ };
+ for (int i=0; i < names.length; i++) {
+ addExcludedName(names[i]);
+ }
+
+ String[] optionalNames = {
+ "XML", "XML.prototype",
+ "XMLList", "XMLList.prototype",
+ };
+ for (int i=0; i < optionalNames.length; i++) {
+ addOptionalExcludedName(optionalNames[i]);
+ }
+ }
+
+ static Object lookupQualifiedName(Scriptable scope,
+ String qualifiedName)
+ {
+ StringTokenizer st = new StringTokenizer(qualifiedName, ".");
+ Object result = scope;
+ while (st.hasMoreTokens()) {
+ String s = st.nextToken();
+ result = ScriptableObject.getProperty((Scriptable)result, s);
+ if (result == null || !(result instanceof Scriptable))
+ break;
+ }
+ return result;
+ }
+
+ static class PendingLookup implements Serializable
+ {
+ static final long serialVersionUID = -2692990309789917727L;
+
+ PendingLookup(String name) { this.name = name; }
+
+ String getName() { return name; }
+
+ private String name;
+ }
+
+ protected Object replaceObject(Object obj) throws IOException
+ {
+ String name = (String) table.get(obj);
+ if (name == null)
+ return obj;
+ return new PendingLookup(name);
+ }
+
+ private Scriptable scope;
+ private Hashtable table;
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/xml/XMLLib.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/xml/XMLLib.java
new file mode 100644
index 0000000..da57ddf
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/xml/XMLLib.java
@@ -0,0 +1,132 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript.xml;
+
+import org.mozilla.javascript.*;
+
+public abstract class XMLLib
+{
+ private static final Object XML_LIB_KEY = new Object();
+
+ /**
+ An object which specifies an XMLLib implementation to be used at runtime.
+
+ This interface should be considered experimental. It may be better
+ (and certainly more flexible) to write an interface that returns an
+ XMLLib object rather than a class name, for example. But that would
+ cause many more ripple effects in the code, all the way back to
+ {@link ScriptRuntime}.
+ */
+ public static abstract class Factory {
+ public static Factory create(final String className) {
+ return new Factory() {
+ public String getImplementationClassName() {
+ return className;
+ }
+ };
+ }
+
+ public abstract String getImplementationClassName();
+ }
+
+ public static XMLLib extractFromScopeOrNull(Scriptable scope)
+ {
+ ScriptableObject so = ScriptRuntime.getLibraryScopeOrNull(scope);
+ if (so == null) {
+ // If library is not yet initialized, return null
+ return null;
+ }
+
+ // Ensure lazily initialization of real XML library instance
+ // which is done on first access to XML property
+ ScriptableObject.getProperty(so, "XML");
+
+ return (XMLLib)so.getAssociatedValue(XML_LIB_KEY);
+ }
+
+ public static XMLLib extractFromScope(Scriptable scope)
+ {
+ XMLLib lib = extractFromScopeOrNull(scope);
+ if (lib != null) {
+ return lib;
+ }
+ String msg = ScriptRuntime.getMessage0("msg.XML.not.available");
+ throw Context.reportRuntimeError(msg);
+ }
+
+ protected final XMLLib bindToScope(Scriptable scope)
+ {
+ ScriptableObject so = ScriptRuntime.getLibraryScopeOrNull(scope);
+ if (so == null) {
+ // standard library should be initialized at this point
+ throw new IllegalStateException();
+ }
+ return (XMLLib)so.associateValue(XML_LIB_KEY, this);
+ }
+
+ public abstract boolean isXMLName(Context cx, Object name);
+
+ public abstract Ref nameRef(Context cx, Object name,
+ Scriptable scope, int memberTypeFlags);
+
+ public abstract Ref nameRef(Context cx, Object namespace, Object name,
+ Scriptable scope, int memberTypeFlags);
+
+ /**
+ * Escapes the reserved characters in a value of an attribute.
+ *
+ * @param value Unescaped text
+ * @return The escaped text
+ */
+ public abstract String escapeAttributeValue(Object value);
+
+ /**
+ * Escapes the reserved characters in a value of a text node.
+ *
+ * @param value Unescaped text
+ * @return The escaped text
+ */
+ public abstract String escapeTextValue(Object value);
+
+
+ /**
+ * Construct namespace for default xml statement.
+ */
+ public abstract Object toDefaultXmlNamespace(Context cx, Object uriValue);
+}
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/xml/XMLObject.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/xml/XMLObject.java
new file mode 100644
index 0000000..5033564
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/xml/XMLObject.java
@@ -0,0 +1,128 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Igor Bukanov
+ * Ethan Hugg
+ * Terry Lucas
+ * Milen Nankov
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript.xml;
+
+import org.mozilla.javascript.*;
+
+/**
+ * This Interface describes what all XML objects (XML, XMLList) should have in common.
+ *
+ */
+public abstract class XMLObject extends IdScriptableObject
+{
+ public XMLObject()
+ {
+ }
+
+ public XMLObject(Scriptable scope, Scriptable prototype)
+ {
+ super(scope, prototype);
+ }
+
+ /**
+ * Implementation of ECMAScript [[Has]].
+ */
+ public abstract boolean ecmaHas(Context cx, Object id);
+
+ /**
+ * Implementation of ECMAScript [[Get]].
+ */
+ public abstract Object ecmaGet(Context cx, Object id);
+
+ /**
+ * Implementation of ECMAScript [[Put]].
+ */
+ public abstract void ecmaPut(Context cx, Object id, Object value);
+
+ /**
+ * Implementation of ECMAScript [[Delete]].
+ */
+ public abstract boolean ecmaDelete(Context cx, Object id);
+
+ /**
+ * Return an additional object to look for methods that runtime should
+ * consider during method search. Return null if no such object available.
+ */
+ public abstract Scriptable getExtraMethodSource(Context cx);
+
+ /**
+ * Generic reference to implement x.@y, x..y etc.
+ */
+ public abstract Ref memberRef(Context cx, Object elem,
+ int memberTypeFlags);
+
+ /**
+ * Generic reference to implement x::ns, x.@ns::y, x..@ns::y etc.
+ */
+ public abstract Ref memberRef(Context cx, Object namespace, Object elem,
+ int memberTypeFlags);
+
+ /**
+ * Wrap this object into NativeWith to implement the with statement.
+ */
+ public abstract NativeWith enterWith(Scriptable scope);
+
+ /**
+ * Wrap this object into NativeWith to implement the .() query.
+ */
+ public abstract NativeWith enterDotQuery(Scriptable scope);
+
+ /**
+ * Custom <tt>+</tt> operator.
+ * Should return {@link Scriptable#NOT_FOUND} if this object does not have
+ * custom addition operator for the given value,
+ * or the result of the addition operation.
+ * <p>
+ * The default implementation returns {@link Scriptable#NOT_FOUND}
+ * to indicate no custom addition operation.
+ *
+ * @param cx the Context object associated with the current thread.
+ * @param thisIsLeft if true, the object should calculate this + value
+ * if false, the object should calculate value + this.
+ * @param value the second argument for addition operation.
+ */
+ public Object addValues(Context cx, boolean thisIsLeft, Object value)
+ {
+ return Scriptable.NOT_FOUND;
+ }
+
+}