2002/03/21 01:19:33
[org.ibex.core.git] / src / org / mozilla / javascript / NodeTransformer.java
diff --git a/src/org/mozilla/javascript/NodeTransformer.java b/src/org/mozilla/javascript/NodeTransformer.java
new file mode 100644 (file)
index 0000000..e73fdc5
--- /dev/null
@@ -0,0 +1,743 @@
+/* -*- 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