summaryrefslogtreecommitdiffstats
path: root/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Codegen.java
diff options
context:
space:
mode:
Diffstat (limited to 'infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Codegen.java')
-rw-r--r--infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Codegen.java5031
1 files changed, 0 insertions, 5031 deletions
diff --git a/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Codegen.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Codegen.java
deleted file mode 100644
index 64952bf..0000000
--- a/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Codegen.java
+++ /dev/null
@@ -1,5031 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Rhino code, released
- * May 6, 1999.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1997-2000
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Norris Boyd
- * Kemal Bayram
- * Igor Bukanov
- * Bob Jervis
- * Roger Lawrence
- * Andi Vajda
- * Hannes Wallnoefer
- *
- * Alternatively, the contents of this file may be used under the terms of
- * the GNU General Public License Version 2 or later (the "GPL"), in which
- * case the provisions of the GPL are applicable instead of those above. If
- * you wish to allow use of your version of this file only under the terms of
- * the GPL and not to allow others to use your version of this file under the
- * MPL, indicate your decision by deleting the provisions above and replacing
- * them with the notice and other provisions required by the GPL. If you do
- * not delete the provisions above, a recipient may use your version of this
- * file under either the MPL or the GPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-
-package org.mozilla.javascript.optimizer;
-
-import org.mozilla.javascript.*;
-import org.mozilla.classfile.*;
-import java.util.*;
-import java.lang.reflect.Constructor;
-import java.util.Hashtable;
-
-/**
- * This class generates code for a given IR tree.
- *
- * @author Norris Boyd
- * @author Roger Lawrence
- */
-
-public class Codegen implements Evaluator
-{
- public void captureStackInfo(RhinoException ex) {
- throw new UnsupportedOperationException();
- }
-
- public String getSourcePositionFromStack(Context cx, int[] linep) {
- throw new UnsupportedOperationException();
- }
-
- public String getPatchedStack(RhinoException ex, String nativeStackTrace) {
- throw new UnsupportedOperationException();
- }
-
- public List getScriptStack(RhinoException ex) {
- throw new UnsupportedOperationException();
- }
-
- public void setEvalScriptFlag(Script script) {
- throw new UnsupportedOperationException();
- }
-
- public Object compile(CompilerEnvirons compilerEnv,
- ScriptOrFnNode tree,
- String encodedSource,
- boolean returnFunction)
- {
- int serial;
- synchronized (globalLock) {
- serial = ++globalSerialClassCounter;
- }
- String mainClassName = "org.mozilla.javascript.gen.c"+serial;
-
- byte[] mainClassBytes = compileToClassFile(compilerEnv, mainClassName,
- tree, encodedSource,
- returnFunction);
-
- return new Object[] { mainClassName, mainClassBytes };
- }
-
- public Script createScriptObject(Object bytecode,
- Object staticSecurityDomain)
- {
- Class cl = defineClass(bytecode, staticSecurityDomain);
-
- Script script;
- try {
- script = (Script)cl.newInstance();
- } catch (Exception ex) {
- throw new RuntimeException
- ("Unable to instantiate compiled class:"+ex.toString());
- }
- return script;
- }
-
- public Function createFunctionObject(Context cx, Scriptable scope,
- Object bytecode,
- Object staticSecurityDomain)
- {
- Class cl = defineClass(bytecode, staticSecurityDomain);
-
- NativeFunction f;
- try {
- Constructor ctor = cl.getConstructors()[0];
- Object[] initArgs = { scope, cx, new Integer(0) };
- f = (NativeFunction)ctor.newInstance(initArgs);
- } catch (Exception ex) {
- throw new RuntimeException
- ("Unable to instantiate compiled class:"+ex.toString());
- }
- return f;
- }
-
- private Class defineClass(Object bytecode,
- Object staticSecurityDomain)
- {
- Object[] nameBytesPair = (Object[])bytecode;
- String className = (String)nameBytesPair[0];
- byte[] classBytes = (byte[])nameBytesPair[1];
-
- // The generated classes in this case refer only to Rhino classes
- // which must be accessible through this class loader
- ClassLoader rhinoLoader = getClass().getClassLoader();
- GeneratedClassLoader loader;
- loader = SecurityController.createLoader(rhinoLoader,
- staticSecurityDomain);
- Exception e;
- try {
- Class cl = loader.defineClass(className, classBytes);
- loader.linkClass(cl);
- return cl;
- } catch (SecurityException x) {
- e = x;
- } catch (IllegalArgumentException x) {
- e = x;
- }
- throw new RuntimeException("Malformed optimizer package " + e);
- }
-
- byte[] compileToClassFile(CompilerEnvirons compilerEnv,
- String mainClassName,
- ScriptOrFnNode scriptOrFn,
- String encodedSource,
- boolean returnFunction)
- {
- this.compilerEnv = compilerEnv;
-
- transform(scriptOrFn);
-
- if (Token.printTrees) {
- /*APPJET*///System.out.println(scriptOrFn.toStringTree(scriptOrFn));
- }
-
- if (returnFunction) {
- scriptOrFn = scriptOrFn.getFunctionNode(0);
- }
-
- initScriptOrFnNodesData(scriptOrFn);
-
- this.mainClassName = mainClassName;
- this.mainClassSignature
- = ClassFileWriter.classNameToSignature(mainClassName);
-
- try {
- return generateCode(encodedSource);
- } catch (ClassFileWriter.ClassFileFormatException e) {
- throw reportClassFileFormatException(scriptOrFn, e.getMessage());
- }
- }
-
- private RuntimeException reportClassFileFormatException(
- ScriptOrFnNode scriptOrFn,
- String message)
- {
- String msg = scriptOrFn instanceof FunctionNode
- ? ScriptRuntime.getMessage2("msg.while.compiling.fn",
- ((FunctionNode)scriptOrFn).getFunctionName(), message)
- : ScriptRuntime.getMessage1("msg.while.compiling.script", message);
- return Context.reportRuntimeError(msg, scriptOrFn.getSourceName(),
- scriptOrFn.getLineno(), null, 0);
- }
-
- private void transform(ScriptOrFnNode tree)
- {
- initOptFunctions_r(tree);
-
- int optLevel = compilerEnv.getOptimizationLevel();
-
- Hashtable possibleDirectCalls = null;
- if (optLevel > 0) {
- /*
- * Collect all of the contained functions into a hashtable
- * so that the call optimizer can access the class name & parameter
- * count for any call it encounters
- */
- if (tree.getType() == Token.SCRIPT) {
- int functionCount = tree.getFunctionCount();
- for (int i = 0; i != functionCount; ++i) {
- OptFunctionNode ofn = OptFunctionNode.get(tree, i);
- if (ofn.fnode.getFunctionType()
- == FunctionNode.FUNCTION_STATEMENT)
- {
- String name = ofn.fnode.getFunctionName();
- if (name.length() != 0) {
- if (possibleDirectCalls == null) {
- possibleDirectCalls = new Hashtable();
- }
- possibleDirectCalls.put(name, ofn);
- }
- }
- }
- }
- }
-
- if (possibleDirectCalls != null) {
- directCallTargets = new ObjArray();
- }
-
- OptTransformer ot = new OptTransformer(possibleDirectCalls,
- directCallTargets);
- ot.transform(tree);
-
- if (optLevel > 0) {
- (new Optimizer()).optimize(tree);
- }
- }
-
- private static void initOptFunctions_r(ScriptOrFnNode scriptOrFn)
- {
- for (int i = 0, N = scriptOrFn.getFunctionCount(); i != N; ++i) {
- FunctionNode fn = scriptOrFn.getFunctionNode(i);
- new OptFunctionNode(fn);
- initOptFunctions_r(fn);
- }
- }
-
- private void initScriptOrFnNodesData(ScriptOrFnNode scriptOrFn)
- {
- ObjArray x = new ObjArray();
- collectScriptOrFnNodes_r(scriptOrFn, x);
-
- int count = x.size();
- scriptOrFnNodes = new ScriptOrFnNode[count];
- x.toArray(scriptOrFnNodes);
-
- scriptOrFnIndexes = new ObjToIntMap(count);
- for (int i = 0; i != count; ++i) {
- scriptOrFnIndexes.put(scriptOrFnNodes[i], i);
- }
- }
-
- private static void collectScriptOrFnNodes_r(ScriptOrFnNode n,
- ObjArray x)
- {
- x.add(n);
- int nestedCount = n.getFunctionCount();
- for (int i = 0; i != nestedCount; ++i) {
- collectScriptOrFnNodes_r(n.getFunctionNode(i), x);
- }
- }
-
- private byte[] generateCode(String encodedSource)
- {
- boolean hasScript = (scriptOrFnNodes[0].getType() == Token.SCRIPT);
- boolean hasFunctions = (scriptOrFnNodes.length > 1 || !hasScript);
-
- String sourceFile = null;
- if (compilerEnv.isGenerateDebugInfo()) {
- sourceFile = scriptOrFnNodes[0].getSourceName();
- }
-
- ClassFileWriter cfw = new ClassFileWriter(mainClassName,
- SUPER_CLASS_NAME,
- sourceFile);
- cfw.addField(ID_FIELD_NAME, "I",
- ClassFileWriter.ACC_PRIVATE);
- cfw.addField(DIRECT_CALL_PARENT_FIELD, mainClassSignature,
- ClassFileWriter.ACC_PRIVATE);
- cfw.addField(REGEXP_ARRAY_FIELD_NAME, REGEXP_ARRAY_FIELD_TYPE,
- ClassFileWriter.ACC_PRIVATE);
-
- if (hasFunctions) {
- generateFunctionConstructor(cfw);
- }
-
- if (hasScript) {
- cfw.addInterface("org/mozilla/javascript/Script");
- generateScriptCtor(cfw);
- generateMain(cfw);
- generateExecute(cfw);
- }
-
- generateCallMethod(cfw);
- generateResumeGenerator(cfw);
-
- generateNativeFunctionOverrides(cfw, encodedSource);
-
- int count = scriptOrFnNodes.length;
- for (int i = 0; i != count; ++i) {
- ScriptOrFnNode n = scriptOrFnNodes[i];
-
- BodyCodegen bodygen = new BodyCodegen();
- bodygen.cfw = cfw;
- bodygen.codegen = this;
- bodygen.compilerEnv = compilerEnv;
- bodygen.scriptOrFn = n;
- bodygen.scriptOrFnIndex = i;
-
- try {
- bodygen.generateBodyCode();
- } catch (ClassFileWriter.ClassFileFormatException e) {
- throw reportClassFileFormatException(n, e.getMessage());
- }
-
- if (n.getType() == Token.FUNCTION) {
- OptFunctionNode ofn = OptFunctionNode.get(n);
- generateFunctionInit(cfw, ofn);
- if (ofn.isTargetOfDirectCall()) {
- emitDirectConstructor(cfw, ofn);
- }
- }
- }
-
- if (directCallTargets != null) {
- int N = directCallTargets.size();
- for (int j = 0; j != N; ++j) {
- cfw.addField(getDirectTargetFieldName(j),
- mainClassSignature,
- ClassFileWriter.ACC_PRIVATE);
- }
- }
-
- emitRegExpInit(cfw);
- emitConstantDudeInitializers(cfw);
-
- return cfw.toByteArray();
- }
-
- private void emitDirectConstructor(ClassFileWriter cfw,
- OptFunctionNode ofn)
- {
-/*
- we generate ..
- Scriptable directConstruct(<directCallArgs>) {
- Scriptable newInstance = createObject(cx, scope);
- Object val = <body-name>(cx, scope, newInstance, <directCallArgs>);
- if (val instanceof Scriptable) {
- return (Scriptable) val;
- }
- return newInstance;
- }
-*/
- cfw.startMethod(getDirectCtorName(ofn.fnode),
- getBodyMethodSignature(ofn.fnode),
- (short)(ClassFileWriter.ACC_STATIC
- | ClassFileWriter.ACC_PRIVATE));
-
- int argCount = ofn.fnode.getParamCount();
- int firstLocal = (4 + argCount * 3) + 1;
-
- cfw.addALoad(0); // this
- cfw.addALoad(1); // cx
- cfw.addALoad(2); // scope
- cfw.addInvoke(ByteCode.INVOKEVIRTUAL,
- "org/mozilla/javascript/BaseFunction",
- "createObject",
- "(Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +")Lorg/mozilla/javascript/Scriptable;");
- cfw.addAStore(firstLocal);
-
- cfw.addALoad(0);
- cfw.addALoad(1);
- cfw.addALoad(2);
- cfw.addALoad(firstLocal);
- for (int i = 0; i < argCount; i++) {
- cfw.addALoad(4 + (i * 3));
- cfw.addDLoad(5 + (i * 3));
- }
- cfw.addALoad(4 + argCount * 3);
- cfw.addInvoke(ByteCode.INVOKESTATIC,
- mainClassName,
- getBodyMethodName(ofn.fnode),
- getBodyMethodSignature(ofn.fnode));
- int exitLabel = cfw.acquireLabel();
- cfw.add(ByteCode.DUP); // make a copy of direct call result
- cfw.add(ByteCode.INSTANCEOF, "org/mozilla/javascript/Scriptable");
- cfw.add(ByteCode.IFEQ, exitLabel);
- // cast direct call result
- cfw.add(ByteCode.CHECKCAST, "org/mozilla/javascript/Scriptable");
- cfw.add(ByteCode.ARETURN);
- cfw.markLabel(exitLabel);
-
- cfw.addALoad(firstLocal);
- cfw.add(ByteCode.ARETURN);
-
- cfw.stopMethod((short)(firstLocal + 1));
- }
-
- static boolean isGenerator(ScriptOrFnNode node)
- {
- return (node.getType() == Token.FUNCTION ) &&
- ((FunctionNode)node).isGenerator();
- }
-
- // How dispatch to generators works:
- // Two methods are generated corresponding to a user-written generator.
- // One of these creates a generator object (NativeGenerator), which is
- // returned to the user. The other method contains all of the body code
- // of the generator.
- // When a user calls a generator, the call() method dispatches control to
- // to the method that creates the NativeGenerator object. Subsequently when
- // the user invokes .next(), .send() or any such method on the generator
- // object, the resumeGenerator() below dispatches the call to the
- // method corresponding to the generator body. As a matter of convention
- // the generator body is given the name of the generator activation function
- // appended by "_gen".
- private void generateResumeGenerator(ClassFileWriter cfw)
- {
- boolean hasGenerators = false;
- for (int i=0; i < scriptOrFnNodes.length; i++) {
- if (isGenerator(scriptOrFnNodes[i]))
- hasGenerators = true;
- }
-
- // if there are no generators defined, we don't implement a
- // resumeGenerator(). The base class provides a default implementation.
- if (!hasGenerators)
- return;
-
- cfw.startMethod("resumeGenerator",
- "(Lorg/mozilla/javascript/Context;" +
- "Lorg/mozilla/javascript/Scriptable;" +
- "ILjava/lang/Object;" +
- "Ljava/lang/Object;)Ljava/lang/Object;",
- (short)(ClassFileWriter.ACC_PUBLIC
- | ClassFileWriter.ACC_FINAL));
-
- // load arguments for dispatch to the corresponding *_gen method
- cfw.addALoad(0);
- cfw.addALoad(1);
- cfw.addALoad(2);
- cfw.addALoad(4);
- cfw.addALoad(5);
- cfw.addILoad(3);
-
- cfw.addLoadThis();
- cfw.add(ByteCode.GETFIELD, cfw.getClassName(), ID_FIELD_NAME, "I");
-
- int startSwitch = cfw.addTableSwitch(0, scriptOrFnNodes.length - 1);
- cfw.markTableSwitchDefault(startSwitch);
- int endlabel = cfw.acquireLabel();
-
- for (int i = 0; i < scriptOrFnNodes.length; i++) {
- ScriptOrFnNode n = scriptOrFnNodes[i];
- cfw.markTableSwitchCase(startSwitch, i, (short)6);
- if (isGenerator(n)) {
- String type = "(" +
- mainClassSignature +
- "Lorg/mozilla/javascript/Context;" +
- "Lorg/mozilla/javascript/Scriptable;" +
- "Ljava/lang/Object;" +
- "Ljava/lang/Object;I)Ljava/lang/Object;";
- cfw.addInvoke(ByteCode.INVOKESTATIC,
- mainClassName,
- getBodyMethodName(n) + "_gen",
- type);
- cfw.add(ByteCode.ARETURN);
- } else {
- cfw.add(ByteCode.GOTO, endlabel);
- }
- }
-
- cfw.markLabel(endlabel);
- pushUndefined(cfw);
- cfw.add(ByteCode.ARETURN);
-
-
- // this method uses as many locals as there are arguments (hence 6)
- cfw.stopMethod((short)6);
- }
-
- private void generateCallMethod(ClassFileWriter cfw)
- {
- cfw.startMethod("call",
- "(Lorg/mozilla/javascript/Context;" +
- "Lorg/mozilla/javascript/Scriptable;" +
- "Lorg/mozilla/javascript/Scriptable;" +
- "[Ljava/lang/Object;)Ljava/lang/Object;",
- (short)(ClassFileWriter.ACC_PUBLIC
- | ClassFileWriter.ACC_FINAL));
-
- // Generate code for:
- // if (!ScriptRuntime.hasTopCall(cx)) {
- // return ScriptRuntime.doTopCall(this, cx, scope, thisObj, args);
- // }
-
- int nonTopCallLabel = cfw.acquireLabel();
- cfw.addALoad(1); //cx
- cfw.addInvoke(ByteCode.INVOKESTATIC,
- "org/mozilla/javascript/ScriptRuntime",
- "hasTopCall",
- "(Lorg/mozilla/javascript/Context;"
- +")Z");
- cfw.add(ByteCode.IFNE, nonTopCallLabel);
- cfw.addALoad(0);
- cfw.addALoad(1);
- cfw.addALoad(2);
- cfw.addALoad(3);
- cfw.addALoad(4);
- cfw.addInvoke(ByteCode.INVOKESTATIC,
- "org/mozilla/javascript/ScriptRuntime",
- "doTopCall",
- "(Lorg/mozilla/javascript/Callable;"
- +"Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"[Ljava/lang/Object;"
- +")Ljava/lang/Object;");
- cfw.add(ByteCode.ARETURN);
- cfw.markLabel(nonTopCallLabel);
-
- // Now generate switch to call the real methods
- cfw.addALoad(0);
- cfw.addALoad(1);
- cfw.addALoad(2);
- cfw.addALoad(3);
- cfw.addALoad(4);
-
- int end = scriptOrFnNodes.length;
- boolean generateSwitch = (2 <= end);
-
- int switchStart = 0;
- int switchStackTop = 0;
- if (generateSwitch) {
- cfw.addLoadThis();
- cfw.add(ByteCode.GETFIELD, cfw.getClassName(), ID_FIELD_NAME, "I");
- // do switch from (1, end - 1) mapping 0 to
- // the default case
- switchStart = cfw.addTableSwitch(1, end - 1);
- }
-
- for (int i = 0; i != end; ++i) {
- ScriptOrFnNode n = scriptOrFnNodes[i];
- if (generateSwitch) {
- if (i == 0) {
- cfw.markTableSwitchDefault(switchStart);
- switchStackTop = cfw.getStackTop();
- } else {
- cfw.markTableSwitchCase(switchStart, i - 1,
- switchStackTop);
- }
- }
- if (n.getType() == Token.FUNCTION) {
- OptFunctionNode ofn = OptFunctionNode.get(n);
- if (ofn.isTargetOfDirectCall()) {
- int pcount = ofn.fnode.getParamCount();
- if (pcount != 0) {
- // loop invariant:
- // stack top == arguments array from addALoad4()
- for (int p = 0; p != pcount; ++p) {
- cfw.add(ByteCode.ARRAYLENGTH);
- cfw.addPush(p);
- int undefArg = cfw.acquireLabel();
- int beyond = cfw.acquireLabel();
- cfw.add(ByteCode.IF_ICMPLE, undefArg);
- // get array[p]
- cfw.addALoad(4);
- cfw.addPush(p);
- cfw.add(ByteCode.AALOAD);
- cfw.add(ByteCode.GOTO, beyond);
- cfw.markLabel(undefArg);
- pushUndefined(cfw);
- cfw.markLabel(beyond);
- // Only one push
- cfw.adjustStackTop(-1);
- cfw.addPush(0.0);
- // restore invariant
- cfw.addALoad(4);
- }
- }
- }
- }
- cfw.addInvoke(ByteCode.INVOKESTATIC,
- mainClassName,
- getBodyMethodName(n),
- getBodyMethodSignature(n));
- cfw.add(ByteCode.ARETURN);
- }
- cfw.stopMethod((short)5);
- // 5: this, cx, scope, js this, args[]
- }
-
- private void generateMain(ClassFileWriter cfw)
- {
- cfw.startMethod("main", "([Ljava/lang/String;)V",
- (short)(ClassFileWriter.ACC_PUBLIC
- | ClassFileWriter.ACC_STATIC));
-
- // load new ScriptImpl()
- cfw.add(ByteCode.NEW, cfw.getClassName());
- cfw.add(ByteCode.DUP);
- cfw.addInvoke(ByteCode.INVOKESPECIAL, cfw.getClassName(),
- "<init>", "()V");
- // load 'args'
- cfw.add(ByteCode.ALOAD_0);
- // Call mainMethodClass.main(Script script, String[] args)
- cfw.addInvoke(ByteCode.INVOKESTATIC,
- mainMethodClass,
- "main",
- "(Lorg/mozilla/javascript/Script;[Ljava/lang/String;)V");
- cfw.add(ByteCode.RETURN);
- // 1 = String[] args
- cfw.stopMethod((short)1);
- }
-
- private void generateExecute(ClassFileWriter cfw)
- {
- cfw.startMethod("exec",
- "(Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +")Ljava/lang/Object;",
- (short)(ClassFileWriter.ACC_PUBLIC
- | ClassFileWriter.ACC_FINAL));
-
- final int CONTEXT_ARG = 1;
- final int SCOPE_ARG = 2;
-
- cfw.addLoadThis();
- cfw.addALoad(CONTEXT_ARG);
- cfw.addALoad(SCOPE_ARG);
- cfw.add(ByteCode.DUP);
- cfw.add(ByteCode.ACONST_NULL);
- cfw.addInvoke(ByteCode.INVOKEVIRTUAL,
- cfw.getClassName(),
- "call",
- "(Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"[Ljava/lang/Object;"
- +")Ljava/lang/Object;");
-
- cfw.add(ByteCode.ARETURN);
- // 3 = this + context + scope
- cfw.stopMethod((short)3);
- }
-
- private void generateScriptCtor(ClassFileWriter cfw)
- {
- cfw.startMethod("<init>", "()V", ClassFileWriter.ACC_PUBLIC);
-
- cfw.addLoadThis();
- cfw.addInvoke(ByteCode.INVOKESPECIAL, SUPER_CLASS_NAME,
- "<init>", "()V");
- // set id to 0
- cfw.addLoadThis();
- cfw.addPush(0);
- cfw.add(ByteCode.PUTFIELD, cfw.getClassName(), ID_FIELD_NAME, "I");
-
- cfw.add(ByteCode.RETURN);
- // 1 parameter = this
- cfw.stopMethod((short)1);
- }
-
- private void generateFunctionConstructor(ClassFileWriter cfw)
- {
- final int SCOPE_ARG = 1;
- final int CONTEXT_ARG = 2;
- final int ID_ARG = 3;
-
- cfw.startMethod("<init>", FUNCTION_CONSTRUCTOR_SIGNATURE,
- ClassFileWriter.ACC_PUBLIC);
- cfw.addALoad(0);
- cfw.addInvoke(ByteCode.INVOKESPECIAL, SUPER_CLASS_NAME,
- "<init>", "()V");
-
- cfw.addLoadThis();
- cfw.addILoad(ID_ARG);
- cfw.add(ByteCode.PUTFIELD, cfw.getClassName(), ID_FIELD_NAME, "I");
-
- cfw.addLoadThis();
- cfw.addALoad(CONTEXT_ARG);
- cfw.addALoad(SCOPE_ARG);
-
- int start = (scriptOrFnNodes[0].getType() == Token.SCRIPT) ? 1 : 0;
- int end = scriptOrFnNodes.length;
- if (start == end) throw badTree();
- boolean generateSwitch = (2 <= end - start);
-
- int switchStart = 0;
- int switchStackTop = 0;
- if (generateSwitch) {
- cfw.addILoad(ID_ARG);
- // do switch from (start + 1, end - 1) mapping start to
- // the default case
- switchStart = cfw.addTableSwitch(start + 1, end - 1);
- }
-
- for (int i = start; i != end; ++i) {
- if (generateSwitch) {
- if (i == start) {
- cfw.markTableSwitchDefault(switchStart);
- switchStackTop = cfw.getStackTop();
- } else {
- cfw.markTableSwitchCase(switchStart, i - 1 - start,
- switchStackTop);
- }
- }
- OptFunctionNode ofn = OptFunctionNode.get(scriptOrFnNodes[i]);
- cfw.addInvoke(ByteCode.INVOKEVIRTUAL,
- mainClassName,
- getFunctionInitMethodName(ofn),
- FUNCTION_INIT_SIGNATURE);
- cfw.add(ByteCode.RETURN);
- }
-
- // 4 = this + scope + context + id
- cfw.stopMethod((short)4);
- }
-
- private void generateFunctionInit(ClassFileWriter cfw,
- OptFunctionNode ofn)
- {
- final int CONTEXT_ARG = 1;
- final int SCOPE_ARG = 2;
- cfw.startMethod(getFunctionInitMethodName(ofn),
- FUNCTION_INIT_SIGNATURE,
- (short)(ClassFileWriter.ACC_PRIVATE
- | ClassFileWriter.ACC_FINAL));
-
- // Call NativeFunction.initScriptFunction
- cfw.addLoadThis();
- cfw.addALoad(CONTEXT_ARG);
- cfw.addALoad(SCOPE_ARG);
- cfw.addInvoke(ByteCode.INVOKEVIRTUAL,
- "org/mozilla/javascript/NativeFunction",
- "initScriptFunction",
- "(Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +")V");
-
- // precompile all regexp literals
- int regexpCount = ofn.fnode.getRegexpCount();
- if (regexpCount != 0) {
- cfw.addLoadThis();
- pushRegExpArray(cfw, ofn.fnode, CONTEXT_ARG, SCOPE_ARG);
- cfw.add(ByteCode.PUTFIELD, mainClassName,
- REGEXP_ARRAY_FIELD_NAME, REGEXP_ARRAY_FIELD_TYPE);
- }
-
- cfw.add(ByteCode.RETURN);
- // 3 = (scriptThis/functionRef) + scope + context
- cfw.stopMethod((short)3);
- }
-
- private void generateNativeFunctionOverrides(ClassFileWriter cfw,
- String encodedSource)
- {
- // Override NativeFunction.getLanguageVersion() with
- // public int getLanguageVersion() { return <version-constant>; }
-
- cfw.startMethod("getLanguageVersion", "()I",
- ClassFileWriter.ACC_PUBLIC);
-
- cfw.addPush(compilerEnv.getLanguageVersion());
- cfw.add(ByteCode.IRETURN);
-
- // 1: this and no argument or locals
- cfw.stopMethod((short)1);
-
- // The rest of NativeFunction overrides require specific code for each
- // script/function id
-
- final int Do_getFunctionName = 0;
- final int Do_getParamCount = 1;
- final int Do_getParamAndVarCount = 2;
- final int Do_getParamOrVarName = 3;
- final int Do_getEncodedSource = 4;
- final int Do_getParamOrVarConst = 5;
- final int SWITCH_COUNT = 6;
-
- for (int methodIndex = 0; methodIndex != SWITCH_COUNT; ++methodIndex) {
- if (methodIndex == Do_getEncodedSource && encodedSource == null) {
- continue;
- }
-
- // Generate:
- // prologue;
- // switch over function id to implement function-specific action
- // epilogue
-
- short methodLocals;
- switch (methodIndex) {
- case Do_getFunctionName:
- methodLocals = 1; // Only this
- cfw.startMethod("getFunctionName", "()Ljava/lang/String;",
- ClassFileWriter.ACC_PUBLIC);
- break;
- case Do_getParamCount:
- methodLocals = 1; // Only this
- cfw.startMethod("getParamCount", "()I",
- ClassFileWriter.ACC_PUBLIC);
- break;
- case Do_getParamAndVarCount:
- methodLocals = 1; // Only this
- cfw.startMethod("getParamAndVarCount", "()I",
- ClassFileWriter.ACC_PUBLIC);
- break;
- case Do_getParamOrVarName:
- methodLocals = 1 + 1; // this + paramOrVarIndex
- cfw.startMethod("getParamOrVarName", "(I)Ljava/lang/String;",
- ClassFileWriter.ACC_PUBLIC);
- break;
- case Do_getParamOrVarConst:
- methodLocals = 1 + 1 + 1; // this + paramOrVarName
- cfw.startMethod("getParamOrVarConst", "(I)Z",
- ClassFileWriter.ACC_PUBLIC);
- break;
- case Do_getEncodedSource:
- methodLocals = 1; // Only this
- cfw.startMethod("getEncodedSource", "()Ljava/lang/String;",
- ClassFileWriter.ACC_PUBLIC);
- cfw.addPush(encodedSource);
- break;
- default:
- throw Kit.codeBug();
- }
-
- int count = scriptOrFnNodes.length;
-
- int switchStart = 0;
- int switchStackTop = 0;
- if (count > 1) {
- // Generate switch but only if there is more then one
- // script/function
- cfw.addLoadThis();
- cfw.add(ByteCode.GETFIELD, cfw.getClassName(),
- ID_FIELD_NAME, "I");
-
- // do switch from 1 .. count - 1 mapping 0 to the default case
- switchStart = cfw.addTableSwitch(1, count - 1);
- }
-
- for (int i = 0; i != count; ++i) {
- ScriptOrFnNode n = scriptOrFnNodes[i];
- if (i == 0) {
- if (count > 1) {
- cfw.markTableSwitchDefault(switchStart);
- switchStackTop = cfw.getStackTop();
- }
- } else {
- cfw.markTableSwitchCase(switchStart, i - 1,
- switchStackTop);
- }
-
- // Impelemnet method-specific switch code
- switch (methodIndex) {
- case Do_getFunctionName:
- // Push function name
- if (n.getType() == Token.SCRIPT) {
- cfw.addPush("");
- } else {
- String name = ((FunctionNode)n).getFunctionName();
- cfw.addPush(name);
- }
- cfw.add(ByteCode.ARETURN);
- break;
-
- case Do_getParamCount:
- // Push number of defined parameters
- cfw.addPush(n.getParamCount());
- cfw.add(ByteCode.IRETURN);
- break;
-
- case Do_getParamAndVarCount:
- // Push number of defined parameters and declared variables
- cfw.addPush(n.getParamAndVarCount());
- cfw.add(ByteCode.IRETURN);
- break;
-
- case Do_getParamOrVarName:
- // Push name of parameter using another switch
- // over paramAndVarCount
- int paramAndVarCount = n.getParamAndVarCount();
- if (paramAndVarCount == 0) {
- // The runtime should never call the method in this
- // case but to make bytecode verifier happy return null
- // as throwing execption takes more code
- cfw.add(ByteCode.ACONST_NULL);
- cfw.add(ByteCode.ARETURN);
- } else if (paramAndVarCount == 1) {
- // As above do not check for valid index but always
- // return the name of the first param
- cfw.addPush(n.getParamOrVarName(0));
- cfw.add(ByteCode.ARETURN);
- } else {
- // Do switch over getParamOrVarName
- cfw.addILoad(1); // param or var index
- // do switch from 1 .. paramAndVarCount - 1 mapping 0
- // to the default case
- int paramSwitchStart = cfw.addTableSwitch(
- 1, paramAndVarCount - 1);
- for (int j = 0; j != paramAndVarCount; ++j) {
- if (cfw.getStackTop() != 0) Kit.codeBug();
- String s = n.getParamOrVarName(j);
- if (j == 0) {
- cfw.markTableSwitchDefault(paramSwitchStart);
- } else {
- cfw.markTableSwitchCase(paramSwitchStart, j - 1,
- 0);
- }
- cfw.addPush(s);
- cfw.add(ByteCode.ARETURN);
- }
- }
- break;
-
- case Do_getParamOrVarConst:
- // Push name of parameter using another switch
- // over paramAndVarCount
- paramAndVarCount = n.getParamAndVarCount();
- boolean [] constness = n.getParamAndVarConst();
- if (paramAndVarCount == 0) {
- // The runtime should never call the method in this
- // case but to make bytecode verifier happy return null
- // as throwing execption takes more code
- cfw.add(ByteCode.ICONST_0);
- cfw.add(ByteCode.IRETURN);
- } else if (paramAndVarCount == 1) {
- // As above do not check for valid index but always
- // return the name of the first param
- cfw.addPush(constness[0]);
- cfw.add(ByteCode.IRETURN);
- } else {
- // Do switch over getParamOrVarName
- cfw.addILoad(1); // param or var index
- // do switch from 1 .. paramAndVarCount - 1 mapping 0
- // to the default case
- int paramSwitchStart = cfw.addTableSwitch(
- 1, paramAndVarCount - 1);
- for (int j = 0; j != paramAndVarCount; ++j) {
- if (cfw.getStackTop() != 0) Kit.codeBug();
- if (j == 0) {
- cfw.markTableSwitchDefault(paramSwitchStart);
- } else {
- cfw.markTableSwitchCase(paramSwitchStart, j - 1,
- 0);
- }
- cfw.addPush(constness[j]);
- cfw.add(ByteCode.IRETURN);
- }
- }
- break;
-
- case Do_getEncodedSource:
- // Push number encoded source start and end
- // to prepare for encodedSource.substring(start, end)
- cfw.addPush(n.getEncodedSourceStart());
- cfw.addPush(n.getEncodedSourceEnd());
- cfw.addInvoke(ByteCode.INVOKEVIRTUAL,
- "java/lang/String",
- "substring",
- "(II)Ljava/lang/String;");
- cfw.add(ByteCode.ARETURN);
- break;
-
- default:
- throw Kit.codeBug();
- }
- }
-
- cfw.stopMethod(methodLocals);
- }
- }
-
- private void emitRegExpInit(ClassFileWriter cfw)
- {
- // precompile all regexp literals
-
- int totalRegCount = 0;
- for (int i = 0; i != scriptOrFnNodes.length; ++i) {
- totalRegCount += scriptOrFnNodes[i].getRegexpCount();
- }
- if (totalRegCount == 0) {
- return;
- }
-
- cfw.startMethod(REGEXP_INIT_METHOD_NAME, REGEXP_INIT_METHOD_SIGNATURE,
- (short)(ClassFileWriter.ACC_STATIC | ClassFileWriter.ACC_PRIVATE
- | ClassFileWriter.ACC_SYNCHRONIZED));
- cfw.addField("_reInitDone", "Z",
- (short)(ClassFileWriter.ACC_STATIC
- | ClassFileWriter.ACC_PRIVATE));
- cfw.add(ByteCode.GETSTATIC, mainClassName, "_reInitDone", "Z");
- int doInit = cfw.acquireLabel();
- cfw.add(ByteCode.IFEQ, doInit);
- cfw.add(ByteCode.RETURN);
- cfw.markLabel(doInit);
-
- for (int i = 0; i != scriptOrFnNodes.length; ++i) {
- ScriptOrFnNode n = scriptOrFnNodes[i];
- int regCount = n.getRegexpCount();
- for (int j = 0; j != regCount; ++j) {
- String reFieldName = getCompiledRegexpName(n, j);
- String reFieldType = "Ljava/lang/Object;";
- String reString = n.getRegexpString(j);
- String reFlags = n.getRegexpFlags(j);
- cfw.addField(reFieldName, reFieldType,
- (short)(ClassFileWriter.ACC_STATIC
- | ClassFileWriter.ACC_PRIVATE));
- cfw.addALoad(0); // proxy
- cfw.addALoad(1); // context
- cfw.addPush(reString);
- if (reFlags == null) {
- cfw.add(ByteCode.ACONST_NULL);
- } else {
- cfw.addPush(reFlags);
- }
- /*APPJET*/cfw.addLineNumberEntry((short)n.getRegexpLineno(j));
- cfw.addInvoke(ByteCode.INVOKEINTERFACE,
- "org/mozilla/javascript/RegExpProxy",
- "compileRegExp",
- "(Lorg/mozilla/javascript/Context;"
- +"Ljava/lang/String;Ljava/lang/String;"
- +")Ljava/lang/Object;");
- cfw.add(ByteCode.PUTSTATIC, mainClassName,
- reFieldName, reFieldType);
- }
- }
-
- cfw.addPush(1);
- cfw.add(ByteCode.PUTSTATIC, mainClassName, "_reInitDone", "Z");
- cfw.add(ByteCode.RETURN);
- cfw.stopMethod((short)2);
- }
-
- private void emitConstantDudeInitializers(ClassFileWriter cfw)
- {
- int N = itsConstantListSize;
- if (N == 0)
- return;
-
- cfw.startMethod("<clinit>", "()V",
- (short)(ClassFileWriter.ACC_STATIC | ClassFileWriter.ACC_FINAL));
-
- double[] array = itsConstantList;
- for (int i = 0; i != N; ++i) {
- double num = array[i];
- String constantName = "_k" + i;
- String constantType = getStaticConstantWrapperType(num);
- cfw.addField(constantName, constantType,
- (short)(ClassFileWriter.ACC_STATIC
- | ClassFileWriter.ACC_PRIVATE));
- int inum = (int)num;
- if (inum == num) {
- cfw.add(ByteCode.NEW, "java/lang/Integer");
- cfw.add(ByteCode.DUP);
- cfw.addPush(inum);
- cfw.addInvoke(ByteCode.INVOKESPECIAL, "java/lang/Integer",
- "<init>", "(I)V");
- } else {
- cfw.addPush(num);
- addDoubleWrap(cfw);
- }
- cfw.add(ByteCode.PUTSTATIC, mainClassName,
- constantName, constantType);
- }
-
- cfw.add(ByteCode.RETURN);
- cfw.stopMethod((short)0);
- }
-
- void pushRegExpArray(ClassFileWriter cfw, ScriptOrFnNode n,
- int contextArg, int scopeArg)
- {
- int regexpCount = n.getRegexpCount();
- if (regexpCount == 0) throw badTree();
-
- cfw.addPush(regexpCount);
- cfw.add(ByteCode.ANEWARRAY, "java/lang/Object");
-
- cfw.addALoad(contextArg);
- cfw.addInvoke(ByteCode.INVOKESTATIC,
- "org/mozilla/javascript/ScriptRuntime",
- "checkRegExpProxy",
- "(Lorg/mozilla/javascript/Context;"
- +")Lorg/mozilla/javascript/RegExpProxy;");
- // Stack: proxy, array
- cfw.add(ByteCode.DUP);
- cfw.addALoad(contextArg);
- cfw.addInvoke(ByteCode.INVOKESTATIC, mainClassName,
- REGEXP_INIT_METHOD_NAME, REGEXP_INIT_METHOD_SIGNATURE);
- for (int i = 0; i != regexpCount; ++i) {
- // Stack: proxy, array
- cfw.add(ByteCode.DUP2);
- cfw.addALoad(contextArg);
- cfw.addALoad(scopeArg);
- cfw.add(ByteCode.GETSTATIC, mainClassName,
- getCompiledRegexpName(n, i), "Ljava/lang/Object;");
- // Stack: compiledRegExp, scope, cx, proxy, array, proxy, array
- cfw.addInvoke(ByteCode.INVOKEINTERFACE,
- "org/mozilla/javascript/RegExpProxy",
- "wrapRegExp",
- "(Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"Ljava/lang/Object;"
- +")Lorg/mozilla/javascript/Scriptable;");
- // Stack: wrappedRegExp, array, proxy, array
- cfw.addPush(i);
- cfw.add(ByteCode.SWAP);
- cfw.add(ByteCode.AASTORE);
- // Stack: proxy, array
- }
- // remove proxy
- cfw.add(ByteCode.POP);
- }
-
- void pushNumberAsObject(ClassFileWriter cfw, double num)
- {
- if (num == 0.0) {
- if (1 / num > 0) {
- // +0.0
- cfw.add(ByteCode.GETSTATIC,
- "org/mozilla/javascript/optimizer/OptRuntime",
- "zeroObj", "Ljava/lang/Double;");
- } else {
- cfw.addPush(num);
- addDoubleWrap(cfw);
- }
-
- } else if (num == 1.0) {
- cfw.add(ByteCode.GETSTATIC,
- "org/mozilla/javascript/optimizer/OptRuntime",
- "oneObj", "Ljava/lang/Double;");
- return;
-
- } else if (num == -1.0) {
- cfw.add(ByteCode.GETSTATIC,
- "org/mozilla/javascript/optimizer/OptRuntime",
- "minusOneObj", "Ljava/lang/Double;");
-
- } else if (num != num) {
- cfw.add(ByteCode.GETSTATIC,
- "org/mozilla/javascript/ScriptRuntime",
- "NaNobj", "Ljava/lang/Double;");
-
- } else if (itsConstantListSize >= 2000) {
- // There appears to be a limit in the JVM on either the number
- // of static fields in a class or the size of the class
- // initializer. Either way, we can't have any more than 2000
- // statically init'd constants.
- cfw.addPush(num);
- addDoubleWrap(cfw);
-
- } else {
- int N = itsConstantListSize;
- int index = 0;
- if (N == 0) {
- itsConstantList = new double[64];
- } else {
- double[] array = itsConstantList;
- while (index != N && array[index] != num) {
- ++index;
- }
- if (N == array.length) {
- array = new double[N * 2];
- System.arraycopy(itsConstantList, 0, array, 0, N);
- itsConstantList = array;
- }
- }
- if (index == N) {
- itsConstantList[N] = num;
- itsConstantListSize = N + 1;
- }
- String constantName = "_k" + index;
- String constantType = getStaticConstantWrapperType(num);
- cfw.add(ByteCode.GETSTATIC, mainClassName,
- constantName, constantType);
- }
- }
-
- private static void addDoubleWrap(ClassFileWriter cfw)
- {
- cfw.addInvoke(ByteCode.INVOKESTATIC,
- "org/mozilla/javascript/optimizer/OptRuntime",
- "wrapDouble", "(D)Ljava/lang/Double;");
- }
-
- private static String getStaticConstantWrapperType(double num)
- {
- int inum = (int)num;
- if (inum == num) {
- return "Ljava/lang/Integer;";
- } else {
- return "Ljava/lang/Double;";
- }
- }
- static void pushUndefined(ClassFileWriter cfw)
- {
- cfw.add(ByteCode.GETSTATIC, "org/mozilla/javascript/Undefined",
- "instance", "Ljava/lang/Object;");
- }
-
- int getIndex(ScriptOrFnNode n)
- {
- return scriptOrFnIndexes.getExisting(n);
- }
-
- static String getDirectTargetFieldName(int i)
- {
- return "_dt" + i;
- }
-
- String getDirectCtorName(ScriptOrFnNode n)
- {
- return "_n"+getIndex(n);
- }
-
- String getBodyMethodName(ScriptOrFnNode n)
- {
- return "_c"+getIndex(n);
- }
-
- String getBodyMethodSignature(ScriptOrFnNode n)
- {
- StringBuffer sb = new StringBuffer();
- sb.append('(');
- sb.append(mainClassSignature);
- sb.append("Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"Lorg/mozilla/javascript/Scriptable;");
- if (n.getType() == Token.FUNCTION) {
- OptFunctionNode ofn = OptFunctionNode.get(n);
- if (ofn.isTargetOfDirectCall()) {
- int pCount = ofn.fnode.getParamCount();
- for (int i = 0; i != pCount; i++) {
- sb.append("Ljava/lang/Object;D");
- }
- }
- }
- sb.append("[Ljava/lang/Object;)Ljava/lang/Object;");
- return sb.toString();
- }
-
- String getFunctionInitMethodName(OptFunctionNode ofn)
- {
- return "_i"+getIndex(ofn.fnode);
- }
-
- String getCompiledRegexpName(ScriptOrFnNode n, int regexpIndex)
- {
- return "_re"+getIndex(n)+"_"+regexpIndex;
- }
-
- static RuntimeException badTree()
- {
- throw new RuntimeException("Bad tree in codegen");
- }
-
- void setMainMethodClass(String className)
- {
- mainMethodClass = className;
- }
-
- static final String DEFAULT_MAIN_METHOD_CLASS
- = "org.mozilla.javascript.optimizer.OptRuntime";
-
- private static final String SUPER_CLASS_NAME
- = "org.mozilla.javascript.NativeFunction";
-
- static final String DIRECT_CALL_PARENT_FIELD = "_dcp";
- private static final String ID_FIELD_NAME = "_id";
-
- private static final String REGEXP_INIT_METHOD_NAME = "_reInit";
- private static final String REGEXP_INIT_METHOD_SIGNATURE
- = "(Lorg/mozilla/javascript/RegExpProxy;"
- +"Lorg/mozilla/javascript/Context;"
- +")V";
- static final String REGEXP_ARRAY_FIELD_NAME = "_re";
- static final String REGEXP_ARRAY_FIELD_TYPE = "[Ljava/lang/Object;";
-
- static final String FUNCTION_INIT_SIGNATURE
- = "(Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +")V";
-
- static final String FUNCTION_CONSTRUCTOR_SIGNATURE
- = "(Lorg/mozilla/javascript/Scriptable;"
- +"Lorg/mozilla/javascript/Context;I)V";
-
- private static final Object globalLock = new Object();
- private static int globalSerialClassCounter;
-
- private CompilerEnvirons compilerEnv;
-
- private ObjArray directCallTargets;
- ScriptOrFnNode[] scriptOrFnNodes;
- private ObjToIntMap scriptOrFnIndexes;
-
- private String mainMethodClass = DEFAULT_MAIN_METHOD_CLASS;
-
- String mainClassName;
- String mainClassSignature;
-
- private double[] itsConstantList;
- private int itsConstantListSize;
-}
-
-
-class BodyCodegen
-{
- void generateBodyCode()
- {
- isGenerator = Codegen.isGenerator(scriptOrFn);
-
- // generate the body of the current function or script object
- initBodyGeneration();
-
- if (isGenerator) {
-
- // All functions in the generated bytecode have a unique name. Every
- // generator has a unique prefix followed by _gen
- String type = "(" +
- codegen.mainClassSignature +
- "Lorg/mozilla/javascript/Context;" +
- "Lorg/mozilla/javascript/Scriptable;" +
- "Ljava/lang/Object;" +
- "Ljava/lang/Object;I)Ljava/lang/Object;";
- cfw.startMethod(codegen.getBodyMethodName(scriptOrFn) + "_gen",
- type,
- (short)(ClassFileWriter.ACC_STATIC
- | ClassFileWriter.ACC_PRIVATE));
- } else {
- cfw.startMethod(codegen.getBodyMethodName(scriptOrFn),
- codegen.getBodyMethodSignature(scriptOrFn),
- (short)(ClassFileWriter.ACC_STATIC
- | ClassFileWriter.ACC_PRIVATE));
- }
-
- generatePrologue();
- Node treeTop;
- if (fnCurrent != null) {
- treeTop = scriptOrFn.getLastChild();
- } else {
- treeTop = scriptOrFn;
- }
- generateStatement(treeTop);
- generateEpilogue();
-
- cfw.stopMethod((short)(localsMax + 1));
-
- if (isGenerator) {
- // generate the user visible method which when invoked will
- // return a generator object
- generateGenerator();
- }
- }
-
- // This creates a the user-facing function that returns a NativeGenerator
- // object.
- private void generateGenerator()
- {
- cfw.startMethod(codegen.getBodyMethodName(scriptOrFn),
- codegen.getBodyMethodSignature(scriptOrFn),
- (short)(ClassFileWriter.ACC_STATIC
- | ClassFileWriter.ACC_PRIVATE));
-
- initBodyGeneration();
- argsLocal = firstFreeLocal++;
- localsMax = firstFreeLocal;
-
- // get top level scope
- if (fnCurrent != null && !inDirectCallFunction
- && (!compilerEnv.isUseDynamicScope()
- || fnCurrent.fnode.getIgnoreDynamicScope()))
- {
- // Unless we're either in a direct call or using dynamic scope,
- // use the enclosing scope of the function as our variable object.
- cfw.addALoad(funObjLocal);
- cfw.addInvoke(ByteCode.INVOKEINTERFACE,
- "org/mozilla/javascript/Scriptable",
- "getParentScope",
- "()Lorg/mozilla/javascript/Scriptable;");
- cfw.addAStore(variableObjectLocal);
- }
-
- // generators are forced to have an activation record
- cfw.addALoad(funObjLocal);
- cfw.addALoad(variableObjectLocal);
- cfw.addALoad(argsLocal);
- addScriptRuntimeInvoke("createFunctionActivation",
- "(Lorg/mozilla/javascript/NativeFunction;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"[Ljava/lang/Object;"
- +")Lorg/mozilla/javascript/Scriptable;");
- cfw.addAStore(variableObjectLocal);
-
- // create a function object
- cfw.add(ByteCode.NEW, codegen.mainClassName);
- // Call function constructor
- cfw.add(ByteCode.DUP);
- cfw.addALoad(variableObjectLocal);
- cfw.addALoad(contextLocal); // load 'cx'
- cfw.addPush(scriptOrFnIndex);
- cfw.addInvoke(ByteCode.INVOKESPECIAL, codegen.mainClassName,
- "<init>", Codegen.FUNCTION_CONSTRUCTOR_SIGNATURE);
-
- // Init mainScript field
- cfw.add(ByteCode.DUP);
- if (isTopLevel) Kit.codeBug(); // Only functions can be generators
- cfw.add(ByteCode.ALOAD_0);
- cfw.add(ByteCode.GETFIELD,
- codegen.mainClassName,
- Codegen.DIRECT_CALL_PARENT_FIELD,
- codegen.mainClassSignature);
- cfw.add(ByteCode.PUTFIELD,
- codegen.mainClassName,
- Codegen.DIRECT_CALL_PARENT_FIELD,
- codegen.mainClassSignature);
-
- generateNestedFunctionInits();
-
- // create the NativeGenerator object that we return
- cfw.addALoad(variableObjectLocal);
- cfw.addALoad(thisObjLocal);
- cfw.addLoadConstant(maxLocals);
- cfw.addLoadConstant(maxStack);
- addOptRuntimeInvoke("createNativeGenerator",
- "(Lorg/mozilla/javascript/NativeFunction;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"Lorg/mozilla/javascript/Scriptable;II"
- +")Lorg/mozilla/javascript/Scriptable;");
-
- cfw.add(ByteCode.ARETURN);
- cfw.stopMethod((short)(localsMax + 1));
- }
-
- private void generateNestedFunctionInits()
- {
- int functionCount = scriptOrFn.getFunctionCount();
- for (int i = 0; i != functionCount; i++) {
- OptFunctionNode ofn = OptFunctionNode.get(scriptOrFn, i);
- if (ofn.fnode.getFunctionType()
- == FunctionNode.FUNCTION_STATEMENT)
- {
- visitFunction(ofn, FunctionNode.FUNCTION_STATEMENT);
- }
- }
- }
-
- private void initBodyGeneration()
- {
- isTopLevel = (scriptOrFn == codegen.scriptOrFnNodes[0]);
-
- varRegisters = null;
- if (scriptOrFn.getType() == Token.FUNCTION) {
- fnCurrent = OptFunctionNode.get(scriptOrFn);
- hasVarsInRegs = !fnCurrent.fnode.requiresActivation();
- if (hasVarsInRegs) {
- int n = fnCurrent.fnode.getParamAndVarCount();
- if (n != 0) {
- varRegisters = new short[n];
- }
- }
- inDirectCallFunction = fnCurrent.isTargetOfDirectCall();
- if (inDirectCallFunction && !hasVarsInRegs) Codegen.badTree();
- } else {
- fnCurrent = null;
- hasVarsInRegs = false;
- inDirectCallFunction = false;
- }
-
- locals = new int[MAX_LOCALS];
-
- funObjLocal = 0;
- contextLocal = 1;
- variableObjectLocal = 2;
- thisObjLocal = 3;
- localsMax = (short) 4; // number of parms + "this"
- firstFreeLocal = 4;
-
- popvLocal = -1;
- argsLocal = -1;
- itsZeroArgArray = -1;
- itsOneArgArray = -1;
- scriptRegexpLocal = -1;
- epilogueLabel = -1;
- enterAreaStartLabel = -1;
- generatorStateLocal = -1;
- }
-
- /**
- * Generate the prologue for a function or script.
- */
- private void generatePrologue()
- {
- if (inDirectCallFunction) {
- int directParameterCount = scriptOrFn.getParamCount();
- // 0 is reserved for function Object 'this'
- // 1 is reserved for context
- // 2 is reserved for parentScope
- // 3 is reserved for script 'this'
- if (firstFreeLocal != 4) Kit.codeBug();
- for (int i = 0; i != directParameterCount; ++i) {
- varRegisters[i] = firstFreeLocal;
- // 3 is 1 for Object parm and 2 for double parm
- firstFreeLocal += 3;
- }
- if (!fnCurrent.getParameterNumberContext()) {
- // make sure that all parameters are objects
- itsForcedObjectParameters = true;
- for (int i = 0; i != directParameterCount; ++i) {
- short reg = varRegisters[i];
- cfw.addALoad(reg);
- cfw.add(ByteCode.GETSTATIC,
- "java/lang/Void",
- "TYPE",
- "Ljava/lang/Class;");
- int isObjectLabel = cfw.acquireLabel();
- cfw.add(ByteCode.IF_ACMPNE, isObjectLabel);
- cfw.addDLoad(reg + 1);
- addDoubleWrap();
- cfw.addAStore(reg);
- cfw.markLabel(isObjectLabel);
- }
- }
- }
-
- if (fnCurrent != null && !inDirectCallFunction
- && (!compilerEnv.isUseDynamicScope()
- || fnCurrent.fnode.getIgnoreDynamicScope()))
- {
- // Unless we're either in a direct call or using dynamic scope,
- // use the enclosing scope of the function as our variable object.
- cfw.addALoad(funObjLocal);
- cfw.addInvoke(ByteCode.INVOKEINTERFACE,
- "org/mozilla/javascript/Scriptable",
- "getParentScope",
- "()Lorg/mozilla/javascript/Scriptable;");
- cfw.addAStore(variableObjectLocal);
- }
-
- // reserve 'args[]'
- argsLocal = firstFreeLocal++;
- localsMax = firstFreeLocal;
-
- // Generate Generator specific prelude
- if (isGenerator) {
-
- // reserve 'args[]'
- operationLocal = firstFreeLocal++;
- localsMax = firstFreeLocal;
-
- // Local 3 is a reference to a GeneratorState object. The rest
- // of codegen expects local 3 to be a reference to the thisObj.
- // So move the value in local 3 to generatorStateLocal, and load
- // the saved thisObj from the GeneratorState object.
- cfw.addALoad(thisObjLocal);
- generatorStateLocal = firstFreeLocal++;
- localsMax = firstFreeLocal;
- cfw.add(ByteCode.CHECKCAST, OptRuntime.GeneratorState.CLASS_NAME);
- cfw.add(ByteCode.DUP);
- cfw.addAStore(generatorStateLocal);
- cfw.add(ByteCode.GETFIELD,
- OptRuntime.GeneratorState.CLASS_NAME,
- OptRuntime.GeneratorState.thisObj_NAME,
- OptRuntime.GeneratorState.thisObj_TYPE);
- cfw.addAStore(thisObjLocal);
-
- if (epilogueLabel == -1) {
- epilogueLabel = cfw.acquireLabel();
- }
-
- ArrayList targets = ((FunctionNode)scriptOrFn).getResumptionPoints();
- if (targets != null) {
- // get resumption point
- generateGetGeneratorResumptionPoint();
-
- // generate dispatch table
- generatorSwitch = cfw.addTableSwitch(0,
- targets.size() + GENERATOR_START);
- generateCheckForThrowOrClose(-1, false, GENERATOR_START);
- }
- }
-
- if (fnCurrent == null) {
- // See comments in case Token.REGEXP
- if (scriptOrFn.getRegexpCount() != 0) {
- scriptRegexpLocal = getNewWordLocal();
- codegen.pushRegExpArray(cfw, scriptOrFn, contextLocal,
- variableObjectLocal);
- cfw.addAStore(scriptRegexpLocal);
- }
- }
-
- if (compilerEnv.isGenerateObserverCount())
- saveCurrentCodeOffset();
-
- if (hasVarsInRegs) {
- // No need to create activation. Pad arguments if need be.
- int parmCount = scriptOrFn.getParamCount();
- if (parmCount > 0 && !inDirectCallFunction) {
- // Set up args array
- // check length of arguments, pad if need be
- cfw.addALoad(argsLocal);
- cfw.add(ByteCode.ARRAYLENGTH);
- cfw.addPush(parmCount);
- int label = cfw.acquireLabel();
- cfw.add(ByteCode.IF_ICMPGE, label);
- cfw.addALoad(argsLocal);
- cfw.addPush(parmCount);
- addScriptRuntimeInvoke("padArguments",
- "([Ljava/lang/Object;I"
- +")[Ljava/lang/Object;");
- cfw.addAStore(argsLocal);
- cfw.markLabel(label);
- }
-
- int paramCount = fnCurrent.fnode.getParamCount();
- int varCount = fnCurrent.fnode.getParamAndVarCount();
- boolean [] constDeclarations = fnCurrent.fnode.getParamAndVarConst();
-
- // REMIND - only need to initialize the vars that don't get a value
- // before the next call and are used in the function
- short firstUndefVar = -1;
- for (int i = 0; i != varCount; ++i) {
- short reg = -1;
- if (i < paramCount) {
- if (!inDirectCallFunction) {
- reg = getNewWordLocal();
- cfw.addALoad(argsLocal);
- cfw.addPush(i);
- cfw.add(ByteCode.AALOAD);
- cfw.addAStore(reg);
- }
- } else if (fnCurrent.isNumberVar(i)) {
- reg = getNewWordPairLocal(constDeclarations[i]);
- cfw.addPush(0.0);
- cfw.addDStore(reg);
- } else {
- reg = getNewWordLocal(constDeclarations[i]);
- if (firstUndefVar == -1) {
- Codegen.pushUndefined(cfw);
- firstUndefVar = reg;
- } else {
- cfw.addALoad(firstUndefVar);
- }
- cfw.addAStore(reg);
- }
- if (reg >= 0) {
- if (constDeclarations[i]) {
- cfw.addPush(0);
- cfw.addIStore(reg + (fnCurrent.isNumberVar(i) ? 2 : 1));
- }
- varRegisters[i] = reg;
- }
-
- // Add debug table entry if we're generating debug info
- if (compilerEnv.isGenerateDebugInfo()) {
- String name = fnCurrent.fnode.getParamOrVarName(i);
- String type = fnCurrent.isNumberVar(i)
- ? "D" : "Ljava/lang/Object;";
- int startPC = cfw.getCurrentCodeOffset();
- if (reg < 0) {
- reg = varRegisters[i];
- }
- cfw.addVariableDescriptor(name, type, startPC, reg);
- }
- }
-
- // Skip creating activation object.
- return;
- }
-
- // skip creating activation object for the body of a generator. The
- // activation record required by a generator has already been created
- // in generateGenerator().
- if (isGenerator)
- return;
-
-
- String debugVariableName;
- if (fnCurrent != null) {
- debugVariableName = "activation";
- cfw.addALoad(funObjLocal);
- cfw.addALoad(variableObjectLocal);
- cfw.addALoad(argsLocal);
- addScriptRuntimeInvoke("createFunctionActivation",
- "(Lorg/mozilla/javascript/NativeFunction;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"[Ljava/lang/Object;"
- +")Lorg/mozilla/javascript/Scriptable;");
- cfw.addAStore(variableObjectLocal);
- cfw.addALoad(contextLocal);
- cfw.addALoad(variableObjectLocal);
- addScriptRuntimeInvoke("enterActivationFunction",
- "(Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +")V");
- } else {
- debugVariableName = "global";
- cfw.addALoad(funObjLocal);
- cfw.addALoad(thisObjLocal);
- cfw.addALoad(contextLocal);
- cfw.addALoad(variableObjectLocal);
- cfw.addPush(0); // false to indicate it is not eval script
- addScriptRuntimeInvoke("initScript",
- "(Lorg/mozilla/javascript/NativeFunction;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"Z"
- +")V");
- }
-
- enterAreaStartLabel = cfw.acquireLabel();
- epilogueLabel = cfw.acquireLabel();
- cfw.markLabel(enterAreaStartLabel);
-
- generateNestedFunctionInits();
-
- // default is to generate debug info
- if (compilerEnv.isGenerateDebugInfo()) {
- cfw.addVariableDescriptor(debugVariableName,
- "Lorg/mozilla/javascript/Scriptable;",
- cfw.getCurrentCodeOffset(), variableObjectLocal);
- }
-
- if (fnCurrent == null) {
- // OPT: use dataflow to prove that this assignment is dead
- popvLocal = getNewWordLocal();
- Codegen.pushUndefined(cfw);
- cfw.addAStore(popvLocal);
-
- int linenum = scriptOrFn.getEndLineno();
- if (linenum != -1)
- cfw.addLineNumberEntry((short)linenum);
-
- } else {
- if (fnCurrent.itsContainsCalls0) {
- itsZeroArgArray = getNewWordLocal();
- cfw.add(ByteCode.GETSTATIC,
- "org/mozilla/javascript/ScriptRuntime",
- "emptyArgs", "[Ljava/lang/Object;");
- cfw.addAStore(itsZeroArgArray);
- }
- if (fnCurrent.itsContainsCalls1) {
- itsOneArgArray = getNewWordLocal();
- cfw.addPush(1);
- cfw.add(ByteCode.ANEWARRAY, "java/lang/Object");
- cfw.addAStore(itsOneArgArray);
- }
- }
- }
-
- private void generateGetGeneratorResumptionPoint()
- {
- cfw.addALoad(generatorStateLocal);
- cfw.add(ByteCode.GETFIELD,
- OptRuntime.GeneratorState.CLASS_NAME,
- OptRuntime.GeneratorState.resumptionPoint_NAME,
- OptRuntime.GeneratorState.resumptionPoint_TYPE);
- }
-
- private void generateSetGeneratorResumptionPoint(int nextState)
- {
- cfw.addALoad(generatorStateLocal);
- cfw.addLoadConstant(nextState);
- cfw.add(ByteCode.PUTFIELD,
- OptRuntime.GeneratorState.CLASS_NAME,
- OptRuntime.GeneratorState.resumptionPoint_NAME,
- OptRuntime.GeneratorState.resumptionPoint_TYPE);
- }
-
- private void generateGetGeneratorStackState()
- {
- cfw.addALoad(generatorStateLocal);
- addOptRuntimeInvoke("getGeneratorStackState",
- "(Ljava/lang/Object;)[Ljava/lang/Object;");
- }
-
- private void generateEpilogue()
- {
- if (compilerEnv.isGenerateObserverCount())
- addInstructionCount();
- if (isGenerator) {
- // generate locals initialization
- HashMap liveLocals = ((FunctionNode)scriptOrFn).getLiveLocals();
- if (liveLocals != null) {
- ArrayList nodes = ((FunctionNode)scriptOrFn).getResumptionPoints();
- for (int i = 0; i < nodes.size(); i++) {
- Node node = (Node) nodes.get(i);
- int[] live = (int [])liveLocals.get(node);
- if (live != null) {
- cfw.markTableSwitchCase(generatorSwitch,
- getNextGeneratorState(node));
- generateGetGeneratorLocalsState();
- for (int j = 0; j < live.length; j++) {
- cfw.add(ByteCode.DUP);
- cfw.addLoadConstant(j);
- cfw.add(ByteCode.AALOAD);
- cfw.addAStore(live[j]);
- }
- cfw.add(ByteCode.POP);
- cfw.add(ByteCode.GOTO, getTargetLabel(node));
- }
- }
- }
-
- // generate dispatch tables for finally
- if (finallys != null) {
- Enumeration en = finallys.keys();
- while(en.hasMoreElements()) {
- Node n = (Node) en.nextElement();
- if (n.getType() == Token.FINALLY) {
- FinallyReturnPoint ret =
- (FinallyReturnPoint)finallys.get(n);
- // the finally will jump here
- cfw.markLabel(ret.tableLabel, (short)1);
-
- // start generating a dispatch table
- int startSwitch = cfw.addTableSwitch(0,
- ret.jsrPoints.size() - 1);
- int c = 0;
- cfw.markTableSwitchDefault(startSwitch);
- for (int i = 0; i < ret.jsrPoints.size(); i++) {
- // generate gotos back to the JSR location
- cfw.markTableSwitchCase(startSwitch, c);
- cfw.add(ByteCode.GOTO,
- ((Integer)ret.jsrPoints.get(i)).intValue());
- c++;
- }
- }
- }
- }
- }
-
- if (epilogueLabel != -1) {
- cfw.markLabel(epilogueLabel);
- }
-
- if (hasVarsInRegs) {
- cfw.add(ByteCode.ARETURN);
- return;
- } else if (isGenerator) {
- if (((FunctionNode)scriptOrFn).getResumptionPoints() != null) {
- cfw.markTableSwitchDefault(generatorSwitch);
- }
-
- // change state for re-entry
- generateSetGeneratorResumptionPoint(GENERATOR_TERMINATE);
-
- // throw StopIteration
- cfw.addALoad(variableObjectLocal);
- addOptRuntimeInvoke("throwStopIteration",
- "(Ljava/lang/Object;)V");
-
- Codegen.pushUndefined(cfw);
- cfw.add(ByteCode.ARETURN);
-
- } else if (fnCurrent == null) {
- cfw.addALoad(popvLocal);
- cfw.add(ByteCode.ARETURN);
- } else {
- generateActivationExit();
- cfw.add(ByteCode.ARETURN);
-
- // Generate catch block to catch all and rethrow to call exit code
- // under exception propagation as well.
-
- int finallyHandler = cfw.acquireLabel();
- cfw.markHandler(finallyHandler);
- short exceptionObject = getNewWordLocal();
- cfw.addAStore(exceptionObject);
-
- // Duplicate generateActivationExit() in the catch block since it
- // takes less space then full-featured ByteCode.JSR/ByteCode.RET
- generateActivationExit();
-
- cfw.addALoad(exceptionObject);
- releaseWordLocal(exceptionObject);
- // rethrow
- cfw.add(ByteCode.ATHROW);
-
- // mark the handler
- cfw.addExceptionHandler(enterAreaStartLabel, epilogueLabel,
- finallyHandler, null); // catch any
- }
- }
-
- private void generateGetGeneratorLocalsState() {
- cfw.addALoad(generatorStateLocal);
- addOptRuntimeInvoke("getGeneratorLocalsState",
- "(Ljava/lang/Object;)[Ljava/lang/Object;");
- }
-
- private void generateActivationExit()
- {
- if (fnCurrent == null || hasVarsInRegs) throw Kit.codeBug();
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke("exitActivationFunction",
- "(Lorg/mozilla/javascript/Context;)V");
- }
-
- private void generateStatement(Node node)
- {
- updateLineNumber(node);
- int type = node.getType();
- Node child = node.getFirstChild();
- switch (type) {
- case Token.LOOP:
- case Token.LABEL:
- case Token.WITH:
- case Token.SCRIPT:
- case Token.BLOCK:
- case Token.EMPTY:
- // no-ops.
- while (child != null) {
- generateStatement(child);
- child = child.getNext();
- }
- break;
-
- case Token.LOCAL_BLOCK: {
- int local = getNewWordLocal();
- if (isGenerator) {
- cfw.add(ByteCode.ACONST_NULL);
- cfw.addAStore(local);
- }
- node.putIntProp(Node.LOCAL_PROP, local);
- while (child != null) {
- generateStatement(child);
- child = child.getNext();
- }
- releaseWordLocal((short)local);
- node.removeProp(Node.LOCAL_PROP);
- break;
- }
-
- case Token.FUNCTION: {
- int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
- OptFunctionNode ofn = OptFunctionNode.get(scriptOrFn, fnIndex);
- int t = ofn.fnode.getFunctionType();
- if (t == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) {
- visitFunction(ofn, t);
- } else {
- if (t != FunctionNode.FUNCTION_STATEMENT) {
- throw Codegen.badTree();
- }
- }
- break;
- }
-
- case Token.TRY:
- visitTryCatchFinally((Node.Jump)node, child);
- break;
-
- case Token.CATCH_SCOPE:
- {
- // nothing stays on the stack on entry into a catch scope
- cfw.setStackTop((short) 0);
-
- int local = getLocalBlockRegister(node);
- int scopeIndex
- = node.getExistingIntProp(Node.CATCH_SCOPE_PROP);
-
- String name = child.getString(); // name of exception
- child = child.getNext();
- generateExpression(child, node); // load expression object
- if (scopeIndex == 0) {
- cfw.add(ByteCode.ACONST_NULL);
- } else {
- // Load previous catch scope object
- cfw.addALoad(local);
- }
- cfw.addPush(name);
- cfw.addALoad(contextLocal);
- cfw.addALoad(variableObjectLocal);
-
- addScriptRuntimeInvoke(
- "newCatchScope",
- "(Ljava/lang/Throwable;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"Ljava/lang/String;"
- +"Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +")Lorg/mozilla/javascript/Scriptable;");
- cfw.addAStore(local);
- }
- break;
-
- case Token.THROW:
- generateExpression(child, node);
- if (compilerEnv.isGenerateObserverCount())
- addInstructionCount();
- generateThrowJavaScriptException();
- break;
-
- case Token.RETHROW:
- if (compilerEnv.isGenerateObserverCount())
- addInstructionCount();
- cfw.addALoad(getLocalBlockRegister(node));
- cfw.add(ByteCode.ATHROW);
- break;
-
- case Token.RETURN_RESULT:
- case Token.RETURN:
- if (!isGenerator) {
- if (child != null) {
- generateExpression(child, node);
- } else if (type == Token.RETURN) {
- Codegen.pushUndefined(cfw);
- } else {
- if (popvLocal < 0) throw Codegen.badTree();
- cfw.addALoad(popvLocal);
- }
- }
- if (compilerEnv.isGenerateObserverCount())
- addInstructionCount();
- if (epilogueLabel == -1) {
- if (!hasVarsInRegs) throw Codegen.badTree();
- epilogueLabel = cfw.acquireLabel();
- }
- cfw.add(ByteCode.GOTO, epilogueLabel);
- break;
-
- case Token.SWITCH:
- if (compilerEnv.isGenerateObserverCount())
- addInstructionCount();
- visitSwitch((Node.Jump)node, child);
- break;
-
- case Token.ENTERWITH:
- generateExpression(child, node);
- cfw.addALoad(contextLocal);
- cfw.addALoad(variableObjectLocal);
- addScriptRuntimeInvoke(
- "enterWith",
- "(Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +")Lorg/mozilla/javascript/Scriptable;");
- cfw.addAStore(variableObjectLocal);
- incReferenceWordLocal(variableObjectLocal);
- break;
-
- case Token.LEAVEWITH:
- cfw.addALoad(variableObjectLocal);
- addScriptRuntimeInvoke(
- "leaveWith",
- "(Lorg/mozilla/javascript/Scriptable;"
- +")Lorg/mozilla/javascript/Scriptable;");
- cfw.addAStore(variableObjectLocal);
- decReferenceWordLocal(variableObjectLocal);
- break;
-
- case Token.ENUM_INIT_KEYS:
- case Token.ENUM_INIT_VALUES:
- case Token.ENUM_INIT_ARRAY:
- generateExpression(child, node);
- cfw.addALoad(contextLocal);
- int enumType = type == Token.ENUM_INIT_KEYS
- ? ScriptRuntime.ENUMERATE_KEYS :
- type == Token.ENUM_INIT_VALUES
- ? ScriptRuntime.ENUMERATE_VALUES :
- ScriptRuntime.ENUMERATE_ARRAY;
- cfw.addPush(enumType);
- addScriptRuntimeInvoke("enumInit",
- "(Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +"I"
- +")Ljava/lang/Object;");
- cfw.addAStore(getLocalBlockRegister(node));
- break;
-
- case Token.EXPR_VOID:
- if (child.getType() == Token.SETVAR) {
- /* special case this so as to avoid unnecessary
- load's & pop's */
- visitSetVar(child, child.getFirstChild(), false);
- }
- else if (child.getType() == Token.SETCONSTVAR) {
- /* special case this so as to avoid unnecessary
- load's & pop's */
- visitSetConstVar(child, child.getFirstChild(), false);
- }
- else if (child.getType() == Token.YIELD) {
- generateYieldPoint(child, false);
- }
- else {
- generateExpression(child, node);
- if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1)
- cfw.add(ByteCode.POP2);
- else
- cfw.add(ByteCode.POP);
- }
- break;
-
- case Token.EXPR_RESULT:
- generateExpression(child, node);
- if (popvLocal < 0) {
- popvLocal = getNewWordLocal();
- }
- cfw.addAStore(popvLocal);
- break;
-
- case Token.TARGET:
- {
- if (compilerEnv.isGenerateObserverCount())
- addInstructionCount();
- int label = getTargetLabel(node);
- cfw.markLabel(label);
- if (compilerEnv.isGenerateObserverCount())
- saveCurrentCodeOffset();
- }
- break;
-
- case Token.JSR:
- case Token.GOTO:
- case Token.IFEQ:
- case Token.IFNE:
- if (compilerEnv.isGenerateObserverCount())
- addInstructionCount();
- visitGoto((Node.Jump)node, type, child);
- break;
-
- case Token.FINALLY:
- {
- if (compilerEnv.isGenerateObserverCount())
- saveCurrentCodeOffset();
- // there is exactly one value on the stack when enterring
- // finally blocks: the return address (or its int encoding)
- cfw.setStackTop((short)1);
-
- // Save return address in a new local
- int finallyRegister = getNewWordLocal();
- if (isGenerator)
- generateIntegerWrap();
- cfw.addAStore(finallyRegister);
-
- while (child != null) {
- generateStatement(child);
- child = child.getNext();
- }
- if (isGenerator) {
- cfw.addALoad(finallyRegister);
- cfw.add(ByteCode.CHECKCAST, "java/lang/Integer");
- generateIntegerUnwrap();
- FinallyReturnPoint ret =
- (FinallyReturnPoint)finallys.get(node);
- ret.tableLabel = cfw.acquireLabel();
- cfw.add(ByteCode.GOTO, ret.tableLabel);
- } else {
- cfw.add(ByteCode.RET, finallyRegister);
- }
- releaseWordLocal((short)finallyRegister);
- }
- break;
-
- case Token.DEBUGGER:
- break;
-
- default:
- throw Codegen.badTree();
- }
-
- }
-
- private void generateIntegerWrap()
- {
- cfw.addInvoke(ByteCode.INVOKESTATIC, "java/lang/Integer", "valueOf",
- "(I)Ljava/lang/Integer;");
- }
-
-
- private void generateIntegerUnwrap()
- {
- cfw.addInvoke(ByteCode.INVOKEVIRTUAL, "java/lang/Integer",
- "intValue", "()I");
- }
-
-
- private void generateThrowJavaScriptException()
- {
- cfw.add(ByteCode.NEW,
- "org/mozilla/javascript/JavaScriptException");
- cfw.add(ByteCode.DUP_X1);
- cfw.add(ByteCode.SWAP);
- cfw.addPush(scriptOrFn.getSourceName());
- cfw.addPush(itsLineNumber);
- cfw.addInvoke(
- ByteCode.INVOKESPECIAL,
- "org/mozilla/javascript/JavaScriptException",
- "<init>",
- "(Ljava/lang/Object;Ljava/lang/String;I)V");
- cfw.add(ByteCode.ATHROW);
- }
-
- private int getNextGeneratorState(Node node)
- {
- int nodeIndex = ((FunctionNode)scriptOrFn).getResumptionPoints()
- .indexOf(node);
- return nodeIndex + GENERATOR_YIELD_START;
- }
-
- private void generateExpression(Node node, Node parent)
- {
- int type = node.getType();
- Node child = node.getFirstChild();
- switch (type) {
- case Token.USE_STACK:
- break;
-
- case Token.FUNCTION:
- if (fnCurrent != null || parent.getType() != Token.SCRIPT) {
- int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
- OptFunctionNode ofn = OptFunctionNode.get(scriptOrFn,
- fnIndex);
- int t = ofn.fnode.getFunctionType();
- if (t != FunctionNode.FUNCTION_EXPRESSION) {
- throw Codegen.badTree();
- }
- visitFunction(ofn, t);
- }
- break;
-
- case Token.NAME:
- {
- cfw.addALoad(contextLocal);
- cfw.addALoad(variableObjectLocal);
- cfw.addPush(node.getString());
- addScriptRuntimeInvoke(
- "name",
- "(Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"Ljava/lang/String;"
- +")Ljava/lang/Object;");
- }
- break;
-
- case Token.CALL:
- case Token.NEW:
- {
- int specialType = node.getIntProp(Node.SPECIALCALL_PROP,
- Node.NON_SPECIALCALL);
- if (specialType == Node.NON_SPECIALCALL) {
- OptFunctionNode target;
- target = (OptFunctionNode)node.getProp(
- Node.DIRECTCALL_PROP);
-
- if (target != null) {
- visitOptimizedCall(node, target, type, child);
- } else if (type == Token.CALL) {
- visitStandardCall(node, child);
- } else {
- visitStandardNew(node, child);
- }
- } else {
- visitSpecialCall(node, type, specialType, child);
- }
- }
- break;
-
- case Token.REF_CALL:
- generateFunctionAndThisObj(child, node);
- // stack: ... functionObj thisObj
- child = child.getNext();
- generateCallArgArray(node, child, false);
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke(
- "callRef",
- "(Lorg/mozilla/javascript/Callable;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"[Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +")Lorg/mozilla/javascript/Ref;");
- break;
-
- case Token.NUMBER:
- {
- double num = node.getDouble();
- if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
- cfw.addPush(num);
- } else {
- codegen.pushNumberAsObject(cfw, num);
- }
- }
- break;
-
- case Token.STRING:
- cfw.addPush(node.getString());
- break;
-
- case Token.THIS:
- cfw.addALoad(thisObjLocal);
- break;
-
- case Token.THISFN:
- cfw.add(ByteCode.ALOAD_0);
- break;
-
- case Token.NULL:
- cfw.add(ByteCode.ACONST_NULL);
- break;
-
- case Token.TRUE:
- cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean",
- "TRUE", "Ljava/lang/Boolean;");
- break;
-
- case Token.FALSE:
- cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean",
- "FALSE", "Ljava/lang/Boolean;");
- break;
-
- case Token.REGEXP:
- {
- int i = node.getExistingIntProp(Node.REGEXP_PROP);
- // Scripts can not use REGEXP_ARRAY_FIELD_NAME since
- // it it will make script.exec non-reentrant so they
- // store regexp array in a local variable while
- // functions always access precomputed
- // REGEXP_ARRAY_FIELD_NAME not to consume locals
- if (fnCurrent == null) {
- cfw.addALoad(scriptRegexpLocal);
- } else {
- cfw.addALoad(funObjLocal);
- cfw.add(ByteCode.GETFIELD, codegen.mainClassName,
- Codegen.REGEXP_ARRAY_FIELD_NAME,
- Codegen.REGEXP_ARRAY_FIELD_TYPE);
- }
- cfw.addPush(i);
- cfw.add(ByteCode.AALOAD);
- }
- break;
-
- case Token.COMMA: {
- Node next = child.getNext();
- while (next != null) {
- generateExpression(child, node);
- cfw.add(ByteCode.POP);
- child = next;
- next = next.getNext();
- }
- generateExpression(child, node);
- break;
- }
-
- case Token.ENUM_NEXT:
- case Token.ENUM_ID: {
- int local = getLocalBlockRegister(node);
- cfw.addALoad(local);
- if (type == Token.ENUM_NEXT) {
- addScriptRuntimeInvoke(
- "enumNext", "(Ljava/lang/Object;)Ljava/lang/Boolean;");
- } else {
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke("enumId",
- "(Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +")Ljava/lang/Object;");
- }
- break;
- }
-
- case Token.ARRAYLIT:
- visitArrayLiteral(node, child);
- break;
-
- case Token.OBJECTLIT:
- visitObjectLiteral(node, child);
- break;
-
- case Token.NOT: {
- int trueTarget = cfw.acquireLabel();
- int falseTarget = cfw.acquireLabel();
- int beyond = cfw.acquireLabel();
- generateIfJump(child, node, trueTarget, falseTarget);
-
- cfw.markLabel(trueTarget);
- cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean",
- "FALSE", "Ljava/lang/Boolean;");
- cfw.add(ByteCode.GOTO, beyond);
- cfw.markLabel(falseTarget);
- cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean",
- "TRUE", "Ljava/lang/Boolean;");
- cfw.markLabel(beyond);
- cfw.adjustStackTop(-1);
- break;
- }
-
- case Token.BITNOT:
- generateExpression(child, node);
- addScriptRuntimeInvoke("toInt32", "(Ljava/lang/Object;)I");
- cfw.addPush(-1); // implement ~a as (a ^ -1)
- cfw.add(ByteCode.IXOR);
- cfw.add(ByteCode.I2D);
- addDoubleWrap();
- break;
-
- case Token.VOID:
- generateExpression(child, node);
- cfw.add(ByteCode.POP);
- Codegen.pushUndefined(cfw);
- break;
-
- case Token.TYPEOF:
- generateExpression(child, node);
- addScriptRuntimeInvoke("typeof",
- "(Ljava/lang/Object;"
- +")Ljava/lang/String;");
- break;
-
- case Token.TYPEOFNAME:
- visitTypeofname(node);
- break;
-
- case Token.INC:
- case Token.DEC:
- visitIncDec(node);
- break;
-
- case Token.OR:
- case Token.AND: {
- generateExpression(child, node);
- cfw.add(ByteCode.DUP);
- addScriptRuntimeInvoke("toBoolean",
- "(Ljava/lang/Object;)Z");
- int falseTarget = cfw.acquireLabel();
- if (type == Token.AND)
- cfw.add(ByteCode.IFEQ, falseTarget);
- else
- cfw.add(ByteCode.IFNE, falseTarget);
- cfw.add(ByteCode.POP);
- generateExpression(child.getNext(), node);
- cfw.markLabel(falseTarget);
- }
- break;
-
- case Token.HOOK : {
- Node ifThen = child.getNext();
- Node ifElse = ifThen.getNext();
- generateExpression(child, node);
- addScriptRuntimeInvoke("toBoolean",
- "(Ljava/lang/Object;)Z");
- int elseTarget = cfw.acquireLabel();
- cfw.add(ByteCode.IFEQ, elseTarget);
- short stack = cfw.getStackTop();
- generateExpression(ifThen, node);
- int afterHook = cfw.acquireLabel();
- cfw.add(ByteCode.GOTO, afterHook);
- cfw.markLabel(elseTarget, stack);
- generateExpression(ifElse, node);
- cfw.markLabel(afterHook);
- }
- break;
-
- case Token.ADD: {
- generateExpression(child, node);
- generateExpression(child.getNext(), node);
- switch (node.getIntProp(Node.ISNUMBER_PROP, -1)) {
- case Node.BOTH:
- cfw.add(ByteCode.DADD);
- break;
- case Node.LEFT:
- addOptRuntimeInvoke("add",
- "(DLjava/lang/Object;)Ljava/lang/Object;");
- break;
- case Node.RIGHT:
- addOptRuntimeInvoke("add",
- "(Ljava/lang/Object;D)Ljava/lang/Object;");
- break;
- default:
- if (child.getType() == Token.STRING) {
- addScriptRuntimeInvoke("add",
- "(Ljava/lang/String;"
- +"Ljava/lang/Object;"
- +")Ljava/lang/String;");
- } else if (child.getNext().getType() == Token.STRING) {
- addScriptRuntimeInvoke("add",
- "(Ljava/lang/Object;"
- +"Ljava/lang/String;"
- +")Ljava/lang/String;");
- } else {
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke("add",
- "(Ljava/lang/Object;"
- +"Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +")Ljava/lang/Object;");
- }
- }
- }
- break;
-
- case Token.MUL:
- visitArithmetic(node, ByteCode.DMUL, child, parent);
- break;
-
- case Token.SUB:
- visitArithmetic(node, ByteCode.DSUB, child, parent);
- break;
-
- case Token.DIV:
- case Token.MOD:
- visitArithmetic(node, type == Token.DIV
- ? ByteCode.DDIV
- : ByteCode.DREM, child, parent);
- break;
-
- case Token.BITOR:
- case Token.BITXOR:
- case Token.BITAND:
- case Token.LSH:
- case Token.RSH:
- case Token.URSH:
- visitBitOp(node, type, child);
- break;
-
- case Token.POS:
- case Token.NEG:
- generateExpression(child, node);
- addObjectToDouble();
- if (type == Token.NEG) {
- cfw.add(ByteCode.DNEG);
- }
- addDoubleWrap();
- break;
-
- case Token.TO_DOUBLE:
- // cnvt to double (not Double)
- generateExpression(child, node);
- addObjectToDouble();
- break;
-
- case Token.TO_OBJECT: {
- // convert from double
- int prop = -1;
- if (child.getType() == Token.NUMBER) {
- prop = child.getIntProp(Node.ISNUMBER_PROP, -1);
- }
- if (prop != -1) {
- child.removeProp(Node.ISNUMBER_PROP);
- generateExpression(child, node);
- child.putIntProp(Node.ISNUMBER_PROP, prop);
- } else {
- generateExpression(child, node);
- addDoubleWrap();
- }
- break;
- }
-
- case Token.IN:
- case Token.INSTANCEOF:
- case Token.LE:
- case Token.LT:
- case Token.GE:
- case Token.GT: {
- int trueGOTO = cfw.acquireLabel();
- int falseGOTO = cfw.acquireLabel();
- visitIfJumpRelOp(node, child, trueGOTO, falseGOTO);
- addJumpedBooleanWrap(trueGOTO, falseGOTO);
- break;
- }
-
- case Token.EQ:
- case Token.NE:
- case Token.SHEQ:
- case Token.SHNE: {
- int trueGOTO = cfw.acquireLabel();
- int falseGOTO = cfw.acquireLabel();
- visitIfJumpEqOp(node, child, trueGOTO, falseGOTO);
- addJumpedBooleanWrap(trueGOTO, falseGOTO);
- break;
- }
-
- case Token.GETPROP:
- case Token.GETPROPNOWARN:
- visitGetProp(node, child);
- break;
-
- case Token.GETELEM:
- generateExpression(child, node); // object
- generateExpression(child.getNext(), node); // id
- cfw.addALoad(contextLocal);
- if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
- addScriptRuntimeInvoke(
- "getObjectIndex",
- "(Ljava/lang/Object;D"
- +"Lorg/mozilla/javascript/Context;"
- +")Ljava/lang/Object;");
- }
- else {
- addScriptRuntimeInvoke(
- "getObjectElem",
- "(Ljava/lang/Object;"
- +"Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +")Ljava/lang/Object;");
- }
- break;
-
- case Token.GET_REF:
- generateExpression(child, node); // reference
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke(
- "refGet",
- "(Lorg/mozilla/javascript/Ref;"
- +"Lorg/mozilla/javascript/Context;"
- +")Ljava/lang/Object;");
- break;
-
- case Token.GETVAR:
- visitGetVar(node);
- break;
-
- case Token.SETVAR:
- visitSetVar(node, child, true);
- break;
-
- case Token.SETNAME:
- visitSetName(node, child);
- break;
-
- case Token.SETCONST:
- visitSetConst(node, child);
- break;
-
- case Token.SETCONSTVAR:
- visitSetConstVar(node, child, true);
- break;
-
- case Token.SETPROP:
- case Token.SETPROP_OP:
- visitSetProp(type, node, child);
- break;
-
- case Token.SETELEM:
- case Token.SETELEM_OP:
- visitSetElem(type, node, child);
- break;
-
- case Token.SET_REF:
- case Token.SET_REF_OP:
- {
- generateExpression(child, node);
- child = child.getNext();
- if (type == Token.SET_REF_OP) {
- cfw.add(ByteCode.DUP);
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke(
- "refGet",
- "(Lorg/mozilla/javascript/Ref;"
- +"Lorg/mozilla/javascript/Context;"
- +")Ljava/lang/Object;");
- }
- generateExpression(child, node);
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke(
- "refSet",
- "(Lorg/mozilla/javascript/Ref;"
- +"Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +")Ljava/lang/Object;");
- }
- break;
-
- case Token.DEL_REF:
- generateExpression(child, node);
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke("refDel",
- "(Lorg/mozilla/javascript/Ref;"
- +"Lorg/mozilla/javascript/Context;"
- +")Ljava/lang/Object;");
- break;
-
- case Token.DELPROP:
- generateExpression(child, node);
- child = child.getNext();
- generateExpression(child, node);
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke("delete",
- "(Ljava/lang/Object;"
- +"Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +")Ljava/lang/Object;");
- break;
-
- case Token.BINDNAME:
- {
- while (child != null) {
- generateExpression(child, node);
- child = child.getNext();
- }
- // Generate code for "ScriptRuntime.bind(varObj, "s")"
- cfw.addALoad(contextLocal);
- cfw.addALoad(variableObjectLocal);
- cfw.addPush(node.getString());
- addScriptRuntimeInvoke(
- "bind",
- "(Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"Ljava/lang/String;"
- +")Lorg/mozilla/javascript/Scriptable;");
- }
- break;
-
- case Token.LOCAL_LOAD:
- cfw.addALoad(getLocalBlockRegister(node));
- break;
-
- case Token.REF_SPECIAL:
- {
- String special = (String)node.getProp(Node.NAME_PROP);
- generateExpression(child, node);
- cfw.addPush(special);
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke(
- "specialRef",
- "(Ljava/lang/Object;"
- +"Ljava/lang/String;"
- +"Lorg/mozilla/javascript/Context;"
- +")Lorg/mozilla/javascript/Ref;");
- }
- break;
-
- case Token.REF_MEMBER:
- case Token.REF_NS_MEMBER:
- case Token.REF_NAME:
- case Token.REF_NS_NAME:
- {
- int memberTypeFlags
- = node.getIntProp(Node.MEMBER_TYPE_PROP, 0);
- // generate possible target, possible namespace and member
- do {
- generateExpression(child, node);
- child = child.getNext();
- } while (child != null);
- cfw.addALoad(contextLocal);
- String methodName, signature;
- switch (type) {
- case Token.REF_MEMBER:
- methodName = "memberRef";
- signature = "(Ljava/lang/Object;"
- +"Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +"I"
- +")Lorg/mozilla/javascript/Ref;";
- break;
- case Token.REF_NS_MEMBER:
- methodName = "memberRef";
- signature = "(Ljava/lang/Object;"
- +"Ljava/lang/Object;"
- +"Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +"I"
- +")Lorg/mozilla/javascript/Ref;";
- break;
- case Token.REF_NAME:
- methodName = "nameRef";
- signature = "(Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"I"
- +")Lorg/mozilla/javascript/Ref;";
- cfw.addALoad(variableObjectLocal);
- break;
- case Token.REF_NS_NAME:
- methodName = "nameRef";
- signature = "(Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"I"
- +")Lorg/mozilla/javascript/Ref;";
- cfw.addALoad(variableObjectLocal);
- break;
- default:
- throw Kit.codeBug();
- }
- cfw.addPush(memberTypeFlags);
- addScriptRuntimeInvoke(methodName, signature);
- }
- break;
-
- case Token.DOTQUERY:
- visitDotQuery(node, child);
- break;
-
- case Token.ESCXMLATTR:
- generateExpression(child, node);
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke("escapeAttributeValue",
- "(Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +")Ljava/lang/String;");
- break;
-
- case Token.ESCXMLTEXT:
- generateExpression(child, node);
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke("escapeTextValue",
- "(Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +")Ljava/lang/String;");
- break;
-
- case Token.DEFAULTNAMESPACE:
- generateExpression(child, node);
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke("setDefaultNamespace",
- "(Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +")Ljava/lang/Object;");
- break;
-
- case Token.YIELD:
- generateYieldPoint(node, true);
- break;
-
- case Token.WITHEXPR: {
- Node enterWith = child;
- Node with = enterWith.getNext();
- Node leaveWith = with.getNext();
- generateStatement(enterWith);
- generateExpression(with.getFirstChild(), with);
- generateStatement(leaveWith);
- break;
- }
-
- case Token.ARRAYCOMP: {
- Node initStmt = child;
- Node expr = child.getNext();
- generateStatement(initStmt);
- generateExpression(expr, node);
- break;
- }
-
- default:
- throw new RuntimeException("Unexpected node type "+type);
- }
-
- }
-
- private void generateYieldPoint(Node node, boolean exprContext) {
- // save stack state
- int top = cfw.getStackTop();
- maxStack = maxStack > top ? maxStack : top;
- if (cfw.getStackTop() != 0) {
- generateGetGeneratorStackState();
- for (int i = 0; i < top; i++) {
- cfw.add(ByteCode.DUP_X1);
- cfw.add(ByteCode.SWAP);
- cfw.addLoadConstant(i);
- cfw.add(ByteCode.SWAP);
- cfw.add(ByteCode.AASTORE);
- }
- // pop the array object
- cfw.add(ByteCode.POP);
- }
-
- // generate the yield argument
- Node child = node.getFirstChild();
- if (child != null)
- generateExpression(child, node);
- else
- Codegen.pushUndefined(cfw);
-
- // change the resumption state
- int nextState = getNextGeneratorState(node);
- generateSetGeneratorResumptionPoint(nextState);
-
- boolean hasLocals = generateSaveLocals(node);
-
- cfw.add(ByteCode.ARETURN);
-
- generateCheckForThrowOrClose(getTargetLabel(node),
- hasLocals, nextState);
-
- // reconstruct the stack
- if (top != 0) {
- generateGetGeneratorStackState();
- for (int i = 0; i < top; i++) {
- cfw.add(ByteCode.DUP);
- cfw.addLoadConstant(top - i - 1);
- cfw.add(ByteCode.AALOAD);
- cfw.add(ByteCode.SWAP);
- }
- cfw.add(ByteCode.POP);
- }
-
- // load return value from yield
- if (exprContext) {
- cfw.addALoad(argsLocal);
- }
- }
-
- private void generateCheckForThrowOrClose(int label,
- boolean hasLocals,
- int nextState) {
- int throwLabel = cfw.acquireLabel();
- int closeLabel = cfw.acquireLabel();
-
- // throw the user provided object, if the operation is .throw()
- cfw.markLabel(throwLabel);
- cfw.addALoad(argsLocal);
- generateThrowJavaScriptException();
-
- // throw our special internal exception if the generator is being closed
- cfw.markLabel(closeLabel);
- cfw.addALoad(argsLocal);
- cfw.add(ByteCode.CHECKCAST, "java/lang/Throwable");
- cfw.add(ByteCode.ATHROW);
-
- // mark the re-entry point
- // jump here after initializing the locals
- if (label != -1)
- cfw.markLabel(label);
- if (!hasLocals) {
- // jump here directly if there are no locals
- cfw.markTableSwitchCase(generatorSwitch, nextState);
- }
-
- // see if we need to dispatch for .close() or .throw()
- cfw.addILoad(operationLocal);
- cfw.addLoadConstant(NativeGenerator.GENERATOR_CLOSE);
- cfw.add(ByteCode.IF_ICMPEQ, closeLabel);
- cfw.addILoad(operationLocal);
- cfw.addLoadConstant(NativeGenerator.GENERATOR_THROW);
- cfw.add(ByteCode.IF_ICMPEQ, throwLabel);
- }
-
- private void generateIfJump(Node node, Node parent,
- int trueLabel, int falseLabel)
- {
- // System.out.println("gen code for " + node.toString());
-
- int type = node.getType();
- Node child = node.getFirstChild();
-
- switch (type) {
- case Token.NOT:
- generateIfJump(child, node, falseLabel, trueLabel);
- break;
-
- case Token.OR:
- case Token.AND: {
- int interLabel = cfw.acquireLabel();
- if (type == Token.AND) {
- generateIfJump(child, node, interLabel, falseLabel);
- }
- else {
- generateIfJump(child, node, trueLabel, interLabel);
- }
- cfw.markLabel(interLabel);
- child = child.getNext();
- generateIfJump(child, node, trueLabel, falseLabel);
- break;
- }
-
- case Token.IN:
- case Token.INSTANCEOF:
- case Token.LE:
- case Token.LT:
- case Token.GE:
- case Token.GT:
- visitIfJumpRelOp(node, child, trueLabel, falseLabel);
- break;
-
- case Token.EQ:
- case Token.NE:
- case Token.SHEQ:
- case Token.SHNE:
- visitIfJumpEqOp(node, child, trueLabel, falseLabel);
- break;
-
- default:
- // Generate generic code for non-optimized jump
- generateExpression(node, parent);
- addScriptRuntimeInvoke("toBoolean", "(Ljava/lang/Object;)Z");
- cfw.add(ByteCode.IFNE, trueLabel);
- cfw.add(ByteCode.GOTO, falseLabel);
- }
- }
-
- private void visitFunction(OptFunctionNode ofn, int functionType)
- {
- int fnIndex = codegen.getIndex(ofn.fnode);
- cfw.add(ByteCode.NEW, codegen.mainClassName);
- // Call function constructor
- cfw.add(ByteCode.DUP);
- cfw.addALoad(variableObjectLocal);
- cfw.addALoad(contextLocal); // load 'cx'
- cfw.addPush(fnIndex);
- cfw.addInvoke(ByteCode.INVOKESPECIAL, codegen.mainClassName,
- "<init>", Codegen.FUNCTION_CONSTRUCTOR_SIGNATURE);
-
- // Init mainScript field;
- cfw.add(ByteCode.DUP);
- if (isTopLevel) {
- cfw.add(ByteCode.ALOAD_0);
- } else {
- cfw.add(ByteCode.ALOAD_0);
- cfw.add(ByteCode.GETFIELD,
- codegen.mainClassName,
- Codegen.DIRECT_CALL_PARENT_FIELD,
- codegen.mainClassSignature);
- }
- cfw.add(ByteCode.PUTFIELD,
- codegen.mainClassName,
- Codegen.DIRECT_CALL_PARENT_FIELD,
- codegen.mainClassSignature);
-
- int directTargetIndex = ofn.getDirectTargetIndex();
- if (directTargetIndex >= 0) {
- cfw.add(ByteCode.DUP);
- if (isTopLevel) {
- cfw.add(ByteCode.ALOAD_0);
- } else {
- cfw.add(ByteCode.ALOAD_0);
- cfw.add(ByteCode.GETFIELD,
- codegen.mainClassName,
- Codegen.DIRECT_CALL_PARENT_FIELD,
- codegen.mainClassSignature);
- }
- cfw.add(ByteCode.SWAP);
- cfw.add(ByteCode.PUTFIELD,
- codegen.mainClassName,
- Codegen.getDirectTargetFieldName(directTargetIndex),
- codegen.mainClassSignature);
- }
-
- if (functionType == FunctionNode.FUNCTION_EXPRESSION) {
- // Leave closure object on stack and do not pass it to
- // initFunction which suppose to connect statements to scope
- return;
- }
- cfw.addPush(functionType);
- cfw.addALoad(variableObjectLocal);
- cfw.addALoad(contextLocal); // load 'cx'
- addOptRuntimeInvoke("initFunction",
- "(Lorg/mozilla/javascript/NativeFunction;"
- +"I"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"Lorg/mozilla/javascript/Context;"
- +")V");
- }
-
- private int getTargetLabel(Node target)
- {
- int labelId = target.labelId();
- if (labelId == -1) {
- labelId = cfw.acquireLabel();
- target.labelId(labelId);
- }
- return labelId;
- }
-
- private void visitGoto(Node.Jump node, int type, Node child)
- {
- Node target = node.target;
- if (type == Token.IFEQ || type == Token.IFNE) {
- if (child == null) throw Codegen.badTree();
- int targetLabel = getTargetLabel(target);
- int fallThruLabel = cfw.acquireLabel();
- if (type == Token.IFEQ)
- generateIfJump(child, node, targetLabel, fallThruLabel);
- else
- generateIfJump(child, node, fallThruLabel, targetLabel);
- cfw.markLabel(fallThruLabel);
- } else {
- if (type == Token.JSR) {
- if (isGenerator) {
- addGotoWithReturn(target);
- } else {
- addGoto(target, ByteCode.JSR);
- }
- } else {
- addGoto(target, ByteCode.GOTO);
- }
- }
- }
-
- private void addGotoWithReturn(Node target) {
- FinallyReturnPoint ret =
- (FinallyReturnPoint)finallys.get(target);
- cfw.addLoadConstant(ret.jsrPoints.size());
- addGoto(target, ByteCode.GOTO);
- int retLabel = cfw.acquireLabel();
- cfw.markLabel(retLabel);
- ret.jsrPoints.add(Integer.valueOf(retLabel));
- }
-
- private void visitArrayLiteral(Node node, Node child)
- {
- int count = 0;
- for (Node cursor = child; cursor != null; cursor = cursor.getNext()) {
- ++count;
- }
- // load array to store array literal objects
- addNewObjectArray(count);
- for (int i = 0; i != count; ++i) {
- cfw.add(ByteCode.DUP);
- cfw.addPush(i);
- generateExpression(child, node);
- cfw.add(ByteCode.AASTORE);
- child = child.getNext();
- }
- int[] skipIndexes = (int[])node.getProp(Node.SKIP_INDEXES_PROP);
- if (skipIndexes == null) {
- cfw.add(ByteCode.ACONST_NULL);
- cfw.add(ByteCode.ICONST_0);
- } else {
- cfw.addPush(OptRuntime.encodeIntArray(skipIndexes));
- cfw.addPush(skipIndexes.length);
- }
- cfw.addALoad(contextLocal);
- cfw.addALoad(variableObjectLocal);
- addOptRuntimeInvoke("newArrayLiteral",
- "([Ljava/lang/Object;"
- +"Ljava/lang/String;"
- +"I"
- +"Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +")Lorg/mozilla/javascript/Scriptable;");
- }
-
- private void visitObjectLiteral(Node node, Node child)
- {
- Object[] properties = (Object[])node.getProp(Node.OBJECT_IDS_PROP);
- int count = properties.length;
-
- // load array with property ids
- addNewObjectArray(count);
- for (int i = 0; i != count; ++i) {
- cfw.add(ByteCode.DUP);
- cfw.addPush(i);
- Object id = properties[i];
- if (id instanceof String) {
- cfw.addPush((String)id);
- } else {
- cfw.addPush(((Integer)id).intValue());
- addScriptRuntimeInvoke("wrapInt", "(I)Ljava/lang/Integer;");
- }
- cfw.add(ByteCode.AASTORE);
- }
- // load array with property values
- addNewObjectArray(count);
- Node child2 = child;
- for (int i = 0; i != count; ++i) {
- cfw.add(ByteCode.DUP);
- cfw.addPush(i);
- int childType = child.getType();
- if (childType == Token.GET) {
- generateExpression(child.getFirstChild(), node);
- } else if (childType == Token.SET) {
- generateExpression(child.getFirstChild(), node);
- } else {
- generateExpression(child, node);
- }
- cfw.add(ByteCode.AASTORE);
- child = child.getNext();
- }
- // load array with getterSetter values
- cfw.addPush(count);
- cfw.add(ByteCode.NEWARRAY, ByteCode.T_INT);
- for (int i = 0; i != count; ++i) {
- cfw.add(ByteCode.DUP);
- cfw.addPush(i);
- int childType = child2.getType();
- if (childType == Token.GET) {
- cfw.add(ByteCode.ICONST_M1);
- } else if (childType == Token.SET) {
- cfw.add(ByteCode.ICONST_1);
- } else {
- cfw.add(ByteCode.ICONST_0);
- }
- cfw.add(ByteCode.IASTORE);
- child2 = child2.getNext();
- }
-
- cfw.addALoad(contextLocal);
- cfw.addALoad(variableObjectLocal);
- addScriptRuntimeInvoke("newObjectLiteral",
- "([Ljava/lang/Object;"
- +"[Ljava/lang/Object;"
- +"[I"
- +"Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +")Lorg/mozilla/javascript/Scriptable;");
- }
-
- private void visitSpecialCall(Node node, int type, int specialType,
- Node child)
- {
- cfw.addALoad(contextLocal);
-
- if (type == Token.NEW) {
- generateExpression(child, node);
- // stack: ... cx functionObj
- } else {
- generateFunctionAndThisObj(child, node);
- // stack: ... cx functionObj thisObj
- }
- child = child.getNext();
-
- generateCallArgArray(node, child, false);
-
- String methodName;
- String callSignature;
-
- if (type == Token.NEW) {
- methodName = "newObjectSpecial";
- callSignature = "(Lorg/mozilla/javascript/Context;"
- +"Ljava/lang/Object;"
- +"[Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"I" // call type
- +")Ljava/lang/Object;";
- cfw.addALoad(variableObjectLocal);
- cfw.addALoad(thisObjLocal);
- cfw.addPush(specialType);
- } else {
- methodName = "callSpecial";
- callSignature = "(Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Callable;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"[Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"I" // call type
- +"Ljava/lang/String;I" // filename, linenumber
- +")Ljava/lang/Object;";
- cfw.addALoad(variableObjectLocal);
- cfw.addALoad(thisObjLocal);
- cfw.addPush(specialType);
- String sourceName = scriptOrFn.getSourceName();
- cfw.addPush(sourceName == null ? "" : sourceName);
- cfw.addPush(itsLineNumber);
- }
-
- addOptRuntimeInvoke(methodName, callSignature);
- }
-
- private void visitStandardCall(Node node, Node child)
- {
- if (node.getType() != Token.CALL) throw Codegen.badTree();
-
- Node firstArgChild = child.getNext();
- int childType = child.getType();
-
- String methodName;
- String signature;
-
- if (firstArgChild == null) {
- if (childType == Token.NAME) {
- // name() call
- String name = child.getString();
- cfw.addPush(name);
- methodName = "callName0";
- signature = "(Ljava/lang/String;"
- +"Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +")Ljava/lang/Object;";
- } else if (childType == Token.GETPROP) {
- // x.name() call
- Node propTarget = child.getFirstChild();
- generateExpression(propTarget, node);
- Node id = propTarget.getNext();
- String property = id.getString();
- cfw.addPush(property);
- methodName = "callProp0";
- signature = "(Ljava/lang/Object;"
- +"Ljava/lang/String;"
- +"Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +")Ljava/lang/Object;";
- } else if (childType == Token.GETPROPNOWARN) {
- throw Kit.codeBug();
- } else {
- generateFunctionAndThisObj(child, node);
- methodName = "call0";
- signature = "(Lorg/mozilla/javascript/Callable;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +")Ljava/lang/Object;";
- }
-
- } else if (childType == Token.NAME) {
- // XXX: this optimization is only possible if name
- // resolution
- // is not affected by arguments evaluation and currently
- // there are no checks for it
- String name = child.getString();
- generateCallArgArray(node, firstArgChild, false);
- cfw.addPush(name);
- methodName = "callName";
- signature = "([Ljava/lang/Object;"
- +"Ljava/lang/String;"
- +"Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +")Ljava/lang/Object;";
- } else {
- int argCount = 0;
- for (Node arg = firstArgChild; arg != null; arg = arg.getNext()) {
- ++argCount;
- }
- generateFunctionAndThisObj(child, node);
- // stack: ... functionObj thisObj
- if (argCount == 1) {
- generateExpression(firstArgChild, node);
- methodName = "call1";
- signature = "(Lorg/mozilla/javascript/Callable;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +")Ljava/lang/Object;";
- } else if (argCount == 2) {
- generateExpression(firstArgChild, node);
- generateExpression(firstArgChild.getNext(), node);
- methodName = "call2";
- signature = "(Lorg/mozilla/javascript/Callable;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"Ljava/lang/Object;"
- +"Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +")Ljava/lang/Object;";
- } else {
- generateCallArgArray(node, firstArgChild, false);
- methodName = "callN";
- signature = "(Lorg/mozilla/javascript/Callable;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"[Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +")Ljava/lang/Object;";
- }
- }
-
- cfw.addALoad(contextLocal);
- cfw.addALoad(variableObjectLocal);
- addOptRuntimeInvoke(methodName, signature);
- }
-
- private void visitStandardNew(Node node, Node child)
- {
- if (node.getType() != Token.NEW) throw Codegen.badTree();
-
- Node firstArgChild = child.getNext();
-
- generateExpression(child, node);
- // stack: ... functionObj
- cfw.addALoad(contextLocal);
- cfw.addALoad(variableObjectLocal);
- // stack: ... functionObj cx scope
- generateCallArgArray(node, firstArgChild, false);
- addScriptRuntimeInvoke(
- "newObject",
- "(Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"[Ljava/lang/Object;"
- +")Lorg/mozilla/javascript/Scriptable;");
- }
-
- private void visitOptimizedCall(Node node, OptFunctionNode target,
- int type, Node child)
- {
- Node firstArgChild = child.getNext();
-
- short thisObjLocal = 0;
- if (type == Token.NEW) {
- generateExpression(child, node);
- } else {
- generateFunctionAndThisObj(child, node);
- thisObjLocal = getNewWordLocal();
- cfw.addAStore(thisObjLocal);
- }
- // stack: ... functionObj
-
- int beyond = cfw.acquireLabel();
-
- int directTargetIndex = target.getDirectTargetIndex();
- if (isTopLevel) {
- cfw.add(ByteCode.ALOAD_0);
- } else {
- cfw.add(ByteCode.ALOAD_0);
- cfw.add(ByteCode.GETFIELD, codegen.mainClassName,
- Codegen.DIRECT_CALL_PARENT_FIELD,
- codegen.mainClassSignature);
- }
- cfw.add(ByteCode.GETFIELD, codegen.mainClassName,
- Codegen.getDirectTargetFieldName(directTargetIndex),
- codegen.mainClassSignature);
-
- cfw.add(ByteCode.DUP2);
- // stack: ... functionObj directFunct functionObj directFunct
-
- int regularCall = cfw.acquireLabel();
- cfw.add(ByteCode.IF_ACMPNE, regularCall);
-
- // stack: ... functionObj directFunct
- short stackHeight = cfw.getStackTop();
- cfw.add(ByteCode.SWAP);
- cfw.add(ByteCode.POP);
- // stack: ... directFunct
- if (compilerEnv.isUseDynamicScope()) {
- cfw.addALoad(contextLocal);
- cfw.addALoad(variableObjectLocal);
- } else {
- cfw.add(ByteCode.DUP);
- // stack: ... directFunct directFunct
- cfw.addInvoke(ByteCode.INVOKEINTERFACE,
- "org/mozilla/javascript/Scriptable",
- "getParentScope",
- "()Lorg/mozilla/javascript/Scriptable;");
- // stack: ... directFunct scope
- cfw.addALoad(contextLocal);
- // stack: ... directFunct scope cx
- cfw.add(ByteCode.SWAP);
- }
- // stack: ... directFunc cx scope
-
- if (type == Token.NEW) {
- cfw.add(ByteCode.ACONST_NULL);
- } else {
- cfw.addALoad(thisObjLocal);
- }
- // stack: ... directFunc cx scope thisObj
-/*
-Remember that directCall parameters are paired in 1 aReg and 1 dReg
-If the argument is an incoming arg, just pass the orginal pair thru.
-Else, if the argument is known to be typed 'Number', pass Void.TYPE
-in the aReg and the number is the dReg
-Else pass the JS object in the aReg and 0.0 in the dReg.
-*/
- Node argChild = firstArgChild;
- while (argChild != null) {
- int dcp_register = nodeIsDirectCallParameter(argChild);
- if (dcp_register >= 0) {
- cfw.addALoad(dcp_register);
- cfw.addDLoad(dcp_register + 1);
- } else if (argChild.getIntProp(Node.ISNUMBER_PROP, -1)
- == Node.BOTH)
- {
- cfw.add(ByteCode.GETSTATIC,
- "java/lang/Void",
- "TYPE",
- "Ljava/lang/Class;");
- generateExpression(argChild, node);
- } else {
- generateExpression(argChild, node);
- cfw.addPush(0.0);
- }
- argChild = argChild.getNext();
- }
-
- cfw.add(ByteCode.GETSTATIC,
- "org/mozilla/javascript/ScriptRuntime",
- "emptyArgs", "[Ljava/lang/Object;");
- cfw.addInvoke(ByteCode.INVOKESTATIC,
- codegen.mainClassName,
- (type == Token.NEW)
- ? codegen.getDirectCtorName(target.fnode)
- : codegen.getBodyMethodName(target.fnode),
- codegen.getBodyMethodSignature(target.fnode));
-
- cfw.add(ByteCode.GOTO, beyond);
-
- cfw.markLabel(regularCall, stackHeight);
- // stack: ... functionObj directFunct
- cfw.add(ByteCode.POP);
- cfw.addALoad(contextLocal);
- cfw.addALoad(variableObjectLocal);
- // stack: ... functionObj cx scope
- if (type != Token.NEW) {
- cfw.addALoad(thisObjLocal);
- releaseWordLocal(thisObjLocal);
- // stack: ... functionObj cx scope thisObj
- }
- // XXX: this will generate code for the child array the second time,
- // so expression code generation better not to alter tree structure...
- generateCallArgArray(node, firstArgChild, true);
-
- if (type == Token.NEW) {
- addScriptRuntimeInvoke(
- "newObject",
- "(Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"[Ljava/lang/Object;"
- +")Lorg/mozilla/javascript/Scriptable;");
- } else {
- cfw.addInvoke(ByteCode.INVOKEINTERFACE,
- "org/mozilla/javascript/Callable",
- "call",
- "(Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"[Ljava/lang/Object;"
- +")Ljava/lang/Object;");
- }
-
- cfw.markLabel(beyond);
- }
-
- private void generateCallArgArray(Node node, Node argChild, boolean directCall)
- {
- int argCount = 0;
- for (Node child = argChild; child != null; child = child.getNext()) {
- ++argCount;
- }
- // load array object to set arguments
- if (argCount == 1 && itsOneArgArray >= 0) {
- cfw.addALoad(itsOneArgArray);
- } else {
- addNewObjectArray(argCount);
- }
- // Copy arguments into it
- for (int i = 0; i != argCount; ++i) {
- // If we are compiling a generator an argument could be the result
- // of a yield. In that case we will have an immediate on the stack
- // which we need to avoid
- if (!isGenerator) {
- cfw.add(ByteCode.DUP);
- cfw.addPush(i);
- }
-
- if (!directCall) {
- generateExpression(argChild, node);
- } else {
- // If this has also been a directCall sequence, the Number
- // flag will have remained set for any parameter so that
- // the values could be copied directly into the outgoing
- // args. Here we want to force it to be treated as not in
- // a Number context, so we set the flag off.
- int dcp_register = nodeIsDirectCallParameter(argChild);
- if (dcp_register >= 0) {
- dcpLoadAsObject(dcp_register);
- } else {
- generateExpression(argChild, node);
- int childNumberFlag
- = argChild.getIntProp(Node.ISNUMBER_PROP, -1);
- if (childNumberFlag == Node.BOTH) {
- addDoubleWrap();
- }
- }
- }
-
- // When compiling generators, any argument to a method may be a
- // yield expression. Hence we compile the argument first and then
- // load the argument index and assign the value to the args array.
- if (isGenerator) {
- short tempLocal = getNewWordLocal();
- cfw.addAStore(tempLocal);
- cfw.add(ByteCode.CHECKCAST, "[Ljava/lang/Object;");
- cfw.add(ByteCode.DUP);
- cfw.addPush(i);
- cfw.addALoad(tempLocal);
- releaseWordLocal(tempLocal);
- }
-
- cfw.add(ByteCode.AASTORE);
-
- argChild = argChild.getNext();
- }
- }
-
- private void generateFunctionAndThisObj(Node node, Node parent)
- {
- // Place on stack (function object, function this) pair
- int type = node.getType();
- switch (node.getType()) {
- case Token.GETPROPNOWARN:
- throw Kit.codeBug();
-
- case Token.GETPROP:
- case Token.GETELEM: {
- Node target = node.getFirstChild();
- generateExpression(target, node);
- Node id = target.getNext();
- if (type == Token.GETPROP) {
- String property = id.getString();
- cfw.addPush(property);
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke(
- "getPropFunctionAndThis",
- "(Ljava/lang/Object;"
- +"Ljava/lang/String;"
- +"Lorg/mozilla/javascript/Context;"
- +")Lorg/mozilla/javascript/Callable;");
- } else {
- // Optimizer do not optimize this case for now
- if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1)
- throw Codegen.badTree();
- generateExpression(id, node); // id
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke(
- "getElemFunctionAndThis",
- "(Ljava/lang/Object;"
- +"Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +")Lorg/mozilla/javascript/Callable;");
- }
- break;
- }
-
- case Token.NAME: {
- String name = node.getString();
- cfw.addPush(name);
- cfw.addALoad(contextLocal);
- cfw.addALoad(variableObjectLocal);
- addScriptRuntimeInvoke(
- "getNameFunctionAndThis",
- "(Ljava/lang/String;"
- +"Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +")Lorg/mozilla/javascript/Callable;");
- break;
- }
-
- default: // including GETVAR
- generateExpression(node, parent);
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke(
- "getValueFunctionAndThis",
- "(Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +")Lorg/mozilla/javascript/Callable;");
- break;
- }
- // Get thisObj prepared by get(Name|Prop|Elem|Value)FunctionAndThis
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke(
- "lastStoredScriptable",
- "(Lorg/mozilla/javascript/Context;"
- +")Lorg/mozilla/javascript/Scriptable;");
- }
-
- private void updateLineNumber(Node node)
- {
- itsLineNumber = node.getLineno();
- if (itsLineNumber == -1)
- return;
- cfw.addLineNumberEntry((short)itsLineNumber);
- }
-
- private void visitTryCatchFinally(Node.Jump node, Node child)
- {
- /* Save the variable object, in case there are with statements
- * enclosed by the try block and we catch some exception.
- * We'll restore it for the catch block so that catch block
- * statements get the right scope.
- */
-
- // OPT we only need to do this if there are enclosed WITH
- // statements; could statically check and omit this if there aren't any.
-
- // XXX OPT Maybe instead do syntactic transforms to associate
- // each 'with' with a try/finally block that does the exitwith.
-
- short savedVariableObject = getNewWordLocal();
- cfw.addALoad(variableObjectLocal);
- cfw.addAStore(savedVariableObject);
-
- /*
- * Generate the code for the tree; most of the work is done in IRFactory
- * and NodeTransformer; Codegen just adds the java handlers for the
- * javascript catch and finally clauses. */
-
- int startLabel = cfw.acquireLabel();
- cfw.markLabel(startLabel, (short)0);
-
- Node catchTarget = node.target;
- Node finallyTarget = node.getFinally();
-
- // create a table for the equivalent of JSR returns
- if (isGenerator && finallyTarget != null) {
- FinallyReturnPoint ret = new FinallyReturnPoint();
- if (finallys == null) {
- finallys = new Hashtable();
- }
- // add the finally target to hashtable
- finallys.put(finallyTarget, ret);
- // add the finally node as well to the hash table
- finallys.put(finallyTarget.getNext(), ret);
- }
-
- while (child != null) {
- generateStatement(child);
- child = child.getNext();
- }
-
- // control flow skips the handlers
- int realEnd = cfw.acquireLabel();
- cfw.add(ByteCode.GOTO, realEnd);
-
- int exceptionLocal = getLocalBlockRegister(node);
- // javascript handler; unwrap exception and GOTO to javascript
- // catch area.
- if (catchTarget != null) {
- // get the label to goto
- int catchLabel = catchTarget.labelId();
-
- generateCatchBlock(JAVASCRIPT_EXCEPTION, savedVariableObject,
- catchLabel, startLabel, exceptionLocal);
- /*
- * catch WrappedExceptions, see if they are wrapped
- * JavaScriptExceptions. Otherwise, rethrow.
- */
- generateCatchBlock(EVALUATOR_EXCEPTION, savedVariableObject,
- catchLabel, startLabel, exceptionLocal);
-
- /*
- we also need to catch EcmaErrors and feed the
- associated error object to the handler
- */
- generateCatchBlock(ECMAERROR_EXCEPTION, savedVariableObject,
- catchLabel, startLabel, exceptionLocal);
-
- Context cx = Context.getCurrentContext();
- if (cx != null &&
- cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS))
- {
- generateCatchBlock(THROWABLE_EXCEPTION, savedVariableObject,
- catchLabel, startLabel, exceptionLocal);
- }
- }
-
- // finally handler; catch all exceptions, store to a local; JSR to
- // the finally, then re-throw.
- if (finallyTarget != null) {
- int finallyHandler = cfw.acquireLabel();
- cfw.markHandler(finallyHandler);
- cfw.addAStore(exceptionLocal);
-
- // reset the variable object local
- cfw.addALoad(savedVariableObject);
- cfw.addAStore(variableObjectLocal);
-
- // get the label to JSR to
- int finallyLabel = finallyTarget.labelId();
- if (isGenerator)
- addGotoWithReturn(finallyTarget);
- else
- cfw.add(ByteCode.JSR, finallyLabel);
-
- // rethrow
- cfw.addALoad(exceptionLocal);
- if (isGenerator)
- cfw.add(ByteCode.CHECKCAST, "java/lang/Throwable");
- cfw.add(ByteCode.ATHROW);
-
- // mark the handler
- cfw.addExceptionHandler(startLabel, finallyLabel,
- finallyHandler, null); // catch any
- }
- releaseWordLocal(savedVariableObject);
- cfw.markLabel(realEnd);
- }
-
- private static final int JAVASCRIPT_EXCEPTION = 0;
- private static final int EVALUATOR_EXCEPTION = 1;
- private static final int ECMAERROR_EXCEPTION = 2;
- private static final int THROWABLE_EXCEPTION = 3;
-
- private void generateCatchBlock(int exceptionType,
- short savedVariableObject,
- int catchLabel, int startLabel,
- int exceptionLocal)
- {
- int handler = cfw.acquireLabel();
- cfw.markHandler(handler);
-
- // MS JVM gets cranky if the exception object is left on the stack
- cfw.addAStore(exceptionLocal);
-
- // reset the variable object local
- cfw.addALoad(savedVariableObject);
- cfw.addAStore(variableObjectLocal);
-
- String exceptionName;
- if (exceptionType == JAVASCRIPT_EXCEPTION) {
- exceptionName = "org/mozilla/javascript/JavaScriptException";
- } else if (exceptionType == EVALUATOR_EXCEPTION) {
- exceptionName = "org/mozilla/javascript/EvaluatorException";
- } else if (exceptionType == ECMAERROR_EXCEPTION) {
- exceptionName = "org/mozilla/javascript/EcmaError";
- } else if (exceptionType == THROWABLE_EXCEPTION) {
- exceptionName = "java/lang/Throwable";
- } else {
- throw Kit.codeBug();
- }
-
- // mark the handler
- cfw.addExceptionHandler(startLabel, catchLabel, handler,
- exceptionName);
-
- cfw.add(ByteCode.GOTO, catchLabel);
- }
-
-
- private boolean generateSaveLocals(Node node)
- {
- int count = 0;
- for (int i = 0; i < firstFreeLocal; i++) {
- if (locals[i] != 0)
- count++;
- }
-
- if (count == 0) {
- ((FunctionNode)scriptOrFn).addLiveLocals(node, null);
- return false;
- }
-
- // calculate the max locals
- maxLocals = maxLocals > count ? maxLocals : count;
-
- // create a locals list
- int[] ls = new int[count];
- int s = 0;
- for (int i = 0; i < firstFreeLocal; i++) {
- if (locals[i] != 0) {
- ls[s] = i;
- s++;
- }
- }
-
- // save the locals
- ((FunctionNode)scriptOrFn).addLiveLocals(node, ls);
-
- // save locals
- generateGetGeneratorLocalsState();
- for (int i = 0; i < count; i++) {
- cfw.add(ByteCode.DUP);
- cfw.addLoadConstant(i);
- cfw.addALoad(ls[i]);
- cfw.add(ByteCode.AASTORE);
- }
- // pop the array off the stack
- cfw.add(ByteCode.POP);
-
- return true;
- }
-
- private void visitSwitch(Node.Jump switchNode, Node child)
- {
- // See comments in IRFactory.createSwitch() for description
- // of SWITCH node
-
- generateExpression(child, switchNode);
- // save selector value
- short selector = getNewWordLocal();
- cfw.addAStore(selector);
-
- for (Node.Jump caseNode = (Node.Jump)child.getNext();
- caseNode != null;
- caseNode = (Node.Jump)caseNode.getNext())
- {
- if (caseNode.getType() != Token.CASE)
- throw Codegen.badTree();
- Node test = caseNode.getFirstChild();
- generateExpression(test, caseNode);
- cfw.addALoad(selector);
- addScriptRuntimeInvoke("shallowEq",
- "(Ljava/lang/Object;"
- +"Ljava/lang/Object;"
- +")Z");
- addGoto(caseNode.target, ByteCode.IFNE);
- }
- releaseWordLocal(selector);
- }
-
- private void visitTypeofname(Node node)
- {
- if (hasVarsInRegs) {
- int varIndex = fnCurrent.fnode.getIndexForNameNode(node);
- if (varIndex >= 0) {
- if (fnCurrent.isNumberVar(varIndex)) {
- cfw.addPush("number");
- } else if (varIsDirectCallParameter(varIndex)) {
- int dcp_register = varRegisters[varIndex];
- cfw.addALoad(dcp_register);
- cfw.add(ByteCode.GETSTATIC, "java/lang/Void", "TYPE",
- "Ljava/lang/Class;");
- int isNumberLabel = cfw.acquireLabel();
- cfw.add(ByteCode.IF_ACMPEQ, isNumberLabel);
- short stack = cfw.getStackTop();
- cfw.addALoad(dcp_register);
- addScriptRuntimeInvoke("typeof",
- "(Ljava/lang/Object;"
- +")Ljava/lang/String;");
- int beyond = cfw.acquireLabel();
- cfw.add(ByteCode.GOTO, beyond);
- cfw.markLabel(isNumberLabel, stack);
- cfw.addPush("number");
- cfw.markLabel(beyond);
- } else {
- cfw.addALoad(varRegisters[varIndex]);
- addScriptRuntimeInvoke("typeof",
- "(Ljava/lang/Object;"
- +")Ljava/lang/String;");
- }
- return;
- }
- }
- cfw.addALoad(variableObjectLocal);
- cfw.addPush(node.getString());
- addScriptRuntimeInvoke("typeofName",
- "(Lorg/mozilla/javascript/Scriptable;"
- +"Ljava/lang/String;"
- +")Ljava/lang/String;");
- }
-
- /**
- * Save the current code offset. This saved code offset is used to
- * compute instruction counts in subsequent calls to
- * {@link #addInstructionCount}.
- */
- private void saveCurrentCodeOffset() {
- savedCodeOffset = cfw.getCurrentCodeOffset();
- }
-
- /**
- * Generate calls to ScriptRuntime.addInstructionCount to keep track of
- * executed instructions and call <code>observeInstructionCount()</code>
- * if a threshold is exceeded.
- */
- private void addInstructionCount() {
- int count = cfw.getCurrentCodeOffset() - savedCodeOffset;
- if (count == 0)
- return;
- cfw.addALoad(contextLocal);
- cfw.addPush(count);
- addScriptRuntimeInvoke("addInstructionCount",
- "(Lorg/mozilla/javascript/Context;"
- +"I)V");
- }
-
- private void visitIncDec(Node node)
- {
- int incrDecrMask = node.getExistingIntProp(Node.INCRDECR_PROP);
- Node child = node.getFirstChild();
- switch (child.getType()) {
- case Token.GETVAR:
- if (!hasVarsInRegs) Kit.codeBug();
- if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
- boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
- int varIndex = fnCurrent.getVarIndex(child);
- short reg = varRegisters[varIndex];
- int offset = varIsDirectCallParameter(varIndex) ? 1 : 0;
- cfw.addDLoad(reg + offset);
- if (post) {
- cfw.add(ByteCode.DUP2);
- }
- cfw.addPush(1.0);
- if ((incrDecrMask & Node.DECR_FLAG) == 0) {
- cfw.add(ByteCode.DADD);
- } else {
- cfw.add(ByteCode.DSUB);
- }
- if (!post) {
- cfw.add(ByteCode.DUP2);
- }
- cfw.addDStore(reg + offset);
- } else {
- boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
- int varIndex = fnCurrent.getVarIndex(child);
- short reg = varRegisters[varIndex];
- cfw.addALoad(reg);
- if (post) {
- cfw.add(ByteCode.DUP);
- }
- addObjectToDouble();
- cfw.addPush(1.0);
- if ((incrDecrMask & Node.DECR_FLAG) == 0) {
- cfw.add(ByteCode.DADD);
- } else {
- cfw.add(ByteCode.DSUB);
- }
- addDoubleWrap();
- if (!post) {
- cfw.add(ByteCode.DUP);
- }
- cfw.addAStore(reg);
- break;
- }
- break;
- case Token.NAME:
- cfw.addALoad(variableObjectLocal);
- cfw.addPush(child.getString()); // push name
- cfw.addALoad(contextLocal);
- cfw.addPush(incrDecrMask);
- addScriptRuntimeInvoke("nameIncrDecr",
- "(Lorg/mozilla/javascript/Scriptable;"
- +"Ljava/lang/String;"
- +"Lorg/mozilla/javascript/Context;"
- +"I)Ljava/lang/Object;");
- break;
- case Token.GETPROPNOWARN:
- throw Kit.codeBug();
- case Token.GETPROP: {
- Node getPropChild = child.getFirstChild();
- generateExpression(getPropChild, node);
- generateExpression(getPropChild.getNext(), node);
- cfw.addALoad(contextLocal);
- cfw.addPush(incrDecrMask);
- addScriptRuntimeInvoke("propIncrDecr",
- "(Ljava/lang/Object;"
- +"Ljava/lang/String;"
- +"Lorg/mozilla/javascript/Context;"
- +"I)Ljava/lang/Object;");
- break;
- }
- case Token.GETELEM: {
- Node elemChild = child.getFirstChild();
- generateExpression(elemChild, node);
- generateExpression(elemChild.getNext(), node);
- cfw.addALoad(contextLocal);
- cfw.addPush(incrDecrMask);
- if (elemChild.getNext().getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
- addOptRuntimeInvoke("elemIncrDecr",
- "(Ljava/lang/Object;"
- +"D"
- +"Lorg/mozilla/javascript/Context;"
- +"I"
- +")Ljava/lang/Object;");
- } else {
- addScriptRuntimeInvoke("elemIncrDecr",
- "(Ljava/lang/Object;"
- +"Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +"I"
- +")Ljava/lang/Object;");
- }
- break;
- }
- case Token.GET_REF: {
- Node refChild = child.getFirstChild();
- generateExpression(refChild, node);
- cfw.addALoad(contextLocal);
- cfw.addPush(incrDecrMask);
- addScriptRuntimeInvoke(
- "refIncrDecr",
- "(Lorg/mozilla/javascript/Ref;"
- +"Lorg/mozilla/javascript/Context;"
- +"I)Ljava/lang/Object;");
- break;
- }
- default:
- Codegen.badTree();
- }
- }
-
- private static boolean isArithmeticNode(Node node)
- {
- int type = node.getType();
- return (type == Token.SUB)
- || (type == Token.MOD)
- || (type == Token.DIV)
- || (type == Token.MUL);
- }
-
- private void visitArithmetic(Node node, int opCode, Node child,
- Node parent)
- {
- int childNumberFlag = node.getIntProp(Node.ISNUMBER_PROP, -1);
- if (childNumberFlag != -1) {
- generateExpression(child, node);
- generateExpression(child.getNext(), node);
- cfw.add(opCode);
- }
- else {
- boolean childOfArithmetic = isArithmeticNode(parent);
- generateExpression(child, node);
- if (!isArithmeticNode(child))
- addObjectToDouble();
- generateExpression(child.getNext(), node);
- if (!isArithmeticNode(child.getNext()))
- addObjectToDouble();
- cfw.add(opCode);
- if (!childOfArithmetic) {
- addDoubleWrap();
- }
- }
- }
-
- private void visitBitOp(Node node, int type, Node child)
- {
- int childNumberFlag = node.getIntProp(Node.ISNUMBER_PROP, -1);
- generateExpression(child, node);
-
- // special-case URSH; work with the target arg as a long, so
- // that we can return a 32-bit unsigned value, and call
- // toUint32 instead of toInt32.
- if (type == Token.URSH) {
- addScriptRuntimeInvoke("toUint32", "(Ljava/lang/Object;)J");
- generateExpression(child.getNext(), node);
- addScriptRuntimeInvoke("toInt32", "(Ljava/lang/Object;)I");
- // Looks like we need to explicitly mask the shift to 5 bits -
- // LUSHR takes 6 bits.
- cfw.addPush(31);
- cfw.add(ByteCode.IAND);
- cfw.add(ByteCode.LUSHR);
- cfw.add(ByteCode.L2D);
- addDoubleWrap();
- return;
- }
- if (childNumberFlag == -1) {
- addScriptRuntimeInvoke("toInt32", "(Ljava/lang/Object;)I");
- generateExpression(child.getNext(), node);
- addScriptRuntimeInvoke("toInt32", "(Ljava/lang/Object;)I");
- }
- else {
- addScriptRuntimeInvoke("toInt32", "(D)I");
- generateExpression(child.getNext(), node);
- addScriptRuntimeInvoke("toInt32", "(D)I");
- }
- switch (type) {
- case Token.BITOR:
- cfw.add(ByteCode.IOR);
- break;
- case Token.BITXOR:
- cfw.add(ByteCode.IXOR);
- break;
- case Token.BITAND:
- cfw.add(ByteCode.IAND);
- break;
- case Token.RSH:
- cfw.add(ByteCode.ISHR);
- break;
- case Token.LSH:
- cfw.add(ByteCode.ISHL);
- break;
- default:
- throw Codegen.badTree();
- }
- cfw.add(ByteCode.I2D);
- if (childNumberFlag == -1) {
- addDoubleWrap();
- }
- }
-
- private int nodeIsDirectCallParameter(Node node)
- {
- if (node.getType() == Token.GETVAR
- && inDirectCallFunction && !itsForcedObjectParameters)
- {
- int varIndex = fnCurrent.getVarIndex(node);
- if (fnCurrent.isParameter(varIndex)) {
- return varRegisters[varIndex];
- }
- }
- return -1;
- }
-
- private boolean varIsDirectCallParameter(int varIndex)
- {
- return fnCurrent.isParameter(varIndex)
- && inDirectCallFunction && !itsForcedObjectParameters;
- }
-
- private void genSimpleCompare(int type, int trueGOTO, int falseGOTO)
- {
- if (trueGOTO == -1) throw Codegen.badTree();
- switch (type) {
- case Token.LE :
- cfw.add(ByteCode.DCMPG);
- cfw.add(ByteCode.IFLE, trueGOTO);
- break;
- case Token.GE :
- cfw.add(ByteCode.DCMPL);
- cfw.add(ByteCode.IFGE, trueGOTO);
- break;
- case Token.LT :
- cfw.add(ByteCode.DCMPG);
- cfw.add(ByteCode.IFLT, trueGOTO);
- break;
- case Token.GT :
- cfw.add(ByteCode.DCMPL);
- cfw.add(ByteCode.IFGT, trueGOTO);
- break;
- default :
- throw Codegen.badTree();
-
- }
- if (falseGOTO != -1)
- cfw.add(ByteCode.GOTO, falseGOTO);
- }
-
- private void visitIfJumpRelOp(Node node, Node child,
- int trueGOTO, int falseGOTO)
- {
- if (trueGOTO == -1 || falseGOTO == -1) throw Codegen.badTree();
- int type = node.getType();
- Node rChild = child.getNext();
- if (type == Token.INSTANCEOF || type == Token.IN) {
- generateExpression(child, node);
- generateExpression(rChild, node);
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke(
- (type == Token.INSTANCEOF) ? "instanceOf" : "in",
- "(Ljava/lang/Object;"
- +"Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +")Z");
- cfw.add(ByteCode.IFNE, trueGOTO);
- cfw.add(ByteCode.GOTO, falseGOTO);
- return;
- }
- int childNumberFlag = node.getIntProp(Node.ISNUMBER_PROP, -1);
- int left_dcp_register = nodeIsDirectCallParameter(child);
- int right_dcp_register = nodeIsDirectCallParameter(rChild);
- if (childNumberFlag != -1) {
- // Force numeric context on both parameters and optimize
- // direct call case as Optimizer currently does not handle it
-
- if (childNumberFlag != Node.RIGHT) {
- // Left already has number content
- generateExpression(child, node);
- } else if (left_dcp_register != -1) {
- dcpLoadAsNumber(left_dcp_register);
- } else {
- generateExpression(child, node);
- addObjectToDouble();
- }
-
- if (childNumberFlag != Node.LEFT) {
- // Right already has number content
- generateExpression(rChild, node);
- } else if (right_dcp_register != -1) {
- dcpLoadAsNumber(right_dcp_register);
- } else {
- generateExpression(rChild, node);
- addObjectToDouble();
- }
-
- genSimpleCompare(type, trueGOTO, falseGOTO);
-
- } else {
- if (left_dcp_register != -1 && right_dcp_register != -1) {
- // Generate code to dynamically check for number content
- // if both operands are dcp
- short stack = cfw.getStackTop();
- int leftIsNotNumber = cfw.acquireLabel();
- cfw.addALoad(left_dcp_register);
- cfw.add(ByteCode.GETSTATIC,
- "java/lang/Void",
- "TYPE",
- "Ljava/lang/Class;");
- cfw.add(ByteCode.IF_ACMPNE, leftIsNotNumber);
- cfw.addDLoad(left_dcp_register + 1);
- dcpLoadAsNumber(right_dcp_register);
- genSimpleCompare(type, trueGOTO, falseGOTO);
- if (stack != cfw.getStackTop()) throw Codegen.badTree();
-
- cfw.markLabel(leftIsNotNumber);
- int rightIsNotNumber = cfw.acquireLabel();
- cfw.addALoad(right_dcp_register);
- cfw.add(ByteCode.GETSTATIC,
- "java/lang/Void",
- "TYPE",
- "Ljava/lang/Class;");
- cfw.add(ByteCode.IF_ACMPNE, rightIsNotNumber);
- cfw.addALoad(left_dcp_register);
- addObjectToDouble();
- cfw.addDLoad(right_dcp_register + 1);
- genSimpleCompare(type, trueGOTO, falseGOTO);
- if (stack != cfw.getStackTop()) throw Codegen.badTree();
-
- cfw.markLabel(rightIsNotNumber);
- // Load both register as objects to call generic cmp_*
- cfw.addALoad(left_dcp_register);
- cfw.addALoad(right_dcp_register);
-
- } else {
- generateExpression(child, node);
- generateExpression(rChild, node);
- }
-
- if (type == Token.GE || type == Token.GT) {
- cfw.add(ByteCode.SWAP);
- }
- String routine = ((type == Token.LT)
- || (type == Token.GT)) ? "cmp_LT" : "cmp_LE";
- addScriptRuntimeInvoke(routine,
- "(Ljava/lang/Object;"
- +"Ljava/lang/Object;"
- +")Z");
- cfw.add(ByteCode.IFNE, trueGOTO);
- cfw.add(ByteCode.GOTO, falseGOTO);
- }
- }
-
- private void visitIfJumpEqOp(Node node, Node child,
- int trueGOTO, int falseGOTO)
- {
- if (trueGOTO == -1 || falseGOTO == -1) throw Codegen.badTree();
-
- short stackInitial = cfw.getStackTop();
- int type = node.getType();
- Node rChild = child.getNext();
-
- // Optimize if one of operands is null
- if (child.getType() == Token.NULL || rChild.getType() == Token.NULL) {
- // eq is symmetric in this case
- if (child.getType() == Token.NULL) {
- child = rChild;
- }
- generateExpression(child, node);
- if (type == Token.SHEQ || type == Token.SHNE) {
- int testCode = (type == Token.SHEQ)
- ? ByteCode.IFNULL : ByteCode.IFNONNULL;
- cfw.add(testCode, trueGOTO);
- } else {
- if (type != Token.EQ) {
- // swap false/true targets for !=
- if (type != Token.NE) throw Codegen.badTree();
- int tmp = trueGOTO;
- trueGOTO = falseGOTO;
- falseGOTO = tmp;
- }
- cfw.add(ByteCode.DUP);
- int undefCheckLabel = cfw.acquireLabel();
- cfw.add(ByteCode.IFNONNULL, undefCheckLabel);
- short stack = cfw.getStackTop();
- cfw.add(ByteCode.POP);
- cfw.add(ByteCode.GOTO, trueGOTO);
- cfw.markLabel(undefCheckLabel, stack);
- Codegen.pushUndefined(cfw);
- cfw.add(ByteCode.IF_ACMPEQ, trueGOTO);
- }
- cfw.add(ByteCode.GOTO, falseGOTO);
- } else {
- int child_dcp_register = nodeIsDirectCallParameter(child);
- if (child_dcp_register != -1
- && rChild.getType() == Token.TO_OBJECT)
- {
- Node convertChild = rChild.getFirstChild();
- if (convertChild.getType() == Token.NUMBER) {
- cfw.addALoad(child_dcp_register);
- cfw.add(ByteCode.GETSTATIC,
- "java/lang/Void",
- "TYPE",
- "Ljava/lang/Class;");
- int notNumbersLabel = cfw.acquireLabel();
- cfw.add(ByteCode.IF_ACMPNE, notNumbersLabel);
- cfw.addDLoad(child_dcp_register + 1);
- cfw.addPush(convertChild.getDouble());
- cfw.add(ByteCode.DCMPL);
- if (type == Token.EQ)
- cfw.add(ByteCode.IFEQ, trueGOTO);
- else
- cfw.add(ByteCode.IFNE, trueGOTO);
- cfw.add(ByteCode.GOTO, falseGOTO);
- cfw.markLabel(notNumbersLabel);
- // fall thru into generic handling
- }
- }
-
- generateExpression(child, node);
- generateExpression(rChild, node);
-
- String name;
- int testCode;
- switch (type) {
- case Token.EQ:
- name = "eq";
- testCode = ByteCode.IFNE;
- break;
- case Token.NE:
- name = "eq";
- testCode = ByteCode.IFEQ;
- break;
- case Token.SHEQ:
- name = "shallowEq";
- testCode = ByteCode.IFNE;
- break;
- case Token.SHNE:
- name = "shallowEq";
- testCode = ByteCode.IFEQ;
- break;
- default:
- throw Codegen.badTree();
- }
- addScriptRuntimeInvoke(name,
- "(Ljava/lang/Object;"
- +"Ljava/lang/Object;"
- +")Z");
- cfw.add(testCode, trueGOTO);
- cfw.add(ByteCode.GOTO, falseGOTO);
- }
- if (stackInitial != cfw.getStackTop()) throw Codegen.badTree();
- }
-
- private void visitSetName(Node node, Node child)
- {
- String name = node.getFirstChild().getString();
- while (child != null) {
- generateExpression(child, node);
- child = child.getNext();
- }
- cfw.addALoad(contextLocal);
- cfw.addALoad(variableObjectLocal);
- cfw.addPush(name);
- addScriptRuntimeInvoke(
- "setName",
- "(Lorg/mozilla/javascript/Scriptable;"
- +"Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +"Ljava/lang/String;"
- +")Ljava/lang/Object;");
- }
-
- private void visitSetConst(Node node, Node child)
- {
- String name = node.getFirstChild().getString();
- while (child != null) {
- generateExpression(child, node);
- child = child.getNext();
- }
- cfw.addALoad(contextLocal);
- cfw.addPush(name);
- addScriptRuntimeInvoke(
- "setConst",
- "(Lorg/mozilla/javascript/Scriptable;"
- +"Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +"Ljava/lang/String;"
- +")Ljava/lang/Object;");
- }
-
- private void visitGetVar(Node node)
- {
- if (!hasVarsInRegs) Kit.codeBug();
- int varIndex = fnCurrent.getVarIndex(node);
- short reg = varRegisters[varIndex];
- if (varIsDirectCallParameter(varIndex)) {
- // Remember that here the isNumber flag means that we
- // want to use the incoming parameter in a Number
- // context, so test the object type and convert the
- // value as necessary.
- if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
- dcpLoadAsNumber(reg);
- } else {
- dcpLoadAsObject(reg);
- }
- } else if (fnCurrent.isNumberVar(varIndex)) {
- cfw.addDLoad(reg);
- } else {
- cfw.addALoad(reg);
- }
- }
-
- private void visitSetVar(Node node, Node child, boolean needValue)
- {
- if (!hasVarsInRegs) Kit.codeBug();
- int varIndex = fnCurrent.getVarIndex(node);
- generateExpression(child.getNext(), node);
- boolean isNumber = (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1);
- short reg = varRegisters[varIndex];
- boolean [] constDeclarations = fnCurrent.fnode.getParamAndVarConst();
- if (constDeclarations[varIndex]) {
- if (!needValue) {
- if (isNumber)
- cfw.add(ByteCode.POP2);
- else
- cfw.add(ByteCode.POP);
- }
- }
- else if (varIsDirectCallParameter(varIndex)) {
- if (isNumber) {
- if (needValue) cfw.add(ByteCode.DUP2);
- cfw.addALoad(reg);
- cfw.add(ByteCode.GETSTATIC,
- "java/lang/Void",
- "TYPE",
- "Ljava/lang/Class;");
- int isNumberLabel = cfw.acquireLabel();
- int beyond = cfw.acquireLabel();
- cfw.add(ByteCode.IF_ACMPEQ, isNumberLabel);
- short stack = cfw.getStackTop();
- addDoubleWrap();
- cfw.addAStore(reg);
- cfw.add(ByteCode.GOTO, beyond);
- cfw.markLabel(isNumberLabel, stack);
- cfw.addDStore(reg + 1);
- cfw.markLabel(beyond);
- }
- else {
- if (needValue) cfw.add(ByteCode.DUP);
- cfw.addAStore(reg);
- }
- } else {
- boolean isNumberVar = fnCurrent.isNumberVar(varIndex);
- if (isNumber) {
- if (isNumberVar) {
- cfw.addDStore(reg);
- if (needValue) cfw.addDLoad(reg);
- } else {
- if (needValue) cfw.add(ByteCode.DUP2);
- // Cannot save number in variable since !isNumberVar,
- // so convert to object
- addDoubleWrap();
- cfw.addAStore(reg);
- }
- } else {
- if (isNumberVar) Kit.codeBug();
- cfw.addAStore(reg);
- if (needValue) cfw.addALoad(reg);
- }
- }
- }
-
- private void visitSetConstVar(Node node, Node child, boolean needValue)
- {
- if (!hasVarsInRegs) Kit.codeBug();
- int varIndex = fnCurrent.getVarIndex(node);
- generateExpression(child.getNext(), node);
- boolean isNumber = (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1);
- short reg = varRegisters[varIndex];
- int beyond = cfw.acquireLabel();
- int noAssign = cfw.acquireLabel();
- if (isNumber) {
- cfw.addILoad(reg + 2);
- cfw.add(ByteCode.IFNE, noAssign);
- short stack = cfw.getStackTop();
- cfw.addPush(1);
- cfw.addIStore(reg + 2);
- cfw.addDStore(reg);
- if (needValue) {
- cfw.addDLoad(reg);
- cfw.markLabel(noAssign, stack);
- } else {
- cfw.add(ByteCode.GOTO, beyond);
- cfw.markLabel(noAssign, stack);
- cfw.add(ByteCode.POP2);
- }
- }
- else {
- cfw.addILoad(reg + 1);
- cfw.add(ByteCode.IFNE, noAssign);
- short stack = cfw.getStackTop();
- cfw.addPush(1);
- cfw.addIStore(reg + 1);
- cfw.addAStore(reg);
- if (needValue) {
- cfw.addALoad(reg);
- cfw.markLabel(noAssign, stack);
- } else {
- cfw.add(ByteCode.GOTO, beyond);
- cfw.markLabel(noAssign, stack);
- cfw.add(ByteCode.POP);
- }
- }
- cfw.markLabel(beyond);
- }
-
- private void visitGetProp(Node node, Node child)
- {
- generateExpression(child, node); // object
- Node nameChild = child.getNext();
- generateExpression(nameChild, node); // the name
- if (node.getType() == Token.GETPROPNOWARN) {
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke(
- "getObjectPropNoWarn",
- "(Ljava/lang/Object;"
- +"Ljava/lang/String;"
- +"Lorg/mozilla/javascript/Context;"
- +")Ljava/lang/Object;");
- return;
- }
- /*
- for 'this.foo' we call getObjectProp(Scriptable...) which can
- skip some casting overhead.
- */
- int childType = child.getType();
- if (childType == Token.THIS && nameChild.getType() == Token.STRING) {
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke(
- "getObjectProp",
- "(Lorg/mozilla/javascript/Scriptable;"
- +"Ljava/lang/String;"
- +"Lorg/mozilla/javascript/Context;"
- +")Ljava/lang/Object;");
- } else {
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke(
- "getObjectProp",
- "(Ljava/lang/Object;"
- +"Ljava/lang/String;"
- +"Lorg/mozilla/javascript/Context;"
- +")Ljava/lang/Object;");
- }
- }
-
- private void visitSetProp(int type, Node node, Node child)
- {
- Node objectChild = child;
- generateExpression(child, node);
- child = child.getNext();
- if (type == Token.SETPROP_OP) {
- cfw.add(ByteCode.DUP);
- }
- Node nameChild = child;
- generateExpression(child, node);
- child = child.getNext();
- if (type == Token.SETPROP_OP) {
- // stack: ... object object name -> ... object name object name
- cfw.add(ByteCode.DUP_X1);
- //for 'this.foo += ...' we call thisGet which can skip some
- //casting overhead.
- if (objectChild.getType() == Token.THIS
- && nameChild.getType() == Token.STRING)
- {
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke(
- "getObjectProp",
- "(Lorg/mozilla/javascript/Scriptable;"
- +"Ljava/lang/String;"
- +"Lorg/mozilla/javascript/Context;"
- +")Ljava/lang/Object;");
- } else {
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke(
- "getObjectProp",
- "(Ljava/lang/Object;"
- +"Ljava/lang/String;"
- +"Lorg/mozilla/javascript/Context;"
- +")Ljava/lang/Object;");
- }
- }
- generateExpression(child, node);
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke(
- "setObjectProp",
- "(Ljava/lang/Object;"
- +"Ljava/lang/String;"
- +"Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +")Ljava/lang/Object;");
- }
-
- private void visitSetElem(int type, Node node, Node child)
- {
- generateExpression(child, node);
- child = child.getNext();
- if (type == Token.SETELEM_OP) {
- cfw.add(ByteCode.DUP);
- }
- generateExpression(child, node);
- child = child.getNext();
- boolean indexIsNumber = (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1);
- if (type == Token.SETELEM_OP) {
- if (indexIsNumber) {
- // stack: ... object object number
- // -> ... object number object number
- cfw.add(ByteCode.DUP2_X1);
- cfw.addALoad(contextLocal);
- addOptRuntimeInvoke(
- "getObjectIndex",
- "(Ljava/lang/Object;D"
- +"Lorg/mozilla/javascript/Context;"
- +")Ljava/lang/Object;");
- } else {
- // stack: ... object object indexObject
- // -> ... object indexObject object indexObject
- cfw.add(ByteCode.DUP_X1);
- cfw.addALoad(contextLocal);
- addScriptRuntimeInvoke(
- "getObjectElem",
- "(Ljava/lang/Object;"
- +"Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +")Ljava/lang/Object;");
- }
- }
- generateExpression(child, node);
- cfw.addALoad(contextLocal);
- if (indexIsNumber) {
- addScriptRuntimeInvoke(
- "setObjectIndex",
- "(Ljava/lang/Object;"
- +"D"
- +"Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +")Ljava/lang/Object;");
- } else {
- addScriptRuntimeInvoke(
- "setObjectElem",
- "(Ljava/lang/Object;"
- +"Ljava/lang/Object;"
- +"Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Context;"
- +")Ljava/lang/Object;");
- }
- }
-
- private void visitDotQuery(Node node, Node child)
- {
- updateLineNumber(node);
- generateExpression(child, node);
- cfw.addALoad(variableObjectLocal);
- addScriptRuntimeInvoke("enterDotQuery",
- "(Ljava/lang/Object;"
- +"Lorg/mozilla/javascript/Scriptable;"
- +")Lorg/mozilla/javascript/Scriptable;");
- cfw.addAStore(variableObjectLocal);
-
- // add push null/pop with label in between to simplify code for loop
- // continue when it is necessary to pop the null result from
- // updateDotQuery
- cfw.add(ByteCode.ACONST_NULL);
- int queryLoopStart = cfw.acquireLabel();
- cfw.markLabel(queryLoopStart); // loop continue jumps here
- cfw.add(ByteCode.POP);
-
- generateExpression(child.getNext(), node);
- addScriptRuntimeInvoke("toBoolean", "(Ljava/lang/Object;)Z");
- cfw.addALoad(variableObjectLocal);
- addScriptRuntimeInvoke("updateDotQuery",
- "(Z"
- +"Lorg/mozilla/javascript/Scriptable;"
- +")Ljava/lang/Object;");
- cfw.add(ByteCode.DUP);
- cfw.add(ByteCode.IFNULL, queryLoopStart);
- // stack: ... non_null_result_of_updateDotQuery
- cfw.addALoad(variableObjectLocal);
- addScriptRuntimeInvoke("leaveDotQuery",
- "(Lorg/mozilla/javascript/Scriptable;"
- +")Lorg/mozilla/javascript/Scriptable;");
- cfw.addAStore(variableObjectLocal);
- }
-
- private int getLocalBlockRegister(Node node)
- {
- Node localBlock = (Node)node.getProp(Node.LOCAL_BLOCK_PROP);
- int localSlot = localBlock.getExistingIntProp(Node.LOCAL_PROP);
- return localSlot;
- }
-
- private void dcpLoadAsNumber(int dcp_register)
- {
- cfw.addALoad(dcp_register);
- cfw.add(ByteCode.GETSTATIC,
- "java/lang/Void",
- "TYPE",
- "Ljava/lang/Class;");
- int isNumberLabel = cfw.acquireLabel();
- cfw.add(ByteCode.IF_ACMPEQ, isNumberLabel);
- short stack = cfw.getStackTop();
- cfw.addALoad(dcp_register);
- addObjectToDouble();
- int beyond = cfw.acquireLabel();
- cfw.add(ByteCode.GOTO, beyond);
- cfw.markLabel(isNumberLabel, stack);
- cfw.addDLoad(dcp_register + 1);
- cfw.markLabel(beyond);
- }
-
- private void dcpLoadAsObject(int dcp_register)
- {
- cfw.addALoad(dcp_register);
- cfw.add(ByteCode.GETSTATIC,
- "java/lang/Void",
- "TYPE",
- "Ljava/lang/Class;");
- int isNumberLabel = cfw.acquireLabel();
- cfw.add(ByteCode.IF_ACMPEQ, isNumberLabel);
- short stack = cfw.getStackTop();
- cfw.addALoad(dcp_register);
- int beyond = cfw.acquireLabel();
- cfw.add(ByteCode.GOTO, beyond);
- cfw.markLabel(isNumberLabel, stack);
- cfw.addDLoad(dcp_register + 1);
- addDoubleWrap();
- cfw.markLabel(beyond);
- }
-
- private void addGoto(Node target, int jumpcode)
- {
- int targetLabel = getTargetLabel(target);
- cfw.add(jumpcode, targetLabel);
- }
-
- private void addObjectToDouble()
- {
- addScriptRuntimeInvoke("toNumber", "(Ljava/lang/Object;)D");
- }
-
- private void addNewObjectArray(int size)
- {
- if (size == 0) {
- if (itsZeroArgArray >= 0) {
- cfw.addALoad(itsZeroArgArray);
- } else {
- cfw.add(ByteCode.GETSTATIC,
- "org/mozilla/javascript/ScriptRuntime",
- "emptyArgs", "[Ljava/lang/Object;");
- }
- } else {
- cfw.addPush(size);
- cfw.add(ByteCode.ANEWARRAY, "java/lang/Object");
- }
- }
-
- private void addScriptRuntimeInvoke(String methodName,
- String methodSignature)
- {
- cfw.addInvoke(ByteCode.INVOKESTATIC,
- "org.mozilla.javascript.ScriptRuntime",
- methodName,
- methodSignature);
- }
-
- private void addOptRuntimeInvoke(String methodName,
- String methodSignature)
- {
- cfw.addInvoke(ByteCode.INVOKESTATIC,
- "org/mozilla/javascript/optimizer/OptRuntime",
- methodName,
- methodSignature);
- }
-
- private void addJumpedBooleanWrap(int trueLabel, int falseLabel)
- {
- cfw.markLabel(falseLabel);
- int skip = cfw.acquireLabel();
- cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean",
- "FALSE", "Ljava/lang/Boolean;");
- cfw.add(ByteCode.GOTO, skip);
- cfw.markLabel(trueLabel);
- cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean",
- "TRUE", "Ljava/lang/Boolean;");
- cfw.markLabel(skip);
- cfw.adjustStackTop(-1); // only have 1 of true/false
- }
-
- private void addDoubleWrap()
- {
- addOptRuntimeInvoke("wrapDouble", "(D)Ljava/lang/Double;");
- }
-
- /**
- * Const locals use an extra slot to hold the has-been-assigned-once flag at
- * runtime.
- * @param isConst true iff the variable is const
- * @return the register for the word pair (double/long)
- */
- private short getNewWordPairLocal(boolean isConst)
- {
- short result = getConsecutiveSlots(2, isConst);
- if (result < (MAX_LOCALS - 1)) {
- locals[result] = 1;
- locals[result + 1] = 1;
- if (isConst)
- locals[result + 2] = 1;
- if (result == firstFreeLocal) {
- for (int i = firstFreeLocal + 2; i < MAX_LOCALS; i++) {
- if (locals[i] == 0) {
- firstFreeLocal = (short) i;
- if (localsMax < firstFreeLocal)
- localsMax = firstFreeLocal;
- return result;
- }
- }
- }
- else {
- return result;
- }
- }
- throw Context.reportRuntimeError("Program too complex " +
- "(out of locals)");
- }
-
- private short getNewWordLocal(boolean isConst)
- {
- short result = getConsecutiveSlots(1, isConst);
- if (result < (MAX_LOCALS - 1)) {
- locals[result] = 1;
- if (isConst)
- locals[result + 1] = 1;
- if (result == firstFreeLocal) {
- for (int i = firstFreeLocal + 2; i < MAX_LOCALS; i++) {
- if (locals[i] == 0) {
- firstFreeLocal = (short) i;
- if (localsMax < firstFreeLocal)
- localsMax = firstFreeLocal;
- return result;
- }
- }
- }
- else {
- return result;
- }
- }
- throw Context.reportRuntimeError("Program too complex " +
- "(out of locals)");
- }
-
- private short getNewWordLocal()
- {
- short result = firstFreeLocal;
- locals[result] = 1;
- for (int i = firstFreeLocal + 1; i < MAX_LOCALS; i++) {
- if (locals[i] == 0) {
- firstFreeLocal = (short) i;
- if (localsMax < firstFreeLocal)
- localsMax = firstFreeLocal;
- return result;
- }
- }
- throw Context.reportRuntimeError("Program too complex " +
- "(out of locals)");
- }
-
- private short getConsecutiveSlots(int count, boolean isConst) {
- if (isConst)
- count++;
- short result = firstFreeLocal;
- while (true) {
- if (result >= (MAX_LOCALS - 1))
- break;
- int i;
- for (i = 0; i < count; i++)
- if (locals[result + i] != 0)
- break;
- if (i >= count)
- break;
- result++;
- }
- return result;
- }
-
- // This is a valid call only for a local that is allocated by default.
- private void incReferenceWordLocal(short local)
- {
- locals[local]++;
- }
-
- // This is a valid call only for a local that is allocated by default.
- private void decReferenceWordLocal(short local)
- {
- locals[local]--;
- }
-
- private void releaseWordLocal(short local)
- {
- if (local < firstFreeLocal)
- firstFreeLocal = local;
- locals[local] = 0;
- }
-
-
- static final int GENERATOR_TERMINATE = -1;
- static final int GENERATOR_START = 0;
- static final int GENERATOR_YIELD_START = 1;
-
- ClassFileWriter cfw;
- Codegen codegen;
- CompilerEnvirons compilerEnv;
- ScriptOrFnNode scriptOrFn;
- public int scriptOrFnIndex;
- private int savedCodeOffset;
-
- private OptFunctionNode fnCurrent;
- private boolean isTopLevel;
-
- private static final int MAX_LOCALS = 256;
- private int[] locals;
- private short firstFreeLocal;
- private short localsMax;
-
- private int itsLineNumber;
-
- private boolean hasVarsInRegs;
- private short[] varRegisters;
- private boolean inDirectCallFunction;
- private boolean itsForcedObjectParameters;
- private int enterAreaStartLabel;
- private int epilogueLabel;
-
- // special known locals. If you add a new local here, be sure
- // to initialize it to -1 in initBodyGeneration
- private short variableObjectLocal;
- private short popvLocal;
- private short contextLocal;
- private short argsLocal;
- private short operationLocal;
- private short thisObjLocal;
- private short funObjLocal;
- private short itsZeroArgArray;
- private short itsOneArgArray;
- private short scriptRegexpLocal;
- private short generatorStateLocal;
-
- private boolean isGenerator;
- private int generatorSwitch;
- private int maxLocals = 0;
- private int maxStack = 0;
-
- private Hashtable finallys;
-
- class FinallyReturnPoint {
- public ArrayList jsrPoints = new ArrayList();
- public int tableLabel = 0;
- }
-}