0001: /* ***** BEGIN LICENSE BLOCK *****
0002: * Version: MPL 1.1/GPL 2.0
0003: *
0004: * The contents of this file are subject to the Mozilla Public License Version
0005: * 1.1 (the "License"); you may not use this file except in compliance with
0006: * the License. You may obtain a copy of the License at
0007: * http://www.mozilla.org/MPL/
0008: *
0009: * Software distributed under the License is distributed on an "AS IS" basis,
0010: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0011: * for the specific language governing rights and limitations under the
0012: * License.
0013: *
0014: * The Original Code is Rhino code, released
0015: * May 6, 1999.
0016: *
0017: * The Initial Developer of the Original Code is
0018: * Netscape Communications Corporation.
0019: * Portions created by the Initial Developer are Copyright (C) 1997-2000
0020: * the Initial Developer. All Rights Reserved.
0021: *
0022: * Contributor(s):
0023: * Norris Boyd
0024: * Kemal Bayram
0025: * Igor Bukanov
0026: * Bob Jervis
0027: * Roger Lawrence
0028: * Andi Vajda
0029: * Hannes Wallnoefer
0030: *
0031: * Alternatively, the contents of this file may be used under the terms of
0032: * the GNU General Public License Version 2 or later (the "GPL"), in which
0033: * case the provisions of the GPL are applicable instead of those above. If
0034: * you wish to allow use of your version of this file only under the terms of
0035: * the GPL and not to allow others to use your version of this file under the
0036: * MPL, indicate your decision by deleting the provisions above and replacing
0037: * them with the notice and other provisions required by the GPL. If you do
0038: * not delete the provisions above, a recipient may use your version of this
0039: * file under either the MPL or the GPL.
0040: *
0041: * ***** END LICENSE BLOCK ***** */
0042:
0043: package org.mozilla.javascript.optimizer;
0044:
0045: import org.mozilla.javascript.*;
0046: import org.mozilla.classfile.*;
0047: import java.util.*;
0048: import java.lang.reflect.Constructor;
0049: import java.util.Hashtable;
0050:
0051: /**
0052: * This class generates code for a given IR tree.
0053: *
0054: * @author Norris Boyd
0055: * @author Roger Lawrence
0056: */
0057:
0058: public class Codegen implements Evaluator {
0059: public void captureStackInfo(RhinoException ex) {
0060: throw new UnsupportedOperationException();
0061: }
0062:
0063: public String getSourcePositionFromStack(Context cx, int[] linep) {
0064: throw new UnsupportedOperationException();
0065: }
0066:
0067: public String getPatchedStack(RhinoException ex,
0068: String nativeStackTrace) {
0069: throw new UnsupportedOperationException();
0070: }
0071:
0072: public List getScriptStack(RhinoException ex) {
0073: throw new UnsupportedOperationException();
0074: }
0075:
0076: public void setEvalScriptFlag(Script script) {
0077: throw new UnsupportedOperationException();
0078: }
0079:
0080: public Object compile(CompilerEnvirons compilerEnv,
0081: ScriptOrFnNode tree, String encodedSource,
0082: boolean returnFunction) {
0083: int serial;
0084: synchronized (globalLock) {
0085: serial = ++globalSerialClassCounter;
0086: }
0087: String mainClassName = "org.mozilla.javascript.gen.c" + serial;
0088:
0089: byte[] mainClassBytes = compileToClassFile(compilerEnv,
0090: mainClassName, tree, encodedSource, returnFunction);
0091:
0092: return new Object[] { mainClassName, mainClassBytes };
0093: }
0094:
0095: public Script createScriptObject(Object bytecode,
0096: Object staticSecurityDomain) {
0097: Class cl = defineClass(bytecode, staticSecurityDomain);
0098:
0099: Script script;
0100: try {
0101: script = (Script) cl.newInstance();
0102: } catch (Exception ex) {
0103: throw new RuntimeException(
0104: "Unable to instantiate compiled class:"
0105: + ex.toString());
0106: }
0107: return script;
0108: }
0109:
0110: public Function createFunctionObject(Context cx, Scriptable scope,
0111: Object bytecode, Object staticSecurityDomain) {
0112: Class cl = defineClass(bytecode, staticSecurityDomain);
0113:
0114: NativeFunction f;
0115: try {
0116: Constructor ctor = cl.getConstructors()[0];
0117: Object[] initArgs = { scope, cx, new Integer(0) };
0118: f = (NativeFunction) ctor.newInstance(initArgs);
0119: } catch (Exception ex) {
0120: throw new RuntimeException(
0121: "Unable to instantiate compiled class:"
0122: + ex.toString());
0123: }
0124: return f;
0125: }
0126:
0127: private Class defineClass(Object bytecode,
0128: Object staticSecurityDomain) {
0129: Object[] nameBytesPair = (Object[]) bytecode;
0130: String className = (String) nameBytesPair[0];
0131: byte[] classBytes = (byte[]) nameBytesPair[1];
0132:
0133: // The generated classes in this case refer only to Rhino classes
0134: // which must be accessible through this class loader
0135: ClassLoader rhinoLoader = getClass().getClassLoader();
0136: GeneratedClassLoader loader;
0137: loader = SecurityController.createLoader(rhinoLoader,
0138: staticSecurityDomain);
0139: Exception e;
0140: try {
0141: Class cl = loader.defineClass(className, classBytes);
0142: loader.linkClass(cl);
0143: return cl;
0144: } catch (SecurityException x) {
0145: e = x;
0146: } catch (IllegalArgumentException x) {
0147: e = x;
0148: }
0149: throw new RuntimeException("Malformed optimizer package " + e);
0150: }
0151:
0152: byte[] compileToClassFile(CompilerEnvirons compilerEnv,
0153: String mainClassName, ScriptOrFnNode scriptOrFn,
0154: String encodedSource, boolean returnFunction) {
0155: this .compilerEnv = compilerEnv;
0156:
0157: transform(scriptOrFn);
0158:
0159: if (Token.printTrees) {
0160: System.out.println(scriptOrFn.toStringTree(scriptOrFn));
0161: }
0162:
0163: if (returnFunction) {
0164: scriptOrFn = scriptOrFn.getFunctionNode(0);
0165: }
0166:
0167: initScriptOrFnNodesData(scriptOrFn);
0168:
0169: this .mainClassName = mainClassName;
0170: this .mainClassSignature = ClassFileWriter
0171: .classNameToSignature(mainClassName);
0172:
0173: try {
0174: return generateCode(encodedSource);
0175: } catch (ClassFileWriter.ClassFileFormatException e) {
0176: throw reportClassFileFormatException(scriptOrFn, e
0177: .getMessage());
0178: }
0179: }
0180:
0181: private RuntimeException reportClassFileFormatException(
0182: ScriptOrFnNode scriptOrFn, String message) {
0183: String msg = scriptOrFn instanceof FunctionNode ? ScriptRuntime
0184: .getMessage2("msg.while.compiling.fn",
0185: ((FunctionNode) scriptOrFn).getFunctionName(),
0186: message) : ScriptRuntime.getMessage1(
0187: "msg.while.compiling.script", message);
0188: return Context.reportRuntimeError(msg, scriptOrFn
0189: .getSourceName(), scriptOrFn.getLineno(), null, 0);
0190: }
0191:
0192: private void transform(ScriptOrFnNode tree) {
0193: initOptFunctions_r(tree);
0194:
0195: int optLevel = compilerEnv.getOptimizationLevel();
0196:
0197: Hashtable possibleDirectCalls = null;
0198: if (optLevel > 0) {
0199: /*
0200: * Collect all of the contained functions into a hashtable
0201: * so that the call optimizer can access the class name & parameter
0202: * count for any call it encounters
0203: */
0204: if (tree.getType() == Token.SCRIPT) {
0205: int functionCount = tree.getFunctionCount();
0206: for (int i = 0; i != functionCount; ++i) {
0207: OptFunctionNode ofn = OptFunctionNode.get(tree, i);
0208: if (ofn.fnode.getFunctionType() == FunctionNode.FUNCTION_STATEMENT) {
0209: String name = ofn.fnode.getFunctionName();
0210: if (name.length() != 0) {
0211: if (possibleDirectCalls == null) {
0212: possibleDirectCalls = new Hashtable();
0213: }
0214: possibleDirectCalls.put(name, ofn);
0215: }
0216: }
0217: }
0218: }
0219: }
0220:
0221: if (possibleDirectCalls != null) {
0222: directCallTargets = new ObjArray();
0223: }
0224:
0225: OptTransformer ot = new OptTransformer(possibleDirectCalls,
0226: directCallTargets);
0227: ot.transform(tree);
0228:
0229: if (optLevel > 0) {
0230: (new Optimizer()).optimize(tree);
0231: }
0232: }
0233:
0234: private static void initOptFunctions_r(ScriptOrFnNode scriptOrFn) {
0235: for (int i = 0, N = scriptOrFn.getFunctionCount(); i != N; ++i) {
0236: FunctionNode fn = scriptOrFn.getFunctionNode(i);
0237: new OptFunctionNode(fn);
0238: initOptFunctions_r(fn);
0239: }
0240: }
0241:
0242: private void initScriptOrFnNodesData(ScriptOrFnNode scriptOrFn) {
0243: ObjArray x = new ObjArray();
0244: collectScriptOrFnNodes_r(scriptOrFn, x);
0245:
0246: int count = x.size();
0247: scriptOrFnNodes = new ScriptOrFnNode[count];
0248: x.toArray(scriptOrFnNodes);
0249:
0250: scriptOrFnIndexes = new ObjToIntMap(count);
0251: for (int i = 0; i != count; ++i) {
0252: scriptOrFnIndexes.put(scriptOrFnNodes[i], i);
0253: }
0254: }
0255:
0256: private static void collectScriptOrFnNodes_r(ScriptOrFnNode n,
0257: ObjArray x) {
0258: x.add(n);
0259: int nestedCount = n.getFunctionCount();
0260: for (int i = 0; i != nestedCount; ++i) {
0261: collectScriptOrFnNodes_r(n.getFunctionNode(i), x);
0262: }
0263: }
0264:
0265: private byte[] generateCode(String encodedSource) {
0266: boolean hasScript = (scriptOrFnNodes[0].getType() == Token.SCRIPT);
0267: boolean hasFunctions = (scriptOrFnNodes.length > 1 || !hasScript);
0268:
0269: String sourceFile = null;
0270: if (compilerEnv.isGenerateDebugInfo()) {
0271: sourceFile = scriptOrFnNodes[0].getSourceName();
0272: }
0273:
0274: ClassFileWriter cfw = new ClassFileWriter(mainClassName,
0275: SUPER_CLASS_NAME, sourceFile);
0276: cfw.addField(ID_FIELD_NAME, "I", ClassFileWriter.ACC_PRIVATE);
0277: cfw.addField(DIRECT_CALL_PARENT_FIELD, mainClassSignature,
0278: ClassFileWriter.ACC_PRIVATE);
0279: cfw.addField(REGEXP_ARRAY_FIELD_NAME, REGEXP_ARRAY_FIELD_TYPE,
0280: ClassFileWriter.ACC_PRIVATE);
0281:
0282: if (hasFunctions) {
0283: generateFunctionConstructor(cfw);
0284: }
0285:
0286: if (hasScript) {
0287: cfw.addInterface("org/mozilla/javascript/Script");
0288: generateScriptCtor(cfw);
0289: generateMain(cfw);
0290: generateExecute(cfw);
0291: }
0292:
0293: generateCallMethod(cfw);
0294: generateResumeGenerator(cfw);
0295:
0296: generateNativeFunctionOverrides(cfw, encodedSource);
0297:
0298: int count = scriptOrFnNodes.length;
0299: for (int i = 0; i != count; ++i) {
0300: ScriptOrFnNode n = scriptOrFnNodes[i];
0301:
0302: BodyCodegen bodygen = new BodyCodegen();
0303: bodygen.cfw = cfw;
0304: bodygen.codegen = this ;
0305: bodygen.compilerEnv = compilerEnv;
0306: bodygen.scriptOrFn = n;
0307: bodygen.scriptOrFnIndex = i;
0308:
0309: try {
0310: bodygen.generateBodyCode();
0311: } catch (ClassFileWriter.ClassFileFormatException e) {
0312: throw reportClassFileFormatException(n, e.getMessage());
0313: }
0314:
0315: if (n.getType() == Token.FUNCTION) {
0316: OptFunctionNode ofn = OptFunctionNode.get(n);
0317: generateFunctionInit(cfw, ofn);
0318: if (ofn.isTargetOfDirectCall()) {
0319: emitDirectConstructor(cfw, ofn);
0320: }
0321: }
0322: }
0323:
0324: if (directCallTargets != null) {
0325: int N = directCallTargets.size();
0326: for (int j = 0; j != N; ++j) {
0327: cfw
0328: .addField(getDirectTargetFieldName(j),
0329: mainClassSignature,
0330: ClassFileWriter.ACC_PRIVATE);
0331: }
0332: }
0333:
0334: emitRegExpInit(cfw);
0335: emitConstantDudeInitializers(cfw);
0336:
0337: return cfw.toByteArray();
0338: }
0339:
0340: private void emitDirectConstructor(ClassFileWriter cfw,
0341: OptFunctionNode ofn) {
0342: /*
0343: we generate ..
0344: Scriptable directConstruct(<directCallArgs>) {
0345: Scriptable newInstance = createObject(cx, scope);
0346: Object val = <body-name>(cx, scope, newInstance, <directCallArgs>);
0347: if (val instanceof Scriptable) {
0348: return (Scriptable) val;
0349: }
0350: return newInstance;
0351: }
0352: */
0353: cfw
0354: .startMethod(
0355: getDirectCtorName(ofn.fnode),
0356: getBodyMethodSignature(ofn.fnode),
0357: (short) (ClassFileWriter.ACC_STATIC | ClassFileWriter.ACC_PRIVATE));
0358:
0359: int argCount = ofn.fnode.getParamCount();
0360: int firstLocal = (4 + argCount * 3) + 1;
0361:
0362: cfw.addALoad(0); // this
0363: cfw.addALoad(1); // cx
0364: cfw.addALoad(2); // scope
0365: cfw.addInvoke(ByteCode.INVOKEVIRTUAL,
0366: "org/mozilla/javascript/BaseFunction", "createObject",
0367: "(Lorg/mozilla/javascript/Context;"
0368: + "Lorg/mozilla/javascript/Scriptable;"
0369: + ")Lorg/mozilla/javascript/Scriptable;");
0370: cfw.addAStore(firstLocal);
0371:
0372: cfw.addALoad(0);
0373: cfw.addALoad(1);
0374: cfw.addALoad(2);
0375: cfw.addALoad(firstLocal);
0376: for (int i = 0; i < argCount; i++) {
0377: cfw.addALoad(4 + (i * 3));
0378: cfw.addDLoad(5 + (i * 3));
0379: }
0380: cfw.addALoad(4 + argCount * 3);
0381: cfw.addInvoke(ByteCode.INVOKESTATIC, mainClassName,
0382: getBodyMethodName(ofn.fnode),
0383: getBodyMethodSignature(ofn.fnode));
0384: int exitLabel = cfw.acquireLabel();
0385: cfw.add(ByteCode.DUP); // make a copy of direct call result
0386: cfw.add(ByteCode.INSTANCEOF,
0387: "org/mozilla/javascript/Scriptable");
0388: cfw.add(ByteCode.IFEQ, exitLabel);
0389: // cast direct call result
0390: cfw
0391: .add(ByteCode.CHECKCAST,
0392: "org/mozilla/javascript/Scriptable");
0393: cfw.add(ByteCode.ARETURN);
0394: cfw.markLabel(exitLabel);
0395:
0396: cfw.addALoad(firstLocal);
0397: cfw.add(ByteCode.ARETURN);
0398:
0399: cfw.stopMethod((short) (firstLocal + 1));
0400: }
0401:
0402: static boolean isGenerator(ScriptOrFnNode node) {
0403: return (node.getType() == Token.FUNCTION)
0404: && ((FunctionNode) node).isGenerator();
0405: }
0406:
0407: // How dispatch to generators works:
0408: // Two methods are generated corresponding to a user-written generator.
0409: // One of these creates a generator object (NativeGenerator), which is
0410: // returned to the user. The other method contains all of the body code
0411: // of the generator.
0412: // When a user calls a generator, the call() method dispatches control to
0413: // to the method that creates the NativeGenerator object. Subsequently when
0414: // the user invokes .next(), .send() or any such method on the generator
0415: // object, the resumeGenerator() below dispatches the call to the
0416: // method corresponding to the generator body. As a matter of convention
0417: // the generator body is given the name of the generator activation function
0418: // appended by "_gen".
0419: private void generateResumeGenerator(ClassFileWriter cfw) {
0420: boolean hasGenerators = false;
0421: for (int i = 0; i < scriptOrFnNodes.length; i++) {
0422: if (isGenerator(scriptOrFnNodes[i]))
0423: hasGenerators = true;
0424: }
0425:
0426: // if there are no generators defined, we don't implement a
0427: // resumeGenerator(). The base class provides a default implementation.
0428: if (!hasGenerators)
0429: return;
0430:
0431: cfw
0432: .startMethod(
0433: "resumeGenerator",
0434: "(Lorg/mozilla/javascript/Context;"
0435: + "Lorg/mozilla/javascript/Scriptable;"
0436: + "ILjava/lang/Object;"
0437: + "Ljava/lang/Object;)Ljava/lang/Object;",
0438: (short) (ClassFileWriter.ACC_PUBLIC | ClassFileWriter.ACC_FINAL));
0439:
0440: // load arguments for dispatch to the corresponding *_gen method
0441: cfw.addALoad(0);
0442: cfw.addALoad(1);
0443: cfw.addALoad(2);
0444: cfw.addALoad(4);
0445: cfw.addALoad(5);
0446: cfw.addILoad(3);
0447:
0448: cfw.addLoadThis();
0449: cfw.add(ByteCode.GETFIELD, cfw.getClassName(), ID_FIELD_NAME,
0450: "I");
0451:
0452: int startSwitch = cfw.addTableSwitch(0,
0453: scriptOrFnNodes.length - 1);
0454: cfw.markTableSwitchDefault(startSwitch);
0455: int endlabel = cfw.acquireLabel();
0456:
0457: for (int i = 0; i < scriptOrFnNodes.length; i++) {
0458: ScriptOrFnNode n = scriptOrFnNodes[i];
0459: cfw.markTableSwitchCase(startSwitch, i, (short) 6);
0460: if (isGenerator(n)) {
0461: String type = "(" + mainClassSignature
0462: + "Lorg/mozilla/javascript/Context;"
0463: + "Lorg/mozilla/javascript/Scriptable;"
0464: + "Ljava/lang/Object;"
0465: + "Ljava/lang/Object;I)Ljava/lang/Object;";
0466: cfw.addInvoke(ByteCode.INVOKESTATIC, mainClassName,
0467: getBodyMethodName(n) + "_gen", type);
0468: cfw.add(ByteCode.ARETURN);
0469: } else {
0470: cfw.add(ByteCode.GOTO, endlabel);
0471: }
0472: }
0473:
0474: cfw.markLabel(endlabel);
0475: pushUndefined(cfw);
0476: cfw.add(ByteCode.ARETURN);
0477:
0478: // this method uses as many locals as there are arguments (hence 6)
0479: cfw.stopMethod((short) 6);
0480: }
0481:
0482: private void generateCallMethod(ClassFileWriter cfw) {
0483: cfw
0484: .startMethod(
0485: "call",
0486: "(Lorg/mozilla/javascript/Context;"
0487: + "Lorg/mozilla/javascript/Scriptable;"
0488: + "Lorg/mozilla/javascript/Scriptable;"
0489: + "[Ljava/lang/Object;)Ljava/lang/Object;",
0490: (short) (ClassFileWriter.ACC_PUBLIC | ClassFileWriter.ACC_FINAL));
0491:
0492: // Generate code for:
0493: // if (!ScriptRuntime.hasTopCall(cx)) {
0494: // return ScriptRuntime.doTopCall(this, cx, scope, thisObj, args);
0495: // }
0496:
0497: int nonTopCallLabel = cfw.acquireLabel();
0498: cfw.addALoad(1); //cx
0499: cfw.addInvoke(ByteCode.INVOKESTATIC,
0500: "org/mozilla/javascript/ScriptRuntime", "hasTopCall",
0501: "(Lorg/mozilla/javascript/Context;" + ")Z");
0502: cfw.add(ByteCode.IFNE, nonTopCallLabel);
0503: cfw.addALoad(0);
0504: cfw.addALoad(1);
0505: cfw.addALoad(2);
0506: cfw.addALoad(3);
0507: cfw.addALoad(4);
0508: cfw
0509: .addInvoke(ByteCode.INVOKESTATIC,
0510: "org/mozilla/javascript/ScriptRuntime",
0511: "doTopCall",
0512: "(Lorg/mozilla/javascript/Callable;"
0513: + "Lorg/mozilla/javascript/Context;"
0514: + "Lorg/mozilla/javascript/Scriptable;"
0515: + "Lorg/mozilla/javascript/Scriptable;"
0516: + "[Ljava/lang/Object;"
0517: + ")Ljava/lang/Object;");
0518: cfw.add(ByteCode.ARETURN);
0519: cfw.markLabel(nonTopCallLabel);
0520:
0521: // Now generate switch to call the real methods
0522: cfw.addALoad(0);
0523: cfw.addALoad(1);
0524: cfw.addALoad(2);
0525: cfw.addALoad(3);
0526: cfw.addALoad(4);
0527:
0528: int end = scriptOrFnNodes.length;
0529: boolean generateSwitch = (2 <= end);
0530:
0531: int switchStart = 0;
0532: int switchStackTop = 0;
0533: if (generateSwitch) {
0534: cfw.addLoadThis();
0535: cfw.add(ByteCode.GETFIELD, cfw.getClassName(),
0536: ID_FIELD_NAME, "I");
0537: // do switch from (1, end - 1) mapping 0 to
0538: // the default case
0539: switchStart = cfw.addTableSwitch(1, end - 1);
0540: }
0541:
0542: for (int i = 0; i != end; ++i) {
0543: ScriptOrFnNode n = scriptOrFnNodes[i];
0544: if (generateSwitch) {
0545: if (i == 0) {
0546: cfw.markTableSwitchDefault(switchStart);
0547: switchStackTop = cfw.getStackTop();
0548: } else {
0549: cfw.markTableSwitchCase(switchStart, i - 1,
0550: switchStackTop);
0551: }
0552: }
0553: if (n.getType() == Token.FUNCTION) {
0554: OptFunctionNode ofn = OptFunctionNode.get(n);
0555: if (ofn.isTargetOfDirectCall()) {
0556: int pcount = ofn.fnode.getParamCount();
0557: if (pcount != 0) {
0558: // loop invariant:
0559: // stack top == arguments array from addALoad4()
0560: for (int p = 0; p != pcount; ++p) {
0561: cfw.add(ByteCode.ARRAYLENGTH);
0562: cfw.addPush(p);
0563: int undefArg = cfw.acquireLabel();
0564: int beyond = cfw.acquireLabel();
0565: cfw.add(ByteCode.IF_ICMPLE, undefArg);
0566: // get array[p]
0567: cfw.addALoad(4);
0568: cfw.addPush(p);
0569: cfw.add(ByteCode.AALOAD);
0570: cfw.add(ByteCode.GOTO, beyond);
0571: cfw.markLabel(undefArg);
0572: pushUndefined(cfw);
0573: cfw.markLabel(beyond);
0574: // Only one push
0575: cfw.adjustStackTop(-1);
0576: cfw.addPush(0.0);
0577: // restore invariant
0578: cfw.addALoad(4);
0579: }
0580: }
0581: }
0582: }
0583: cfw.addInvoke(ByteCode.INVOKESTATIC, mainClassName,
0584: getBodyMethodName(n), getBodyMethodSignature(n));
0585: cfw.add(ByteCode.ARETURN);
0586: }
0587: cfw.stopMethod((short) 5);
0588: // 5: this, cx, scope, js this, args[]
0589: }
0590:
0591: private void generateMain(ClassFileWriter cfw) {
0592: cfw
0593: .startMethod(
0594: "main",
0595: "([Ljava/lang/String;)V",
0596: (short) (ClassFileWriter.ACC_PUBLIC | ClassFileWriter.ACC_STATIC));
0597:
0598: // load new ScriptImpl()
0599: cfw.add(ByteCode.NEW, cfw.getClassName());
0600: cfw.add(ByteCode.DUP);
0601: cfw.addInvoke(ByteCode.INVOKESPECIAL, cfw.getClassName(),
0602: "<init>", "()V");
0603: // load 'args'
0604: cfw.add(ByteCode.ALOAD_0);
0605: // Call mainMethodClass.main(Script script, String[] args)
0606: cfw
0607: .addInvoke(ByteCode.INVOKESTATIC, mainMethodClass,
0608: "main",
0609: "(Lorg/mozilla/javascript/Script;[Ljava/lang/String;)V");
0610: cfw.add(ByteCode.RETURN);
0611: // 1 = String[] args
0612: cfw.stopMethod((short) 1);
0613: }
0614:
0615: private void generateExecute(ClassFileWriter cfw) {
0616: cfw
0617: .startMethod(
0618: "exec",
0619: "(Lorg/mozilla/javascript/Context;"
0620: + "Lorg/mozilla/javascript/Scriptable;"
0621: + ")Ljava/lang/Object;",
0622: (short) (ClassFileWriter.ACC_PUBLIC | ClassFileWriter.ACC_FINAL));
0623:
0624: final int CONTEXT_ARG = 1;
0625: final int SCOPE_ARG = 2;
0626:
0627: cfw.addLoadThis();
0628: cfw.addALoad(CONTEXT_ARG);
0629: cfw.addALoad(SCOPE_ARG);
0630: cfw.add(ByteCode.DUP);
0631: cfw.add(ByteCode.ACONST_NULL);
0632: cfw
0633: .addInvoke(ByteCode.INVOKEVIRTUAL, cfw.getClassName(),
0634: "call", "(Lorg/mozilla/javascript/Context;"
0635: + "Lorg/mozilla/javascript/Scriptable;"
0636: + "Lorg/mozilla/javascript/Scriptable;"
0637: + "[Ljava/lang/Object;"
0638: + ")Ljava/lang/Object;");
0639:
0640: cfw.add(ByteCode.ARETURN);
0641: // 3 = this + context + scope
0642: cfw.stopMethod((short) 3);
0643: }
0644:
0645: private void generateScriptCtor(ClassFileWriter cfw) {
0646: cfw.startMethod("<init>", "()V", ClassFileWriter.ACC_PUBLIC);
0647:
0648: cfw.addLoadThis();
0649: cfw.addInvoke(ByteCode.INVOKESPECIAL, SUPER_CLASS_NAME,
0650: "<init>", "()V");
0651: // set id to 0
0652: cfw.addLoadThis();
0653: cfw.addPush(0);
0654: cfw.add(ByteCode.PUTFIELD, cfw.getClassName(), ID_FIELD_NAME,
0655: "I");
0656:
0657: cfw.add(ByteCode.RETURN);
0658: // 1 parameter = this
0659: cfw.stopMethod((short) 1);
0660: }
0661:
0662: private void generateFunctionConstructor(ClassFileWriter cfw) {
0663: final int SCOPE_ARG = 1;
0664: final int CONTEXT_ARG = 2;
0665: final int ID_ARG = 3;
0666:
0667: cfw.startMethod("<init>", FUNCTION_CONSTRUCTOR_SIGNATURE,
0668: ClassFileWriter.ACC_PUBLIC);
0669: cfw.addALoad(0);
0670: cfw.addInvoke(ByteCode.INVOKESPECIAL, SUPER_CLASS_NAME,
0671: "<init>", "()V");
0672:
0673: cfw.addLoadThis();
0674: cfw.addILoad(ID_ARG);
0675: cfw.add(ByteCode.PUTFIELD, cfw.getClassName(), ID_FIELD_NAME,
0676: "I");
0677:
0678: cfw.addLoadThis();
0679: cfw.addALoad(CONTEXT_ARG);
0680: cfw.addALoad(SCOPE_ARG);
0681:
0682: int start = (scriptOrFnNodes[0].getType() == Token.SCRIPT) ? 1
0683: : 0;
0684: int end = scriptOrFnNodes.length;
0685: if (start == end)
0686: throw badTree();
0687: boolean generateSwitch = (2 <= end - start);
0688:
0689: int switchStart = 0;
0690: int switchStackTop = 0;
0691: if (generateSwitch) {
0692: cfw.addILoad(ID_ARG);
0693: // do switch from (start + 1, end - 1) mapping start to
0694: // the default case
0695: switchStart = cfw.addTableSwitch(start + 1, end - 1);
0696: }
0697:
0698: for (int i = start; i != end; ++i) {
0699: if (generateSwitch) {
0700: if (i == start) {
0701: cfw.markTableSwitchDefault(switchStart);
0702: switchStackTop = cfw.getStackTop();
0703: } else {
0704: cfw.markTableSwitchCase(switchStart, i - 1 - start,
0705: switchStackTop);
0706: }
0707: }
0708: OptFunctionNode ofn = OptFunctionNode
0709: .get(scriptOrFnNodes[i]);
0710: cfw.addInvoke(ByteCode.INVOKEVIRTUAL, mainClassName,
0711: getFunctionInitMethodName(ofn),
0712: FUNCTION_INIT_SIGNATURE);
0713: cfw.add(ByteCode.RETURN);
0714: }
0715:
0716: // 4 = this + scope + context + id
0717: cfw.stopMethod((short) 4);
0718: }
0719:
0720: private void generateFunctionInit(ClassFileWriter cfw,
0721: OptFunctionNode ofn) {
0722: final int CONTEXT_ARG = 1;
0723: final int SCOPE_ARG = 2;
0724: cfw
0725: .startMethod(
0726: getFunctionInitMethodName(ofn),
0727: FUNCTION_INIT_SIGNATURE,
0728: (short) (ClassFileWriter.ACC_PRIVATE | ClassFileWriter.ACC_FINAL));
0729:
0730: // Call NativeFunction.initScriptFunction
0731: cfw.addLoadThis();
0732: cfw.addALoad(CONTEXT_ARG);
0733: cfw.addALoad(SCOPE_ARG);
0734: cfw.addInvoke(ByteCode.INVOKEVIRTUAL,
0735: "org/mozilla/javascript/NativeFunction",
0736: "initScriptFunction",
0737: "(Lorg/mozilla/javascript/Context;"
0738: + "Lorg/mozilla/javascript/Scriptable;" + ")V");
0739:
0740: // precompile all regexp literals
0741: int regexpCount = ofn.fnode.getRegexpCount();
0742: if (regexpCount != 0) {
0743: cfw.addLoadThis();
0744: pushRegExpArray(cfw, ofn.fnode, CONTEXT_ARG, SCOPE_ARG);
0745: cfw.add(ByteCode.PUTFIELD, mainClassName,
0746: REGEXP_ARRAY_FIELD_NAME, REGEXP_ARRAY_FIELD_TYPE);
0747: }
0748:
0749: cfw.add(ByteCode.RETURN);
0750: // 3 = (scriptThis/functionRef) + scope + context
0751: cfw.stopMethod((short) 3);
0752: }
0753:
0754: private void generateNativeFunctionOverrides(ClassFileWriter cfw,
0755: String encodedSource) {
0756: // Override NativeFunction.getLanguageVersion() with
0757: // public int getLanguageVersion() { return <version-constant>; }
0758:
0759: cfw.startMethod("getLanguageVersion", "()I",
0760: ClassFileWriter.ACC_PUBLIC);
0761:
0762: cfw.addPush(compilerEnv.getLanguageVersion());
0763: cfw.add(ByteCode.IRETURN);
0764:
0765: // 1: this and no argument or locals
0766: cfw.stopMethod((short) 1);
0767:
0768: // The rest of NativeFunction overrides require specific code for each
0769: // script/function id
0770:
0771: final int Do_getFunctionName = 0;
0772: final int Do_getParamCount = 1;
0773: final int Do_getParamAndVarCount = 2;
0774: final int Do_getParamOrVarName = 3;
0775: final int Do_getEncodedSource = 4;
0776: final int Do_getParamOrVarConst = 5;
0777: final int SWITCH_COUNT = 6;
0778:
0779: for (int methodIndex = 0; methodIndex != SWITCH_COUNT; ++methodIndex) {
0780: if (methodIndex == Do_getEncodedSource
0781: && encodedSource == null) {
0782: continue;
0783: }
0784:
0785: // Generate:
0786: // prologue;
0787: // switch over function id to implement function-specific action
0788: // epilogue
0789:
0790: short methodLocals;
0791: switch (methodIndex) {
0792: case Do_getFunctionName:
0793: methodLocals = 1; // Only this
0794: cfw.startMethod("getFunctionName",
0795: "()Ljava/lang/String;",
0796: ClassFileWriter.ACC_PUBLIC);
0797: break;
0798: case Do_getParamCount:
0799: methodLocals = 1; // Only this
0800: cfw.startMethod("getParamCount", "()I",
0801: ClassFileWriter.ACC_PUBLIC);
0802: break;
0803: case Do_getParamAndVarCount:
0804: methodLocals = 1; // Only this
0805: cfw.startMethod("getParamAndVarCount", "()I",
0806: ClassFileWriter.ACC_PUBLIC);
0807: break;
0808: case Do_getParamOrVarName:
0809: methodLocals = 1 + 1; // this + paramOrVarIndex
0810: cfw.startMethod("getParamOrVarName",
0811: "(I)Ljava/lang/String;",
0812: ClassFileWriter.ACC_PUBLIC);
0813: break;
0814: case Do_getParamOrVarConst:
0815: methodLocals = 1 + 1 + 1; // this + paramOrVarName
0816: cfw.startMethod("getParamOrVarConst", "(I)Z",
0817: ClassFileWriter.ACC_PUBLIC);
0818: break;
0819: case Do_getEncodedSource:
0820: methodLocals = 1; // Only this
0821: cfw.startMethod("getEncodedSource",
0822: "()Ljava/lang/String;",
0823: ClassFileWriter.ACC_PUBLIC);
0824: cfw.addPush(encodedSource);
0825: break;
0826: default:
0827: throw Kit.codeBug();
0828: }
0829:
0830: int count = scriptOrFnNodes.length;
0831:
0832: int switchStart = 0;
0833: int switchStackTop = 0;
0834: if (count > 1) {
0835: // Generate switch but only if there is more then one
0836: // script/function
0837: cfw.addLoadThis();
0838: cfw.add(ByteCode.GETFIELD, cfw.getClassName(),
0839: ID_FIELD_NAME, "I");
0840:
0841: // do switch from 1 .. count - 1 mapping 0 to the default case
0842: switchStart = cfw.addTableSwitch(1, count - 1);
0843: }
0844:
0845: for (int i = 0; i != count; ++i) {
0846: ScriptOrFnNode n = scriptOrFnNodes[i];
0847: if (i == 0) {
0848: if (count > 1) {
0849: cfw.markTableSwitchDefault(switchStart);
0850: switchStackTop = cfw.getStackTop();
0851: }
0852: } else {
0853: cfw.markTableSwitchCase(switchStart, i - 1,
0854: switchStackTop);
0855: }
0856:
0857: // Impelemnet method-specific switch code
0858: switch (methodIndex) {
0859: case Do_getFunctionName:
0860: // Push function name
0861: if (n.getType() == Token.SCRIPT) {
0862: cfw.addPush("");
0863: } else {
0864: String name = ((FunctionNode) n)
0865: .getFunctionName();
0866: cfw.addPush(name);
0867: }
0868: cfw.add(ByteCode.ARETURN);
0869: break;
0870:
0871: case Do_getParamCount:
0872: // Push number of defined parameters
0873: cfw.addPush(n.getParamCount());
0874: cfw.add(ByteCode.IRETURN);
0875: break;
0876:
0877: case Do_getParamAndVarCount:
0878: // Push number of defined parameters and declared variables
0879: cfw.addPush(n.getParamAndVarCount());
0880: cfw.add(ByteCode.IRETURN);
0881: break;
0882:
0883: case Do_getParamOrVarName:
0884: // Push name of parameter using another switch
0885: // over paramAndVarCount
0886: int paramAndVarCount = n.getParamAndVarCount();
0887: if (paramAndVarCount == 0) {
0888: // The runtime should never call the method in this
0889: // case but to make bytecode verifier happy return null
0890: // as throwing execption takes more code
0891: cfw.add(ByteCode.ACONST_NULL);
0892: cfw.add(ByteCode.ARETURN);
0893: } else if (paramAndVarCount == 1) {
0894: // As above do not check for valid index but always
0895: // return the name of the first param
0896: cfw.addPush(n.getParamOrVarName(0));
0897: cfw.add(ByteCode.ARETURN);
0898: } else {
0899: // Do switch over getParamOrVarName
0900: cfw.addILoad(1); // param or var index
0901: // do switch from 1 .. paramAndVarCount - 1 mapping 0
0902: // to the default case
0903: int paramSwitchStart = cfw.addTableSwitch(1,
0904: paramAndVarCount - 1);
0905: for (int j = 0; j != paramAndVarCount; ++j) {
0906: if (cfw.getStackTop() != 0)
0907: Kit.codeBug();
0908: String s = n.getParamOrVarName(j);
0909: if (j == 0) {
0910: cfw
0911: .markTableSwitchDefault(paramSwitchStart);
0912: } else {
0913: cfw.markTableSwitchCase(
0914: paramSwitchStart, j - 1, 0);
0915: }
0916: cfw.addPush(s);
0917: cfw.add(ByteCode.ARETURN);
0918: }
0919: }
0920: break;
0921:
0922: case Do_getParamOrVarConst:
0923: // Push name of parameter using another switch
0924: // over paramAndVarCount
0925: paramAndVarCount = n.getParamAndVarCount();
0926: boolean[] constness = n.getParamAndVarConst();
0927: if (paramAndVarCount == 0) {
0928: // The runtime should never call the method in this
0929: // case but to make bytecode verifier happy return null
0930: // as throwing execption takes more code
0931: cfw.add(ByteCode.ICONST_0);
0932: cfw.add(ByteCode.IRETURN);
0933: } else if (paramAndVarCount == 1) {
0934: // As above do not check for valid index but always
0935: // return the name of the first param
0936: cfw.addPush(constness[0]);
0937: cfw.add(ByteCode.IRETURN);
0938: } else {
0939: // Do switch over getParamOrVarName
0940: cfw.addILoad(1); // param or var index
0941: // do switch from 1 .. paramAndVarCount - 1 mapping 0
0942: // to the default case
0943: int paramSwitchStart = cfw.addTableSwitch(1,
0944: paramAndVarCount - 1);
0945: for (int j = 0; j != paramAndVarCount; ++j) {
0946: if (cfw.getStackTop() != 0)
0947: Kit.codeBug();
0948: if (j == 0) {
0949: cfw
0950: .markTableSwitchDefault(paramSwitchStart);
0951: } else {
0952: cfw.markTableSwitchCase(
0953: paramSwitchStart, j - 1, 0);
0954: }
0955: cfw.addPush(constness[j]);
0956: cfw.add(ByteCode.IRETURN);
0957: }
0958: }
0959: break;
0960:
0961: case Do_getEncodedSource:
0962: // Push number encoded source start and end
0963: // to prepare for encodedSource.substring(start, end)
0964: cfw.addPush(n.getEncodedSourceStart());
0965: cfw.addPush(n.getEncodedSourceEnd());
0966: cfw.addInvoke(ByteCode.INVOKEVIRTUAL,
0967: "java/lang/String", "substring",
0968: "(II)Ljava/lang/String;");
0969: cfw.add(ByteCode.ARETURN);
0970: break;
0971:
0972: default:
0973: throw Kit.codeBug();
0974: }
0975: }
0976:
0977: cfw.stopMethod(methodLocals);
0978: }
0979: }
0980:
0981: private void emitRegExpInit(ClassFileWriter cfw) {
0982: // precompile all regexp literals
0983:
0984: int totalRegCount = 0;
0985: for (int i = 0; i != scriptOrFnNodes.length; ++i) {
0986: totalRegCount += scriptOrFnNodes[i].getRegexpCount();
0987: }
0988: if (totalRegCount == 0) {
0989: return;
0990: }
0991:
0992: cfw
0993: .startMethod(
0994: REGEXP_INIT_METHOD_NAME,
0995: REGEXP_INIT_METHOD_SIGNATURE,
0996: (short) (ClassFileWriter.ACC_STATIC
0997: | ClassFileWriter.ACC_PRIVATE | ClassFileWriter.ACC_SYNCHRONIZED));
0998: cfw
0999: .addField(
1000: "_reInitDone",
1001: "Z",
1002: (short) (ClassFileWriter.ACC_STATIC | ClassFileWriter.ACC_PRIVATE));
1003: cfw.add(ByteCode.GETSTATIC, mainClassName, "_reInitDone", "Z");
1004: int doInit = cfw.acquireLabel();
1005: cfw.add(ByteCode.IFEQ, doInit);
1006: cfw.add(ByteCode.RETURN);
1007: cfw.markLabel(doInit);
1008:
1009: for (int i = 0; i != scriptOrFnNodes.length; ++i) {
1010: ScriptOrFnNode n = scriptOrFnNodes[i];
1011: int regCount = n.getRegexpCount();
1012: for (int j = 0; j != regCount; ++j) {
1013: String reFieldName = getCompiledRegexpName(n, j);
1014: String reFieldType = "Ljava/lang/Object;";
1015: String reString = n.getRegexpString(j);
1016: String reFlags = n.getRegexpFlags(j);
1017: cfw
1018: .addField(
1019: reFieldName,
1020: reFieldType,
1021: (short) (ClassFileWriter.ACC_STATIC | ClassFileWriter.ACC_PRIVATE));
1022: cfw.addALoad(0); // proxy
1023: cfw.addALoad(1); // context
1024: cfw.addPush(reString);
1025: if (reFlags == null) {
1026: cfw.add(ByteCode.ACONST_NULL);
1027: } else {
1028: cfw.addPush(reFlags);
1029: }
1030: cfw
1031: .addInvoke(
1032: ByteCode.INVOKEINTERFACE,
1033: "org/mozilla/javascript/RegExpProxy",
1034: "compileRegExp",
1035: "(Lorg/mozilla/javascript/Context;"
1036: + "Ljava/lang/String;Ljava/lang/String;"
1037: + ")Ljava/lang/Object;");
1038: cfw.add(ByteCode.PUTSTATIC, mainClassName, reFieldName,
1039: reFieldType);
1040: }
1041: }
1042:
1043: cfw.addPush(1);
1044: cfw.add(ByteCode.PUTSTATIC, mainClassName, "_reInitDone", "Z");
1045: cfw.add(ByteCode.RETURN);
1046: cfw.stopMethod((short) 2);
1047: }
1048:
1049: private void emitConstantDudeInitializers(ClassFileWriter cfw) {
1050: int N = itsConstantListSize;
1051: if (N == 0)
1052: return;
1053:
1054: cfw
1055: .startMethod(
1056: "<clinit>",
1057: "()V",
1058: (short) (ClassFileWriter.ACC_STATIC | ClassFileWriter.ACC_FINAL));
1059:
1060: double[] array = itsConstantList;
1061: for (int i = 0; i != N; ++i) {
1062: double num = array[i];
1063: String constantName = "_k" + i;
1064: String constantType = getStaticConstantWrapperType(num);
1065: cfw
1066: .addField(
1067: constantName,
1068: constantType,
1069: (short) (ClassFileWriter.ACC_STATIC | ClassFileWriter.ACC_PRIVATE));
1070: int inum = (int) num;
1071: if (inum == num) {
1072: cfw.add(ByteCode.NEW, "java/lang/Integer");
1073: cfw.add(ByteCode.DUP);
1074: cfw.addPush(inum);
1075: cfw.addInvoke(ByteCode.INVOKESPECIAL,
1076: "java/lang/Integer", "<init>", "(I)V");
1077: } else {
1078: cfw.addPush(num);
1079: addDoubleWrap(cfw);
1080: }
1081: cfw.add(ByteCode.PUTSTATIC, mainClassName, constantName,
1082: constantType);
1083: }
1084:
1085: cfw.add(ByteCode.RETURN);
1086: cfw.stopMethod((short) 0);
1087: }
1088:
1089: void pushRegExpArray(ClassFileWriter cfw, ScriptOrFnNode n,
1090: int contextArg, int scopeArg) {
1091: int regexpCount = n.getRegexpCount();
1092: if (regexpCount == 0)
1093: throw badTree();
1094:
1095: cfw.addPush(regexpCount);
1096: cfw.add(ByteCode.ANEWARRAY, "java/lang/Object");
1097:
1098: cfw.addALoad(contextArg);
1099: cfw.addInvoke(ByteCode.INVOKESTATIC,
1100: "org/mozilla/javascript/ScriptRuntime",
1101: "checkRegExpProxy", "(Lorg/mozilla/javascript/Context;"
1102: + ")Lorg/mozilla/javascript/RegExpProxy;");
1103: // Stack: proxy, array
1104: cfw.add(ByteCode.DUP);
1105: cfw.addALoad(contextArg);
1106: cfw.addInvoke(ByteCode.INVOKESTATIC, mainClassName,
1107: REGEXP_INIT_METHOD_NAME, REGEXP_INIT_METHOD_SIGNATURE);
1108: for (int i = 0; i != regexpCount; ++i) {
1109: // Stack: proxy, array
1110: cfw.add(ByteCode.DUP2);
1111: cfw.addALoad(contextArg);
1112: cfw.addALoad(scopeArg);
1113: cfw.add(ByteCode.GETSTATIC, mainClassName,
1114: getCompiledRegexpName(n, i), "Ljava/lang/Object;");
1115: // Stack: compiledRegExp, scope, cx, proxy, array, proxy, array
1116: cfw.addInvoke(ByteCode.INVOKEINTERFACE,
1117: "org/mozilla/javascript/RegExpProxy", "wrapRegExp",
1118: "(Lorg/mozilla/javascript/Context;"
1119: + "Lorg/mozilla/javascript/Scriptable;"
1120: + "Ljava/lang/Object;"
1121: + ")Lorg/mozilla/javascript/Scriptable;");
1122: // Stack: wrappedRegExp, array, proxy, array
1123: cfw.addPush(i);
1124: cfw.add(ByteCode.SWAP);
1125: cfw.add(ByteCode.AASTORE);
1126: // Stack: proxy, array
1127: }
1128: // remove proxy
1129: cfw.add(ByteCode.POP);
1130: }
1131:
1132: void pushNumberAsObject(ClassFileWriter cfw, double num) {
1133: if (num == 0.0) {
1134: if (1 / num > 0) {
1135: // +0.0
1136: cfw.add(ByteCode.GETSTATIC,
1137: "org/mozilla/javascript/optimizer/OptRuntime",
1138: "zeroObj", "Ljava/lang/Double;");
1139: } else {
1140: cfw.addPush(num);
1141: addDoubleWrap(cfw);
1142: }
1143:
1144: } else if (num == 1.0) {
1145: cfw.add(ByteCode.GETSTATIC,
1146: "org/mozilla/javascript/optimizer/OptRuntime",
1147: "oneObj", "Ljava/lang/Double;");
1148: return;
1149:
1150: } else if (num == -1.0) {
1151: cfw.add(ByteCode.GETSTATIC,
1152: "org/mozilla/javascript/optimizer/OptRuntime",
1153: "minusOneObj", "Ljava/lang/Double;");
1154:
1155: } else if (num != num) {
1156: cfw.add(ByteCode.GETSTATIC,
1157: "org/mozilla/javascript/ScriptRuntime", "NaNobj",
1158: "Ljava/lang/Double;");
1159:
1160: } else if (itsConstantListSize >= 2000) {
1161: // There appears to be a limit in the JVM on either the number
1162: // of static fields in a class or the size of the class
1163: // initializer. Either way, we can't have any more than 2000
1164: // statically init'd constants.
1165: cfw.addPush(num);
1166: addDoubleWrap(cfw);
1167:
1168: } else {
1169: int N = itsConstantListSize;
1170: int index = 0;
1171: if (N == 0) {
1172: itsConstantList = new double[64];
1173: } else {
1174: double[] array = itsConstantList;
1175: while (index != N && array[index] != num) {
1176: ++index;
1177: }
1178: if (N == array.length) {
1179: array = new double[N * 2];
1180: System.arraycopy(itsConstantList, 0, array, 0, N);
1181: itsConstantList = array;
1182: }
1183: }
1184: if (index == N) {
1185: itsConstantList[N] = num;
1186: itsConstantListSize = N + 1;
1187: }
1188: String constantName = "_k" + index;
1189: String constantType = getStaticConstantWrapperType(num);
1190: cfw.add(ByteCode.GETSTATIC, mainClassName, constantName,
1191: constantType);
1192: }
1193: }
1194:
1195: private static void addDoubleWrap(ClassFileWriter cfw) {
1196: cfw.addInvoke(ByteCode.INVOKESTATIC,
1197: "org/mozilla/javascript/optimizer/OptRuntime",
1198: "wrapDouble", "(D)Ljava/lang/Double;");
1199: }
1200:
1201: private static String getStaticConstantWrapperType(double num) {
1202: int inum = (int) num;
1203: if (inum == num) {
1204: return "Ljava/lang/Integer;";
1205: } else {
1206: return "Ljava/lang/Double;";
1207: }
1208: }
1209:
1210: static void pushUndefined(ClassFileWriter cfw) {
1211: cfw.add(ByteCode.GETSTATIC, "org/mozilla/javascript/Undefined",
1212: "instance", "Ljava/lang/Object;");
1213: }
1214:
1215: int getIndex(ScriptOrFnNode n) {
1216: return scriptOrFnIndexes.getExisting(n);
1217: }
1218:
1219: static String getDirectTargetFieldName(int i) {
1220: return "_dt" + i;
1221: }
1222:
1223: String getDirectCtorName(ScriptOrFnNode n) {
1224: return "_n" + getIndex(n);
1225: }
1226:
1227: String getBodyMethodName(ScriptOrFnNode n) {
1228: return "_c" + getIndex(n);
1229: }
1230:
1231: String getBodyMethodSignature(ScriptOrFnNode n) {
1232: StringBuffer sb = new StringBuffer();
1233: sb.append('(');
1234: sb.append(mainClassSignature);
1235: sb.append("Lorg/mozilla/javascript/Context;"
1236: + "Lorg/mozilla/javascript/Scriptable;"
1237: + "Lorg/mozilla/javascript/Scriptable;");
1238: if (n.getType() == Token.FUNCTION) {
1239: OptFunctionNode ofn = OptFunctionNode.get(n);
1240: if (ofn.isTargetOfDirectCall()) {
1241: int pCount = ofn.fnode.getParamCount();
1242: for (int i = 0; i != pCount; i++) {
1243: sb.append("Ljava/lang/Object;D");
1244: }
1245: }
1246: }
1247: sb.append("[Ljava/lang/Object;)Ljava/lang/Object;");
1248: return sb.toString();
1249: }
1250:
1251: String getFunctionInitMethodName(OptFunctionNode ofn) {
1252: return "_i" + getIndex(ofn.fnode);
1253: }
1254:
1255: String getCompiledRegexpName(ScriptOrFnNode n, int regexpIndex) {
1256: return "_re" + getIndex(n) + "_" + regexpIndex;
1257: }
1258:
1259: static RuntimeException badTree() {
1260: throw new RuntimeException("Bad tree in codegen");
1261: }
1262:
1263: void setMainMethodClass(String className) {
1264: mainMethodClass = className;
1265: }
1266:
1267: static final String DEFAULT_MAIN_METHOD_CLASS = "org.mozilla.javascript.optimizer.OptRuntime";
1268:
1269: private static final String SUPER_CLASS_NAME = "org.mozilla.javascript.NativeFunction";
1270:
1271: static final String DIRECT_CALL_PARENT_FIELD = "_dcp";
1272: private static final String ID_FIELD_NAME = "_id";
1273:
1274: private static final String REGEXP_INIT_METHOD_NAME = "_reInit";
1275: private static final String REGEXP_INIT_METHOD_SIGNATURE = "(Lorg/mozilla/javascript/RegExpProxy;"
1276: + "Lorg/mozilla/javascript/Context;" + ")V";
1277: static final String REGEXP_ARRAY_FIELD_NAME = "_re";
1278: static final String REGEXP_ARRAY_FIELD_TYPE = "[Ljava/lang/Object;";
1279:
1280: static final String FUNCTION_INIT_SIGNATURE = "(Lorg/mozilla/javascript/Context;"
1281: + "Lorg/mozilla/javascript/Scriptable;" + ")V";
1282:
1283: static final String FUNCTION_CONSTRUCTOR_SIGNATURE = "(Lorg/mozilla/javascript/Scriptable;"
1284: + "Lorg/mozilla/javascript/Context;I)V";
1285:
1286: private static final Object globalLock = new Object();
1287: private static int globalSerialClassCounter;
1288:
1289: private CompilerEnvirons compilerEnv;
1290:
1291: private ObjArray directCallTargets;
1292: ScriptOrFnNode[] scriptOrFnNodes;
1293: private ObjToIntMap scriptOrFnIndexes;
1294:
1295: private String mainMethodClass = DEFAULT_MAIN_METHOD_CLASS;
1296:
1297: String mainClassName;
1298: String mainClassSignature;
1299:
1300: private double[] itsConstantList;
1301: private int itsConstantListSize;
1302: }
1303:
1304: class BodyCodegen {
1305: void generateBodyCode() {
1306: isGenerator = Codegen.isGenerator(scriptOrFn);
1307:
1308: // generate the body of the current function or script object
1309: initBodyGeneration();
1310:
1311: if (isGenerator) {
1312:
1313: // All functions in the generated bytecode have a unique name. Every
1314: // generator has a unique prefix followed by _gen
1315: String type = "(" + codegen.mainClassSignature
1316: + "Lorg/mozilla/javascript/Context;"
1317: + "Lorg/mozilla/javascript/Scriptable;"
1318: + "Ljava/lang/Object;"
1319: + "Ljava/lang/Object;I)Ljava/lang/Object;";
1320: cfw
1321: .startMethod(
1322: codegen.getBodyMethodName(scriptOrFn)
1323: + "_gen",
1324: type,
1325: (short) (ClassFileWriter.ACC_STATIC | ClassFileWriter.ACC_PRIVATE));
1326: } else {
1327: cfw
1328: .startMethod(
1329: codegen.getBodyMethodName(scriptOrFn),
1330: codegen.getBodyMethodSignature(scriptOrFn),
1331: (short) (ClassFileWriter.ACC_STATIC | ClassFileWriter.ACC_PRIVATE));
1332: }
1333:
1334: generatePrologue();
1335: Node treeTop;
1336: if (fnCurrent != null) {
1337: treeTop = scriptOrFn.getLastChild();
1338: } else {
1339: treeTop = scriptOrFn;
1340: }
1341: generateStatement(treeTop);
1342: generateEpilogue();
1343:
1344: cfw.stopMethod((short) (localsMax + 1));
1345:
1346: if (isGenerator) {
1347: // generate the user visible method which when invoked will
1348: // return a generator object
1349: generateGenerator();
1350: }
1351: }
1352:
1353: // This creates a the user-facing function that returns a NativeGenerator
1354: // object.
1355: private void generateGenerator() {
1356: cfw
1357: .startMethod(
1358: codegen.getBodyMethodName(scriptOrFn),
1359: codegen.getBodyMethodSignature(scriptOrFn),
1360: (short) (ClassFileWriter.ACC_STATIC | ClassFileWriter.ACC_PRIVATE));
1361:
1362: initBodyGeneration();
1363: argsLocal = firstFreeLocal++;
1364: localsMax = firstFreeLocal;
1365:
1366: // get top level scope
1367: if (fnCurrent != null
1368: && !inDirectCallFunction
1369: && (!compilerEnv.isUseDynamicScope() || fnCurrent.fnode
1370: .getIgnoreDynamicScope())) {
1371: // Unless we're either in a direct call or using dynamic scope,
1372: // use the enclosing scope of the function as our variable object.
1373: cfw.addALoad(funObjLocal);
1374: cfw.addInvoke(ByteCode.INVOKEINTERFACE,
1375: "org/mozilla/javascript/Scriptable",
1376: "getParentScope",
1377: "()Lorg/mozilla/javascript/Scriptable;");
1378: cfw.addAStore(variableObjectLocal);
1379: }
1380:
1381: // generators are forced to have an activation record
1382: cfw.addALoad(funObjLocal);
1383: cfw.addALoad(variableObjectLocal);
1384: cfw.addALoad(argsLocal);
1385: addScriptRuntimeInvoke("createFunctionActivation",
1386: "(Lorg/mozilla/javascript/NativeFunction;"
1387: + "Lorg/mozilla/javascript/Scriptable;"
1388: + "[Ljava/lang/Object;"
1389: + ")Lorg/mozilla/javascript/Scriptable;");
1390: cfw.addAStore(variableObjectLocal);
1391:
1392: // create a function object
1393: cfw.add(ByteCode.NEW, codegen.mainClassName);
1394: // Call function constructor
1395: cfw.add(ByteCode.DUP);
1396: cfw.addALoad(variableObjectLocal);
1397: cfw.addALoad(contextLocal); // load 'cx'
1398: cfw.addPush(scriptOrFnIndex);
1399: cfw.addInvoke(ByteCode.INVOKESPECIAL, codegen.mainClassName,
1400: "<init>", Codegen.FUNCTION_CONSTRUCTOR_SIGNATURE);
1401:
1402: // Init mainScript field
1403: cfw.add(ByteCode.DUP);
1404: if (isTopLevel)
1405: Kit.codeBug(); // Only functions can be generators
1406: cfw.add(ByteCode.ALOAD_0);
1407: cfw.add(ByteCode.GETFIELD, codegen.mainClassName,
1408: Codegen.DIRECT_CALL_PARENT_FIELD,
1409: codegen.mainClassSignature);
1410: cfw.add(ByteCode.PUTFIELD, codegen.mainClassName,
1411: Codegen.DIRECT_CALL_PARENT_FIELD,
1412: codegen.mainClassSignature);
1413:
1414: generateNestedFunctionInits();
1415:
1416: // create the NativeGenerator object that we return
1417: cfw.addALoad(variableObjectLocal);
1418: cfw.addALoad(this ObjLocal);
1419: cfw.addLoadConstant(maxLocals);
1420: cfw.addLoadConstant(maxStack);
1421: addOptRuntimeInvoke("createNativeGenerator",
1422: "(Lorg/mozilla/javascript/NativeFunction;"
1423: + "Lorg/mozilla/javascript/Scriptable;"
1424: + "Lorg/mozilla/javascript/Scriptable;II"
1425: + ")Lorg/mozilla/javascript/Scriptable;");
1426:
1427: cfw.add(ByteCode.ARETURN);
1428: cfw.stopMethod((short) (localsMax + 1));
1429: }
1430:
1431: private void generateNestedFunctionInits() {
1432: int functionCount = scriptOrFn.getFunctionCount();
1433: for (int i = 0; i != functionCount; i++) {
1434: OptFunctionNode ofn = OptFunctionNode.get(scriptOrFn, i);
1435: if (ofn.fnode.getFunctionType() == FunctionNode.FUNCTION_STATEMENT) {
1436: visitFunction(ofn, FunctionNode.FUNCTION_STATEMENT);
1437: }
1438: }
1439: }
1440:
1441: private void initBodyGeneration() {
1442: isTopLevel = (scriptOrFn == codegen.scriptOrFnNodes[0]);
1443:
1444: varRegisters = null;
1445: if (scriptOrFn.getType() == Token.FUNCTION) {
1446: fnCurrent = OptFunctionNode.get(scriptOrFn);
1447: hasVarsInRegs = !fnCurrent.fnode.requiresActivation();
1448: if (hasVarsInRegs) {
1449: int n = fnCurrent.fnode.getParamAndVarCount();
1450: if (n != 0) {
1451: varRegisters = new short[n];
1452: }
1453: }
1454: inDirectCallFunction = fnCurrent.isTargetOfDirectCall();
1455: if (inDirectCallFunction && !hasVarsInRegs)
1456: Codegen.badTree();
1457: } else {
1458: fnCurrent = null;
1459: hasVarsInRegs = false;
1460: inDirectCallFunction = false;
1461: }
1462:
1463: locals = new int[MAX_LOCALS];
1464:
1465: funObjLocal = 0;
1466: contextLocal = 1;
1467: variableObjectLocal = 2;
1468: this ObjLocal = 3;
1469: localsMax = (short) 4; // number of parms + "this"
1470: firstFreeLocal = 4;
1471:
1472: popvLocal = -1;
1473: argsLocal = -1;
1474: itsZeroArgArray = -1;
1475: itsOneArgArray = -1;
1476: scriptRegexpLocal = -1;
1477: epilogueLabel = -1;
1478: enterAreaStartLabel = -1;
1479: generatorStateLocal = -1;
1480: }
1481:
1482: /**
1483: * Generate the prologue for a function or script.
1484: */
1485: private void generatePrologue() {
1486: if (inDirectCallFunction) {
1487: int directParameterCount = scriptOrFn.getParamCount();
1488: // 0 is reserved for function Object 'this'
1489: // 1 is reserved for context
1490: // 2 is reserved for parentScope
1491: // 3 is reserved for script 'this'
1492: if (firstFreeLocal != 4)
1493: Kit.codeBug();
1494: for (int i = 0; i != directParameterCount; ++i) {
1495: varRegisters[i] = firstFreeLocal;
1496: // 3 is 1 for Object parm and 2 for double parm
1497: firstFreeLocal += 3;
1498: }
1499: if (!fnCurrent.getParameterNumberContext()) {
1500: // make sure that all parameters are objects
1501: itsForcedObjectParameters = true;
1502: for (int i = 0; i != directParameterCount; ++i) {
1503: short reg = varRegisters[i];
1504: cfw.addALoad(reg);
1505: cfw.add(ByteCode.GETSTATIC, "java/lang/Void",
1506: "TYPE", "Ljava/lang/Class;");
1507: int isObjectLabel = cfw.acquireLabel();
1508: cfw.add(ByteCode.IF_ACMPNE, isObjectLabel);
1509: cfw.addDLoad(reg + 1);
1510: addDoubleWrap();
1511: cfw.addAStore(reg);
1512: cfw.markLabel(isObjectLabel);
1513: }
1514: }
1515: }
1516:
1517: if (fnCurrent != null
1518: && !inDirectCallFunction
1519: && (!compilerEnv.isUseDynamicScope() || fnCurrent.fnode
1520: .getIgnoreDynamicScope())) {
1521: // Unless we're either in a direct call or using dynamic scope,
1522: // use the enclosing scope of the function as our variable object.
1523: cfw.addALoad(funObjLocal);
1524: cfw.addInvoke(ByteCode.INVOKEINTERFACE,
1525: "org/mozilla/javascript/Scriptable",
1526: "getParentScope",
1527: "()Lorg/mozilla/javascript/Scriptable;");
1528: cfw.addAStore(variableObjectLocal);
1529: }
1530:
1531: // reserve 'args[]'
1532: argsLocal = firstFreeLocal++;
1533: localsMax = firstFreeLocal;
1534:
1535: // Generate Generator specific prelude
1536: if (isGenerator) {
1537:
1538: // reserve 'args[]'
1539: operationLocal = firstFreeLocal++;
1540: localsMax = firstFreeLocal;
1541:
1542: // Local 3 is a reference to a GeneratorState object. The rest
1543: // of codegen expects local 3 to be a reference to the thisObj.
1544: // So move the value in local 3 to generatorStateLocal, and load
1545: // the saved thisObj from the GeneratorState object.
1546: cfw.addALoad(this ObjLocal);
1547: generatorStateLocal = firstFreeLocal++;
1548: localsMax = firstFreeLocal;
1549: cfw.add(ByteCode.CHECKCAST,
1550: OptRuntime.GeneratorState.CLASS_NAME);
1551: cfw.add(ByteCode.DUP);
1552: cfw.addAStore(generatorStateLocal);
1553: cfw.add(ByteCode.GETFIELD,
1554: OptRuntime.GeneratorState.CLASS_NAME,
1555: OptRuntime.GeneratorState.this Obj_NAME,
1556: OptRuntime.GeneratorState.this Obj_TYPE);
1557: cfw.addAStore(this ObjLocal);
1558:
1559: if (epilogueLabel == -1) {
1560: epilogueLabel = cfw.acquireLabel();
1561: }
1562:
1563: ArrayList targets = ((FunctionNode) scriptOrFn)
1564: .getResumptionPoints();
1565: if (targets != null) {
1566: // get resumption point
1567: generateGetGeneratorResumptionPoint();
1568:
1569: // generate dispatch table
1570: generatorSwitch = cfw.addTableSwitch(0, targets.size()
1571: + GENERATOR_START);
1572: generateCheckForThrowOrClose(-1, false, GENERATOR_START);
1573: }
1574: }
1575:
1576: if (fnCurrent == null) {
1577: // See comments in case Token.REGEXP
1578: if (scriptOrFn.getRegexpCount() != 0) {
1579: scriptRegexpLocal = getNewWordLocal();
1580: codegen.pushRegExpArray(cfw, scriptOrFn, contextLocal,
1581: variableObjectLocal);
1582: cfw.addAStore(scriptRegexpLocal);
1583: }
1584: }
1585:
1586: if (compilerEnv.isGenerateObserverCount())
1587: saveCurrentCodeOffset();
1588:
1589: if (hasVarsInRegs) {
1590: // No need to create activation. Pad arguments if need be.
1591: int parmCount = scriptOrFn.getParamCount();
1592: if (parmCount > 0 && !inDirectCallFunction) {
1593: // Set up args array
1594: // check length of arguments, pad if need be
1595: cfw.addALoad(argsLocal);
1596: cfw.add(ByteCode.ARRAYLENGTH);
1597: cfw.addPush(parmCount);
1598: int label = cfw.acquireLabel();
1599: cfw.add(ByteCode.IF_ICMPGE, label);
1600: cfw.addALoad(argsLocal);
1601: cfw.addPush(parmCount);
1602: addScriptRuntimeInvoke("padArguments",
1603: "([Ljava/lang/Object;I"
1604: + ")[Ljava/lang/Object;");
1605: cfw.addAStore(argsLocal);
1606: cfw.markLabel(label);
1607: }
1608:
1609: int paramCount = fnCurrent.fnode.getParamCount();
1610: int varCount = fnCurrent.fnode.getParamAndVarCount();
1611: boolean[] constDeclarations = fnCurrent.fnode
1612: .getParamAndVarConst();
1613:
1614: // REMIND - only need to initialize the vars that don't get a value
1615: // before the next call and are used in the function
1616: short firstUndefVar = -1;
1617: for (int i = 0; i != varCount; ++i) {
1618: short reg = -1;
1619: if (i < paramCount) {
1620: if (!inDirectCallFunction) {
1621: reg = getNewWordLocal();
1622: cfw.addALoad(argsLocal);
1623: cfw.addPush(i);
1624: cfw.add(ByteCode.AALOAD);
1625: cfw.addAStore(reg);
1626: }
1627: } else if (fnCurrent.isNumberVar(i)) {
1628: reg = getNewWordPairLocal(constDeclarations[i]);
1629: cfw.addPush(0.0);
1630: cfw.addDStore(reg);
1631: } else {
1632: reg = getNewWordLocal(constDeclarations[i]);
1633: if (firstUndefVar == -1) {
1634: Codegen.pushUndefined(cfw);
1635: firstUndefVar = reg;
1636: } else {
1637: cfw.addALoad(firstUndefVar);
1638: }
1639: cfw.addAStore(reg);
1640: }
1641: if (reg >= 0) {
1642: if (constDeclarations[i]) {
1643: cfw.addPush(0);
1644: cfw.addIStore(reg
1645: + (fnCurrent.isNumberVar(i) ? 2 : 1));
1646: }
1647: varRegisters[i] = reg;
1648: }
1649:
1650: // Add debug table entry if we're generating debug info
1651: if (compilerEnv.isGenerateDebugInfo()) {
1652: String name = fnCurrent.fnode.getParamOrVarName(i);
1653: String type = fnCurrent.isNumberVar(i) ? "D"
1654: : "Ljava/lang/Object;";
1655: int startPC = cfw.getCurrentCodeOffset();
1656: if (reg < 0) {
1657: reg = varRegisters[i];
1658: }
1659: cfw.addVariableDescriptor(name, type, startPC, reg);
1660: }
1661: }
1662:
1663: // Skip creating activation object.
1664: return;
1665: }
1666:
1667: // skip creating activation object for the body of a generator. The
1668: // activation record required by a generator has already been created
1669: // in generateGenerator().
1670: if (isGenerator)
1671: return;
1672:
1673: String debugVariableName;
1674: if (fnCurrent != null) {
1675: debugVariableName = "activation";
1676: cfw.addALoad(funObjLocal);
1677: cfw.addALoad(variableObjectLocal);
1678: cfw.addALoad(argsLocal);
1679: addScriptRuntimeInvoke("createFunctionActivation",
1680: "(Lorg/mozilla/javascript/NativeFunction;"
1681: + "Lorg/mozilla/javascript/Scriptable;"
1682: + "[Ljava/lang/Object;"
1683: + ")Lorg/mozilla/javascript/Scriptable;");
1684: cfw.addAStore(variableObjectLocal);
1685: cfw.addALoad(contextLocal);
1686: cfw.addALoad(variableObjectLocal);
1687: addScriptRuntimeInvoke("enterActivationFunction",
1688: "(Lorg/mozilla/javascript/Context;"
1689: + "Lorg/mozilla/javascript/Scriptable;"
1690: + ")V");
1691: } else {
1692: debugVariableName = "global";
1693: cfw.addALoad(funObjLocal);
1694: cfw.addALoad(this ObjLocal);
1695: cfw.addALoad(contextLocal);
1696: cfw.addALoad(variableObjectLocal);
1697: cfw.addPush(0); // false to indicate it is not eval script
1698: addScriptRuntimeInvoke("initScript",
1699: "(Lorg/mozilla/javascript/NativeFunction;"
1700: + "Lorg/mozilla/javascript/Scriptable;"
1701: + "Lorg/mozilla/javascript/Context;"
1702: + "Lorg/mozilla/javascript/Scriptable;"
1703: + "Z" + ")V");
1704: }
1705:
1706: enterAreaStartLabel = cfw.acquireLabel();
1707: epilogueLabel = cfw.acquireLabel();
1708: cfw.markLabel(enterAreaStartLabel);
1709:
1710: generateNestedFunctionInits();
1711:
1712: // default is to generate debug info
1713: if (compilerEnv.isGenerateDebugInfo()) {
1714: cfw.addVariableDescriptor(debugVariableName,
1715: "Lorg/mozilla/javascript/Scriptable;", cfw
1716: .getCurrentCodeOffset(),
1717: variableObjectLocal);
1718: }
1719:
1720: if (fnCurrent == null) {
1721: // OPT: use dataflow to prove that this assignment is dead
1722: popvLocal = getNewWordLocal();
1723: Codegen.pushUndefined(cfw);
1724: cfw.addAStore(popvLocal);
1725:
1726: int linenum = scriptOrFn.getEndLineno();
1727: if (linenum != -1)
1728: cfw.addLineNumberEntry((short) linenum);
1729:
1730: } else {
1731: if (fnCurrent.itsContainsCalls0) {
1732: itsZeroArgArray = getNewWordLocal();
1733: cfw.add(ByteCode.GETSTATIC,
1734: "org/mozilla/javascript/ScriptRuntime",
1735: "emptyArgs", "[Ljava/lang/Object;");
1736: cfw.addAStore(itsZeroArgArray);
1737: }
1738: if (fnCurrent.itsContainsCalls1) {
1739: itsOneArgArray = getNewWordLocal();
1740: cfw.addPush(1);
1741: cfw.add(ByteCode.ANEWARRAY, "java/lang/Object");
1742: cfw.addAStore(itsOneArgArray);
1743: }
1744: }
1745: }
1746:
1747: private void generateGetGeneratorResumptionPoint() {
1748: cfw.addALoad(generatorStateLocal);
1749: cfw.add(ByteCode.GETFIELD,
1750: OptRuntime.GeneratorState.CLASS_NAME,
1751: OptRuntime.GeneratorState.resumptionPoint_NAME,
1752: OptRuntime.GeneratorState.resumptionPoint_TYPE);
1753: }
1754:
1755: private void generateSetGeneratorResumptionPoint(int nextState) {
1756: cfw.addALoad(generatorStateLocal);
1757: cfw.addLoadConstant(nextState);
1758: cfw.add(ByteCode.PUTFIELD,
1759: OptRuntime.GeneratorState.CLASS_NAME,
1760: OptRuntime.GeneratorState.resumptionPoint_NAME,
1761: OptRuntime.GeneratorState.resumptionPoint_TYPE);
1762: }
1763:
1764: private void generateGetGeneratorStackState() {
1765: cfw.addALoad(generatorStateLocal);
1766: addOptRuntimeInvoke("getGeneratorStackState",
1767: "(Ljava/lang/Object;)[Ljava/lang/Object;");
1768: }
1769:
1770: private void generateEpilogue() {
1771: if (compilerEnv.isGenerateObserverCount())
1772: addInstructionCount();
1773: if (isGenerator) {
1774: // generate locals initialization
1775: HashMap liveLocals = ((FunctionNode) scriptOrFn)
1776: .getLiveLocals();
1777: if (liveLocals != null) {
1778: ArrayList nodes = ((FunctionNode) scriptOrFn)
1779: .getResumptionPoints();
1780: for (int i = 0; i < nodes.size(); i++) {
1781: Node node = (Node) nodes.get(i);
1782: int[] live = (int[]) liveLocals.get(node);
1783: if (live != null) {
1784: cfw.markTableSwitchCase(generatorSwitch,
1785: getNextGeneratorState(node));
1786: generateGetGeneratorLocalsState();
1787: for (int j = 0; j < live.length; j++) {
1788: cfw.add(ByteCode.DUP);
1789: cfw.addLoadConstant(j);
1790: cfw.add(ByteCode.AALOAD);
1791: cfw.addAStore(live[j]);
1792: }
1793: cfw.add(ByteCode.POP);
1794: cfw.add(ByteCode.GOTO, getTargetLabel(node));
1795: }
1796: }
1797: }
1798:
1799: // generate dispatch tables for finally
1800: if (finallys != null) {
1801: Enumeration en = finallys.keys();
1802: while (en.hasMoreElements()) {
1803: Node n = (Node) en.nextElement();
1804: if (n.getType() == Token.FINALLY) {
1805: FinallyReturnPoint ret = (FinallyReturnPoint) finallys
1806: .get(n);
1807: // the finally will jump here
1808: cfw.markLabel(ret.tableLabel, (short) 1);
1809:
1810: // start generating a dispatch table
1811: int startSwitch = cfw.addTableSwitch(0,
1812: ret.jsrPoints.size() - 1);
1813: int c = 0;
1814: cfw.markTableSwitchDefault(startSwitch);
1815: for (int i = 0; i < ret.jsrPoints.size(); i++) {
1816: // generate gotos back to the JSR location
1817: cfw.markTableSwitchCase(startSwitch, c);
1818: cfw.add(ByteCode.GOTO,
1819: ((Integer) ret.jsrPoints.get(i))
1820: .intValue());
1821: c++;
1822: }
1823: }
1824: }
1825: }
1826: }
1827:
1828: if (epilogueLabel != -1) {
1829: cfw.markLabel(epilogueLabel);
1830: }
1831:
1832: if (hasVarsInRegs) {
1833: cfw.add(ByteCode.ARETURN);
1834: return;
1835: } else if (isGenerator) {
1836: if (((FunctionNode) scriptOrFn).getResumptionPoints() != null) {
1837: cfw.markTableSwitchDefault(generatorSwitch);
1838: }
1839:
1840: // change state for re-entry
1841: generateSetGeneratorResumptionPoint(GENERATOR_TERMINATE);
1842:
1843: // throw StopIteration
1844: cfw.addALoad(variableObjectLocal);
1845: addOptRuntimeInvoke("throwStopIteration",
1846: "(Ljava/lang/Object;)V");
1847:
1848: Codegen.pushUndefined(cfw);
1849: cfw.add(ByteCode.ARETURN);
1850:
1851: } else if (fnCurrent == null) {
1852: cfw.addALoad(popvLocal);
1853: cfw.add(ByteCode.ARETURN);
1854: } else {
1855: generateActivationExit();
1856: cfw.add(ByteCode.ARETURN);
1857:
1858: // Generate catch block to catch all and rethrow to call exit code
1859: // under exception propagation as well.
1860:
1861: int finallyHandler = cfw.acquireLabel();
1862: cfw.markHandler(finallyHandler);
1863: short exceptionObject = getNewWordLocal();
1864: cfw.addAStore(exceptionObject);
1865:
1866: // Duplicate generateActivationExit() in the catch block since it
1867: // takes less space then full-featured ByteCode.JSR/ByteCode.RET
1868: generateActivationExit();
1869:
1870: cfw.addALoad(exceptionObject);
1871: releaseWordLocal(exceptionObject);
1872: // rethrow
1873: cfw.add(ByteCode.ATHROW);
1874:
1875: // mark the handler
1876: cfw.addExceptionHandler(enterAreaStartLabel, epilogueLabel,
1877: finallyHandler, null); // catch any
1878: }
1879: }
1880:
1881: private void generateGetGeneratorLocalsState() {
1882: cfw.addALoad(generatorStateLocal);
1883: addOptRuntimeInvoke("getGeneratorLocalsState",
1884: "(Ljava/lang/Object;)[Ljava/lang/Object;");
1885: }
1886:
1887: private void generateActivationExit() {
1888: if (fnCurrent == null || hasVarsInRegs)
1889: throw Kit.codeBug();
1890: cfw.addALoad(contextLocal);
1891: addScriptRuntimeInvoke("exitActivationFunction",
1892: "(Lorg/mozilla/javascript/Context;)V");
1893: }
1894:
1895: private void generateStatement(Node node) {
1896: updateLineNumber(node);
1897: int type = node.getType();
1898: Node child = node.getFirstChild();
1899: switch (type) {
1900: case Token.LOOP:
1901: case Token.LABEL:
1902: case Token.WITH:
1903: case Token.SCRIPT:
1904: case Token.BLOCK:
1905: case Token.EMPTY:
1906: // no-ops.
1907: while (child != null) {
1908: generateStatement(child);
1909: child = child.getNext();
1910: }
1911: break;
1912:
1913: case Token.LOCAL_BLOCK: {
1914: int local = getNewWordLocal();
1915: if (isGenerator) {
1916: cfw.add(ByteCode.ACONST_NULL);
1917: cfw.addAStore(local);
1918: }
1919: node.putIntProp(Node.LOCAL_PROP, local);
1920: while (child != null) {
1921: generateStatement(child);
1922: child = child.getNext();
1923: }
1924: releaseWordLocal((short) local);
1925: node.removeProp(Node.LOCAL_PROP);
1926: break;
1927: }
1928:
1929: case Token.FUNCTION: {
1930: int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
1931: OptFunctionNode ofn = OptFunctionNode.get(scriptOrFn,
1932: fnIndex);
1933: int t = ofn.fnode.getFunctionType();
1934: if (t == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) {
1935: visitFunction(ofn, t);
1936: } else {
1937: if (t != FunctionNode.FUNCTION_STATEMENT) {
1938: throw Codegen.badTree();
1939: }
1940: }
1941: break;
1942: }
1943:
1944: case Token.TRY:
1945: visitTryCatchFinally((Node.Jump) node, child);
1946: break;
1947:
1948: case Token.CATCH_SCOPE: {
1949: // nothing stays on the stack on entry into a catch scope
1950: cfw.setStackTop((short) 0);
1951:
1952: int local = getLocalBlockRegister(node);
1953: int scopeIndex = node
1954: .getExistingIntProp(Node.CATCH_SCOPE_PROP);
1955:
1956: String name = child.getString(); // name of exception
1957: child = child.getNext();
1958: generateExpression(child, node); // load expression object
1959: if (scopeIndex == 0) {
1960: cfw.add(ByteCode.ACONST_NULL);
1961: } else {
1962: // Load previous catch scope object
1963: cfw.addALoad(local);
1964: }
1965: cfw.addPush(name);
1966: cfw.addALoad(contextLocal);
1967: cfw.addALoad(variableObjectLocal);
1968:
1969: addScriptRuntimeInvoke("newCatchScope",
1970: "(Ljava/lang/Throwable;"
1971: + "Lorg/mozilla/javascript/Scriptable;"
1972: + "Ljava/lang/String;"
1973: + "Lorg/mozilla/javascript/Context;"
1974: + "Lorg/mozilla/javascript/Scriptable;"
1975: + ")Lorg/mozilla/javascript/Scriptable;");
1976: cfw.addAStore(local);
1977: }
1978: break;
1979:
1980: case Token.THROW:
1981: generateExpression(child, node);
1982: if (compilerEnv.isGenerateObserverCount())
1983: addInstructionCount();
1984: generateThrowJavaScriptException();
1985: break;
1986:
1987: case Token.RETHROW:
1988: if (compilerEnv.isGenerateObserverCount())
1989: addInstructionCount();
1990: cfw.addALoad(getLocalBlockRegister(node));
1991: cfw.add(ByteCode.ATHROW);
1992: break;
1993:
1994: case Token.RETURN_RESULT:
1995: case Token.RETURN:
1996: if (!isGenerator) {
1997: if (child != null) {
1998: generateExpression(child, node);
1999: } else if (type == Token.RETURN) {
2000: Codegen.pushUndefined(cfw);
2001: } else {
2002: if (popvLocal < 0)
2003: throw Codegen.badTree();
2004: cfw.addALoad(popvLocal);
2005: }
2006: }
2007: if (compilerEnv.isGenerateObserverCount())
2008: addInstructionCount();
2009: if (epilogueLabel == -1) {
2010: if (!hasVarsInRegs)
2011: throw Codegen.badTree();
2012: epilogueLabel = cfw.acquireLabel();
2013: }
2014: cfw.add(ByteCode.GOTO, epilogueLabel);
2015: break;
2016:
2017: case Token.SWITCH:
2018: if (compilerEnv.isGenerateObserverCount())
2019: addInstructionCount();
2020: visitSwitch((Node.Jump) node, child);
2021: break;
2022:
2023: case Token.ENTERWITH:
2024: generateExpression(child, node);
2025: cfw.addALoad(contextLocal);
2026: cfw.addALoad(variableObjectLocal);
2027: addScriptRuntimeInvoke("enterWith", "(Ljava/lang/Object;"
2028: + "Lorg/mozilla/javascript/Context;"
2029: + "Lorg/mozilla/javascript/Scriptable;"
2030: + ")Lorg/mozilla/javascript/Scriptable;");
2031: cfw.addAStore(variableObjectLocal);
2032: incReferenceWordLocal(variableObjectLocal);
2033: break;
2034:
2035: case Token.LEAVEWITH:
2036: cfw.addALoad(variableObjectLocal);
2037: addScriptRuntimeInvoke("leaveWith",
2038: "(Lorg/mozilla/javascript/Scriptable;"
2039: + ")Lorg/mozilla/javascript/Scriptable;");
2040: cfw.addAStore(variableObjectLocal);
2041: decReferenceWordLocal(variableObjectLocal);
2042: break;
2043:
2044: case Token.ENUM_INIT_KEYS:
2045: case Token.ENUM_INIT_VALUES:
2046: case Token.ENUM_INIT_ARRAY:
2047: generateExpression(child, node);
2048: cfw.addALoad(contextLocal);
2049: int enumType = type == Token.ENUM_INIT_KEYS ? ScriptRuntime.ENUMERATE_KEYS
2050: : type == Token.ENUM_INIT_VALUES ? ScriptRuntime.ENUMERATE_VALUES
2051: : ScriptRuntime.ENUMERATE_ARRAY;
2052: cfw.addPush(enumType);
2053: addScriptRuntimeInvoke("enumInit", "(Ljava/lang/Object;"
2054: + "Lorg/mozilla/javascript/Context;" + "I"
2055: + ")Ljava/lang/Object;");
2056: cfw.addAStore(getLocalBlockRegister(node));
2057: break;
2058:
2059: case Token.EXPR_VOID:
2060: if (child.getType() == Token.SETVAR) {
2061: /* special case this so as to avoid unnecessary
2062: load's & pop's */
2063: visitSetVar(child, child.getFirstChild(), false);
2064: } else if (child.getType() == Token.SETCONSTVAR) {
2065: /* special case this so as to avoid unnecessary
2066: load's & pop's */
2067: visitSetConstVar(child, child.getFirstChild(), false);
2068: } else if (child.getType() == Token.YIELD) {
2069: generateYieldPoint(child, false);
2070: } else {
2071: generateExpression(child, node);
2072: if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1)
2073: cfw.add(ByteCode.POP2);
2074: else
2075: cfw.add(ByteCode.POP);
2076: }
2077: break;
2078:
2079: case Token.EXPR_RESULT:
2080: generateExpression(child, node);
2081: if (popvLocal < 0) {
2082: popvLocal = getNewWordLocal();
2083: }
2084: cfw.addAStore(popvLocal);
2085: break;
2086:
2087: case Token.TARGET: {
2088: if (compilerEnv.isGenerateObserverCount())
2089: addInstructionCount();
2090: int label = getTargetLabel(node);
2091: cfw.markLabel(label);
2092: if (compilerEnv.isGenerateObserverCount())
2093: saveCurrentCodeOffset();
2094: }
2095: break;
2096:
2097: case Token.JSR:
2098: case Token.GOTO:
2099: case Token.IFEQ:
2100: case Token.IFNE:
2101: if (compilerEnv.isGenerateObserverCount())
2102: addInstructionCount();
2103: visitGoto((Node.Jump) node, type, child);
2104: break;
2105:
2106: case Token.FINALLY: {
2107: if (compilerEnv.isGenerateObserverCount())
2108: saveCurrentCodeOffset();
2109: // there is exactly one value on the stack when enterring
2110: // finally blocks: the return address (or its int encoding)
2111: cfw.setStackTop((short) 1);
2112:
2113: // Save return address in a new local
2114: int finallyRegister = getNewWordLocal();
2115: if (isGenerator)
2116: generateIntegerWrap();
2117: cfw.addAStore(finallyRegister);
2118:
2119: while (child != null) {
2120: generateStatement(child);
2121: child = child.getNext();
2122: }
2123: if (isGenerator) {
2124: cfw.addALoad(finallyRegister);
2125: cfw.add(ByteCode.CHECKCAST, "java/lang/Integer");
2126: generateIntegerUnwrap();
2127: FinallyReturnPoint ret = (FinallyReturnPoint) finallys
2128: .get(node);
2129: ret.tableLabel = cfw.acquireLabel();
2130: cfw.add(ByteCode.GOTO, ret.tableLabel);
2131: } else {
2132: cfw.add(ByteCode.RET, finallyRegister);
2133: }
2134: releaseWordLocal((short) finallyRegister);
2135: }
2136: break;
2137:
2138: case Token.DEBUGGER:
2139: break;
2140:
2141: default:
2142: throw Codegen.badTree();
2143: }
2144:
2145: }
2146:
2147: private void generateIntegerWrap() {
2148: cfw.addInvoke(ByteCode.INVOKESTATIC, "java/lang/Integer",
2149: "valueOf", "(I)Ljava/lang/Integer;");
2150: }
2151:
2152: private void generateIntegerUnwrap() {
2153: cfw.addInvoke(ByteCode.INVOKEVIRTUAL, "java/lang/Integer",
2154: "intValue", "()I");
2155: }
2156:
2157: private void generateThrowJavaScriptException() {
2158: cfw.add(ByteCode.NEW,
2159: "org/mozilla/javascript/JavaScriptException");
2160: cfw.add(ByteCode.DUP_X1);
2161: cfw.add(ByteCode.SWAP);
2162: cfw.addPush(scriptOrFn.getSourceName());
2163: cfw.addPush(itsLineNumber);
2164: cfw.addInvoke(ByteCode.INVOKESPECIAL,
2165: "org/mozilla/javascript/JavaScriptException", "<init>",
2166: "(Ljava/lang/Object;Ljava/lang/String;I)V");
2167: cfw.add(ByteCode.ATHROW);
2168: }
2169:
2170: private int getNextGeneratorState(Node node) {
2171: int nodeIndex = ((FunctionNode) scriptOrFn)
2172: .getResumptionPoints().indexOf(node);
2173: return nodeIndex + GENERATOR_YIELD_START;
2174: }
2175:
2176: private void generateExpression(Node node, Node parent) {
2177: int type = node.getType();
2178: Node child = node.getFirstChild();
2179: switch (type) {
2180: case Token.USE_STACK:
2181: break;
2182:
2183: case Token.FUNCTION:
2184: if (fnCurrent != null || parent.getType() != Token.SCRIPT) {
2185: int fnIndex = node
2186: .getExistingIntProp(Node.FUNCTION_PROP);
2187: OptFunctionNode ofn = OptFunctionNode.get(scriptOrFn,
2188: fnIndex);
2189: int t = ofn.fnode.getFunctionType();
2190: if (t != FunctionNode.FUNCTION_EXPRESSION) {
2191: throw Codegen.badTree();
2192: }
2193: visitFunction(ofn, t);
2194: }
2195: break;
2196:
2197: case Token.NAME: {
2198: cfw.addALoad(contextLocal);
2199: cfw.addALoad(variableObjectLocal);
2200: cfw.addPush(node.getString());
2201: addScriptRuntimeInvoke("name",
2202: "(Lorg/mozilla/javascript/Context;"
2203: + "Lorg/mozilla/javascript/Scriptable;"
2204: + "Ljava/lang/String;"
2205: + ")Ljava/lang/Object;");
2206: }
2207: break;
2208:
2209: case Token.CALL:
2210: case Token.NEW: {
2211: int specialType = node.getIntProp(Node.SPECIALCALL_PROP,
2212: Node.NON_SPECIALCALL);
2213: if (specialType == Node.NON_SPECIALCALL) {
2214: OptFunctionNode target;
2215: target = (OptFunctionNode) node
2216: .getProp(Node.DIRECTCALL_PROP);
2217:
2218: if (target != null) {
2219: visitOptimizedCall(node, target, type, child);
2220: } else if (type == Token.CALL) {
2221: visitStandardCall(node, child);
2222: } else {
2223: visitStandardNew(node, child);
2224: }
2225: } else {
2226: visitSpecialCall(node, type, specialType, child);
2227: }
2228: }
2229: break;
2230:
2231: case Token.REF_CALL:
2232: generateFunctionAndThisObj(child, node);
2233: // stack: ... functionObj thisObj
2234: child = child.getNext();
2235: generateCallArgArray(node, child, false);
2236: cfw.addALoad(contextLocal);
2237: addScriptRuntimeInvoke("callRef",
2238: "(Lorg/mozilla/javascript/Callable;"
2239: + "Lorg/mozilla/javascript/Scriptable;"
2240: + "[Ljava/lang/Object;"
2241: + "Lorg/mozilla/javascript/Context;"
2242: + ")Lorg/mozilla/javascript/Ref;");
2243: break;
2244:
2245: case Token.NUMBER: {
2246: double num = node.getDouble();
2247: if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
2248: cfw.addPush(num);
2249: } else {
2250: codegen.pushNumberAsObject(cfw, num);
2251: }
2252: }
2253: break;
2254:
2255: case Token.STRING:
2256: cfw.addPush(node.getString());
2257: break;
2258:
2259: case Token.THIS:
2260: cfw.addALoad(this ObjLocal);
2261: break;
2262:
2263: case Token.THISFN:
2264: cfw.add(ByteCode.ALOAD_0);
2265: break;
2266:
2267: case Token.NULL:
2268: cfw.add(ByteCode.ACONST_NULL);
2269: break;
2270:
2271: case Token.TRUE:
2272: cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean", "TRUE",
2273: "Ljava/lang/Boolean;");
2274: break;
2275:
2276: case Token.FALSE:
2277: cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean", "FALSE",
2278: "Ljava/lang/Boolean;");
2279: break;
2280:
2281: case Token.REGEXP: {
2282: int i = node.getExistingIntProp(Node.REGEXP_PROP);
2283: // Scripts can not use REGEXP_ARRAY_FIELD_NAME since
2284: // it it will make script.exec non-reentrant so they
2285: // store regexp array in a local variable while
2286: // functions always access precomputed
2287: // REGEXP_ARRAY_FIELD_NAME not to consume locals
2288: if (fnCurrent == null) {
2289: cfw.addALoad(scriptRegexpLocal);
2290: } else {
2291: cfw.addALoad(funObjLocal);
2292: cfw.add(ByteCode.GETFIELD, codegen.mainClassName,
2293: Codegen.REGEXP_ARRAY_FIELD_NAME,
2294: Codegen.REGEXP_ARRAY_FIELD_TYPE);
2295: }
2296: cfw.addPush(i);
2297: cfw.add(ByteCode.AALOAD);
2298: }
2299: break;
2300:
2301: case Token.COMMA: {
2302: Node next = child.getNext();
2303: while (next != null) {
2304: generateExpression(child, node);
2305: cfw.add(ByteCode.POP);
2306: child = next;
2307: next = next.getNext();
2308: }
2309: generateExpression(child, node);
2310: break;
2311: }
2312:
2313: case Token.ENUM_NEXT:
2314: case Token.ENUM_ID: {
2315: int local = getLocalBlockRegister(node);
2316: cfw.addALoad(local);
2317: if (type == Token.ENUM_NEXT) {
2318: addScriptRuntimeInvoke("enumNext",
2319: "(Ljava/lang/Object;)Ljava/lang/Boolean;");
2320: } else {
2321: cfw.addALoad(contextLocal);
2322: addScriptRuntimeInvoke("enumId", "(Ljava/lang/Object;"
2323: + "Lorg/mozilla/javascript/Context;"
2324: + ")Ljava/lang/Object;");
2325: }
2326: break;
2327: }
2328:
2329: case Token.ARRAYLIT:
2330: visitArrayLiteral(node, child);
2331: break;
2332:
2333: case Token.OBJECTLIT:
2334: visitObjectLiteral(node, child);
2335: break;
2336:
2337: case Token.NOT: {
2338: int trueTarget = cfw.acquireLabel();
2339: int falseTarget = cfw.acquireLabel();
2340: int beyond = cfw.acquireLabel();
2341: generateIfJump(child, node, trueTarget, falseTarget);
2342:
2343: cfw.markLabel(trueTarget);
2344: cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean", "FALSE",
2345: "Ljava/lang/Boolean;");
2346: cfw.add(ByteCode.GOTO, beyond);
2347: cfw.markLabel(falseTarget);
2348: cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean", "TRUE",
2349: "Ljava/lang/Boolean;");
2350: cfw.markLabel(beyond);
2351: cfw.adjustStackTop(-1);
2352: break;
2353: }
2354:
2355: case Token.BITNOT:
2356: generateExpression(child, node);
2357: addScriptRuntimeInvoke("toInt32", "(Ljava/lang/Object;)I");
2358: cfw.addPush(-1); // implement ~a as (a ^ -1)
2359: cfw.add(ByteCode.IXOR);
2360: cfw.add(ByteCode.I2D);
2361: addDoubleWrap();
2362: break;
2363:
2364: case Token.VOID:
2365: generateExpression(child, node);
2366: cfw.add(ByteCode.POP);
2367: Codegen.pushUndefined(cfw);
2368: break;
2369:
2370: case Token.TYPEOF:
2371: generateExpression(child, node);
2372: addScriptRuntimeInvoke("typeof", "(Ljava/lang/Object;"
2373: + ")Ljava/lang/String;");
2374: break;
2375:
2376: case Token.TYPEOFNAME:
2377: visitTypeofname(node);
2378: break;
2379:
2380: case Token.INC:
2381: case Token.DEC:
2382: visitIncDec(node);
2383: break;
2384:
2385: case Token.OR:
2386: case Token.AND: {
2387: generateExpression(child, node);
2388: cfw.add(ByteCode.DUP);
2389: addScriptRuntimeInvoke("toBoolean", "(Ljava/lang/Object;)Z");
2390: int falseTarget = cfw.acquireLabel();
2391: if (type == Token.AND)
2392: cfw.add(ByteCode.IFEQ, falseTarget);
2393: else
2394: cfw.add(ByteCode.IFNE, falseTarget);
2395: cfw.add(ByteCode.POP);
2396: generateExpression(child.getNext(), node);
2397: cfw.markLabel(falseTarget);
2398: }
2399: break;
2400:
2401: case Token.HOOK: {
2402: Node ifThen = child.getNext();
2403: Node ifElse = ifThen.getNext();
2404: generateExpression(child, node);
2405: addScriptRuntimeInvoke("toBoolean", "(Ljava/lang/Object;)Z");
2406: int elseTarget = cfw.acquireLabel();
2407: cfw.add(ByteCode.IFEQ, elseTarget);
2408: short stack = cfw.getStackTop();
2409: generateExpression(ifThen, node);
2410: int afterHook = cfw.acquireLabel();
2411: cfw.add(ByteCode.GOTO, afterHook);
2412: cfw.markLabel(elseTarget, stack);
2413: generateExpression(ifElse, node);
2414: cfw.markLabel(afterHook);
2415: }
2416: break;
2417:
2418: case Token.ADD: {
2419: generateExpression(child, node);
2420: generateExpression(child.getNext(), node);
2421: switch (node.getIntProp(Node.ISNUMBER_PROP, -1)) {
2422: case Node.BOTH:
2423: cfw.add(ByteCode.DADD);
2424: break;
2425: case Node.LEFT:
2426: addOptRuntimeInvoke("add",
2427: "(DLjava/lang/Object;)Ljava/lang/Object;");
2428: break;
2429: case Node.RIGHT:
2430: addOptRuntimeInvoke("add",
2431: "(Ljava/lang/Object;D)Ljava/lang/Object;");
2432: break;
2433: default:
2434: if (child.getType() == Token.STRING) {
2435: addScriptRuntimeInvoke("add", "(Ljava/lang/String;"
2436: + "Ljava/lang/Object;"
2437: + ")Ljava/lang/String;");
2438: } else if (child.getNext().getType() == Token.STRING) {
2439: addScriptRuntimeInvoke("add", "(Ljava/lang/Object;"
2440: + "Ljava/lang/String;"
2441: + ")Ljava/lang/String;");
2442: } else {
2443: cfw.addALoad(contextLocal);
2444: addScriptRuntimeInvoke("add", "(Ljava/lang/Object;"
2445: + "Ljava/lang/Object;"
2446: + "Lorg/mozilla/javascript/Context;"
2447: + ")Ljava/lang/Object;");
2448: }
2449: }
2450: }
2451: break;
2452:
2453: case Token.MUL:
2454: visitArithmetic(node, ByteCode.DMUL, child, parent);
2455: break;
2456:
2457: case Token.SUB:
2458: visitArithmetic(node, ByteCode.DSUB, child, parent);
2459: break;
2460:
2461: case Token.DIV:
2462: case Token.MOD:
2463: visitArithmetic(node, type == Token.DIV ? ByteCode.DDIV
2464: : ByteCode.DREM, child, parent);
2465: break;
2466:
2467: case Token.BITOR:
2468: case Token.BITXOR:
2469: case Token.BITAND:
2470: case Token.LSH:
2471: case Token.RSH:
2472: case Token.URSH:
2473: visitBitOp(node, type, child);
2474: break;
2475:
2476: case Token.POS:
2477: case Token.NEG:
2478: generateExpression(child, node);
2479: addObjectToDouble();
2480: if (type == Token.NEG) {
2481: cfw.add(ByteCode.DNEG);
2482: }
2483: addDoubleWrap();
2484: break;
2485:
2486: case Token.TO_DOUBLE:
2487: // cnvt to double (not Double)
2488: generateExpression(child, node);
2489: addObjectToDouble();
2490: break;
2491:
2492: case Token.TO_OBJECT: {
2493: // convert from double
2494: int prop = -1;
2495: if (child.getType() == Token.NUMBER) {
2496: prop = child.getIntProp(Node.ISNUMBER_PROP, -1);
2497: }
2498: if (prop != -1) {
2499: child.removeProp(Node.ISNUMBER_PROP);
2500: generateExpression(child, node);
2501: child.putIntProp(Node.ISNUMBER_PROP, prop);
2502: } else {
2503: generateExpression(child, node);
2504: addDoubleWrap();
2505: }
2506: break;
2507: }
2508:
2509: case Token.IN:
2510: case Token.INSTANCEOF:
2511: case Token.LE:
2512: case Token.LT:
2513: case Token.GE:
2514: case Token.GT: {
2515: int trueGOTO = cfw.acquireLabel();
2516: int falseGOTO = cfw.acquireLabel();
2517: visitIfJumpRelOp(node, child, trueGOTO, falseGOTO);
2518: addJumpedBooleanWrap(trueGOTO, falseGOTO);
2519: break;
2520: }
2521:
2522: case Token.EQ:
2523: case Token.NE:
2524: case Token.SHEQ:
2525: case Token.SHNE: {
2526: int trueGOTO = cfw.acquireLabel();
2527: int falseGOTO = cfw.acquireLabel();
2528: visitIfJumpEqOp(node, child, trueGOTO, falseGOTO);
2529: addJumpedBooleanWrap(trueGOTO, falseGOTO);
2530: break;
2531: }
2532:
2533: case Token.GETPROP:
2534: case Token.GETPROPNOWARN:
2535: visitGetProp(node, child);
2536: break;
2537:
2538: case Token.GETELEM:
2539: generateExpression(child, node); // object
2540: generateExpression(child.getNext(), node); // id
2541: cfw.addALoad(contextLocal);
2542: if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
2543: addScriptRuntimeInvoke("getObjectIndex",
2544: "(Ljava/lang/Object;D"
2545: + "Lorg/mozilla/javascript/Context;"
2546: + ")Ljava/lang/Object;");
2547: } else {
2548: addScriptRuntimeInvoke("getObjectElem",
2549: "(Ljava/lang/Object;" + "Ljava/lang/Object;"
2550: + "Lorg/mozilla/javascript/Context;"
2551: + ")Ljava/lang/Object;");
2552: }
2553: break;
2554:
2555: case Token.GET_REF:
2556: generateExpression(child, node); // reference
2557: cfw.addALoad(contextLocal);
2558: addScriptRuntimeInvoke("refGet",
2559: "(Lorg/mozilla/javascript/Ref;"
2560: + "Lorg/mozilla/javascript/Context;"
2561: + ")Ljava/lang/Object;");
2562: break;
2563:
2564: case Token.GETVAR:
2565: visitGetVar(node);
2566: break;
2567:
2568: case Token.SETVAR:
2569: visitSetVar(node, child, true);
2570: break;
2571:
2572: case Token.SETNAME:
2573: visitSetName(node, child);
2574: break;
2575:
2576: case Token.SETCONST:
2577: visitSetConst(node, child);
2578: break;
2579:
2580: case Token.SETCONSTVAR:
2581: visitSetConstVar(node, child, true);
2582: break;
2583:
2584: case Token.SETPROP:
2585: case Token.SETPROP_OP:
2586: visitSetProp(type, node, child);
2587: break;
2588:
2589: case Token.SETELEM:
2590: case Token.SETELEM_OP:
2591: visitSetElem(type, node, child);
2592: break;
2593:
2594: case Token.SET_REF:
2595: case Token.SET_REF_OP: {
2596: generateExpression(child, node);
2597: child = child.getNext();
2598: if (type == Token.SET_REF_OP) {
2599: cfw.add(ByteCode.DUP);
2600: cfw.addALoad(contextLocal);
2601: addScriptRuntimeInvoke("refGet",
2602: "(Lorg/mozilla/javascript/Ref;"
2603: + "Lorg/mozilla/javascript/Context;"
2604: + ")Ljava/lang/Object;");
2605: }
2606: generateExpression(child, node);
2607: cfw.addALoad(contextLocal);
2608: addScriptRuntimeInvoke("refSet",
2609: "(Lorg/mozilla/javascript/Ref;"
2610: + "Ljava/lang/Object;"
2611: + "Lorg/mozilla/javascript/Context;"
2612: + ")Ljava/lang/Object;");
2613: }
2614: break;
2615:
2616: case Token.DEL_REF:
2617: generateExpression(child, node);
2618: cfw.addALoad(contextLocal);
2619: addScriptRuntimeInvoke("refDel",
2620: "(Lorg/mozilla/javascript/Ref;"
2621: + "Lorg/mozilla/javascript/Context;"
2622: + ")Ljava/lang/Object;");
2623: break;
2624:
2625: case Token.DELPROP:
2626: generateExpression(child, node);
2627: child = child.getNext();
2628: generateExpression(child, node);
2629: cfw.addALoad(contextLocal);
2630: addScriptRuntimeInvoke("delete", "(Ljava/lang/Object;"
2631: + "Ljava/lang/Object;"
2632: + "Lorg/mozilla/javascript/Context;"
2633: + ")Ljava/lang/Object;");
2634: break;
2635:
2636: case Token.BINDNAME: {
2637: while (child != null) {
2638: generateExpression(child, node);
2639: child = child.getNext();
2640: }
2641: // Generate code for "ScriptRuntime.bind(varObj, "s")"
2642: cfw.addALoad(contextLocal);
2643: cfw.addALoad(variableObjectLocal);
2644: cfw.addPush(node.getString());
2645: addScriptRuntimeInvoke("bind",
2646: "(Lorg/mozilla/javascript/Context;"
2647: + "Lorg/mozilla/javascript/Scriptable;"
2648: + "Ljava/lang/String;"
2649: + ")Lorg/mozilla/javascript/Scriptable;");
2650: }
2651: break;
2652:
2653: case Token.LOCAL_LOAD:
2654: cfw.addALoad(getLocalBlockRegister(node));
2655: break;
2656:
2657: case Token.REF_SPECIAL: {
2658: String special = (String) node.getProp(Node.NAME_PROP);
2659: generateExpression(child, node);
2660: cfw.addPush(special);
2661: cfw.addALoad(contextLocal);
2662: addScriptRuntimeInvoke("specialRef", "(Ljava/lang/Object;"
2663: + "Ljava/lang/String;"
2664: + "Lorg/mozilla/javascript/Context;"
2665: + ")Lorg/mozilla/javascript/Ref;");
2666: }
2667: break;
2668:
2669: case Token.REF_MEMBER:
2670: case Token.REF_NS_MEMBER:
2671: case Token.REF_NAME:
2672: case Token.REF_NS_NAME: {
2673: int memberTypeFlags = node.getIntProp(
2674: Node.MEMBER_TYPE_PROP, 0);
2675: // generate possible target, possible namespace and member
2676: do {
2677: generateExpression(child, node);
2678: child = child.getNext();
2679: } while (child != null);
2680: cfw.addALoad(contextLocal);
2681: String methodName, signature;
2682: switch (type) {
2683: case Token.REF_MEMBER:
2684: methodName = "memberRef";
2685: signature = "(Ljava/lang/Object;"
2686: + "Ljava/lang/Object;"
2687: + "Lorg/mozilla/javascript/Context;" + "I"
2688: + ")Lorg/mozilla/javascript/Ref;";
2689: break;
2690: case Token.REF_NS_MEMBER:
2691: methodName = "memberRef";
2692: signature = "(Ljava/lang/Object;"
2693: + "Ljava/lang/Object;" + "Ljava/lang/Object;"
2694: + "Lorg/mozilla/javascript/Context;" + "I"
2695: + ")Lorg/mozilla/javascript/Ref;";
2696: break;
2697: case Token.REF_NAME:
2698: methodName = "nameRef";
2699: signature = "(Ljava/lang/Object;"
2700: + "Lorg/mozilla/javascript/Context;"
2701: + "Lorg/mozilla/javascript/Scriptable;" + "I"
2702: + ")Lorg/mozilla/javascript/Ref;";
2703: cfw.addALoad(variableObjectLocal);
2704: break;
2705: case Token.REF_NS_NAME:
2706: methodName = "nameRef";
2707: signature = "(Ljava/lang/Object;"
2708: + "Lorg/mozilla/javascript/Context;"
2709: + "Lorg/mozilla/javascript/Scriptable;" + "I"
2710: + ")Lorg/mozilla/javascript/Ref;";
2711: cfw.addALoad(variableObjectLocal);
2712: break;
2713: default:
2714: throw Kit.codeBug();
2715: }
2716: cfw.addPush(memberTypeFlags);
2717: addScriptRuntimeInvoke(methodName, signature);
2718: }
2719: break;
2720:
2721: case Token.DOTQUERY:
2722: visitDotQuery(node, child);
2723: break;
2724:
2725: case Token.ESCXMLATTR:
2726: generateExpression(child, node);
2727: cfw.addALoad(contextLocal);
2728: addScriptRuntimeInvoke("escapeAttributeValue",
2729: "(Ljava/lang/Object;"
2730: + "Lorg/mozilla/javascript/Context;"
2731: + ")Ljava/lang/String;");
2732: break;
2733:
2734: case Token.ESCXMLTEXT:
2735: generateExpression(child, node);
2736: cfw.addALoad(contextLocal);
2737: addScriptRuntimeInvoke("escapeTextValue",
2738: "(Ljava/lang/Object;"
2739: + "Lorg/mozilla/javascript/Context;"
2740: + ")Ljava/lang/String;");
2741: break;
2742:
2743: case Token.DEFAULTNAMESPACE:
2744: generateExpression(child, node);
2745: cfw.addALoad(contextLocal);
2746: addScriptRuntimeInvoke("setDefaultNamespace",
2747: "(Ljava/lang/Object;"
2748: + "Lorg/mozilla/javascript/Context;"
2749: + ")Ljava/lang/Object;");
2750: break;
2751:
2752: case Token.YIELD:
2753: generateYieldPoint(node, true);
2754: break;
2755:
2756: case Token.WITHEXPR: {
2757: Node enterWith = child;
2758: Node with = enterWith.getNext();
2759: Node leaveWith = with.getNext();
2760: generateStatement(enterWith);
2761: generateExpression(with.getFirstChild(), with);
2762: generateStatement(leaveWith);
2763: break;
2764: }
2765:
2766: case Token.ARRAYCOMP: {
2767: Node initStmt = child;
2768: Node expr = child.getNext();
2769: generateStatement(initStmt);
2770: generateExpression(expr, node);
2771: break;
2772: }
2773:
2774: default:
2775: throw new RuntimeException("Unexpected node type " + type);
2776: }
2777:
2778: }
2779:
2780: private void generateYieldPoint(Node node, boolean exprContext) {
2781: // save stack state
2782: int top = cfw.getStackTop();
2783: maxStack = maxStack > top ? maxStack : top;
2784: if (cfw.getStackTop() != 0) {
2785: generateGetGeneratorStackState();
2786: for (int i = 0; i < top; i++) {
2787: cfw.add(ByteCode.DUP_X1);
2788: cfw.add(ByteCode.SWAP);
2789: cfw.addLoadConstant(i);
2790: cfw.add(ByteCode.SWAP);
2791: cfw.add(ByteCode.AASTORE);
2792: }
2793: // pop the array object
2794: cfw.add(ByteCode.POP);
2795: }
2796:
2797: // generate the yield argument
2798: Node child = node.getFirstChild();
2799: if (child != null)
2800: generateExpression(child, node);
2801: else
2802: Codegen.pushUndefined(cfw);
2803:
2804: // change the resumption state
2805: int nextState = getNextGeneratorState(node);
2806: generateSetGeneratorResumptionPoint(nextState);
2807:
2808: boolean hasLocals = generateSaveLocals(node);
2809:
2810: cfw.add(ByteCode.ARETURN);
2811:
2812: generateCheckForThrowOrClose(getTargetLabel(node), hasLocals,
2813: nextState);
2814:
2815: // reconstruct the stack
2816: if (top != 0) {
2817: generateGetGeneratorStackState();
2818: for (int i = 0; i < top; i++) {
2819: cfw.add(ByteCode.DUP);
2820: cfw.addLoadConstant(top - i - 1);
2821: cfw.add(ByteCode.AALOAD);
2822: cfw.add(ByteCode.SWAP);
2823: }
2824: cfw.add(ByteCode.POP);
2825: }
2826:
2827: // load return value from yield
2828: if (exprContext) {
2829: cfw.addALoad(argsLocal);
2830: }
2831: }
2832:
2833: private void generateCheckForThrowOrClose(int label,
2834: boolean hasLocals, int nextState) {
2835: int throwLabel = cfw.acquireLabel();
2836: int closeLabel = cfw.acquireLabel();
2837:
2838: // throw the user provided object, if the operation is .throw()
2839: cfw.markLabel(throwLabel);
2840: cfw.addALoad(argsLocal);
2841: generateThrowJavaScriptException();
2842:
2843: // throw our special internal exception if the generator is being closed
2844: cfw.markLabel(closeLabel);
2845: cfw.addALoad(argsLocal);
2846: cfw.add(ByteCode.CHECKCAST, "java/lang/Throwable");
2847: cfw.add(ByteCode.ATHROW);
2848:
2849: // mark the re-entry point
2850: // jump here after initializing the locals
2851: if (label != -1)
2852: cfw.markLabel(label);
2853: if (!hasLocals) {
2854: // jump here directly if there are no locals
2855: cfw.markTableSwitchCase(generatorSwitch, nextState);
2856: }
2857:
2858: // see if we need to dispatch for .close() or .throw()
2859: cfw.addILoad(operationLocal);
2860: cfw.addLoadConstant(NativeGenerator.GENERATOR_CLOSE);
2861: cfw.add(ByteCode.IF_ICMPEQ, closeLabel);
2862: cfw.addILoad(operationLocal);
2863: cfw.addLoadConstant(NativeGenerator.GENERATOR_THROW);
2864: cfw.add(ByteCode.IF_ICMPEQ, throwLabel);
2865: }
2866:
2867: private void generateIfJump(Node node, Node parent, int trueLabel,
2868: int falseLabel) {
2869: // System.out.println("gen code for " + node.toString());
2870:
2871: int type = node.getType();
2872: Node child = node.getFirstChild();
2873:
2874: switch (type) {
2875: case Token.NOT:
2876: generateIfJump(child, node, falseLabel, trueLabel);
2877: break;
2878:
2879: case Token.OR:
2880: case Token.AND: {
2881: int interLabel = cfw.acquireLabel();
2882: if (type == Token.AND) {
2883: generateIfJump(child, node, interLabel, falseLabel);
2884: } else {
2885: generateIfJump(child, node, trueLabel, interLabel);
2886: }
2887: cfw.markLabel(interLabel);
2888: child = child.getNext();
2889: generateIfJump(child, node, trueLabel, falseLabel);
2890: break;
2891: }
2892:
2893: case Token.IN:
2894: case Token.INSTANCEOF:
2895: case Token.LE:
2896: case Token.LT:
2897: case Token.GE:
2898: case Token.GT:
2899: visitIfJumpRelOp(node, child, trueLabel, falseLabel);
2900: break;
2901:
2902: case Token.EQ:
2903: case Token.NE:
2904: case Token.SHEQ:
2905: case Token.SHNE:
2906: visitIfJumpEqOp(node, child, trueLabel, falseLabel);
2907: break;
2908:
2909: default:
2910: // Generate generic code for non-optimized jump
2911: generateExpression(node, parent);
2912: addScriptRuntimeInvoke("toBoolean", "(Ljava/lang/Object;)Z");
2913: cfw.add(ByteCode.IFNE, trueLabel);
2914: cfw.add(ByteCode.GOTO, falseLabel);
2915: }
2916: }
2917:
2918: private void visitFunction(OptFunctionNode ofn, int functionType) {
2919: int fnIndex = codegen.getIndex(ofn.fnode);
2920: cfw.add(ByteCode.NEW, codegen.mainClassName);
2921: // Call function constructor
2922: cfw.add(ByteCode.DUP);
2923: cfw.addALoad(variableObjectLocal);
2924: cfw.addALoad(contextLocal); // load 'cx'
2925: cfw.addPush(fnIndex);
2926: cfw.addInvoke(ByteCode.INVOKESPECIAL, codegen.mainClassName,
2927: "<init>", Codegen.FUNCTION_CONSTRUCTOR_SIGNATURE);
2928:
2929: // Init mainScript field;
2930: cfw.add(ByteCode.DUP);
2931: if (isTopLevel) {
2932: cfw.add(ByteCode.ALOAD_0);
2933: } else {
2934: cfw.add(ByteCode.ALOAD_0);
2935: cfw.add(ByteCode.GETFIELD, codegen.mainClassName,
2936: Codegen.DIRECT_CALL_PARENT_FIELD,
2937: codegen.mainClassSignature);
2938: }
2939: cfw.add(ByteCode.PUTFIELD, codegen.mainClassName,
2940: Codegen.DIRECT_CALL_PARENT_FIELD,
2941: codegen.mainClassSignature);
2942:
2943: int directTargetIndex = ofn.getDirectTargetIndex();
2944: if (directTargetIndex >= 0) {
2945: cfw.add(ByteCode.DUP);
2946: if (isTopLevel) {
2947: cfw.add(ByteCode.ALOAD_0);
2948: } else {
2949: cfw.add(ByteCode.ALOAD_0);
2950: cfw.add(ByteCode.GETFIELD, codegen.mainClassName,
2951: Codegen.DIRECT_CALL_PARENT_FIELD,
2952: codegen.mainClassSignature);
2953: }
2954: cfw.add(ByteCode.SWAP);
2955: cfw.add(ByteCode.PUTFIELD, codegen.mainClassName, Codegen
2956: .getDirectTargetFieldName(directTargetIndex),
2957: codegen.mainClassSignature);
2958: }
2959:
2960: if (functionType == FunctionNode.FUNCTION_EXPRESSION) {
2961: // Leave closure object on stack and do not pass it to
2962: // initFunction which suppose to connect statements to scope
2963: return;
2964: }
2965: cfw.addPush(functionType);
2966: cfw.addALoad(variableObjectLocal);
2967: cfw.addALoad(contextLocal); // load 'cx'
2968: addOptRuntimeInvoke("initFunction",
2969: "(Lorg/mozilla/javascript/NativeFunction;" + "I"
2970: + "Lorg/mozilla/javascript/Scriptable;"
2971: + "Lorg/mozilla/javascript/Context;" + ")V");
2972: }
2973:
2974: private int getTargetLabel(Node target) {
2975: int labelId = target.labelId();
2976: if (labelId == -1) {
2977: labelId = cfw.acquireLabel();
2978: target.labelId(labelId);
2979: }
2980: return labelId;
2981: }
2982:
2983: private void visitGoto(Node.Jump node, int type, Node child) {
2984: Node target = node.target;
2985: if (type == Token.IFEQ || type == Token.IFNE) {
2986: if (child == null)
2987: throw Codegen.badTree();
2988: int targetLabel = getTargetLabel(target);
2989: int fallThruLabel = cfw.acquireLabel();
2990: if (type == Token.IFEQ)
2991: generateIfJump(child, node, targetLabel, fallThruLabel);
2992: else
2993: generateIfJump(child, node, fallThruLabel, targetLabel);
2994: cfw.markLabel(fallThruLabel);
2995: } else {
2996: if (type == Token.JSR) {
2997: if (isGenerator) {
2998: addGotoWithReturn(target);
2999: } else {
3000: addGoto(target, ByteCode.JSR);
3001: }
3002: } else {
3003: addGoto(target, ByteCode.GOTO);
3004: }
3005: }
3006: }
3007:
3008: private void addGotoWithReturn(Node target) {
3009: FinallyReturnPoint ret = (FinallyReturnPoint) finallys
3010: .get(target);
3011: cfw.addLoadConstant(ret.jsrPoints.size());
3012: addGoto(target, ByteCode.GOTO);
3013: int retLabel = cfw.acquireLabel();
3014: cfw.markLabel(retLabel);
3015: ret.jsrPoints.add(Integer.valueOf(retLabel));
3016: }
3017:
3018: private void visitArrayLiteral(Node node, Node child) {
3019: int count = 0;
3020: for (Node cursor = child; cursor != null; cursor = cursor
3021: .getNext()) {
3022: ++count;
3023: }
3024: // load array to store array literal objects
3025: addNewObjectArray(count);
3026: for (int i = 0; i != count; ++i) {
3027: cfw.add(ByteCode.DUP);
3028: cfw.addPush(i);
3029: generateExpression(child, node);
3030: cfw.add(ByteCode.AASTORE);
3031: child = child.getNext();
3032: }
3033: int[] skipIndexes = (int[]) node
3034: .getProp(Node.SKIP_INDEXES_PROP);
3035: if (skipIndexes == null) {
3036: cfw.add(ByteCode.ACONST_NULL);
3037: cfw.add(ByteCode.ICONST_0);
3038: } else {
3039: cfw.addPush(OptRuntime.encodeIntArray(skipIndexes));
3040: cfw.addPush(skipIndexes.length);
3041: }
3042: cfw.addALoad(contextLocal);
3043: cfw.addALoad(variableObjectLocal);
3044: addOptRuntimeInvoke("newArrayLiteral", "([Ljava/lang/Object;"
3045: + "Ljava/lang/String;" + "I"
3046: + "Lorg/mozilla/javascript/Context;"
3047: + "Lorg/mozilla/javascript/Scriptable;"
3048: + ")Lorg/mozilla/javascript/Scriptable;");
3049: }
3050:
3051: private void visitObjectLiteral(Node node, Node child) {
3052: Object[] properties = (Object[]) node
3053: .getProp(Node.OBJECT_IDS_PROP);
3054: int count = properties.length;
3055:
3056: // load array with property ids
3057: addNewObjectArray(count);
3058: for (int i = 0; i != count; ++i) {
3059: cfw.add(ByteCode.DUP);
3060: cfw.addPush(i);
3061: Object id = properties[i];
3062: if (id instanceof String) {
3063: cfw.addPush((String) id);
3064: } else {
3065: cfw.addPush(((Integer) id).intValue());
3066: addScriptRuntimeInvoke("wrapInt",
3067: "(I)Ljava/lang/Integer;");
3068: }
3069: cfw.add(ByteCode.AASTORE);
3070: }
3071: // load array with property values
3072: addNewObjectArray(count);
3073: Node child2 = child;
3074: for (int i = 0; i != count; ++i) {
3075: cfw.add(ByteCode.DUP);
3076: cfw.addPush(i);
3077: int childType = child.getType();
3078: if (childType == Token.GET) {
3079: generateExpression(child.getFirstChild(), node);
3080: } else if (childType == Token.SET) {
3081: generateExpression(child.getFirstChild(), node);
3082: } else {
3083: generateExpression(child, node);
3084: }
3085: cfw.add(ByteCode.AASTORE);
3086: child = child.getNext();
3087: }
3088: // load array with getterSetter values
3089: cfw.addPush(count);
3090: cfw.add(ByteCode.NEWARRAY, ByteCode.T_INT);
3091: for (int i = 0; i != count; ++i) {
3092: cfw.add(ByteCode.DUP);
3093: cfw.addPush(i);
3094: int childType = child2.getType();
3095: if (childType == Token.GET) {
3096: cfw.add(ByteCode.ICONST_M1);
3097: } else if (childType == Token.SET) {
3098: cfw.add(ByteCode.ICONST_1);
3099: } else {
3100: cfw.add(ByteCode.ICONST_0);
3101: }
3102: cfw.add(ByteCode.IASTORE);
3103: child2 = child2.getNext();
3104: }
3105:
3106: cfw.addALoad(contextLocal);
3107: cfw.addALoad(variableObjectLocal);
3108: addScriptRuntimeInvoke("newObjectLiteral",
3109: "([Ljava/lang/Object;" + "[Ljava/lang/Object;" + "[I"
3110: + "Lorg/mozilla/javascript/Context;"
3111: + "Lorg/mozilla/javascript/Scriptable;"
3112: + ")Lorg/mozilla/javascript/Scriptable;");
3113: }
3114:
3115: private void visitSpecialCall(Node node, int type, int specialType,
3116: Node child) {
3117: cfw.addALoad(contextLocal);
3118:
3119: if (type == Token.NEW) {
3120: generateExpression(child, node);
3121: // stack: ... cx functionObj
3122: } else {
3123: generateFunctionAndThisObj(child, node);
3124: // stack: ... cx functionObj thisObj
3125: }
3126: child = child.getNext();
3127:
3128: generateCallArgArray(node, child, false);
3129:
3130: String methodName;
3131: String callSignature;
3132:
3133: if (type == Token.NEW) {
3134: methodName = "newObjectSpecial";
3135: callSignature = "(Lorg/mozilla/javascript/Context;"
3136: + "Ljava/lang/Object;" + "[Ljava/lang/Object;"
3137: + "Lorg/mozilla/javascript/Scriptable;"
3138: + "Lorg/mozilla/javascript/Scriptable;" + "I" // call type
3139: + ")Ljava/lang/Object;";
3140: cfw.addALoad(variableObjectLocal);
3141: cfw.addALoad(this ObjLocal);
3142: cfw.addPush(specialType);
3143: } else {
3144: methodName = "callSpecial";
3145: callSignature = "(Lorg/mozilla/javascript/Context;"
3146: + "Lorg/mozilla/javascript/Callable;"
3147: + "Lorg/mozilla/javascript/Scriptable;"
3148: + "[Ljava/lang/Object;"
3149: + "Lorg/mozilla/javascript/Scriptable;"
3150: + "Lorg/mozilla/javascript/Scriptable;" + "I" // call type
3151: + "Ljava/lang/String;I" // filename, linenumber
3152: + ")Ljava/lang/Object;";
3153: cfw.addALoad(variableObjectLocal);
3154: cfw.addALoad(this ObjLocal);
3155: cfw.addPush(specialType);
3156: String sourceName = scriptOrFn.getSourceName();
3157: cfw.addPush(sourceName == null ? "" : sourceName);
3158: cfw.addPush(itsLineNumber);
3159: }
3160:
3161: addOptRuntimeInvoke(methodName, callSignature);
3162: }
3163:
3164: private void visitStandardCall(Node node, Node child) {
3165: if (node.getType() != Token.CALL)
3166: throw Codegen.badTree();
3167:
3168: Node firstArgChild = child.getNext();
3169: int childType = child.getType();
3170:
3171: String methodName;
3172: String signature;
3173:
3174: if (firstArgChild == null) {
3175: if (childType == Token.NAME) {
3176: // name() call
3177: String name = child.getString();
3178: cfw.addPush(name);
3179: methodName = "callName0";
3180: signature = "(Ljava/lang/String;"
3181: + "Lorg/mozilla/javascript/Context;"
3182: + "Lorg/mozilla/javascript/Scriptable;"
3183: + ")Ljava/lang/Object;";
3184: } else if (childType == Token.GETPROP) {
3185: // x.name() call
3186: Node propTarget = child.getFirstChild();
3187: generateExpression(propTarget, node);
3188: Node id = propTarget.getNext();
3189: String property = id.getString();
3190: cfw.addPush(property);
3191: methodName = "callProp0";
3192: signature = "(Ljava/lang/Object;"
3193: + "Ljava/lang/String;"
3194: + "Lorg/mozilla/javascript/Context;"
3195: + "Lorg/mozilla/javascript/Scriptable;"
3196: + ")Ljava/lang/Object;";
3197: } else if (childType == Token.GETPROPNOWARN) {
3198: throw Kit.codeBug();
3199: } else {
3200: generateFunctionAndThisObj(child, node);
3201: methodName = "call0";
3202: signature = "(Lorg/mozilla/javascript/Callable;"
3203: + "Lorg/mozilla/javascript/Scriptable;"
3204: + "Lorg/mozilla/javascript/Context;"
3205: + "Lorg/mozilla/javascript/Scriptable;"
3206: + ")Ljava/lang/Object;";
3207: }
3208:
3209: } else if (childType == Token.NAME) {
3210: // XXX: this optimization is only possible if name
3211: // resolution
3212: // is not affected by arguments evaluation and currently
3213: // there are no checks for it
3214: String name = child.getString();
3215: generateCallArgArray(node, firstArgChild, false);
3216: cfw.addPush(name);
3217: methodName = "callName";
3218: signature = "([Ljava/lang/Object;" + "Ljava/lang/String;"
3219: + "Lorg/mozilla/javascript/Context;"
3220: + "Lorg/mozilla/javascript/Scriptable;"
3221: + ")Ljava/lang/Object;";
3222: } else {
3223: int argCount = 0;
3224: for (Node arg = firstArgChild; arg != null; arg = arg
3225: .getNext()) {
3226: ++argCount;
3227: }
3228: generateFunctionAndThisObj(child, node);
3229: // stack: ... functionObj thisObj
3230: if (argCount == 1) {
3231: generateExpression(firstArgChild, node);
3232: methodName = "call1";
3233: signature = "(Lorg/mozilla/javascript/Callable;"
3234: + "Lorg/mozilla/javascript/Scriptable;"
3235: + "Ljava/lang/Object;"
3236: + "Lorg/mozilla/javascript/Context;"
3237: + "Lorg/mozilla/javascript/Scriptable;"
3238: + ")Ljava/lang/Object;";
3239: } else if (argCount == 2) {
3240: generateExpression(firstArgChild, node);
3241: generateExpression(firstArgChild.getNext(), node);
3242: methodName = "call2";
3243: signature = "(Lorg/mozilla/javascript/Callable;"
3244: + "Lorg/mozilla/javascript/Scriptable;"
3245: + "Ljava/lang/Object;" + "Ljava/lang/Object;"
3246: + "Lorg/mozilla/javascript/Context;"
3247: + "Lorg/mozilla/javascript/Scriptable;"
3248: + ")Ljava/lang/Object;";
3249: } else {
3250: generateCallArgArray(node, firstArgChild, false);
3251: methodName = "callN";
3252: signature = "(Lorg/mozilla/javascript/Callable;"
3253: + "Lorg/mozilla/javascript/Scriptable;"
3254: + "[Ljava/lang/Object;"
3255: + "Lorg/mozilla/javascript/Context;"
3256: + "Lorg/mozilla/javascript/Scriptable;"
3257: + ")Ljava/lang/Object;";
3258: }
3259: }
3260:
3261: cfw.addALoad(contextLocal);
3262: cfw.addALoad(variableObjectLocal);
3263: addOptRuntimeInvoke(methodName, signature);
3264: }
3265:
3266: private void visitStandardNew(Node node, Node child) {
3267: if (node.getType() != Token.NEW)
3268: throw Codegen.badTree();
3269:
3270: Node firstArgChild = child.getNext();
3271:
3272: generateExpression(child, node);
3273: // stack: ... functionObj
3274: cfw.addALoad(contextLocal);
3275: cfw.addALoad(variableObjectLocal);
3276: // stack: ... functionObj cx scope
3277: generateCallArgArray(node, firstArgChild, false);
3278: addScriptRuntimeInvoke("newObject", "(Ljava/lang/Object;"
3279: + "Lorg/mozilla/javascript/Context;"
3280: + "Lorg/mozilla/javascript/Scriptable;"
3281: + "[Ljava/lang/Object;"
3282: + ")Lorg/mozilla/javascript/Scriptable;");
3283: }
3284:
3285: private void visitOptimizedCall(Node node, OptFunctionNode target,
3286: int type, Node child) {
3287: Node firstArgChild = child.getNext();
3288:
3289: short this ObjLocal = 0;
3290: if (type == Token.NEW) {
3291: generateExpression(child, node);
3292: } else {
3293: generateFunctionAndThisObj(child, node);
3294: this ObjLocal = getNewWordLocal();
3295: cfw.addAStore(this ObjLocal);
3296: }
3297: // stack: ... functionObj
3298:
3299: int beyond = cfw.acquireLabel();
3300:
3301: int directTargetIndex = target.getDirectTargetIndex();
3302: if (isTopLevel) {
3303: cfw.add(ByteCode.ALOAD_0);
3304: } else {
3305: cfw.add(ByteCode.ALOAD_0);
3306: cfw.add(ByteCode.GETFIELD, codegen.mainClassName,
3307: Codegen.DIRECT_CALL_PARENT_FIELD,
3308: codegen.mainClassSignature);
3309: }
3310: cfw.add(ByteCode.GETFIELD, codegen.mainClassName, Codegen
3311: .getDirectTargetFieldName(directTargetIndex),
3312: codegen.mainClassSignature);
3313:
3314: cfw.add(ByteCode.DUP2);
3315: // stack: ... functionObj directFunct functionObj directFunct
3316:
3317: int regularCall = cfw.acquireLabel();
3318: cfw.add(ByteCode.IF_ACMPNE, regularCall);
3319:
3320: // stack: ... functionObj directFunct
3321: short stackHeight = cfw.getStackTop();
3322: cfw.add(ByteCode.SWAP);
3323: cfw.add(ByteCode.POP);
3324: // stack: ... directFunct
3325: if (compilerEnv.isUseDynamicScope()) {
3326: cfw.addALoad(contextLocal);
3327: cfw.addALoad(variableObjectLocal);
3328: } else {
3329: cfw.add(ByteCode.DUP);
3330: // stack: ... directFunct directFunct
3331: cfw.addInvoke(ByteCode.INVOKEINTERFACE,
3332: "org/mozilla/javascript/Scriptable",
3333: "getParentScope",
3334: "()Lorg/mozilla/javascript/Scriptable;");
3335: // stack: ... directFunct scope
3336: cfw.addALoad(contextLocal);
3337: // stack: ... directFunct scope cx
3338: cfw.add(ByteCode.SWAP);
3339: }
3340: // stack: ... directFunc cx scope
3341:
3342: if (type == Token.NEW) {
3343: cfw.add(ByteCode.ACONST_NULL);
3344: } else {
3345: cfw.addALoad(this ObjLocal);
3346: }
3347: // stack: ... directFunc cx scope thisObj
3348: /*
3349: Remember that directCall parameters are paired in 1 aReg and 1 dReg
3350: If the argument is an incoming arg, just pass the orginal pair thru.
3351: Else, if the argument is known to be typed 'Number', pass Void.TYPE
3352: in the aReg and the number is the dReg
3353: Else pass the JS object in the aReg and 0.0 in the dReg.
3354: */
3355: Node argChild = firstArgChild;
3356: while (argChild != null) {
3357: int dcp_register = nodeIsDirectCallParameter(argChild);
3358: if (dcp_register >= 0) {
3359: cfw.addALoad(dcp_register);
3360: cfw.addDLoad(dcp_register + 1);
3361: } else if (argChild.getIntProp(Node.ISNUMBER_PROP, -1) == Node.BOTH) {
3362: cfw.add(ByteCode.GETSTATIC, "java/lang/Void", "TYPE",
3363: "Ljava/lang/Class;");
3364: generateExpression(argChild, node);
3365: } else {
3366: generateExpression(argChild, node);
3367: cfw.addPush(0.0);
3368: }
3369: argChild = argChild.getNext();
3370: }
3371:
3372: cfw.add(ByteCode.GETSTATIC,
3373: "org/mozilla/javascript/ScriptRuntime", "emptyArgs",
3374: "[Ljava/lang/Object;");
3375: cfw.addInvoke(ByteCode.INVOKESTATIC, codegen.mainClassName,
3376: (type == Token.NEW) ? codegen
3377: .getDirectCtorName(target.fnode) : codegen
3378: .getBodyMethodName(target.fnode), codegen
3379: .getBodyMethodSignature(target.fnode));
3380:
3381: cfw.add(ByteCode.GOTO, beyond);
3382:
3383: cfw.markLabel(regularCall, stackHeight);
3384: // stack: ... functionObj directFunct
3385: cfw.add(ByteCode.POP);
3386: cfw.addALoad(contextLocal);
3387: cfw.addALoad(variableObjectLocal);
3388: // stack: ... functionObj cx scope
3389: if (type != Token.NEW) {
3390: cfw.addALoad(this ObjLocal);
3391: releaseWordLocal(this ObjLocal);
3392: // stack: ... functionObj cx scope thisObj
3393: }
3394: // XXX: this will generate code for the child array the second time,
3395: // so expression code generation better not to alter tree structure...
3396: generateCallArgArray(node, firstArgChild, true);
3397:
3398: if (type == Token.NEW) {
3399: addScriptRuntimeInvoke("newObject", "(Ljava/lang/Object;"
3400: + "Lorg/mozilla/javascript/Context;"
3401: + "Lorg/mozilla/javascript/Scriptable;"
3402: + "[Ljava/lang/Object;"
3403: + ")Lorg/mozilla/javascript/Scriptable;");
3404: } else {
3405: cfw.addInvoke(ByteCode.INVOKEINTERFACE,
3406: "org/mozilla/javascript/Callable", "call",
3407: "(Lorg/mozilla/javascript/Context;"
3408: + "Lorg/mozilla/javascript/Scriptable;"
3409: + "Lorg/mozilla/javascript/Scriptable;"
3410: + "[Ljava/lang/Object;"
3411: + ")Ljava/lang/Object;");
3412: }
3413:
3414: cfw.markLabel(beyond);
3415: }
3416:
3417: private void generateCallArgArray(Node node, Node argChild,
3418: boolean directCall) {
3419: int argCount = 0;
3420: for (Node child = argChild; child != null; child = child
3421: .getNext()) {
3422: ++argCount;
3423: }
3424: // load array object to set arguments
3425: if (argCount == 1 && itsOneArgArray >= 0) {
3426: cfw.addALoad(itsOneArgArray);
3427: } else {
3428: addNewObjectArray(argCount);
3429: }
3430: // Copy arguments into it
3431: for (int i = 0; i != argCount; ++i) {
3432: // If we are compiling a generator an argument could be the result
3433: // of a yield. In that case we will have an immediate on the stack
3434: // which we need to avoid
3435: if (!isGenerator) {
3436: cfw.add(ByteCode.DUP);
3437: cfw.addPush(i);
3438: }
3439:
3440: if (!directCall) {
3441: generateExpression(argChild, node);
3442: } else {
3443: // If this has also been a directCall sequence, the Number
3444: // flag will have remained set for any parameter so that
3445: // the values could be copied directly into the outgoing
3446: // args. Here we want to force it to be treated as not in
3447: // a Number context, so we set the flag off.
3448: int dcp_register = nodeIsDirectCallParameter(argChild);
3449: if (dcp_register >= 0) {
3450: dcpLoadAsObject(dcp_register);
3451: } else {
3452: generateExpression(argChild, node);
3453: int childNumberFlag = argChild.getIntProp(
3454: Node.ISNUMBER_PROP, -1);
3455: if (childNumberFlag == Node.BOTH) {
3456: addDoubleWrap();
3457: }
3458: }
3459: }
3460:
3461: // When compiling generators, any argument to a method may be a
3462: // yield expression. Hence we compile the argument first and then
3463: // load the argument index and assign the value to the args array.
3464: if (isGenerator) {
3465: short tempLocal = getNewWordLocal();
3466: cfw.addAStore(tempLocal);
3467: cfw.add(ByteCode.CHECKCAST, "[Ljava/lang/Object;");
3468: cfw.add(ByteCode.DUP);
3469: cfw.addPush(i);
3470: cfw.addALoad(tempLocal);
3471: releaseWordLocal(tempLocal);
3472: }
3473:
3474: cfw.add(ByteCode.AASTORE);
3475:
3476: argChild = argChild.getNext();
3477: }
3478: }
3479:
3480: private void generateFunctionAndThisObj(Node node, Node parent) {
3481: // Place on stack (function object, function this) pair
3482: int type = node.getType();
3483: switch (node.getType()) {
3484: case Token.GETPROPNOWARN:
3485: throw Kit.codeBug();
3486:
3487: case Token.GETPROP:
3488: case Token.GETELEM: {
3489: Node target = node.getFirstChild();
3490: generateExpression(target, node);
3491: Node id = target.getNext();
3492: if (type == Token.GETPROP) {
3493: String property = id.getString();
3494: cfw.addPush(property);
3495: cfw.addALoad(contextLocal);
3496: addScriptRuntimeInvoke("getPropFunctionAndThis",
3497: "(Ljava/lang/Object;" + "Ljava/lang/String;"
3498: + "Lorg/mozilla/javascript/Context;"
3499: + ")Lorg/mozilla/javascript/Callable;");
3500: } else {
3501: // Optimizer do not optimize this case for now
3502: if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1)
3503: throw Codegen.badTree();
3504: generateExpression(id, node); // id
3505: cfw.addALoad(contextLocal);
3506: addScriptRuntimeInvoke("getElemFunctionAndThis",
3507: "(Ljava/lang/Object;" + "Ljava/lang/Object;"
3508: + "Lorg/mozilla/javascript/Context;"
3509: + ")Lorg/mozilla/javascript/Callable;");
3510: }
3511: break;
3512: }
3513:
3514: case Token.NAME: {
3515: String name = node.getString();
3516: cfw.addPush(name);
3517: cfw.addALoad(contextLocal);
3518: cfw.addALoad(variableObjectLocal);
3519: addScriptRuntimeInvoke("getNameFunctionAndThis",
3520: "(Ljava/lang/String;"
3521: + "Lorg/mozilla/javascript/Context;"
3522: + "Lorg/mozilla/javascript/Scriptable;"
3523: + ")Lorg/mozilla/javascript/Callable;");
3524: break;
3525: }
3526:
3527: default: // including GETVAR
3528: generateExpression(node, parent);
3529: cfw.addALoad(contextLocal);
3530: addScriptRuntimeInvoke("getValueFunctionAndThis",
3531: "(Ljava/lang/Object;"
3532: + "Lorg/mozilla/javascript/Context;"
3533: + ")Lorg/mozilla/javascript/Callable;");
3534: break;
3535: }
3536: // Get thisObj prepared by get(Name|Prop|Elem|Value)FunctionAndThis
3537: cfw.addALoad(contextLocal);
3538: addScriptRuntimeInvoke("lastStoredScriptable",
3539: "(Lorg/mozilla/javascript/Context;"
3540: + ")Lorg/mozilla/javascript/Scriptable;");
3541: }
3542:
3543: private void updateLineNumber(Node node) {
3544: itsLineNumber = node.getLineno();
3545: if (itsLineNumber == -1)
3546: return;
3547: cfw.addLineNumberEntry((short) itsLineNumber);
3548: }
3549:
3550: private void visitTryCatchFinally(Node.Jump node, Node child) {
3551: /* Save the variable object, in case there are with statements
3552: * enclosed by the try block and we catch some exception.
3553: * We'll restore it for the catch block so that catch block
3554: * statements get the right scope.
3555: */
3556:
3557: // OPT we only need to do this if there are enclosed WITH
3558: // statements; could statically check and omit this if there aren't any.
3559: // XXX OPT Maybe instead do syntactic transforms to associate
3560: // each 'with' with a try/finally block that does the exitwith.
3561: short savedVariableObject = getNewWordLocal();
3562: cfw.addALoad(variableObjectLocal);
3563: cfw.addAStore(savedVariableObject);
3564:
3565: /*
3566: * Generate the code for the tree; most of the work is done in IRFactory
3567: * and NodeTransformer; Codegen just adds the java handlers for the
3568: * javascript catch and finally clauses. */
3569:
3570: int startLabel = cfw.acquireLabel();
3571: cfw.markLabel(startLabel, (short) 0);
3572:
3573: Node catchTarget = node.target;
3574: Node finallyTarget = node.getFinally();
3575:
3576: // create a table for the equivalent of JSR returns
3577: if (isGenerator && finallyTarget != null) {
3578: FinallyReturnPoint ret = new FinallyReturnPoint();
3579: if (finallys == null) {
3580: finallys = new Hashtable();
3581: }
3582: // add the finally target to hashtable
3583: finallys.put(finallyTarget, ret);
3584: // add the finally node as well to the hash table
3585: finallys.put(finallyTarget.getNext(), ret);
3586: }
3587:
3588: while (child != null) {
3589: generateStatement(child);
3590: child = child.getNext();
3591: }
3592:
3593: // control flow skips the handlers
3594: int realEnd = cfw.acquireLabel();
3595: cfw.add(ByteCode.GOTO, realEnd);
3596:
3597: int exceptionLocal = getLocalBlockRegister(node);
3598: // javascript handler; unwrap exception and GOTO to javascript
3599: // catch area.
3600: if (catchTarget != null) {
3601: // get the label to goto
3602: int catchLabel = catchTarget.labelId();
3603:
3604: generateCatchBlock(JAVASCRIPT_EXCEPTION,
3605: savedVariableObject, catchLabel, startLabel,
3606: exceptionLocal);
3607: /*
3608: * catch WrappedExceptions, see if they are wrapped
3609: * JavaScriptExceptions. Otherwise, rethrow.
3610: */
3611: generateCatchBlock(EVALUATOR_EXCEPTION,
3612: savedVariableObject, catchLabel, startLabel,
3613: exceptionLocal);
3614:
3615: /*
3616: we also need to catch EcmaErrors and feed the
3617: associated error object to the handler
3618: */
3619: generateCatchBlock(ECMAERROR_EXCEPTION,
3620: savedVariableObject, catchLabel, startLabel,
3621: exceptionLocal);
3622:
3623: Context cx = Context.getCurrentContext();
3624: if (cx != null
3625: && cx
3626: .hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)) {
3627: generateCatchBlock(THROWABLE_EXCEPTION,
3628: savedVariableObject, catchLabel, startLabel,
3629: exceptionLocal);
3630: }
3631: }
3632:
3633: // finally handler; catch all exceptions, store to a local; JSR to
3634: // the finally, then re-throw.
3635: if (finallyTarget != null) {
3636: int finallyHandler = cfw.acquireLabel();
3637: cfw.markHandler(finallyHandler);
3638: cfw.addAStore(exceptionLocal);
3639:
3640: // reset the variable object local
3641: cfw.addALoad(savedVariableObject);
3642: cfw.addAStore(variableObjectLocal);
3643:
3644: // get the label to JSR to
3645: int finallyLabel = finallyTarget.labelId();
3646: if (isGenerator)
3647: addGotoWithReturn(finallyTarget);
3648: else
3649: cfw.add(ByteCode.JSR, finallyLabel);
3650:
3651: // rethrow
3652: cfw.addALoad(exceptionLocal);
3653: if (isGenerator)
3654: cfw.add(ByteCode.CHECKCAST, "java/lang/Throwable");
3655: cfw.add(ByteCode.ATHROW);
3656:
3657: // mark the handler
3658: cfw.addExceptionHandler(startLabel, finallyLabel,
3659: finallyHandler, null); // catch any
3660: }
3661: releaseWordLocal(savedVariableObject);
3662: cfw.markLabel(realEnd);
3663: }
3664:
3665: private static final int JAVASCRIPT_EXCEPTION = 0;
3666: private static final int EVALUATOR_EXCEPTION = 1;
3667: private static final int ECMAERROR_EXCEPTION = 2;
3668: private static final int THROWABLE_EXCEPTION = 3;
3669:
3670: private void generateCatchBlock(int exceptionType,
3671: short savedVariableObject, int catchLabel, int startLabel,
3672: int exceptionLocal) {
3673: int handler = cfw.acquireLabel();
3674: cfw.markHandler(handler);
3675:
3676: // MS JVM gets cranky if the exception object is left on the stack
3677: cfw.addAStore(exceptionLocal);
3678:
3679: // reset the variable object local
3680: cfw.addALoad(savedVariableObject);
3681: cfw.addAStore(variableObjectLocal);
3682:
3683: String exceptionName;
3684: if (exceptionType == JAVASCRIPT_EXCEPTION) {
3685: exceptionName = "org/mozilla/javascript/JavaScriptException";
3686: } else if (exceptionType == EVALUATOR_EXCEPTION) {
3687: exceptionName = "org/mozilla/javascript/EvaluatorException";
3688: } else if (exceptionType == ECMAERROR_EXCEPTION) {
3689: exceptionName = "org/mozilla/javascript/EcmaError";
3690: } else if (exceptionType == THROWABLE_EXCEPTION) {
3691: exceptionName = "java/lang/Throwable";
3692: } else {
3693: throw Kit.codeBug();
3694: }
3695:
3696: // mark the handler
3697: cfw.addExceptionHandler(startLabel, catchLabel, handler,
3698: exceptionName);
3699:
3700: cfw.add(ByteCode.GOTO, catchLabel);
3701: }
3702:
3703: private boolean generateSaveLocals(Node node) {
3704: int count = 0;
3705: for (int i = 0; i < firstFreeLocal; i++) {
3706: if (locals[i] != 0)
3707: count++;
3708: }
3709:
3710: if (count == 0) {
3711: ((FunctionNode) scriptOrFn).addLiveLocals(node, null);
3712: return false;
3713: }
3714:
3715: // calculate the max locals
3716: maxLocals = maxLocals > count ? maxLocals : count;
3717:
3718: // create a locals list
3719: int[] ls = new int[count];
3720: int s = 0;
3721: for (int i = 0; i < firstFreeLocal; i++) {
3722: if (locals[i] != 0) {
3723: ls[s] = i;
3724: s++;
3725: }
3726: }
3727:
3728: // save the locals
3729: ((FunctionNode) scriptOrFn).addLiveLocals(node, ls);
3730:
3731: // save locals
3732: generateGetGeneratorLocalsState();
3733: for (int i = 0; i < count; i++) {
3734: cfw.add(ByteCode.DUP);
3735: cfw.addLoadConstant(i);
3736: cfw.addALoad(ls[i]);
3737: cfw.add(ByteCode.AASTORE);
3738: }
3739: // pop the array off the stack
3740: cfw.add(ByteCode.POP);
3741:
3742: return true;
3743: }
3744:
3745: private void visitSwitch(Node.Jump switchNode, Node child) {
3746: // See comments in IRFactory.createSwitch() for description
3747: // of SWITCH node
3748:
3749: generateExpression(child, switchNode);
3750: // save selector value
3751: short selector = getNewWordLocal();
3752: cfw.addAStore(selector);
3753:
3754: for (Node.Jump caseNode = (Node.Jump) child.getNext(); caseNode != null; caseNode = (Node.Jump) caseNode
3755: .getNext()) {
3756: if (caseNode.getType() != Token.CASE)
3757: throw Codegen.badTree();
3758: Node test = caseNode.getFirstChild();
3759: generateExpression(test, caseNode);
3760: cfw.addALoad(selector);
3761: addScriptRuntimeInvoke("shallowEq", "(Ljava/lang/Object;"
3762: + "Ljava/lang/Object;" + ")Z");
3763: addGoto(caseNode.target, ByteCode.IFNE);
3764: }
3765: releaseWordLocal(selector);
3766: }
3767:
3768: private void visitTypeofname(Node node) {
3769: if (hasVarsInRegs) {
3770: int varIndex = fnCurrent.fnode.getIndexForNameNode(node);
3771: if (varIndex >= 0) {
3772: if (fnCurrent.isNumberVar(varIndex)) {
3773: cfw.addPush("number");
3774: } else if (varIsDirectCallParameter(varIndex)) {
3775: int dcp_register = varRegisters[varIndex];
3776: cfw.addALoad(dcp_register);
3777: cfw.add(ByteCode.GETSTATIC, "java/lang/Void",
3778: "TYPE", "Ljava/lang/Class;");
3779: int isNumberLabel = cfw.acquireLabel();
3780: cfw.add(ByteCode.IF_ACMPEQ, isNumberLabel);
3781: short stack = cfw.getStackTop();
3782: cfw.addALoad(dcp_register);
3783: addScriptRuntimeInvoke("typeof",
3784: "(Ljava/lang/Object;"
3785: + ")Ljava/lang/String;");
3786: int beyond = cfw.acquireLabel();
3787: cfw.add(ByteCode.GOTO, beyond);
3788: cfw.markLabel(isNumberLabel, stack);
3789: cfw.addPush("number");
3790: cfw.markLabel(beyond);
3791: } else {
3792: cfw.addALoad(varRegisters[varIndex]);
3793: addScriptRuntimeInvoke("typeof",
3794: "(Ljava/lang/Object;"
3795: + ")Ljava/lang/String;");
3796: }
3797: return;
3798: }
3799: }
3800: cfw.addALoad(variableObjectLocal);
3801: cfw.addPush(node.getString());
3802: addScriptRuntimeInvoke("typeofName",
3803: "(Lorg/mozilla/javascript/Scriptable;"
3804: + "Ljava/lang/String;" + ")Ljava/lang/String;");
3805: }
3806:
3807: /**
3808: * Save the current code offset. This saved code offset is used to
3809: * compute instruction counts in subsequent calls to
3810: * {@link #addInstructionCount}.
3811: */
3812: private void saveCurrentCodeOffset() {
3813: savedCodeOffset = cfw.getCurrentCodeOffset();
3814: }
3815:
3816: /**
3817: * Generate calls to ScriptRuntime.addInstructionCount to keep track of
3818: * executed instructions and call <code>observeInstructionCount()</code>
3819: * if a threshold is exceeded.
3820: */
3821: private void addInstructionCount() {
3822: int count = cfw.getCurrentCodeOffset() - savedCodeOffset;
3823: if (count == 0)
3824: return;
3825: cfw.addALoad(contextLocal);
3826: cfw.addPush(count);
3827: addScriptRuntimeInvoke("addInstructionCount",
3828: "(Lorg/mozilla/javascript/Context;" + "I)V");
3829: }
3830:
3831: private void visitIncDec(Node node) {
3832: int incrDecrMask = node.getExistingIntProp(Node.INCRDECR_PROP);
3833: Node child = node.getFirstChild();
3834: switch (child.getType()) {
3835: case Token.GETVAR:
3836: if (!hasVarsInRegs)
3837: Kit.codeBug();
3838: if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
3839: boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
3840: int varIndex = fnCurrent.getVarIndex(child);
3841: short reg = varRegisters[varIndex];
3842: int offset = varIsDirectCallParameter(varIndex) ? 1 : 0;
3843: cfw.addDLoad(reg + offset);
3844: if (post) {
3845: cfw.add(ByteCode.DUP2);
3846: }
3847: cfw.addPush(1.0);
3848: if ((incrDecrMask & Node.DECR_FLAG) == 0) {
3849: cfw.add(ByteCode.DADD);
3850: } else {
3851: cfw.add(ByteCode.DSUB);
3852: }
3853: if (!post) {
3854: cfw.add(ByteCode.DUP2);
3855: }
3856: cfw.addDStore(reg + offset);
3857: } else {
3858: boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
3859: int varIndex = fnCurrent.getVarIndex(child);
3860: short reg = varRegisters[varIndex];
3861: cfw.addALoad(reg);
3862: if (post) {
3863: cfw.add(ByteCode.DUP);
3864: }
3865: addObjectToDouble();
3866: cfw.addPush(1.0);
3867: if ((incrDecrMask & Node.DECR_FLAG) == 0) {
3868: cfw.add(ByteCode.DADD);
3869: } else {
3870: cfw.add(ByteCode.DSUB);
3871: }
3872: addDoubleWrap();
3873: if (!post) {
3874: cfw.add(ByteCode.DUP);
3875: }
3876: cfw.addAStore(reg);
3877: break;
3878: }
3879: break;
3880: case Token.NAME:
3881: cfw.addALoad(variableObjectLocal);
3882: cfw.addPush(child.getString()); // push name
3883: cfw.addALoad(contextLocal);
3884: cfw.addPush(incrDecrMask);
3885: addScriptRuntimeInvoke("nameIncrDecr",
3886: "(Lorg/mozilla/javascript/Scriptable;"
3887: + "Ljava/lang/String;"
3888: + "Lorg/mozilla/javascript/Context;"
3889: + "I)Ljava/lang/Object;");
3890: break;
3891: case Token.GETPROPNOWARN:
3892: throw Kit.codeBug();
3893: case Token.GETPROP: {
3894: Node getPropChild = child.getFirstChild();
3895: generateExpression(getPropChild, node);
3896: generateExpression(getPropChild.getNext(), node);
3897: cfw.addALoad(contextLocal);
3898: cfw.addPush(incrDecrMask);
3899: addScriptRuntimeInvoke("propIncrDecr",
3900: "(Ljava/lang/Object;" + "Ljava/lang/String;"
3901: + "Lorg/mozilla/javascript/Context;"
3902: + "I)Ljava/lang/Object;");
3903: break;
3904: }
3905: case Token.GETELEM: {
3906: Node elemChild = child.getFirstChild();
3907: generateExpression(elemChild, node);
3908: generateExpression(elemChild.getNext(), node);
3909: cfw.addALoad(contextLocal);
3910: cfw.addPush(incrDecrMask);
3911: if (elemChild.getNext().getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
3912: addOptRuntimeInvoke("elemIncrDecr",
3913: "(Ljava/lang/Object;" + "D"
3914: + "Lorg/mozilla/javascript/Context;"
3915: + "I" + ")Ljava/lang/Object;");
3916: } else {
3917: addScriptRuntimeInvoke("elemIncrDecr",
3918: "(Ljava/lang/Object;" + "Ljava/lang/Object;"
3919: + "Lorg/mozilla/javascript/Context;"
3920: + "I" + ")Ljava/lang/Object;");
3921: }
3922: break;
3923: }
3924: case Token.GET_REF: {
3925: Node refChild = child.getFirstChild();
3926: generateExpression(refChild, node);
3927: cfw.addALoad(contextLocal);
3928: cfw.addPush(incrDecrMask);
3929: addScriptRuntimeInvoke("refIncrDecr",
3930: "(Lorg/mozilla/javascript/Ref;"
3931: + "Lorg/mozilla/javascript/Context;"
3932: + "I)Ljava/lang/Object;");
3933: break;
3934: }
3935: default:
3936: Codegen.badTree();
3937: }
3938: }
3939:
3940: private static boolean isArithmeticNode(Node node) {
3941: int type = node.getType();
3942: return (type == Token.SUB) || (type == Token.MOD)
3943: || (type == Token.DIV) || (type == Token.MUL);
3944: }
3945:
3946: private void visitArithmetic(Node node, int opCode, Node child,
3947: Node parent) {
3948: int childNumberFlag = node.getIntProp(Node.ISNUMBER_PROP, -1);
3949: if (childNumberFlag != -1) {
3950: generateExpression(child, node);
3951: generateExpression(child.getNext(), node);
3952: cfw.add(opCode);
3953: } else {
3954: boolean childOfArithmetic = isArithmeticNode(parent);
3955: generateExpression(child, node);
3956: if (!isArithmeticNode(child))
3957: addObjectToDouble();
3958: generateExpression(child.getNext(), node);
3959: if (!isArithmeticNode(child.getNext()))
3960: addObjectToDouble();
3961: cfw.add(opCode);
3962: if (!childOfArithmetic) {
3963: addDoubleWrap();
3964: }
3965: }
3966: }
3967:
3968: private void visitBitOp(Node node, int type, Node child) {
3969: int childNumberFlag = node.getIntProp(Node.ISNUMBER_PROP, -1);
3970: generateExpression(child, node);
3971:
3972: // special-case URSH; work with the target arg as a long, so
3973: // that we can return a 32-bit unsigned value, and call
3974: // toUint32 instead of toInt32.
3975: if (type == Token.URSH) {
3976: addScriptRuntimeInvoke("toUint32", "(Ljava/lang/Object;)J");
3977: generateExpression(child.getNext(), node);
3978: addScriptRuntimeInvoke("toInt32", "(Ljava/lang/Object;)I");
3979: // Looks like we need to explicitly mask the shift to 5 bits -
3980: // LUSHR takes 6 bits.
3981: cfw.addPush(31);
3982: cfw.add(ByteCode.IAND);
3983: cfw.add(ByteCode.LUSHR);
3984: cfw.add(ByteCode.L2D);
3985: addDoubleWrap();
3986: return;
3987: }
3988: if (childNumberFlag == -1) {
3989: addScriptRuntimeInvoke("toInt32", "(Ljava/lang/Object;)I");
3990: generateExpression(child.getNext(), node);
3991: addScriptRuntimeInvoke("toInt32", "(Ljava/lang/Object;)I");
3992: } else {
3993: addScriptRuntimeInvoke("toInt32", "(D)I");
3994: generateExpression(child.getNext(), node);
3995: addScriptRuntimeInvoke("toInt32", "(D)I");
3996: }
3997: switch (type) {
3998: case Token.BITOR:
3999: cfw.add(ByteCode.IOR);
4000: break;
4001: case Token.BITXOR:
4002: cfw.add(ByteCode.IXOR);
4003: break;
4004: case Token.BITAND:
4005: cfw.add(ByteCode.IAND);
4006: break;
4007: case Token.RSH:
4008: cfw.add(ByteCode.ISHR);
4009: break;
4010: case Token.LSH:
4011: cfw.add(ByteCode.ISHL);
4012: break;
4013: default:
4014: throw Codegen.badTree();
4015: }
4016: cfw.add(ByteCode.I2D);
4017: if (childNumberFlag == -1) {
4018: addDoubleWrap();
4019: }
4020: }
4021:
4022: private int nodeIsDirectCallParameter(Node node) {
4023: if (node.getType() == Token.GETVAR && inDirectCallFunction
4024: && !itsForcedObjectParameters) {
4025: int varIndex = fnCurrent.getVarIndex(node);
4026: if (fnCurrent.isParameter(varIndex)) {
4027: return varRegisters[varIndex];
4028: }
4029: }
4030: return -1;
4031: }
4032:
4033: private boolean varIsDirectCallParameter(int varIndex) {
4034: return fnCurrent.isParameter(varIndex) && inDirectCallFunction
4035: && !itsForcedObjectParameters;
4036: }
4037:
4038: private void genSimpleCompare(int type, int trueGOTO, int falseGOTO) {
4039: if (trueGOTO == -1)
4040: throw Codegen.badTree();
4041: switch (type) {
4042: case Token.LE:
4043: cfw.add(ByteCode.DCMPG);
4044: cfw.add(ByteCode.IFLE, trueGOTO);
4045: break;
4046: case Token.GE:
4047: cfw.add(ByteCode.DCMPL);
4048: cfw.add(ByteCode.IFGE, trueGOTO);
4049: break;
4050: case Token.LT:
4051: cfw.add(ByteCode.DCMPG);
4052: cfw.add(ByteCode.IFLT, trueGOTO);
4053: break;
4054: case Token.GT:
4055: cfw.add(ByteCode.DCMPL);
4056: cfw.add(ByteCode.IFGT, trueGOTO);
4057: break;
4058: default:
4059: throw Codegen.badTree();
4060:
4061: }
4062: if (falseGOTO != -1)
4063: cfw.add(ByteCode.GOTO, falseGOTO);
4064: }
4065:
4066: private void visitIfJumpRelOp(Node node, Node child, int trueGOTO,
4067: int falseGOTO) {
4068: if (trueGOTO == -1 || falseGOTO == -1)
4069: throw Codegen.badTree();
4070: int type = node.getType();
4071: Node rChild = child.getNext();
4072: if (type == Token.INSTANCEOF || type == Token.IN) {
4073: generateExpression(child, node);
4074: generateExpression(rChild, node);
4075: cfw.addALoad(contextLocal);
4076: addScriptRuntimeInvoke(
4077: (type == Token.INSTANCEOF) ? "instanceOf" : "in",
4078: "(Ljava/lang/Object;" + "Ljava/lang/Object;"
4079: + "Lorg/mozilla/javascript/Context;" + ")Z");
4080: cfw.add(ByteCode.IFNE, trueGOTO);
4081: cfw.add(ByteCode.GOTO, falseGOTO);
4082: return;
4083: }
4084: int childNumberFlag = node.getIntProp(Node.ISNUMBER_PROP, -1);
4085: int left_dcp_register = nodeIsDirectCallParameter(child);
4086: int right_dcp_register = nodeIsDirectCallParameter(rChild);
4087: if (childNumberFlag != -1) {
4088: // Force numeric context on both parameters and optimize
4089: // direct call case as Optimizer currently does not handle it
4090:
4091: if (childNumberFlag != Node.RIGHT) {
4092: // Left already has number content
4093: generateExpression(child, node);
4094: } else if (left_dcp_register != -1) {
4095: dcpLoadAsNumber(left_dcp_register);
4096: } else {
4097: generateExpression(child, node);
4098: addObjectToDouble();
4099: }
4100:
4101: if (childNumberFlag != Node.LEFT) {
4102: // Right already has number content
4103: generateExpression(rChild, node);
4104: } else if (right_dcp_register != -1) {
4105: dcpLoadAsNumber(right_dcp_register);
4106: } else {
4107: generateExpression(rChild, node);
4108: addObjectToDouble();
4109: }
4110:
4111: genSimpleCompare(type, trueGOTO, falseGOTO);
4112:
4113: } else {
4114: if (left_dcp_register != -1 && right_dcp_register != -1) {
4115: // Generate code to dynamically check for number content
4116: // if both operands are dcp
4117: short stack = cfw.getStackTop();
4118: int leftIsNotNumber = cfw.acquireLabel();
4119: cfw.addALoad(left_dcp_register);
4120: cfw.add(ByteCode.GETSTATIC, "java/lang/Void", "TYPE",
4121: "Ljava/lang/Class;");
4122: cfw.add(ByteCode.IF_ACMPNE, leftIsNotNumber);
4123: cfw.addDLoad(left_dcp_register + 1);
4124: dcpLoadAsNumber(right_dcp_register);
4125: genSimpleCompare(type, trueGOTO, falseGOTO);
4126: if (stack != cfw.getStackTop())
4127: throw Codegen.badTree();
4128:
4129: cfw.markLabel(leftIsNotNumber);
4130: int rightIsNotNumber = cfw.acquireLabel();
4131: cfw.addALoad(right_dcp_register);
4132: cfw.add(ByteCode.GETSTATIC, "java/lang/Void", "TYPE",
4133: "Ljava/lang/Class;");
4134: cfw.add(ByteCode.IF_ACMPNE, rightIsNotNumber);
4135: cfw.addALoad(left_dcp_register);
4136: addObjectToDouble();
4137: cfw.addDLoad(right_dcp_register + 1);
4138: genSimpleCompare(type, trueGOTO, falseGOTO);
4139: if (stack != cfw.getStackTop())
4140: throw Codegen.badTree();
4141:
4142: cfw.markLabel(rightIsNotNumber);
4143: // Load both register as objects to call generic cmp_*
4144: cfw.addALoad(left_dcp_register);
4145: cfw.addALoad(right_dcp_register);
4146:
4147: } else {
4148: generateExpression(child, node);
4149: generateExpression(rChild, node);
4150: }
4151:
4152: if (type == Token.GE || type == Token.GT) {
4153: cfw.add(ByteCode.SWAP);
4154: }
4155: String routine = ((type == Token.LT) || (type == Token.GT)) ? "cmp_LT"
4156: : "cmp_LE";
4157: addScriptRuntimeInvoke(routine, "(Ljava/lang/Object;"
4158: + "Ljava/lang/Object;" + ")Z");
4159: cfw.add(ByteCode.IFNE, trueGOTO);
4160: cfw.add(ByteCode.GOTO, falseGOTO);
4161: }
4162: }
4163:
4164: private void visitIfJumpEqOp(Node node, Node child, int trueGOTO,
4165: int falseGOTO) {
4166: if (trueGOTO == -1 || falseGOTO == -1)
4167: throw Codegen.badTree();
4168:
4169: short stackInitial = cfw.getStackTop();
4170: int type = node.getType();
4171: Node rChild = child.getNext();
4172:
4173: // Optimize if one of operands is null
4174: if (child.getType() == Token.NULL
4175: || rChild.getType() == Token.NULL) {
4176: // eq is symmetric in this case
4177: if (child.getType() == Token.NULL) {
4178: child = rChild;
4179: }
4180: generateExpression(child, node);
4181: if (type == Token.SHEQ || type == Token.SHNE) {
4182: int testCode = (type == Token.SHEQ) ? ByteCode.IFNULL
4183: : ByteCode.IFNONNULL;
4184: cfw.add(testCode, trueGOTO);
4185: } else {
4186: if (type != Token.EQ) {
4187: // swap false/true targets for !=
4188: if (type != Token.NE)
4189: throw Codegen.badTree();
4190: int tmp = trueGOTO;
4191: trueGOTO = falseGOTO;
4192: falseGOTO = tmp;
4193: }
4194: cfw.add(ByteCode.DUP);
4195: int undefCheckLabel = cfw.acquireLabel();
4196: cfw.add(ByteCode.IFNONNULL, undefCheckLabel);
4197: short stack = cfw.getStackTop();
4198: cfw.add(ByteCode.POP);
4199: cfw.add(ByteCode.GOTO, trueGOTO);
4200: cfw.markLabel(undefCheckLabel, stack);
4201: Codegen.pushUndefined(cfw);
4202: cfw.add(ByteCode.IF_ACMPEQ, trueGOTO);
4203: }
4204: cfw.add(ByteCode.GOTO, falseGOTO);
4205: } else {
4206: int child_dcp_register = nodeIsDirectCallParameter(child);
4207: if (child_dcp_register != -1
4208: && rChild.getType() == Token.TO_OBJECT) {
4209: Node convertChild = rChild.getFirstChild();
4210: if (convertChild.getType() == Token.NUMBER) {
4211: cfw.addALoad(child_dcp_register);
4212: cfw.add(ByteCode.GETSTATIC, "java/lang/Void",
4213: "TYPE", "Ljava/lang/Class;");
4214: int notNumbersLabel = cfw.acquireLabel();
4215: cfw.add(ByteCode.IF_ACMPNE, notNumbersLabel);
4216: cfw.addDLoad(child_dcp_register + 1);
4217: cfw.addPush(convertChild.getDouble());
4218: cfw.add(ByteCode.DCMPL);
4219: if (type == Token.EQ)
4220: cfw.add(ByteCode.IFEQ, trueGOTO);
4221: else
4222: cfw.add(ByteCode.IFNE, trueGOTO);
4223: cfw.add(ByteCode.GOTO, falseGOTO);
4224: cfw.markLabel(notNumbersLabel);
4225: // fall thru into generic handling
4226: }
4227: }
4228:
4229: generateExpression(child, node);
4230: generateExpression(rChild, node);
4231:
4232: String name;
4233: int testCode;
4234: switch (type) {
4235: case Token.EQ:
4236: name = "eq";
4237: testCode = ByteCode.IFNE;
4238: break;
4239: case Token.NE:
4240: name = "eq";
4241: testCode = ByteCode.IFEQ;
4242: break;
4243: case Token.SHEQ:
4244: name = "shallowEq";
4245: testCode = ByteCode.IFNE;
4246: break;
4247: case Token.SHNE:
4248: name = "shallowEq";
4249: testCode = ByteCode.IFEQ;
4250: break;
4251: default:
4252: throw Codegen.badTree();
4253: }
4254: addScriptRuntimeInvoke(name, "(Ljava/lang/Object;"
4255: + "Ljava/lang/Object;" + ")Z");
4256: cfw.add(testCode, trueGOTO);
4257: cfw.add(ByteCode.GOTO, falseGOTO);
4258: }
4259: if (stackInitial != cfw.getStackTop())
4260: throw Codegen.badTree();
4261: }
4262:
4263: private void visitSetName(Node node, Node child) {
4264: String name = node.getFirstChild().getString();
4265: while (child != null) {
4266: generateExpression(child, node);
4267: child = child.getNext();
4268: }
4269: cfw.addALoad(contextLocal);
4270: cfw.addALoad(variableObjectLocal);
4271: cfw.addPush(name);
4272: addScriptRuntimeInvoke("setName",
4273: "(Lorg/mozilla/javascript/Scriptable;"
4274: + "Ljava/lang/Object;"
4275: + "Lorg/mozilla/javascript/Context;"
4276: + "Lorg/mozilla/javascript/Scriptable;"
4277: + "Ljava/lang/String;" + ")Ljava/lang/Object;");
4278: }
4279:
4280: private void visitSetConst(Node node, Node child) {
4281: String name = node.getFirstChild().getString();
4282: while (child != null) {
4283: generateExpression(child, node);
4284: child = child.getNext();
4285: }
4286: cfw.addALoad(contextLocal);
4287: cfw.addPush(name);
4288: addScriptRuntimeInvoke("setConst",
4289: "(Lorg/mozilla/javascript/Scriptable;"
4290: + "Ljava/lang/Object;"
4291: + "Lorg/mozilla/javascript/Context;"
4292: + "Ljava/lang/String;" + ")Ljava/lang/Object;");
4293: }
4294:
4295: private void visitGetVar(Node node) {
4296: if (!hasVarsInRegs)
4297: Kit.codeBug();
4298: int varIndex = fnCurrent.getVarIndex(node);
4299: short reg = varRegisters[varIndex];
4300: if (varIsDirectCallParameter(varIndex)) {
4301: // Remember that here the isNumber flag means that we
4302: // want to use the incoming parameter in a Number
4303: // context, so test the object type and convert the
4304: // value as necessary.
4305: if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
4306: dcpLoadAsNumber(reg);
4307: } else {
4308: dcpLoadAsObject(reg);
4309: }
4310: } else if (fnCurrent.isNumberVar(varIndex)) {
4311: cfw.addDLoad(reg);
4312: } else {
4313: cfw.addALoad(reg);
4314: }
4315: }
4316:
4317: private void visitSetVar(Node node, Node child, boolean needValue) {
4318: if (!hasVarsInRegs)
4319: Kit.codeBug();
4320: int varIndex = fnCurrent.getVarIndex(node);
4321: generateExpression(child.getNext(), node);
4322: boolean isNumber = (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1);
4323: short reg = varRegisters[varIndex];
4324: boolean[] constDeclarations = fnCurrent.fnode
4325: .getParamAndVarConst();
4326: if (constDeclarations[varIndex]) {
4327: if (!needValue) {
4328: if (isNumber)
4329: cfw.add(ByteCode.POP2);
4330: else
4331: cfw.add(ByteCode.POP);
4332: }
4333: } else if (varIsDirectCallParameter(varIndex)) {
4334: if (isNumber) {
4335: if (needValue)
4336: cfw.add(ByteCode.DUP2);
4337: cfw.addALoad(reg);
4338: cfw.add(ByteCode.GETSTATIC, "java/lang/Void", "TYPE",
4339: "Ljava/lang/Class;");
4340: int isNumberLabel = cfw.acquireLabel();
4341: int beyond = cfw.acquireLabel();
4342: cfw.add(ByteCode.IF_ACMPEQ, isNumberLabel);
4343: short stack = cfw.getStackTop();
4344: addDoubleWrap();
4345: cfw.addAStore(reg);
4346: cfw.add(ByteCode.GOTO, beyond);
4347: cfw.markLabel(isNumberLabel, stack);
4348: cfw.addDStore(reg + 1);
4349: cfw.markLabel(beyond);
4350: } else {
4351: if (needValue)
4352: cfw.add(ByteCode.DUP);
4353: cfw.addAStore(reg);
4354: }
4355: } else {
4356: boolean isNumberVar = fnCurrent.isNumberVar(varIndex);
4357: if (isNumber) {
4358: if (isNumberVar) {
4359: cfw.addDStore(reg);
4360: if (needValue)
4361: cfw.addDLoad(reg);
4362: } else {
4363: if (needValue)
4364: cfw.add(ByteCode.DUP2);
4365: // Cannot save number in variable since !isNumberVar,
4366: // so convert to object
4367: addDoubleWrap();
4368: cfw.addAStore(reg);
4369: }
4370: } else {
4371: if (isNumberVar)
4372: Kit.codeBug();
4373: cfw.addAStore(reg);
4374: if (needValue)
4375: cfw.addALoad(reg);
4376: }
4377: }
4378: }
4379:
4380: private void visitSetConstVar(Node node, Node child,
4381: boolean needValue) {
4382: if (!hasVarsInRegs)
4383: Kit.codeBug();
4384: int varIndex = fnCurrent.getVarIndex(node);
4385: generateExpression(child.getNext(), node);
4386: boolean isNumber = (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1);
4387: short reg = varRegisters[varIndex];
4388: int beyond = cfw.acquireLabel();
4389: int noAssign = cfw.acquireLabel();
4390: if (isNumber) {
4391: cfw.addILoad(reg + 2);
4392: cfw.add(ByteCode.IFNE, noAssign);
4393: short stack = cfw.getStackTop();
4394: cfw.addPush(1);
4395: cfw.addIStore(reg + 2);
4396: cfw.addDStore(reg);
4397: if (needValue) {
4398: cfw.addDLoad(reg);
4399: cfw.markLabel(noAssign, stack);
4400: } else {
4401: cfw.add(ByteCode.GOTO, beyond);
4402: cfw.markLabel(noAssign, stack);
4403: cfw.add(ByteCode.POP2);
4404: }
4405: } else {
4406: cfw.addILoad(reg + 1);
4407: cfw.add(ByteCode.IFNE, noAssign);
4408: short stack = cfw.getStackTop();
4409: cfw.addPush(1);
4410: cfw.addIStore(reg + 1);
4411: cfw.addAStore(reg);
4412: if (needValue) {
4413: cfw.addALoad(reg);
4414: cfw.markLabel(noAssign, stack);
4415: } else {
4416: cfw.add(ByteCode.GOTO, beyond);
4417: cfw.markLabel(noAssign, stack);
4418: cfw.add(ByteCode.POP);
4419: }
4420: }
4421: cfw.markLabel(beyond);
4422: }
4423:
4424: private void visitGetProp(Node node, Node child) {
4425: generateExpression(child, node); // object
4426: Node nameChild = child.getNext();
4427: generateExpression(nameChild, node); // the name
4428: if (node.getType() == Token.GETPROPNOWARN) {
4429: cfw.addALoad(contextLocal);
4430: addScriptRuntimeInvoke("getObjectPropNoWarn",
4431: "(Ljava/lang/Object;" + "Ljava/lang/String;"
4432: + "Lorg/mozilla/javascript/Context;"
4433: + ")Ljava/lang/Object;");
4434: return;
4435: }
4436: /*
4437: for 'this.foo' we call getObjectProp(Scriptable...) which can
4438: skip some casting overhead.
4439: */
4440: int childType = child.getType();
4441: if (childType == Token.THIS
4442: && nameChild.getType() == Token.STRING) {
4443: cfw.addALoad(contextLocal);
4444: addScriptRuntimeInvoke("getObjectProp",
4445: "(Lorg/mozilla/javascript/Scriptable;"
4446: + "Ljava/lang/String;"
4447: + "Lorg/mozilla/javascript/Context;"
4448: + ")Ljava/lang/Object;");
4449: } else {
4450: cfw.addALoad(contextLocal);
4451: addScriptRuntimeInvoke("getObjectProp",
4452: "(Ljava/lang/Object;" + "Ljava/lang/String;"
4453: + "Lorg/mozilla/javascript/Context;"
4454: + ")Ljava/lang/Object;");
4455: }
4456: }
4457:
4458: private void visitSetProp(int type, Node node, Node child) {
4459: Node objectChild = child;
4460: generateExpression(child, node);
4461: child = child.getNext();
4462: if (type == Token.SETPROP_OP) {
4463: cfw.add(ByteCode.DUP);
4464: }
4465: Node nameChild = child;
4466: generateExpression(child, node);
4467: child = child.getNext();
4468: if (type == Token.SETPROP_OP) {
4469: // stack: ... object object name -> ... object name object name
4470: cfw.add(ByteCode.DUP_X1);
4471: //for 'this.foo += ...' we call thisGet which can skip some
4472: //casting overhead.
4473: if (objectChild.getType() == Token.THIS
4474: && nameChild.getType() == Token.STRING) {
4475: cfw.addALoad(contextLocal);
4476: addScriptRuntimeInvoke("getObjectProp",
4477: "(Lorg/mozilla/javascript/Scriptable;"
4478: + "Ljava/lang/String;"
4479: + "Lorg/mozilla/javascript/Context;"
4480: + ")Ljava/lang/Object;");
4481: } else {
4482: cfw.addALoad(contextLocal);
4483: addScriptRuntimeInvoke("getObjectProp",
4484: "(Ljava/lang/Object;" + "Ljava/lang/String;"
4485: + "Lorg/mozilla/javascript/Context;"
4486: + ")Ljava/lang/Object;");
4487: }
4488: }
4489: generateExpression(child, node);
4490: cfw.addALoad(contextLocal);
4491: addScriptRuntimeInvoke("setObjectProp", "(Ljava/lang/Object;"
4492: + "Ljava/lang/String;" + "Ljava/lang/Object;"
4493: + "Lorg/mozilla/javascript/Context;"
4494: + ")Ljava/lang/Object;");
4495: }
4496:
4497: private void visitSetElem(int type, Node node, Node child) {
4498: generateExpression(child, node);
4499: child = child.getNext();
4500: if (type == Token.SETELEM_OP) {
4501: cfw.add(ByteCode.DUP);
4502: }
4503: generateExpression(child, node);
4504: child = child.getNext();
4505: boolean indexIsNumber = (node
4506: .getIntProp(Node.ISNUMBER_PROP, -1) != -1);
4507: if (type == Token.SETELEM_OP) {
4508: if (indexIsNumber) {
4509: // stack: ... object object number
4510: // -> ... object number object number
4511: cfw.add(ByteCode.DUP2_X1);
4512: cfw.addALoad(contextLocal);
4513: addOptRuntimeInvoke("getObjectIndex",
4514: "(Ljava/lang/Object;D"
4515: + "Lorg/mozilla/javascript/Context;"
4516: + ")Ljava/lang/Object;");
4517: } else {
4518: // stack: ... object object indexObject
4519: // -> ... object indexObject object indexObject
4520: cfw.add(ByteCode.DUP_X1);
4521: cfw.addALoad(contextLocal);
4522: addScriptRuntimeInvoke("getObjectElem",
4523: "(Ljava/lang/Object;" + "Ljava/lang/Object;"
4524: + "Lorg/mozilla/javascript/Context;"
4525: + ")Ljava/lang/Object;");
4526: }
4527: }
4528: generateExpression(child, node);
4529: cfw.addALoad(contextLocal);
4530: if (indexIsNumber) {
4531: addScriptRuntimeInvoke("setObjectIndex",
4532: "(Ljava/lang/Object;" + "D" + "Ljava/lang/Object;"
4533: + "Lorg/mozilla/javascript/Context;"
4534: + ")Ljava/lang/Object;");
4535: } else {
4536: addScriptRuntimeInvoke("setObjectElem",
4537: "(Ljava/lang/Object;" + "Ljava/lang/Object;"
4538: + "Ljava/lang/Object;"
4539: + "Lorg/mozilla/javascript/Context;"
4540: + ")Ljava/lang/Object;");
4541: }
4542: }
4543:
4544: private void visitDotQuery(Node node, Node child) {
4545: updateLineNumber(node);
4546: generateExpression(child, node);
4547: cfw.addALoad(variableObjectLocal);
4548: addScriptRuntimeInvoke("enterDotQuery", "(Ljava/lang/Object;"
4549: + "Lorg/mozilla/javascript/Scriptable;"
4550: + ")Lorg/mozilla/javascript/Scriptable;");
4551: cfw.addAStore(variableObjectLocal);
4552:
4553: // add push null/pop with label in between to simplify code for loop
4554: // continue when it is necessary to pop the null result from
4555: // updateDotQuery
4556: cfw.add(ByteCode.ACONST_NULL);
4557: int queryLoopStart = cfw.acquireLabel();
4558: cfw.markLabel(queryLoopStart); // loop continue jumps here
4559: cfw.add(ByteCode.POP);
4560:
4561: generateExpression(child.getNext(), node);
4562: addScriptRuntimeInvoke("toBoolean", "(Ljava/lang/Object;)Z");
4563: cfw.addALoad(variableObjectLocal);
4564: addScriptRuntimeInvoke("updateDotQuery", "(Z"
4565: + "Lorg/mozilla/javascript/Scriptable;"
4566: + ")Ljava/lang/Object;");
4567: cfw.add(ByteCode.DUP);
4568: cfw.add(ByteCode.IFNULL, queryLoopStart);
4569: // stack: ... non_null_result_of_updateDotQuery
4570: cfw.addALoad(variableObjectLocal);
4571: addScriptRuntimeInvoke("leaveDotQuery",
4572: "(Lorg/mozilla/javascript/Scriptable;"
4573: + ")Lorg/mozilla/javascript/Scriptable;");
4574: cfw.addAStore(variableObjectLocal);
4575: }
4576:
4577: private int getLocalBlockRegister(Node node) {
4578: Node localBlock = (Node) node.getProp(Node.LOCAL_BLOCK_PROP);
4579: int localSlot = localBlock.getExistingIntProp(Node.LOCAL_PROP);
4580: return localSlot;
4581: }
4582:
4583: private void dcpLoadAsNumber(int dcp_register) {
4584: cfw.addALoad(dcp_register);
4585: cfw.add(ByteCode.GETSTATIC, "java/lang/Void", "TYPE",
4586: "Ljava/lang/Class;");
4587: int isNumberLabel = cfw.acquireLabel();
4588: cfw.add(ByteCode.IF_ACMPEQ, isNumberLabel);
4589: short stack = cfw.getStackTop();
4590: cfw.addALoad(dcp_register);
4591: addObjectToDouble();
4592: int beyond = cfw.acquireLabel();
4593: cfw.add(ByteCode.GOTO, beyond);
4594: cfw.markLabel(isNumberLabel, stack);
4595: cfw.addDLoad(dcp_register + 1);
4596: cfw.markLabel(beyond);
4597: }
4598:
4599: private void dcpLoadAsObject(int dcp_register) {
4600: cfw.addALoad(dcp_register);
4601: cfw.add(ByteCode.GETSTATIC, "java/lang/Void", "TYPE",
4602: "Ljava/lang/Class;");
4603: int isNumberLabel = cfw.acquireLabel();
4604: cfw.add(ByteCode.IF_ACMPEQ, isNumberLabel);
4605: short stack = cfw.getStackTop();
4606: cfw.addALoad(dcp_register);
4607: int beyond = cfw.acquireLabel();
4608: cfw.add(ByteCode.GOTO, beyond);
4609: cfw.markLabel(isNumberLabel, stack);
4610: cfw.addDLoad(dcp_register + 1);
4611: addDoubleWrap();
4612: cfw.markLabel(beyond);
4613: }
4614:
4615: private void addGoto(Node target, int jumpcode) {
4616: int targetLabel = getTargetLabel(target);
4617: cfw.add(jumpcode, targetLabel);
4618: }
4619:
4620: private void addObjectToDouble() {
4621: addScriptRuntimeInvoke("toNumber", "(Ljava/lang/Object;)D");
4622: }
4623:
4624: private void addNewObjectArray(int size) {
4625: if (size == 0) {
4626: if (itsZeroArgArray >= 0) {
4627: cfw.addALoad(itsZeroArgArray);
4628: } else {
4629: cfw.add(ByteCode.GETSTATIC,
4630: "org/mozilla/javascript/ScriptRuntime",
4631: "emptyArgs", "[Ljava/lang/Object;");
4632: }
4633: } else {
4634: cfw.addPush(size);
4635: cfw.add(ByteCode.ANEWARRAY, "java/lang/Object");
4636: }
4637: }
4638:
4639: private void addScriptRuntimeInvoke(String methodName,
4640: String methodSignature) {
4641: cfw.addInvoke(ByteCode.INVOKESTATIC,
4642: "org.mozilla.javascript.ScriptRuntime", methodName,
4643: methodSignature);
4644: }
4645:
4646: private void addOptRuntimeInvoke(String methodName,
4647: String methodSignature) {
4648: cfw.addInvoke(ByteCode.INVOKESTATIC,
4649: "org/mozilla/javascript/optimizer/OptRuntime",
4650: methodName, methodSignature);
4651: }
4652:
4653: private void addJumpedBooleanWrap(int trueLabel, int falseLabel) {
4654: cfw.markLabel(falseLabel);
4655: int skip = cfw.acquireLabel();
4656: cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean", "FALSE",
4657: "Ljava/lang/Boolean;");
4658: cfw.add(ByteCode.GOTO, skip);
4659: cfw.markLabel(trueLabel);
4660: cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean", "TRUE",
4661: "Ljava/lang/Boolean;");
4662: cfw.markLabel(skip);
4663: cfw.adjustStackTop(-1); // only have 1 of true/false
4664: }
4665:
4666: private void addDoubleWrap() {
4667: addOptRuntimeInvoke("wrapDouble", "(D)Ljava/lang/Double;");
4668: }
4669:
4670: /**
4671: * Const locals use an extra slot to hold the has-been-assigned-once flag at
4672: * runtime.
4673: * @param isConst true iff the variable is const
4674: * @return the register for the word pair (double/long)
4675: */
4676: private short getNewWordPairLocal(boolean isConst) {
4677: short result = getConsecutiveSlots(2, isConst);
4678: if (result < (MAX_LOCALS - 1)) {
4679: locals[result] = 1;
4680: locals[result + 1] = 1;
4681: if (isConst)
4682: locals[result + 2] = 1;
4683: if (result == firstFreeLocal) {
4684: for (int i = firstFreeLocal + 2; i < MAX_LOCALS; i++) {
4685: if (locals[i] == 0) {
4686: firstFreeLocal = (short) i;
4687: if (localsMax < firstFreeLocal)
4688: localsMax = firstFreeLocal;
4689: return result;
4690: }
4691: }
4692: } else {
4693: return result;
4694: }
4695: }
4696: throw Context.reportRuntimeError("Program too complex "
4697: + "(out of locals)");
4698: }
4699:
4700: private short getNewWordLocal(boolean isConst) {
4701: short result = getConsecutiveSlots(1, isConst);
4702: if (result < (MAX_LOCALS - 1)) {
4703: locals[result] = 1;
4704: if (isConst)
4705: locals[result + 1] = 1;
4706: if (result == firstFreeLocal) {
4707: for (int i = firstFreeLocal + 2; i < MAX_LOCALS; i++) {
4708: if (locals[i] == 0) {
4709: firstFreeLocal = (short) i;
4710: if (localsMax < firstFreeLocal)
4711: localsMax = firstFreeLocal;
4712: return result;
4713: }
4714: }
4715: } else {
4716: return result;
4717: }
4718: }
4719: throw Context.reportRuntimeError("Program too complex "
4720: + "(out of locals)");
4721: }
4722:
4723: private short getNewWordLocal() {
4724: short result = firstFreeLocal;
4725: locals[result] = 1;
4726: for (int i = firstFreeLocal + 1; i < MAX_LOCALS; i++) {
4727: if (locals[i] == 0) {
4728: firstFreeLocal = (short) i;
4729: if (localsMax < firstFreeLocal)
4730: localsMax = firstFreeLocal;
4731: return result;
4732: }
4733: }
4734: throw Context.reportRuntimeError("Program too complex "
4735: + "(out of locals)");
4736: }
4737:
4738: private short getConsecutiveSlots(int count, boolean isConst) {
4739: if (isConst)
4740: count++;
4741: short result = firstFreeLocal;
4742: while (true) {
4743: if (result >= (MAX_LOCALS - 1))
4744: break;
4745: int i;
4746: for (i = 0; i < count; i++)
4747: if (locals[result + i] != 0)
4748: break;
4749: if (i >= count)
4750: break;
4751: result++;
4752: }
4753: return result;
4754: }
4755:
4756: // This is a valid call only for a local that is allocated by default.
4757: private void incReferenceWordLocal(short local) {
4758: locals[local]++;
4759: }
4760:
4761: // This is a valid call only for a local that is allocated by default.
4762: private void decReferenceWordLocal(short local) {
4763: locals[local]--;
4764: }
4765:
4766: private void releaseWordLocal(short local) {
4767: if (local < firstFreeLocal)
4768: firstFreeLocal = local;
4769: locals[local] = 0;
4770: }
4771:
4772: static final int GENERATOR_TERMINATE = -1;
4773: static final int GENERATOR_START = 0;
4774: static final int GENERATOR_YIELD_START = 1;
4775:
4776: ClassFileWriter cfw;
4777: Codegen codegen;
4778: CompilerEnvirons compilerEnv;
4779: ScriptOrFnNode scriptOrFn;
4780: public int scriptOrFnIndex;
4781: private int savedCodeOffset;
4782:
4783: private OptFunctionNode fnCurrent;
4784: private boolean isTopLevel;
4785:
4786: private static final int MAX_LOCALS = 256;
4787: private int[] locals;
4788: private short firstFreeLocal;
4789: private short localsMax;
4790:
4791: private int itsLineNumber;
4792:
4793: private boolean hasVarsInRegs;
4794: private short[] varRegisters;
4795: private boolean inDirectCallFunction;
4796: private boolean itsForcedObjectParameters;
4797: private int enterAreaStartLabel;
4798: private int epilogueLabel;
4799:
4800: // special known locals. If you add a new local here, be sure
4801: // to initialize it to -1 in initBodyGeneration
4802: private short variableObjectLocal;
4803: private short popvLocal;
4804: private short contextLocal;
4805: private short argsLocal;
4806: private short operationLocal;
4807: private short this ObjLocal;
4808: private short funObjLocal;
4809: private short itsZeroArgArray;
4810: private short itsOneArgArray;
4811: private short scriptRegexpLocal;
4812: private short generatorStateLocal;
4813:
4814: private boolean isGenerator;
4815: private int generatorSwitch;
4816: private int maxLocals = 0;
4817: private int maxStack = 0;
4818:
4819: private Hashtable finallys;
4820:
4821: class FinallyReturnPoint {
4822: public ArrayList jsrPoints = new ArrayList();
4823: public int tableLabel = 0;
4824: }
4825: }
|