From 98e2821b38a775737e42a2479a6bc65107210859 Mon Sep 17 00:00:00 2001 From: Elliot Kroo Date: Thu, 11 Mar 2010 15:21:30 -0800 Subject: reorganizing the first level of folders (trunk/branch folders are not the git way :) --- infrastructure/rhino1_7R1/src/build.xml | 98 + infrastructure/rhino1_7R1/src/manifest | 3 + .../src/org/mozilla/classfile/ByteCode.java | 274 ++ .../src/org/mozilla/classfile/ClassFileWriter.java | 3038 ++++++++++++ .../src/org/mozilla/javascript/Arguments.java | 311 ++ .../src/org/mozilla/javascript/BaseFunction.java | 553 +++ .../src/org/mozilla/javascript/Callable.java | 59 + .../src/org/mozilla/javascript/ClassCache.java | 220 + .../src/org/mozilla/javascript/ClassShutter.java | 89 + .../org/mozilla/javascript/CompilerEnvirons.java | 233 + .../org/mozilla/javascript/ConstProperties.java | 109 + .../src/org/mozilla/javascript/Context.java | 2526 ++++++++++ .../src/org/mozilla/javascript/ContextAction.java | 59 + .../src/org/mozilla/javascript/ContextFactory.java | 594 +++ .../org/mozilla/javascript/ContextListener.java | 60 + .../src/org/mozilla/javascript/DToA.java | 1271 +++++ .../src/org/mozilla/javascript/Decompiler.java | 918 ++++ .../mozilla/javascript/DefaultErrorReporter.java | 113 + .../mozilla/javascript/DefiningClassLoader.java | 88 + .../src/org/mozilla/javascript/Delegator.java | 266 ++ .../src/org/mozilla/javascript/EcmaError.java | 160 + .../src/org/mozilla/javascript/ErrorReporter.java | 106 + .../src/org/mozilla/javascript/Evaluator.java | 118 + .../org/mozilla/javascript/EvaluatorException.java | 123 + .../src/org/mozilla/javascript/Function.java | 84 + .../src/org/mozilla/javascript/FunctionNode.java | 117 + .../src/org/mozilla/javascript/FunctionObject.java | 569 +++ .../mozilla/javascript/GeneratedClassLoader.java | 66 + .../src/org/mozilla/javascript/IRFactory.java | 1607 +++++++ .../src/org/mozilla/javascript/IdFunctionCall.java | 55 + .../org/mozilla/javascript/IdFunctionObject.java | 189 + .../org/mozilla/javascript/IdScriptableObject.java | 734 +++ .../org/mozilla/javascript/ImporterTopLevel.java | 318 ++ .../org/mozilla/javascript/InformativeParser.java | 225 + .../org/mozilla/javascript/InterfaceAdapter.java | 156 + .../mozilla/javascript/InterpretedFunction.java | 221 + .../src/org/mozilla/javascript/Interpreter.java | 4643 ++++++++++++++++++ .../org/mozilla/javascript/InterpreterData.java | 192 + .../src/org/mozilla/javascript/JavaAdapter.java | 1129 +++++ .../src/org/mozilla/javascript/JavaMembers.java | 935 ++++ .../mozilla/javascript/JavaScriptException.java | 117 + .../rhino1_7R1/src/org/mozilla/javascript/Kit.java | 486 ++ .../org/mozilla/javascript/LazilyLoadedCtor.java | 136 + .../src/org/mozilla/javascript/MemberBox.java | 362 ++ .../src/org/mozilla/javascript/NativeArray.java | 1727 +++++++ .../src/org/mozilla/javascript/NativeBoolean.java | 170 + .../src/org/mozilla/javascript/NativeCall.java | 154 + .../src/org/mozilla/javascript/NativeDate.java | 1604 +++++++ .../src/org/mozilla/javascript/NativeError.java | 227 + .../src/org/mozilla/javascript/NativeFunction.java | 169 + .../org/mozilla/javascript/NativeGenerator.java | 281 ++ .../src/org/mozilla/javascript/NativeGlobal.java | 790 +++ .../src/org/mozilla/javascript/NativeIterator.java | 260 + .../org/mozilla/javascript/NativeJavaArray.java | 168 + .../org/mozilla/javascript/NativeJavaClass.java | 320 ++ .../mozilla/javascript/NativeJavaConstructor.java | 85 + .../org/mozilla/javascript/NativeJavaMethod.java | 576 +++ .../org/mozilla/javascript/NativeJavaObject.java | 1002 ++++ .../org/mozilla/javascript/NativeJavaPackage.java | 199 + .../mozilla/javascript/NativeJavaTopPackage.java | 187 + .../src/org/mozilla/javascript/NativeMath.java | 399 ++ .../src/org/mozilla/javascript/NativeNumber.java | 244 + .../src/org/mozilla/javascript/NativeObject.java | 316 ++ .../src/org/mozilla/javascript/NativeScript.java | 221 + .../src/org/mozilla/javascript/NativeString.java | 983 ++++ .../src/org/mozilla/javascript/NativeWith.java | 207 + .../src/org/mozilla/javascript/Node.java | 1394 ++++++ .../org/mozilla/javascript/NodeTransformer.java | 565 +++ .../src/org/mozilla/javascript/ObjArray.java | 388 ++ .../src/org/mozilla/javascript/ObjToIntMap.java | 697 +++ .../src/org/mozilla/javascript/Parser.java | 2554 ++++++++++ .../javascript/PolicySecurityController.java | 223 + .../rhino1_7R1/src/org/mozilla/javascript/Ref.java | 64 + .../src/org/mozilla/javascript/RefCallable.java | 59 + .../src/org/mozilla/javascript/RegExpProxy.java | 71 + .../src/org/mozilla/javascript/RhinoException.java | 306 ++ .../src/org/mozilla/javascript/Script.java | 73 + .../src/org/mozilla/javascript/ScriptOrFnNode.java | 241 + .../src/org/mozilla/javascript/ScriptRuntime.java | 3830 +++++++++++++++ .../src/org/mozilla/javascript/Scriptable.java | 342 ++ .../org/mozilla/javascript/ScriptableObject.java | 2428 ++++++++++ .../src/org/mozilla/javascript/SecureCaller.java | 198 + .../org/mozilla/javascript/SecurityController.java | 211 + .../org/mozilla/javascript/SecurityUtilities.java | 80 + .../src/org/mozilla/javascript/SpecialRef.java | 151 + .../src/org/mozilla/javascript/Synchronizer.java | 81 + .../src/org/mozilla/javascript/Token.java | 436 ++ .../src/org/mozilla/javascript/TokenStream.java | 1500 ++++++ .../src/org/mozilla/javascript/UintMap.java | 659 +++ .../src/org/mozilla/javascript/Undefined.java | 60 + .../src/org/mozilla/javascript/UniqueTag.java | 120 + .../src/org/mozilla/javascript/VMBridge.java | 183 + .../src/org/mozilla/javascript/WrapFactory.java | 183 + .../org/mozilla/javascript/WrappedException.java | 93 + .../src/org/mozilla/javascript/Wrapper.java | 58 + .../javascript/continuations/Continuation.java | 136 + .../org/mozilla/javascript/debug/DebugFrame.java | 91 + .../mozilla/javascript/debug/DebuggableObject.java | 61 + .../mozilla/javascript/debug/DebuggableScript.java | 119 + .../src/org/mozilla/javascript/debug/Debugger.java | 69 + .../mozilla/javascript/jdk11/VMBridge_jdk11.java | 84 + .../mozilla/javascript/jdk13/VMBridge_jdk13.java | 157 + .../mozilla/javascript/jdk15/VMBridge_jdk15.java | 87 + .../org/mozilla/javascript/optimizer/Block.java | 615 +++ .../javascript/optimizer/ClassCompiler.java | 214 + .../org/mozilla/javascript/optimizer/Codegen.java | 5031 ++++++++++++++++++++ .../javascript/optimizer/DataFlowBitSet.java | 134 + .../javascript/optimizer/OptFunctionNode.java | 149 + .../mozilla/javascript/optimizer/OptRuntime.java | 311 ++ .../javascript/optimizer/OptTransformer.java | 133 + .../mozilla/javascript/optimizer/Optimizer.java | 510 ++ .../mozilla/javascript/regexp/NativeRegExp.java | 2782 +++++++++++ .../javascript/regexp/NativeRegExpCtor.java | 289 ++ .../org/mozilla/javascript/regexp/RegExpImpl.java | 541 +++ .../org/mozilla/javascript/regexp/SubString.java | 75 + .../javascript/resources/Messages.properties | 778 +++ .../javascript/resources/Messages_fr.properties | 329 ++ .../serialize/ScriptableInputStream.java | 112 + .../serialize/ScriptableOutputStream.java | 207 + .../src/org/mozilla/javascript/xml/XMLLib.java | 132 + .../src/org/mozilla/javascript/xml/XMLObject.java | 128 + 121 files changed, 64541 insertions(+) create mode 100644 infrastructure/rhino1_7R1/src/build.xml create mode 100644 infrastructure/rhino1_7R1/src/manifest create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/classfile/ByteCode.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/classfile/ClassFileWriter.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/Arguments.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/BaseFunction.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/Callable.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/ClassCache.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/ClassShutter.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/CompilerEnvirons.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/ConstProperties.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/Context.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextAction.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextFactory.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextListener.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/DToA.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/Decompiler.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/DefaultErrorReporter.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/DefiningClassLoader.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/Delegator.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/EcmaError.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/ErrorReporter.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/Evaluator.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/EvaluatorException.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/Function.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/FunctionNode.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/FunctionObject.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/GeneratedClassLoader.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/IRFactory.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdFunctionCall.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdFunctionObject.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdScriptableObject.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/ImporterTopLevel.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/InformativeParser.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterfaceAdapter.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterpretedFunction.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/Interpreter.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterpreterData.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaAdapter.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaMembers.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaScriptException.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/Kit.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/LazilyLoadedCtor.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/MemberBox.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeArray.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeBoolean.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeCall.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeDate.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeError.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeFunction.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGenerator.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGlobal.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeIterator.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaArray.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaClass.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaConstructor.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaMethod.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaObject.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaPackage.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaTopPackage.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeMath.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeNumber.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeObject.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeScript.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeString.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeWith.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/Node.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/NodeTransformer.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/ObjArray.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/ObjToIntMap.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/Parser.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/PolicySecurityController.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/Ref.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/RefCallable.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/RegExpProxy.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/RhinoException.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/Script.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptOrFnNode.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptRuntime.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/Scriptable.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptableObject.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/SecureCaller.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/SecurityController.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/SecurityUtilities.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/SpecialRef.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/Synchronizer.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/Token.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/TokenStream.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/UintMap.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/Undefined.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/UniqueTag.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/VMBridge.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/WrapFactory.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/WrappedException.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/Wrapper.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/continuations/Continuation.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/debug/DebugFrame.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/debug/DebuggableObject.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/debug/DebuggableScript.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/debug/Debugger.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/jdk11/VMBridge_jdk11.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/jdk13/VMBridge_jdk13.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/jdk15/VMBridge_jdk15.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Block.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/ClassCompiler.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Codegen.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/DataFlowBitSet.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/OptFunctionNode.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/OptRuntime.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/OptTransformer.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Optimizer.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/regexp/NativeRegExp.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/regexp/RegExpImpl.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/regexp/SubString.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/resources/Messages.properties create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/resources/Messages_fr.properties create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/serialize/ScriptableInputStream.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/serialize/ScriptableOutputStream.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/xml/XMLLib.java create mode 100644 infrastructure/rhino1_7R1/src/org/mozilla/javascript/xml/XMLObject.java (limited to 'infrastructure/rhino1_7R1/src') diff --git a/infrastructure/rhino1_7R1/src/build.xml b/infrastructure/rhino1_7R1/src/build.xml new file mode 100644 index 0000000..a0a1e13 --- /dev/null +++ b/infrastructure/rhino1_7R1/src/build.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/infrastructure/rhino1_7R1/src/manifest b/infrastructure/rhino1_7R1/src/manifest new file mode 100644 index 0000000..b7d0c06 --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/classfile/ByteCode.java b/infrastructure/rhino1_7R1/src/org/mozilla/classfile/ByteCode.java new file mode 100644 index 0000000..fa4713e --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/classfile/ClassFileWriter.java b/infrastructure/rhino1_7R1/src/org/mozilla/classfile/ClassFileWriter.java new file mode 100644 index 0000000..b9c6c96 --- /dev/null +++ b/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 startMethod. + * + * @param maxLocals the maximum number of local variable slots + * (a.k.a. Java registers) used by the method + */ + public void stopMethod(short maxLocals) { + if (itsCurrentMethod == null) + throw new IllegalStateException("No method to stop"); + + fixLabelGotos(); + + itsMaxLocals = maxLocals; + + 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, "", "(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 start <= i <= end and + * s.substring(start, i) fits JVM UTF string encoding limit. + */ + int getUtfEncodingLimit(String s, int start, int end) + { + if ((end - start) * 3 <= MAX_UTF_ENCODING_SIZE) { + return end; + } + int limit = MAX_UTF_ENCODING_SIZE; + for (int i = start; i != end; i++) { + int c = s.charAt(i); + if (0 != c && c <= 0x7F) { + --limit; + } else if (c < 0x7FF) { + limit -= 2; + } else { + limit -= 3; + } + if (limit < 0) { + return i; + } + } + return end; + } + + short addUtf8(String k) + { + int theIndex = itsUtf8Hash.get(k, -1); + if (theIndex == -1) { + int strLen = k.length(); + boolean tooBigString; + if (strLen > MAX_UTF_ENCODING_SIZE) { + tooBigString = true; + } else { + tooBigString = false; + // Ask for worst case scenario buffer when each char takes 3 + // bytes + ensure(1 + 2 + strLen * 3); + int top = itsTop; + + itsPool[top++] = CONSTANT_Utf8; + top += 2; // skip length + + char[] chars = cfw.getCharBuffer(strLen); + k.getChars(0, strLen, chars, 0); + + for (int i = 0; i != strLen; i++) { + int c = chars[i]; + if (c != 0 && c <= 0x7F) { + itsPool[top++] = (byte)c; + } else if (c > 0x7FF) { + itsPool[top++] = (byte)(0xE0 | (c >> 12)); + itsPool[top++] = (byte)(0x80 | ((c >> 6) & 0x3F)); + itsPool[top++] = (byte)(0x80 | (c & 0x3F)); + } else { + itsPool[top++] = (byte)(0xC0 | (c >> 6)); + itsPool[top++] = (byte)(0x80 | (c & 0x3F)); + } + } + + int utfLen = top - (itsTop + 1 + 2); + if (utfLen > MAX_UTF_ENCODING_SIZE) { + tooBigString = true; + } else { + // Write back length + itsPool[itsTop + 1] = (byte)(utfLen >>> 8); + itsPool[itsTop + 2] = (byte)utfLen; + + itsTop = top; + theIndex = itsTopIndex++; + itsUtf8Hash.put(k, theIndex); + } + } + if (tooBigString) { + throw new IllegalArgumentException("Too big string"); + } + } + 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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Arguments.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Arguments.java new file mode 100644 index 0000000..954b078 --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/BaseFunction.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/BaseFunction.java new file mode 100644 index 0000000..d7d8992 --- /dev/null +++ b/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. + *

+ * + * foo = new Foo();
+ * foo instanceof Foo; // true
+ *
+ * + * @param instance The value that appeared on the LHS of the instanceof + * operator + * @return true if the "prototype" property of "this" appears in + * value's prototype chain + * + */ + 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 thisObj argument when invoking + * {@link #call}. + * The methos is allowed to return null to indicate that + * {@link #call} will create a new object itself. In this case + * {@link #construct} will set scope and prototype on the result + * {@link #call} unless they are already set. + */ + public Scriptable createObject(Context cx, Scriptable scope) + { + Scriptable newInstance = new NativeObject(); + newInstance.setPrototype(getClassPrototype()); + newInstance.setParentScope(getParentScope()); + return newInstance; + } + + /** + * Decompile the source information associated with this js + * function/script back into a string. + * + * @param indent How much to indent the decompiled result. + * + * @param flags Flags specifying format of decompilation output. + */ + String decompile(int indent, int flags) + { + 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() + { + // .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 .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 = ""; + 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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Callable.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Callable.java new file mode 100644 index 0000000..03e0fce --- /dev/null +++ b/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 this object + * @param args the array of arguments + * @return the result of the call + */ + public Object call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args); +} + diff --git a/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ClassCache.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ClassCache.java new file mode 100644 index 0000000..9047278 --- /dev/null +++ b/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,JavaMembers> classTable + = new HashMap,JavaMembers>(); + private HashMap,JavaMembers> javaAdapterGeneratedClasses + = new HashMap,JavaMembers>(); + private HashMap> classAdapterCache + = new HashMap>(); + private HashMap,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. + *

+ * By default, the engine will cache the results of + * Class.getMethods() and similar calls. + * This can speed execution dramatically, but increases the memory + * footprint. Also, with caching enabled, references may be held to + * objects past the lifetime of any real usage. + *

+ * If caching is enabled and this method is called with a + * false argument, the caches will be emptied. + *

+ * Caching is enabled by default. + * + * @param enabled if true, caching is enabled + * + * @see #clearCaches() + */ + public synchronized void setCachingEnabled(boolean enabled) + { + if (enabled == cachingIsEnabled) + return; + if (!enabled) + clearCaches(); + cachingIsEnabled = enabled; + } + + /** + * @return a map from classes to associated JavaMembers objects + */ + Map,JavaMembers> getClassCacheMap() { + return classTable; + } + + Map> 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,Object>(); + } + interfaceAdapterCache.put(cl, iadapter); + } + } +} diff --git a/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ClassShutter.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ClassShutter.java new file mode 100644 index 0000000..d5f4cd6 --- /dev/null +++ b/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. + *

+ * An embedding may filter which Java classes are exposed through + * LiveConnect to JavaScript scripts. + *

+ * Due to the fact that there is no package reflection in Java, + * this method will also be called with package names. There + * is no way for Rhino to tell if "Packages.a.b" is a package name + * or a class that doesn't exist. What Rhino does is attempt + * to load each segment of "Packages.a.b.c": It first attempts to + * load class "a", then attempts to load class "a.b", then + * finally attempts to load class "a.b.c". On a Rhino installation + * without any ClassShutter set, and without any of the + * above classes, the expression "Packages.a.b.c" will result in + * a [JavaPackage a.b.c] and not an error. + *

+ * With ClassShutter supplied, Rhino will first call + * visibleToScripts before attempting to look up the class name. If + * visibleToScripts returns false, the class name lookup is not + * performed and subsequent Rhino execution assumes the class is + * not present. So for "java.lang.System.out.println" the lookup + * of "java.lang.System" is skipped and thus Rhino assumes that + * "java.lang.System" doesn't exist. So then for "java.lang.System.out", + * Rhino attempts to load the class "java.lang.System.out" because + * it assumes that "java.lang.System" is a package name. + *

+ * @param fullClassName the full name of the class (including the package + * name, with '.' as a delimiter). For example the + * standard string class is "java.lang.String" + * @return whether or not to reveal this class to scripts + */ + public boolean visibleToScripts(String fullClassName); +} diff --git a/infrastructure/rhino1_7R1/src/org/mozilla/javascript/CompilerEnvirons.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/CompilerEnvirons.java new file mode 100644 index 0000000..645d098 --- /dev/null +++ b/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. + *

