--- /dev/null
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation. Portions created by Netscape are\r
+ * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s): \r
+ * Norris Boyd\r
+ * Roger Lawrence\r
+ * Mike McCabe\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL. If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.util.Hashtable;\r
+import java.util.Stack;\r
+import java.util.Vector;\r
+\r
+/**\r
+ * This class transforms a tree to a lower-level representation for codegen.\r
+ *\r
+ * @see Node\r
+ * @author Norris Boyd\r
+ */\r
+\r
+public class NodeTransformer {\r
+ \r
+ /**\r
+ * Return new instance of this class. So that derived classes\r
+ * can override methods of the transformer.\r
+ */\r
+ public NodeTransformer newInstance() {\r
+ return new NodeTransformer();\r
+ }\r
+ \r
+ public IRFactory createIRFactory(TokenStream ts, Scriptable scope) {\r
+ return new IRFactory(ts, scope);\r
+ }\r
+\r
+ public Node transform(Node tree, Node enclosing, TokenStream ts,\r
+ Scriptable scope) \r
+ {\r
+ loops = new Stack();\r
+ loopEnds = new Stack();\r
+ inFunction = tree.getType() == TokenStream.FUNCTION;\r
+ if (!inFunction) {\r
+ addVariables(tree, getVariableTable(tree));\r
+ }\r
+ irFactory = createIRFactory(ts, scope);\r
+\r
+ // to save against upchecks if no finally blocks are used.\r
+ boolean hasFinally = false;\r
+\r
+ PreorderNodeIterator iterator = tree.getPreorderIterator();\r
+ Node node;\r
+ while ((node = iterator.nextNode()) != null) {\r
+ int type = node.getType();\r
+\r
+ typeswitch:\r
+ switch (type) {\r
+\r
+ case TokenStream.FUNCTION:\r
+ if (node == tree) {\r
+ // Add the variables to variable table, the\r
+ // parameters were added earlier.\r
+ VariableTable vars = getVariableTable(tree);\r
+ addVariables(tree, vars);\r
+\r
+ // Add return to end if needed.\r
+ Node stmts = node.getLastChild();\r
+ Node lastStmt = stmts.getLastChild();\r
+ if (lastStmt == null ||\r
+ lastStmt.getType() != TokenStream.RETURN)\r
+ {\r
+ stmts.addChildToBack(new Node(TokenStream.RETURN));\r
+ }\r
+\r
+ } else {\r
+ FunctionNode fnNode = (FunctionNode)\r
+ node.getProp(Node.FUNCTION_PROP);\r
+ if (inFunction) {\r
+ // Functions containing other functions require \r
+ // activation objects \r
+ ((FunctionNode) tree).setRequiresActivation(true);\r
+\r
+ // Nested functions must check their 'this' value to\r
+ // insure it is not an activation object:\r
+ // see 10.1.6 Activation Object\r
+ fnNode.setCheckThis(true);\r
+ }\r
+ addParameters(fnNode);\r
+ NodeTransformer inner = newInstance();\r
+ fnNode = (FunctionNode) \r
+ inner.transform(fnNode, tree, ts, scope);\r
+ node.putProp(Node.FUNCTION_PROP, fnNode);\r
+ Vector fns = (Vector) tree.getProp(Node.FUNCTION_PROP);\r
+ if (fns == null) {\r
+ fns = new Vector(7);\r
+ tree.putProp(Node.FUNCTION_PROP, fns);\r
+ }\r
+ fns.addElement(fnNode);\r
+ }\r
+ break;\r
+\r
+ case TokenStream.LABEL:\r
+ {\r
+ Node child = node.getFirstChild();\r
+ node.removeChild(child);\r
+ String id = child.getString();\r
+\r
+ // check against duplicate labels...\r
+ for (int i=loops.size()-1; i >= 0; i--) {\r
+ Node n = (Node) loops.elementAt(i);\r
+ if (n.getType() == TokenStream.LABEL) {\r
+ String otherId = (String)n.getProp(Node.LABEL_PROP);\r
+ if (id.equals(otherId)) {\r
+ String message = Context.getMessage1(\r
+ "msg.dup.label", id);\r
+ reportMessage(Context.getContext(), message, node, \r
+ tree, true, scope);\r
+ break typeswitch;\r
+ }\r
+ }\r
+ }\r
+\r
+ node.putProp(Node.LABEL_PROP, id);\r
+\r
+ /* Make a target and put it _after_ the following\r
+ * node. And in the LABEL node, so breaks get the\r
+ * right target.\r
+ */\r
+ Node breakTarget = new Node(TokenStream.TARGET);\r
+ Node parent = iterator.getCurrentParent();\r
+ Node next = node.getNextSibling();\r
+ while (next != null &&\r
+ (next.getType() == TokenStream.LABEL ||\r
+ next.getType() == TokenStream.TARGET))\r
+ next = next.getNextSibling();\r
+ if (next == null)\r
+ break;\r
+ parent.addChildAfter(breakTarget, next);\r
+ node.putProp(Node.BREAK_PROP, breakTarget);\r
+ \r
+ if (next.getType() == TokenStream.LOOP) {\r
+ node.putProp(Node.CONTINUE_PROP, \r
+ next.getProp(Node.CONTINUE_PROP));\r
+ } \r
+\r
+ loops.push(node);\r
+ loopEnds.push(breakTarget);\r
+\r
+ break;\r
+ }\r
+\r
+ case TokenStream.SWITCH:\r
+ {\r
+ Node breakTarget = new Node(TokenStream.TARGET);\r
+ Node parent = iterator.getCurrentParent();\r
+ parent.addChildAfter(breakTarget, node);\r
+\r
+ // make all children siblings except for selector\r
+ Node sib = node;\r
+ Node child = node.getFirstChild().next;\r
+ while (child != null) {\r
+ Node next = child.next;\r
+ node.removeChild(child);\r
+ parent.addChildAfter(child, sib);\r
+ sib = child;\r
+ child = next;\r
+ }\r
+\r
+ node.putProp(Node.BREAK_PROP, breakTarget);\r
+ loops.push(node);\r
+ loopEnds.push(breakTarget);\r
+ node.putProp(Node.CASES_PROP, new Vector(13));\r
+ break;\r
+ }\r
+\r
+ case TokenStream.DEFAULT:\r
+ case TokenStream.CASE:\r
+ {\r
+ Node sw = (Node) loops.peek();\r
+ if (type == TokenStream.CASE) {\r
+ Vector cases = (Vector) sw.getProp(Node.CASES_PROP);\r
+ cases.addElement(node);\r
+ } else {\r
+ sw.putProp(Node.DEFAULT_PROP, node);\r
+ }\r
+ break;\r
+ }\r
+\r
+ case TokenStream.NEWLOCAL : {\r
+ Integer localCount\r
+ = (Integer)(tree.getProp(Node.LOCALCOUNT_PROP));\r
+ if (localCount == null) {\r
+ tree.putProp(Node.LOCALCOUNT_PROP, new Integer(1));\r
+ }\r
+ else {\r
+ tree.putProp(Node.LOCALCOUNT_PROP,\r
+ new Integer(localCount.intValue() + 1));\r
+ }\r
+ }\r
+ break;\r
+\r
+ case TokenStream.LOOP:\r
+ loops.push(node);\r
+ loopEnds.push(node.getProp(Node.BREAK_PROP));\r
+ break;\r
+\r
+ case TokenStream.WITH:\r
+ {\r
+ if (inFunction) {\r
+ // With statements require an activation object.\r
+ ((FunctionNode) tree).setRequiresActivation(true);\r
+ }\r
+ loops.push(node);\r
+ Node leave = node.getNextSibling();\r
+ if (leave.getType() != TokenStream.LEAVEWITH) {\r
+ throw new RuntimeException("Unexpected tree");\r
+ }\r
+ loopEnds.push(leave);\r
+ break;\r
+ }\r
+\r
+ case TokenStream.TRY:\r
+ {\r
+ Node finallytarget = (Node)node.getProp(Node.FINALLY_PROP);\r
+ if (finallytarget != null) {\r
+ hasFinally = true;\r
+ loops.push(node);\r
+ loopEnds.push(finallytarget);\r
+ }\r
+ Integer localCount\r
+ = (Integer)(tree.getProp(Node.LOCALCOUNT_PROP));\r
+ if (localCount == null) {\r
+ tree.putProp(Node.LOCALCOUNT_PROP, new Integer(1));\r
+ }\r
+ else {\r
+ tree.putProp(Node.LOCALCOUNT_PROP,\r
+ new Integer(localCount.intValue() + 1));\r
+ }\r
+ break;\r
+ }\r
+\r
+ case TokenStream.TARGET:\r
+ case TokenStream.LEAVEWITH:\r
+ if (!loopEnds.empty() && loopEnds.peek() == node) {\r
+ loopEnds.pop();\r
+ loops.pop();\r
+ }\r
+ break;\r
+\r
+ case TokenStream.RETURN:\r
+ {\r
+ /* If we didn't support try/finally, it wouldn't be\r
+ * necessary to put LEAVEWITH nodes here... but as\r
+ * we do need a series of JSR FINALLY nodes before\r
+ * each RETURN, we need to ensure that each finally\r
+ * block gets the correct scope... which could mean\r
+ * that some LEAVEWITH nodes are necessary.\r
+ */\r
+ if (!hasFinally)\r
+ break; // skip the whole mess.\r
+\r
+ Node parent = iterator.getCurrentParent();\r
+ for (int i=loops.size()-1; i >= 0; i--) {\r
+ Node n = (Node) loops.elementAt(i);\r
+ int elemtype = n.getType();\r
+ if (elemtype == TokenStream.TRY) {\r
+ Node jsrnode = new Node(TokenStream.JSR);\r
+ Object jsrtarget = n.getProp(Node.FINALLY_PROP);\r
+ jsrnode.putProp(Node.TARGET_PROP, jsrtarget);\r
+ parent.addChildBefore(jsrnode, node);\r
+ } else if (elemtype == TokenStream.WITH) {\r
+ parent.addChildBefore(new Node(TokenStream.LEAVEWITH),\r
+ node);\r
+ }\r
+ }\r
+ break;\r
+ }\r
+\r
+ case TokenStream.BREAK:\r
+ case TokenStream.CONTINUE:\r
+ {\r
+ Node loop = null;\r
+ boolean labelled = node.hasChildren();\r
+ String id = null;\r
+ if (labelled) {\r
+ /* get the label */\r
+ Node child = node.getFirstChild();\r
+ id = child.getString();\r
+ node.removeChild(child);\r
+ }\r
+\r
+ int i;\r
+ Node parent = iterator.getCurrentParent();\r
+ for (i=loops.size()-1; i >= 0; i--) {\r
+ Node n = (Node) loops.elementAt(i);\r
+ int elemtype = n.getType();\r
+ if (elemtype == TokenStream.WITH) {\r
+ parent.addChildBefore(new Node(TokenStream.LEAVEWITH),\r
+ node);\r
+ } else if (elemtype == TokenStream.TRY) {\r
+ Node jsrFinally = new Node(TokenStream.JSR);\r
+ Object jsrTarget = n.getProp(Node.FINALLY_PROP);\r
+ jsrFinally.putProp(Node.TARGET_PROP, jsrTarget);\r
+ parent.addChildBefore(jsrFinally, node);\r
+ } else if (!labelled &&\r
+ (elemtype == TokenStream.LOOP ||\r
+ (elemtype == TokenStream.SWITCH &&\r
+ type == TokenStream.BREAK)))\r
+ {\r
+ /* if it's a simple break/continue, break from the\r
+ * nearest enclosing loop or switch\r
+ */\r
+ loop = n;\r
+ break;\r
+ } else if (labelled &&\r
+ elemtype == TokenStream.LABEL &&\r
+ id.equals((String)n.getProp(Node.LABEL_PROP)))\r
+ {\r
+ loop = n;\r
+ break;\r
+ }\r
+ }\r
+ int propType = type == TokenStream.BREAK\r
+ ? Node.BREAK_PROP\r
+ : Node.CONTINUE_PROP;\r
+ Node target = loop == null \r
+ ? null\r
+ : (Node) loop.getProp(propType);\r
+ if (loop == null || target == null) {\r
+ String message;\r
+ if (!labelled) {\r
+ // didn't find an appropriate target\r
+ if (type == TokenStream.CONTINUE) {\r
+ message = Context.getMessage\r
+ ("msg.continue.outside", null);\r
+ } else {\r
+ message = Context.getMessage\r
+ ("msg.bad.break", null);\r
+ }\r
+ } else if (loop != null) {\r
+ message = Context.getMessage0("msg.continue.nonloop");\r
+ } else {\r
+ Object[] errArgs = { id };\r
+ message = Context.getMessage\r
+ ("msg.undef.label", errArgs);\r
+ }\r
+ reportMessage(Context.getContext(), message, node, \r
+ tree, true, scope);\r
+ node.setType(TokenStream.NOP);\r
+ break;\r
+ }\r
+ node.setType(TokenStream.GOTO);\r
+ node.putProp(Node.TARGET_PROP, target);\r
+ break;\r
+ }\r
+\r
+ case TokenStream.CALL:\r
+ if (isSpecialCallName(tree, node))\r
+ node.putProp(Node.SPECIALCALL_PROP, Boolean.TRUE);\r
+ visitCall(node, tree);\r
+ break;\r
+\r
+ case TokenStream.NEW:\r
+ if (isSpecialCallName(tree, node))\r
+ node.putProp(Node.SPECIALCALL_PROP, Boolean.TRUE);\r
+ visitNew(node, tree);\r
+ break;\r
+\r
+ case TokenStream.DOT:\r
+ {\r
+ Node right = node.getLastChild();\r
+ right.setType(TokenStream.STRING);\r
+ break;\r
+ }\r
+\r
+ case TokenStream.EXPRSTMT:\r
+ node.setType(inFunction ? TokenStream.POP : TokenStream.POPV);\r
+ break;\r
+\r
+ case TokenStream.OBJECT:\r
+ {\r
+ Vector regexps = (Vector) tree.getProp(Node.REGEXP_PROP);\r
+ if (regexps == null) {\r
+ regexps = new Vector(3);\r
+ tree.putProp(Node.REGEXP_PROP, regexps);\r
+ }\r
+ regexps.addElement(node);\r
+ Node n = new Node(TokenStream.OBJECT);\r
+ iterator.replaceCurrent(n);\r
+ n.putProp(Node.REGEXP_PROP, node);\r
+ break;\r
+ }\r
+\r
+ case TokenStream.VAR:\r
+ {\r
+ ShallowNodeIterator i = node.getChildIterator();\r
+ Node result = new Node(TokenStream.BLOCK);\r
+ while (i.hasMoreElements()) {\r
+ Node n = i.nextNode();\r
+ if (!n.hasChildren())\r
+ continue;\r
+ Node init = n.getFirstChild();\r
+ n.removeChild(init);\r
+ Node asn = (Node) irFactory.createAssignment(\r
+ TokenStream.NOP, n, init, null,\r
+ false);\r
+ Node pop = new Node(TokenStream.POP, asn, node.getDatum());\r
+ result.addChildToBack(pop);\r
+ }\r
+ iterator.replaceCurrent(result);\r
+ break;\r
+ }\r
+\r
+ case TokenStream.DELPROP:\r
+ case TokenStream.SETNAME:\r
+ {\r
+ if (!inFunction || inWithStatement())\r
+ break;\r
+ Node bind = node.getFirstChild();\r
+ if (bind == null || bind.getType() != TokenStream.BINDNAME)\r
+ break;\r
+ String name = bind.getString();\r
+ Context cx = Context.getCurrentContext();\r
+ if (cx != null && cx.isActivationNeeded(name)) {\r
+ // use of "arguments" requires an activation object.\r
+ ((FunctionNode) tree).setRequiresActivation(true);\r
+ }\r
+ VariableTable vars = getVariableTable(tree);\r
+ if (vars.getVariable(name) != null) {\r
+ if (type == TokenStream.SETNAME) {\r
+ node.setType(TokenStream.SETVAR);\r
+ bind.setType(TokenStream.STRING);\r
+ } else {\r
+ // Local variables are by definition permanent\r
+ Node n = new Node(TokenStream.PRIMARY,\r
+ new Integer(TokenStream.FALSE));\r
+ iterator.replaceCurrent(n);\r
+ }\r
+ }\r
+ break;\r
+ }\r
+ \r
+ case TokenStream.GETPROP:\r
+ if (inFunction) {\r
+ Node n = node.getFirstChild().getNextSibling();\r
+ String name = n == null ? "" : n.getString();\r
+ Context cx = Context.getCurrentContext();\r
+ if ((cx != null && cx.isActivationNeeded(name)) ||\r
+ (name.equals("length") && \r
+ Context.getContext().getLanguageVersion() == \r
+ Context.VERSION_1_2))\r
+ {\r
+ // Use of "arguments" or "length" in 1.2 requires \r
+ // an activation object.\r
+ ((FunctionNode) tree).setRequiresActivation(true);\r
+ }\r
+ }\r
+ break;\r
+\r
+ case TokenStream.NAME:\r
+ {\r
+ if (!inFunction || inWithStatement())\r
+ break;\r
+ String name = node.getString();\r
+ Context cx = Context.getCurrentContext();\r
+ if (cx != null && cx.isActivationNeeded(name)) {\r
+ // Use of "arguments" requires an activation object.\r
+ ((FunctionNode) tree).setRequiresActivation(true);\r
+ }\r
+ VariableTable vars = getVariableTable(tree);\r
+ if (vars.getVariable(name) != null) {\r
+ node.setType(TokenStream.GETVAR);\r
+ }\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ return tree;\r
+ }\r
+\r
+ protected void addVariables(Node tree, VariableTable vars) {\r
+ // OPT: a whole pass to collect variables seems expensive.\r
+ // Could special case to go into statements only.\r
+ boolean inFunction = tree.getType() == TokenStream.FUNCTION;\r
+ PreorderNodeIterator iterator = tree.getPreorderIterator();\r
+ Hashtable ht = null;\r
+ Node node;\r
+ while ((node = iterator.nextNode()) != null) {\r
+ int nodeType = node.getType();\r
+ if (inFunction && nodeType == TokenStream.FUNCTION &&\r
+ node != tree && \r
+ ((FunctionNode) node.getProp(Node.FUNCTION_PROP)).getFunctionType() == \r
+ FunctionNode.FUNCTION_EXPRESSION_STATEMENT) \r
+ {\r
+ // In a function with both "var x" and "function x",\r
+ // disregard the var statement, independent of order.\r
+ String name = node.getString();\r
+ if (name == null)\r
+ continue;\r
+ vars.removeLocal(name);\r
+ if (ht == null)\r
+ ht = new Hashtable();\r
+ ht.put(name, Boolean.TRUE);\r
+ }\r
+ if (nodeType != TokenStream.VAR)\r
+ continue;\r
+ ShallowNodeIterator i = node.getChildIterator();\r
+ while (i.hasMoreElements()) {\r
+ Node n = i.nextNode();\r
+ if (ht == null || ht.get(n.getString()) == null)\r
+ vars.addLocal(n.getString());\r
+ }\r
+ }\r
+ String name = (String) tree.getDatum();\r
+ if (inFunction && ((FunctionNode) tree).getFunctionType() ==\r
+ FunctionNode.FUNCTION_EXPRESSION &&\r
+ name != null && name.length() > 0 &&\r
+ vars.getVariable(name) == null)\r
+ {\r
+ // A function expression needs to have its name as a variable\r
+ // (if it isn't already allocated as a variable). See \r
+ // ECMA Ch. 13. We add code to the beginning of the function\r
+ // to initialize a local variable of the function's name\r
+ // to the function value.\r
+ vars.addLocal(name);\r
+ Node block = tree.getLastChild();\r
+ Node setFn = new Node(TokenStream.POP,\r
+ new Node(TokenStream.SETVAR,\r
+ new Node(TokenStream.STRING, name),\r
+ new Node(TokenStream.PRIMARY,\r
+ new Integer(TokenStream.THISFN))));\r
+ block.addChildrenToFront(setFn);\r
+ }\r
+ }\r
+\r
+ protected void addParameters(FunctionNode fnNode) {\r
+ VariableTable vars = fnNode.getVariableTable();\r
+ Node args = fnNode.getFirstChild();\r
+ if (args.getType() == TokenStream.LP && vars.getParameterCount() == 0)\r
+ {\r
+ // Add parameters\r
+ ShallowNodeIterator i = args.getChildIterator();\r
+ while (i.hasMoreElements()) {\r
+ Node n = i.nextNode();\r
+ String arg = n.getString();\r
+ vars.addParameter(arg);\r
+ }\r
+ }\r
+ }\r
+ \r
+ protected void visitNew(Node node, Node tree) {\r
+ }\r
+\r
+ protected void visitCall(Node node, Node tree) {\r
+ /*\r
+ * For\r
+ * Call(GetProp(a, b), c, d) // or GetElem...\r
+ * we wish to evaluate as\r
+ * Call(GetProp(tmp=a, b), tmp, c, d)\r
+ *\r
+ * for\r
+ * Call(Name("a"), b, c)\r
+ * we wish to evaluate as\r
+ * Call(GetProp(tmp=GetBase("a"), "a"), tmp, b, c)\r
+ *\r
+ * and for\r
+ * Call(a, b, c);\r
+ * we wish to evaluate as\r
+ * Call(tmp=a, Parent(tmp), c, d)\r
+ */\r
+ Node left = node.getFirstChild();\r
+ // count the arguments\r
+ int argCount = 0;\r
+ Node arg = left.getNextSibling();\r
+ while (arg != null) {\r
+ arg = arg.getNextSibling();\r
+ argCount++;\r
+ }\r
+ boolean addGetThis = false;\r
+ if (left.getType() == TokenStream.NAME) {\r
+ VariableTable vars = getVariableTable(tree);\r
+ String name = left.getString();\r
+ if (inFunction && vars.getVariable(name) != null && \r
+ !inWithStatement()) \r
+ {\r
+ // call to a var. Transform to Call(GetVar("a"), b, c)\r
+ left.setType(TokenStream.GETVAR);\r
+ // fall through to code to add GetParent\r
+ } else {\r
+ // transform to Call(GetProp(GetBase("a"), "a"), b, c)\r
+\r
+ node.removeChild(left);\r
+ left.setType(TokenStream.GETBASE);\r
+ Node str = left.cloneNode();\r
+ str.setType(TokenStream.STRING);\r
+ Node getProp = new Node(TokenStream.GETPROP, left, str);\r
+ node.addChildToFront(getProp);\r
+ left = getProp;\r
+\r
+ // Conditionally set a flag to add a GETTHIS node.\r
+ // The getThis entry in the runtime will take a\r
+ // Scriptable object intended to be used as a 'this'\r
+ // and make sure that it is neither a With object or\r
+ // an activation object.\r
+ // Executing getThis requires at least two instanceof\r
+ // tests, so we only include it if we are currently\r
+ // inside a 'with' statement, or if we are executing\r
+ // a script (to protect against an eval inside a with).\r
+ addGetThis = inWithStatement() || !inFunction;\r
+ // fall through to GETPROP code\r
+ }\r
+ }\r
+ if (left.getType() != TokenStream.GETPROP &&\r
+ left.getType() != TokenStream.GETELEM)\r
+ {\r
+ node.removeChild(left);\r
+ Node tmp = irFactory.createNewTemp(left);\r
+ Node use = irFactory.createUseTemp(tmp);\r
+ use.putProp(Node.TEMP_PROP, tmp);\r
+ Node parent = new Node(TokenStream.PARENT, use);\r
+ node.addChildToFront(parent);\r
+ node.addChildToFront(tmp);\r
+ return;\r
+ }\r
+ Node leftLeft = left.getFirstChild();\r
+ left.removeChild(leftLeft);\r
+ Node tmp = irFactory.createNewTemp(leftLeft);\r
+ left.addChildToFront(tmp);\r
+ Node use = irFactory.createUseTemp(tmp);\r
+ use.putProp(Node.TEMP_PROP, tmp);\r
+ if (addGetThis)\r
+ use = new Node(TokenStream.GETTHIS, use);\r
+ node.addChildAfter(use, left);\r
+ }\r
+\r
+ protected boolean inWithStatement() {\r
+ for (int i=loops.size()-1; i >= 0; i--) {\r
+ Node n = (Node) loops.elementAt(i);\r
+ if (n.getType() == TokenStream.WITH)\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Return true if the node is a call to a function that requires \r
+ * access to the enclosing activation object.\r
+ */\r
+ private boolean isSpecialCallName(Node tree, Node node) {\r
+ Node left = node.getFirstChild();\r
+ boolean isSpecial = false;\r
+ if (left.getType() == TokenStream.NAME) {\r
+ String name = left.getString();\r
+ isSpecial = name.equals("eval") || name.equals("With");\r
+ } else {\r
+ if (left.getType() == TokenStream.GETPROP) {\r
+ String name = left.getLastChild().getString();\r
+ isSpecial = name.equals("exec");\r
+ }\r
+ }\r
+ if (isSpecial) {\r
+ // Calls to these functions require activation objects.\r
+ if (inFunction)\r
+ ((FunctionNode) tree).setRequiresActivation(true);\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+\r
+ protected VariableTable createVariableTable() {\r
+ return new VariableTable();\r
+ }\r
+\r
+ protected VariableTable getVariableTable(Node tree) {\r
+ if (inFunction) {\r
+ return ((FunctionNode)tree).getVariableTable();\r
+ }\r
+ VariableTable result = (VariableTable)(tree.getProp(Node.VARS_PROP));\r
+ if (result == null) {\r
+ result = createVariableTable();\r
+ tree.putProp(Node.VARS_PROP, result);\r
+ }\r
+ return result;\r
+ }\r
+ \r
+ protected void reportMessage(Context cx, String msg, Node stmt, \r
+ Node tree, boolean isError,\r
+ Scriptable scope)\r
+ {\r
+ Object obj = stmt.getDatum();\r
+ int lineno = 0;\r
+ if (obj != null && obj instanceof Integer)\r
+ lineno = ((Integer) obj).intValue();\r
+ Object prop = tree == null \r
+ ? null\r
+ : tree.getProp(Node.SOURCENAME_PROP);\r
+ if (isError) {\r
+ if (scope != null)\r
+ throw NativeGlobal.constructError(\r
+ cx, "SyntaxError", msg, scope, \r
+ (String) prop, lineno, 0, null);\r
+ else\r
+ cx.reportError(msg, (String) prop, lineno, null, 0);\r
+ }\r
+ else\r
+ cx.reportWarning(msg, (String) prop, lineno, null, 0); \r
+ }\r
+\r
+ protected Stack loops;\r
+ protected Stack loopEnds;\r
+ protected boolean inFunction;\r
+ protected IRFactory irFactory;\r
+}\r
+\r