From 98e2821b38a775737e42a2479a6bc65107210859 Mon Sep 17 00:00:00 2001 From: Elliot Kroo Date: Thu, 11 Mar 2010 15:21:30 -0800 Subject: reorganizing the first level of folders (trunk/branch folders are not the git way :) --- .../org/mozilla/javascript/optimizer/Codegen.java | 5031 -------------------- 1 file changed, 5031 deletions(-) delete mode 100644 trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Codegen.java (limited to 'trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Codegen.java') diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Codegen.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/optimizer/Codegen.java deleted file mode 100644 index 64952bf..0000000 --- a/trunk/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() { - Scriptable newInstance = createObject(cx, scope); - Object val = (cx, scope, newInstance, ); - 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(), - "", "()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("", "()V", ClassFileWriter.ACC_PUBLIC); - - cfw.addLoadThis(); - cfw.addInvoke(ByteCode.INVOKESPECIAL, SUPER_CLASS_NAME, - "", "()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("", FUNCTION_CONSTRUCTOR_SIGNATURE, - ClassFileWriter.ACC_PUBLIC); - cfw.addALoad(0); - cfw.addInvoke(ByteCode.INVOKESPECIAL, SUPER_CLASS_NAME, - "", "()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 ; } - - 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("", "()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", - "", "(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, - "", 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", - "", - "(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, - "", 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 observeInstructionCount() - * 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; - } -} -- cgit v1.2.3