+ * 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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ConstProperties.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ConstProperties.java new file mode 100644 index 0000000..860db79 --- /dev/null +++ b/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. + *

+ * The property is specified by a string name + * as defined for Scriptable.get. + *

+ * The possible values that may be passed in are as defined for + * Scriptable.get. A class that implements this method may choose + * to ignore calls to set certain properties, in which case those + * properties are effectively read-only.

+ * For properties defined in a prototype chain, + * use putProperty in ScriptableObject.

+ * Note that if a property a is defined in the prototype p + * of an object o, then evaluating o.a = 23 will cause + * set to be called on the prototype p with + * o as the start parameter. + * To preserve JavaScript semantics, it is the Scriptable + * object's responsibility to modify o.

+ * This design allows properties to be defined in prototypes and implemented + * in terms of getters and setters of Java values without consuming slots + * in each instance.

+ *

+ * The values that may be set are limited to the following: + *

+ * Arbitrary Java objects may be wrapped in a Scriptable by first calling + * Context.toObject. This allows the property of a JavaScript + * object to contain an arbitrary Java object as a value.

+ * Note that has will be called by the runtime first before + * set is called to determine in which object the + * property is defined. + * Note that this method is not expected to traverse the prototype chain, + * which is different from the ECMA [[Put]] operation. + * @param name the name of the property + * @param start the object whose property is being set + * @param value value to set the property to + * @see org.mozilla.javascript.Scriptable#has(String, Scriptable) + * @see org.mozilla.javascript.Scriptable#get(String, Scriptable) + * @see org.mozilla.javascript.ScriptableObject#putProperty(Scriptable, String, Object) + * @see org.mozilla.javascript.Context#toObject(Object, Scriptable) + */ + public void putConst(String name, Scriptable start, Object value); + + /** + * Reserves a definition spot for a const. This will set up a definition + * of the const property, but set its value to undefined. The semantics of + * the start parameter is the same as for putConst. + * @param name The name of the property. + * @param start The object whose property is being reserved. + */ + public void defineConst(String name, Scriptable start); + + /** + * Returns true if the named property is defined as a const on this object. + * @param name + * @return true if the named property is defined as a const, false + * otherwise. + */ + public boolean isConst(String name); +} diff --git a/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Context.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Context.java new file mode 100644 index 0000000..0833883 --- /dev/null +++ b/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.

