/* -*- Mode: java; tab-width: 8 -*- * Copyright © 1997, 1998 Netscape Communications Corporation, * All Rights Reserved. */ /** * This class allows the creation of nodes, and follows the Factory pattern. * * @see Node * @see NodeFactory * @see ASTFactory * @author Mike McCabe * @author Norris Boyd */ public class IRFactory { public IRFactory(TokenStream ts) { this.ts = ts; } /** * Script (for associating file/url names with toplevel scripts.) */ public Object createScript(Object body, String sourceName, int baseLineno, int endLineno, Object source) { Node result = new Node(TokenStream.SCRIPT, sourceName); Node children = ((Node) body).getFirstChild(); if (children != null) result.addChildrenToBack(children); result.putProp(Node.SOURCENAME_PROP, sourceName); result.putProp(Node.BASE_LINENO_PROP, new Integer(baseLineno)); result.putProp(Node.END_LINENO_PROP, new Integer(endLineno)); if (source != null) result.putProp(Node.SOURCE_PROP, source); return result; } /** * Leaf */ public Object createLeaf(int nodeType) { return new Node(nodeType); } public Object createLeaf(int nodeType, String id) { return new Node(nodeType, id); } public Object createLeaf(int nodeType, int nodeOp) { return new Node(nodeType, new Integer(nodeOp)); } /** * Statement leaf nodes. */ public Object createSwitch(int lineno) { return new Node(TokenStream.SWITCH, new Integer(lineno)); } public Object createVariables(int lineno) { return new Node(TokenStream.VAR, new Integer(lineno)); } public Object createExprStatement(Object expr, int lineno) { return new Node(TokenStream.EXPRSTMT, (Node) expr, new Integer(lineno)); } /** * Name */ public Object createName(String name) { return new Node(TokenStream.NAME, name); } /** * String (for literals) */ public Object createString(String string) { return new Node(TokenStream.STRING, string); } /** * Number (for literals) */ public Object createNumber(Number number) { return new Node(TokenStream.NUMBER, number); } /** * Catch clause of try/catch/finally * @param varName the name of the variable to bind to the exception * @param catchCond the condition under which to catch the exception. * May be null if no condition is given. * @param stmts the statements in the catch clause * @param lineno the starting line number of the catch clause */ public Object createCatch(String varName, Object catchCond, Object stmts, int lineno) { if (catchCond == null) catchCond = new Node(TokenStream.PRIMARY, new Integer(TokenStream.TRUE)); Node result = new Node(TokenStream.CATCH, (Node)createName(varName), (Node)catchCond, (Node)stmts); result.setDatum(new Integer(lineno)); return result; } /** * Throw */ public Object createThrow(Object expr, int lineno) { return new Node(TokenStream.THROW, (Node)expr, new Integer(lineno)); } /** * Return */ public Object createReturn(Object expr, int lineno) { return expr == null ? new Node(TokenStream.RETURN, new Integer(lineno)) : new Node(TokenStream.RETURN, (Node)expr, new Integer(lineno)); } /** * Label */ public Object createLabel(String label, int lineno) { Node result = new Node(TokenStream.LABEL, new Integer(lineno)); Node name = new Node(TokenStream.NAME, label); result.addChildToBack(name); return result; } /** * Break (possibly labeled) */ public Object createBreak(String label, int lineno) { Node result = new Node(TokenStream.BREAK, new Integer(lineno)); if (label == null) { return result; } else { Node name = new Node(TokenStream.NAME, label); result.addChildToBack(name); return result; } } /** * Continue (possibly labeled) */ public Object createContinue(String label, int lineno) { Node result = new Node(TokenStream.CONTINUE, new Integer(lineno)); if (label == null) { return result; } else { Node name = new Node(TokenStream.NAME, label); result.addChildToBack(name); return result; } } /** * Statement block * Creates the empty statement block * Must make subsequent calls to add statements to the node */ public Object createBlock(int lineno) { return new Node(TokenStream.BLOCK, new Integer(lineno)); } public Object createFunctionNode(String name, Object args, Object statements) { if (name == null) name = ""; return new FunctionNode(name, (Node) args, (Node) statements); } public Object createFunction(String name, Object args, Object statements, String sourceName, int baseLineno, int endLineno, Object source) { Node f = (Node) createFunctionNode(name, args, statements); f.putProp(Node.SOURCENAME_PROP, sourceName); f.putProp(Node.BASE_LINENO_PROP, new Integer(baseLineno)); f.putProp(Node.END_LINENO_PROP, new Integer(endLineno)); if (source != null) f.putProp(Node.SOURCE_PROP, source); Node result = new Node(TokenStream.FUNCTION, name); result.putProp(Node.FUNCTION_PROP, f); return result; } /** * Add a child to the back of the given node. This function * breaks the Factory abstraction, but it removes a requirement * from implementors of Node. */ public void addChildToBack(Object parent, Object child) { ((Node)parent).addChildToBack((Node)child); } /** * While */ public Object createWhile(Object cond, Object body, int lineno) { // Just add a GOTO to the condition in the do..while Node result = (Node) createDoWhile(body, cond, lineno); Node condTarget = (Node) result.getProp(Node.CONTINUE_PROP); Node GOTO = new Node(TokenStream.GOTO); GOTO.putProp(Node.TARGET_PROP, condTarget); result.addChildToFront(GOTO); return result; } /** * DoWhile */ public Object createDoWhile(Object body, Object cond, int lineno) { Node result = new Node(TokenStream.LOOP, new Integer(lineno)); Node bodyTarget = new Node(TokenStream.TARGET); Node condTarget = new Node(TokenStream.TARGET); Node IFEQ = new Node(TokenStream.IFEQ, (Node)cond); IFEQ.putProp(Node.TARGET_PROP, bodyTarget); Node breakTarget = new Node(TokenStream.TARGET); result.addChildToBack(bodyTarget); result.addChildrenToBack((Node)body); result.addChildToBack(condTarget); result.addChildToBack(IFEQ); result.addChildToBack(breakTarget); result.putProp(Node.BREAK_PROP, breakTarget); result.putProp(Node.CONTINUE_PROP, condTarget); return result; } /** * For */ public Object createFor(Object init, Object test, Object incr, Object body, int lineno) { if (((Node) test).getType() == TokenStream.VOID) { test = new Node(TokenStream.PRIMARY, new Integer(TokenStream.TRUE)); } Node result = (Node)createWhile(test, body, lineno); Node initNode = (Node) init; if (initNode.getType() != TokenStream.VOID) { if (initNode.getType() != TokenStream.VAR) initNode = new Node(TokenStream.POP, initNode); result.addChildToFront(initNode); } Node condTarget = (Node)result.getProp(Node.CONTINUE_PROP); Node incrTarget = new Node(TokenStream.TARGET); result.addChildBefore(incrTarget, condTarget); if (((Node) incr).getType() != TokenStream.VOID) { incr = createUnary(TokenStream.POP, incr); result.addChildAfter((Node)incr, incrTarget); } result.putProp(Node.CONTINUE_PROP, incrTarget); return result; } /** * For .. In * */ public Object createForIn(Object lhs, Object obj, Object body, int lineno) { String name; Node lhsNode = (Node) lhs; Node objNode = (Node) obj; int type = lhsNode.getType(); Node lvalue = lhsNode; switch (type) { case TokenStream.NAME: case TokenStream.GETPROP: case TokenStream.GETELEM: break; case TokenStream.VAR: /* * check that there was only one variable given. * we can't do this in the parser, because then the * parser would have to know something about the * 'init' node of the for-in loop. */ Node lastChild = lhsNode.getLastChild(); if (lhsNode.getFirstChild() != lastChild) { reportError("msg.mult.index"); } lvalue = new Node(TokenStream.NAME, lastChild.getString()); break; default: reportError("msg.bad.for.in.lhs"); return objNode; } Node init = new Node(TokenStream.ENUMINIT, objNode); Node next = new Node(TokenStream.ENUMNEXT); next.putProp(Node.ENUM_PROP, init); Node temp = createNewTemp(next); Node cond = new Node(TokenStream.EQOP, new Integer(TokenStream.NE)); cond.addChildToBack(temp); cond.addChildToBack(new Node(TokenStream.PRIMARY, new Integer(TokenStream.NULL))); Node newBody = new Node(TokenStream.BLOCK); Node assign = (Node) createAssignment(TokenStream.NOP, lvalue, createUseTemp(temp), null, false); newBody.addChildToBack(new Node(TokenStream.POP, assign)); newBody.addChildToBack((Node) body); Node result = (Node) createWhile(cond, newBody, lineno); result.addChildToFront(init); if (type == TokenStream.VAR) result.addChildToFront(lhsNode); Node done = new Node(TokenStream.ENUMDONE); done.putProp(Node.ENUM_PROP, init); result.addChildToBack(done); return result; } /** * Try/Catch/Finally * * The IRFactory tries to express as much as possible in the tree; * the responsibilities remaining for Codegen are to add the Java * handlers: (Either (but not both) of TARGET and FINALLY might not * be defined) * - a catch handler for javascript exceptions that unwraps the * exception onto the stack and GOTOes to the catch target - * TARGET_PROP in the try node. * - a finally handler that catches any exception, stores it to a * temporary, and JSRs to the finally target - FINALLY_PROP in the * try node - before re-throwing the exception. * ... and a goto to GOTO around these handlers. */ public Object createTryCatchFinally(Object tryblock, Object catchblocks, Object finallyblock, int lineno) { Node trynode = (Node)tryblock; // short circuit if (trynode.getType() == TokenStream.BLOCK && !trynode.hasChildren()) return trynode; Node pn = new Node(TokenStream.TRY, trynode, new Integer(lineno)); Node catchNodes = (Node)catchblocks; boolean hasCatch = catchNodes.hasChildren(); boolean hasFinally = false; Node finallyNode = null; Node finallyTarget = null; if (finallyblock != null) { finallyNode = (Node)finallyblock; hasFinally = (finallyNode.getType() != TokenStream.BLOCK || finallyNode.hasChildren()); if (hasFinally) { // make a TARGET for the finally that the tcf node knows about finallyTarget = new Node(TokenStream.TARGET); pn.putProp(Node.FINALLY_PROP, finallyTarget); // add jsr finally to the try block Node jsrFinally = new Node(TokenStream.JSR); jsrFinally.putProp(Node.TARGET_PROP, finallyTarget); pn.addChildToBack(jsrFinally); } } // short circuit if (!hasFinally && !hasCatch) // bc finally might be an empty block... return trynode; Node endTarget = new Node(TokenStream.TARGET); Node GOTOToEnd = new Node(TokenStream.GOTO); GOTOToEnd.putProp(Node.TARGET_PROP, endTarget); pn.addChildToBack(GOTOToEnd); if (hasCatch) { /* * Given try { throw 3; } catch (e: e instanceof Object) { print("object"); } catch (e2) { print(e2); } rewrite as try { throw 3; } catch (x) { o = newScope(); o.e = x; with (o) { if (e instanceof Object) { print("object"); } } o2 = newScope(); o2.e2 = x; with (o2) { if (true) { print(e2); } } } */ // make a TARGET for the catch that the tcf node knows about Node catchTarget = new Node(TokenStream.TARGET); pn.putProp(Node.TARGET_PROP, catchTarget); // mark it pn.addChildToBack(catchTarget); // get the exception object and store it in a temp Node exn = createNewLocal(new Node(TokenStream.VOID)); pn.addChildToBack(new Node(TokenStream.POP, exn)); Node endCatch = new Node(TokenStream.TARGET); // add [jsr finally?] goto end to each catch block // expects catchNode children to be (cond block) pairs. Node cb = catchNodes.getFirstChild(); while (cb != null) { Node catchStmt = new Node(TokenStream.BLOCK); int catchLineNo = ((Integer)cb.getDatum()).intValue(); Node name = cb.getFirstChild(); Node cond = name.getNextSibling(); Node catchBlock = cond.getNextSibling(); cb.removeChild(name); cb.removeChild(cond); cb.removeChild(catchBlock); Node newScope = createNewLocal(new Node(TokenStream.NEWSCOPE)); Node initScope = new Node(TokenStream.SETPROP, newScope, new Node(TokenStream.STRING, name.getString()), createUseLocal(exn)); catchStmt.addChildToBack(new Node(TokenStream.POP, initScope)); catchBlock.addChildToBack(new Node(TokenStream.LEAVEWITH)); Node GOTOToEndCatch = new Node(TokenStream.GOTO); GOTOToEndCatch.putProp(Node.TARGET_PROP, endCatch); catchBlock.addChildToBack(GOTOToEndCatch); Node ifStmt = (Node) createIf(cond, catchBlock, null, catchLineNo); // Try..catch produces "with" code in order to limit // the scope of the exception object. // OPT: We should be able to figure out the correct // scoping at compile-time and avoid the // runtime overhead. Node withStmt = (Node) createWith(createUseLocal(newScope), ifStmt, catchLineNo); catchStmt.addChildToBack(withStmt); pn.addChildToBack(catchStmt); // move to next cb cb = cb.getNextSibling(); } // Generate code to rethrow if no catch clause was executed Node rethrow = new Node(TokenStream.THROW, createUseLocal(exn)); pn.addChildToBack(rethrow); pn.addChildToBack(endCatch); // add a JSR finally if needed if (hasFinally) { Node jsrFinally = new Node(TokenStream.JSR); jsrFinally.putProp(Node.TARGET_PROP, finallyTarget); pn.addChildToBack(jsrFinally); Node GOTO = new Node(TokenStream.GOTO); GOTO.putProp(Node.TARGET_PROP, endTarget); pn.addChildToBack(GOTO); } } if (hasFinally) { pn.addChildToBack(finallyTarget); Node returnTemp = createNewLocal(new Node(TokenStream.VOID)); Node popAndMake = new Node(TokenStream.POP, returnTemp); pn.addChildToBack(popAndMake); pn.addChildToBack(finallyNode); Node ret = createUseLocal(returnTemp); // add the magic prop that makes it output a RET ret.putProp(Node.TARGET_PROP, Boolean.TRUE); pn.addChildToBack(ret); } pn.addChildToBack(endTarget); return pn; } /** * Throw, Return, Label, Break and Continue are defined in ASTFactory. */ /** * With */ public Object createWith(Object obj, Object body, int lineno) { Node result = new Node(TokenStream.BLOCK, new Integer(lineno)); result.addChildToBack(new Node(TokenStream.ENTERWITH, (Node)obj)); Node bodyNode = new Node(TokenStream.WITH, (Node) body, new Integer(lineno)); result.addChildrenToBack(bodyNode); result.addChildToBack(new Node(TokenStream.LEAVEWITH)); return result; } /** * Array Literal *
createArrayLiteral rewrites its argument as array creation * plus a series of array element entries, so later compiler * stages don't need to know about array literals. */ public Object createArrayLiteral(Object obj) { Node array; Node result; array = result = new Node(TokenStream.NEW, new Node(TokenStream.NAME, "Array")); Node temp = createNewTemp(result); result = temp; java.util.Enumeration children = ((Node) obj).getChildIterator(); Node elem = null; int i = 0; while (children.hasMoreElements()) { elem = (Node) children.nextElement(); if (elem.getType() == TokenStream.PRIMARY && elem.getInt() == TokenStream.UNDEFINED) { i++; continue; } Node addelem = new Node(TokenStream.SETELEM, createUseTemp(temp), new Node(TokenStream.NUMBER, new Integer(i)), elem); i++; result = new Node(TokenStream.COMMA, result, addelem); } /* * If the version is 120, then new Array(4) means create a new * array with 4 as the first element. In this case, we might * need to explicitly check against trailing undefined * elements in the array literal, and set the length manually * if these occur. Otherwise, we can add an argument to the * node specifying new Array() to provide the array length. * (Which will make Array optimizations involving allocating a * Java array to back the javascript array work better.) */ if (Context.getContext().getLanguageVersion() == Context.VERSION_1_2) { /* When last array element is empty, we need to set the * length explicitly, because we can't depend on SETELEM * to do it for us - because empty [,,] array elements * never set anything at all. */ if (elem != null && elem.getType() == TokenStream.PRIMARY && elem.getInt() == TokenStream.UNDEFINED) { Node setlength = new Node(TokenStream.SETPROP, createUseTemp(temp), new Node(TokenStream.STRING, "length"), new Node(TokenStream.NUMBER, new Integer(i))); result = new Node(TokenStream.COMMA, result, setlength); } } else { array.addChildToBack(new Node(TokenStream.NUMBER, new Integer(i))); } return new Node(TokenStream.COMMA, result, createUseTemp(temp)); } /** * Object Literals *
createObjectLiteral rewrites its argument as object * creation plus object property entries, so later compiler * stages don't need to know about object literals. */ public Object createObjectLiteral(Object obj) { Node result = new Node(TokenStream.NEW, new Node(TokenStream.NAME, "Object")); Node temp = createNewTemp(result); result = temp; java.util.Enumeration children = ((Node) obj).getChildIterator(); while (children.hasMoreElements()) { Node elem = (Node)children.nextElement(); int op = (elem.getType() == TokenStream.NAME) ? TokenStream.SETPROP : TokenStream.SETELEM; Node addelem = new Node(op, createUseTemp(temp), elem, (Node)children.nextElement()); result = new Node(TokenStream.COMMA, result, addelem); } return new Node(TokenStream.COMMA, result, createUseTemp(temp)); } /** * Regular expressions */ public Object createRegExp(String string, String flags) { return flags.length() == 0 ? new Node(TokenStream.OBJECT, new Node(TokenStream.STRING, string)) : new Node(TokenStream.OBJECT, new Node(TokenStream.STRING, string), new Node(TokenStream.STRING, flags)); } /** * If statement */ public Object createIf(Object cond, Object ifTrue, Object ifFalse, int lineno) { Node result = new Node(TokenStream.BLOCK, new Integer(lineno)); Node ifNotTarget = new Node(TokenStream.TARGET); Node IFNE = new Node(TokenStream.IFNE, (Node) cond); IFNE.putProp(Node.TARGET_PROP, ifNotTarget); result.addChildToBack(IFNE); result.addChildrenToBack((Node)ifTrue); if (ifFalse != null) { Node GOTOToEnd = new Node(TokenStream.GOTO); Node endTarget = new Node(TokenStream.TARGET); GOTOToEnd.putProp(Node.TARGET_PROP, endTarget); result.addChildToBack(GOTOToEnd); result.addChildToBack(ifNotTarget); result.addChildrenToBack((Node)ifFalse); result.addChildToBack(endTarget); } else { result.addChildToBack(ifNotTarget); } return result; } public Object createTernary(Object cond, Object ifTrue, Object ifFalse) { return createIf(cond, ifTrue, ifFalse, -1); } /** * Unary */ public Object createUnary(int nodeType, Object child) { Node childNode = (Node) child; if (nodeType == TokenStream.DELPROP) { int childType = childNode.getType(); Node left; Node right; if (childType == TokenStream.NAME) { // Transform Delete(Name "a") // to Delete(Bind("a"), String("a")) childNode.setType(TokenStream.BINDNAME); left = childNode; right = childNode.cloneNode(); right.setType(TokenStream.STRING); } else if (childType == TokenStream.GETPROP || childType == TokenStream.GETELEM) { left = childNode.getFirstChild(); right = childNode.getLastChild(); childNode.removeChild(left); childNode.removeChild(right); } else { reportError("msg.del.nonref"); return new Node(TokenStream.PRIMARY, new Integer(TokenStream.TRUE)); } return new Node(nodeType, left, right); } return new Node(nodeType, childNode); } public Object createUnary(int nodeType, int nodeOp, Object child) { Node childNode = (Node) child; int childType = childNode.getType(); if (nodeOp == TokenStream.TYPEOF && childType == TokenStream.NAME) { childNode.setType(TokenStream.TYPEOF); return childNode; } if (nodeType == TokenStream.INC || nodeType == TokenStream.DEC) { if (!hasSideEffects(childNode) && (nodeOp == TokenStream.POST) && (childType == TokenStream.NAME || childType == TokenStream.GETPROP || childType == TokenStream.GETELEM)) { // if it's not a LHS type, createAssignment (below) will throw // an exception. return new Node(nodeType, childNode); } /* * Transform INC/DEC ops to +=1, -=1, * expecting later optimization of all +/-=1 cases to INC, DEC. */ // we have to use Double for now, because // 0.0 and 1.0 are stored as dconst_[01], // and using a Float creates a stack mismatch. Node rhs = (Node) createNumber(new Double(1.0)); return createAssignment(nodeType == TokenStream.INC ? TokenStream.ADD : TokenStream.SUB, childNode, rhs, ScriptRuntime.NumberClass, nodeOp == TokenStream.POST); } Node result = new Node(nodeType, new Integer(nodeOp)); result.addChildToBack((Node)child); return result; } /** * Binary */ public Object createBinary(int nodeType, Object left, Object right) { Node temp; switch (nodeType) { case TokenStream.DOT: nodeType = TokenStream.GETPROP; Node idNode = (Node) right; idNode.setType(TokenStream.STRING); String id = idNode.getString(); if (id.equals("__proto__") || id.equals("__parent__")) { Node result = new Node(nodeType, (Node) left); result.putProp(Node.SPECIAL_PROP_PROP, id); return result; } break; case TokenStream.LB: // OPT: could optimize to GETPROP iff string can't be a number nodeType = TokenStream.GETELEM; break; /* case TokenStream.AND: temp = createNewTemp((Node) left); return createTernary(temp, right, createUseTemp(temp)); case TokenStream.OR: temp = createNewTemp((Node) left); return createTernary(temp, createUseTemp(temp), right); */ } return new Node(nodeType, (Node)left, (Node)right); } public Object createBinary(int nodeType, int nodeOp, Object left, Object right) { if (nodeType == TokenStream.ASSIGN) { return createAssignment(nodeOp, (Node) left, (Node) right, null, false); } return new Node(nodeType, (Node) left, (Node) right, new Integer(nodeOp)); } public Object createAssignment(int nodeOp, Node left, Node right, Class convert, boolean postfix) { int nodeType = left.getType(); String idString; Node id = null; switch (nodeType) { case TokenStream.NAME: return createSetName(nodeOp, left, right, convert, postfix); case TokenStream.GETPROP: idString = (String) left.getProp(Node.SPECIAL_PROP_PROP); if (idString != null) id = new Node(TokenStream.STRING, idString); /* fall through */ case TokenStream.GETELEM: if (id == null) id = left.getLastChild(); return createSetProp(nodeType, nodeOp, left.getFirstChild(), id, right, convert, postfix); default: reportError("msg.bad.lhs.assign"); return left; } } private Node createConvert(Class toType, Node expr) { if (toType == null) return expr; Node result = new Node(TokenStream.CONVERT, expr); result.putProp(Node.TYPE_PROP, ScriptRuntime.NumberClass); return result; } private Object createSetName(int nodeOp, Node left, Node right, Class convert, boolean postfix) { if (nodeOp == TokenStream.NOP) { left.setType(TokenStream.BINDNAME); return new Node(TokenStream.SETNAME, left, right); } String s = left.getString(); if (s.equals("__proto__") || s.equals("__parent__")) { Node result = new Node(TokenStream.SETPROP, left, right); result.putProp(Node.SPECIAL_PROP_PROP, s); return result; } Node opLeft = new Node(TokenStream.NAME, s); if (convert != null) opLeft = createConvert(convert, opLeft); if (postfix) opLeft = createNewTemp(opLeft); Node op = new Node(nodeOp, opLeft, right); Node lvalueLeft = new Node(TokenStream.BINDNAME, s); Node result = new Node(TokenStream.SETNAME, lvalueLeft, op); if (postfix) { result = new Node(TokenStream.COMMA, result, createUseTemp(opLeft)); } return result; } public Node createNewTemp(Node n) { int type = n.getType(); if (type == TokenStream.STRING || type == TokenStream.NUMBER) { // Optimization: clone these values rather than storing // and loading from a temp return n; } Node result = new Node(TokenStream.NEWTEMP, n); return result; } public Node createUseTemp(Node newTemp) { int type = newTemp.getType(); if (type == TokenStream.NEWTEMP) { Node result = new Node(TokenStream.USETEMP); result.putProp(Node.TEMP_PROP, newTemp); Integer n = (Integer) newTemp.getProp(Node.USES_PROP); if (n == null) { n = new Integer(1); } else { if (n.intValue() < Integer.MAX_VALUE) n = new Integer(n.intValue() + 1); } newTemp.putProp(Node.USES_PROP, n); return result; } return newTemp.cloneNode(); } public Node createNewLocal(Node n) { Node result = new Node(TokenStream.NEWLOCAL, n); return result; } public Node createUseLocal(Node newLocal) { int type = newLocal.getType(); if (type == TokenStream.NEWLOCAL) { Node result = new Node(TokenStream.USELOCAL); result.putProp(Node.LOCAL_PROP, newLocal); return result; } return newLocal.cloneNode(); // what's this path for ? } public static boolean hasSideEffects(Node exprTree) { switch (exprTree.getType()) { case TokenStream.SETPROP: case TokenStream.SETELEM: case TokenStream.CALL: case TokenStream.NEW: return true; default: Node child = exprTree.getFirstChild(); while (child != null) { if (hasSideEffects(child)) return true; else child = child.getNextSibling(); } break; } return false; } private Node createSetProp(int nodeType, int nodeOp, Node obj, Node id, Node expr, Class convert, boolean postfix) { int type = nodeType == TokenStream.GETPROP ? TokenStream.SETPROP : TokenStream.SETELEM; Object datum = id.getDatum(); if (type == TokenStream.SETPROP && datum != null && datum instanceof String) { String s = (String) datum; if (s.equals("__proto__") || s.equals("__parent__")) { Node result = new Node(type, obj, expr); result.putProp(Node.SPECIAL_PROP_PROP, s); return result; } } if (nodeOp == TokenStream.NOP) return new Node(type, obj, id, expr); /* if the RHS expression could modify the LHS we have to construct a temporary to hold the LHS context prior to running the expression */ Node tmp1, tmp2, opLeft; if (hasSideEffects(expr) || (obj.getType() != TokenStream.NAME)) { tmp1 = createNewTemp(obj); Node useTmp1 = createUseTemp(tmp1); tmp2 = createNewTemp(id); Node useTmp2 = createUseTemp(tmp2); opLeft = new Node(nodeType, useTmp1, useTmp2); } else { tmp1 = obj.cloneNode(); tmp2 = id.cloneNode(); opLeft = new Node(nodeType, obj, id); } if (convert != null) opLeft = createConvert(convert, opLeft); if (postfix) opLeft = createNewTemp(opLeft); Node op = new Node(nodeOp, opLeft, expr); Node result = new Node(type, tmp1, tmp2, op); if (postfix) { result = new Node(TokenStream.COMMA, result, createUseTemp(opLeft)); } return result; } private void reportError(String msgResource) { String message = Context.getMessage(msgResource, null); Context.reportError(message, ts.getSourceName(), ts.getLineno(), ts.getLine(), ts.getOffset()); } // Only needed to get file/line information. Could create an interface // that TokenStream implements if we want to make the connection less // direct. private TokenStream ts; }