0001: /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
0002: *
0003: * ***** BEGIN LICENSE BLOCK *****
0004: * Version: MPL 1.1/GPL 2.0
0005: *
0006: * The contents of this file are subject to the Mozilla Public License Version
0007: * 1.1 (the "License"); you may not use this file except in compliance with
0008: * the License. You may obtain a copy of the License at
0009: * http://www.mozilla.org/MPL/
0010: *
0011: * Software distributed under the License is distributed on an "AS IS" basis,
0012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0013: * for the specific language governing rights and limitations under the
0014: * License.
0015: *
0016: * The Original Code is Rhino code, released
0017: * May 6, 1999.
0018: *
0019: * The Initial Developer of the Original Code is
0020: * Netscape Communications Corporation.
0021: * Portions created by the Initial Developer are Copyright (C) 1997-1999
0022: * the Initial Developer. All Rights Reserved.
0023: *
0024: * Contributor(s):
0025: * Norris Boyd
0026: * Igor Bukanov
0027: * Ethan Hugg
0028: * Bob Jervis
0029: * Terry Lucas
0030: * Milen Nankov
0031: *
0032: * Alternatively, the contents of this file may be used under the terms of
0033: * the GNU General Public License Version 2 or later (the "GPL"), in which
0034: * case the provisions of the GPL are applicable instead of those above. If
0035: * you wish to allow use of your version of this file only under the terms of
0036: * the GPL and not to allow others to use your version of this file under the
0037: * MPL, indicate your decision by deleting the provisions above and replacing
0038: * them with the notice and other provisions required by the GPL. If you do
0039: * not delete the provisions above, a recipient may use your version of this
0040: * file under either the MPL or the GPL.
0041: *
0042: * ***** END LICENSE BLOCK ***** */
0043:
0044: package org.mozilla.javascript;
0045:
0046: import java.util.List;
0047: import java.util.ArrayList;
0048:
0049: /**
0050: * This class allows the creation of nodes, and follows the Factory pattern.
0051: *
0052: * @see Node
0053: * @author Mike McCabe
0054: * @author Norris Boyd
0055: */
0056: final class IRFactory {
0057: IRFactory(Parser parser) {
0058: this .parser = parser;
0059: }
0060:
0061: ScriptOrFnNode createScript() {
0062: return new ScriptOrFnNode(Token.SCRIPT);
0063: }
0064:
0065: /**
0066: * Script (for associating file/url names with toplevel scripts.)
0067: */
0068: void initScript(ScriptOrFnNode scriptNode, Node body) {
0069: Node children = body.getFirstChild();
0070: if (children != null) {
0071: scriptNode.addChildrenToBack(children);
0072: }
0073: }
0074:
0075: /**
0076: * Leaf
0077: */
0078: Node createLeaf(int nodeType) {
0079: return new Node(nodeType);
0080: }
0081:
0082: /**
0083: * Statement leaf nodes.
0084: */
0085:
0086: Node createSwitch(Node expr, int lineno) {
0087: //
0088: // The switch will be rewritten from:
0089: //
0090: // switch (expr) {
0091: // case test1: statements1;
0092: // ...
0093: // default: statementsDefault;
0094: // ...
0095: // case testN: statementsN;
0096: // }
0097: //
0098: // to:
0099: //
0100: // {
0101: // switch (expr) {
0102: // case test1: goto label1;
0103: // ...
0104: // case testN: goto labelN;
0105: // }
0106: // goto labelDefault;
0107: // label1:
0108: // statements1;
0109: // ...
0110: // labelDefault:
0111: // statementsDefault;
0112: // ...
0113: // labelN:
0114: // statementsN;
0115: // breakLabel:
0116: // }
0117: //
0118: // where inside switch each "break;" without label will be replaced
0119: // by "goto breakLabel".
0120: //
0121: // If the original switch does not have the default label, then
0122: // the transformed code would contain after the switch instead of
0123: // goto labelDefault;
0124: // the following goto:
0125: // goto breakLabel;
0126: //
0127:
0128: Node.Jump switchNode = new Node.Jump(Token.SWITCH, expr, lineno);
0129: Node block = new Node(Token.BLOCK, switchNode);
0130: return block;
0131: }
0132:
0133: /**
0134: * If caseExpression argument is null it indicate default label.
0135: */
0136: void addSwitchCase(Node switchBlock, Node caseExpression,
0137: Node statements) {
0138: if (switchBlock.getType() != Token.BLOCK)
0139: throw Kit.codeBug();
0140: Node.Jump switchNode = (Node.Jump) switchBlock.getFirstChild();
0141: if (switchNode.getType() != Token.SWITCH)
0142: throw Kit.codeBug();
0143:
0144: Node gotoTarget = Node.newTarget();
0145: if (caseExpression != null) {
0146: Node.Jump caseNode = new Node.Jump(Token.CASE,
0147: caseExpression);
0148: caseNode.target = gotoTarget;
0149: switchNode.addChildToBack(caseNode);
0150: } else {
0151: switchNode.setDefault(gotoTarget);
0152: }
0153: switchBlock.addChildToBack(gotoTarget);
0154: switchBlock.addChildToBack(statements);
0155: }
0156:
0157: void closeSwitch(Node switchBlock) {
0158: if (switchBlock.getType() != Token.BLOCK)
0159: throw Kit.codeBug();
0160: Node.Jump switchNode = (Node.Jump) switchBlock.getFirstChild();
0161: if (switchNode.getType() != Token.SWITCH)
0162: throw Kit.codeBug();
0163:
0164: Node switchBreakTarget = Node.newTarget();
0165: // switchNode.target is only used by NodeTransformer
0166: // to detect switch end
0167: switchNode.target = switchBreakTarget;
0168:
0169: Node defaultTarget = switchNode.getDefault();
0170: if (defaultTarget == null) {
0171: defaultTarget = switchBreakTarget;
0172: }
0173:
0174: switchBlock.addChildAfter(makeJump(Token.GOTO, defaultTarget),
0175: switchNode);
0176: switchBlock.addChildToBack(switchBreakTarget);
0177: }
0178:
0179: Node createVariables(int token, int lineno) {
0180: return new Node(token, lineno);
0181: }
0182:
0183: Node createExprStatement(Node expr, int lineno) {
0184: int type;
0185: if (parser.insideFunction()) {
0186: type = Token.EXPR_VOID;
0187: } else {
0188: type = Token.EXPR_RESULT;
0189: }
0190: return new Node(type, expr, lineno);
0191: }
0192:
0193: Node createExprStatementNoReturn(Node expr, int lineno) {
0194: return new Node(Token.EXPR_VOID, expr, lineno);
0195: }
0196:
0197: Node createDefaultNamespace(Node expr, int lineno) {
0198: // default xml namespace requires activation
0199: setRequiresActivation();
0200: Node n = createUnary(Token.DEFAULTNAMESPACE, expr);
0201: Node result = createExprStatement(n, lineno);
0202: return result;
0203: }
0204:
0205: /**
0206: * Name
0207: */
0208: Node createName(String name) {
0209: checkActivationName(name, Token.NAME);
0210: return Node.newString(Token.NAME, name);
0211: }
0212:
0213: private Node createName(int type, String name, Node child) {
0214: Node result = createName(name);
0215: result.setType(type);
0216: if (child != null)
0217: result.addChildToBack(child);
0218: return result;
0219: }
0220:
0221: /**
0222: * String (for literals)
0223: */
0224: Node createString(String string) {
0225: return Node.newString(string);
0226: }
0227:
0228: /**
0229: * Number (for literals)
0230: */
0231: Node createNumber(double number) {
0232: return Node.newNumber(number);
0233: }
0234:
0235: /**
0236: * Catch clause of try/catch/finally
0237: * @param varName the name of the variable to bind to the exception
0238: * @param catchCond the condition under which to catch the exception.
0239: * May be null if no condition is given.
0240: * @param stmts the statements in the catch clause
0241: * @param lineno the starting line number of the catch clause
0242: */
0243: Node createCatch(String varName, Node catchCond, Node stmts,
0244: int lineno) {
0245: if (catchCond == null) {
0246: catchCond = new Node(Token.EMPTY);
0247: }
0248: return new Node(Token.CATCH, createName(varName), catchCond,
0249: stmts, lineno);
0250: }
0251:
0252: /**
0253: * Throw
0254: */
0255: Node createThrow(Node expr, int lineno) {
0256: return new Node(Token.THROW, expr, lineno);
0257: }
0258:
0259: /**
0260: * Return
0261: */
0262: Node createReturn(Node expr, int lineno) {
0263: return expr == null ? new Node(Token.RETURN, lineno)
0264: : new Node(Token.RETURN, expr, lineno);
0265: }
0266:
0267: /**
0268: * Debugger
0269: */
0270: Node createDebugger(int lineno) {
0271: return new Node(Token.DEBUGGER, lineno);
0272: }
0273:
0274: /**
0275: * Label
0276: */
0277: Node createLabel(int lineno) {
0278: return new Node.Jump(Token.LABEL, lineno);
0279: }
0280:
0281: Node getLabelLoop(Node label) {
0282: return ((Node.Jump) label).getLoop();
0283: }
0284:
0285: /**
0286: * Label
0287: */
0288: Node createLabeledStatement(Node labelArg, Node statement) {
0289: Node.Jump label = (Node.Jump) labelArg;
0290:
0291: // Make a target and put it _after_ the statement
0292: // node. And in the LABEL node, so breaks get the
0293: // right target.
0294:
0295: Node breakTarget = Node.newTarget();
0296: Node block = new Node(Token.BLOCK, label, statement,
0297: breakTarget);
0298: label.target = breakTarget;
0299:
0300: return block;
0301: }
0302:
0303: /**
0304: * Break (possibly labeled)
0305: */
0306: Node createBreak(Node breakStatement, int lineno) {
0307: Node.Jump n = new Node.Jump(Token.BREAK, lineno);
0308: Node.Jump jumpStatement;
0309: int t = breakStatement.getType();
0310: if (t == Token.LOOP || t == Token.LABEL) {
0311: jumpStatement = (Node.Jump) breakStatement;
0312: } else if (t == Token.BLOCK
0313: && breakStatement.getFirstChild().getType() == Token.SWITCH) {
0314: jumpStatement = (Node.Jump) breakStatement.getFirstChild();
0315: } else {
0316: throw Kit.codeBug();
0317: }
0318: n.setJumpStatement(jumpStatement);
0319: return n;
0320: }
0321:
0322: /**
0323: * Continue (possibly labeled)
0324: */
0325: Node createContinue(Node loop, int lineno) {
0326: if (loop.getType() != Token.LOOP)
0327: Kit.codeBug();
0328: Node.Jump n = new Node.Jump(Token.CONTINUE, lineno);
0329: n.setJumpStatement((Node.Jump) loop);
0330: return n;
0331: }
0332:
0333: /**
0334: * Statement block
0335: * Creates the empty statement block
0336: * Must make subsequent calls to add statements to the node
0337: */
0338: Node createBlock(int lineno) {
0339: return new Node(Token.BLOCK, lineno);
0340: }
0341:
0342: FunctionNode createFunction(String name) {
0343: return new FunctionNode(name);
0344: }
0345:
0346: Node initFunction(FunctionNode fnNode, int functionIndex,
0347: Node statements, int functionType) {
0348: fnNode.itsFunctionType = functionType;
0349: fnNode.addChildToBack(statements);
0350:
0351: int functionCount = fnNode.getFunctionCount();
0352: if (functionCount != 0) {
0353: // Functions containing other functions require activation objects
0354: fnNode.itsNeedsActivation = true;
0355: }
0356:
0357: if (functionType == FunctionNode.FUNCTION_EXPRESSION) {
0358: String name = fnNode.getFunctionName();
0359: if (name != null && name.length() != 0) {
0360: // A function expression needs to have its name as a
0361: // variable (if it isn't already allocated as a variable).
0362: // See ECMA Ch. 13. We add code to the beginning of the
0363: // function to initialize a local variable of the
0364: // function's name to the function value.
0365: Node setFn = new Node(Token.EXPR_VOID, new Node(
0366: Token.SETNAME, Node.newString(Token.BINDNAME,
0367: name), new Node(Token.THISFN)));
0368: statements.addChildrenToFront(setFn);
0369: }
0370: }
0371:
0372: // Add return to end if needed.
0373: Node lastStmt = statements.getLastChild();
0374: if (lastStmt == null || lastStmt.getType() != Token.RETURN) {
0375: statements.addChildToBack(new Node(Token.RETURN));
0376: }
0377:
0378: Node result = Node.newString(Token.FUNCTION, fnNode
0379: .getFunctionName());
0380: result.putIntProp(Node.FUNCTION_PROP, functionIndex);
0381: return result;
0382: }
0383:
0384: /**
0385: * Add a child to the back of the given node. This function
0386: * breaks the Factory abstraction, but it removes a requirement
0387: * from implementors of Node.
0388: */
0389: void addChildToBack(Node parent, Node child) {
0390: parent.addChildToBack(child);
0391: }
0392:
0393: /**
0394: * Create a node that can be used to hold lexically scoped variable
0395: * definitions (via let declarations).
0396: *
0397: * @param token the token of the node to create
0398: * @param lineno line number of source
0399: * @return the created node
0400: */
0401: Node createScopeNode(int token, int lineno) {
0402: return new Node.Scope(token, lineno);
0403: }
0404:
0405: /**
0406: * Create loop node. The parser will later call
0407: * createWhile|createDoWhile|createFor|createForIn
0408: * to finish loop generation.
0409: */
0410: Node createLoopNode(Node loopLabel, int lineno) {
0411: Node.Jump result = new Node.Scope(Token.LOOP, lineno);
0412: if (loopLabel != null) {
0413: ((Node.Jump) loopLabel).setLoop(result);
0414: }
0415: return result;
0416: }
0417:
0418: /**
0419: * While
0420: */
0421: Node createWhile(Node loop, Node cond, Node body) {
0422: return createLoop((Node.Jump) loop, LOOP_WHILE, body, cond,
0423: null, null);
0424: }
0425:
0426: /**
0427: * DoWhile
0428: */
0429: Node createDoWhile(Node loop, Node body, Node cond) {
0430: return createLoop((Node.Jump) loop, LOOP_DO_WHILE, body, cond,
0431: null, null);
0432: }
0433:
0434: /**
0435: * For
0436: */
0437: Node createFor(Node loop, Node init, Node test, Node incr, Node body) {
0438: if (init.getType() == Token.LET) {
0439: // rewrite "for (let i=s; i < N; i++)..." as
0440: // "let (i=s) { for (; i < N; i++)..." so that "s" is evaluated
0441: // outside the scope of the for.
0442: Node.Scope let = Node.Scope.splitScope((Node.Scope) loop);
0443: let.setType(Token.LET);
0444: let.addChildrenToBack(init);
0445: let.addChildToBack(createLoop((Node.Jump) loop, LOOP_FOR,
0446: body, test, new Node(Token.EMPTY), incr));
0447: return let;
0448: }
0449: return createLoop((Node.Jump) loop, LOOP_FOR, body, test, init,
0450: incr);
0451: }
0452:
0453: private Node createLoop(Node.Jump loop, int loopType, Node body,
0454: Node cond, Node init, Node incr) {
0455: Node bodyTarget = Node.newTarget();
0456: Node condTarget = Node.newTarget();
0457: if (loopType == LOOP_FOR && cond.getType() == Token.EMPTY) {
0458: cond = new Node(Token.TRUE);
0459: }
0460: Node.Jump IFEQ = new Node.Jump(Token.IFEQ, cond);
0461: IFEQ.target = bodyTarget;
0462: Node breakTarget = Node.newTarget();
0463:
0464: loop.addChildToBack(bodyTarget);
0465: loop.addChildrenToBack(body);
0466: if (loopType == LOOP_WHILE || loopType == LOOP_FOR) {
0467: // propagate lineno to condition
0468: loop.addChildrenToBack(new Node(Token.EMPTY, loop
0469: .getLineno()));
0470: }
0471: loop.addChildToBack(condTarget);
0472: loop.addChildToBack(IFEQ);
0473: loop.addChildToBack(breakTarget);
0474:
0475: loop.target = breakTarget;
0476: Node continueTarget = condTarget;
0477:
0478: if (loopType == LOOP_WHILE || loopType == LOOP_FOR) {
0479: // Just add a GOTO to the condition in the do..while
0480: loop.addChildToFront(makeJump(Token.GOTO, condTarget));
0481:
0482: if (loopType == LOOP_FOR) {
0483: int initType = init.getType();
0484: if (initType != Token.EMPTY) {
0485: if (initType != Token.VAR && initType != Token.LET) {
0486: init = new Node(Token.EXPR_VOID, init);
0487: }
0488: loop.addChildToFront(init);
0489: }
0490: Node incrTarget = Node.newTarget();
0491: loop.addChildAfter(incrTarget, body);
0492: if (incr.getType() != Token.EMPTY) {
0493: incr = new Node(Token.EXPR_VOID, incr);
0494: loop.addChildAfter(incr, incrTarget);
0495: }
0496: continueTarget = incrTarget;
0497: }
0498: }
0499:
0500: loop.setContinue(continueTarget);
0501:
0502: return loop;
0503: }
0504:
0505: /**
0506: * For .. In
0507: *
0508: */
0509: Node createForIn(int declType, Node loop, Node lhs, Node obj,
0510: Node body, boolean isForEach) {
0511: int destructuring = -1;
0512: int destructuringLen = 0;
0513: Node lvalue;
0514: int type = lhs.getType();
0515: if (type == Token.VAR || type == Token.LET) {
0516: Node lastChild = lhs.getLastChild();
0517: if (lhs.getFirstChild() != lastChild) {
0518: /*
0519: * check that there was only one variable given.
0520: * we can't do this in the parser, because then the
0521: * parser would have to know something about the
0522: * 'init' node of the for-in loop.
0523: */
0524: parser.reportError("msg.mult.index");
0525: }
0526: if (lastChild.getType() == Token.ARRAYLIT
0527: || lastChild.getType() == Token.OBJECTLIT) {
0528: type = destructuring = lastChild.getType();
0529: lvalue = lastChild;
0530: destructuringLen = lastChild.getIntProp(
0531: Node.DESTRUCTURING_ARRAY_LENGTH, 0);
0532: } else if (lastChild.getType() == Token.NAME) {
0533: lvalue = Node.newString(Token.NAME, lastChild
0534: .getString());
0535: } else {
0536: parser.reportError("msg.bad.for.in.lhs");
0537: return obj;
0538: }
0539: } else if (type == Token.ARRAYLIT || type == Token.OBJECTLIT) {
0540: destructuring = type;
0541: lvalue = lhs;
0542: destructuringLen = lhs.getIntProp(
0543: Node.DESTRUCTURING_ARRAY_LENGTH, 0);
0544: } else {
0545: lvalue = makeReference(lhs);
0546: if (lvalue == null) {
0547: parser.reportError("msg.bad.for.in.lhs");
0548: return obj;
0549: }
0550: }
0551:
0552: Node localBlock = new Node(Token.LOCAL_BLOCK);
0553: int initType = (isForEach) ? Token.ENUM_INIT_VALUES
0554: : (destructuring != -1) ? Token.ENUM_INIT_ARRAY
0555: : Token.ENUM_INIT_KEYS;
0556: Node init = new Node(initType, obj);
0557: init.putProp(Node.LOCAL_BLOCK_PROP, localBlock);
0558: Node cond = new Node(Token.ENUM_NEXT);
0559: cond.putProp(Node.LOCAL_BLOCK_PROP, localBlock);
0560: Node id = new Node(Token.ENUM_ID);
0561: id.putProp(Node.LOCAL_BLOCK_PROP, localBlock);
0562:
0563: Node newBody = new Node(Token.BLOCK);
0564: Node assign;
0565: if (destructuring != -1) {
0566: assign = createDestructuringAssignment(declType, lvalue, id);
0567: if (!isForEach
0568: && (destructuring == Token.OBJECTLIT || destructuringLen != 2)) {
0569: // destructuring assignment is only allowed in for..each or
0570: // with an array type of length 2 (to hold key and value)
0571: parser.reportError("msg.bad.for.in.destruct");
0572: }
0573: } else {
0574: assign = simpleAssignment(lvalue, id);
0575: }
0576: newBody.addChildToBack(new Node(Token.EXPR_VOID, assign));
0577: newBody.addChildToBack(body);
0578:
0579: loop = createWhile(loop, cond, newBody);
0580: loop.addChildToFront(init);
0581: if (type == Token.VAR || type == Token.LET)
0582: loop.addChildToFront(lhs);
0583: localBlock.addChildToBack(loop);
0584:
0585: return localBlock;
0586: }
0587:
0588: /**
0589: * Try/Catch/Finally
0590: *
0591: * The IRFactory tries to express as much as possible in the tree;
0592: * the responsibilities remaining for Codegen are to add the Java
0593: * handlers: (Either (but not both) of TARGET and FINALLY might not
0594: * be defined)
0595:
0596: * - a catch handler for javascript exceptions that unwraps the
0597: * exception onto the stack and GOTOes to the catch target
0598:
0599: * - a finally handler
0600:
0601: * ... and a goto to GOTO around these handlers.
0602: */
0603: Node createTryCatchFinally(Node tryBlock, Node catchBlocks,
0604: Node finallyBlock, int lineno) {
0605: boolean hasFinally = (finallyBlock != null)
0606: && (finallyBlock.getType() != Token.BLOCK || finallyBlock
0607: .hasChildren());
0608:
0609: // short circuit
0610: if (tryBlock.getType() == Token.BLOCK
0611: && !tryBlock.hasChildren() && !hasFinally) {
0612: return tryBlock;
0613: }
0614:
0615: boolean hasCatch = catchBlocks.hasChildren();
0616:
0617: // short circuit
0618: if (!hasFinally && !hasCatch) {
0619: // bc finally might be an empty block...
0620: return tryBlock;
0621: }
0622:
0623: Node handlerBlock = new Node(Token.LOCAL_BLOCK);
0624: Node.Jump pn = new Node.Jump(Token.TRY, tryBlock, lineno);
0625: pn.putProp(Node.LOCAL_BLOCK_PROP, handlerBlock);
0626:
0627: if (hasCatch) {
0628: // jump around catch code
0629: Node endCatch = Node.newTarget();
0630: pn.addChildToBack(makeJump(Token.GOTO, endCatch));
0631:
0632: // make a TARGET for the catch that the tcf node knows about
0633: Node catchTarget = Node.newTarget();
0634: pn.target = catchTarget;
0635: // mark it
0636: pn.addChildToBack(catchTarget);
0637:
0638: //
0639: // Given
0640: //
0641: // try {
0642: // tryBlock;
0643: // } catch (e if condition1) {
0644: // something1;
0645: // ...
0646: //
0647: // } catch (e if conditionN) {
0648: // somethingN;
0649: // } catch (e) {
0650: // somethingDefault;
0651: // }
0652: //
0653: // rewrite as
0654: //
0655: // try {
0656: // tryBlock;
0657: // goto after_catch:
0658: // } catch (x) {
0659: // with (newCatchScope(e, x)) {
0660: // if (condition1) {
0661: // something1;
0662: // goto after_catch;
0663: // }
0664: // }
0665: // ...
0666: // with (newCatchScope(e, x)) {
0667: // if (conditionN) {
0668: // somethingN;
0669: // goto after_catch;
0670: // }
0671: // }
0672: // with (newCatchScope(e, x)) {
0673: // somethingDefault;
0674: // goto after_catch;
0675: // }
0676: // }
0677: // after_catch:
0678: //
0679: // If there is no default catch, then the last with block
0680: // arround "somethingDefault;" is replaced by "rethrow;"
0681:
0682: // It is assumed that catch handler generation will store
0683: // exeception object in handlerBlock register
0684:
0685: // Block with local for exception scope objects
0686: Node catchScopeBlock = new Node(Token.LOCAL_BLOCK);
0687:
0688: // expects catchblocks children to be (cond block) pairs.
0689: Node cb = catchBlocks.getFirstChild();
0690: boolean hasDefault = false;
0691: int scopeIndex = 0;
0692: while (cb != null) {
0693: int catchLineNo = cb.getLineno();
0694:
0695: Node name = cb.getFirstChild();
0696: Node cond = name.getNext();
0697: Node catchStatement = cond.getNext();
0698: cb.removeChild(name);
0699: cb.removeChild(cond);
0700: cb.removeChild(catchStatement);
0701:
0702: // Add goto to the catch statement to jump out of catch
0703: // but prefix it with LEAVEWITH since try..catch produces
0704: // "with"code in order to limit the scope of the exception
0705: // object.
0706: catchStatement
0707: .addChildToBack(new Node(Token.LEAVEWITH));
0708: catchStatement.addChildToBack(makeJump(Token.GOTO,
0709: endCatch));
0710:
0711: // Create condition "if" when present
0712: Node condStmt;
0713: if (cond.getType() == Token.EMPTY) {
0714: condStmt = catchStatement;
0715: hasDefault = true;
0716: } else {
0717: condStmt = createIf(cond, catchStatement, null,
0718: catchLineNo);
0719: }
0720:
0721: // Generate code to create the scope object and store
0722: // it in catchScopeBlock register
0723: Node catchScope = new Node(Token.CATCH_SCOPE, name,
0724: createUseLocal(handlerBlock));
0725: catchScope.putProp(Node.LOCAL_BLOCK_PROP,
0726: catchScopeBlock);
0727: catchScope
0728: .putIntProp(Node.CATCH_SCOPE_PROP, scopeIndex);
0729: catchScopeBlock.addChildToBack(catchScope);
0730:
0731: // Add with statement based on catch scope object
0732: catchScopeBlock.addChildToBack(createWith(
0733: createUseLocal(catchScopeBlock), condStmt,
0734: catchLineNo));
0735:
0736: // move to next cb
0737: cb = cb.getNext();
0738: ++scopeIndex;
0739: }
0740: pn.addChildToBack(catchScopeBlock);
0741: if (!hasDefault) {
0742: // Generate code to rethrow if no catch clause was executed
0743: Node rethrow = new Node(Token.RETHROW);
0744: rethrow.putProp(Node.LOCAL_BLOCK_PROP, handlerBlock);
0745: pn.addChildToBack(rethrow);
0746: }
0747:
0748: pn.addChildToBack(endCatch);
0749: }
0750:
0751: if (hasFinally) {
0752: Node finallyTarget = Node.newTarget();
0753: pn.setFinally(finallyTarget);
0754:
0755: // add jsr finally to the try block
0756: pn.addChildToBack(makeJump(Token.JSR, finallyTarget));
0757:
0758: // jump around finally code
0759: Node finallyEnd = Node.newTarget();
0760: pn.addChildToBack(makeJump(Token.GOTO, finallyEnd));
0761:
0762: pn.addChildToBack(finallyTarget);
0763: Node fBlock = new Node(Token.FINALLY, finallyBlock);
0764: fBlock.putProp(Node.LOCAL_BLOCK_PROP, handlerBlock);
0765: pn.addChildToBack(fBlock);
0766:
0767: pn.addChildToBack(finallyEnd);
0768: }
0769: handlerBlock.addChildToBack(pn);
0770: return handlerBlock;
0771: }
0772:
0773: /**
0774: * Throw, Return, Label, Break and Continue are defined in ASTFactory.
0775: */
0776:
0777: /**
0778: * With
0779: */
0780: Node createWith(Node obj, Node body, int lineno) {
0781: setRequiresActivation();
0782: Node result = new Node(Token.BLOCK, lineno);
0783: result.addChildToBack(new Node(Token.ENTERWITH, obj));
0784: Node bodyNode = new Node(Token.WITH, body, lineno);
0785: result.addChildrenToBack(bodyNode);
0786: result.addChildToBack(new Node(Token.LEAVEWITH));
0787: return result;
0788: }
0789:
0790: /**
0791: * DOTQUERY
0792: */
0793: public Node createDotQuery(Node obj, Node body, int lineno) {
0794: setRequiresActivation();
0795: Node result = new Node(Token.DOTQUERY, obj, body, lineno);
0796: return result;
0797: }
0798:
0799: Node createArrayLiteral(ObjArray elems, int skipCount,
0800: int destructuringLen) {
0801: int length = elems.size();
0802: int[] skipIndexes = null;
0803: if (skipCount != 0) {
0804: skipIndexes = new int[skipCount];
0805: }
0806: Node array = new Node(Token.ARRAYLIT);
0807: for (int i = 0, j = 0; i != length; ++i) {
0808: Node elem = (Node) elems.get(i);
0809: if (elem != null) {
0810: array.addChildToBack(elem);
0811: } else {
0812: skipIndexes[j] = i;
0813: ++j;
0814: }
0815: }
0816: if (skipCount != 0) {
0817: array.putProp(Node.SKIP_INDEXES_PROP, skipIndexes);
0818: }
0819: array.putIntProp(Node.DESTRUCTURING_ARRAY_LENGTH,
0820: destructuringLen);
0821: return array;
0822: }
0823:
0824: /**
0825: * Object Literals
0826: * <BR> createObjectLiteral rewrites its argument as object
0827: * creation plus object property entries, so later compiler
0828: * stages don't need to know about object literals.
0829: */
0830: Node createObjectLiteral(ObjArray elems) {
0831: int size = elems.size() / 2;
0832: Node object = new Node(Token.OBJECTLIT);
0833: Object[] properties;
0834: if (size == 0) {
0835: properties = ScriptRuntime.emptyArgs;
0836: } else {
0837: properties = new Object[size];
0838: for (int i = 0; i != size; ++i) {
0839: properties[i] = elems.get(2 * i);
0840: Node value = (Node) elems.get(2 * i + 1);
0841: object.addChildToBack(value);
0842: }
0843: }
0844: object.putProp(Node.OBJECT_IDS_PROP, properties);
0845: return object;
0846: }
0847:
0848: /**
0849: * Regular expressions
0850: */
0851: Node createRegExp(int regexpIndex) {
0852: Node n = new Node(Token.REGEXP);
0853: n.putIntProp(Node.REGEXP_PROP, regexpIndex);
0854: return n;
0855: }
0856:
0857: /**
0858: * If statement
0859: */
0860: Node createIf(Node cond, Node ifTrue, Node ifFalse, int lineno) {
0861: int condStatus = isAlwaysDefinedBoolean(cond);
0862: if (condStatus == ALWAYS_TRUE_BOOLEAN) {
0863: return ifTrue;
0864: } else if (condStatus == ALWAYS_FALSE_BOOLEAN) {
0865: if (ifFalse != null) {
0866: return ifFalse;
0867: }
0868: // Replace if (false) xxx by empty block
0869: return new Node(Token.BLOCK, lineno);
0870: }
0871:
0872: Node result = new Node(Token.BLOCK, lineno);
0873: Node ifNotTarget = Node.newTarget();
0874: Node.Jump IFNE = new Node.Jump(Token.IFNE, cond);
0875: IFNE.target = ifNotTarget;
0876:
0877: result.addChildToBack(IFNE);
0878: result.addChildrenToBack(ifTrue);
0879:
0880: if (ifFalse != null) {
0881: Node endTarget = Node.newTarget();
0882: result.addChildToBack(makeJump(Token.GOTO, endTarget));
0883: result.addChildToBack(ifNotTarget);
0884: result.addChildrenToBack(ifFalse);
0885: result.addChildToBack(endTarget);
0886: } else {
0887: result.addChildToBack(ifNotTarget);
0888: }
0889:
0890: return result;
0891: }
0892:
0893: Node createCondExpr(Node cond, Node ifTrue, Node ifFalse) {
0894: int condStatus = isAlwaysDefinedBoolean(cond);
0895: if (condStatus == ALWAYS_TRUE_BOOLEAN) {
0896: return ifTrue;
0897: } else if (condStatus == ALWAYS_FALSE_BOOLEAN) {
0898: return ifFalse;
0899: }
0900: return new Node(Token.HOOK, cond, ifTrue, ifFalse);
0901: }
0902:
0903: /**
0904: * Unary
0905: */
0906: Node createUnary(int nodeType, Node child) {
0907: int childType = child.getType();
0908: switch (nodeType) {
0909: case Token.DELPROP: {
0910: Node n;
0911: if (childType == Token.NAME) {
0912: // Transform Delete(Name "a")
0913: // to Delete(Bind("a"), String("a"))
0914: child.setType(Token.BINDNAME);
0915: Node left = child;
0916: Node right = Node.newString(child.getString());
0917: n = new Node(nodeType, left, right);
0918: } else if (childType == Token.GETPROP
0919: || childType == Token.GETELEM) {
0920: Node left = child.getFirstChild();
0921: Node right = child.getLastChild();
0922: child.removeChild(left);
0923: child.removeChild(right);
0924: n = new Node(nodeType, left, right);
0925: } else if (childType == Token.GET_REF) {
0926: Node ref = child.getFirstChild();
0927: child.removeChild(ref);
0928: n = new Node(Token.DEL_REF, ref);
0929: } else {
0930: n = new Node(Token.TRUE);
0931: }
0932: return n;
0933: }
0934: case Token.TYPEOF:
0935: if (childType == Token.NAME) {
0936: child.setType(Token.TYPEOFNAME);
0937: return child;
0938: }
0939: break;
0940: case Token.BITNOT:
0941: if (childType == Token.NUMBER) {
0942: int value = ScriptRuntime.toInt32(child.getDouble());
0943: child.setDouble(~value);
0944: return child;
0945: }
0946: break;
0947: case Token.NEG:
0948: if (childType == Token.NUMBER) {
0949: child.setDouble(-child.getDouble());
0950: return child;
0951: }
0952: break;
0953: case Token.NOT: {
0954: int status = isAlwaysDefinedBoolean(child);
0955: if (status != 0) {
0956: int type;
0957: if (status == ALWAYS_TRUE_BOOLEAN) {
0958: type = Token.FALSE;
0959: } else {
0960: type = Token.TRUE;
0961: }
0962: if (childType == Token.TRUE || childType == Token.FALSE) {
0963: child.setType(type);
0964: return child;
0965: }
0966: return new Node(type);
0967: }
0968: break;
0969: }
0970: }
0971: return new Node(nodeType, child);
0972: }
0973:
0974: Node createYield(Node child, int lineno) {
0975: if (!parser.insideFunction()) {
0976: parser.reportError("msg.bad.yield");
0977: }
0978: setRequiresActivation();
0979: setIsGenerator();
0980: if (child != null)
0981: return new Node(Token.YIELD, child, lineno);
0982: else
0983: return new Node(Token.YIELD, lineno);
0984: }
0985:
0986: Node createCallOrNew(int nodeType, Node child) {
0987: int type = Node.NON_SPECIALCALL;
0988: if (child.getType() == Token.NAME) {
0989: String name = child.getString();
0990: if (name.equals("eval")) {
0991: type = Node.SPECIALCALL_EVAL;
0992: } else if (name.equals("With")) {
0993: type = Node.SPECIALCALL_WITH;
0994: }
0995: } else if (child.getType() == Token.GETPROP) {
0996: String name = child.getLastChild().getString();
0997: if (name.equals("eval")) {
0998: type = Node.SPECIALCALL_EVAL;
0999: }
1000: }
1001: Node node = new Node(nodeType, child);
1002: if (type != Node.NON_SPECIALCALL) {
1003: // Calls to these functions require activation objects.
1004: setRequiresActivation();
1005: node.putIntProp(Node.SPECIALCALL_PROP, type);
1006: }
1007: return node;
1008: }
1009:
1010: Node createIncDec(int nodeType, boolean post, Node child) {
1011: child = makeReference(child);
1012: if (child == null) {
1013: String msg;
1014: if (nodeType == Token.DEC) {
1015: msg = "msg.bad.decr";
1016: } else {
1017: msg = "msg.bad.incr";
1018: }
1019: parser.reportError(msg);
1020: return null;
1021: }
1022:
1023: int childType = child.getType();
1024:
1025: switch (childType) {
1026: case Token.NAME:
1027: case Token.GETPROP:
1028: case Token.GETELEM:
1029: case Token.GET_REF: {
1030: Node n = new Node(nodeType, child);
1031: int incrDecrMask = 0;
1032: if (nodeType == Token.DEC) {
1033: incrDecrMask |= Node.DECR_FLAG;
1034: }
1035: if (post) {
1036: incrDecrMask |= Node.POST_FLAG;
1037: }
1038: n.putIntProp(Node.INCRDECR_PROP, incrDecrMask);
1039: return n;
1040: }
1041: }
1042: throw Kit.codeBug();
1043: }
1044:
1045: Node createPropertyGet(Node target, String namespace, String name,
1046: int memberTypeFlags) {
1047: if (namespace == null && memberTypeFlags == 0) {
1048: if (target == null) {
1049: return createName(name);
1050: }
1051: checkActivationName(name, Token.GETPROP);
1052: if (ScriptRuntime.isSpecialProperty(name)) {
1053: Node ref = new Node(Token.REF_SPECIAL, target);
1054: ref.putProp(Node.NAME_PROP, name);
1055: return new Node(Token.GET_REF, ref);
1056: }
1057: return new Node(Token.GETPROP, target, createString(name));
1058: }
1059: Node elem = createString(name);
1060: memberTypeFlags |= Node.PROPERTY_FLAG;
1061: return createMemberRefGet(target, namespace, elem,
1062: memberTypeFlags);
1063: }
1064:
1065: Node createElementGet(Node target, String namespace, Node elem,
1066: int memberTypeFlags) {
1067: // OPT: could optimize to createPropertyGet
1068: // iff elem is string that can not be number
1069: if (namespace == null && memberTypeFlags == 0) {
1070: // stand-alone [aaa] as primary expression is array literal
1071: // declaration and should not come here!
1072: if (target == null)
1073: throw Kit.codeBug();
1074: return new Node(Token.GETELEM, target, elem);
1075: }
1076: return createMemberRefGet(target, namespace, elem,
1077: memberTypeFlags);
1078: }
1079:
1080: private Node createMemberRefGet(Node target, String namespace,
1081: Node elem, int memberTypeFlags) {
1082: Node nsNode = null;
1083: if (namespace != null) {
1084: // See 11.1.2 in ECMA 357
1085: if (namespace.equals("*")) {
1086: nsNode = new Node(Token.NULL);
1087: } else {
1088: nsNode = createName(namespace);
1089: }
1090: }
1091: Node ref;
1092: if (target == null) {
1093: if (namespace == null) {
1094: ref = new Node(Token.REF_NAME, elem);
1095: } else {
1096: ref = new Node(Token.REF_NS_NAME, nsNode, elem);
1097: }
1098: } else {
1099: if (namespace == null) {
1100: ref = new Node(Token.REF_MEMBER, target, elem);
1101: } else {
1102: ref = new Node(Token.REF_NS_MEMBER, target, nsNode,
1103: elem);
1104: }
1105: }
1106: if (memberTypeFlags != 0) {
1107: ref.putIntProp(Node.MEMBER_TYPE_PROP, memberTypeFlags);
1108: }
1109: return new Node(Token.GET_REF, ref);
1110: }
1111:
1112: /**
1113: * Binary
1114: */
1115: Node createBinary(int nodeType, Node left, Node right) {
1116: switch (nodeType) {
1117:
1118: case Token.ADD:
1119: // numerical addition and string concatenation
1120: if (left.type == Token.STRING) {
1121: String s2;
1122: if (right.type == Token.STRING) {
1123: s2 = right.getString();
1124: } else if (right.type == Token.NUMBER) {
1125: s2 = ScriptRuntime.numberToString(
1126: right.getDouble(), 10);
1127: } else {
1128: break;
1129: }
1130: String s1 = left.getString();
1131: left.setString(s1.concat(s2));
1132: return left;
1133: } else if (left.type == Token.NUMBER) {
1134: if (right.type == Token.NUMBER) {
1135: left
1136: .setDouble(left.getDouble()
1137: + right.getDouble());
1138: return left;
1139: } else if (right.type == Token.STRING) {
1140: String s1, s2;
1141: s1 = ScriptRuntime.numberToString(left.getDouble(),
1142: 10);
1143: s2 = right.getString();
1144: right.setString(s1.concat(s2));
1145: return right;
1146: }
1147: }
1148: // can't do anything if we don't know both types - since
1149: // 0 + object is supposed to call toString on the object and do
1150: // string concantenation rather than addition
1151: break;
1152:
1153: case Token.SUB:
1154: // numerical subtraction
1155: if (left.type == Token.NUMBER) {
1156: double ld = left.getDouble();
1157: if (right.type == Token.NUMBER) {
1158: //both numbers
1159: left.setDouble(ld - right.getDouble());
1160: return left;
1161: } else if (ld == 0.0) {
1162: // first 0: 0-x -> -x
1163: return new Node(Token.NEG, right);
1164: }
1165: } else if (right.type == Token.NUMBER) {
1166: if (right.getDouble() == 0.0) {
1167: //second 0: x - 0 -> +x
1168: // can not make simply x because x - 0 must be number
1169: return new Node(Token.POS, left);
1170: }
1171: }
1172: break;
1173:
1174: case Token.MUL:
1175: // numerical multiplication
1176: if (left.type == Token.NUMBER) {
1177: double ld = left.getDouble();
1178: if (right.type == Token.NUMBER) {
1179: //both numbers
1180: left.setDouble(ld * right.getDouble());
1181: return left;
1182: } else if (ld == 1.0) {
1183: // first 1: 1 * x -> +x
1184: return new Node(Token.POS, right);
1185: }
1186: } else if (right.type == Token.NUMBER) {
1187: if (right.getDouble() == 1.0) {
1188: //second 1: x * 1 -> +x
1189: // can not make simply x because x - 0 must be number
1190: return new Node(Token.POS, left);
1191: }
1192: }
1193: // can't do x*0: Infinity * 0 gives NaN, not 0
1194: break;
1195:
1196: case Token.DIV:
1197: // number division
1198: if (right.type == Token.NUMBER) {
1199: double rd = right.getDouble();
1200: if (left.type == Token.NUMBER) {
1201: // both constants -- just divide, trust Java to handle x/0
1202: left.setDouble(left.getDouble() / rd);
1203: return left;
1204: } else if (rd == 1.0) {
1205: // second 1: x/1 -> +x
1206: // not simply x to force number convertion
1207: return new Node(Token.POS, left);
1208: }
1209: }
1210: break;
1211:
1212: case Token.AND: {
1213: // Since x && y gives x, not false, when Boolean(x) is false,
1214: // and y, not Boolean(y), when Boolean(x) is true, x && y
1215: // can only be simplified if x is defined. See bug 309957.
1216:
1217: int leftStatus = isAlwaysDefinedBoolean(left);
1218: if (leftStatus == ALWAYS_FALSE_BOOLEAN) {
1219: // if the first one is false, just return it
1220: return left;
1221: } else if (leftStatus == ALWAYS_TRUE_BOOLEAN) {
1222: // if first is true, set to second
1223: return right;
1224: }
1225: break;
1226: }
1227:
1228: case Token.OR: {
1229: // Since x || y gives x, not true, when Boolean(x) is true,
1230: // and y, not Boolean(y), when Boolean(x) is false, x || y
1231: // can only be simplified if x is defined. See bug 309957.
1232:
1233: int leftStatus = isAlwaysDefinedBoolean(left);
1234: if (leftStatus == ALWAYS_TRUE_BOOLEAN) {
1235: // if the first one is true, just return it
1236: return left;
1237: } else if (leftStatus == ALWAYS_FALSE_BOOLEAN) {
1238: // if first is false, set to second
1239: return right;
1240: }
1241: break;
1242: }
1243: }
1244:
1245: return new Node(nodeType, left, right);
1246: }
1247:
1248: private Node simpleAssignment(Node left, Node right) {
1249: int nodeType = left.getType();
1250: switch (nodeType) {
1251: case Token.NAME:
1252: left.setType(Token.BINDNAME);
1253: return new Node(Token.SETNAME, left, right);
1254:
1255: case Token.GETPROP:
1256: case Token.GETELEM: {
1257: Node obj = left.getFirstChild();
1258: Node id = left.getLastChild();
1259: int type;
1260: if (nodeType == Token.GETPROP) {
1261: type = Token.SETPROP;
1262: } else {
1263: type = Token.SETELEM;
1264: }
1265: return new Node(type, obj, id, right);
1266: }
1267: case Token.GET_REF: {
1268: Node ref = left.getFirstChild();
1269: checkMutableReference(ref);
1270: return new Node(Token.SET_REF, ref, right);
1271: }
1272: }
1273:
1274: throw Kit.codeBug();
1275: }
1276:
1277: private void checkMutableReference(Node n) {
1278: int memberTypeFlags = n.getIntProp(Node.MEMBER_TYPE_PROP, 0);
1279: if ((memberTypeFlags & Node.DESCENDANTS_FLAG) != 0) {
1280: parser.reportError("msg.bad.assign.left");
1281: }
1282: }
1283:
1284: Node createAssignment(int assignType, Node left, Node right) {
1285: Node ref = makeReference(left);
1286: if (ref == null) {
1287: if (left.getType() == Token.ARRAYLIT
1288: || left.getType() == Token.OBJECTLIT) {
1289: if (assignType != Token.ASSIGN) {
1290: parser.reportError("msg.bad.destruct.op");
1291: return right;
1292: }
1293: return createDestructuringAssignment(-1, left, right);
1294: }
1295: parser.reportError("msg.bad.assign.left");
1296: return right;
1297: }
1298: left = ref;
1299:
1300: int assignOp;
1301: switch (assignType) {
1302: case Token.ASSIGN:
1303: return simpleAssignment(left, right);
1304: case Token.ASSIGN_BITOR:
1305: assignOp = Token.BITOR;
1306: break;
1307: case Token.ASSIGN_BITXOR:
1308: assignOp = Token.BITXOR;
1309: break;
1310: case Token.ASSIGN_BITAND:
1311: assignOp = Token.BITAND;
1312: break;
1313: case Token.ASSIGN_LSH:
1314: assignOp = Token.LSH;
1315: break;
1316: case Token.ASSIGN_RSH:
1317: assignOp = Token.RSH;
1318: break;
1319: case Token.ASSIGN_URSH:
1320: assignOp = Token.URSH;
1321: break;
1322: case Token.ASSIGN_ADD:
1323: assignOp = Token.ADD;
1324: break;
1325: case Token.ASSIGN_SUB:
1326: assignOp = Token.SUB;
1327: break;
1328: case Token.ASSIGN_MUL:
1329: assignOp = Token.MUL;
1330: break;
1331: case Token.ASSIGN_DIV:
1332: assignOp = Token.DIV;
1333: break;
1334: case Token.ASSIGN_MOD:
1335: assignOp = Token.MOD;
1336: break;
1337: default:
1338: throw Kit.codeBug();
1339: }
1340:
1341: int nodeType = left.getType();
1342: switch (nodeType) {
1343: case Token.NAME: {
1344: Node op = new Node(assignOp, left, right);
1345: Node lvalueLeft = Node.newString(Token.BINDNAME, left
1346: .getString());
1347: return new Node(Token.SETNAME, lvalueLeft, op);
1348: }
1349: case Token.GETPROP:
1350: case Token.GETELEM: {
1351: Node obj = left.getFirstChild();
1352: Node id = left.getLastChild();
1353:
1354: int type = nodeType == Token.GETPROP ? Token.SETPROP_OP
1355: : Token.SETELEM_OP;
1356:
1357: Node opLeft = new Node(Token.USE_STACK);
1358: Node op = new Node(assignOp, opLeft, right);
1359: return new Node(type, obj, id, op);
1360: }
1361: case Token.GET_REF: {
1362: ref = left.getFirstChild();
1363: checkMutableReference(ref);
1364: Node opLeft = new Node(Token.USE_STACK);
1365: Node op = new Node(assignOp, opLeft, right);
1366: return new Node(Token.SET_REF_OP, ref, op);
1367: }
1368: }
1369:
1370: throw Kit.codeBug();
1371: }
1372:
1373: /**
1374: * Given a destructuring assignment with a left hand side parsed
1375: * as an array or object literal and a right hand side expression,
1376: * rewrite as a series of assignments to the variables defined in
1377: * left from property accesses to the expression on the right.
1378: * @param type declaration type: Token.VAR or Token.LET or -1
1379: * @param left array or object literal containing NAME nodes for
1380: * variables to assign
1381: * @param right expression to assign from
1382: * @return expression that performs a series of assignments to
1383: * the variables defined in left
1384: */
1385: Node createDestructuringAssignment(int type, Node left, Node right) {
1386: String tempName = parser.currentScriptOrFn.getNextTempName();
1387: Node result = destructuringAssignmentHelper(type, left, right,
1388: tempName);
1389: Node comma = result.getLastChild();
1390: comma.addChildToBack(createName(tempName));
1391: return result;
1392: }
1393:
1394: private Node destructuringAssignmentHelper(int variableType,
1395: Node left, Node right, String tempName) {
1396: Node result = createScopeNode(Token.LETEXPR, parser
1397: .getCurrentLineNumber());
1398: result.addChildToFront(new Node(Token.LET, createName(
1399: Token.NAME, tempName, right)));
1400: try {
1401: parser.pushScope(result);
1402: parser.defineSymbol(Token.LET, tempName);
1403: } finally {
1404: parser.popScope();
1405: }
1406: Node comma = new Node(Token.COMMA);
1407: result.addChildToBack(comma);
1408: final int setOp = variableType == Token.CONST ? Token.SETCONST
1409: : Token.SETNAME;
1410: List<String> destructuringNames = new ArrayList<String>();
1411: boolean empty = true;
1412: int type = left.getType();
1413: if (type == Token.ARRAYLIT) {
1414: int index = 0;
1415: int[] skipIndices = (int[]) left
1416: .getProp(Node.SKIP_INDEXES_PROP);
1417: int skip = 0;
1418: Node n = left.getFirstChild();
1419: for (;;) {
1420: if (skipIndices != null) {
1421: while (skip < skipIndices.length
1422: && skipIndices[skip] == index) {
1423: skip++;
1424: index++;
1425: }
1426: }
1427: if (n == null)
1428: break;
1429: Node rightElem = new Node(Token.GETELEM,
1430: createName(tempName), createNumber(index));
1431: if (n.getType() == Token.NAME) {
1432: String name = n.getString();
1433: comma.addChildToBack(new Node(setOp, createName(
1434: Token.BINDNAME, name, null), rightElem));
1435: if (variableType != -1) {
1436: parser.defineSymbol(variableType, name);
1437: destructuringNames.add(name);
1438: }
1439: } else {
1440: comma
1441: .addChildToBack(destructuringAssignmentHelper(
1442: variableType, n, rightElem,
1443: parser.currentScriptOrFn
1444: .getNextTempName()));
1445: }
1446: index++;
1447: empty = false;
1448: n = n.getNext();
1449: }
1450: } else if (type == Token.OBJECTLIT) {
1451: int index = 0;
1452: Object[] propertyIds = (Object[]) left
1453: .getProp(Node.OBJECT_IDS_PROP);
1454: for (Node n = left.getFirstChild(); n != null; n = n
1455: .getNext()) {
1456: Object id = propertyIds[index];
1457: Node rightElem = id instanceof String ? new Node(
1458: Token.GETPROP, createName(tempName),
1459: createString((String) id)) : new Node(
1460: Token.GETELEM, createName(tempName),
1461: createNumber(((Number) id).intValue()));
1462: if (n.getType() == Token.NAME) {
1463: String name = n.getString();
1464: comma.addChildToBack(new Node(setOp, createName(
1465: Token.BINDNAME, name, null), rightElem));
1466: if (variableType != -1) {
1467: parser.defineSymbol(variableType, name);
1468: destructuringNames.add(name);
1469: }
1470: } else {
1471: comma
1472: .addChildToBack(destructuringAssignmentHelper(
1473: variableType, n, rightElem,
1474: parser.currentScriptOrFn
1475: .getNextTempName()));
1476: }
1477: index++;
1478: empty = false;
1479: }
1480: } else if (type == Token.GETPROP || type == Token.GETELEM) {
1481: comma.addChildToBack(simpleAssignment(left,
1482: createName(tempName)));
1483: } else {
1484: parser.reportError("msg.bad.assign.left");
1485: }
1486: if (empty) {
1487: // Don't want a COMMA node with no children. Just add a zero.
1488: comma.addChildToBack(createNumber(0));
1489: }
1490: result.putProp(Node.DESTRUCTURING_NAMES, destructuringNames);
1491: return result;
1492: }
1493:
1494: Node createUseLocal(Node localBlock) {
1495: if (Token.LOCAL_BLOCK != localBlock.getType())
1496: throw Kit.codeBug();
1497: Node result = new Node(Token.LOCAL_LOAD);
1498: result.putProp(Node.LOCAL_BLOCK_PROP, localBlock);
1499: return result;
1500: }
1501:
1502: private Node.Jump makeJump(int type, Node target) {
1503: Node.Jump n = new Node.Jump(type);
1504: n.target = target;
1505: return n;
1506: }
1507:
1508: private Node makeReference(Node node) {
1509: int type = node.getType();
1510: switch (type) {
1511: case Token.NAME:
1512: case Token.GETPROP:
1513: case Token.GETELEM:
1514: case Token.GET_REF:
1515: return node;
1516: case Token.CALL:
1517: node.setType(Token.REF_CALL);
1518: return new Node(Token.GET_REF, node);
1519: }
1520: // Signal caller to report error
1521: return null;
1522: }
1523:
1524: // Check if Node always mean true or false in boolean context
1525: private static int isAlwaysDefinedBoolean(Node node) {
1526: switch (node.getType()) {
1527: case Token.FALSE:
1528: case Token.NULL:
1529: return ALWAYS_FALSE_BOOLEAN;
1530: case Token.TRUE:
1531: return ALWAYS_TRUE_BOOLEAN;
1532: case Token.NUMBER: {
1533: double num = node.getDouble();
1534: if (num == num && num != 0.0) {
1535: return ALWAYS_TRUE_BOOLEAN;
1536: } else {
1537: return ALWAYS_FALSE_BOOLEAN;
1538: }
1539: }
1540: }
1541: return 0;
1542: }
1543:
1544: private void checkActivationName(String name, int token) {
1545: if (parser.insideFunction()) {
1546: boolean activation = false;
1547: if ("arguments".equals(name)
1548: || (parser.compilerEnv.activationNames != null && parser.compilerEnv.activationNames
1549: .containsKey(name))) {
1550: activation = true;
1551: } else if ("length".equals(name)) {
1552: if (token == Token.GETPROP
1553: && parser.compilerEnv.getLanguageVersion() == Context.VERSION_1_2) {
1554: // Use of "length" in 1.2 requires an activation object.
1555: activation = true;
1556: }
1557: }
1558: if (activation) {
1559: setRequiresActivation();
1560: }
1561: }
1562: }
1563:
1564: private void setRequiresActivation() {
1565: if (parser.insideFunction()) {
1566: ((FunctionNode) parser.currentScriptOrFn).itsNeedsActivation = true;
1567: }
1568: }
1569:
1570: private void setIsGenerator() {
1571: if (parser.insideFunction()) {
1572: ((FunctionNode) parser.currentScriptOrFn).itsIsGenerator = true;
1573: }
1574: }
1575:
1576: private Parser parser;
1577:
1578: private static final int LOOP_DO_WHILE = 0;
1579: private static final int LOOP_WHILE = 1;
1580: private static final int LOOP_FOR = 2;
1581:
1582: private static final int ALWAYS_TRUE_BOOLEAN = 1;
1583: private static final int ALWAYS_FALSE_BOOLEAN = -1;
1584: }
|