+ * + * Different forms of script execution are supported. Scripts may be + * evaluated from the source directly, or first compiled and then later + * executed. Interactive execution is also supported.

+ * + * Some aspects of script execution, such as type conversions and + * object creation, may be accessed directly through methods of + * Context. + * + * @see Scriptable + * @author Norris Boyd + * @author Brendan Eich + */ + +public class Context +{ + /** + * 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 Date.prototype.getYear(). + * If hasFeature(FEATURE_NON_ECMA_GET_YEAR) returns true, + * Date.prototype.getYear subtructs 1900 only if 1900 <= date < 2000. + * The default behavior of {@link #hasFeature(int)} is always to subtruct + * 1900 as rquired by ECMAScript B.2.4. + */ + public static final int FEATURE_NON_ECMA_GET_YEAR = 1; + + /** + * Control if member expression as function name extension is available. + * If hasFeature(FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME) returns + * true, allow function memberExpression(args) { body } to be + * syntax sugar for memberExpression = function(args) { body }, + * when memberExpression is not a simple identifier. + * See ECMAScript-262, section 11.2 for definition of memberExpression. + * By default {@link #hasFeature(int)} returns false. + */ + public static final int FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME = 2; + + /** + * Control if reserved keywords are treated as identifiers. + * If hasFeature(RESERVED_KEYWORD_AS_IDENTIFIER) returns true, + * treat future reserved keyword (see Ecma-262, section 7.5.3) as ordinary + * identifiers but warn about this usage. + * + * By default {@link #hasFeature(int)} returns false. + */ + public static final int FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER = 3; + + /** + * Control if toString() should returns the same result + * as toSource() when applied to objects and arrays. + * If hasFeature(FEATURE_TO_STRING_AS_SOURCE) returns true, + * calling toString() on JS objects gives the same result as + * calling toSource(). That is it returns JS source with code + * to create an object with all enumeratable fields of the original object + * instead of printing [object result of + * {@link Scriptable#getClassName()}]. + *

+ * By default {@link #hasFeature(int)} returns true only if + * the current JS version is set to {@link #VERSION_1_2}. + */ + public static final int FEATURE_TO_STRING_AS_SOURCE = 4; + + /** + * Control if properties __proto__ and __parent__ + * are treated specially. + * If hasFeature(FEATURE_PARENT_PROTO_PROPERTIES) returns true, + * treat __parent__ and __proto__ as special properties. + *

+ * The properties allow to query and set scope and prototype chains for the + * objects. The special meaning of the properties is available + * only when they are used as the right hand side of the dot operator. + * For example, while x.__proto__ = y changes the prototype + * chain of the object x to point to y, + * x["__proto__"] = y simply assigns a new value to the property + * __proto__ in x even when the feature is on. + * + * By default {@link #hasFeature(int)} returns true. + */ + public static final int FEATURE_PARENT_PROTO_PROPERTIES = 5; + + /** + * @deprecated In previous releases, this name was given to + * FEATURE_PARENT_PROTO_PROPERTIES. + */ + public static final int FEATURE_PARENT_PROTO_PROPRTIES = 5; + + /** + * Control if support for E4X(ECMAScript for XML) extension is available. + * If hasFeature(FEATURE_E4X) returns true, the XML syntax is available. + *

+ * By default {@link #hasFeature(int)} returns true if + * the current JS version is set to {@link #VERSION_DEFAULT} + * or is at least {@link #VERSION_1_6}. + * @since 1.6 Release 1 + */ + public static final int FEATURE_E4X = 6; + + /** + * Control if dynamic scope should be used for name access. + * If hasFeature(FEATURE_DYNAMIC_SCOPE) returns true, then the name lookup + * during name resolution will use the top scope of the script or function + * which is at the top of JS execution stack instead of the top scope of the + * script or function from the current stack frame if the top scope of + * the top stack frame contains the top scope of the current stack frame + * on its prototype chain. + *

+ * This is useful to define shared scope containing functions that can + * be called from scripts and functions using private scopes. + *

+ * By default {@link #hasFeature(int)} returns false. + * @since 1.6 Release 1 + */ + public static final int FEATURE_DYNAMIC_SCOPE = 7; + + /** + * Control if strict variable mode is enabled. + * When the feature is on Rhino reports runtime errors if assignment + * to a global variable that does not exist is executed. When the feature + * is off such assignments creates new variable in the global scope as + * required by ECMA 262. + *

+ * By default {@link #hasFeature(int)} returns false. + * @since 1.6 Release 1 + */ + public static final int FEATURE_STRICT_VARS = 8; + + /** + * Control if strict eval mode is enabled. + * When the feature is on Rhino reports runtime errors if non-string + * argument is passed to the eval function. When the feature is off + * eval simply return non-string argument as is without performing any + * evaluation as required by ECMA 262. + *

+ * By default {@link #hasFeature(int)} returns false. + * @since 1.6 Release 1 + */ + public static final int FEATURE_STRICT_EVAL = 9; + + /** + * When the feature is on Rhino will add a "fileName" and "lineNumber" + * properties to Error objects automatically. When the feature is off, you + * have to explicitly pass them as the second and third argument to the + * Error constructor. Note that neither 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. + *

+ * By default {@link #hasFeature(int)} returns false. + * @since 1.6 Release 6 + */ + public static final int FEATURE_LOCATION_INFORMATION_IN_ERROR = 10; + + /** + * Controls whether JS 1.5 'strict mode' is enabled. + * When the feature is on, Rhino reports more than a dozen different + * warnings. When the feature is off, these warnings are not generated. + * FEATURE_STRICT_MODE implies FEATURE_STRICT_VARS and FEATURE_STRICT_EVAL. + *

+ * By default {@link #hasFeature(int)} returns false. + * @since 1.6 Release 6 + */ + public static final int FEATURE_STRICT_MODE = 11; + + /** + * Controls whether a warning should be treated as an error. + * @since 1.6 Release 6 + */ + public static final int FEATURE_WARNING_AS_ERROR = 12; + + /** + * Enables enhanced access to Java. + * Specifically, controls whether private and protected members can be + * accessed, and whether scripts can catch all Java exceptions. + *

+ * Note that this feature should only be enabled for trusted scripts. + *

+ * By default {@link #hasFeature(int)} returns false. + * @since 1.7 Release 1 + */ + public static final int FEATURE_ENHANCED_JAVA_ACCESS = 13; + + + 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.

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

+ * The same as enter() except that cx + * is associated with the current thread and returned if + * the current thread has no associated context and cx + * is not associated with any other thread. + * @param cx a Context to associate with the thread if possible + * @return a Context associated with the current thread + * @deprecated use {@link ContextFactory#enterContext(Context)} instead as + * this method relies on usage of a static singleton "global" ContextFactory. + * @see ContextFactory#enterContext(Context) + * @see ContextFactory#call(ContextAction) + */ + public static Context enter(Context cx) + { + return enter(cx, ContextFactory.getGlobal()); + } + + static final Context enter(Context cx, ContextFactory factory) + { + Object helper = VMBridge.instance.getThreadContextHelper(); + Context old = VMBridge.instance.getContext(helper); + if (old != null) { + cx = old; + } else { + if (cx == null) { + cx = factory.makeContext(); + if (cx.enterCount != 0) { + throw new IllegalStateException("factory.makeContext() returned Context instance already associated with some thread"); + } + factory.onContextCreated(cx); + if (factory.isSealed() && !cx.isSealed()) { + cx.seal(null); + } + } else { + if (cx.enterCount != 0) { + throw new IllegalStateException("can not use Context instance already associated with some thread"); + } + } + VMBridge.instance.setContext(helper, cx); + } + ++cx.enterCount; + return cx; + } + + /** + * Exit a block of code requiring a Context. + * + * Calling exit() will remove the association between + * the current thread and a Context if the prior call to + * {@link ContextFactory#enterContext()} on this thread newly associated a + * Context with this thread. Once the current thread no longer has an + * associated Context, it cannot be used to execute JavaScript until it is + * again associated with a Context. + * @see ContextFactory#enterContext() + */ + public static void exit() + { + Object helper = VMBridge.instance.getThreadContextHelper(); + Context cx = VMBridge.instance.getContext(helper); + if (cx == null) { + throw new IllegalStateException( + "Calling Context.exit without previous Context.enter"); + } + if (cx.enterCount < 1) Kit.codeBug(); + if (--cx.enterCount == 0) { + VMBridge.instance.setContext(helper, null); + cx.factory.onContextReleased(cx); + } + } + + /** + * Call {@link ContextAction#run(Context cx)} + * using the Context instance associated with the current thread. + * If no Context is associated with the thread, then + * ContextFactory.getGlobal().makeContext() will be called to + * construct new Context instance. The instance will be temporary + * associated with the thread during call to + * {@link ContextAction#run(Context)}. + * @deprecated use {@link ContextFactory#call(ContextAction)} instead as + * this method relies on usage of a static singleton "global" + * ContextFactory. + * @return The result of {@link ContextAction#run(Context)}. + */ + 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)}. + *

+ * It is allowed but not advisable to use null for factory + * argument in which case the global static singleton ContextFactory + * instance will be used to create new context instances. + * @see ContextFactory#call(ContextAction) + */ + public static Object call(ContextFactory factory, final Callable callable, + final Scriptable scope, final Scriptable thisObj, + final Object[] args) + { + if(factory == null) { + factory = ContextFactory.getGlobal(); + } + return call(factory, 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. + *

+ * If sealKey is not null, calling + * {@link #unseal(Object sealKey)} with the same key unseals + * the object. If sealKey is null, unsealing is no longer possible. + * + * @see #isSealed() + * @see #unseal(Object) + */ + public final void seal(Object sealKey) + { + if (sealed) onSealedMutation(); + sealed = true; + this.sealKey = sealKey; + } + + /** + * Unseal previously sealed Context object. + * The sealKey argument should not be null and should match + * sealKey suplied with the last call to + * {@link #seal(Object)} or an exception will be thrown. + * + * @see #isSealed() + * @see #seal(Object sealKey) + */ + public final void unseal(Object sealKey) + { + if (sealKey == null) throw new IllegalArgumentException(); + if (this.sealKey != sealKey) throw new IllegalArgumentException(); + if (!sealed) throw new IllegalStateException(); + sealed = false; + this.sealKey = null; + } + + static void onSealedMutation() + { + throw new IllegalStateException(); + } + + /** + * Get the current language version. + *

+ * The language version number affects JavaScript semantics as detailed + * in the overview documentation. + * + * @return an integer that is one of VERSION_1_0, VERSION_1_1, etc. + */ + public final int getLanguageVersion() + { + return version; + } + + /** + * Set the language version. + * + *

+ * Setting the language version will affect functions and scripts compiled + * subsequently. See the overview documentation for version-specific + * behavior. + * + * @param version the version as specified by VERSION_1_0, VERSION_1_1, etc. + */ + public void setLanguageVersion(int version) + { + if (sealed) onSealedMutation(); + checkLanguageVersion(version); + Object listeners = propertyListeners; + if (listeners != null && version != this.version) { + firePropertyChangeImpl(listeners, languageVersionProperty, + 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. + * + *

+ * The implementation version is of the form + *

+     *    "name langVer release relNum date"
+     * 
+ * where name is the name of the product, langVer is + * the language version, relNum is the release number, and + * date is the release date for that specific + * release in the form "yyyy mm dd". + * + * @return a string that encodes the product, language version, release + * number, and date. + */ + public final String getImplementationVersion() + { + // 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.

+ * + * This method must be called to initialize a scope before scripts + * can be evaluated in that scope.

+ * + * This method does not affect the Context it is called upon. + * + * @return the initialized scope + */ + public final ScriptableObject initStandardObjects() + { + return initStandardObjects(null, false); + } + + /** + * Initialize the standard objects. + * + * Creates instances of the standard objects and their constructors + * (Object, String, Number, Date, etc.), setting up 'scope' to act + * as a global object as in ECMA 15.1.

+ * + * This method must be called to initialize a scope before scripts + * can be evaluated in that scope.

+ * + * This method does not affect the Context it is called upon. + * + * @param scope the scope to initialize, or null, in which case a new + * object will be created to serve as the scope + * @return the initialized scope. The method returns the value of the scope + * argument if it is not null or newly allocated scope object which + * is an instance {@link ScriptableObject}. + */ + public final Scriptable initStandardObjects(ScriptableObject scope) + { + return initStandardObjects(scope, false); + } + + /** + * Initialize the standard objects. + * + * Creates instances of the standard objects and their constructors + * (Object, String, Number, Date, etc.), setting up 'scope' to act + * as a global object as in ECMA 15.1.

+ * + * This method must be called to initialize a scope before scripts + * can be evaluated in that scope.

+ * + * This method does not affect the Context it is called upon.

+ * + * This form of the method also allows for creating "sealed" standard + * objects. An object that is sealed cannot have properties added, changed, + * or removed. This is useful to create a "superglobal" that can be shared + * among several top-level objects. Note that sealing is not allowed in + * the current ECMA/ISO language specification, but is likely for + * the next version. + * + * @param scope the scope to initialize, or null, in which case a new + * object will be created to serve as the scope + * @param sealed whether or not to create sealed standard objects that + * cannot be modified. + * @return the initialized scope. The method returns the value of the scope + * argument if it is not null or newly allocated scope object. + * @since 1.4R3 + */ + public ScriptableObject initStandardObjects(ScriptableObject scope, + boolean sealed) + { + return ScriptRuntime.initStandardObjects(this, scope, sealed); + } + + /** + * 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. + *

+ * stringIsCompilableUnit is intended to support interactive compilation of + * javascript. If compiling the string would result in an error + * that might be fixed by appending more source, this method + * returns false. In every other case, it returns true. + *

+ * Interactive shells may accumulate source lines, using this + * method after each new line is appended to check whether the + * statement being entered is complete. + * + * @param source the source buffer to check + * @return whether the source is ready for compilation + * @since 1.4 Release 2 + */ + public final boolean stringIsCompilableUnit(String source) + { + boolean errorseen = false; + CompilerEnvirons compilerEnv = new CompilerEnvirons(); + compilerEnv.initFromContext(this); + // no source name or source text manager, because we're just + // going to throw away the result. + compilerEnv.setGeneratingSource(false); + /*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. + *

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

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

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

+ * The canonical source of the script is returned. + * + * @param script the script to decompile + * @param indent the number of spaces to indent the result + * @return a string representing the script source + */ + public final String decompileScript(Script script, int indent) + { + NativeFunction scriptImpl = (NativeFunction) script; + return scriptImpl.decompile(indent, 0); + } + + /** + * Decompile a JavaScript Function. + *

+ * Decompiles a previously compiled JavaScript function object to + * canonical source. + *

+ * Returns function body of '[native code]' if no decompilation + * information is available. + * + * @param fun the JavaScript function to decompile + * @param indent the number of spaces to indent the result + * @return a string representing the function source + */ + public final String decompileFunction(Function fun, int indent) + { + if (fun instanceof BaseFunction) + return ((BaseFunction)fun).decompile(indent, 0); + else + return "function " + fun.getClassName() + + "() {\n\t[native code]\n}\n"; + } + + /** + * Decompile the body of a JavaScript Function. + *

+ * Decompiles the body a previously compiled JavaScript Function + * object to canonical source, omitting the function header and + * trailing brace. + * + * Returns '[native code]' if no decompilation information is available. + * + * @param fun the JavaScript function to decompile + * @param indent the number of spaces to indent the result + * @return a string representing the function body source. + */ + public final String decompileFunctionBody(Function fun, int indent) + { + if (fun instanceof BaseFunction) { + BaseFunction bf = (BaseFunction)fun; + return bf.decompile(indent, Decompiler.ONLY_BODY_FLAG); + } + // ALERT: not sure what the right response here is. + return "[native code]\n"; + } + + /** + * Create a new JavaScript object. + * + * Equivalent to evaluating "new Object()". + * @param scope the scope to search for the constructor and to evaluate + * against + * @return the new object + */ + public final Scriptable newObject(Scriptable scope) + { + return newObject(scope, "Object", ScriptRuntime.emptyArgs); + } + + /** + * Create a new JavaScript object by executing the named constructor. + * + * The call newObject(scope, "Foo") is equivalent to + * evaluating "new Foo()". + * + * @param scope the scope to search for the constructor and to evaluate against + * @param constructorName the name of the constructor to call + * @return the new object + */ + public final Scriptable newObject(Scriptable scope, String constructorName) + { + return newObject(scope, constructorName, ScriptRuntime.emptyArgs); + } + + /** + * Creates a new JavaScript object by executing the named constructor. + * + * Searches scope for the named constructor, calls it with + * the given arguments, and returns the result.

+ * + * The code + *

+     * Object[] args = { "a", "b" };
+     * newObject(scope, "Foo", args)
+ * is equivalent to evaluating "new Foo('a', 'b')", assuming that the Foo + * constructor has been defined in scope. + * + * @param scope The scope to search for the constructor and to evaluate + * against + * @param constructorName the name of the constructor to call + * @param args the array of arguments for the constructor + * @return the new object + */ + public 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. + *

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

+ * If the object defines a length property convertible to double number, + * then the number is converted Uint32 value as defined in Ecma 9.6 + * and Java array of that size is allocated. + * The array is initialized with the values obtained by + * calling get() on object for each value of i in [0,length-1]. If + * there is not a defined value for a property the Undefined value + * is used to initialize the corresponding element in the array. The + * Java array is then returned. + * If the object doesn't define a length property or it is not a number, + * empty array is returned. + * @param object the JavaScript array or array-like object + * @return a Java array of objects + * @since 1.4 release 2 + */ + public final Object[] getElements(Scriptable object) + { + return ScriptRuntime.getArrayElements(object); + } + + /** + * Convert the value to a JavaScript boolean value. + *

+ * See ECMA 9.2. + * + * @param value a JavaScript value + * @return the corresponding boolean value converted using + * the ECMA rules + */ + public static boolean toBoolean(Object value) + { + return ScriptRuntime.toBoolean(value); + } + + /** + * Convert the value to a JavaScript Number value. + *

+ * Returns a Java double for the JavaScript Number. + *

+ * See ECMA 9.3. + * + * @param value a JavaScript value + * @return the corresponding double value converted using + * the ECMA rules + */ + public static double toNumber(Object value) + { + return ScriptRuntime.toNumber(value); + } + + /** + * Convert the value to a JavaScript String value. + *

+ * See ECMA 9.8. + *

+ * @param value a JavaScript value + * @return the corresponding String value converted using + * the ECMA rules + */ + public static String toString(Object value) + { + return ScriptRuntime.toString(value); + } + + /** + * Convert the value to an JavaScript object value. + *

+ * Note that a scope must be provided to look up the constructors + * for Number, Boolean, and String. + *

+ * See ECMA 9.9. + *

+ * Additionally, arbitrary Java objects and classes will be + * wrapped in a Scriptable object with its Java fields and methods + * reflected as JavaScript properties of the object. + * + * @param value any Java object + * @param scope global scope containing constructors for Number, + * Boolean, and String + * @return new JavaScript object + */ + public static Scriptable toObject(Object value, Scriptable scope) + { + return ScriptRuntime.toObject(scope, value); + } + + /** + * @deprecated + * @see #toObject(Object, Scriptable) + */ + public static Scriptable toObject(Object value, Scriptable scope, + Class staticType) + { + return ScriptRuntime.toObject(scope, value); + } + + /** + * Convenient method to convert java value to its closest representation + * in JavaScript. + *

+ * If value is an instance of String, Number, Boolean, Function or + * Scriptable, it is returned as it and will be treated as the corresponding + * JavaScript type of string, number, boolean, function and object. + *

+ * Note that for Number instances during any arithmetic operation in + * JavaScript the engine will always use the result of + * Number.doubleValue() resulting in a precision loss if + * the number can not fit into double. + *

+ * If value is an instance of Character, it will be converted to string of + * length 1 and its JavaScript type will be string. + *

+ * The rest of values will be wrapped as LiveConnect objects + * by calling {@link WrapFactory#wrap(Context cx, Scriptable scope, + * Object obj, Class staticType)} as in: + *

+     *    Context cx = Context.getCurrentContext();
+     *    return cx.getWrapFactory().wrap(cx, scope, value, null);
+     * 
+ * + * @param value any Java object + * @param scope top scope object + * @return value suitable to pass to any API that takes JavaScript values. + */ + public static Object javaToJS(Object value, Scriptable scope) + { + 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. + *

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

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

+ * Setting the generation of debug information on will set the + * optimization level to zero. + * @since 1.3 + */ + public final void setGeneratingDebug(boolean generatingDebug) + { + if (sealed) onSealedMutation(); + generatingDebugChanged = true; + if (generatingDebug && getOptimizationLevel() > 0) + setOptimizationLevel(0); + this.generatingDebug = generatingDebug; + } + + /** + * Tell whether source information is being generated. + * @since 1.3 + */ + public final boolean isGeneratingSource() + { + return generatingSource; + } + + /** + * Specify whether or not source information should be generated. + *

+ * Without source information, evaluating the "toString" method + * on JavaScript functions produces only "[native code]" for + * the body of the function. + * Note that code generated without source is not fully ECMA + * conformant. + * @since 1.3 + */ + public final void setGeneratingSource(boolean generatingSource) + { + if (sealed) onSealedMutation(); + this.generatingSource = generatingSource; + } + + /** + * Get the current optimization level. + *

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

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

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

{@link ClassShutter} may only be set if it is currently null. + * Otherwise a SecurityException is thrown. + * @param shutter a ClassShutter object + * @throws SecurityException if there is already a ClassShutter + * object for this Context + */ + public final 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. + *

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

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

+ * @param key the key used to index the value + * @param value the value to save + */ + public final 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. + *

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

+ * The default implementation calls + * {@link ContextFactory#hasFeature(Context cx, int featureIndex)} + * that allows to customize Context behavior without introducing + * Context subclasses. {@link ContextFactory} documentation gives + * an example of hasFeature implementation. + * + * @param featureIndex feature index to check + * @return true if the featureIndex feature is turned on + * @see #FEATURE_NON_ECMA_GET_YEAR + * @see #FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME + * @see #FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER + * @see #FEATURE_TO_STRING_AS_SOURCE + * @see #FEATURE_PARENT_PROTO_PROPRTIES + * @see #FEATURE_E4X + * @see #FEATURE_DYNAMIC_SCOPE + * @see #FEATURE_STRICT_VARS + * @see #FEATURE_STRICT_EVAL + * @see #FEATURE_LOCATION_INFORMATION_IN_ERROR + * @see #FEATURE_STRICT_MODE + * @see #FEATURE_WARNING_AS_ERROR + * @see #FEATURE_ENHANCED_JAVA_ACCESS + */ + public boolean hasFeature(int featureIndex) + { + ContextFactory f = getFactory(); + return f.hasFeature(this, featureIndex); + } + + /** + Returns an object which specifies an E4X implementation to use within + this Context. Note + that the XMLLib.Factory interface should be considered experimental. + + The default implementation uses the implementation provided by this + Context's {@link ContextFactory}. + + @return An XMLLib.Factory. Should not return null if + {@link #FEATURE_E4X} is enabled. See {@link #hasFeature}. + */ + public XMLLib.Factory getE4xImplementationFactory() { + return getFactory().getE4xImplementationFactory(); + } + + /** + * Get threshold of executed instructions counter that triggers call to + * observeInstructionCount(). + * When the threshold is zero, instruction counting is disabled, + * otherwise each time the run-time executes at least the threshold value + * of script instructions, observeInstructionCount() will + * be called. + */ + public final int getInstructionObserverThreshold() + { + return instructionThreshold; + } + + /** + * Set threshold of executed instructions counter that triggers call to + * observeInstructionCount(). + * When the threshold is zero, instruction counting is disabled, + * otherwise each time the run-time executes at least the threshold value + * of script instructions, observeInstructionCount() will + * be called.

+ * Note that the meaning of "instruction" is not guaranteed to be + * consistent between compiled and interpretive modes: executing a given + * script or function in the different modes will result in different + * instruction counts against the threshold. + * {@link #setGenerateObserverCount} is called with true if + * threshold is greater than zero, false otherwise. + * @param threshold The instruction threshold + */ + public final void setInstructionObserverThreshold(int threshold) + { + if (sealed) onSealedMutation(); + if (threshold < 0) throw new IllegalArgumentException(); + instructionThreshold = threshold; + setGenerateObserverCount(threshold > 0); + } + + /** + * Turn on or off generation of code with callbacks to + * track the count of executed instructions. + * Currently only affects JVM byte code generation: this slows down the + * generated code, but code generated without the callbacks will not + * be counted toward instruction thresholds. Rhino's interpretive + * mode does instruction counting without inserting callbacks, so + * there is no requirement to compile code differently. + * @param generateObserverCount if true, generated code will contain + * calls to accumulate an estimate of the instructions executed. + */ + public void setGenerateObserverCount(boolean generateObserverCount) { + this.generateObserverCount = generateObserverCount; + } + + /** + * Allow application to monitor counter of executed script instructions + * in Context subclasses. + * Run-time calls this when instruction counting is enabled and the counter + * reaches limit set by setInstructionObserverThreshold(). + * The method is useful to observe long running scripts and if necessary + * to terminate them. + *

+ * The instruction counting support is available only for interpreted + * scripts generated when the optimization level is set to -1. + *

+ * The default implementation calls + * {@link ContextFactory#observeInstructionCount(Context cx, + * int instructionCount)} + * that allows to customize Context behavior without introducing + * Context subclasses. + * + * @param instructionCount amount of script instruction executed since + * last call to observeInstructionCount + * @throws Error to terminate the script + * @see #setOptimizationLevel(int) + */ + protected void observeInstructionCount(int instructionCount) + { + ContextFactory f = getFactory(); + f.observeInstructionCount(this, instructionCount); + } + + /** + * Create class loader for generated classes. + * The method calls {@link ContextFactory#createClassLoader(ClassLoader)} + * using the result of {@link #getFactory()}. + */ + public GeneratedClassLoader createClassLoader(ClassLoader parent) + { + ContextFactory f = getFactory(); + return f.createClassLoader(parent); + } + + public final ClassLoader getApplicationClassLoader() + { + if (applicationClassLoader == null) { + ContextFactory f = getFactory(); + ClassLoader loader = f.getApplicationClassLoader(); + if (loader == null) { + ClassLoader threadLoader + = 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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextAction.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextAction.java new file mode 100644 index 0000000..1c584a9 --- /dev/null +++ b/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, cx 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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextFactory.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextFactory.java new file mode 100644 index 0000000..4f9fde2 --- /dev/null +++ b/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 ContextFactory can also notify listeners + * about context creation and release. + *

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

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

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

+ * import org.mozilla.javascript.*;
+ *
+ * class MyFactory extends ContextFactory
+ * {
+ *
+ *     // Custom {@link Context} to store execution time.
+ *     private static class MyContext extends Context
+ *     {
+ *         long startTime;
+ *     }
+ *
+ *     static {
+ *         // Initialize GlobalFactory with custom factory
+ *         ContextFactory.initGlobal(new MyFactory());
+ *     }
+ *
+ *     // Override {@link #makeContext()}
+ *     protected Context makeContext()
+ *     {
+ *         MyContext cx = new MyContext();
+ *         // 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);
+ *     }
+ *
+ * }
+ *
+ * 
+ */ + +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. makeContext() is allowed to call + * {@link Context#seal(Object)} on the result to prevent + * {@link Context} changes by hostile scripts or applets. + */ + protected Context makeContext() + { + return new Context(this); + } + + /** + * Implementation of {@link Context#hasFeature(int featureIndex)}. + * This can be used to customize {@link Context} without introducing + * additional subclasses. + */ + protected boolean hasFeature(Context cx, int featureIndex) + { + int version; + switch (featureIndex) { + case Context.FEATURE_NON_ECMA_GET_YEAR: + /* + * During the great date rewrite of 1.3, we tried to track the + * evolving ECMA standard, which then had a definition of + * getYear which always subtracted 1900. Which we + * implemented, not realizing that it was incompatible with + * the old behavior... now, rather than thrash the behavior + * yet again, we've decided to leave it with the - 1900 + * behavior and point people to the getFullYear method. But + * we try to protect existing scripts that have specified a + * version... + */ + version = cx.getLanguageVersion(); + return (version == Context.VERSION_1_0 + || version == Context.VERSION_1_1 + || version == Context.VERSION_1_2); + + case Context.FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME: + return false; + + case Context.FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER: + return 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 Context instances produced by this + * factory. See {@link Context#getE4xImplementationFactory} for details. + * + * May return null, in which case E4X functionality is not supported in + * Rhino. + * + * The default implementation now prefers the DOM3 E4X implementation. + */ + protected org.mozilla.javascript.xml.XMLLib.Factory + getE4xImplementationFactory() + { + // Must provide default implementation, rather than abstract method, + // so that past implementors of ContextFactory do not fail at runtime + // upon invocation of this method. + // Note that the default implementation returns null if we + // neither have XMLBeans nor a DOM3 implementation present. + + if (isDom3Present()) { + return org.mozilla.javascript.xml.XMLLib.Factory.create( + "org.mozilla.javascript.xmlimpl.XMLLibImpl" + ); + } 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. + *

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

+     *      Context cx = contextFactory.enterContext();
+     *      try {
+     *          ...
+     *          cx.evaluateString(...);
+     *      } finally {
+     *          Context.exit();
+     *      }
+     * 
+ * Instead of using enterContext(), exit() pair consider + * using {@link #call(ContextAction)} which guarantees proper association + * of Context instances with the current thread. + * With this method the above example becomes: + *
+     *      ContextFactory.call(new ContextAction() {
+     *          public Object run(Context cx) {
+     *              ...
+     *              cx.evaluateString(...);
+     *              return null;
+     *          }
+     *      });
+     * 
+ * @return a Context associated with the current thread + * @see Context#getCurrentContext() + * @see Context#exit() + * @see #call(ContextAction) + */ + public Context enterContext() + { + return enterContext(null); + } + + /** + * @deprecated use {@link #enterContext()} instead + * @return a Context associated with the current thread + */ + 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. + *

+ * The same as enterContext() except that cx + * is associated with the current thread and returned if the current thread + * has no associated context and cx is not associated with any + * other thread. + * @param cx a Context to associate with the thread if possible + * @return a Context associated with the current thread + * @see #enterContext() + * @see #call(ContextAction) + * @throws IllegalStateException if cx is already associated + * with a different thread + */ + public final Context enterContext(Context cx) + { + return Context.enter(cx, this); + } +} \ No newline at end of file diff --git a/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextListener.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ContextListener.java new file mode 100644 index 0000000..5e17145 --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DToA.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DToA.java new file mode 100644 index 0000000..ad2a68a --- /dev/null +++ b/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 digits after the decimal point; exponential if number is large */ + DTOSTR_EXPONENTIAL = 3, /* Always exponential format; significant digits */ + DTOSTR_PRECISION = 4; /* Either fixed or exponential format; significant digits */ + + + private static final int Frac_mask = 0xfffff; + private static final int Exp_shift = 20; + private static final int Exp_msk1 = 0x100000; + + private static final long Frac_maskL = 0xfffffffffffffL; + private static final int Exp_shiftL = 52; + private static final long Exp_msk1L = 0x10000000000000L; + + private static final int Bias = 1023; + private static final int P = 53; + + private static final int Exp_shift1 = 20; + private static final int Exp_mask = 0x7ff00000; + private static final int Exp_mask_shifted = 0x7ff; + private static final int Bndry_mask = 0xfffff; + private static final int Log2P = 1; + + private static final int Sign_bit = 0x80000000; + private static final int Exp_11 = 0x3ff00000; + private static final int Ten_pmax = 22; + private static final int Quick_max = 14; + private static final int Bletch = 0x10; + private static final int Frac_mask1 = 0xfffff; + private static final int Int_max = 14; + private static final int n_bigtens = 5; + + + private static final double tens[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22 + }; + + private static final double bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; + + private static int lo0bits(int y) + { + int k; + int x = y; + + if ((x & 7) != 0) { + if ((x & 1) != 0) + return 0; + if ((x & 2) != 0) { + return 1; + } + return 2; + } + k = 0; + if ((x & 0xffff) == 0) { + k = 16; + x >>>= 16; + } + if ((x & 0xff) == 0) { + k += 8; + x >>>= 8; + } + if ((x & 0xf) == 0) { + k += 4; + x >>>= 4; + } + if ((x & 0x3) == 0) { + k += 2; + x >>>= 2; + } + if ((x & 1) == 0) { + k++; + x >>>= 1; + if ((x & 1) == 0) + return 32; + } + return k; + } + + /* Return the number (0 through 32) of most significant zero bits in x. */ + private static int hi0bits(int x) + { + int k = 0; + + if ((x & 0xffff0000) == 0) { + k = 16; + x <<= 16; + } + if ((x & 0xff000000) == 0) { + k += 8; + x <<= 8; + } + if ((x & 0xf0000000) == 0) { + k += 4; + x <<= 4; + } + if ((x & 0xc0000000) == 0) { + k += 2; + x <<= 2; + } + if ((x & 0x80000000) == 0) { + k++; + if ((x & 0x40000000) == 0) + return 32; + } + return k; + } + + private static void stuffBits(byte bits[], int offset, int val) + { + bits[offset] = (byte)(val >> 24); + bits[offset + 1] = (byte)(val >> 16); + bits[offset + 2] = (byte)(val >> 8); + bits[offset + 3] = (byte)(val); + } + + /* Convert d into the form b*2^e, where b is an odd integer. b is the returned + * Bigint and e is the returned binary exponent. Return the number of significant + * bits in b in bits. d must be finite and nonzero. */ + private static BigInteger d2b(double d, int[] e, int[] bits) + { + byte dbl_bits[]; + int i, k, y, z, de; + long dBits = Double.doubleToLongBits(d); + int d0 = (int)(dBits >>> 32); + int d1 = (int)(dBits); + + z = d0 & Frac_mask; + d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ + + if ((de = (d0 >>> Exp_shift)) != 0) + z |= Exp_msk1; + + if ((y = d1) != 0) { + dbl_bits = new byte[8]; + k = lo0bits(y); + y >>>= k; + if (k != 0) { + stuffBits(dbl_bits, 4, y | z << (32 - k)); + z >>= k; + } + else + stuffBits(dbl_bits, 4, y); + stuffBits(dbl_bits, 0, z); + i = (z != 0) ? 2 : 1; + } + else { + // JS_ASSERT(z); + dbl_bits = new byte[4]; + k = lo0bits(z); + z >>>= k; + stuffBits(dbl_bits, 0, z); + k += 32; + i = 1; + } + if (de != 0) { + e[0] = de - Bias - (P-1) + k; + bits[0] = P - k; + } + else { + e[0] = de - Bias - (P-1) + 1 + k; + bits[0] = 32*i - hi0bits(z); + } + return new BigInteger(dbl_bits); + } + + static String JS_dtobasestr(int base, double d) + { + if (!(2 <= base && base <= 36)) + throw new IllegalArgumentException("Bad base: "+base); + + /* Check for Infinity and NaN */ + if (Double.isNaN(d)) { + return "NaN"; + } else if (Double.isInfinite(d)) { + return (d > 0.0) ? "Infinity" : "-Infinity"; + } else if (d == 0) { + // ALERT: should it distinguish -0.0 from +0.0 ? + return "0"; + } + + boolean negative; + if (d >= 0.0) { + negative = false; + } else { + negative = true; + d = -d; + } + + /* Get the integer part of d including '-' sign. */ + String intDigits; + + double dfloor = Math.floor(d); + long lfloor = (long)dfloor; + if (lfloor == dfloor) { + // int part fits long + intDigits = Long.toString((negative) ? -lfloor : lfloor, base); + } else { + // BigInteger should be used + long floorBits = Double.doubleToLongBits(dfloor); + int exp = (int)(floorBits >> Exp_shiftL) & Exp_mask_shifted; + long mantissa; + if (exp == 0) { + mantissa = (floorBits & Frac_maskL) << 1; + } else { + mantissa = (floorBits & Frac_maskL) | Exp_msk1L; + } + if (negative) { + mantissa = -mantissa; + } + exp -= 1075; + BigInteger x = BigInteger.valueOf(mantissa); + if (exp > 0) { + x = x.shiftLeft(exp); + } else if (exp < 0) { + x = x.shiftRight(-exp); + } + intDigits = x.toString(base); + } + + if (d == dfloor) { + // No fraction part + return intDigits; + } 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< 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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Decompiler.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Decompiler.java new file mode 100644 index 0000000..8547d37 --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DefaultErrorReporter.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DefaultErrorReporter.java new file mode 100644 index 0000000..c7d93d4 --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DefiningClassLoader.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/DefiningClassLoader.java new file mode 100644 index 0000000..5864b5d --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Delegator.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Delegator.java new file mode 100644 index 0000000..e044863 --- /dev/null +++ b/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. . + * 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 hint parameter is null, + * ScriptRuntime.ScriptableClass or + * ScriptRuntime.FunctionClass. Instead the object + * itself is returned. + * + * @param hint the type hint + * @return the default value + * + * @see org.mozilla.javascript.Scriptable#getDefaultValue + */ + 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 delegee is null, + * this method creates a new instance of the Delegator itself + * rathert than forwarding the call to the + * delegee. This permits the use of Delegator + * prototypes. + * + * @param cx the current Context for this thread + * @param scope an enclosing scope of the caller except + * when the function is called from a closure. + * @param args the array of arguments + * @return the allocated object + * + * @see Function#construct(Context, Scriptable, Object[]) + */ + 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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/EcmaError.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/EcmaError.java new file mode 100644 index 0000000..1fd8f03 --- /dev/null +++ b/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 null. + */ + public Scriptable getErrorObject() + { + return null; + } +} diff --git a/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ErrorReporter.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ErrorReporter.java new file mode 100644 index 0000000..4649370 --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Evaluator.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Evaluator.java new file mode 100644 index 0000000..e222af3 --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/EvaluatorException.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/EvaluatorException.java new file mode 100644 index 0000000..7b4e7cc --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Function.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Function.java new file mode 100644 index 0000000..a4377e6 --- /dev/null +++ b/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 this object + * @param args the array of arguments + * @return the result of the call + */ + public Object call(Context cx, Scriptable scope, Scriptable thisObj, + Object[] args); + + /** + * Call the function as a constructor. + * + * This method is invoked by the runtime in order to satisfy a use + * of the JavaScript new operator. This method is + * expected to create a new object and return it. + * + * @param cx the current Context for this thread + * @param scope an enclosing scope of the caller except + * when the function is called from a closure. + * @param args the array of arguments + * @return the allocated object + */ + public Scriptable construct(Context cx, Scriptable scope, Object[] args); +} diff --git a/infrastructure/rhino1_7R1/src/org/mozilla/javascript/FunctionNode.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/FunctionNode.java new file mode 100644 index 0000000..484167e --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/FunctionObject.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/FunctionObject.java new file mode 100644 index 0000000..8fa4e68 --- /dev/null +++ b/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. + * + *

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

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

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

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

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

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

+ * + * Otherwise, if the FunctionObject will not be used to define a + * constructor, the member must be a static Method with parameters + * (Context cx, Scriptable thisObj, Object[] args, + * Function funObj) + *

+     * and an Object result.

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

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

+ * + * @param name the name of the function + * @param methodOrConstructor a java.lang.reflect.Method or a java.lang.reflect.Constructor + * that defines the object + * @param scope enclosing scope of function + * @see org.mozilla.javascript.Scriptable + */ + public FunctionObject(String name, Member methodOrConstructor, + Scriptable scope) + { + if (methodOrConstructor instanceof Constructor) { + member = new MemberBox((Constructor) methodOrConstructor); + isStatic = true; // well, doesn't take a 'this' + } else { + member = new MemberBox((Method) methodOrConstructor); + isStatic = member.isStatic(); + } + String methodName = member.getName(); + this.functionName = name; + Class[] types = member.argTypes; + int arity = types.length; + if (arity == 4 && (types[1].isArray() || types[2].isArray())) { + // Either variable args or an error. + if (types[1].isArray()) { + if (!isStatic || + types[0] != ScriptRuntime.ContextClass || + types[1].getComponentType() != ScriptRuntime.ObjectClass || + types[2] != ScriptRuntime.FunctionClass || + types[3] != Boolean.TYPE) + { + throw Context.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 JAVA_*_TYPE constants to indicate desired type + * or {@link #JAVA_UNSUPPORTED_TYPE} if the convertion is not + * possible + */ + public static int getTypeTag(Class type) + { + if (type == ScriptRuntime.StringClass) + return JAVA_STRING_TYPE; + if (type == ScriptRuntime.IntegerClass || type == Integer.TYPE) + return JAVA_INT_TYPE; + if (type == ScriptRuntime.BooleanClass || type == Boolean.TYPE) + return JAVA_BOOLEAN_TYPE; + if (type == ScriptRuntime.DoubleClass || type == Double.TYPE) + return JAVA_DOUBLE_TYPE; + if (ScriptRuntime.ScriptableClass.isAssignableFrom(type)) + return JAVA_SCRIPTABLE_TYPE; + if (type == ScriptRuntime.ObjectClass) + return JAVA_OBJECT_TYPE; + + // Note that the long type is not supported; see the javadoc for + // the constructor for this class + + return JAVA_UNSUPPORTED_TYPE; + } + + public static Object convertArg(Context cx, Scriptable scope, + Object arg, int typeTag) + { + switch (typeTag) { + case JAVA_STRING_TYPE: + if (arg instanceof String) + return arg; + return ScriptRuntime.toString(arg); + case JAVA_INT_TYPE: + if (arg instanceof Integer) + return arg; + return 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. + *

+ * Sets up the "prototype" and "constructor" properties. Also + * calls setParent and setPrototype with appropriate values. + * Then adds the function object as a property of the given scope, using + * prototype.getClassName() + * as the name of the property. + * + * @param scope the scope in which to define the constructor (typically + * the global object) + * @param prototype the prototype object + * @see org.mozilla.javascript.Scriptable#setParentScope + * @see org.mozilla.javascript.Scriptable#setPrototype + * @see org.mozilla.javascript.Scriptable#getClassName + */ + public void addAsConstructor(Scriptable scope, Scriptable prototype) + { + initAsConstructor(scope, prototype); + defineProperty(scope, prototype.getClassName(), + this, ScriptableObject.DONTENUM); + } + + void initAsConstructor(Scriptable scope, Scriptable prototype) + { + ScriptRuntime.setFunctionProtoAndParent(this, scope); + setImmunePrototypeProperty(prototype); + + prototype.setParentScope(this); + + defineProperty(prototype, "constructor", this, + ScriptableObject.DONTENUM | + ScriptableObject.PERMANENT | + ScriptableObject.READONLY); + setParentScope(scope); + } + + /** + * @deprecated Use {@link #getTypeTag(Class)} + * and {@link #convertArg(Context, Scriptable, Object, int)} + * for type 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. + *

+ * 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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/GeneratedClassLoader.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/GeneratedClassLoader.java new file mode 100644 index 0000000..0f73615 --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IRFactory.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IRFactory.java new file mode 100644 index 0000000..1f51cb1 --- /dev/null +++ b/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 + *
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 destructuringNames = new ArrayList(); + 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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdFunctionCall.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdFunctionCall.java new file mode 100644 index 0000000..713fabf --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdFunctionObject.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdFunctionObject.java new file mode 100644 index 0000000..c578dfa --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdScriptableObject.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/IdScriptableObject.java new file mode 100644 index 0000000..2b3ecf3 --- /dev/null +++ b/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 .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: + *

+     *  private static NativeSomething realThis(Scriptable thisObj,
+     *                                          IdFunctionObject f)
+     *  {
+     *      if (!(thisObj instanceof NativeSomething))
+     *          throw incompatibleCallError(f);
+     *      return (NativeSomething)thisObj;
+     * }
+     * 
+ * 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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ImporterTopLevel.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ImporterTopLevel.java new file mode 100644 index 0000000..294deab --- /dev/null +++ b/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. + *

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

+ *  Scriptable scope = new ImporterTopLevel(cx);
+ * 
+ * Then JavaScript code will have access to the following methods: + *
    + *
  • importClass - will "import" a class by making its unqualified name + * available as a property of the top-level scope + *
  • importPackage - will "import" all the classes of the package by + * searching for unqualified names as classes qualified + * by the given package. + *
+ * The following code from the shell illustrates this use: + *
+ * js> importClass(java.io.File)
+ * js> f = new File('help.txt')
+ * help.txt
+ * js> importPackage(java.util)
+ * js> v = new Vector()
+ * []
+ *
+ * @author Norris Boyd
+ */
+public class ImporterTopLevel extends 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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InformativeParser.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InformativeParser.java
new file mode 100644
index 0000000..c73db34
--- /dev/null
+++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterfaceAdapter.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterfaceAdapter.java
new file mode 100644
index 0000000..877e6a2
--- /dev/null
+++ b/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 cl 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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterpretedFunction.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterpretedFunction.java
new file mode 100644
index 0000000..db84299
--- /dev/null
+++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Interpreter.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Interpreter.java
new file mode 100644
index 0000000..a68c025
--- /dev/null
+++ b/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 .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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterpreterData.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InterpreterData.java
new file mode 100644
index 0000000..7435b10
--- /dev/null
+++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaAdapter.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaAdapter.java
new file mode 100644
index 0000000..6e0a827
--- /dev/null
+++ b/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> generated
+            = cache.getInterfaceAdapterCacheMap();
+
+        ObjToIntMap names = getObjectFunctionNames(obj);
+        JavaAdapterSignature sig;
+        sig = new JavaAdapterSignature(superClass, interfaces, names);
+        Class adapterClass = generated.get(sig);
+        if (adapterClass == null) {
+            String adapterName = "adapter"
+                                 + cache.newClassSerialNumber();
+            byte[] code = createAdapterCode(names, adapterName,
+                                            superClass, interfaces, null);
+
+            adapterClass = loadAdapterClass(adapterName, code);
+            if (cache.isCachingEnabled()) {
+                generated.put(sig, adapterClass);
+            }
+        }
+        return adapterClass;
+    }
+
+    public static byte[] createAdapterCode(ObjToIntMap functionNames,
+                                           String adapterName,
+                                           Class superClass,
+                                           Class[] interfaces,
+                                           String scriptClassName)
+    {
+        ClassFileWriter cfw = new ClassFileWriter(adapterName,
+                                                  superClass.getName(),
+                                                  "");
+        cfw.addField("factory", "Lorg/mozilla/javascript/ContextFactory;",
+                     (short) (ClassFileWriter.ACC_PUBLIC |
+                              ClassFileWriter.ACC_FINAL));
+        cfw.addField("delegee", "Lorg/mozilla/javascript/Scriptable;",
+                     (short) (ClassFileWriter.ACC_PUBLIC |
+                              ClassFileWriter.ACC_FINAL));
+        cfw.addField("self", "Lorg/mozilla/javascript/Scriptable;",
+                     (short) (ClassFileWriter.ACC_PUBLIC |
+                              ClassFileWriter.ACC_FINAL));
+        int interfacesCount = interfaces == null ? 0 : interfaces.length;
+        for (int i=0; i < interfacesCount; i++) {
+            if (interfaces[i] != null)
+                cfw.addInterface(interfaces[i].getName());
+        }
+
+        String superName = superClass.getName().replace('.', '/');
+        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 list = new ArrayList();
+        HashSet skip = new HashSet();
+        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("",
+                        "(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, "", "()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("",
+                        "(Lorg/mozilla/javascript/ContextFactory;"
+                        +"Lorg/mozilla/javascript/Scriptable;"
+                        +"Lorg/mozilla/javascript/Scriptable;"
+                        +")V",
+                        ClassFileWriter.ACC_PUBLIC);
+
+        // Invoke base class constructor
+        cfw.add(ByteCode.ALOAD_0);  // this
+        cfw.addInvoke(ByteCode.INVOKESPECIAL, superName, "", "()V");
+
+        // Save parameter in instance variable "factory"
+        cfw.add(ByteCode.ALOAD_0);  // this
+        cfw.add(ByteCode.ALOAD_1);  // first arg: ContextFactory instance
+        cfw.add(ByteCode.PUTFIELD, adapterName, "factory",
+                "Lorg/mozilla/javascript/ContextFactory;");
+
+        // Save parameter in instance variable "delegee"
+        cfw.add(ByteCode.ALOAD_0);  // this
+        cfw.add(ByteCode.ALOAD_2);  // second arg: Scriptable delegee
+        cfw.add(ByteCode.PUTFIELD, adapterName, "delegee",
+                "Lorg/mozilla/javascript/Scriptable;");
+        // save self
+        cfw.add(ByteCode.ALOAD_0);  // this
+        cfw.add(ByteCode.ALOAD_3);  // 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("", "()V", ClassFileWriter.ACC_PUBLIC);
+
+        // Invoke base class constructor
+        cfw.add(ByteCode.ALOAD_0);  // this
+        cfw.addInvoke(ByteCode.INVOKESPECIAL, superName, "", "()V");
+
+        // Set factory to null to use current global when necessary
+        cfw.add(ByteCode.ALOAD_0);
+        cfw.add(ByteCode.ACONST_NULL);
+        cfw.add(ByteCode.PUTFIELD, adapterName, "factory",
+                "Lorg/mozilla/javascript/ContextFactory;");
+
+        // Load script class
+        cfw.add(ByteCode.NEW, scriptClassName);
+        cfw.add(ByteCode.DUP);
+        cfw.addInvoke(ByteCode.INVOKESPECIAL, scriptClassName, "", "()V");
+
+        // Run script and save resulting scope
+        cfw.addInvoke(ByteCode.INVOKESTATIC,
+                      "org/mozilla/javascript/JavaAdapter",
+                      "runScript",
+                      "(Lorg/mozilla/javascript/Script;"
+                      +")Lorg/mozilla/javascript/Scriptable;");
+        cfw.add(ByteCode.ASTORE_1);
+
+        // Save the Scriptable in instance variable "delegee"
+        cfw.add(ByteCode.ALOAD_0);  // this
+        cfw.add(ByteCode.ALOAD_1);  // the Scriptable
+        cfw.add(ByteCode.PUTFIELD, adapterName, "delegee",
+                "Lorg/mozilla/javascript/Scriptable;");
+
+        cfw.add(ByteCode.ALOAD_0);  // this for the following PUTFIELD for self
+        // create a wrapper object to be used as "this" in method calls
+        cfw.add(ByteCode.ALOAD_1);  // the Scriptable
+        cfw.add(ByteCode.ALOAD_0);  // this
+        cfw.addInvoke(ByteCode.INVOKESTATIC,
+                      "org/mozilla/javascript/JavaAdapter",
+                      "createAdapterWrapper",
+                      "(Lorg/mozilla/javascript/Scriptable;"
+                      +"Ljava/lang/Object;"
+                      +")Lorg/mozilla/javascript/Scriptable;");
+        cfw.add(ByteCode.PUTFIELD, adapterName, "self",
+                "Lorg/mozilla/javascript/Scriptable;");
+
+        cfw.add(ByteCode.RETURN);
+        cfw.stopMethod((short)2); // this + delegee
+    }
+
+    /**
+     * Generates code to wrap Java arguments into Object[].
+     * Non-primitive Java types are left as-is pending conversion
+     * in the helper method. Leaves the array object on the top of the stack.
+     */
+    static void generatePushWrappedArgs(ClassFileWriter cfw,
+                                        Class[] argTypes,
+                                        int arrayLength)
+    {
+        // push arguments
+        cfw.addPush(arrayLength);
+        cfw.add(ByteCode.ANEWARRAY, "java/lang/Object");
+        int paramOffset = 1;
+        for (int i = 0; i != argTypes.length; ++i) {
+            cfw.add(ByteCode.DUP); // duplicate array reference
+            cfw.addPush(i);
+            paramOffset += generateWrapArg(cfw, paramOffset, argTypes[i]);
+            cfw.add(ByteCode.AASTORE);
+        }
+    }
+
+    /**
+     * Generates code to wrap Java argument into Object.
+     * Non-primitive Java types are left unconverted pending conversion
+     * in the helper method. Leaves the wrapper object on the top of the stack.
+     */
+    private static int generateWrapArg(ClassFileWriter cfw, int paramOffset,
+                                       Class argType)
+    {
+        int size = 1;
+        if (!argType.isPrimitive()) {
+            cfw.add(ByteCode.ALOAD, paramOffset);
+
+        } else if (argType == Boolean.TYPE) {
+            // wrap boolean values with java.lang.Boolean.
+            cfw.add(ByteCode.NEW, "java/lang/Boolean");
+            cfw.add(ByteCode.DUP);
+            cfw.add(ByteCode.ILOAD, paramOffset);
+            cfw.addInvoke(ByteCode.INVOKESPECIAL, "java/lang/Boolean",
+                          "", "(Z)V");
+
+        } else if (argType == Character.TYPE) {
+            // Create a string of length 1 using the character parameter.
+            cfw.add(ByteCode.ILOAD, paramOffset);
+            cfw.addInvoke(ByteCode.INVOKESTATIC, "java/lang/String",
+                          "valueOf", "(C)Ljava/lang/String;");
+
+        } else {
+            // convert all numeric values to java.lang.Double.
+            cfw.add(ByteCode.NEW, "java/lang/Double");
+            cfw.add(ByteCode.DUP);
+            String typeName = argType.getName();
+            switch (typeName.charAt(0)) {
+            case 'b':
+            case 's':
+            case 'i':
+                // load an int value, convert to double.
+                cfw.add(ByteCode.ILOAD, paramOffset);
+                cfw.add(ByteCode.I2D);
+                break;
+            case 'l':
+                // load a long, convert to double.
+                cfw.add(ByteCode.LLOAD, paramOffset);
+                cfw.add(ByteCode.L2D);
+                size = 2;
+                break;
+            case 'f':
+                // load a float, convert to double.
+                cfw.add(ByteCode.FLOAD, paramOffset);
+                cfw.add(ByteCode.F2D);
+                break;
+            case 'd':
+                cfw.add(ByteCode.DLOAD, paramOffset);
+                size = 2;
+                break;
+            }
+            cfw.addInvoke(ByteCode.INVOKESPECIAL, "java/lang/Double",
+                          "", "(D)V");
+        }
+        return size;
+    }
+
+    /**
+     * Generates code to convert a wrapped value type to a primitive type.
+     * Handles unwrapping java.lang.Boolean, and java.lang.Number types.
+     * Generates the appropriate RETURN bytecode.
+     */
+    static void generateReturnResult(ClassFileWriter cfw, Class retType,
+                                     boolean callConvertResult)
+    {
+        // wrap boolean values with java.lang.Boolean, convert all other
+        // primitive values to java.lang.Double.
+        if (retType == Void.TYPE) {
+            cfw.add(ByteCode.POP);
+            cfw.add(ByteCode.RETURN);
+
+        } else if (retType == Boolean.TYPE) {
+            cfw.addInvoke(ByteCode.INVOKESTATIC,
+                          "org/mozilla/javascript/Context",
+                          "toBoolean", "(Ljava/lang/Object;)Z");
+            cfw.add(ByteCode.IRETURN);
+
+        } else if (retType == Character.TYPE) {
+            // characters are represented as strings in JavaScript.
+            // return the first character.
+            // first convert the value to a string if possible.
+            cfw.addInvoke(ByteCode.INVOKESTATIC,
+                          "org/mozilla/javascript/Context",
+                          "toString",
+                          "(Ljava/lang/Object;)Ljava/lang/String;");
+            cfw.add(ByteCode.ICONST_0);
+            cfw.addInvoke(ByteCode.INVOKEVIRTUAL, "java/lang/String",
+                          "charAt", "(I)C");
+            cfw.add(ByteCode.IRETURN);
+
+        } else if (retType.isPrimitive()) {
+            cfw.addInvoke(ByteCode.INVOKESTATIC,
+                          "org/mozilla/javascript/Context",
+                          "toNumber", "(Ljava/lang/Object;)D");
+            String typeName = retType.getName();
+            switch (typeName.charAt(0)) {
+            case 'b':
+            case 's':
+            case 'i':
+                cfw.add(ByteCode.D2I);
+                cfw.add(ByteCode.IRETURN);
+                break;
+            case 'l':
+                cfw.add(ByteCode.D2L);
+                cfw.add(ByteCode.LRETURN);
+                break;
+            case 'f':
+                cfw.add(ByteCode.D2F);
+                cfw.add(ByteCode.FRETURN);
+                break;
+            case 'd':
+                cfw.add(ByteCode.DRETURN);
+                break;
+            default:
+                throw new RuntimeException("Unexpected return type " +
+                                           retType.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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaMembers.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaMembers.java
new file mode 100644
index 0000000..84ef2d4
--- /dev/null
+++ b/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,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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaScriptException.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaScriptException.java
new file mode 100644
index 0000000..11ebedf
--- /dev/null
+++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Kit.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Kit.java
new file mode 100644
index 0000000..f7b4cad
--- /dev/null
+++ b/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
+     * ex.initCause(cause) or otherwise do nothing.
+     * @return The ex 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 c is a hexadecimal digit, return
+     * accumulator * 16 plus corresponding
+     * number. Otherise return -1.
+     */
+    public static int xDigitToInt(int c, int accumulator)
+    {
+        check: {
+            // Use 0..9 < A..Z < a..z
+            if (c <= '9') {
+                c -= '0';
+                if (0 <= c) { break check; }
+            } else if (c <= 'F') {
+                if ('A' <= c) {
+                    c -= ('A' - 10);
+                    break check;
+                }
+            } else if (c <= 'f') {
+                if ('a' <= c) {
+                    c -= ('a' - 10);
+                    break check;
+                }
+            }
+            return -1;
+        }
+        return (accumulator << 4) | c;
+    }
+
+    /**
+     * Add listener to bag of listeners.
+     * The function does not modify bag and return a new collection
+     * containing listener and all listeners from bag.
+     * Bag without listeners always represented as the null value.
+     * 

+ * Usage example: + *

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

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

+ * For usage example, see {@link #addListener(Object bag, Object listener)}. + * + * @param bag Current collection of listeners. + * @param index Index of the listener to access. + * @return Listener at the given index or null. + * @see #addListener(Object bag, Object listener) + * @see #removeListener(Object bag, Object listener) + */ + public static Object getListener(Object bag, int index) + { + if (index == 0) { + if (bag == null) + return null; + if (!(bag instanceof Object[])) + return bag; + Object[] array = (Object[])bag; + // bag has at least 2 elements if it is array + if (array.length < 2) throw new IllegalArgumentException(); + return array[0]; + } else if (index == 1) { + if (!(bag instanceof Object[])) { + if (bag == null) throw new IllegalArgumentException(); + return null; + } + Object[] array = (Object[])bag; + // the array access will check for index on its own + return array[1]; + } else { + // bag has to array + Object[] array = (Object[])bag; + int L = array.length; + if (L < 2) throw new IllegalArgumentException(); + if (index == L) + return null; + return array[index]; + } + } + + static Object initHash(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 throw Kit.codeBug() if plain + * Kit.codeBug() triggers unreachable code error. + */ + public static RuntimeException codeBug() + throws RuntimeException + { + RuntimeException ex = new IllegalStateException("FAILED ASSERTION"); + // Print stack trace ASAP + ex.printStackTrace(System.err); + throw ex; + } +} + diff --git a/infrastructure/rhino1_7R1/src/org/mozilla/javascript/LazilyLoadedCtor.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/LazilyLoadedCtor.java new file mode 100644 index 0000000..4153372 --- /dev/null +++ b/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. + * + *

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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/MemberBox.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/MemberBox.java new file mode 100644 index 0000000..2d3553f --- /dev/null +++ b/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 + * + * 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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeArray.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeArray.java new file mode 100644 index 0000000..b170ff4 --- /dev/null +++ b/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 dense. + */ + private boolean denseOnly; + + /** + * The maximum size of dense that will be allocated initially. + */ + private static int maximumInitialCapacity = 10000; + + /** + * The default capacity for dense. + */ + private static final int DEFAULT_INITIAL_CAPACITY = 10; + + /** + * The factor to grow dense by. + */ + private static final double GROW_FACTOR = 1.5; + private static final int MAX_PRE_GROW_SIZE = (int)(Integer.MAX_VALUE / GROW_FACTOR); +} diff --git a/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeBoolean.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeBoolean.java new file mode 100644 index 0000000..b6a106a --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeCall.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeCall.java new file mode 100644 index 0000000..b196ac3 --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeDate.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeDate.java new file mode 100644 index 0000000..75d41ab --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeError.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeError.java new file mode 100644 index 0000000..4aff10c --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeFunction.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeFunction.java new file mode 100644 index 0000000..ac70556 --- /dev/null +++ b/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 index < {@link #getParamCount()}, then return the name of the + * corresponding parameter. Otherwise return the name of variable. + */ + protected abstract String getParamOrVarName(int index); + + /** + * Get parameter or variable const-ness. + * If index < {@link #getParamCount()}, then return the const-ness + * of the corresponding parameter. Otherwise return whether the variable is + * const. + */ + protected boolean getParamOrVarConst(int index) + { + // By default return false to preserve compatibility with existing + // classes subclassing this class, which are mostly generated by jsc + // from earlier Rhino versions. See Bugzilla #396117. + return false; + } +} + diff --git a/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGenerator.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGenerator.java new file mode 100644 index 0000000..0a8da9f --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGlobal.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGlobal.java new file mode 100644 index 0000000..58faad4 --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeIterator.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeIterator.java new file mode 100644 index 0000000..c61f417 --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaArray.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaArray.java new file mode 100644 index 0000000..2f711a0 --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaClass.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaClass.java new file mode 100644 index 0000000..ab8af5c --- /dev/null +++ b/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 + * + * 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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaConstructor.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaConstructor.java new file mode 100644 index 0000000..530bf81 --- /dev/null +++ b/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 "".concat(sig); + } + + public String toString() + { + return "[JavaConstructor " + ctor.getName() + "]"; + } +} + diff --git a/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaMethod.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaMethod.java new file mode 100644 index 0000000..eb66f40 --- /dev/null +++ b/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 + * + * 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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaObject.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaObject.java new file mode 100644 index 0000000..3d27852 --- /dev/null +++ b/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.

+ * + * @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 + * + * "preferred method conversions" from Live Connect 3 + */ + static int getConversionWeight(Object fromObj, Class to) { + int fromCode = getJSTypeCode(fromObj); + + switch (fromCode) { + + case JSTYPE_UNDEFINED: + if (to == ScriptRuntime.StringClass || + to == ScriptRuntime.ObjectClass) { + return 1; + } + break; + + case JSTYPE_NULL: + if (!to.isPrimitive()) { + return 1; + } + break; + + case JSTYPE_BOOLEAN: + // "boolean" is #1 + if (to == Boolean.TYPE) { + return 1; + } + else if (to == ScriptRuntime.BooleanClass) { + return 2; + } + else if (to == ScriptRuntime.ObjectClass) { + return 3; + } + else if (to == ScriptRuntime.StringClass) { + return 4; + } + break; + + case JSTYPE_NUMBER: + 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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaPackage.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaPackage.java new file mode 100644 index 0000000..71f09f7 --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaTopPackage.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaTopPackage.java new file mode 100644 index 0000000..b5c9b49 --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeMath.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeMath.java new file mode 100644 index 0000000..36b66b4 --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeNumber.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeNumber.java new file mode 100644 index 0000000..8fc9fb0 --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeObject.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeObject.java new file mode 100644 index 0000000..19aff63 --- /dev/null +++ b/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/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeScript.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeScript.java new file mode 100644 index 0000000..7b5191e --- /dev/null +++ b/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 = "