<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>src/org/mozilla/javascript/ast/ArrayComprehension.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/ArrayComprehensionLoop.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/ArrayLiteral.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/Assignment.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/AstNode.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/AstRoot.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/Block.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/BreakStatement.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/CatchClause.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/Comment.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/ConditionalExpression.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/ContinueStatement.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/DestructuringForm.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/DoLoop.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/ElementGet.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/EmptyExpression.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/ErrorCollector.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/ErrorNode.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/ExpressionStatement.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/ForInLoop.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/ForLoop.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/FunctionCall.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/FunctionNode.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/IdeErrorReporter.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/IfStatement.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/InfixExpression.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/Jump.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/KeywordLiteral.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/Label.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/LabeledStatement.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/LetNode.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/Loop.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/Name.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/NewExpression.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/NodeVisitor.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/NumberLiteral.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/ObjectLiteral.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/ObjectProperty.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/ParenthesizedExpression.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/ParseProblem.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/PropertyGet.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/RegExpLiteral.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/ReturnStatement.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/Scope.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/ScriptNode.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/StringLiteral.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/SwitchCase.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/SwitchStatement.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/Symbol.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/ThrowStatement.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/TryStatement.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/UnaryExpression.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/VariableDeclaration.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/VariableInitializer.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/WhileLoop.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/WithStatement.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/XmlDotQuery.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/XmlElemRef.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/XmlExpression.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/XmlFragment.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/XmlLiteral.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/XmlMemberGet.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/XmlPropRef.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/XmlRef.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/XmlString.java</filename>
    </added>
    <added>
      <filename>src/org/mozilla/javascript/ast/Yield.java</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -36,9 +36,9 @@
 
 name: rhino
 Name: Rhino
-version: 1_7R2pre
+version: 1_7R3pre
 # See Context#getImplementationVersion() for format of this!
-implementation.version: Rhino 1.7 release 2 PRERELEASE ${implementation.date}
+implementation.version: Rhino 1.7 release 3 PRERELEASE ${implementation.date}
 
 build.dir: build
 rhino.jar: js.jar</diff>
      <filename>build.properties</filename>
    </modified>
    <modified>
      <diff>@@ -81,6 +81,14 @@
     &lt;ant antfile=&quot;deprecatedsrc/build.xml&quot; target=&quot;compile&quot;/&gt;
   &lt;/target&gt;
 
+  &lt;target name=&quot;graph&quot; depends=&quot;init&quot;&gt;
+    &lt;ant antfile=&quot;src/build.xml&quot; target=&quot;graph&quot;/&gt;
+  &lt;/target&gt; 
+
+  &lt;target name=&quot;shell&quot; depends=&quot;compile&quot;&gt;
+    &lt;ant antfile=&quot;src/build.xml&quot; target=&quot;shell&quot;/&gt;
+  &lt;/target&gt; 
+
   &lt;target name=&quot;copy-source&quot; depends=&quot;init&quot;&gt;
     &lt;ant antfile=&quot;src/build.xml&quot; target=&quot;copy-source&quot;/&gt;
     &lt;ant antfile=&quot;toolsrc/build.xml&quot; target=&quot;copy-source&quot;/&gt;
@@ -110,10 +118,18 @@
     &lt;jar jarfile=&quot;${jarfile}&quot;
          basedir=&quot;${classes}&quot;
          manifest=&quot;src/manifest&quot;
-     compress=&quot;${jar-compression}&quot;
+         compress=&quot;${jar-compression}&quot;
      /&gt;
   &lt;/target&gt;
 
+  &lt;target name=&quot;console&quot; depends=&quot;jar&quot;&gt;
+    &lt;property name=&quot;jarfile&quot; location=&quot;${dist.dir}/${rhino.jar}&quot;/&gt;
+    &lt;java jar=&quot;${jarfile}&quot;
+          fork=&quot;true&quot;&gt;
+      &lt;arg line=&quot;-version 170&quot;/&gt;
+    &lt;/java&gt;
+  &lt;/target&gt;
+
   &lt;target name=&quot;retrotranslator&quot; depends=&quot;retrotranslator-check,retrotranslator-download&quot;&gt;
     &lt;taskdef name=&quot;retrotranslator&quot; classpath=&quot;build/download/Retrotranslator-1.2.3-bin/retrotranslator-transformer-1.2.3.jar&quot;
       classname=&quot;net.sf.retrotranslator.transformer.RetrotranslatorTask&quot;/&gt;</diff>
      <filename>build.xml</filename>
    </modified>
    <modified>
      <diff>@@ -48,6 +48,14 @@ Requires Ant version 1.2
   &lt;target name=&quot;compile&quot; depends=&quot;compile-most,compile-jdk15&quot;&gt;
   &lt;/target&gt;
 
+  &lt;target name=&quot;shell&quot; depends=&quot;compile&quot;&gt;
+    &lt;java classname=&quot;org.mozilla.javascript.tools.shell.Main&quot;
+          classpath=&quot;${classes}&quot;
+          fork=&quot;true&quot;&gt;
+      &lt;arg line=&quot;-version 170&quot;/&gt;
+    &lt;/java&gt;
+  &lt;/target&gt;
+
   &lt;target name=&quot;compile-most&quot;&gt;
     &lt;javac srcdir=&quot;src&quot;
            destdir=&quot;${classes}&quot;</diff>
      <filename>src/build.xml</filename>
    </modified>
    <modified>
      <diff>@@ -24,6 +24,7 @@
  * Contributor(s):
  *   Igor Bukanov, igor@fastmail.fm
  *   Bob Jervis
+ *   Steve Yegge
  *
  * Alternatively, the contents of this file may be used under the terms of
  * the GNU General Public License Version 2 or later (the &quot;GPL&quot;), in which
@@ -41,6 +42,8 @@ package org.mozilla.javascript;
 
 import java.util.Set;
 
+import org.mozilla.javascript.ast.ErrorCollector;
+
 public class CompilerEnvirons
 {
     public CompilerEnvirons()
@@ -57,6 +60,7 @@ public class CompilerEnvirons
         strictMode = false;
         warningAsError = false;
         generateObserverCount = false;
+        allowSharpComments = false;
     }
 
     public void initFromContext(Context cx)
@@ -132,6 +136,10 @@ public class CompilerEnvirons
         reservedKeywordAsIdentifier = flag;
     }
 
+    /**
+     * Extension to ECMA: if 'function &amp;lt;name&amp;gt;' is not followed
+     * by '(', assume &amp;lt;name&amp;gt; starts a {@code memberExpr}
+     */
     public final boolean isAllowMemberExprAsFunctionName()
     {
         return allowMemberExprAsFunctionName;
@@ -168,11 +176,24 @@ public class CompilerEnvirons
         return generatingSource;
     }
 
+    public boolean getWarnTrailingComma() {
+        return warnTrailingComma;
+    }
+
+    public void setWarnTrailingComma(boolean warn) {
+        warnTrailingComma = warn;
+    }
+
     public final boolean isStrictMode()
     {
         return strictMode;
     }
 
+    public void setStrictMode(boolean strict)
+    {
+        strictMode = strict;
+    }
+
     public final boolean reportWarningAsError()
     {
         return warningAsError;
@@ -215,6 +236,76 @@ public class CompilerEnvirons
         this.generateObserverCount = generateObserverCount;
     }
 
+    public boolean isRecordingComments() {
+        return recordingComments;
+    }
+
+    public void setRecordingComments(boolean record) {
+        recordingComments = record;
+    }
+
+    /**
+     * Turn on or off full error recovery.  In this mode, parse errors do not
+     * throw an exception, and the parser attempts to build a full syntax tree
+     * from the input.  Useful for IDEs and other frontends.
+     */
+    public void setRecoverFromErrors(boolean recover) {
+        recoverFromErrors = recover;
+    }
+
+    public boolean recoverFromErrors() {
+        return recoverFromErrors;
+    }
+
+    /**
+     * Puts the parser in &quot;IDE&quot; mode.  This enables some slightly more expensive
+     * computations, such as figuring out helpful error bounds.
+     */
+    public void setIdeMode(boolean ide) {
+        ideMode = ide;
+    }
+
+    public boolean isIdeMode() {
+        return ideMode;
+    }
+
+    public Set&lt;String&gt; getActivationNames() {
+        return activationNames;
+    }
+
+    public void setActivationNames(Set&lt;String&gt; activationNames) {
+        this.activationNames = activationNames;
+    }
+
+    /**
+     * Mozilla sources use the C preprocessor.
+     */
+    public void setAllowSharpComments(boolean allow) {
+        allowSharpComments = allow;
+    }
+
+    public boolean getAllowSharpComments() {
+        return allowSharpComments;
+    }
+
+    /**
+     * Returns a {@code CompilerEnvirons} suitable for using Rhino
+     * in an IDE environment.  Most features are enabled by default.
+     * The {@link ErrorReporter} is set to an {@link ErrorCollector}.
+     */
+    public static CompilerEnvirons ideEnvirons() {
+      CompilerEnvirons env = new CompilerEnvirons();
+      env.setRecoverFromErrors(true);
+      env.setRecordingComments(true);
+      env.setStrictMode(true);
+      env.setWarnTrailingComma(true);
+      env.setLanguageVersion(170);
+      env.setReservedKeywordAsIdentifier(true);
+      env.setIdeMode(true);
+      env.setErrorReporter(new ErrorCollector());
+      return env;
+    }
+
     private ErrorReporter errorReporter;
 
     private int languageVersion;
@@ -228,6 +319,10 @@ public class CompilerEnvirons
     private boolean strictMode;
     private boolean warningAsError;
     private boolean generateObserverCount;
+    private boolean recordingComments;
+    private boolean recoverFromErrors;
+    private boolean warnTrailingComma;
+    private boolean ideMode;
+    private boolean allowSharpComments;
     Set&lt;String&gt; activationNames;
 }
-</diff>
      <filename>src/org/mozilla/javascript/CompilerEnvirons.java</filename>
    </modified>
    <modified>
      <diff>@@ -56,6 +56,8 @@ import java.util.Set;
 import java.util.HashSet;
 import java.util.Locale;
 
+import org.mozilla.javascript.ast.AstRoot;
+import org.mozilla.javascript.ast.ScriptNode;
 import org.mozilla.javascript.debug.DebuggableScript;
 import org.mozilla.javascript.debug.Debugger;
 import org.mozilla.javascript.xml.XMLLib;
@@ -204,12 +206,12 @@ public class Context
      */
     public static final int FEATURE_PARENT_PROTO_PROPERTIES = 5;
 
-	/**
-	 * @deprecated In previous releases, this name was given to
-	 * FEATURE_PARENT_PROTO_PROPERTIES.
-	 */
+        /**
+         * @deprecated In previous releases, this name was given to
+         * FEATURE_PARENT_PROTO_PROPERTIES.
+         */
     public static final int FEATURE_PARENT_PROTO_PROPRTIES = 5;
-	
+
     /**
      * Control if support for E4X(ECMAScript for XML) extension is available.
      * If hasFeature(FEATURE_E4X) returns true, the XML syntax is available.
@@ -266,11 +268,11 @@ public class Context
      * When the feature is on Rhino will add a &quot;fileName&quot; and &quot;lineNumber&quot;
      * properties to Error objects automatically. When the feature is off, you
      * have to explicitly pass them as the second and third argument to the
-     * Error constructor. Note that neither behavior is fully ECMA 262 
-     * compliant (as 262 doesn't specify a three-arg constructor), but keeping 
+     * Error constructor. Note that neither behavior is fully ECMA 262
+     * compliant (as 262 doesn't specify a three-arg constructor), but keeping
      * the feature off results in Error objects that don't have
      * additional non-ECMA properties when constructed using the ECMA-defined
-     * single-arg constructor and is thus desirable if a stricter ECMA 
+     * single-arg constructor and is thus desirable if a stricter ECMA
      * compliance is desired, specifically adherence to the point 15.11.5. of
      * the standard.
      * &lt;p&gt;
@@ -297,7 +299,7 @@ public class Context
     public static final int FEATURE_WARNING_AS_ERROR = 12;
 
     /**
-     * Enables enhanced access to Java. 
+     * Enables enhanced access to Java.
      * Specifically, controls whether private and protected members can be
      * accessed, and whether scripts can catch all Java exceptions.
      * &lt;p&gt;
@@ -396,7 +398,7 @@ public class Context
      * is not associated with any other thread.
      * @param cx a Context to associate with the thread if possible
      * @return a Context associated with the current thread
-     * @deprecated use {@link ContextFactory#enterContext(Context)} instead as 
+     * @deprecated use {@link ContextFactory#enterContext(Context)} instead as
      * this method relies on usage of a static singleton &quot;global&quot; ContextFactory.
      * @see ContextFactory#enterContext(Context)
      * @see ContextFactory#call(ContextAction)
@@ -405,7 +407,7 @@ public class Context
     {
         return enter(cx, ContextFactory.getGlobal());
     }
-    
+
     static final Context enter(Context cx, ContextFactory factory)
     {
         Object helper = VMBridge.instance.getThreadContextHelper();
@@ -438,9 +440,9 @@ public class Context
      *
      * Calling &lt;code&gt;exit()&lt;/code&gt; will remove the association between
      * the current thread and a Context if the prior call to
-     * {@link ContextFactory#enterContext()} on this thread newly associated a 
-     * Context with this thread. Once the current thread no longer has an 
-     * associated Context, it cannot be used to execute JavaScript until it is 
+     * {@link ContextFactory#enterContext()} on this thread newly associated a
+     * Context with this thread. Once the current thread no longer has an
+     * associated Context, it cannot be used to execute JavaScript until it is
      * again associated with a Context.
      * @see ContextFactory#enterContext()
      */
@@ -458,7 +460,7 @@ public class Context
             cx.factory.onContextReleased(cx);
         }
     }
-    
+
     /**
      * Call {@link ContextAction#run(Context cx)}
      * using the Context instance associated with the current thread.
@@ -467,8 +469,8 @@ public class Context
      * construct new Context instance. The instance will be temporary
      * associated with the thread during call to
      * {@link ContextAction#run(Context)}.
-     * @deprecated use {@link ContextFactory#call(ContextAction)} instead as 
-     * this method relies on usage of a static singleton &quot;global&quot; 
+     * @deprecated use {@link ContextFactory#call(ContextAction)} instead as
+     * this method relies on usage of a static singleton &quot;global&quot;
      * ContextFactory.
      * @return The result of {@link ContextAction#run(Context)}.
      */
@@ -487,8 +489,8 @@ public class Context
      * new Context instance. The instance will be temporary associated
      * with the thread during call to {@link ContextAction#run(Context)}.
      * &lt;p&gt;
-     * It is allowed but not advisable to use null for &lt;tt&gt;factory&lt;/tt&gt; 
-     * argument in which case the global static singleton ContextFactory 
+     * It is allowed but not advisable to use null for &lt;tt&gt;factory&lt;/tt&gt;
+     * argument in which case the global static singleton ContextFactory
      * instance will be used to create new context instances.
      * @see ContextFactory#call(ContextAction)
      */
@@ -505,7 +507,7 @@ public class Context
             }
         });
     }
-    
+
     /**
      * The method implements {@link ContextFactory#call(ContextAction)} logic.
      */
@@ -1329,7 +1331,8 @@ public class Context
      *
      * @param source the source string
      * @param sourceName a string describing the source, such as a filename
-     * @param lineno the starting line number for reporting errors
+     * @param lineno the starting line number for reporting errors. Use 
+     *        0 if the line number is unknown.
      * @param securityDomain an arbitrary object that specifies security
      *        information about the origin or owner of the script. For
      *        implementations that don't care about security, this value
@@ -1835,7 +1838,7 @@ public class Context
     {
         return optimizationLevel;
     }
-    
+
     /**
      * Set the current optimization level.
      * &lt;p&gt;
@@ -1881,17 +1884,17 @@ public class Context
     }
 
     /**
-     * Returns the maximum stack depth (in terms of number of call frames) 
+     * Returns the maximum stack depth (in terms of number of call frames)
      * allowed in a single invocation of interpreter. If the set depth would be
      * exceeded, the interpreter will throw an EvaluatorException in the script.
-     * Defaults to Integer.MAX_VALUE. The setting only has effect for 
+     * Defaults to Integer.MAX_VALUE. The setting only has effect for
      * interpreted functions (those compiled with optimization level set to -1).
      * As the interpreter doesn't use the Java stack but rather manages its own
-     * stack in the heap memory, a runaway recursion in interpreted code would 
-     * eventually consume all available memory and cause OutOfMemoryError 
+     * stack in the heap memory, a runaway recursion in interpreted code would
+     * eventually consume all available memory and cause OutOfMemoryError
      * instead of a StackOverflowError limited to only a single thread. This
      * setting helps prevent such situations.
-     *  
+     *
      * @return The current maximum interpreter stack depth.
      */
     public final int getMaximumInterpreterStackDepth()
@@ -1900,21 +1903,21 @@ public class Context
     }
 
     /**
-     * Sets the maximum stack depth (in terms of number of call frames) 
+     * Sets the maximum stack depth (in terms of number of call frames)
      * allowed in a single invocation of interpreter. If the set depth would be
      * exceeded, the interpreter will throw an EvaluatorException in the script.
-     * Defaults to Integer.MAX_VALUE. The setting only has effect for 
+     * Defaults to Integer.MAX_VALUE. The setting only has effect for
      * interpreted functions (those compiled with optimization level set to -1).
      * As the interpreter doesn't use the Java stack but rather manages its own
-     * stack in the heap memory, a runaway recursion in interpreted code would 
-     * eventually consume all available memory and cause OutOfMemoryError 
+     * stack in the heap memory, a runaway recursion in interpreted code would
+     * eventually consume all available memory and cause OutOfMemoryError
      * instead of a StackOverflowError limited to only a single thread. This
      * setting helps prevent such situations.
-     * 
+     *
      * @param max the new maximum interpreter stack depth
      * @throws IllegalStateException if this context's optimization level is not
      * -1
-     * @throws IllegalArgumentException if the new depth is not at least 1 
+     * @throws IllegalArgumentException if the new depth is not at least 1
      */
     public final void setMaximumInterpreterStackDepth(int max)
     {
@@ -1927,7 +1930,7 @@ public class Context
         }
         maximumInterpreterStackDepth = max;
     }
-    
+
     /**
      * Set the security controller for this context.
      * &lt;p&gt; SecurityController may only be set if it is currently null
@@ -2159,21 +2162,21 @@ public class Context
         ContextFactory f = getFactory();
         return f.hasFeature(this, featureIndex);
     }
-	
-	/**
-		Returns an object which specifies an E4X implementation to use within
-		this &lt;code&gt;Context&lt;/code&gt;.  Note
-		that the XMLLib.Factory interface should be considered experimental.
-	 
-		The default implementation uses the implementation provided by this
-		&lt;code&gt;Context&lt;/code&gt;'s {@link ContextFactory}.
-	 
-		@return An XMLLib.Factory.  Should not return &lt;code&gt;null&lt;/code&gt; if
-			{@link #FEATURE_E4X} is enabled.  See {@link #hasFeature}.
-	 */
-	public XMLLib.Factory getE4xImplementationFactory() {
-		return getFactory().getE4xImplementationFactory();
-	}
+
+        /**
+                Returns an object which specifies an E4X implementation to use within
+                this &lt;code&gt;Context&lt;/code&gt;.  Note
+                that the XMLLib.Factory interface should be considered experimental.
+
+                The default implementation uses the implementation provided by this
+                &lt;code&gt;Context&lt;/code&gt;'s {@link ContextFactory}.
+
+                @return An XMLLib.Factory.  Should not return &lt;code&gt;null&lt;/code&gt; if
+                        {@link #FEATURE_E4X} is enabled.  See {@link #hasFeature}.
+         */
+        public XMLLib.Factory getE4xImplementationFactory() {
+                return getFactory().getE4xImplementationFactory();
+        }
 
     /**
      * Get threshold of executed instructions counter that triggers call to
@@ -2223,7 +2226,7 @@ public class Context
      * calls to accumulate an estimate of the instructions executed.
      */
     public void setGenerateObserverCount(boolean generateObserverCount) {
-    	this.generateObserverCount = generateObserverCount;
+        this.generateObserverCount = generateObserverCount;
     }
 
     /**
@@ -2363,18 +2366,18 @@ public class Context
         if (returnFunction) {
             p.calledByCompileFunction = true;
         }
-        ScriptOrFnNode tree;
+        AstRoot ast;
         if (sourceString != null) {
-            tree = p.parse(sourceString, sourceName, lineno);
+            ast = p.parse(sourceString, sourceName, lineno);
         } else {
-            tree = p.parse(sourceReader, sourceName, lineno);
+            ast = p.parse(sourceReader, sourceName, lineno);
         }
         if (returnFunction) {
-            if (!(tree.getFunctionCount() == 1
-                  &amp;&amp; tree.getFirstChild() != null
-                  &amp;&amp; tree.getFirstChild().getType() == Token.FUNCTION))
+            // parser no longer adds function to script node
+            if (!(ast.getFirstChild() != null
+                  &amp;&amp; ast.getFirstChild().getType() == Token.FUNCTION))
             {
-                // XXX: the check just look for the first child
+                // XXX: the check just looks for the first child
                 // and allows for more nodes after it for compatibility
                 // with sources like function() {};;;
                 throw new IllegalArgumentException(
@@ -2382,16 +2385,21 @@ public class Context
             }
         }
 
+        IRFactory irf = new IRFactory(compilerEnv, compilationErrorReporter);
+        ScriptNode tree = irf.transformTree(ast);
+
+        // discard everything but the IR tree
+        p = null;
+        ast = null;
+        irf = null;
+
         if (compiler == null) {
             compiler = createCompiler();
         }
 
-        String encodedSource = p.getEncodedSource();
-
         Object bytecode = compiler.compile(compilerEnv,
-                                           tree, encodedSource,
+                                           tree, tree.getEncodedSource(),
                                            returnFunction);
-
         if (debugger != null) {
             if (sourceString == null) Kit.codeBug();
             if (bytecode instanceof DebuggableScript) {
@@ -2635,5 +2643,5 @@ public class Context
     Scriptable scratchScriptable;
 
     // Generate an observer count on compiled code
-	public boolean generateObserverCount = false;
+    public boolean generateObserverCount = false;
 }</diff>
      <filename>src/org/mozilla/javascript/Context.java</filename>
    </modified>
    <modified>
      <diff>@@ -41,6 +41,8 @@
 
 package org.mozilla.javascript;
 
+import org.mozilla.javascript.ast.FunctionNode;
+
 /**
  * The following class save decompilation information about the source.
  * Source information is returned from the parser as a String</diff>
      <filename>src/org/mozilla/javascript/Decompiler.java</filename>
    </modified>
    <modified>
      <diff>@@ -30,6 +30,8 @@
 
 package org.mozilla.javascript;
 
+import org.mozilla.javascript.ast.ScriptNode;
+
 import java.util.List;
 
 /**
@@ -43,7 +45,7 @@ public interface Evaluator {
      * tree into an executable form.
      *
      * @param compilerEnv Compiler environment
-     * @param tree intermediate representation
+     * @param tree parse tree
      * @param encodedSource encoding of the source code for decompilation
      * @param returnFunction if true, compiling a function
      * @return an opaque object that can be passed to either
@@ -51,9 +53,9 @@ public interface Evaluator {
      *         value of returnFunction
      */
     public Object compile(CompilerEnvirons compilerEnv,
-                      ScriptOrFnNode tree,
-                      String encodedSource,
-                      boolean returnFunction);
+                          ScriptNode tree,
+                          String encodedSource,
+                          boolean returnFunction);
 
     /**
      * Create a function object.</diff>
      <filename>src/org/mozilla/javascript/Evaluator.java</filename>
    </modified>
    <modified>
      <diff>@@ -28,6 +28,7 @@
  *   Bob Jervis
  *   Terry Lucas
  *   Milen Nankov
+ *   Steve Yegge
  *
  * Alternatively, the contents of this file may be used under the terms of
  * the GNU General Public License Version 2 or later (the &quot;GPL&quot;), in which
@@ -43,52 +44,804 @@
 
 package org.mozilla.javascript;
 
+import org.mozilla.javascript.ast.*;
+
 import java.util.List;
 import java.util.ArrayList;
 
 /**
- * This class allows the creation of nodes, and follows the Factory pattern.
+ * This class rewrites the parse tree into an IR suitable for codegen.
  *
  * @see Node
  * @author Mike McCabe
  * @author Norris Boyd
  */
-final class IRFactory
+public final class IRFactory extends Parser
 {
-    IRFactory(Parser parser)
-    {
-        this.parser = parser;
+    private static final int LOOP_DO_WHILE = 0;
+    private static final int LOOP_WHILE    = 1;
+    private static final int LOOP_FOR      = 2;
+
+    private static final int ALWAYS_TRUE_BOOLEAN = 1;
+    private static final int ALWAYS_FALSE_BOOLEAN = -1;
+
+    private Decompiler decompiler = new Decompiler();
+
+    public IRFactory() {
+        super();
     }
 
-    ScriptOrFnNode createScript()
-    {
-        return new ScriptOrFnNode(Token.SCRIPT);
+    public IRFactory(CompilerEnvirons env) {
+        this(env, env.getErrorReporter());
     }
 
-    /**
-     * Script (for associating file/url names with toplevel scripts.)
-     */
-    void initScript(ScriptOrFnNode scriptNode, Node body)
-    {
-        Node children = body.getFirstChild();
-        if (children != null) { scriptNode.addChildrenToBack(children); }
+    public IRFactory(CompilerEnvirons env, ErrorReporter errorReporter) {
+        super(env, errorReporter);
     }
 
     /**
-     * Leaf
+     * Transforms the tree into a lower-level IR suitable for codegen.
+     * Optionally generates the encoded source.
      */
-    Node createLeaf(int nodeType)
-    {
-        return new Node(nodeType);
+    public ScriptNode transformTree(AstRoot root) {
+        currentScriptOrFn = root;
+        int sourceStartOffset = decompiler.getCurrentOffset();
+
+        ScriptNode script = (ScriptNode)transform(root);
+
+        int sourceEndOffset = decompiler.getCurrentOffset();
+        script.setEncodedSourceBounds(sourceStartOffset,
+                                      sourceEndOffset);
+
+        if (compilerEnv.isGeneratingSource()) {
+            script.setEncodedSource(decompiler.getEncodedSource());
+        }
+
+        decompiler = null;
+        return script;
     }
 
-    /**
-     * Statement leaf nodes.
-     */
+    // Might want to convert this to polymorphism - move transform*
+    // functions into the AstNode subclasses.  OTOH that would make
+    // IR transformation part of the public AST API - desirable?
+    // Another possibility:  create AstTransformer interface and adapter.
+    public Node transform(AstNode node) {
+        switch (node.getType()) {
+          case Token.ARRAYCOMP:
+              return transformArrayComp((ArrayComprehension)node);
+          case Token.ARRAYLIT:
+              return transformArrayLiteral((ArrayLiteral)node);
+          case Token.BLOCK:
+              return transformBlock(node);
+          case Token.BREAK:
+              return transformBreak((BreakStatement)node);
+          case Token.CALL:
+              return transformFunctionCall((FunctionCall)node);
+          case Token.CONTINUE:
+              return transformContinue((ContinueStatement)node);
+          case Token.DO:
+              return transformDoLoop((DoLoop)node);
+          case Token.EMPTY:
+              return node;
+          case Token.FOR:
+              if (node instanceof ForInLoop) {
+                  return transformForInLoop((ForInLoop)node);
+              } else {
+                  return transformForLoop((ForLoop)node);
+              }
+          case Token.FUNCTION:
+              return transformFunction((FunctionNode)node);
+          case Token.GETELEM:
+              return transformElementGet((ElementGet)node);
+          case Token.GETPROP:
+              return transformPropertyGet((PropertyGet)node);
+          case Token.HOOK:
+              return transformCondExpr((ConditionalExpression)node);
+          case Token.IF:
+              return transformIf((IfStatement)node);
 
-    Node createSwitch(Node expr, int lineno)
-    {
+          case Token.TRUE:
+          case Token.FALSE:
+          case Token.THIS:
+          case Token.NULL:
+          case Token.DEBUGGER:
+              return transformLiteral(node);
+
+          case Token.NAME:
+              return transformName((Name)node);
+          case Token.NUMBER:
+              return transformNumber((NumberLiteral)node);
+          case Token.NEW:
+              return transformNewExpr((NewExpression)node);
+          case Token.OBJECTLIT:
+              return transformObjectLiteral((ObjectLiteral)node);
+          case Token.REGEXP:
+              return transformRegExp((RegExpLiteral)node);
+          case Token.RETURN:
+              return transformReturn((ReturnStatement)node);
+          case Token.SCRIPT:
+              return transformScript((ScriptNode)node);
+          case Token.STRING:
+              return transformString((StringLiteral)node);
+          case Token.SWITCH:
+              return transformSwitch((SwitchStatement)node);
+          case Token.THROW:
+              return transformThrow((ThrowStatement)node);
+          case Token.TRY:
+              return transformTry((TryStatement)node);
+          case Token.WHILE:
+              return transformWhileLoop((WhileLoop)node);
+          case Token.WITH:
+              return transformWith((WithStatement)node);
+          case Token.YIELD:
+              return transformYield((Yield)node);
+          default:
+              if (node instanceof ExpressionStatement) {
+                  return transformExprStmt((ExpressionStatement)node);
+              }
+              if (node instanceof Assignment) {
+                  return transformAssignment((Assignment)node);
+              }
+              if (node instanceof UnaryExpression) {
+                  return transformUnary((UnaryExpression)node);
+              }
+              if (node instanceof XmlMemberGet) {
+                  return transformXmlMemberGet((XmlMemberGet)node);
+              }
+              if (node instanceof InfixExpression) {
+                  return transformInfix((InfixExpression)node);
+              }
+              if (node instanceof VariableDeclaration) {
+                  return transformVariables((VariableDeclaration)node);
+              }
+              if (node instanceof ParenthesizedExpression) {
+                  return transformParenExpr((ParenthesizedExpression)node);
+              }
+              if (node instanceof LabeledStatement) {
+                  return transformLabeledStatement((LabeledStatement)node);
+              }
+              if (node instanceof LetNode) {
+                  return transformLetNode((LetNode)node);
+              }
+              if (node instanceof XmlRef) {
+                  return transformXmlRef((XmlRef)node);
+              }
+              if (node instanceof XmlLiteral) {
+                  return transformXmlLiteral((XmlLiteral)node);
+              }
+              throw new IllegalArgumentException(&quot;Can't transform: &quot; + node);
+        }
+    }
+
+    private Node transformArrayComp(ArrayComprehension node) {
+        // An array comprehension expression such as
+        //
+        //   [expr for (x in foo) for each ([y, z] in bar) if (cond)]
         //
+        // is rewritten approximately as
+        //
+        // new Scope(ARRAYCOMP) {
+        //   new Node(BLOCK) {
+        //     let tmp1 = new Array;
+        //     for (let x in foo) {
+        //       for each (let tmp2 in bar) {
+        //         if (cond) {
+        //           tmp1.push([y, z] = tmp2, expr);
+        //         }
+        //       }
+        //     }
+        //   }
+        //   createName(tmp1)
+        // }
+
+        int lineno = node.getLineno();
+        Scope scopeNode = createScopeNode(Token.ARRAYCOMP, lineno);
+        String arrayName = currentScriptOrFn.getNextTempName();
+        pushScope(scopeNode);
+        try {
+            defineSymbol(Token.LET, arrayName, false);
+            Node block = new Node(Token.BLOCK, lineno);
+            Node newArray = createCallOrNew(Token.NEW, createName(&quot;Array&quot;));
+            Node init = new Node(Token.EXPR_VOID,
+                                 createAssignment(Token.ASSIGN,
+                                                  createName(arrayName),
+                                                  newArray),
+                                 lineno);
+            block.addChildToBack(init);
+            block.addChildToBack(arrayCompTransformHelper(node, arrayName));
+            scopeNode.addChildToBack(block);
+            scopeNode.addChildToBack(createName(arrayName));
+            return scopeNode;
+        } finally {
+            popScope();
+        }
+    }
+
+    private Node arrayCompTransformHelper(ArrayComprehension node,
+                                          String arrayName) {
+        decompiler.addToken(Token.LB);
+        int lineno = node.getLineno();
+        Node expr = transform(node.getResult());
+
+        List&lt;ArrayComprehensionLoop&gt; loops = node.getLoops();
+        int numLoops = loops.size();
+
+        // Walk through loops, collecting and defining their iterator symbols.
+        Node[] iterators = new Node[numLoops];
+        Node[] iteratedObjs = new Node[numLoops];
+
+        for (int i = 0; i &lt; numLoops; i++) {
+            ArrayComprehensionLoop acl = loops.get(i);
+            decompiler.addName(&quot; &quot;);
+            decompiler.addToken(Token.FOR);
+            if (acl.isForEach()) {
+                decompiler.addName(&quot;each &quot;);
+            }
+            decompiler.addToken(Token.LP);
+
+            AstNode iter = acl.getIterator();
+            String name = null;
+            if (iter.getType() == Token.NAME) {
+                name = iter.getString();
+                decompiler.addName(name);
+            } else {
+                // destructuring assignment
+                decompile(iter);
+                name = currentScriptOrFn.getNextTempName();
+                defineSymbol(Token.LP, name, false);
+                expr = createBinary(Token.COMMA,
+                                    createAssignment(Token.ASSIGN,
+                                                     iter,
+                                                     createName(name)),
+                                    expr);
+            }
+            Node init = createName(name);
+            // Define as a let since we want the scope of the variable to
+            // be restricted to the array comprehension
+            defineSymbol(Token.LET, name, false);
+            iterators[i] = init;
+
+            decompiler.addToken(Token.IN);
+            iteratedObjs[i] = transform(acl.getIteratedObject());
+            decompiler.addToken(Token.RP);
+        }
+
+        // generate code for tmpArray.push(body)
+        Node call = createCallOrNew(Token.CALL,
+                                    createPropertyGet(createName(arrayName),
+                                                      null,
+                                                      &quot;push&quot;, 0));
+
+        Node body = new Node(Token.EXPR_VOID, call, lineno);
+
+        if (node.getFilter() != null) {
+            decompiler.addName(&quot; &quot;);
+            decompiler.addToken(Token.IF);
+            decompiler.addToken(Token.LP);
+            body = createIf(transform(node.getFilter()), body, null, lineno);
+            decompiler.addToken(Token.RP);
+        }
+
+        // Now walk loops in reverse to build up the body statement.
+        int pushed = 0;
+        try {
+            for (int i = numLoops-1; i &gt;= 0; i--) {
+                ArrayComprehensionLoop acl = loops.get(i);
+                Scope loop = createLoopNode(null,  // no label
+                                            acl.getLineno());
+                pushScope(loop);
+                pushed++;
+                body = createForIn(Token.LET,
+                                   loop,
+                                   iterators[i],
+                                   iteratedObjs[i],
+                                   body,
+                                   acl.isForEach());
+            }
+        } finally {
+            for (int i = 0; i &lt; pushed; i++) {
+                popScope();
+            }
+        }
+
+        decompiler.addToken(Token.RB);
+
+        // Now that we've accumulated any destructuring forms,
+        // add expr to the call node; it's pushed on each iteration.
+        call.addChildToBack(expr);
+        return body;
+    }
+
+    private Node transformArrayLiteral(ArrayLiteral node) {
+        if (node.isDestructuring()) {
+            return node;
+        }
+        decompiler.addToken(Token.LB);
+        List&lt;AstNode&gt; elems = node.getElements();
+        Node array = new Node(Token.ARRAYLIT);
+        List&lt;Integer&gt; skipIndexes = null;
+        for (int i = 0, j = 0; i &lt; elems.size(); ++i) {
+            AstNode elem = elems.get(i);
+            if (elem.getType() != Token.EMPTY) {
+                array.addChildToBack(transform(elem));
+            } else {
+                if (skipIndexes == null) {
+                    skipIndexes = new ArrayList&lt;Integer&gt;();
+                }
+                skipIndexes.add(i);
+            }
+            if (i &lt; elems.size() - 1)
+                decompiler.addToken(Token.COMMA);
+        }
+        decompiler.addToken(Token.RB);
+        array.putIntProp(Node.DESTRUCTURING_ARRAY_LENGTH,
+                         node.getDestructuringLength());
+        if (skipIndexes != null) {
+            int[] skips = new int[skipIndexes.size()];
+            for (int i = 0; i &lt; skipIndexes.size(); i++)
+                skips[i] = skipIndexes.get(i);
+            array.putProp(Node.SKIP_INDEXES_PROP, skips);
+        }
+        return array;
+    }
+
+    private Node transformAssignment(Assignment node) {
+        AstNode left = removeParens(node.getLeft());
+        Node target = null;
+        if (isDestructuring(left)) {
+            decompile(left);
+            // Bug: for code like &quot;var obj={p:3};[obj.p]=[9];&quot;, &quot;left&quot; will
+            // be ARRAYLITERAL with an embedded GETPROP. This causes errors
+            // at codegen.
+            target = left;
+        } else {
+            target = transform(left);
+        }
+        decompiler.addToken(node.getType());
+        return createAssignment(node.getType(),
+                                target,
+                                transform(node.getRight()));
+    }
+
+    private Node transformBlock(AstNode node) {
+        if (node instanceof Scope) {
+            pushScope((Scope)node);
+        }
+        try {
+            List&lt;Node&gt; kids = new ArrayList&lt;Node&gt;();
+            for (Node kid : node) {
+                kids.add(transform((AstNode)kid));
+            }
+            node.removeChildren();
+            for (Node kid : kids) {
+                node.addChildToBack(kid);
+            }
+            return node;
+        } finally {
+            if (node instanceof Scope) {
+                popScope();
+            }
+        }
+    }
+
+    private Node transformBreak(BreakStatement node) {
+        decompiler.addToken(Token.BREAK);
+        if (node.getBreakLabel() != null) {
+            decompiler.addName(node.getBreakLabel().getIdentifier());
+        }
+        decompiler.addEOL(Token.SEMI);
+        return node;
+    }
+
+    private Node transformCondExpr(ConditionalExpression node) {
+        Node test = transform(node.getTestExpression());
+        decompiler.addToken(Token.HOOK);
+        Node ifTrue = transform(node.getTrueExpression());
+        decompiler.addToken(Token.COLON);
+        Node ifFalse = transform(node.getFalseExpression());
+        return createCondExpr(test, ifTrue, ifFalse);
+    }
+
+    private Node transformContinue(ContinueStatement node) {
+        decompiler.addToken(Token.CONTINUE);
+        if (node.getLabel() != null) {
+            decompiler.addName(node.getLabel().getIdentifier());
+        }
+        decompiler.addEOL(Token.SEMI);
+        return node;
+    }
+
+    private Node transformDoLoop(DoLoop loop) {
+        loop.setType(Token.LOOP);
+        pushScope(loop);
+        try {
+            decompiler.addToken(Token.DO);
+            decompiler.addEOL(Token.LC);
+            Node body = transform(loop.getBody());
+            decompiler.addToken(Token.RC);
+            decompiler.addToken(Token.WHILE);
+            decompiler.addToken(Token.LP);
+            Node cond = transform(loop.getCondition());
+            decompiler.addToken(Token.RP);
+            decompiler.addEOL(Token.SEMI);
+            return createLoop(loop, LOOP_DO_WHILE,
+                              body, cond, null, null);
+        } finally {
+            popScope();
+        }
+    }
+
+    private Node transformElementGet(ElementGet node) {
+        // OPT: could optimize to createPropertyGet
+        // iff elem is string that can not be number
+        Node target = transform(node.getTarget());
+        decompiler.addToken(Token.LB);
+        Node element = transform(node.getElement());
+        decompiler.addToken(Token.RB);
+        return new Node(Token.GETELEM, target, element);
+    }
+
+    private Node transformExprStmt(ExpressionStatement node) {
+        Node expr = transform(node.getExpression());
+        decompiler.addEOL(Token.SEMI);
+        return new Node(node.getType(), expr, node.getLineno());
+    }
+
+    private Node transformForInLoop(ForInLoop loop) {
+        decompiler.addToken(Token.FOR);
+        if (loop.isForEach())
+            decompiler.addName(&quot;each &quot;);
+        decompiler.addToken(Token.LP);
+
+        loop.setType(Token.LOOP);
+        pushScope(loop);
+        try {
+            int declType = -1;
+            AstNode iter = loop.getIterator();
+            if (iter instanceof VariableDeclaration) {
+                declType = ((VariableDeclaration)iter).getType();
+            }
+            Node lhs = transform(iter);
+            decompiler.addToken(Token.IN);
+            Node obj = transform(loop.getIteratedObject());
+            decompiler.addToken(Token.RP);
+            decompiler.addEOL(Token.LC);
+            Node body = transform(loop.getBody());
+            decompiler.addEOL(Token.RC);
+            return createForIn(declType, loop, lhs, obj, body,
+                               loop.isForEach());
+        } finally {
+            popScope();
+        }
+    }
+
+    private Node transformForLoop(ForLoop loop) {
+        decompiler.addToken(Token.FOR);
+        decompiler.addToken(Token.LP);
+        loop.setType(Token.LOOP);
+        // XXX: Can't use pushScope/popScope here since 'createFor' may split
+        // the scope
+        Scope savedScope = currentScope;
+        currentScope = loop;
+        try {
+            Node init = transform(loop.getInitializer());
+            decompiler.addToken(Token.SEMI);
+            Node test = transform(loop.getCondition());
+            decompiler.addToken(Token.SEMI);
+            Node incr = transform(loop.getIncrement());
+            decompiler.addToken(Token.RP);
+            decompiler.addEOL(Token.LC);
+            Node body = transform(loop.getBody());
+            decompiler.addEOL(Token.RC);
+            return createFor(loop, init, test, incr, body);
+        } finally {
+            currentScope = savedScope;
+        }
+    }
+
+    private Node transformFunction(FunctionNode fn) {
+        int functionType = fn.getFunctionType();
+        int start = decompiler.markFunctionStart(functionType);
+        Node mexpr = decompileFunctionHeader(fn);
+        int index = currentScriptOrFn.addFunction(fn);
+
+        PerFunctionVariables savedVars = new PerFunctionVariables(fn);
+        try {
+            // If we start needing to record much more codegen metadata during
+            // function parsing, we should lump it all into a helper class.
+            Node destructuring = (Node)fn.getProp(Node.DESTRUCTURING_PARAMS);
+            fn.removeProp(Node.DESTRUCTURING_PARAMS);
+
+            int lineno = fn.getBody().getLineno();
+            ++nestingOfFunction;  // only for body, not params
+            Node body = transform(fn.getBody());
+
+            decompiler.addToken(Token.RC);
+            fn.setEncodedSourceBounds(start, decompiler.markFunctionEnd(start));
+
+            if (functionType != FunctionNode.FUNCTION_EXPRESSION) {
+                // Add EOL only if function is not part of expression
+                // since it gets SEMI + EOL from Statement in that case
+                decompiler.addToken(Token.EOL);
+            }
+
+            if (destructuring != null) {
+                body.addChildToFront(new Node(Token.EXPR_VOID,
+                                              destructuring, lineno));
+            }
+
+            int syntheticType = fn.getFunctionType();
+            Node pn = initFunction(fn, index, body, syntheticType);
+            if (mexpr != null) {
+                pn = createAssignment(Token.ASSIGN, mexpr, pn);
+                if (syntheticType != FunctionNode.FUNCTION_EXPRESSION) {
+                    pn = createExprStatementNoReturn(pn, fn.getLineno());
+                }
+            }
+            return pn;
+
+        } finally {
+            --nestingOfFunction;
+            savedVars.restore();
+        }
+    }
+
+    private Node transformFunctionCall(FunctionCall node) {
+        Node call = createCallOrNew(Token.CALL, transform(node.getTarget()));
+        call.setLineno(node.getLineno());
+        decompiler.addToken(Token.LP);
+        List&lt;AstNode&gt; args = node.getArguments();
+        for (int i = 0; i &lt; args.size(); i++) {
+            AstNode arg = args.get(i);
+            call.addChildToBack(transform(arg));
+            if (i &lt; args.size() - 1) {
+                decompiler.addToken(Token.COMMA);
+            }
+        }
+        decompiler.addToken(Token.RP);
+        return call;
+    }
+
+    private Node transformIf(IfStatement n) {
+        decompiler.addToken(Token.IF);
+        decompiler.addToken(Token.LP);
+        Node cond = transform(n.getCondition());
+        decompiler.addToken(Token.RP);
+        decompiler.addEOL(Token.LC);
+        Node ifTrue = transform(n.getThenPart());
+        Node ifFalse = null;        
+        if (n.getElsePart() != null) {
+            decompiler.addToken(Token.RC);
+            decompiler.addToken(Token.ELSE);
+            decompiler.addEOL(Token.LC);
+            ifFalse = transform(n.getElsePart());
+        }
+        decompiler.addEOL(Token.RC);
+        return createIf(cond, ifTrue, ifFalse, n.getLineno());
+    }
+
+    private Node transformInfix(InfixExpression node) {
+        Node left = transform(node.getLeft());
+        decompiler.addToken(node.getType());
+        Node right = transform(node.getRight());
+        if (node instanceof XmlDotQuery) {
+            decompiler.addToken(Token.RP);
+        }
+        return createBinary(node.getType(), left, right);
+    }
+
+    private Node transformLabeledStatement(LabeledStatement ls) {
+        for (Label lb : ls.getLabels()) {
+            decompiler.addName(lb.getName());
+            decompiler.addEOL(Token.COLON);
+        }
+        Label label = ls.getFirstLabel();
+        Node statement = transform(ls.getStatement());
+
+        // Make a target and put it _after_ the statement node.  Add in the
+        // LABEL node, so breaks get the right target.
+        Node breakTarget = Node.newTarget();
+        Node block = new Node(Token.BLOCK, label, statement, breakTarget);
+        label.target = breakTarget;
+
+        return block;
+    }
+
+    private Node transformLetNode(LetNode node) {
+        pushScope(node);
+        try {
+            decompiler.addToken(Token.LET);
+            decompiler.addToken(Token.LP);
+            Node vars = transformVariableInitializers(node.getVariables());
+            decompiler.addToken(Token.RP);
+            node.addChildToBack(vars);
+            boolean letExpr = node.getType() == Token.LETEXPR;
+            if (node.getBody() != null) {
+                if (letExpr) {
+                    decompiler.addName(&quot; &quot;);
+                } else {
+                    decompiler.addEOL(Token.LC);
+                }
+                node.addChildToBack(transform(node.getBody()));
+                if (!letExpr) {
+                    decompiler.addEOL(Token.RC);
+                }
+            }
+            return node;
+        } finally {
+            popScope();
+        }
+    }
+
+    private Node transformLiteral(AstNode node) {
+        decompiler.addToken(node.getType());
+        return node;
+    }
+
+    private Node transformName(Name node) {
+        decompiler.addName(node.getIdentifier());
+        return node;
+    }
+
+    private Node transformNewExpr(NewExpression node) {
+        decompiler.addToken(Token.NEW);
+        Node nx = createCallOrNew(Token.NEW, transform(node.getTarget()));
+        nx.setLineno(node.getLineno());
+        List&lt;AstNode&gt; args = node.getArguments();
+        if (!args.isEmpty())
+            decompiler.addToken(Token.LP);
+        for (int i = 0; i &lt; args.size(); i++) {
+            AstNode arg = args.get(i);
+            nx.addChildToBack(transform(arg));
+            if (i &lt; args.size() - 1) {
+                decompiler.addToken(Token.COMMA);
+            }
+        }
+        if (!args.isEmpty())
+            decompiler.addToken(Token.RP);
+        if (node.getInitializer() != null) {
+            nx.addChildToBack(transformObjectLiteral(node.getInitializer()));
+        }
+        return nx;
+    }
+
+    private Node transformNumber(NumberLiteral node) {
+        decompiler.addNumber(node.getNumber());
+        return node;
+    }
+
+    private Node transformObjectLiteral(ObjectLiteral node) {
+        if (node.isDestructuring()) {
+            return node;
+        }
+        // createObjectLiteral rewrites its argument as object
+        // creation plus object property entries, so later compiler
+        // stages don't need to know about object literals.
+        decompiler.addToken(Token.LC);
+        List&lt;ObjectProperty&gt; elems = node.getElements();
+        Node object = new Node(Token.OBJECTLIT);
+        Object[] properties;
+        if (elems.isEmpty()) {
+            properties = ScriptRuntime.emptyArgs;
+        } else {
+            int size = elems.size(), i = 0;
+            properties = new Object[size];
+            for (ObjectProperty prop : elems) {
+                if (prop.isGetter()) {
+                    decompiler.addToken(Token.GET);
+                } else if (prop.isSetter()) {
+                    decompiler.addToken(Token.SET);
+                }
+
+                properties[i++] = getPropKey(prop.getLeft());
+
+                // OBJECTLIT is used as ':' in object literal for
+                // decompilation to solve spacing ambiguity.
+                if (!(prop.isGetter() || prop.isSetter())) {
+                    decompiler.addToken(Token.OBJECTLIT);
+                }
+
+                Node right = transform(prop.getRight());
+                if (prop.isGetter()) {
+                    right = createUnary(Token.GET, right);
+                } else if (prop.isSetter()) {
+                    right = createUnary(Token.SET, right);
+                }
+                object.addChildToBack(right);
+
+                if (i &lt; size) {
+                    decompiler.addToken(Token.COMMA);
+                }
+            }
+        }
+        decompiler.addToken(Token.RC);
+        object.putProp(Node.OBJECT_IDS_PROP, properties);
+        return object;
+    }
+
+    private Object getPropKey(Node id) {
+        Object key;
+        if (id instanceof Name) {
+            String s = ((Name)id).getIdentifier();
+            decompiler.addName(s);
+            key = ScriptRuntime.getIndexObject(s);
+        } else if (id instanceof StringLiteral) {
+            String s = ((StringLiteral)id).getValue();
+            decompiler.addString(s);
+            key = ScriptRuntime.getIndexObject(s);
+        } else if (id instanceof NumberLiteral) {
+            double n = ((NumberLiteral)id).getNumber();
+            decompiler.addNumber(n);
+            key = ScriptRuntime.getIndexObject(n);
+        } else {
+            throw Kit.codeBug();
+        }
+        return key;
+    }
+
+    private Node transformParenExpr(ParenthesizedExpression node) {
+        AstNode expr = node.getExpression();
+        decompiler.addToken(Token.LP);
+        int count = 1;
+        while (expr instanceof ParenthesizedExpression) {
+            decompiler.addToken(Token.LP);
+            count++;
+            expr = ((ParenthesizedExpression)expr).getExpression();
+        }
+        Node result = transform(expr);
+        for (int i = 0; i &lt; count; i++) {
+            decompiler.addToken(Token.RP);
+        }
+        result.putProp(Node.PARENTHESIZED_PROP, Boolean.TRUE);
+        return result;
+    }
+
+    private Node transformPropertyGet(PropertyGet node) {
+        Node target = transform(node.getTarget());
+        String name = node.getProperty().getIdentifier();
+        decompiler.addToken(Token.DOT);
+        decompiler.addName(name);
+        return createPropertyGet(target, null, name, 0);
+    }
+
+    private Node transformRegExp(RegExpLiteral node) {
+        decompiler.addRegexp(node.getValue(), node.getFlags());
+        currentScriptOrFn.addRegExp(node);
+        return node;
+    }
+
+    private Node transformReturn(ReturnStatement node) {
+        decompiler.addToken(Token.RETURN);
+        AstNode rv = node.getReturnValue();
+        Node value = rv == null ? null : transform(rv);
+        decompiler.addEOL(Token.SEMI);
+        return rv == null
+            ? new Node(Token.RETURN, node.getLineno())
+            : new Node(Token.RETURN, value, node.getLineno());
+    }
+
+    private Node transformScript(ScriptNode node) {
+        decompiler.addToken(Token.SCRIPT);
+        if (currentScope != null) Kit.codeBug();
+        currentScope = node;
+        Node body = new Node(Token.BLOCK);
+        for (Node kid : node) {
+            body.addChildToBack(transform((AstNode)kid));
+        }
+        node.removeChildren();
+        Node children = body.getFirstChild();
+        if (children != null) {
+            node.addChildrenToBack(children);
+        }
+        return node;
+    }
+
+    private Node transformString(StringLiteral node) {
+        decompiler.addString(node.getValue());
+        return Node.newString(node.getValue());
+    }
+
+    private Node transformSwitch(SwitchStatement node) {
         // The switch will be rewritten from:
         //
         // switch (expr) {
@@ -123,29 +876,335 @@ final class IRFactory
         // by &quot;goto breakLabel&quot;.
         //
         // If the original switch does not have the default label, then
-        // the transformed code would contain after the switch instead of
-        //     goto labelDefault;
-        // the following goto:
+        // after the switch he transformed code would contain this goto:
         //     goto breakLabel;
-        //
+        // instead of:
+        //     goto labelDefault;
+
+        decompiler.addToken(Token.SWITCH);
+        decompiler.addToken(Token.LP);
+        Node switchExpr = transform(node.getExpression());
+        decompiler.addToken(Token.RP);
+        node.addChildToBack(switchExpr);
 
-        Node.Jump switchNode = new Node.Jump(Token.SWITCH, expr, lineno);
-        Node block = new Node(Token.BLOCK, switchNode);
+        Node block = new Node(Token.BLOCK, node, node.getLineno());
+        decompiler.addEOL(Token.LC);
+
+        for (SwitchCase sc : node.getCases()) {
+            AstNode expr = sc.getExpression();
+            Node caseExpr = null;
+
+            if (expr != null) {
+                decompiler.addToken(Token.CASE);
+                caseExpr = transform(expr);
+            } else {
+                decompiler.addToken(Token.DEFAULT);
+            }
+            decompiler.addEOL(Token.COLON);
+
+            List&lt;AstNode&gt; stmts = sc.getStatements();
+            Node body = new Block();
+            if (stmts != null) {
+                for (AstNode kid : stmts) {
+                    body.addChildToBack(transform(kid));
+                }
+            }
+            addSwitchCase(block, caseExpr, body);
+        }
+        decompiler.addEOL(Token.RC);
+        closeSwitch(block);
         return block;
     }
 
+    private Node transformThrow(ThrowStatement node) {
+        decompiler.addToken(Token.THROW);
+        Node value = transform(node.getExpression());
+        decompiler.addEOL(Token.SEMI);
+        return new Node(Token.THROW, value, node.getLineno());
+    }
+
+    private Node transformTry(TryStatement node) {
+        decompiler.addToken(Token.TRY);
+        decompiler.addEOL(Token.LC);
+        Node tryBlock = transform(node.getTryBlock());
+        decompiler.addEOL(Token.RC);
+
+        Node catchBlocks = new Block();
+        for (CatchClause cc : node.getCatchClauses()) {
+            decompiler.addToken(Token.CATCH);
+            decompiler.addToken(Token.LP);
+
+            String varName = cc.getVarName().getIdentifier();
+            decompiler.addName(varName);
+
+            Node catchCond = null;
+            AstNode ccc = cc.getCatchCondition();
+            if (ccc != null) {
+                decompiler.addName(&quot; &quot;);
+                decompiler.addToken(Token.IF);
+                catchCond = transform(ccc);
+            } else {
+                catchCond = new EmptyExpression();
+            }
+            decompiler.addToken(Token.RP);
+            decompiler.addEOL(Token.LC);
+
+            Node body = transform(cc.getBody());
+            decompiler.addEOL(Token.RC);
+
+            catchBlocks.addChildToBack(createCatch(varName, catchCond,
+                                                   body, cc.getLineno()));
+        }
+        Node finallyBlock = null;
+        if (node.getFinallyBlock() != null) {
+            decompiler.addToken(Token.FINALLY);
+            decompiler.addEOL(Token.LC);
+            finallyBlock = transform(node.getFinallyBlock());
+            decompiler.addEOL(Token.RC);
+        }
+        return createTryCatchFinally(tryBlock, catchBlocks,
+                                     finallyBlock, node.getLineno());
+    }
+
+    private Node transformUnary(UnaryExpression node) {
+        int type = node.getType();
+        if (type == Token.DEFAULTNAMESPACE) {
+            return transformDefaultXmlNamepace(node);
+        }
+        if (node.isPrefix()) {
+            decompiler.addToken(type);
+        }
+        Node child = transform(node.getOperand());
+        if (node.isPostfix()) {
+            decompiler.addToken(type);
+        }
+        if (type == Token.INC || type == Token.DEC) {
+            return createIncDec(type, node.isPostfix(), child);
+        }
+        return createUnary(type, child);
+    }
+
+    private Node transformVariables(VariableDeclaration node) {
+        decompiler.addToken(node.getType());
+        transformVariableInitializers(node);
+
+        // Might be most robust to have parser record whether it was
+        // a variable declaration statement, possibly as a node property.
+        AstNode parent = node.getParent();
+        if (!(parent instanceof Loop)
+            &amp;&amp; !(parent instanceof LetNode)) {
+            decompiler.addEOL(Token.SEMI);
+        }
+        return node;
+    }
+
+    private Node transformVariableInitializers(VariableDeclaration node) {
+        List&lt;VariableInitializer&gt; vars = node.getVariables();
+        int size = vars.size(), i = 0;
+        for (VariableInitializer var : vars) {
+            AstNode target = var.getTarget();
+            AstNode init = var.getInitializer();
+
+            Node left = null;
+            if (var.isDestructuring()) {
+                decompile(target);  // decompile but don't transform
+                left = target;
+            } else {
+                left = transform(target);
+            }
+
+            Node right = null;
+            if (init != null) {
+                decompiler.addToken(Token.ASSIGN);
+                right = transform(init);
+            }
+
+            if (var.isDestructuring()) {
+                if (right == null) {  // TODO:  should this ever happen?
+                    node.addChildToBack(left);
+                } else {
+                    Node d = createDestructuringAssignment(node.getType(),
+                                                           left, right);
+                    node.addChildToBack(d);
+                }
+            } else {
+                if (right != null) {
+                    left.addChildToBack(right);
+                }
+                node.addChildToBack(left);
+            }
+            if (i++ &lt; size-1) {
+                decompiler.addToken(Token.COMMA);
+            }
+        }
+        return node;
+    }
+
+    private Node transformWhileLoop(WhileLoop loop) {
+        decompiler.addToken(Token.WHILE);
+        loop.setType(Token.LOOP);
+        pushScope(loop);
+        try {
+            decompiler.addToken(Token.LP);
+            Node cond = transform(loop.getCondition());
+            decompiler.addToken(Token.RP);
+            decompiler.addEOL(Token.LC);
+            Node body = transform(loop.getBody());
+            decompiler.addEOL(Token.RC);
+            return createLoop(loop, LOOP_WHILE, body, cond, null, null);
+        } finally {
+            popScope();
+        }
+    }
+
+    private Node transformWith(WithStatement node) {
+        decompiler.addToken(Token.WITH);
+        decompiler.addToken(Token.LP);
+        Node expr = transform(node.getExpression());
+        decompiler.addToken(Token.RP);
+        decompiler.addEOL(Token.LC);
+        Node stmt = transform(node.getStatement());
+        decompiler.addEOL(Token.RC);
+        return createWith(expr, stmt, node.getLineno());
+    }
+
+    private Node transformYield(Yield node) {
+        decompiler.addToken(Token.YIELD);
+        Node kid = node.getValue() == null ? null : transform(node.getValue());
+        if (kid != null)
+            return new Node(Token.YIELD, kid, node.getLineno());
+        else
+            return new Node(Token.YIELD, node.getLineno());
+    }
+
+    private Node transformXmlLiteral(XmlLiteral node) {
+        // a literal like &lt;foo&gt;{bar}&lt;/foo&gt; is rewritten as
+        //   new XML(&quot;&lt;foo&gt;&quot; + bar + &quot;&lt;/foo&gt;&quot;);
+
+        Node pnXML = new Node(Token.NEW, node.getLineno());
+        List&lt;XmlFragment&gt; frags = node.getFragments();
+
+        XmlString first = (XmlString)frags.get(0);
+        boolean anon = first.getXml().trim().startsWith(&quot;&lt;&gt;&quot;);
+        pnXML.addChildToBack(createName(anon ? &quot;XMLList&quot; : &quot;XML&quot;));
+
+        Node pn = null;
+        for (XmlFragment frag : frags) {
+            if (frag instanceof XmlString) {
+                String xml = ((XmlString)frag).getXml();
+                decompiler.addName(xml);
+                if (pn == null) {
+                    pn = createString(xml);
+                } else {
+                    pn = createBinary(Token.ADD, pn, createString(xml));
+                }
+            } else {
+                XmlExpression xexpr = (XmlExpression)frag;
+                boolean isXmlAttr = xexpr.isXmlAttribute();
+                Node expr;
+                decompiler.addToken(Token.LC);
+                if (xexpr.getExpression() instanceof EmptyExpression) {
+                    expr = createString(&quot;&quot;);
+                } else {
+                    expr = transform(xexpr.getExpression());
+                }
+                decompiler.addToken(Token.RC);
+                if (isXmlAttr) {
+                    // Need to put the result in double quotes
+                    expr = createUnary(Token.ESCXMLATTR, expr);
+                    Node prepend = createBinary(Token.ADD,
+                                                createString(&quot;\&quot;&quot;),
+                                                expr);
+                    expr = createBinary(Token.ADD,
+                                        prepend,
+                                        createString(&quot;\&quot;&quot;));
+                } else {
+                    expr = createUnary(Token.ESCXMLTEXT, expr);
+                }
+                pn = createBinary(Token.ADD, pn, expr);
+            }
+        }
+
+        pnXML.addChildToBack(pn);
+        return pnXML;
+    }
+
+    private Node transformXmlMemberGet(XmlMemberGet node) {
+        XmlRef ref = node.getMemberRef();
+        Node pn = transform(node.getLeft());
+        int flags = ref.isAttributeAccess() ? Node.ATTRIBUTE_FLAG : 0;
+        if (node.getType() == Token.DOTDOT) {
+            flags |= Node.DESCENDANTS_FLAG;
+            decompiler.addToken(Token.DOTDOT);
+        } else {
+            decompiler.addToken(Token.DOT);
+        }
+        return transformXmlRef(pn, ref, flags);
+    }
+
+    // We get here if we weren't a child of a . or .. infix node
+    private Node transformXmlRef(XmlRef node) {
+        int memberTypeFlags = node.isAttributeAccess()
+            ? Node.ATTRIBUTE_FLAG : 0;
+        return transformXmlRef(null, node, memberTypeFlags);
+    }
+
+    private Node transformXmlRef(Node pn, XmlRef node, int memberTypeFlags) {
+        if ((memberTypeFlags &amp; Node.ATTRIBUTE_FLAG) != 0)
+            decompiler.addToken(Token.XMLATTR);
+        Name namespace = node.getNamespace();
+        String ns = namespace != null ? namespace.getIdentifier() : null;
+        if (ns != null) {
+            decompiler.addName(ns);
+            decompiler.addToken(Token.COLONCOLON);
+        }
+        if (node instanceof XmlPropRef) {
+            String name = ((XmlPropRef)node).getPropName().getIdentifier();
+            decompiler.addName(name);
+            return createPropertyGet(pn, ns, name, memberTypeFlags);
+        } else {
+            decompiler.addToken(Token.LB);
+            Node expr = transform(((XmlElemRef)node).getExpression());
+            decompiler.addToken(Token.RB);
+            return createElementGet(pn, ns, expr, memberTypeFlags);
+        }
+    }
+
+    private Node transformDefaultXmlNamepace(UnaryExpression node) {
+        decompiler.addToken(Token.DEFAULT);
+        decompiler.addName(&quot; xml&quot;);
+        decompiler.addName(&quot; namespace&quot;);
+        decompiler.addToken(Token.ASSIGN);
+        Node child = transform(node.getOperand());
+        return createUnary(Token.DEFAULTNAMESPACE, child);
+    }
+
+    /**
+     * If stmt is the child of a labeled statement, return the first label.
+     * @return a Label node, or {@code null}
+     */
+    private Node getLabel(AstNode stmt) {
+        Node parent = stmt.getParent();
+        if (parent instanceof LabeledStatement) {
+            LabeledStatement ls = (LabeledStatement) parent;
+            return ls.getFirstLabel();
+        }
+        return null;
+    }
+
     /**
      * If caseExpression argument is null it indicate default label.
      */
-    void addSwitchCase(Node switchBlock, Node caseExpression, Node statements)
+    private void addSwitchCase(Node switchBlock, Node caseExpression,
+                               Node statements)
     {
         if (switchBlock.getType() != Token.BLOCK) throw Kit.codeBug();
-        Node.Jump switchNode = (Node.Jump)switchBlock.getFirstChild();
+        Jump switchNode = (Jump)switchBlock.getFirstChild();
         if (switchNode.getType() != Token.SWITCH) throw Kit.codeBug();
 
         Node gotoTarget = Node.newTarget();
         if (caseExpression != null) {
-            Node.Jump caseNode = new Node.Jump(Token.CASE, caseExpression);
+            Jump caseNode = new Jump(Token.CASE, caseExpression);
             caseNode.target = gotoTarget;
             switchNode.addChildToBack(caseNode);
         } else {
@@ -155,10 +1214,10 @@ final class IRFactory
         switchBlock.addChildToBack(statements);
     }
 
-    void closeSwitch(Node switchBlock)
+    private void closeSwitch(Node switchBlock)
     {
         if (switchBlock.getType() != Token.BLOCK) throw Kit.codeBug();
-        Node.Jump switchNode = (Node.Jump)switchBlock.getFirstChild();
+        Jump switchNode = (Jump)switchBlock.getFirstChild();
         if (switchNode.getType() != Token.SWITCH) throw Kit.codeBug();
 
         Node switchBreakTarget = Node.newTarget();
@@ -176,71 +1235,15 @@ final class IRFactory
         switchBlock.addChildToBack(switchBreakTarget);
     }
 
-    Node createVariables(int token, int lineno)
-    {
-        return new Node(token, lineno);
-    }
-
-    Node createExprStatement(Node expr, int lineno)
-    {
-        int type;
-        if (parser.insideFunction()) {
-            type = Token.EXPR_VOID;
-        } else {
-            type = Token.EXPR_RESULT;
-        }
-        return new Node(type, expr, lineno);
-    }
-
-    Node createExprStatementNoReturn(Node expr, int lineno)
-    {
+    private Node createExprStatementNoReturn(Node expr, int lineno) {
         return new Node(Token.EXPR_VOID, expr, lineno);
     }
 
-    Node createDefaultNamespace(Node expr, int lineno)
-    {
-        // default xml namespace requires activation
-        setRequiresActivation();
-        Node n = createUnary(Token.DEFAULTNAMESPACE, expr);
-        Node result = createExprStatement(n, lineno);
-        return result;
-    }
-
-    /**
-     * Name
-     */
-    Node createName(String name)
-    {
-        checkActivationName(name, Token.NAME);
-        return Node.newString(Token.NAME, name);
-    }
-    
-    private Node createName(int type, String name, Node child)
-    {
-        Node result = createName(name);
-        result.setType(type);
-        if (child != null)
-            result.addChildToBack(child);
-        return result;
-    }
-
-    /**
-     * String (for literals)
-     */
-    Node createString(String string)
-    {
+    private Node createString(String string) {
         return Node.newString(string);
     }
 
     /**
-     * Number (for literals)
-     */
-    Node createNumber(double number)
-    {
-        return Node.newNumber(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.
@@ -248,8 +1251,8 @@ final class IRFactory
      * @param stmts the statements in the catch clause
      * @param lineno the starting line number of the catch clause
      */
-    Node createCatch(String varName, Node catchCond, Node stmts, int lineno)
-    {
+    private Node createCatch(String varName, Node catchCond, Node stmts,
+                             int lineno) {
         if (catchCond == null) {
             catchCond = new Node(Token.EMPTY);
         }
@@ -257,124 +1260,19 @@ final class IRFactory
                         catchCond, stmts, lineno);
     }
 
-    /**
-     * Throw
-     */
-    Node createThrow(Node expr, int lineno)
-    {
-        return new Node(Token.THROW, expr, lineno);
-    }
-
-    /**
-     * Return
-     */
-    Node createReturn(Node expr, int lineno)
-    {
-        return expr == null
-            ? new Node(Token.RETURN, lineno)
-            : new Node(Token.RETURN, expr, lineno);
-    }
-
-    /**
-     * Debugger
-     */
-    Node createDebugger(int lineno)
-    {
-        return new Node(Token.DEBUGGER,  lineno);
-    }
-  
-    /**
-     * Label
-     */
-    Node createLabel(int lineno)
-    {
-        return new Node.Jump(Token.LABEL, lineno);
-    }
-
-    Node getLabelLoop(Node label)
-    {
-        return ((Node.Jump)label).getLoop();
-    }
-
-    /**
-     * Label
-     */
-    Node createLabeledStatement(Node labelArg, Node statement)
-    {
-        Node.Jump label = (Node.Jump)labelArg;
-
-        // Make a target and put it _after_ the statement
-        // node.  And in the LABEL node, so breaks get the
-        // right target.
-
-        Node breakTarget = Node.newTarget();
-        Node block = new Node(Token.BLOCK, label, statement, breakTarget);
-        label.target = breakTarget;
-
-        return block;
-    }
-
-    /**
-     * Break (possibly labeled)
-     */
-    Node createBreak(Node breakStatement, int lineno)
-    {
-        Node.Jump n = new Node.Jump(Token.BREAK, lineno);
-        Node.Jump jumpStatement;
-        int t = breakStatement.getType();
-        if (t == Token.LOOP || t == Token.LABEL) {
-            jumpStatement = (Node.Jump)breakStatement;
-        } else if (t == Token.BLOCK
-                   &amp;&amp; breakStatement.getFirstChild().getType() == Token.SWITCH)
-        {
-            jumpStatement = (Node.Jump)breakStatement.getFirstChild();
-        } else {
-            throw Kit.codeBug();
-        }
-        n.setJumpStatement(jumpStatement);
-        return n;
-    }
-
-    /**
-     * Continue (possibly labeled)
-     */
-    Node createContinue(Node loop, int lineno)
-    {
-        if (loop.getType() != Token.LOOP) Kit.codeBug();
-        Node.Jump n = new Node.Jump(Token.CONTINUE, lineno);
-        n.setJumpStatement((Node.Jump)loop);
-        return n;
-    }
-
-    /**
-     * Statement block
-     * Creates the empty statement block
-     * Must make subsequent calls to add statements to the node
-     */
-    Node createBlock(int lineno)
-    {
-        return new Node(Token.BLOCK, lineno);
-    }
-
-    FunctionNode createFunction(String name)
-    {
-        return new FunctionNode(name);
-    }
-
-    Node initFunction(FunctionNode fnNode, int functionIndex,
-                      Node statements, int functionType)
-    {
-        fnNode.itsFunctionType = functionType;
+    private Node initFunction(FunctionNode fnNode, int functionIndex,
+                              Node statements, int functionType) {
+        fnNode.setFunctionType(functionType);
         fnNode.addChildToBack(statements);
 
         int functionCount = fnNode.getFunctionCount();
         if (functionCount != 0) {
             // Functions containing other functions require activation objects
-            fnNode.itsNeedsActivation = true;
+            fnNode.setRequiresActivation();
         }
 
         if (functionType == FunctionNode.FUNCTION_EXPRESSION) {
-            String name = fnNode.getFunctionName();
+            Name name = fnNode.getFunctionName();
             if (name != null &amp;&amp; name.length() != 0) {
                 // A function expression needs to have its name as a
                 // variable (if it isn't already allocated as a variable).
@@ -383,7 +1281,8 @@ final class IRFactory
                 // function's name to the function value.
                 Node setFn = new Node(Token.EXPR_VOID,
                                  new Node(Token.SETNAME,
-                                     Node.newString(Token.BINDNAME, name),
+                                          Node.newString(Token.BINDNAME,
+                                                         name.getIdentifier()),
                                      new Node(Token.THISFN)));
                 statements.addChildrenToFront(setFn);
             }
@@ -395,94 +1294,49 @@ final class IRFactory
             statements.addChildToBack(new Node(Token.RETURN));
         }
 
-        Node result = Node.newString(Token.FUNCTION,
-                                     fnNode.getFunctionName());
+        Node result = Node.newString(Token.FUNCTION, fnNode.getName());
         result.putIntProp(Node.FUNCTION_PROP, functionIndex);
         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.
-     */
-    void addChildToBack(Node parent, Node child)
-    {
-        parent.addChildToBack(child);
-    }
-
-    /**
-     * Create a node that can be used to hold lexically scoped variable
-     * definitions (via let declarations).
-     * 
-     * @param token the token of the node to create
-     * @param lineno line number of source
-     * @return the created node
-     */
-    Node createScopeNode(int token, int lineno) {
-        return new Node.Scope(token, lineno);
-    }
-
-    /**
-     * Create loop node. The parser will later call
+     * Create loop node. The code generator will later call
      * createWhile|createDoWhile|createFor|createForIn
      * to finish loop generation.
      */
-    Node createLoopNode(Node loopLabel, int lineno)
-    {
-        Node.Jump result = new Node.Scope(Token.LOOP, lineno);
+    private Scope createLoopNode(Node loopLabel, int lineno) {
+        Scope result = createScopeNode(Token.LOOP, lineno);
         if (loopLabel != null) {
-            ((Node.Jump)loopLabel).setLoop(result);
+            ((Jump)loopLabel).setLoop(result);
         }
         return result;
     }
 
-    /**
-     * While
-     */
-    Node createWhile(Node loop, Node cond, Node body)
-    {
-        return createLoop((Node.Jump)loop, LOOP_WHILE, body, cond,
-                          null, null);
-    }
-
-    /**
-     * DoWhile
-     */
-    Node createDoWhile(Node loop, Node body, Node cond)
-    {
-        return createLoop((Node.Jump)loop, LOOP_DO_WHILE, body, cond,
-                          null, null);
-    }
-
-    /**
-     * For
-     */
-    Node createFor(Node loop, Node init, Node test, Node incr, Node body)
-    {
+    private Node createFor(Scope loop, Node init,
+                           Node test, Node incr, Node body) {
         if (init.getType() == Token.LET) {
-            // rewrite &quot;for (let i=s; i &lt; N; i++)...&quot; as 
+            // rewrite &quot;for (let i=s; i &lt; N; i++)...&quot; as
             // &quot;let (i=s) { for (; i &lt; N; i++)...&quot; so that &quot;s&quot; is evaluated
             // outside the scope of the for.
-            Node.Scope let = Node.Scope.splitScope((Node.Scope)loop);
+            Scope let = Scope.splitScope(loop);
             let.setType(Token.LET);
             let.addChildrenToBack(init);
-            let.addChildToBack(createLoop((Node.Jump)loop, LOOP_FOR, body, test,
+            let.addChildToBack(createLoop(loop, LOOP_FOR, body, test,
                 new Node(Token.EMPTY), incr));
             return let;
         }
-        return createLoop((Node.Jump)loop, LOOP_FOR, body, test, init, incr);
+        return createLoop(loop, LOOP_FOR, body, test, init, incr);
     }
 
-    private Node createLoop(Node.Jump loop, int loopType, Node body, Node cond,
-                            Node init, Node incr)
+    private Node createLoop(Jump loop, int loopType, Node body,
+                            Node cond, Node init, Node incr)
     {
         Node bodyTarget = Node.newTarget();
         Node condTarget = Node.newTarget();
         if (loopType == LOOP_FOR &amp;&amp; cond.getType() == Token.EMPTY) {
             cond = new Node(Token.TRUE);
         }
-        Node.Jump IFEQ = new Node.Jump(Token.IFEQ, cond);
+        Jump IFEQ = new Jump(Token.IFEQ, cond);
         IFEQ.target = bodyTarget;
         Node breakTarget = Node.newTarget();
 
@@ -522,61 +1376,54 @@ final class IRFactory
         }
 
         loop.setContinue(continueTarget);
-
         return loop;
     }
 
     /**
-     * For .. In
-     *
+     * Generate IR for a for..in loop.
      */
-    Node createForIn(int declType, Node loop, Node lhs, Node obj, Node body,
-                     boolean isForEach)
+    private Node createForIn(int declType, Node loop, Node lhs,
+                             Node obj, Node body, boolean isForEach)
     {
         int destructuring = -1;
         int destructuringLen = 0;
         Node lvalue;
         int type = lhs.getType();
         if (type == Token.VAR || type == Token.LET) {
-            Node lastChild = lhs.getLastChild();
-            if (lhs.getFirstChild() != lastChild) {
-                /*
-                 * 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.
-                 */
-                parser.reportError(&quot;msg.mult.index&quot;);
-            }
-            if (lastChild.getType() == Token.ARRAYLIT ||
-                lastChild.getType() == Token.OBJECTLIT)
+            Node kid = lhs.getLastChild();
+            int kidType = kid.getType();
+            if (kidType == Token.ARRAYLIT || kidType == Token.OBJECTLIT)
             {
-                type = destructuring = lastChild.getType();
-                lvalue = lastChild;
-                destructuringLen = lastChild.getIntProp(
-                    Node.DESTRUCTURING_ARRAY_LENGTH, 0);
-            } else if (lastChild.getType() == Token.NAME) {
-                lvalue = Node.newString(Token.NAME, lastChild.getString());
+                type = destructuring = kidType;
+                lvalue = kid;
+                destructuringLen = 0;
+                if (kid instanceof ArrayLiteral)
+                    destructuringLen = ((ArrayLiteral) kid).getDestructuringLength();
+            } else if (kidType == Token.NAME) {
+                lvalue = Node.newString(Token.NAME, kid.getString());
             } else {
-                parser.reportError(&quot;msg.bad.for.in.lhs&quot;);
-                return obj;
+                reportError(&quot;msg.bad.for.in.lhs&quot;);
+                return null;
             }
         } else if (type == Token.ARRAYLIT || type == Token.OBJECTLIT) {
             destructuring = type;
             lvalue = lhs;
-            destructuringLen = lhs.getIntProp(Node.DESTRUCTURING_ARRAY_LENGTH, 0);
+            destructuringLen = 0;
+            if (lhs instanceof ArrayLiteral)
+                destructuringLen = ((ArrayLiteral) lhs).getDestructuringLength();
         } else {
             lvalue = makeReference(lhs);
             if (lvalue == null) {
-                parser.reportError(&quot;msg.bad.for.in.lhs&quot;);
-                return obj;
+                reportError(&quot;msg.bad.for.in.lhs&quot;);
+                return null;
             }
         }
 
         Node localBlock = new Node(Token.LOCAL_BLOCK);
-        int initType = (isForEach)           ? Token.ENUM_INIT_VALUES :
-                       (destructuring != -1) ? Token.ENUM_INIT_ARRAY :
-                                               Token.ENUM_INIT_KEYS;
+        int initType = isForEach ? Token.ENUM_INIT_VALUES
+                                 : (destructuring != -1
+                                    ? Token.ENUM_INIT_ARRAY
+                                    : Token.ENUM_INIT_KEYS);
         Node init = new Node(initType, obj);
         init.putProp(Node.LOCAL_BLOCK_PROP, localBlock);
         Node cond = new Node(Token.ENUM_NEXT);
@@ -593,7 +1440,7 @@ final class IRFactory
             {
                 // destructuring assignment is only allowed in for..each or
                 // with an array type of length 2 (to hold key and value)
-                parser.reportError(&quot;msg.bad.for.in.destruct&quot;);
+                reportError(&quot;msg.bad.for.in.destruct&quot;);
             }
         } else {
             assign = simpleAssignment(lvalue, id);
@@ -601,7 +1448,7 @@ final class IRFactory
         newBody.addChildToBack(new Node(Token.EXPR_VOID, assign));
         newBody.addChildToBack(body);
 
-        loop = createWhile(loop, cond, newBody);
+        loop = createLoop((Jump)loop, LOOP_WHILE, newBody, cond, null, null);
         loop.addChildToFront(init);
         if (type == Token.VAR || type == Token.LET)
             loop.addChildToFront(lhs);
@@ -617,16 +1464,16 @@ final class IRFactory
      * 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
-
+     *
      * - a finally handler
-
+     *
      * ... and a goto to GOTO around these handlers.
      */
-    Node createTryCatchFinally(Node tryBlock, Node catchBlocks,
-                               Node finallyBlock, int lineno)
+    private Node createTryCatchFinally(Node tryBlock, Node catchBlocks,
+                                       Node finallyBlock, int lineno)
     {
         boolean hasFinally = (finallyBlock != null)
                              &amp;&amp; (finallyBlock.getType() != Token.BLOCK
@@ -647,9 +1494,8 @@ final class IRFactory
             return tryBlock;
         }
 
-
         Node handlerBlock  = new Node(Token.LOCAL_BLOCK);
-        Node.Jump pn = new Node.Jump(Token.TRY, tryBlock, lineno);
+        Jump pn = new Jump(Token.TRY, tryBlock, lineno);
         pn.putProp(Node.LOCAL_BLOCK_PROP, handlerBlock);
 
         if (hasCatch) {
@@ -794,15 +1640,7 @@ final class IRFactory
         return handlerBlock;
     }
 
-    /**
-     * Throw, Return, Label, Break and Continue are defined in ASTFactory.
-     */
-
-    /**
-     * With
-     */
-    Node createWith(Node obj, Node body, int lineno)
-    {
+    private Node createWith(Node obj, Node body, int lineno) {
         setRequiresActivation();
         Node result = new Node(Token.BLOCK, lineno);
         result.addChildToBack(new Node(Token.ENTERWITH, obj));
@@ -812,79 +1650,7 @@ final class IRFactory
         return result;
     }
 
-    /**
-     * DOTQUERY
-     */
-    public Node createDotQuery (Node obj, Node body, int lineno)
-    {
-        setRequiresActivation();
-        Node result = new Node(Token.DOTQUERY, obj, body, lineno);
-        return result;
-    }
-
-    Node createArrayLiteral(ObjArray elems, int skipCount, int destructuringLen)
-    {
-        int length = elems.size();
-        int[] skipIndexes = null;
-        if (skipCount != 0) {
-            skipIndexes = new int[skipCount];
-        }
-        Node array = new Node(Token.ARRAYLIT);
-        for (int i = 0, j = 0; i != length; ++i) {
-            Node elem = (Node)elems.get(i);
-            if (elem != null) {
-                array.addChildToBack(elem);
-            } else {
-                skipIndexes[j] = i;
-                ++j;
-            }
-        }
-        if (skipCount != 0) {
-            array.putProp(Node.SKIP_INDEXES_PROP, skipIndexes);
-        }
-        array.putIntProp(Node.DESTRUCTURING_ARRAY_LENGTH, destructuringLen);
-        return array;
-    }
-
-    /**
-     * Object Literals
-     * &lt;BR&gt; createObjectLiteral rewrites its argument as object
-     * creation plus object property entries, so later compiler
-     * stages don't need to know about object literals.
-     */
-    Node createObjectLiteral(ObjArray elems)
-    {
-        int size = elems.size() / 2;
-        Node object = new Node(Token.OBJECTLIT);
-        Object[] properties;
-        if (size == 0) {
-            properties = ScriptRuntime.emptyArgs;
-        } else {
-            properties = new Object[size];
-            for (int i = 0; i != size; ++i) {
-                properties[i] = elems.get(2 * i);
-                Node value = (Node)elems.get(2 * i + 1);
-                object.addChildToBack(value);
-            }
-        }
-        object.putProp(Node.OBJECT_IDS_PROP, properties);
-        return object;
-    }
-
-    /**
-     * Regular expressions
-     */
-    Node createRegExp(int regexpIndex)
-    {
-        Node n = new Node(Token.REGEXP);
-        n.putIntProp(Node.REGEXP_PROP, regexpIndex);
-        return n;
-    }
-
-    /**
-     * If statement
-     */
-    Node createIf(Node cond, Node ifTrue, Node ifFalse, int lineno)
+    private Node createIf(Node cond, Node ifTrue, Node ifFalse, int lineno)
     {
         int condStatus = isAlwaysDefinedBoolean(cond);
         if (condStatus == ALWAYS_TRUE_BOOLEAN) {
@@ -899,7 +1665,7 @@ final class IRFactory
 
         Node result = new Node(Token.BLOCK, lineno);
         Node ifNotTarget = Node.newTarget();
-        Node.Jump IFNE = new Node.Jump(Token.IFNE, cond);
+        Jump IFNE = new Jump(Token.IFNE, cond);
         IFNE.target = ifNotTarget;
 
         result.addChildToBack(IFNE);
@@ -918,8 +1684,7 @@ final class IRFactory
         return result;
     }
 
-    Node createCondExpr(Node cond, Node ifTrue, Node ifFalse)
-    {
+    private Node createCondExpr(Node cond, Node ifTrue, Node ifFalse) {
         int condStatus = isAlwaysDefinedBoolean(cond);
         if (condStatus == ALWAYS_TRUE_BOOLEAN) {
             return ifTrue;
@@ -929,10 +1694,7 @@ final class IRFactory
         return new Node(Token.HOOK, cond, ifTrue, ifFalse);
     }
 
-    /**
-     * Unary
-     */
-    Node createUnary(int nodeType, Node child)
+    private Node createUnary(int nodeType, Node child)
     {
         int childType = child.getType();
         switch (nodeType) {
@@ -1002,21 +1764,7 @@ final class IRFactory
         return new Node(nodeType, child);
     }
 
-    Node createYield(Node child, int lineno)
-    {
-      if (!parser.insideFunction()) {
-        parser.reportError(&quot;msg.bad.yield&quot;);
-      }
-      setRequiresActivation();
-      setIsGenerator();
-      if (child != null)
-        return new Node(Token.YIELD, child, lineno);
-      else
-        return new Node(Token.YIELD, lineno);
-    }
-
-    Node createCallOrNew(int nodeType, Node child)
-    {
+    private Node createCallOrNew(int nodeType, Node child) {
         int type = Node.NON_SPECIALCALL;
         if (child.getType() == Token.NAME) {
             String name = child.getString();
@@ -1040,20 +1788,9 @@ final class IRFactory
         return node;
     }
 
-    Node createIncDec(int nodeType, boolean post, Node child)
+    private Node createIncDec(int nodeType, boolean post, Node child)
     {
         child = makeReference(child);
-        if (child == null) {
-            String msg;
-            if (nodeType == Token.DEC) {
-                msg = &quot;msg.bad.decr&quot;;
-            } else {
-                msg = &quot;msg.bad.incr&quot;;
-            }
-            parser.reportError(msg);
-            return null;
-        }
-
         int childType = child.getType();
 
         switch (childType) {
@@ -1076,8 +1813,8 @@ final class IRFactory
         throw Kit.codeBug();
     }
 
-    Node createPropertyGet(Node target, String namespace, String name,
-                           int memberTypeFlags)
+    private Node createPropertyGet(Node target, String namespace, String name,
+                                   int memberTypeFlags)
     {
         if (namespace == null &amp;&amp; memberTypeFlags == 0) {
             if (target == null) {
@@ -1089,15 +1826,21 @@ final class IRFactory
                 ref.putProp(Node.NAME_PROP, name);
                 return new Node(Token.GET_REF, ref);
             }
-            return new Node(Token.GETPROP, target, createString(name));
+            return new Node(Token.GETPROP, target, Node.newString(name));
         }
-        Node elem = createString(name);
+        Node elem = Node.newString(name);
         memberTypeFlags |= Node.PROPERTY_FLAG;
         return createMemberRefGet(target, namespace, elem, memberTypeFlags);
     }
 
-    Node createElementGet(Node target, String namespace, Node elem,
-                          int memberTypeFlags)
+    /**
+     * @param target the node before the LB
+     * @param namespace optional namespace
+     * @param elem the node in the brackets
+     * @param memberTypeFlags E4X flags
+     */
+    private Node createElementGet(Node target, String namespace, Node elem,
+                                  int memberTypeFlags)
     {
         // OPT: could optimize to createPropertyGet
         // iff elem is string that can not be number
@@ -1142,11 +1885,7 @@ final class IRFactory
         return new Node(Token.GET_REF, ref);
     }
 
-    /**
-     * Binary
-     */
-    Node createBinary(int nodeType, Node left, Node right)
-    {
+    private Node createBinary(int nodeType, Node left, Node right) {
         switch (nodeType) {
 
           case Token.ADD:
@@ -1275,58 +2014,20 @@ final class IRFactory
         return new Node(nodeType, left, right);
     }
 
-    private Node simpleAssignment(Node left, Node right)
-    {
-        int nodeType = left.getType();
-        switch (nodeType) {
-          case Token.NAME:
-            left.setType(Token.BINDNAME);
-            return new Node(Token.SETNAME, left, right);
-
-          case Token.GETPROP:
-          case Token.GETELEM: {
-            Node obj = left.getFirstChild();
-            Node id = left.getLastChild();
-            int type;
-            if (nodeType == Token.GETPROP) {
-                type = Token.SETPROP;
-            } else {
-                type = Token.SETELEM;
-            }
-            return new Node(type, obj, id, right);
-          }
-          case Token.GET_REF: {
-            Node ref = left.getFirstChild();
-            checkMutableReference(ref);
-            return new Node(Token.SET_REF, ref, right);
-          }
-        }
-
-        throw Kit.codeBug();
-    }
-
-    private void checkMutableReference(Node n)
-    {
-        int memberTypeFlags = n.getIntProp(Node.MEMBER_TYPE_PROP, 0);
-        if ((memberTypeFlags &amp; Node.DESCENDANTS_FLAG) != 0) {
-            parser.reportError(&quot;msg.bad.assign.left&quot;);
-        }
-    }
-
-    Node createAssignment(int assignType, Node left, Node right)
+    private Node createAssignment(int assignType, Node left, Node right)
     {
         Node ref = makeReference(left);
         if (ref == null) {
-            if (left.getType() == Token.ARRAYLIT || 
+            if (left.getType() == Token.ARRAYLIT ||
                 left.getType() == Token.OBJECTLIT)
             {
                 if (assignType != Token.ASSIGN) {
-                    parser.reportError(&quot;msg.bad.destruct.op&quot;);
+                    reportError(&quot;msg.bad.destruct.op&quot;);
                     return right;
                 }
                 return createDestructuringAssignment(-1, left, right);
             }
-            parser.reportError(&quot;msg.bad.assign.left&quot;);
+            reportError(&quot;msg.bad.assign.left&quot;);
             return right;
         }
         left = ref;
@@ -1380,148 +2081,21 @@ final class IRFactory
 
         throw Kit.codeBug();
     }
-    
-    /**
-     * Given a destructuring assignment with a left hand side parsed
-     * as an array or object literal and a right hand side expression,
-     * rewrite as a series of assignments to the variables defined in
-     * left from property accesses to the expression on the right.
-     * @param type declaration type: Token.VAR or Token.LET or -1
-     * @param left array or object literal containing NAME nodes for
-     *        variables to assign
-     * @param right expression to assign from
-     * @return expression that performs a series of assignments to
-     *         the variables defined in left
-     */
-    Node createDestructuringAssignment(int type, Node left, Node right)
-    {
-        String tempName = parser.currentScriptOrFn.getNextTempName();
-        Node result = destructuringAssignmentHelper(type, left, right,
-            tempName);
-        Node comma = result.getLastChild();
-        comma.addChildToBack(createName(tempName));
-        return result;
-    }
 
-    private Node destructuringAssignmentHelper(int variableType, Node left, 
-                                               Node right, String tempName)
-    {
-        Node result = createScopeNode(Token.LETEXPR,
-            parser.getCurrentLineNumber());
-        result.addChildToFront(new Node(Token.LET,
-            createName(Token.NAME, tempName, right)));
-        try {
-            parser.pushScope(result);
-            parser.defineSymbol(Token.LET, true, tempName);
-        } finally {
-            parser.popScope();
-        }
-        Node comma = new Node(Token.COMMA);
-        result.addChildToBack(comma);
-        final int setOp = variableType == Token.CONST ? Token.SETCONST
-        		                                      : Token.SETNAME;
-        List&lt;String&gt; destructuringNames = new ArrayList&lt;String&gt;();
-        boolean empty = true;
-        int type = left.getType();
-        if (type == Token.ARRAYLIT) {
-            int index = 0;
-            int[] skipIndices = (int[])left.getProp(Node.SKIP_INDEXES_PROP);
-            int skip = 0;
-            Node n = left.getFirstChild();
-            for (;;) {
-                if (skipIndices != null) {
-                    while (skip &lt; skipIndices.length &amp;&amp; 
-                           skipIndices[skip] == index) {
-                        skip++;
-                        index++;
-                    }
-                }
-                if (n == null)
-                    break;
-                Node rightElem = new Node(Token.GETELEM,
-                    createName(tempName), 
-                    createNumber(index));
-                if (n.getType() == Token.NAME) {
-                    String name = n.getString();
-                    comma.addChildToBack(new Node(setOp, 
-                        createName(Token.BINDNAME, name, null),
-                        rightElem));
-                    if (variableType != -1) {
-                        parser.defineSymbol(variableType, true, name);
-                        destructuringNames.add(name);
-                    }
-                } else {
-                    comma.addChildToBack(
-                        destructuringAssignmentHelper(variableType, n,
-                            rightElem,
-                            parser.currentScriptOrFn.getNextTempName()));
-                }
-                index++;
-                empty = false;
-                n = n.getNext();
-            }
-        } else if (type == Token.OBJECTLIT) {
-            int index = 0;
-            Object[] propertyIds = (Object[])
-                left.getProp(Node.OBJECT_IDS_PROP);
-            for (Node n = left.getFirstChild(); n != null; n = n.getNext())
-            {
-                Object id = propertyIds[index];
-                Node rightElem = id instanceof String 
-                    ? new Node(Token.GETPROP,
-                        createName(tempName), 
-                        createString((String)id))
-                    : new Node(Token.GETELEM,
-                        createName(tempName), 
-                        createNumber(((Number)id).intValue()));
-                if (n.getType() == Token.NAME) {
-                    String name = n.getString();
-                    comma.addChildToBack(new Node(setOp, 
-                        createName(Token.BINDNAME, name, null),
-                        rightElem));
-                    if (variableType != -1) {
-                        parser.defineSymbol(variableType, true, name);
-                        destructuringNames.add(name);
-                    }
-                } else {
-                    comma.addChildToBack(
-                        destructuringAssignmentHelper(variableType, n,
-                            rightElem,
-                            parser.currentScriptOrFn.getNextTempName()));
-                }
-                index++;
-                empty = false;
-            }
-        } else if (type == Token.GETPROP || type == Token.GETELEM) {
-            comma.addChildToBack(simpleAssignment(left, createName(tempName)));
-        } else {
-            parser.reportError(&quot;msg.bad.assign.left&quot;);
-        }
-        if (empty) {
-            // Don't want a COMMA node with no children. Just add a zero.
-            comma.addChildToBack(createNumber(0));
-        }
-        result.putProp(Node.DESTRUCTURING_NAMES, destructuringNames);
-        return result;
-    }
-
-    Node createUseLocal(Node localBlock)
-    {
+    private Node createUseLocal(Node localBlock) {
         if (Token.LOCAL_BLOCK != localBlock.getType()) throw Kit.codeBug();
         Node result = new Node(Token.LOCAL_LOAD);
         result.putProp(Node.LOCAL_BLOCK_PROP, localBlock);
         return result;
     }
 
-    private Node.Jump makeJump(int type, Node target)
-    {
-        Node.Jump n = new Node.Jump(type);
+    private Jump makeJump(int type, Node target) {
+        Jump n = new Jump(type);
         n.target = target;
         return n;
     }
 
-    private Node makeReference(Node node)
-    {
+    private Node makeReference(Node node) {
         int type = node.getType();
         switch (type) {
           case Token.NAME:
@@ -1538,8 +2112,7 @@ final class IRFactory
     }
 
     // Check if Node always mean true or false in boolean context
-    private static int isAlwaysDefinedBoolean(Node node)
-    {
+    private static int isAlwaysDefinedBoolean(Node node) {
         switch (node.getType()) {
           case Token.FALSE:
           case Token.NULL:
@@ -1557,51 +2130,108 @@ final class IRFactory
         }
         return 0;
     }
-    
-    private void checkActivationName(String name, int token)
-    {
-        if (parser.insideFunction()) {
-            boolean activation = false;
-            if (&quot;arguments&quot;.equals(name)
-                || (parser.compilerEnv.activationNames != null
-                    &amp;&amp; parser.compilerEnv.activationNames.contains(name)))
-            {
-                activation = true;
-            } else if (&quot;length&quot;.equals(name)) {
-                if (token == Token.GETPROP
-                    &amp;&amp; parser.compilerEnv.getLanguageVersion()
-                       == Context.VERSION_1_2)
-                {
-                    // Use of &quot;length&quot; in 1.2 requires an activation object.
-                    activation = true;
-                }
-            }
-            if (activation) {
-                setRequiresActivation();
+
+    // Check if node is the target of a destructuring bind.
+    boolean isDestructuring(Node n) {
+        return n instanceof DestructuringForm
+            &amp;&amp; ((DestructuringForm)n).isDestructuring();
+    }
+
+    Node decompileFunctionHeader(FunctionNode fn) {
+        Node mexpr = null;
+        if (fn.getFunctionName() != null) {
+            decompiler.addName(fn.getName());
+        } else if (fn.getMemberExprNode() != null) {
+            mexpr = transform(fn.getMemberExprNode());
+        }
+        decompiler.addToken(Token.LP);
+        List&lt;AstNode&gt; params = fn.getParams();
+        for (int i = 0; i &lt; params.size(); i++) {
+            decompile(params.get(i));
+            if (i &lt; params.size() - 1) {
+                decompiler.addToken(Token.COMMA);
             }
         }
+        decompiler.addToken(Token.RP);
+        decompiler.addEOL(Token.LC);
+        return mexpr;
     }
 
-    private void setRequiresActivation()
-    {
-        if (parser.insideFunction()) {
-            ((FunctionNode)parser.currentScriptOrFn).itsNeedsActivation = true;
+    void decompile(AstNode node) {
+        switch (node.getType()) {
+          case Token.ARRAYLIT:
+              decompileArrayLiteral((ArrayLiteral)node);
+              break;
+          case Token.OBJECTLIT:
+              decompileObjectLiteral((ObjectLiteral)node);
+              break;
+          case Token.STRING:
+              decompiler.addString(((StringLiteral)node).getValue());
+              break;
+          case Token.NAME:
+              decompiler.addName(((Name)node).getIdentifier());
+              break;
+          case Token.NUMBER:
+              decompiler.addNumber(((NumberLiteral)node).getNumber());
+              break;
+          case Token.GETPROP:
+              decompilePropertyGet((PropertyGet)node);
+              break;
+          case Token.EMPTY:
+              break;
+          case Token.GETELEM:
+              decompileElementGet((ElementGet) node);
+              break;
+          default:
+              Kit.codeBug(&quot;unexpected token: &quot;
+                          + Token.typeToName(node.getType()));
         }
     }
 
-    private void setIsGenerator()
-    {
-        if (parser.insideFunction()) {
-            ((FunctionNode)parser.currentScriptOrFn).itsIsGenerator = true;
+    // used for destructuring forms, since we don't transform() them
+    void decompileArrayLiteral(ArrayLiteral node) {
+        decompiler.addToken(Token.LB);
+        List&lt;AstNode&gt; elems = node.getElements();
+        int size = elems.size();
+        for (int i = 0; i &lt; size; i++) {
+            AstNode elem = elems.get(i);
+            decompile(elem);
+            if (i &lt; size - 1) {
+                decompiler.addToken(Token.COMMA);
+            }
         }
+        decompiler.addToken(Token.RB);
     }
 
-    private Parser parser;
-
-    private static final int LOOP_DO_WHILE = 0;
-    private static final int LOOP_WHILE    = 1;
-    private static final int LOOP_FOR      = 2;
+    // only used for destructuring forms
+    void decompileObjectLiteral(ObjectLiteral node) {
+        decompiler.addToken(Token.LC);
+        List&lt;ObjectProperty&gt; props = node.getElements();
+        int size = props.size();
+        for (int i = 0; i &lt; size; i++) {
+            ObjectProperty prop = props.get(i);
+            decompile(prop.getLeft());
+            decompiler.addToken(Token.COLON);
+            decompile(prop.getRight());
+            if (i &lt; size - 1) {
+                decompiler.addToken(Token.COMMA);
+            }
+        }
+        decompiler.addToken(Token.RC);
+    }
 
-    private static final int ALWAYS_TRUE_BOOLEAN = 1;
-    private static final int ALWAYS_FALSE_BOOLEAN = -1;
+    // only used for destructuring forms
+    void decompilePropertyGet(PropertyGet node) {
+        decompile(node.getTarget());
+        decompiler.addToken(Token.DOT);
+        decompile(node.getProperty());
+    }
+    
+    // only used for destructuring forms
+    void decompileElementGet(ElementGet node) {
+        decompile(node.getTarget());
+        decompiler.addToken(Token.LB);
+        decompile(node.getElement());
+        decompiler.addToken(Token.RB);
+    }
 }</diff>
      <filename>src/org/mozilla/javascript/IRFactory.java</filename>
    </modified>
    <modified>
      <diff>@@ -51,176 +51,26 @@ import java.io.Serializable;
 import java.util.List;
 import java.util.ArrayList;
 
+import org.mozilla.javascript.ast.FunctionNode;
+import org.mozilla.javascript.ast.ScriptNode;
 import org.mozilla.javascript.ScriptRuntime.NoSuchMethodShim;
 import org.mozilla.javascript.debug.DebugFrame;
 
-public class Interpreter implements Evaluator
+public final class Interpreter extends Icode implements Evaluator
 {
-
-// Additional interpreter-specific codes
-
-    private static final int
-
-    // Stack: ... value1 -&gt; ... value1 value1
-        Icode_DUP                       = -1,
-
-    // Stack: ... value2 value1 -&gt; ... value2 value1 value2 value1
-        Icode_DUP2                      = -2,
-
-    // Stack: ... value2 value1 -&gt; ... value1 value2
-        Icode_SWAP                      = -3,
-
-    // Stack: ... value1 -&gt; ...
-        Icode_POP                       = -4,
-
-    // Store stack top into return register and then pop it
-        Icode_POP_RESULT                = -5,
-
-    // To jump conditionally and pop additional stack value
-        Icode_IFEQ_POP                  = -6,
-
-    // various types of ++/--
-        Icode_VAR_INC_DEC               = -7,
-        Icode_NAME_INC_DEC              = -8,
-        Icode_PROP_INC_DEC              = -9,
-        Icode_ELEM_INC_DEC              = -10,
-        Icode_REF_INC_DEC               = -11,
-
-    // load/save scope from/to local
-        Icode_SCOPE_LOAD                = -12,
-        Icode_SCOPE_SAVE                = -13,
-
-        Icode_TYPEOFNAME                = -14,
-
-    // helper for function calls
-        Icode_NAME_AND_THIS             = -15,
-        Icode_PROP_AND_THIS             = -16,
-        Icode_ELEM_AND_THIS             = -17,
-        Icode_VALUE_AND_THIS            = -18,
-
-    // Create closure object for nested functions
-        Icode_CLOSURE_EXPR              = -19,
-        Icode_CLOSURE_STMT              = -20,
-
-    // Special calls
-        Icode_CALLSPECIAL               = -21,
-
-    // To return undefined value
-        Icode_RETUNDEF                  = -22,
-
-    // Exception handling implementation
-        Icode_GOSUB                     = -23,
-        Icode_STARTSUB                  = -24,
-        Icode_RETSUB                    = -25,
-
-    // To indicating a line number change in icodes.
-        Icode_LINE                      = -26,
-
-    // To store shorts and ints inline
-        Icode_SHORTNUMBER               = -27,
-        Icode_INTNUMBER                 = -28,
-
-    // To create and populate array to hold values for [] and {} literals
-        Icode_LITERAL_NEW               = -29,
-        Icode_LITERAL_SET               = -30,
-
-    // Array literal with skipped index like [1,,2]
-        Icode_SPARE_ARRAYLIT            = -31,
-
-    // Load index register to prepare for the following index operation
-        Icode_REG_IND_C0                = -32,
-        Icode_REG_IND_C1                = -33,
-        Icode_REG_IND_C2                = -34,
-        Icode_REG_IND_C3                = -35,
-        Icode_REG_IND_C4                = -36,
-        Icode_REG_IND_C5                = -37,
-        Icode_REG_IND1                  = -38,
-        Icode_REG_IND2                  = -39,
-        Icode_REG_IND4                  = -40,
-
-    // Load string register to prepare for the following string operation
-        Icode_REG_STR_C0                = -41,
-        Icode_REG_STR_C1                = -42,
-        Icode_REG_STR_C2                = -43,
-        Icode_REG_STR_C3                = -44,
-        Icode_REG_STR1                  = -45,
-        Icode_REG_STR2                  = -46,
-        Icode_REG_STR4                  = -47,
-
-    // Version of getvar/setvar that read var index directly from bytecode
-        Icode_GETVAR1                   = -48,
-        Icode_SETVAR1                   = -49,
-
-    // Load unefined
-        Icode_UNDEF                     = -50,
-        Icode_ZERO                      = -51,
-        Icode_ONE                       = -52,
-
-    // entrance and exit from .()
-       Icode_ENTERDQ                    = -53,
-       Icode_LEAVEDQ                    = -54,
-
-       Icode_TAIL_CALL                  = -55,
-
-    // Clear local to allow GC its context
-       Icode_LOCAL_CLEAR                = -56,
-
-    // Literal get/set
-       Icode_LITERAL_GETTER             = -57,
-       Icode_LITERAL_SETTER             = -58,
-
-    // const
-       Icode_SETCONST                   = -59,
-       Icode_SETCONSTVAR                = -60,
-       Icode_SETCONSTVAR1               = -61,
-
-    // Generator opcodes (along with Token.YIELD)
-       Icode_GENERATOR                  = -62,
-       Icode_GENERATOR_END              = -63,
-
-       Icode_DEBUGGER                   = -64,
-
-       // Last icode
-        MIN_ICODE                       = -64;
-
     // data for parsing
-
-    private CompilerEnvirons compilerEnv;
-
-    private boolean itsInFunctionFlag;
-    private boolean itsInTryFlag;
-
-    private InterpreterData itsData;
-    private ScriptOrFnNode scriptOrFn;
-    private int itsICodeTop;
-    private int itsStackDepth;
-    private int itsLineNumber;
-    private int itsDoubleTableTop;
-    private ObjToIntMap itsStrings = new ObjToIntMap(20);
-    private int itsLocalTop;
-
-    private static final int MIN_LABEL_TABLE_SIZE = 32;
-    private static final int MIN_FIXUP_TABLE_SIZE = 40;
-    private int[] itsLabelTable;
-    private int itsLabelTableTop;
-// itsFixupTable[i] = (label_index &lt;&lt; 32) | fixup_site
-    private long[] itsFixupTable;
-    private int itsFixupTableTop;
-    private ObjArray itsLiteralIds = new ObjArray();
-
-    private int itsExceptionTableTop;
-    private static final int EXCEPTION_TRY_START_SLOT  = 0;
-    private static final int EXCEPTION_TRY_END_SLOT    = 1;
-    private static final int EXCEPTION_HANDLER_SLOT    = 2;
-    private static final int EXCEPTION_TYPE_SLOT       = 3;
-    private static final int EXCEPTION_LOCAL_SLOT      = 4;
-    private static final int EXCEPTION_SCOPE_SLOT      = 5;
+    CompilerEnvirons compilerEnv;
+    InterpreterData itsData;
+
+    static final int EXCEPTION_TRY_START_SLOT  = 0;
+    static final int EXCEPTION_TRY_END_SLOT    = 1;
+    static final int EXCEPTION_HANDLER_SLOT    = 2;
+    static final int EXCEPTION_TYPE_SLOT       = 3;
+    static final int EXCEPTION_LOCAL_SLOT      = 4;
+    static final int EXCEPTION_SCOPE_SLOT      = 5;
     // SLOT_SIZE: space for try start/end, handler, start, handler type,
     //            exception local and scope local
-    private static final int EXCEPTION_SLOT_SIZE       = 6;
-
-// ECF_ or Expression Context Flags constants: for now only TAIL is available
-    private static final int ECF_TAIL = 1 &lt;&lt; 0;
+    static final int EXCEPTION_SLOT_SIZE       = 6;
 
     /**
      * Class to hold data corresponding to one interpreted call stack frame.
@@ -375,135 +225,14 @@ public class Interpreter implements Evaluator
         }
     }
 
-    private static String bytecodeName(int bytecode)
-    {
-        if (!validBytecode(bytecode)) {
-            throw new IllegalArgumentException(String.valueOf(bytecode));
-        }
-
-        if (!Token.printICode) {
-            return String.valueOf(bytecode);
-        }
-
-        if (validTokenCode(bytecode)) {
-            return Token.name(bytecode);
-        }
-
-        switch (bytecode) {
-          case Icode_DUP:              return &quot;DUP&quot;;
-          case Icode_DUP2:             return &quot;DUP2&quot;;
-          case Icode_SWAP:             return &quot;SWAP&quot;;
-          case Icode_POP:              return &quot;POP&quot;;
-          case Icode_POP_RESULT:       return &quot;POP_RESULT&quot;;
-          case Icode_IFEQ_POP:         return &quot;IFEQ_POP&quot;;
-          case Icode_VAR_INC_DEC:      return &quot;VAR_INC_DEC&quot;;
-          case Icode_NAME_INC_DEC:     return &quot;NAME_INC_DEC&quot;;
-          case Icode_PROP_INC_DEC:     return &quot;PROP_INC_DEC&quot;;
-          case Icode_ELEM_INC_DEC:     return &quot;ELEM_INC_DEC&quot;;
-          case Icode_REF_INC_DEC:      return &quot;REF_INC_DEC&quot;;
-          case Icode_SCOPE_LOAD:       return &quot;SCOPE_LOAD&quot;;
-          case Icode_SCOPE_SAVE:       return &quot;SCOPE_SAVE&quot;;
-          case Icode_TYPEOFNAME:       return &quot;TYPEOFNAME&quot;;
-          case Icode_NAME_AND_THIS:    return &quot;NAME_AND_THIS&quot;;
-          case Icode_PROP_AND_THIS:    return &quot;PROP_AND_THIS&quot;;
-          case Icode_ELEM_AND_THIS:    return &quot;ELEM_AND_THIS&quot;;
-          case Icode_VALUE_AND_THIS:   return &quot;VALUE_AND_THIS&quot;;
-          case Icode_CLOSURE_EXPR:     return &quot;CLOSURE_EXPR&quot;;
-          case Icode_CLOSURE_STMT:     return &quot;CLOSURE_STMT&quot;;
-          case Icode_CALLSPECIAL:      return &quot;CALLSPECIAL&quot;;
-          case Icode_RETUNDEF:         return &quot;RETUNDEF&quot;;
-          case Icode_GOSUB:            return &quot;GOSUB&quot;;
-          case Icode_STARTSUB:         return &quot;STARTSUB&quot;;
-          case Icode_RETSUB:           return &quot;RETSUB&quot;;
-          case Icode_LINE:             return &quot;LINE&quot;;
-          case Icode_SHORTNUMBER:      return &quot;SHORTNUMBER&quot;;
-          case Icode_INTNUMBER:        return &quot;INTNUMBER&quot;;
-          case Icode_LITERAL_NEW:      return &quot;LITERAL_NEW&quot;;
-          case Icode_LITERAL_SET:      return &quot;LITERAL_SET&quot;;
-          case Icode_SPARE_ARRAYLIT:   return &quot;SPARE_ARRAYLIT&quot;;
-          case Icode_REG_IND_C0:       return &quot;REG_IND_C0&quot;;
-          case Icode_REG_IND_C1:       return &quot;REG_IND_C1&quot;;
-          case Icode_REG_IND_C2:       return &quot;REG_IND_C2&quot;;
-          case Icode_REG_IND_C3:       return &quot;REG_IND_C3&quot;;
-          case Icode_REG_IND_C4:       return &quot;REG_IND_C4&quot;;
-          case Icode_REG_IND_C5:       return &quot;REG_IND_C5&quot;;
-          case Icode_REG_IND1:         return &quot;LOAD_IND1&quot;;
-          case Icode_REG_IND2:         return &quot;LOAD_IND2&quot;;
-          case Icode_REG_IND4:         return &quot;LOAD_IND4&quot;;
-          case Icode_REG_STR_C0:       return &quot;REG_STR_C0&quot;;
-          case Icode_REG_STR_C1:       return &quot;REG_STR_C1&quot;;
-          case Icode_REG_STR_C2:       return &quot;REG_STR_C2&quot;;
-          case Icode_REG_STR_C3:       return &quot;REG_STR_C3&quot;;
-          case Icode_REG_STR1:         return &quot;LOAD_STR1&quot;;
-          case Icode_REG_STR2:         return &quot;LOAD_STR2&quot;;
-          case Icode_REG_STR4:         return &quot;LOAD_STR4&quot;;
-          case Icode_GETVAR1:          return &quot;GETVAR1&quot;;
-          case Icode_SETVAR1:          return &quot;SETVAR1&quot;;
-          case Icode_UNDEF:            return &quot;UNDEF&quot;;
-          case Icode_ZERO:             return &quot;ZERO&quot;;
-          case Icode_ONE:              return &quot;ONE&quot;;
-          case Icode_ENTERDQ:          return &quot;ENTERDQ&quot;;
-          case Icode_LEAVEDQ:          return &quot;LEAVEDQ&quot;;
-          case Icode_TAIL_CALL:        return &quot;TAIL_CALL&quot;;
-          case Icode_LOCAL_CLEAR:      return &quot;LOCAL_CLEAR&quot;;
-          case Icode_LITERAL_GETTER:   return &quot;LITERAL_GETTER&quot;;
-          case Icode_LITERAL_SETTER:   return &quot;LITERAL_SETTER&quot;;
-          case Icode_SETCONST:         return &quot;SETCONST&quot;;
-          case Icode_SETCONSTVAR:      return &quot;SETCONSTVAR&quot;;
-          case Icode_SETCONSTVAR1:     return &quot;SETCONSTVAR1&quot;;
-          case Icode_GENERATOR:        return &quot;GENERATOR&quot;;
-          case Icode_GENERATOR_END:    return &quot;GENERATOR_END&quot;;
-          case Icode_DEBUGGER:         return &quot;DEBUGGER&quot;;
-        }
-
-        // icode without name
-        throw new IllegalStateException(String.valueOf(bytecode));
-    }
-
-    private static boolean validIcode(int icode)
-    {
-        return MIN_ICODE &lt;= icode &amp;&amp; icode &lt;= -1;
-    }
-
-    private static boolean validTokenCode(int token)
-    {
-        return Token.FIRST_BYTECODE_TOKEN &lt;= token
-               &amp;&amp; token &lt;= Token.LAST_BYTECODE_TOKEN;
-    }
-
-    private static boolean validBytecode(int bytecode)
-    {
-        return validIcode(bytecode) || validTokenCode(bytecode);
-    }
-
     public Object compile(CompilerEnvirons compilerEnv,
-                          ScriptOrFnNode tree,
+                          ScriptNode tree,
                           String encodedSource,
                           boolean returnFunction)
     {
         this.compilerEnv = compilerEnv;
-        new NodeTransformer().transform(tree);
-
-        if (Token.printTrees) {
-            System.out.println(tree.toStringTree(tree));
-        }
-
-        if (returnFunction) {
-            tree = tree.getFunctionNode(0);
-        }
-
-        scriptOrFn = tree;
-        itsData = new InterpreterData(compilerEnv.getLanguageVersion(),
-                                      scriptOrFn.getSourceName(),
-                                      encodedSource);
-        itsData.topLevel = true;
-
-        if (returnFunction) {
-            generateFunctionICode();
-        } else {
-            generateICodeFromTree(scriptOrFn);
-        }
-
+        CodeGenerator cgen = new CodeGenerator();
+        itsData = cgen.compile(compilerEnv, tree, encodedSource, returnFunction);
         return itsData;
     }
 
@@ -520,7 +249,7 @@ public class Interpreter implements Evaluator
     public void setEvalScriptFlag(Script script) {
         ((InterpretedFunction)script).idata.evalScriptFlag = true;
     }
-    
+
 
     public Function createFunctionObject(Context cx, Scriptable scope,
             Object bytecode, Object staticSecurityDomain)
@@ -533,1377 +262,6 @@ public class Interpreter implements Evaluator
                                                   staticSecurityDomain);
     }
 
-    private void generateFunctionICode()
-    {
-        itsInFunctionFlag = true;
-
-        FunctionNode theFunction = (FunctionNode)scriptOrFn;
-
-        itsData.itsFunctionType = theFunction.getFunctionType();
-        itsData.itsNeedsActivation = theFunction.requiresActivation();
-        itsData.itsName = theFunction.getFunctionName();
-        if (!theFunction.getIgnoreDynamicScope()) {
-            if (compilerEnv.isUseDynamicScope()) {
-                itsData.useDynamicScope = true;
-            }
-        }
-        if (theFunction.isGenerator()) {
-          addIcode(Icode_GENERATOR);
-          addUint16(theFunction.getBaseLineno() &amp; 0xFFFF);
-        }
-
-        generateICodeFromTree(theFunction.getLastChild());
-    }
-
-    private void generateICodeFromTree(Node tree)
-    {
-        generateNestedFunctions();
-
-        generateRegExpLiterals();
-
-        visitStatement(tree, 0);
-        fixLabelGotos();
-        // add RETURN_RESULT only to scripts as function always ends with RETURN
-        if (itsData.itsFunctionType == 0) {
-            addToken(Token.RETURN_RESULT);
-        }
-
-        if (itsData.itsICode.length != itsICodeTop) {
-            // Make itsData.itsICode length exactly itsICodeTop to save memory
-            // and catch bugs with jumps beyond icode as early as possible
-            byte[] tmp = new byte[itsICodeTop];
-            System.arraycopy(itsData.itsICode, 0, tmp, 0, itsICodeTop);
-            itsData.itsICode = tmp;
-        }
-        if (itsStrings.size() == 0) {
-            itsData.itsStringTable = null;
-        } else {
-            itsData.itsStringTable = new String[itsStrings.size()];
-            ObjToIntMap.Iterator iter = itsStrings.newIterator();
-            for (iter.start(); !iter.done(); iter.next()) {
-                String str = (String)iter.getKey();
-                int index = iter.getValue();
-                if (itsData.itsStringTable[index] != null) Kit.codeBug();
-                itsData.itsStringTable[index] = str;
-            }
-        }
-        if (itsDoubleTableTop == 0) {
-            itsData.itsDoubleTable = null;
-        } else if (itsData.itsDoubleTable.length != itsDoubleTableTop) {
-            double[] tmp = new double[itsDoubleTableTop];
-            System.arraycopy(itsData.itsDoubleTable, 0, tmp, 0,
-                             itsDoubleTableTop);
-            itsData.itsDoubleTable = tmp;
-        }
-        if (itsExceptionTableTop != 0
-            &amp;&amp; itsData.itsExceptionTable.length != itsExceptionTableTop)
-        {
-            int[] tmp = new int[itsExceptionTableTop];
-            System.arraycopy(itsData.itsExceptionTable, 0, tmp, 0,
-                             itsExceptionTableTop);
-            itsData.itsExceptionTable = tmp;
-        }
-
-        itsData.itsMaxVars = scriptOrFn.getParamAndVarCount();
-        // itsMaxFrameArray: interpret method needs this amount for its
-        // stack and sDbl arrays
-        itsData.itsMaxFrameArray = itsData.itsMaxVars
-                                   + itsData.itsMaxLocals
-                                   + itsData.itsMaxStack;
-
-        itsData.argNames = scriptOrFn.getParamAndVarNames();
-        itsData.argIsConst = scriptOrFn.getParamAndVarConst();
-        itsData.argCount = scriptOrFn.getParamCount();
-
-        itsData.encodedSourceStart = scriptOrFn.getEncodedSourceStart();
-        itsData.encodedSourceEnd = scriptOrFn.getEncodedSourceEnd();
-
-        if (itsLiteralIds.size() != 0) {
-            itsData.literalIds = itsLiteralIds.toArray();
-        }
-
-        if (Token.printICode) dumpICode(itsData);
-    }
-
-    private void generateNestedFunctions()
-    {
-        int functionCount = scriptOrFn.getFunctionCount();
-        if (functionCount == 0) return;
-
-        InterpreterData[] array = new InterpreterData[functionCount];
-        for (int i = 0; i != functionCount; i++) {
-            FunctionNode def = scriptOrFn.getFunctionNode(i);
-            Interpreter jsi = new Interpreter();
-            jsi.compilerEnv = compilerEnv;
-            jsi.scriptOrFn = def;
-            jsi.itsData = new InterpreterData(itsData);
-            jsi.generateFunctionICode();
-            array[i] = jsi.itsData;
-        }
-        itsData.itsNestedFunctions = array;
-    }
-
-    private void generateRegExpLiterals()
-    {
-        int N = scriptOrFn.getRegexpCount();
-        if (N == 0) return;
-
-        Context cx = Context.getContext();
-        RegExpProxy rep = ScriptRuntime.checkRegExpProxy(cx);
-        Object[] array = new Object[N];
-        for (int i = 0; i != N; i++) {
-            String string = scriptOrFn.getRegexpString(i);
-            String flags = scriptOrFn.getRegexpFlags(i);
-            array[i] = rep.compileRegExp(cx, string, flags);
-        }
-        itsData.itsRegExpLiterals = array;
-    }
-
-    private void updateLineNumber(Node node)
-    {
-        int lineno = node.getLineno();
-        if (lineno != itsLineNumber &amp;&amp; lineno &gt;= 0) {
-            if (itsData.firstLinePC &lt; 0) {
-                itsData.firstLinePC = lineno;
-            }
-            itsLineNumber = lineno;
-            addIcode(Icode_LINE);
-            addUint16(lineno &amp; 0xFFFF);
-        }
-    }
-
-    private RuntimeException badTree(Node node)
-    {
-        throw new RuntimeException(node.toString());
-    }
-
-    private void visitStatement(Node node, int initialStackDepth)
-    {
-        int type = node.getType();
-        Node child = node.getFirstChild();
-        switch (type) {
-
-          case Token.FUNCTION:
-            {
-                int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
-                int fnType = scriptOrFn.getFunctionNode(fnIndex).
-                                 getFunctionType();
-                // Only function expressions or function expression
-                // statements need closure code creating new function
-                // object on stack as function statements are initialized
-                // at script/function start.
-                // In addition, function expressions can not be present here
-                // at statement level, they must only be present as expressions.
-                if (fnType == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) {
-                    addIndexOp(Icode_CLOSURE_STMT, fnIndex);
-                } else {
-                    if (fnType != FunctionNode.FUNCTION_STATEMENT) {
-                        throw Kit.codeBug();
-                    }
-                }
-                // For function statements or function expression statements
-                // in scripts, we need to ensure that the result of the script
-                // is the function if it is the last statement in the script.
-                // For example, eval(&quot;function () {}&quot;) should return a
-                // function, not undefined.
-                if (!itsInFunctionFlag) {
-                    addIndexOp(Icode_CLOSURE_EXPR, fnIndex);
-                    stackChange(1);
-                    addIcode(Icode_POP_RESULT);
-                    stackChange(-1);
-                }
-            }
-            break;
-
-          case Token.LABEL:
-          case Token.LOOP:
-          case Token.BLOCK:
-          case Token.EMPTY:
-          case Token.WITH:
-            updateLineNumber(node);
-          case Token.SCRIPT:
-            // fall through
-            while (child != null) {
-                visitStatement(child, initialStackDepth);
-                child = child.getNext();
-            }
-            break;
-
-          case Token.ENTERWITH:
-            visitExpression(child, 0);
-            addToken(Token.ENTERWITH);
-            stackChange(-1);
-            break;
-
-          case Token.LEAVEWITH:
-            addToken(Token.LEAVEWITH);
-            break;
-
-          case Token.LOCAL_BLOCK:
-            {
-                int local = allocLocal();
-                node.putIntProp(Node.LOCAL_PROP, local);
-                updateLineNumber(node);
-                while (child != null) {
-                    visitStatement(child, initialStackDepth);
-                    child = child.getNext();
-                }
-                addIndexOp(Icode_LOCAL_CLEAR, local);
-                releaseLocal(local);
-            }
-            break;
-
-          case Token.DEBUGGER:
-            addIcode(Icode_DEBUGGER);
-            break;
-
-          case Token.SWITCH:
-            updateLineNumber(node);
-            // See comments in IRFactory.createSwitch() for description
-            // of SWITCH node
-            {
-                visitExpression(child, 0);
-                for (Node.Jump caseNode = (Node.Jump)child.getNext();
-                     caseNode != null;
-                     caseNode = (Node.Jump)caseNode.getNext())
-                {
-                    if (caseNode.getType() != Token.CASE)
-                        throw badTree(caseNode);
-                    Node test = caseNode.getFirstChild();
-                    addIcode(Icode_DUP);
-                    stackChange(1);
-                    visitExpression(test, 0);
-                    addToken(Token.SHEQ);
-                    stackChange(-1);
-                    // If true, Icode_IFEQ_POP will jump and remove case
-                    // value from stack
-                    addGoto(caseNode.target, Icode_IFEQ_POP);
-                    stackChange(-1);
-                }
-                addIcode(Icode_POP);
-                stackChange(-1);
-            }
-            break;
-
-          case Token.TARGET:
-            markTargetLabel(node);
-            break;
-
-          case Token.IFEQ :
-          case Token.IFNE :
-            {
-                Node target = ((Node.Jump)node).target;
-                visitExpression(child, 0);
-                addGoto(target, type);
-                stackChange(-1);
-            }
-            break;
-
-          case Token.GOTO:
-            {
-                Node target = ((Node.Jump)node).target;
-                addGoto(target, type);
-            }
-            break;
-
-          case Token.JSR:
-            {
-                Node target = ((Node.Jump)node).target;
-                addGoto(target, Icode_GOSUB);
-            }
-            break;
-
-          case Token.FINALLY:
-            {
-                // Account for incomming GOTOSUB address
-                stackChange(1);
-                int finallyRegister = getLocalBlockRef(node);
-                addIndexOp(Icode_STARTSUB, finallyRegister);
-                stackChange(-1);
-                while (child != null) {
-                    visitStatement(child, initialStackDepth);
-                    child = child.getNext();
-                }
-                addIndexOp(Icode_RETSUB, finallyRegister);
-            }
-            break;
-
-          case Token.EXPR_VOID:
-          case Token.EXPR_RESULT:
-            updateLineNumber(node);
-            visitExpression(child, 0);
-            addIcode((type == Token.EXPR_VOID) ? Icode_POP : Icode_POP_RESULT);
-            stackChange(-1);
-            break;
-
-          case Token.TRY:
-            {
-                Node.Jump tryNode = (Node.Jump)node;
-                int exceptionObjectLocal = getLocalBlockRef(tryNode);
-                int scopeLocal = allocLocal();
-
-                addIndexOp(Icode_SCOPE_SAVE, scopeLocal);
-
-                int tryStart = itsICodeTop;
-                boolean savedFlag = itsInTryFlag;
-                itsInTryFlag = true;
-                while (child != null) {
-                    visitStatement(child, initialStackDepth);
-                    child = child.getNext();
-                }
-                itsInTryFlag = savedFlag;
-
-                Node catchTarget = tryNode.target;
-                if (catchTarget != null) {
-                    int catchStartPC
-                        = itsLabelTable[getTargetLabel(catchTarget)];
-                    addExceptionHandler(
-                        tryStart, catchStartPC, catchStartPC,
-                        false, exceptionObjectLocal, scopeLocal);
-                }
-                Node finallyTarget = tryNode.getFinally();
-                if (finallyTarget != null) {
-                    int finallyStartPC
-                        = itsLabelTable[getTargetLabel(finallyTarget)];
-                    addExceptionHandler(
-                        tryStart, finallyStartPC, finallyStartPC,
-                        true, exceptionObjectLocal, scopeLocal);
-                }
-
-                addIndexOp(Icode_LOCAL_CLEAR, scopeLocal);
-                releaseLocal(scopeLocal);
-            }
-            break;
-
-          case Token.CATCH_SCOPE:
-            {
-                int localIndex = getLocalBlockRef(node);
-                int scopeIndex = node.getExistingIntProp(Node.CATCH_SCOPE_PROP);
-                String name = child.getString();
-                child = child.getNext();
-                visitExpression(child, 0); // load expression object
-                addStringPrefix(name);
-                addIndexPrefix(localIndex);
-                addToken(Token.CATCH_SCOPE);
-                addUint8(scopeIndex != 0 ? 1 : 0);
-                stackChange(-1);
-            }
-            break;
-
-          case Token.THROW:
-            updateLineNumber(node);
-            visitExpression(child, 0);
-            addToken(Token.THROW);
-            addUint16(itsLineNumber &amp; 0xFFFF);
-            stackChange(-1);
-            break;
-
-          case Token.RETHROW:
-            updateLineNumber(node);
-            addIndexOp(Token.RETHROW, getLocalBlockRef(node));
-            break;
-
-          case Token.RETURN:
-            updateLineNumber(node);
-            if (node.getIntProp(Node.GENERATOR_END_PROP, 0) != 0) {
-                // We're in a generator, so change RETURN to GENERATOR_END
-                addIcode(Icode_GENERATOR_END);
-                addUint16(itsLineNumber &amp; 0xFFFF);
-            } else if (child != null) {
-                visitExpression(child, ECF_TAIL);
-                addToken(Token.RETURN);
-                stackChange(-1);
-            } else {
-                addIcode(Icode_RETUNDEF);
-            }
-            break;
-
-          case Token.RETURN_RESULT:
-            updateLineNumber(node);
-            addToken(Token.RETURN_RESULT);
-            break;
-
-          case Token.ENUM_INIT_KEYS:
-          case Token.ENUM_INIT_VALUES:
-          case Token.ENUM_INIT_ARRAY:
-            visitExpression(child, 0);
-            addIndexOp(type, getLocalBlockRef(node));
-            stackChange(-1);
-            break;
-
-          case Icode_GENERATOR:
-            break;
-
-          default:
-            throw badTree(node);
-        }
-
-        if (itsStackDepth != initialStackDepth) {
-            throw Kit.codeBug();
-        }
-    }
-
-    private void visitExpression(Node node, int contextFlags)
-    {
-        int type = node.getType();
-        Node child = node.getFirstChild();
-        int savedStackDepth = itsStackDepth;
-        switch (type) {
-
-          case Token.FUNCTION:
-            {
-                int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
-                FunctionNode fn = scriptOrFn.getFunctionNode(fnIndex);
-                // See comments in visitStatement for Token.FUNCTION case
-                if (fn.getFunctionType() != FunctionNode.FUNCTION_EXPRESSION) {
-                    throw Kit.codeBug();
-                }
-                addIndexOp(Icode_CLOSURE_EXPR, fnIndex);
-                stackChange(1);
-            }
-            break;
-
-          case Token.LOCAL_LOAD:
-            {
-                int localIndex = getLocalBlockRef(node);
-                addIndexOp(Token.LOCAL_LOAD, localIndex);
-                stackChange(1);
-            }
-            break;
-
-          case Token.COMMA:
-            {
-                Node lastChild = node.getLastChild();
-                while (child != lastChild) {
-                    visitExpression(child, 0);
-                    addIcode(Icode_POP);
-                    stackChange(-1);
-                    child = child.getNext();
-                }
-                // Preserve tail context flag if any
-                visitExpression(child, contextFlags &amp; ECF_TAIL);
-            }
-            break;
-
-          case Token.USE_STACK:
-            // Indicates that stack was modified externally,
-            // like placed catch object
-            stackChange(1);
-            break;
-
-          case Token.REF_CALL:
-          case Token.CALL:
-          case Token.NEW:
-            {
-                if (type == Token.NEW) {
-                    visitExpression(child, 0);
-                } else {
-                    generateCallFunAndThis(child);
-                }
-                int argCount = 0;
-                while ((child = child.getNext()) != null) {
-                    visitExpression(child, 0);
-                    ++argCount;
-                }
-                int callType = node.getIntProp(Node.SPECIALCALL_PROP,
-                                               Node.NON_SPECIALCALL);
-                if (callType != Node.NON_SPECIALCALL) {
-                    // embed line number and source filename
-                    addIndexOp(Icode_CALLSPECIAL, argCount);
-                    addUint8(callType);
-                    addUint8(type == Token.NEW ? 1 : 0);
-                    addUint16(itsLineNumber &amp; 0xFFFF);
-                } else {
-                    // Only use the tail call optimization if we're not in a try
-                    // or we're not generating debug info (since the
-                    // optimization will confuse the debugger)
-                    if (type == Token.CALL &amp;&amp; (contextFlags &amp; ECF_TAIL) != 0 &amp;&amp;
-                        !compilerEnv.isGenerateDebugInfo() &amp;&amp; !itsInTryFlag)
-                    {
-                        type = Icode_TAIL_CALL;
-                    }
-                    addIndexOp(type, argCount);
-                }
-                // adjust stack
-                if (type == Token.NEW) {
-                    // new: f, args -&gt; result
-                    stackChange(-argCount);
-                } else {
-                    // call: f, thisObj, args -&gt; result
-                    // ref_call: f, thisObj, args -&gt; ref
-                    stackChange(-1 - argCount);
-                }
-                if (argCount &gt; itsData.itsMaxCalleeArgs) {
-                    itsData.itsMaxCalleeArgs = argCount;
-                }
-            }
-            break;
-
-          case Token.AND:
-          case Token.OR:
-            {
-                visitExpression(child, 0);
-                addIcode(Icode_DUP);
-                stackChange(1);
-                int afterSecondJumpStart = itsICodeTop;
-                int jump = (type == Token.AND) ? Token.IFNE : Token.IFEQ;
-                addGotoOp(jump);
-                stackChange(-1);
-                addIcode(Icode_POP);
-                stackChange(-1);
-                child = child.getNext();
-                // Preserve tail context flag if any
-                visitExpression(child, contextFlags &amp; ECF_TAIL);
-                resolveForwardGoto(afterSecondJumpStart);
-            }
-            break;
-
-          case Token.HOOK:
-            {
-                Node ifThen = child.getNext();
-                Node ifElse = ifThen.getNext();
-                visitExpression(child, 0);
-                int elseJumpStart = itsICodeTop;
-                addGotoOp(Token.IFNE);
-                stackChange(-1);
-                // Preserve tail context flag if any
-                visitExpression(ifThen, contextFlags &amp; ECF_TAIL);
-                int afterElseJumpStart = itsICodeTop;
-                addGotoOp(Token.GOTO);
-                resolveForwardGoto(elseJumpStart);
-                itsStackDepth = savedStackDepth;
-                // Preserve tail context flag if any
-                visitExpression(ifElse, contextFlags &amp; ECF_TAIL);
-                resolveForwardGoto(afterElseJumpStart);
-            }
-            break;
-
-          case Token.GETPROP:
-          case Token.GETPROPNOWARN:
-            visitExpression(child, 0);
-            child = child.getNext();
-            addStringOp(type, child.getString());
-            break;
-
-          case Token.GETELEM:
-          case Token.DELPROP:
-          case Token.BITAND:
-          case Token.BITOR:
-          case Token.BITXOR:
-          case Token.LSH:
-          case Token.RSH:
-          case Token.URSH:
-          case Token.ADD:
-          case Token.SUB:
-          case Token.MOD:
-          case Token.DIV:
-          case Token.MUL:
-          case Token.EQ:
-          case Token.NE:
-          case Token.SHEQ:
-          case Token.SHNE:
-          case Token.IN:
-          case Token.INSTANCEOF:
-          case Token.LE:
-          case Token.LT:
-          case Token.GE:
-          case Token.GT:
-            visitExpression(child, 0);
-            child = child.getNext();
-            visitExpression(child, 0);
-            addToken(type);
-            stackChange(-1);
-            break;
-
-          case Token.POS:
-          case Token.NEG:
-          case Token.NOT:
-          case Token.BITNOT:
-          case Token.TYPEOF:
-          case Token.VOID:
-            visitExpression(child, 0);
-            if (type == Token.VOID) {
-                addIcode(Icode_POP);
-                addIcode(Icode_UNDEF);
-            } else {
-                addToken(type);
-            }
-            break;
-
-          case Token.GET_REF:
-          case Token.DEL_REF:
-            visitExpression(child, 0);
-            addToken(type);
-            break;
-
-          case Token.SETPROP:
-          case Token.SETPROP_OP:
-            {
-                visitExpression(child, 0);
-                child = child.getNext();
-                String property = child.getString();
-                child = child.getNext();
-                if (type == Token.SETPROP_OP) {
-                    addIcode(Icode_DUP);
-                    stackChange(1);
-                    addStringOp(Token.GETPROP, property);
-                    // Compensate for the following USE_STACK
-                    stackChange(-1);
-                }
-                visitExpression(child, 0);
-                addStringOp(Token.SETPROP, property);
-                stackChange(-1);
-            }
-            break;
-
-          case Token.SETELEM:
-          case Token.SETELEM_OP:
-            visitExpression(child, 0);
-            child = child.getNext();
-            visitExpression(child, 0);
-            child = child.getNext();
-            if (type == Token.SETELEM_OP) {
-                addIcode(Icode_DUP2);
-                stackChange(2);
-                addToken(Token.GETELEM);
-                stackChange(-1);
-                // Compensate for the following USE_STACK
-                stackChange(-1);
-            }
-            visitExpression(child, 0);
-            addToken(Token.SETELEM);
-            stackChange(-2);
-            break;
-
-          case Token.SET_REF:
-          case Token.SET_REF_OP:
-            visitExpression(child, 0);
-            child = child.getNext();
-            if (type == Token.SET_REF_OP) {
-                addIcode(Icode_DUP);
-                stackChange(1);
-                addToken(Token.GET_REF);
-                // Compensate for the following USE_STACK
-                stackChange(-1);
-            }
-            visitExpression(child, 0);
-            addToken(Token.SET_REF);
-            stackChange(-1);
-            break;
-
-          case Token.SETNAME:
-            {
-                String name = child.getString();
-                visitExpression(child, 0);
-                child = child.getNext();
-                visitExpression(child, 0);
-                addStringOp(Token.SETNAME, name);
-                stackChange(-1);
-            }
-            break;
-
-          case Token.SETCONST:
-            {
-                String name = child.getString();
-                visitExpression(child, 0);
-                child = child.getNext();
-                visitExpression(child, 0);
-                addStringOp(Icode_SETCONST, name);
-                stackChange(-1);
-            }
-            break;
-
-          case Token.TYPEOFNAME:
-            {
-                int index = -1;
-                // use typeofname if an activation frame exists
-                // since the vars all exist there instead of in jregs
-                if (itsInFunctionFlag &amp;&amp; !itsData.itsNeedsActivation)
-                    index = scriptOrFn.getIndexForNameNode(node);
-                if (index == -1) {
-                    addStringOp(Icode_TYPEOFNAME, node.getString());
-                    stackChange(1);
-                } else {
-                    addVarOp(Token.GETVAR, index);
-                    stackChange(1);
-                    addToken(Token.TYPEOF);
-                }
-            }
-            break;
-
-          case Token.BINDNAME:
-          case Token.NAME:
-          case Token.STRING:
-            addStringOp(type, node.getString());
-            stackChange(1);
-            break;
-
-          case Token.INC:
-          case Token.DEC:
-            visitIncDec(node, child);
-            break;
-
-          case Token.NUMBER:
-            {
-                double num = node.getDouble();
-                int inum = (int)num;
-                if (inum == num) {
-                    if (inum == 0) {
-                        addIcode(Icode_ZERO);
-                        // Check for negative zero
-                        if (1.0 / num &lt; 0.0) {
-                            addToken(Token.NEG);
-                        }
-                    } else if (inum == 1) {
-                        addIcode(Icode_ONE);
-                    } else if ((short)inum == inum) {
-                        addIcode(Icode_SHORTNUMBER);
-                        // write short as uin16 bit pattern
-                        addUint16(inum &amp; 0xFFFF);
-                    } else {
-                        addIcode(Icode_INTNUMBER);
-                        addInt(inum);
-                    }
-                } else {
-                    int index = getDoubleIndex(num);
-                    addIndexOp(Token.NUMBER, index);
-                }
-                stackChange(1);
-            }
-            break;
-
-          case Token.GETVAR:
-            {
-                if (itsData.itsNeedsActivation) Kit.codeBug();
-                int index = scriptOrFn.getIndexForNameNode(node);
-                addVarOp(Token.GETVAR, index);
-                stackChange(1);
-            }
-            break;
-
-          case Token.SETVAR:
-            {
-                if (itsData.itsNeedsActivation) Kit.codeBug();
-                int index = scriptOrFn.getIndexForNameNode(child);
-                child = child.getNext();
-                visitExpression(child, 0);
-                addVarOp(Token.SETVAR, index);
-            }
-            break;
-
-          case Token.SETCONSTVAR:
-            {
-                if (itsData.itsNeedsActivation) Kit.codeBug();
-                int index = scriptOrFn.getIndexForNameNode(child);
-                child = child.getNext();
-                visitExpression(child, 0);
-                addVarOp(Token.SETCONSTVAR, index);
-            }
-            break;
-
-          case Token.NULL:
-          case Token.THIS:
-          case Token.THISFN:
-          case Token.FALSE:
-          case Token.TRUE:
-            addToken(type);
-            stackChange(1);
-            break;
-
-          case Token.ENUM_NEXT:
-          case Token.ENUM_ID:
-            addIndexOp(type, getLocalBlockRef(node));
-            stackChange(1);
-            break;
-
-          case Token.REGEXP:
-            {
-                int index = node.getExistingIntProp(Node.REGEXP_PROP);
-                addIndexOp(Token.REGEXP, index);
-                stackChange(1);
-            }
-            break;
-
-          case Token.ARRAYLIT:
-          case Token.OBJECTLIT:
-            visitLiteral(node, child);
-            break;
-
-          case Token.ARRAYCOMP:
-            visitArrayComprehension(node, child, child.getNext());
-            break;
-
-          case Token.REF_SPECIAL:
-            visitExpression(child, 0);
-            addStringOp(type, (String)node.getProp(Node.NAME_PROP));
-            break;
-
-          case Token.REF_MEMBER:
-          case Token.REF_NS_MEMBER:
-          case Token.REF_NAME:
-          case Token.REF_NS_NAME:
-            {
-                int memberTypeFlags = node.getIntProp(Node.MEMBER_TYPE_PROP, 0);
-                // generate possible target, possible namespace and member
-                int childCount = 0;
-                do {
-                    visitExpression(child, 0);
-                    ++childCount;
-                    child = child.getNext();
-                } while (child != null);
-                addIndexOp(type, memberTypeFlags);
-                stackChange(1 - childCount);
-            }
-            break;
-
-          case Token.DOTQUERY:
-            {
-                int queryPC;
-                updateLineNumber(node);
-                visitExpression(child, 0);
-                addIcode(Icode_ENTERDQ);
-                stackChange(-1);
-                queryPC = itsICodeTop;
-                visitExpression(child.getNext(), 0);
-                addBackwardGoto(Icode_LEAVEDQ, queryPC);
-            }
-            break;
-
-          case Token.DEFAULTNAMESPACE :
-          case Token.ESCXMLATTR :
-          case Token.ESCXMLTEXT :
-            visitExpression(child, 0);
-            addToken(type);
-            break;
-
-          case Token.YIELD:
-            if (child != null) {
-                visitExpression(child, 0);
-            } else {
-                addIcode(Icode_UNDEF);
-                stackChange(1);
-            }
-            addToken(Token.YIELD);
-            addUint16(node.getLineno() &amp; 0xFFFF);
-            break;
-
-          case Token.WITHEXPR: {
-            Node enterWith = node.getFirstChild();
-            Node with = enterWith.getNext();
-            visitExpression(enterWith.getFirstChild(), 0);
-            addToken(Token.ENTERWITH);
-            stackChange(-1);
-            visitExpression(with.getFirstChild(), 0);
-            addToken(Token.LEAVEWITH);
-            break;
-          }
-
-          default:
-            throw badTree(node);
-        }
-        if (savedStackDepth + 1 != itsStackDepth) {
-            Kit.codeBug();
-        }
-    }
-
-    private void generateCallFunAndThis(Node left)
-    {
-        // Generate code to place on stack function and thisObj
-        int type = left.getType();
-        switch (type) {
-          case Token.NAME: {
-            String name = left.getString();
-            // stack: ... -&gt; ... function thisObj
-            addStringOp(Icode_NAME_AND_THIS, name);
-            stackChange(2);
-            break;
-          }
-          case Token.GETPROP:
-          case Token.GETELEM: {
-            Node target = left.getFirstChild();
-            visitExpression(target, 0);
-            Node id = target.getNext();
-            if (type == Token.GETPROP) {
-                String property = id.getString();
-                // stack: ... target -&gt; ... function thisObj
-                addStringOp(Icode_PROP_AND_THIS, property);
-                stackChange(1);
-            } else {
-                visitExpression(id, 0);
-                // stack: ... target id -&gt; ... function thisObj
-                addIcode(Icode_ELEM_AND_THIS);
-            }
-            break;
-          }
-          default:
-            // Including Token.GETVAR
-            visitExpression(left, 0);
-            // stack: ... value -&gt; ... function thisObj
-            addIcode(Icode_VALUE_AND_THIS);
-            stackChange(1);
-            break;
-        }
-    }
-
-    private void visitIncDec(Node node, Node child)
-    {
-        int incrDecrMask = node.getExistingIntProp(Node.INCRDECR_PROP);
-        int childType = child.getType();
-        switch (childType) {
-          case Token.GETVAR : {
-            if (itsData.itsNeedsActivation) Kit.codeBug();
-            int i = scriptOrFn.getIndexForNameNode(child);
-            addVarOp(Icode_VAR_INC_DEC, i);
-            addUint8(incrDecrMask);
-            stackChange(1);
-            break;
-          }
-          case Token.NAME : {
-            String name = child.getString();
-            addStringOp(Icode_NAME_INC_DEC, name);
-            addUint8(incrDecrMask);
-            stackChange(1);
-            break;
-          }
-          case Token.GETPROP : {
-            Node object = child.getFirstChild();
-            visitExpression(object, 0);
-            String property = object.getNext().getString();
-            addStringOp(Icode_PROP_INC_DEC, property);
-            addUint8(incrDecrMask);
-            break;
-          }
-          case Token.GETELEM : {
-            Node object = child.getFirstChild();
-            visitExpression(object, 0);
-            Node index = object.getNext();
-            visitExpression(index, 0);
-            addIcode(Icode_ELEM_INC_DEC);
-            addUint8(incrDecrMask);
-            stackChange(-1);
-            break;
-          }
-          case Token.GET_REF : {
-            Node ref = child.getFirstChild();
-            visitExpression(ref, 0);
-            addIcode(Icode_REF_INC_DEC);
-            addUint8(incrDecrMask);
-            break;
-          }
-          default : {
-            throw badTree(node);
-          }
-        }
-    }
-
-    private void visitLiteral(Node node, Node child)
-    {
-        int type = node.getType();
-        int count;
-        Object[] propertyIds = null;
-        if (type == Token.ARRAYLIT) {
-            count = 0;
-            for (Node n = child; n != null; n = n.getNext()) {
-                ++count;
-            }
-        } else if (type == Token.OBJECTLIT) {
-            propertyIds = (Object[])node.getProp(Node.OBJECT_IDS_PROP);
-            count = propertyIds.length;
-        } else {
-            throw badTree(node);
-        }
-        addIndexOp(Icode_LITERAL_NEW, count);
-        stackChange(2);
-        while (child != null) {
-            int childType = child.getType();
-            if (childType == Token.GET) {
-                visitExpression(child.getFirstChild(), 0);
-                addIcode(Icode_LITERAL_GETTER);
-            } else if (childType == Token.SET) {
-                visitExpression(child.getFirstChild(), 0);
-                addIcode(Icode_LITERAL_SETTER);
-            } else {
-                visitExpression(child, 0);
-                addIcode(Icode_LITERAL_SET);
-            }
-            stackChange(-1);
-            child = child.getNext();
-        }
-        if (type == Token.ARRAYLIT) {
-            int[] skipIndexes = (int[])node.getProp(Node.SKIP_INDEXES_PROP);
-            if (skipIndexes == null) {
-                addToken(Token.ARRAYLIT);
-            } else {
-                int index = itsLiteralIds.size();
-                itsLiteralIds.add(skipIndexes);
-                addIndexOp(Icode_SPARE_ARRAYLIT, index);
-            }
-        } else {
-            int index = itsLiteralIds.size();
-            itsLiteralIds.add(propertyIds);
-            addIndexOp(Token.OBJECTLIT, index);
-        }
-        stackChange(-1);
-    }
-    
-    private void visitArrayComprehension(Node node, Node initStmt, Node expr)
-    {
-        // A bit of a hack: array comprehensions are implemented using
-        // statement nodes for the iteration, yet they appear in an
-        // expression context. So we pass the current stack depth to
-        // visitStatement so it can check that the depth is not altered
-        // by statements.
-        visitStatement(initStmt, itsStackDepth);
-        visitExpression(expr, 0);
-    }
-
-    private int getLocalBlockRef(Node node)
-    {
-        Node localBlock = (Node)node.getProp(Node.LOCAL_BLOCK_PROP);
-        return localBlock.getExistingIntProp(Node.LOCAL_PROP);
-    }
-
-    private int getTargetLabel(Node target)
-    {
-        int label = target.labelId();
-        if (label != -1) {
-            return label;
-        }
-        label = itsLabelTableTop;
-        if (itsLabelTable == null || label == itsLabelTable.length) {
-            if (itsLabelTable == null) {
-                itsLabelTable = new int[MIN_LABEL_TABLE_SIZE];
-            }else {
-                int[] tmp = new int[itsLabelTable.length * 2];
-                System.arraycopy(itsLabelTable, 0, tmp, 0, label);
-                itsLabelTable = tmp;
-            }
-        }
-        itsLabelTableTop = label + 1;
-        itsLabelTable[label] = -1;
-
-        target.labelId(label);
-        return label;
-    }
-
-    private void markTargetLabel(Node target)
-    {
-        int label = getTargetLabel(target);
-        if (itsLabelTable[label] != -1) {
-            // Can mark label only once
-            Kit.codeBug();
-        }
-        itsLabelTable[label] = itsICodeTop;
-    }
-
-    private void addGoto(Node target, int gotoOp)
-    {
-        int label = getTargetLabel(target);
-        if (!(label &lt; itsLabelTableTop)) Kit.codeBug();
-        int targetPC = itsLabelTable[label];
-
-        if (targetPC != -1) {
-            addBackwardGoto(gotoOp, targetPC);
-        } else {
-            int gotoPC = itsICodeTop;
-            addGotoOp(gotoOp);
-            int top = itsFixupTableTop;
-            if (itsFixupTable == null || top == itsFixupTable.length) {
-                if (itsFixupTable == null) {
-                    itsFixupTable = new long[MIN_FIXUP_TABLE_SIZE];
-                } else {
-                    long[] tmp = new long[itsFixupTable.length * 2];
-                    System.arraycopy(itsFixupTable, 0, tmp, 0, top);
-                    itsFixupTable = tmp;
-                }
-            }
-            itsFixupTableTop = top + 1;
-            itsFixupTable[top] = ((long)label &lt;&lt; 32) | gotoPC;
-        }
-    }
-
-    private void fixLabelGotos()
-    {
-        for (int i = 0; i &lt; itsFixupTableTop; i++) {
-            long fixup = itsFixupTable[i];
-            int label = (int)(fixup &gt;&gt; 32);
-            int jumpSource = (int)fixup;
-            int pc = itsLabelTable[label];
-            if (pc == -1) {
-                // Unlocated label
-                throw Kit.codeBug();
-            }
-            resolveGoto(jumpSource, pc);
-        }
-        itsFixupTableTop = 0;
-    }
-
-    private void addBackwardGoto(int gotoOp, int jumpPC)
-    {
-        int fromPC = itsICodeTop;
-        // Ensure that this is a jump backward
-        if (fromPC &lt;= jumpPC) throw Kit.codeBug();
-        addGotoOp(gotoOp);
-        resolveGoto(fromPC, jumpPC);
-    }
-
-    private void resolveForwardGoto(int fromPC)
-    {
-        // Ensure that forward jump skips at least self bytecode
-        if (itsICodeTop &lt; fromPC + 3) throw Kit.codeBug();
-        resolveGoto(fromPC, itsICodeTop);
-    }
-
-    private void resolveGoto(int fromPC, int jumpPC)
-    {
-        int offset = jumpPC - fromPC;
-        // Ensure that jumps do not overlap
-        if (0 &lt;= offset &amp;&amp; offset &lt;= 2) throw Kit.codeBug();
-        int offsetSite = fromPC + 1;
-        if (offset != (short)offset) {
-            if (itsData.longJumps == null) {
-                itsData.longJumps = new UintMap();
-            }
-            itsData.longJumps.put(offsetSite, jumpPC);
-            offset = 0;
-        }
-        byte[] array = itsData.itsICode;
-        array[offsetSite] = (byte)(offset &gt;&gt; 8);
-        array[offsetSite + 1] = (byte)offset;
-    }
-
-    private void addToken(int token)
-    {
-        if (!validTokenCode(token)) throw Kit.codeBug();
-        addUint8(token);
-    }
-
-    private void addIcode(int icode)
-    {
-        if (!validIcode(icode)) throw Kit.codeBug();
-        // Write negative icode as uint8 bits
-        addUint8(icode &amp; 0xFF);
-    }
-
-    private void addUint8(int value)
-    {
-        if ((value &amp; ~0xFF) != 0) throw Kit.codeBug();
-        byte[] array = itsData.itsICode;
-        int top = itsICodeTop;
-        if (top == array.length) {
-            array = increaseICodeCapacity(1);
-        }
-        array[top] = (byte)value;
-        itsICodeTop = top + 1;
-    }
-
-    private void addUint16(int value)
-    {
-        if ((value &amp; ~0xFFFF) != 0) throw Kit.codeBug();
-        byte[] array = itsData.itsICode;
-        int top = itsICodeTop;
-        if (top + 2 &gt; array.length) {
-            array = increaseICodeCapacity(2);
-        }
-        array[top] = (byte)(value &gt;&gt;&gt; 8);
-        array[top + 1] = (byte)value;
-        itsICodeTop = top + 2;
-    }
-
-    private void addInt(int i)
-    {
-        byte[] array = itsData.itsICode;
-        int top = itsICodeTop;
-        if (top + 4 &gt; array.length) {
-            array = increaseICodeCapacity(4);
-        }
-        array[top] = (byte)(i &gt;&gt;&gt; 24);
-        array[top + 1] = (byte)(i &gt;&gt;&gt; 16);
-        array[top + 2] = (byte)(i &gt;&gt;&gt; 8);
-        array[top + 3] = (byte)i;
-        itsICodeTop = top + 4;
-    }
-
-    private int getDoubleIndex(double num)
-    {
-        int index = itsDoubleTableTop;
-        if (index == 0) {
-            itsData.itsDoubleTable = new double[64];
-        } else if (itsData.itsDoubleTable.length == index) {
-            double[] na = new double[index * 2];
-            System.arraycopy(itsData.itsDoubleTable, 0, na, 0, index);
-            itsData.itsDoubleTable = na;
-        }
-        itsData.itsDoubleTable[index] = num;
-        itsDoubleTableTop = index + 1;
-        return index;
-    }
-
-    private void addGotoOp(int gotoOp)
-    {
-        byte[] array = itsData.itsICode;
-        int top = itsICodeTop;
-        if (top + 3 &gt; array.length) {
-            array = increaseICodeCapacity(3);
-        }
-        array[top] = (byte)gotoOp;
-        // Offset would written later
-        itsICodeTop = top + 1 + 2;
-    }
-
-    private void addVarOp(int op, int varIndex)
-    {
-        switch (op) {
-          case Token.SETCONSTVAR:
-            if (varIndex &lt; 128) {
-                addIcode(Icode_SETCONSTVAR1);
-                addUint8(varIndex);
-                return;
-            }
-            addIndexOp(Icode_SETCONSTVAR, varIndex);
-            return;
-          case Token.GETVAR:
-          case Token.SETVAR:
-            if (varIndex &lt; 128) {
-                addIcode(op == Token.GETVAR ? Icode_GETVAR1 : Icode_SETVAR1);
-                addUint8(varIndex);
-                return;
-            }
-            // fallthrough
-          case Icode_VAR_INC_DEC:
-            addIndexOp(op, varIndex);
-            return;
-        }
-        throw Kit.codeBug();
-    }
-
-    private void addStringOp(int op, String str)
-    {
-        addStringPrefix(str);
-        if (validIcode(op)) {
-            addIcode(op);
-        } else {
-            addToken(op);
-        }
-    }
-
-    private void addIndexOp(int op, int index)
-    {
-        addIndexPrefix(index);
-        if (validIcode(op)) {
-            addIcode(op);
-        } else {
-            addToken(op);
-        }
-    }
-
-    private void addStringPrefix(String str)
-    {
-        int index = itsStrings.get(str, -1);
-        if (index == -1) {
-            index = itsStrings.size();
-            itsStrings.put(str, index);
-        }
-        if (index &lt; 4) {
-            addIcode(Icode_REG_STR_C0 - index);
-        } else if (index &lt;= 0xFF) {
-            addIcode(Icode_REG_STR1);
-            addUint8(index);
-         } else if (index &lt;= 0xFFFF) {
-            addIcode(Icode_REG_STR2);
-            addUint16(index);
-         } else {
-            addIcode(Icode_REG_STR4);
-            addInt(index);
-        }
-    }
-
-    private void addIndexPrefix(int index)
-    {
-        if (index &lt; 0) Kit.codeBug();
-        if (index &lt; 6) {
-            addIcode(Icode_REG_IND_C0 - index);
-        } else if (index &lt;= 0xFF) {
-            addIcode(Icode_REG_IND1);
-            addUint8(index);
-         } else if (index &lt;= 0xFFFF) {
-            addIcode(Icode_REG_IND2);
-            addUint16(index);
-         } else {
-            addIcode(Icode_REG_IND4);
-            addInt(index);
-        }
-    }
-
-    private void addExceptionHandler(int icodeStart, int icodeEnd,
-                                     int handlerStart, boolean isFinally,
-                                     int exceptionObjectLocal, int scopeLocal)
-    {
-        int top = itsExceptionTableTop;
-        int[] table = itsData.itsExceptionTable;
-        if (table == null) {
-            if (top != 0) Kit.codeBug();
-            table = new int[EXCEPTION_SLOT_SIZE * 2];
-            itsData.itsExceptionTable = table;
-        } else if (table.length == top) {
-            table = new int[table.length * 2];
-            System.arraycopy(itsData.itsExceptionTable, 0, table, 0, top);
-            itsData.itsExceptionTable = table;
-        }
-        table[top + EXCEPTION_TRY_START_SLOT]  = icodeStart;
-        table[top + EXCEPTION_TRY_END_SLOT]    = icodeEnd;
-        table[top + EXCEPTION_HANDLER_SLOT]    = handlerStart;
-        table[top + EXCEPTION_TYPE_SLOT]       = isFinally ? 1 : 0;
-        table[top + EXCEPTION_LOCAL_SLOT]      = exceptionObjectLocal;
-        table[top + EXCEPTION_SCOPE_SLOT]      = scopeLocal;
-
-        itsExceptionTableTop = top + EXCEPTION_SLOT_SIZE;
-    }
-
-    private byte[] increaseICodeCapacity(int extraSize)
-    {
-        int capacity = itsData.itsICode.length;
-        int top = itsICodeTop;
-        if (top + extraSize &lt;= capacity) throw Kit.codeBug();
-        capacity *= 2;
-        if (top + extraSize &gt; capacity) {
-            capacity = top + extraSize;
-        }
-        byte[] array = new byte[capacity];
-        System.arraycopy(itsData.itsICode, 0, array, 0, top);
-        itsData.itsICode = array;
-        return array;
-    }
-
-    private void stackChange(int change)
-    {
-        if (change &lt;= 0) {
-            itsStackDepth += change;
-        } else {
-            int newDepth = itsStackDepth + change;
-            if (newDepth &gt; itsData.itsMaxStack) {
-                itsData.itsMaxStack = newDepth;
-            }
-            itsStackDepth = newDepth;
-        }
-    }
-
-    private int allocLocal()
-    {
-        int localSlot = itsLocalTop;
-        ++itsLocalTop;
-        if (itsLocalTop &gt; itsData.itsMaxLocals) {
-            itsData.itsMaxLocals = itsLocalTop;
-        }
-        return localSlot;
-    }
-
-    private void releaseLocal(int localSlot)
-    {
-        --itsLocalTop;
-        if (localSlot != itsLocalTop) Kit.codeBug();
-    }
-
     private static int getShort(byte[] iCode, int pc) {
         return (iCode[pc] &lt;&lt; 8) | (iCode[pc + 1] &amp; 0xFF);
     }
@@ -1960,7 +318,7 @@ public class Interpreter implements Evaluator
         return best;
     }
 
-    private static void dumpICode(InterpreterData idata)
+    static void dumpICode(InterpreterData idata)
     {
         if (!Token.printICode) {
             return;
@@ -1980,7 +338,7 @@ public class Interpreter implements Evaluator
             out.print(&quot; [&quot; + pc + &quot;] &quot;);
             int token = iCode[pc];
             int icodeLength = bytecodeSpan(token);
-            String tname = bytecodeName(token);
+            String tname = Icode.bytecodeName(token);
             int old_pc = pc;
             ++pc;
             switch (token) {
@@ -2405,7 +763,7 @@ public class Interpreter implements Evaluator
         if (ex.interpreterStackInfo == null) {
             return null;
         }
-        
+
         List&lt;String&gt; list = new ArrayList&lt;String&gt;();
         String lineSeparator =
                 SecurityUtilities.getSystemProperty(&quot;line.separator&quot;);
@@ -2442,7 +800,7 @@ public class Interpreter implements Evaluator
         }
         return list;
     }
-        
+
     static String getEncodedSource(InterpreterData idata)
     {
         if (idata.encodedSource == null) {
@@ -2651,7 +1009,7 @@ switch (op) {
           frame.pc--; // we want to come back here when we resume
           CallFrame generatorFrame = captureFrameForGenerator(frame);
           generatorFrame.frozen = true;
-          NativeGenerator generator = new NativeGenerator(frame.scope, 
+          NativeGenerator generator = new NativeGenerator(frame.scope,
               generatorFrame.fnOrScript, generatorFrame);
           frame.result = generator;
           break Loop;
@@ -3290,7 +1648,7 @@ switch (op) {
                         frame.parentFrame, false);
                 continue Loop;
             }
-            // Bug 405654 -- make best effort to keep Function.apply and 
+            // Bug 405654 -- make best effort to keep Function.apply and
             // Function.call within this interpreter loop invocation
             if (BaseFunction.isApplyOrCall(ifun)) {
                 Callable applyCallable = ScriptRuntime.getCallable(funThisObj);
@@ -3562,9 +1920,9 @@ switch (op) {
         if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
         --stackTop;
         indexReg += frame.localShift;
-        int enumType = op == Token.ENUM_INIT_KEYS 
+        int enumType = op == Token.ENUM_INIT_KEYS
                          ? ScriptRuntime.ENUMERATE_KEYS :
-                       op == Token.ENUM_INIT_VALUES 
+                       op == Token.ENUM_INIT_VALUES
                          ? ScriptRuntime.ENUMERATE_VALUES :
                        ScriptRuntime.ENUMERATE_ARRAY;
         stack[indexReg] = ScriptRuntime.enumInit(lhs, cx, enumType);
@@ -3882,7 +2240,7 @@ switch (op) {
                 generatorState.operation == NativeGenerator.GENERATOR_CLOSE &amp;&amp;
                 throwable == generatorState.value)
             {
-                exState = EX_FINALLY_STATE;            	
+                exState = EX_FINALLY_STATE;
             } else if (throwable instanceof JavaScriptException) {
                 exState = EX_CATCH_STATE;
             } else if (throwable instanceof EcmaError) {
@@ -4101,7 +2459,7 @@ switch (op) {
                 // XXX Deal with exceptios!!!
                 frame = frame.cloneFrozen();
             }
-            
+
             int[] table = frame.idata.itsExceptionTable;
 
             frame.pc = table[indexReg + EXCEPTION_HANDLER_SLOT];
@@ -4252,9 +2610,9 @@ switch (op) {
         }
         CallFrame calleeFrame = new CallFrame();
         if(BaseFunction.isApply(ifun)) {
-            Object[] callArgs = indexReg &lt; 2 ? ScriptRuntime.emptyArgs : 
+            Object[] callArgs = indexReg &lt; 2 ? ScriptRuntime.emptyArgs :
                 ScriptRuntime.getApplyArguments(cx, stack[stackTop + 3]);
-            initFrame(cx, calleeScope, applyThis, callArgs, null, 0, 
+            initFrame(cx, calleeScope, applyThis, callArgs, null, 0,
                     callArgs.length, iApplyCallable, frame, calleeFrame);
         }
         else {
@@ -4264,10 +2622,10 @@ switch (op) {
                 sDbl[stackTop + 1 + i] = sDbl[stackTop + 2 + i];
             }
             int argCount = indexReg &lt; 2 ? 0 : indexReg - 1;
-            initFrame(cx, calleeScope, applyThis, stack, sDbl, stackTop + 2, 
+            initFrame(cx, calleeScope, applyThis, stack, sDbl, stackTop + 2,
                     argCount, iApplyCallable, frame, calleeFrame);
         }
-        
+
         frame = calleeFrame;
         return frame;
     }
@@ -4435,32 +2793,32 @@ switch (op) {
         return frame.debuggerFrame != null || frame.idata.itsNeedsActivation;
     }
 
-    private static void enterFrame(Context cx, CallFrame frame, Object[] args, 
+    private static void enterFrame(Context cx, CallFrame frame, Object[] args,
                                    boolean continuationRestart)
     {
-        boolean usesActivation = frame.idata.itsNeedsActivation; 
+        boolean usesActivation = frame.idata.itsNeedsActivation;
         boolean isDebugged = frame.debuggerFrame != null;
         if(usesActivation || isDebugged) {
             Scriptable scope = frame.scope;
             if(scope == null) {
                 Kit.codeBug();
             } else if (continuationRestart) {
-                // Walk the parent chain of frame.scope until a NativeCall is 
-                // found. Normally, frame.scope is a NativeCall when called 
-                // from initFrame() for a debugged or activatable function. 
+                // Walk the parent chain of frame.scope until a NativeCall is
+                // found. Normally, frame.scope is a NativeCall when called
+                // from initFrame() for a debugged or activatable function.
                 // However, when called from interpretLoop() as part of
-                // restarting a continuation, it can also be a NativeWith if 
-                // the continuation was captured within a &quot;with&quot; or &quot;catch&quot; 
-                // block (&quot;catch&quot; implicitly uses NativeWith to create a scope 
+                // restarting a continuation, it can also be a NativeWith if
+                // the continuation was captured within a &quot;with&quot; or &quot;catch&quot;
+                // block (&quot;catch&quot; implicitly uses NativeWith to create a scope
                 // to expose the exception variable).
                 for(;;) {
                     if(scope instanceof NativeWith) {
                         scope = scope.getParentScope();
-                        if (scope == null || (frame.parentFrame != null &amp;&amp; 
+                        if (scope == null || (frame.parentFrame != null &amp;&amp;
                                               frame.parentFrame.scope == scope))
                         {
-                            // If we get here, we didn't find a NativeCall in 
-                            // the call chain before reaching parent frame's 
+                            // If we get here, we didn't find a NativeCall in
+                            // the call chain before reaching parent frame's
                             // scope. This should not be possible.
                             Kit.codeBug();
                             break; // Never reached, but keeps the static analyzer happy about &quot;scope&quot; not being null 5 lines above.
@@ -4474,8 +2832,8 @@ switch (op) {
             if (isDebugged) {
                 frame.debuggerFrame.onEnter(cx, scope, frame.thisObj, args);
             }
-            // Enter activation only when itsNeedsActivation true, 
-            // since debugger should not interfere with activation 
+            // Enter activation only when itsNeedsActivation true,
+            // since debugger should not interfere with activation
             // chaining
             if (usesActivation) {
                 ScriptRuntime.enterActivationFunction(cx, scope);</diff>
      <filename>src/org/mozilla/javascript/Interpreter.java</filename>
    </modified>
    <modified>
      <diff>@@ -451,5 +451,20 @@ public class Kit
         ex.printStackTrace(System.err);
         throw ex;
     }
-}
 
+    /**
+     * Throws RuntimeException to indicate failed assertion.
+     * The function never returns and its return type is RuntimeException
+     * only to be able to write &lt;tt&gt;throw Kit.codeBug()&lt;/tt&gt; if plain
+     * &lt;tt&gt;Kit.codeBug()&lt;/tt&gt; triggers unreachable code error.
+     */
+    public static RuntimeException codeBug(String msg)
+        throws RuntimeException
+    {
+        msg = &quot;FAILED ASSERTION: &quot; + msg;
+        RuntimeException ex = new IllegalStateException(msg);
+        // Print stack trace ASAP
+        ex.printStackTrace(System.err);
+        throw ex;
+    }
+}</diff>
      <filename>src/org/mozilla/javascript/Kit.java</filename>
    </modified>
    <modified>
      <diff>@@ -208,8 +208,9 @@ public class NativeJavaClass extends NativeJavaObject implements Function
                 Object v = topLevel.get(&quot;JavaAdapter&quot;, topLevel);
                 if (v != NOT_FOUND) {
                     Function f = (Function) v;
+                    // Args are (interface, js object)
                     Object[] adapterArgs = { this, args[0] };
-                    return f.construct(cx, topLevel,adapterArgs);
+                    return f.construct(cx, topLevel, adapterArgs);
                 }
             } catch (Exception ex) {
                 // fall through to error</diff>
      <filename>src/org/mozilla/javascript/NativeJavaClass.java</filename>
    </modified>
    <modified>
      <diff>@@ -205,14 +205,14 @@ public class NativeJavaPackage extends ScriptableObject
         }
         return false;
     }
-    
+
     @Override
     public int hashCode() {
-        return packageName.hashCode() ^ 
+        return packageName.hashCode() ^
                (classLoader == null ? 0 : classLoader.hashCode());
     }
 
     private String packageName;
     private ClassLoader classLoader;
     private Set&lt;String&gt; negativeCache = null;
-}
\ No newline at end of file
+}</diff>
      <filename>src/org/mozilla/javascript/NativeJavaPackage.java</filename>
    </modified>
    <modified>
      <diff>@@ -41,10 +41,18 @@
 
 package org.mozilla.javascript;
 
-import java.util.Map;
-import java.util.LinkedHashMap;
-import java.util.Iterator;
+import org.mozilla.javascript.ast.FunctionNode;
+import org.mozilla.javascript.ast.Jump;
+import org.mozilla.javascript.ast.Name;
+import org.mozilla.javascript.ast.NumberLiteral;
+import org.mozilla.javascript.ast.Scope;
+import org.mozilla.javascript.ast.ScriptNode;
+
 import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.NoSuchElementException;
 
 /**
  * This class implements the root of the intermediate representation.
@@ -52,8 +60,7 @@ import java.util.Collections;
  * @author Norris Boyd
  * @author Mike McCabe
  */
-
-public class Node
+public class Node implements Iterable&lt;Node&gt;
 {
     public static final int
         FUNCTION_PROP      =  1,
@@ -61,36 +68,36 @@ public class Node
         LOCAL_BLOCK_PROP   =  3,
         REGEXP_PROP        =  4,
         CASEARRAY_PROP     =  5,
-    /*
-        the following properties are defined and manipulated by the
-        optimizer -
-        TARGETBLOCK_PROP - the block referenced by a branch node
-        VARIABLE_PROP - the variable referenced by a BIND or NAME node
-        ISNUMBER_PROP - this node generates code on Number children and
-                        delivers a Number result (as opposed to Objects)
-        DIRECTCALL_PROP - this call node should emit code to test the function
-                          object against the known class and call direct if it
-                          matches.
-    */
-
-        TARGETBLOCK_PROP   =  6,
-        VARIABLE_PROP      =  7,
-        ISNUMBER_PROP      =  8,
-        DIRECTCALL_PROP    =  9,
-        SPECIALCALL_PROP   = 10,
-        SKIP_INDEXES_PROP  = 11, // array of skipped indexes of array literal
-        OBJECT_IDS_PROP    = 12, // array of properties for object literal
-        INCRDECR_PROP      = 13, // pre or post type of increment/decrement
-        CATCH_SCOPE_PROP   = 14, // index of catch scope block in catch
-        LABEL_ID_PROP      = 15, // label id: code generation uses it
-        MEMBER_TYPE_PROP   = 16, // type of element access operation
-        NAME_PROP          = 17, // property name
-        CONTROL_BLOCK_PROP = 18, // flags a control block that can drop off
-        PARENTHESIZED_PROP = 19, // expression is parenthesized
-        GENERATOR_END_PROP = 20,
+
+    //  the following properties are defined and manipulated by the
+    //  optimizer -
+    //  TARGETBLOCK_PROP - the block referenced by a branch node
+    //  VARIABLE_PROP - the variable referenced by a BIND or NAME node
+    //  ISNUMBER_PROP - this node generates code on Number children and
+    //                  delivers a Number result (as opposed to Objects)
+    //  DIRECTCALL_PROP - this call node should emit code to test the function
+    //                    object against the known class and call direct if it
+    //                    matches.
+
+        TARGETBLOCK_PROP     =  6,
+        VARIABLE_PROP        =  7,
+        ISNUMBER_PROP        =  8,
+        DIRECTCALL_PROP      =  9,
+        SPECIALCALL_PROP     = 10,
+        SKIP_INDEXES_PROP    = 11, // array of skipped indexes of array literal
+        OBJECT_IDS_PROP      = 12, // array of properties for object literal
+        INCRDECR_PROP        = 13, // pre or post type of increment/decrement
+        CATCH_SCOPE_PROP     = 14, // index of catch scope block in catch
+        LABEL_ID_PROP        = 15, // label id: code generation uses it
+        MEMBER_TYPE_PROP     = 16, // type of element access operation
+        NAME_PROP            = 17, // property name
+        CONTROL_BLOCK_PROP   = 18, // flags a control block that can drop off
+        PARENTHESIZED_PROP   = 19, // expression is parenthesized
+        GENERATOR_END_PROP   = 20,
         DESTRUCTURING_ARRAY_LENGTH = 21,
-        DESTRUCTURING_NAMES= 22,
-        LAST_PROP          = 22;
+        DESTRUCTURING_NAMES  = 22,
+        DESTRUCTURING_PARAMS = 23,
+        LAST_PROP            = 23;
 
     // values of ISNUMBER_PROP to specify
     // which of the children are Number types
@@ -113,227 +120,6 @@ public class Node
         ATTRIBUTE_FLAG   = 0x2, // x.@y or x..@y
         DESCENDANTS_FLAG = 0x4; // x..y or x..@i
 
-    private static class NumberNode extends Node
-    {
-        NumberNode(double number)
-        {
-            super(Token.NUMBER);
-            this.number = number;
-        }
-
-        double number;
-    }
-
-    private static class StringNode extends Node
-    {
-        StringNode(int type, String str) {
-            super(type);
-            this.str = str;
-        }
-
-        String str;
-        Node.Scope scope;
-    }
-
-    public static class Jump extends Node
-    {
-        public Jump(int type)
-        {
-            super(type);
-        }
-
-        Jump(int type, int lineno)
-        {
-            super(type, lineno);
-        }
-
-        Jump(int type, Node child)
-        {
-            super(type, child);
-        }
-
-        Jump(int type, Node child, int lineno)
-        {
-            super(type, child, lineno);
-        }
-
-        public final Jump getJumpStatement()
-        {
-            if (!(type == Token.BREAK || type == Token.CONTINUE)) Kit.codeBug();
-            return jumpNode;
-        }
-
-        public final void setJumpStatement(Jump jumpStatement)
-        {
-            if (!(type == Token.BREAK || type == Token.CONTINUE)) Kit.codeBug();
-            if (jumpStatement == null) Kit.codeBug();
-            if (this.jumpNode != null) Kit.codeBug(); //only once
-            this.jumpNode = jumpStatement;
-        }
-
-        public final Node getDefault()
-        {
-            if (!(type == Token.SWITCH)) Kit.codeBug();
-            return target2;
-        }
-
-        public final void setDefault(Node defaultTarget)
-        {
-            if (!(type == Token.SWITCH)) Kit.codeBug();
-            if (defaultTarget.type != Token.TARGET) Kit.codeBug();
-            if (target2 != null) Kit.codeBug(); //only once
-            target2 = defaultTarget;
-        }
-
-        public final Node getFinally()
-        {
-            if (!(type == Token.TRY)) Kit.codeBug();
-            return target2;
-        }
-
-        public final void setFinally(Node finallyTarget)
-        {
-            if (!(type == Token.TRY)) Kit.codeBug();
-            if (finallyTarget.type != Token.TARGET) Kit.codeBug();
-            if (target2 != null) Kit.codeBug(); //only once
-            target2 = finallyTarget;
-        }
-
-        public final Jump getLoop()
-        {
-            if (!(type == Token.LABEL)) Kit.codeBug();
-            return jumpNode;
-        }
-
-        public final void setLoop(Jump loop)
-        {
-            if (!(type == Token.LABEL)) Kit.codeBug();
-            if (loop == null) Kit.codeBug();
-            if (jumpNode != null) Kit.codeBug(); //only once
-            jumpNode = loop;
-        }
-
-        public final Node getContinue()
-        {
-            if (type != Token.LOOP) Kit.codeBug();
-            return target2;
-        }
-
-        public final void setContinue(Node continueTarget)
-        {
-            if (type != Token.LOOP) Kit.codeBug();
-            if (continueTarget.type != Token.TARGET) Kit.codeBug();
-            if (target2 != null) Kit.codeBug(); //only once
-            target2 = continueTarget;
-        }
-
-        public Node target;
-        private Node target2;
-        private Jump jumpNode;
-    }
-    
-    static class Symbol {
-        Symbol(int declType, String name) {
-            this.declType = declType;
-            this.name = name;
-            this.index = -1;
-        }
-        /**
-         * One of Token.FUNCTION, Token.LP (for parameters), Token.VAR, 
-         * Token.LET, or Token.CONST
-         */
-        int declType;
-        int index;
-        String name;
-        Node.Scope containingTable;
-    }
-    
-    static class Scope extends Jump {
-        public Scope(int nodeType) {
-            super(nodeType);
-        }
-        
-        public Scope(int nodeType, int lineno) {
-            super(nodeType, lineno);
-        }
-        
-        public Scope(int nodeType, Node n, int lineno) {
-            super(nodeType, n, lineno);
-        }
-        
-        /*
-         * Creates a new scope node, moving symbol table information
-         * from &quot;scope&quot; to the new node, and making &quot;scope&quot; a nested
-         * scope contained by the new node.
-         * Useful for injecting a new scope in a scope chain.
-         */
-        public static Scope splitScope(Scope scope) {
-            Scope result = new Scope(scope.getType());
-            result.symbolTable = scope.symbolTable;
-            scope.symbolTable = null;
-            result.parent = scope.parent;
-            scope.parent = result;
-            result.top = scope.top;
-            return result;
-        }
-
-        public static void joinScopes(Scope source, Scope dest) {
-            source.ensureSymbolTable();
-            dest.ensureSymbolTable();
-            if (!Collections.disjoint(source.symbolTable.keySet(),
-                                      dest.symbolTable.keySet()))
-            {
-                throw Kit.codeBug();
-            }
-            dest.symbolTable.putAll(source.symbolTable);
-        }
-        
-        public void setParent(Scope parent) {
-            this.parent = parent;
-            this.top = parent == null ? (ScriptOrFnNode)this : parent.top;
-        }
-        
-        public Scope getParentScope() {
-            return parent;
-        }
-  
-        public Scope getDefiningScope(String name) {
-            for (Scope sn=this; sn != null; sn = sn.parent) {
-                if (sn.symbolTable == null)
-                    continue;
-                if (sn.symbolTable.containsKey(name))
-                    return sn;
-            }
-            return null;
-        }
-        
-        public Symbol getSymbol(String name) {
-            return symbolTable == null ? null : symbolTable.get(name);
-        }
-        
-        public void putSymbol(String name, Symbol symbol) {
-            ensureSymbolTable();
-            symbolTable.put(name, symbol);
-            symbol.containingTable = this;
-            top.addSymbol(symbol);
-        }
-        
-        public Map&lt;String,Symbol&gt; getSymbolTable() {
-            return symbolTable;
-        }
-        
-        private void ensureSymbolTable() {
-            if (symbolTable == null) {
-                symbolTable = new LinkedHashMap&lt;String,Symbol&gt;(5);
-            }
-        }
-  
-        // Use LinkedHashMap so that the iteration order is the insertion order
-        protected LinkedHashMap&lt;String,Symbol&gt; symbolTable;
-        private Scope parent;
-        private ScriptOrFnNode top;
-    }
-
     private static class PropListItem
     {
         PropListItem next;
@@ -342,7 +128,6 @@ public class Node
         Object objectValue;
     }
 
-
     public Node(int nodeType) {
         type = nodeType;
     }
@@ -391,23 +176,32 @@ public class Node
     }
 
     public static Node newNumber(double number) {
-        return new NumberNode(number);
+        NumberLiteral n = new NumberLiteral();
+        n.setNumber(number);
+        return n;
     }
 
     public static Node newString(String str) {
-        return new StringNode(Token.STRING, str);
+        return newString(Token.STRING, str);
     }
 
     public static Node newString(int type, String str) {
-        return new StringNode(type, str);
+        Name name = new Name();
+        name.setIdentifier(str);
+        name.setType(type);
+        return name;
     }
 
     public int getType() {
         return type;
     }
 
-    public void setType(int type) {
+    /**
+     * Sets the node type and returns this node.
+     */
+    public Node setType(int type) {
         this.type = type;
+        return this;
     }
 
     public boolean hasChildren() {
@@ -544,37 +338,100 @@ public class Node
         child.next = null;
     }
 
+    public void removeChildren() {
+        first = last = null;
+    }
+
+    private static final Node NOT_SET = new Node(Token.ERROR);
+
+    /**
+     * Iterates over the children of this Node.  Supports child removal.  Not
+     * thread-safe.  If anyone changes the child list before the iterator
+     * finishes, the results are undefined and probably bad.
+     */
+    public class NodeIterator implements Iterator&lt;Node&gt; {
+        private Node cursor;  // points to node to be returned next
+        private Node prev = NOT_SET;
+        private Node prev2;
+        private boolean removed = false;
+
+        public NodeIterator() {
+            cursor = Node.this.first;
+        }
+
+        public boolean hasNext() {
+            return cursor != null;
+        }
+
+        public Node next() {
+            if (cursor == null) {
+                throw new NoSuchElementException();
+            }
+            removed = false;
+            prev2 = prev;
+            prev = cursor;
+            cursor = cursor.next;
+            return prev;
+        }
+
+        public void remove() {
+            if (prev == NOT_SET) {
+                throw new IllegalStateException(&quot;next() has not been called&quot;);
+            }
+            if (removed) {
+                throw new IllegalStateException(
+                    &quot;remove() already called for current element&quot;);
+            }
+            if (prev == first) {
+                first = prev.next;
+            } else if (prev == last) {
+                prev2.next = null;
+                last = prev2;
+            } else {
+                prev2.next = cursor;
+            }
+        }
+    }
+
+    /**
+     * Returns an {@link java.util.Iterator} over the node's children.
+     */
+    public Iterator&lt;Node&gt; iterator() {
+        return new NodeIterator();
+    }
+
     private static final String propToString(int propType)
     {
         if (Token.printTrees) {
             // If Context.printTrees is false, the compiler
             // can remove all these strings.
             switch (propType) {
-                case FUNCTION_PROP:      return &quot;function&quot;;
-                case LOCAL_PROP:         return &quot;local&quot;;
-                case LOCAL_BLOCK_PROP:   return &quot;local_block&quot;;
-                case REGEXP_PROP:        return &quot;regexp&quot;;
-                case CASEARRAY_PROP:     return &quot;casearray&quot;;
-
-                case TARGETBLOCK_PROP:   return &quot;targetblock&quot;;
-                case VARIABLE_PROP:      return &quot;variable&quot;;
-                case ISNUMBER_PROP:      return &quot;isnumber&quot;;
-                case DIRECTCALL_PROP:    return &quot;directcall&quot;;
-
-                case SPECIALCALL_PROP:   return &quot;specialcall&quot;;
-                case SKIP_INDEXES_PROP:  return &quot;skip_indexes&quot;;
-                case OBJECT_IDS_PROP:    return &quot;object_ids_prop&quot;;
-                case INCRDECR_PROP:      return &quot;incrdecr_prop&quot;;
-                case CATCH_SCOPE_PROP:   return &quot;catch_scope_prop&quot;;
-                case LABEL_ID_PROP:      return &quot;label_id_prop&quot;;
-                case MEMBER_TYPE_PROP:   return &quot;member_type_prop&quot;;
-                case NAME_PROP:          return &quot;name_prop&quot;;
-                case CONTROL_BLOCK_PROP: return &quot;control_block_prop&quot;;
-                case PARENTHESIZED_PROP: return &quot;parenthesized_prop&quot;;
-                case GENERATOR_END_PROP: return &quot;generator_end&quot;;
+                case FUNCTION_PROP:        return &quot;function&quot;;
+                case LOCAL_PROP:           return &quot;local&quot;;
+                case LOCAL_BLOCK_PROP:     return &quot;local_block&quot;;
+                case REGEXP_PROP:          return &quot;regexp&quot;;
+                case CASEARRAY_PROP:       return &quot;casearray&quot;;
+
+                case TARGETBLOCK_PROP:     return &quot;targetblock&quot;;
+                case VARIABLE_PROP:        return &quot;variable&quot;;
+                case ISNUMBER_PROP:        return &quot;isnumber&quot;;
+                case DIRECTCALL_PROP:      return &quot;directcall&quot;;
+
+                case SPECIALCALL_PROP:     return &quot;specialcall&quot;;
+                case SKIP_INDEXES_PROP:    return &quot;skip_indexes&quot;;
+                case OBJECT_IDS_PROP:      return &quot;object_ids_prop&quot;;
+                case INCRDECR_PROP:        return &quot;incrdecr_prop&quot;;
+                case CATCH_SCOPE_PROP:     return &quot;catch_scope_prop&quot;;
+                case LABEL_ID_PROP:        return &quot;label_id_prop&quot;;
+                case MEMBER_TYPE_PROP:     return &quot;member_type_prop&quot;;
+                case NAME_PROP:            return &quot;name_prop&quot;;
+                case CONTROL_BLOCK_PROP:   return &quot;control_block_prop&quot;;
+                case PARENTHESIZED_PROP:   return &quot;parenthesized_prop&quot;;
+                case GENERATOR_END_PROP:   return &quot;generator_end&quot;;
                 case DESTRUCTURING_ARRAY_LENGTH:
-                                         return &quot;destructuring_array_length&quot;;
-                case DESTRUCTURING_NAMES:return &quot;destructuring_names&quot;;
+                                           return &quot;destructuring_array_length&quot;;
+                case DESTRUCTURING_NAMES:  return &quot;destructuring_names&quot;;
+                case DESTRUCTURING_PARAMS: return &quot;destructuring_params&quot;;
 
                 default: Kit.codeBug();
             }
@@ -658,42 +515,50 @@ public class Node
         item.intValue = prop;
     }
 
+    /**
+     * Return the line number recorded for this node.
+     * @return the line number
+     */
     public int getLineno() {
         return lineno;
     }
 
+    public void setLineno(int lineno) {
+        this.lineno = lineno;
+    }
+
     /** Can only be called when &lt;tt&gt;getType() == Token.NUMBER&lt;/tt&gt; */
     public final double getDouble() {
-        return ((NumberNode)this).number;
+        return ((NumberLiteral)this).getNumber();
     }
 
     public final void setDouble(double number) {
-        ((NumberNode)this).number = number;
+        ((NumberLiteral)this).setNumber(number);
     }
 
     /** Can only be called when node has String context. */
     public final String getString() {
-        return ((StringNode)this).str;
+        return ((Name)this).getIdentifier();
     }
 
     /** Can only be called when node has String context. */
     public final void setString(String s) {
         if (s == null) Kit.codeBug();
-        ((StringNode)this).str = s;
+        ((Name)this).setIdentifier(s);
     }
-    
+
     /** Can only be called when node has String context. */
-    public final Scope getScope() {
-        return ((StringNode)this).scope;
+    public Scope getScope() {
+        return ((Name)this).getScope();
     }
 
     /** Can only be called when node has String context. */
-    public final void setScope(Scope s) {
+    public void setScope(Scope s) {
         if (s == null) Kit.codeBug();
-        if (!(this instanceof StringNode)) {
+        if (!(this instanceof Name)) {
             throw Kit.codeBug();
         }
-        ((StringNode)this).scope = s;
+        ((Name)this).setScope(s);
     }
 
     public static Node newTarget()
@@ -712,7 +577,7 @@ public class Node
         if (type != Token.TARGET  &amp;&amp; type != Token.YIELD) Kit.codeBug();
         putIntProp(LABEL_ID_PROP, labelId);
     }
-    
+
 
     /**
      * Does consistent-return analysis on the function body when strict mode is
@@ -720,7 +585,7 @@ public class Node
      *
      *   function (x) { return (x+1) }
      * is ok, but
-     *   function (x) { if (x &lt; 0) return (x+1); }
+     *   function (x) { if (x &amp;lt; 0) return (x+1); }
      * is not becuase the function can potentially return a value when the
      * condition is satisfied and if not, the function does not explicitly
      * return value.
@@ -729,7 +594,8 @@ public class Node
      * used in the same function. Warnings are not emitted if inconsistent
      * returns exist in code that can be statically shown to be unreachable.
      * Ex.
-     *   function (x) { while (true) { ... if (..) { return value } ... } }
+     * &lt;pre&gt;function (x) { while (true) { ... if (..) { return value } ... } }
+     * &lt;/pre&gt;
      * emits no warning. However if the loop had a break statement, then a
      * warning would be emitted.
      *
@@ -766,11 +632,11 @@ public class Node
      * }
      * Will be detected as (END_DROPS_OFF | END_RETURN_VALUE) by endCheck()
      */
-    static final int END_UNREACHED = 0;
-    static final int END_DROPS_OFF = 1;
-    static final int END_RETURNS = 2;
-    static final int END_RETURNS_VALUE = 4;
-    static final int END_YIELDS = 8;
+    public static final int END_UNREACHED = 0;
+    public static final int END_DROPS_OFF = 1;
+    public static final int END_RETURNS = 2;
+    public static final int END_RETURNS_VALUE = 4;
+    public static final int END_YIELDS = 8;
 
     /**
      * Checks that every return usage in a function body is consistent with the
@@ -820,26 +686,26 @@ public class Node
         int rv = END_UNREACHED;
 
         // examine the cases
-        for (n = first.next; n != null; n = n.next)
-        {
-            if (n.type == Token.CASE) {
-                rv |= ((Jump)n).target.endCheck();
-            } else
-                break;
-        }
-
-        // we don't care how the cases drop into each other
-        rv &amp;= ~END_DROPS_OFF;
-
-        // examine the default
-        n = ((Jump)this).getDefault();
-        if (n != null)
-            rv |= n.endCheck();
-        else
-            rv |= END_DROPS_OFF;
-
-        // remove the switch block
-        rv |= getIntProp(CONTROL_BLOCK_PROP, END_UNREACHED);
+//         for (n = first.next; n != null; n = n.next)
+//         {
+//             if (n.type == Token.CASE) {
+//                 rv |= ((Jump)n).target.endCheck();
+//             } else
+//                 break;
+//         }
+
+//         // we don't care how the cases drop into each other
+//         rv &amp;= ~END_DROPS_OFF;
+
+//         // examine the default
+//         n = ((Jump)this).getDefault();
+//         if (n != null)
+//             rv |= n.endCheck();
+//         else
+//             rv |= END_DROPS_OFF;
+
+//         // remove the switch block
+//         rv |= getIntProp(CONTROL_BLOCK_PROP, END_UNREACHED);
 
         return rv;
     }
@@ -857,34 +723,36 @@ public class Node
         Node n;
         int rv = END_UNREACHED;
 
-        // check the finally if it exists
-        n = ((Jump)this).getFinally();
-        if(n != null) {
-            rv = n.next.first.endCheck();
-        } else {
-            rv = END_DROPS_OFF;
-        }
-
-        // if the finally block always returns, then none of the returns
-        // in the try or catch blocks matter
-        if ((rv &amp; END_DROPS_OFF) != 0) {
-            rv &amp;= ~END_DROPS_OFF;
+        // a TryStatement isn't a jump - needs rewriting
 
-            // examine the try block
-            rv |= first.endCheck();
-
-            // check each catch block
-            n = ((Jump)this).target;
-            if (n != null)
-            {
-                // point to the first catch_scope
-                for (n = n.next.first; n != null; n = n.next.next)
-                {
-                    // check the block of user code in the catch_scope
-                    rv |= n.next.first.next.first.endCheck();
-                }
-            }
-        }
+        // check the finally if it exists
+//         n = ((Jump)this).getFinally();
+//         if(n != null) {
+//             rv = n.next.first.endCheck();
+//         } else {
+//             rv = END_DROPS_OFF;
+//         }
+
+//         // if the finally block always returns, then none of the returns
+//         // in the try or catch blocks matter
+//         if ((rv &amp; END_DROPS_OFF) != 0) {
+//             rv &amp;= ~END_DROPS_OFF;
+
+//             // examine the try block
+//             rv |= first.endCheck();
+
+//             // check each catch block
+//             n = ((Jump)this).target;
+//             if (n != null)
+//             {
+//                 // point to the first catch_scope
+//                 for (n = n.next.first; n != null; n = n.next.next)
+//                 {
+//                     // check the block of user code in the catch_scope
+//                     rv |= n.next.first.next.first.endCheck();
+//                 }
+//             }
+//         }
 
         return rv;
     }
@@ -895,10 +763,12 @@ public class Node
      * The only exception is a loop with a constant true condition. Code that
      * follows such a loop is examined only if one can statically determine
      * that there is a break out of the loop.
-     *  for(&lt;&gt; ; &lt;&gt;; &lt;&gt;) {}
-     *  for(&lt;&gt; in &lt;&gt; ) {}
-     *  while(&lt;&gt;) { }
-     *  do { } while(&lt;&gt;)
+     * &lt;pre&gt;
+     *  for(&amp;lt;&amp;gt; ; &amp;lt;&amp;gt;; &amp;lt;&amp;gt;) {}
+     *  for(&amp;lt;&amp;gt; in &amp;lt;&amp;gt; ) {}
+     *  while(&amp;lt;&amp;gt;) { }
+     *  do { } while(&amp;lt;&amp;gt;)
+     * &lt;/pre&gt;
      * @return logical OR of END_* flags
      */
     private int endCheckLoop()
@@ -930,7 +800,6 @@ public class Node
         return rv;
     }
 
-
     /**
      * A general block of code is examined statement by statement. If any
      * statement (even compound ones) returns in all branches, then subsequent
@@ -976,7 +845,7 @@ public class Node
      */
     private int endCheckBreak()
     {
-        Node n = ((Jump) this).jumpNode;
+        Node n = ((Jump) this).getJumpStatement();
         n.putIntProp(CONTROL_BLOCK_PROP, END_DROPS_OFF);
         return END_UNREACHED;
     }
@@ -1164,7 +1033,7 @@ public class Node
     {
         if (Token.printTrees) {
             sb.append(Token.name(type));
-            if (this instanceof StringNode) {
+            if (this instanceof Name) {
                 sb.append(' ');
                 sb.append(getString());
                 Scope scope = getScope();
@@ -1173,13 +1042,13 @@ public class Node
                     appendPrintId(scope, printIds, sb);
                     sb.append(&quot;]&quot;);
                 }
-            } else if (this instanceof Node.Scope) {
-                if (this instanceof ScriptOrFnNode) {
-                    ScriptOrFnNode sof = (ScriptOrFnNode)this;
+            } else if (this instanceof Scope) {
+                if (this instanceof ScriptNode) {
+                    ScriptNode sof = (ScriptNode)this;
                     if (this instanceof FunctionNode) {
                         FunctionNode fn = (FunctionNode)this;
                         sb.append(' ');
-                        sb.append(fn.getFunctionName());
+                        sb.append(fn.getName());
                     }
                     sb.append(&quot; [source name: &quot;);
                     sb.append(sof.getSourceName());
@@ -1192,12 +1061,12 @@ public class Node
                     sb.append(sof.getEndLineno());
                     sb.append(']');
                 }
-                if (((Node.Scope)this).symbolTable != null) {
+                if (((Scope)this).getSymbolTable() != null) {
                     sb.append(&quot; [scope &quot;);
                     appendPrintId(this, printIds, sb);
                     sb.append(&quot;: &quot;);
                     Iterator&lt;String&gt; iter =
-                        ((Node.Scope) this).symbolTable.keySet().iterator();
+                        ((Scope) this).getSymbolTable().keySet().iterator();
                     while (iter.hasNext()) {
                         sb.append(iter.next());
                         sb.append(&quot; &quot;);
@@ -1318,7 +1187,7 @@ public class Node
         }
     }
 
-    public String toStringTree(ScriptOrFnNode treeTop) {
+    public String toStringTree(ScriptNode treeTop) {
         if (Token.printTrees) {
             StringBuffer sb = new StringBuffer();
             toStringTreeHelper(treeTop, this, null, 0, sb);
@@ -1327,7 +1196,7 @@ public class Node
         return null;
     }
 
-    private static void toStringTreeHelper(ScriptOrFnNode treeTop, Node n,
+    private static void toStringTreeHelper(ScriptNode treeTop, Node n,
                                            ObjToIntMap printIds,
                                            int level, StringBuffer sb)
     {
@@ -1349,7 +1218,7 @@ public class Node
                     FunctionNode fn = treeTop.getFunctionNode(fnIndex);
                     toStringTreeHelper(fn, fn, null, level + 1, sb);
                 } else {
-                    toStringTreeHelper(treeTop, cursor, printIds, level + 1, sb);
+                    toStringTreeHelper(treeTop, cursor, printIds, level+1, sb);
                 }
             }
         }
@@ -1383,10 +1252,10 @@ public class Node
         }
     }
 
-    int type;              // type of the node; Token.NAME for example
-    Node next;             // next sibling
-    private Node first;    // first element of a linked list of children
-    private Node last;     // last element of a linked list of children
+    protected int type = Token.ERROR; // type of the node, e.g. Token.NAME
+    protected Node next;             // next sibling
+    protected Node first;    // first element of a linked list of children
+    protected Node last;     // last element of a linked list of children
     protected int lineno = -1;
 
     /**
@@ -1395,5 +1264,5 @@ public class Node
      * fast lookup. If this does not holds, propListHead can be replaced
      * by UintMap.
      */
-    private PropListItem propListHead;
+    protected PropListItem propListHead;
 }</diff>
      <filename>src/org/mozilla/javascript/Node.java</filename>
    </modified>
    <modified>
      <diff>@@ -42,6 +42,11 @@
 
 package org.mozilla.javascript;
 
+import org.mozilla.javascript.ast.FunctionNode;
+import org.mozilla.javascript.ast.Jump;
+import org.mozilla.javascript.ast.Scope;
+import org.mozilla.javascript.ast.ScriptNode;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -59,7 +64,7 @@ public class NodeTransformer
     {
     }
 
-    public final void transform(ScriptOrFnNode tree)
+    public final void transform(ScriptNode tree)
     {
         transformCompilationUnit(tree);
         for (int i = 0; i != tree.getFunctionCount(); ++i) {
@@ -68,7 +73,7 @@ public class NodeTransformer
         }
     }
 
-    private void transformCompilationUnit(ScriptOrFnNode tree)
+    private void transformCompilationUnit(ScriptNode tree)
     {
         loops = new ObjArray();
         loopEnds = new ObjArray();
@@ -82,13 +87,13 @@ public class NodeTransformer
         tree.flattenSymbolTable(!createScopeObjects);
 
         //uncomment to print tree before transformation
-        //if (Token.printTrees) System.out.println(tree.toStringTree(tree));
+        if (Token.printTrees) System.out.println(tree.toStringTree(tree));
         transformCompilationUnit_r(tree, tree, tree, createScopeObjects);
     }
 
-    private void transformCompilationUnit_r(final ScriptOrFnNode tree,
+    private void transformCompilationUnit_r(final ScriptNode tree,
                                             final Node parent,
-                                            Node.Scope scope,
+                                            Scope scope,
                                             boolean createScopeObjects)
     {
         Node node = null;
@@ -109,20 +114,20 @@ public class NodeTransformer
             if (createScopeObjects &amp;&amp;
                 (type == Token.BLOCK || type == Token.LOOP ||
                  type == Token.ARRAYCOMP) &amp;&amp;
-                (node instanceof Node.Scope))
+                (node instanceof Scope))
             {
-                Node.Scope newScope = (Node.Scope) node;
-                if (newScope.symbolTable != null) {
+                Scope newScope = (Scope) node;
+                if (newScope.getSymbolTable() != null) {
                     // transform to let statement so we get a with statement
                     // created to contain scoped let variables
                     Node let = new Node(type == Token.ARRAYCOMP ? Token.LETEXPR
                                                                 : Token.LET);
                     Node innerLet = new Node(Token.LET);
                     let.addChildToBack(innerLet);
-                    for (String name: newScope.symbolTable.keySet()) {
+                    for (String name: newScope.getSymbolTable().keySet()) {
                         innerLet.addChildToBack(Node.newString(Token.NAME, name));
                     }
-                    newScope.symbolTable = null; // so we don't transform again
+                    newScope.setSymbolTable(null); // so we don't transform again
                     Node oldNode = node;
                     node = replaceCurrent(parent, previous, node, let);
                     type = node.getType();
@@ -136,7 +141,7 @@ public class NodeTransformer
               case Token.SWITCH:
               case Token.LOOP:
                 loops.push(node);
-                loopEnds.push(((Node.Jump)node).target);
+                loopEnds.push(((Jump)node).target);
                 break;
 
               case Token.WITH:
@@ -152,7 +157,7 @@ public class NodeTransformer
 
               case Token.TRY:
               {
-                Node.Jump jump = (Node.Jump)node;
+                Jump jump = (Jump)node;
                 Node finallytarget = jump.getFinally();
                 if (finallytarget != null) {
                     hasFinally = true;
@@ -197,8 +202,8 @@ public class NodeTransformer
                     if (elemtype == Token.TRY || elemtype == Token.WITH) {
                         Node unwind;
                         if (elemtype == Token.TRY) {
-                            Node.Jump jsrnode = new Node.Jump(Token.JSR);
-                            Node jsrtarget = ((Node.Jump)n).getFinally();
+                            Jump jsrnode = new Jump(Token.JSR);
+                            Node jsrtarget = ((Jump)n).getFinally();
                             jsrnode.target = jsrtarget;
                             unwind = jsrnode;
                         } else {
@@ -235,8 +240,8 @@ public class NodeTransformer
               case Token.BREAK:
               case Token.CONTINUE:
               {
-                Node.Jump jump = (Node.Jump)node;
-                Node.Jump jumpStatement = jump.getJumpStatement();
+                Jump jump = (Jump)node;
+                Jump jumpStatement = jump.getJumpStatement();
                 if (jumpStatement == null) Kit.codeBug();
 
                 for (int i = loops.size(); ;) {
@@ -258,8 +263,8 @@ public class NodeTransformer
                         previous = addBeforeCurrent(parent, previous, node,
                                                     leave);
                     } else if (elemtype == Token.TRY) {
-                        Node.Jump tryNode = (Node.Jump)n;
-                        Node.Jump jsrFinally = new Node.Jump(Token.JSR);
+                        Jump tryNode = (Jump)n;
+                        Jump jsrFinally = new Jump(Token.JSR);
                         jsrFinally.target = tryNode.getFinally();
                         previous = addBeforeCurrent(parent, previous, node,
                                                     jsrFinally);
@@ -332,7 +337,7 @@ public class NodeTransformer
               }
 
               case Token.TYPEOFNAME: {
-                Node.Scope defining = scope.getDefiningScope(node.getString());
+                Scope defining = scope.getDefiningScope(node.getString());
                 if (defining != null) {
                     node.setScope(defining);
                 }
@@ -393,7 +398,7 @@ public class NodeTransformer
                     break; // already have a scope set
                 }
                 String name = nameSource.getString();
-                Node.Scope defining = scope.getDefiningScope(name);
+                Scope defining = scope.getDefiningScope(name);
                 if (defining != null) {
                     nameSource.setScope(defining);
                     if (type == Token.NAME) {
@@ -417,15 +422,15 @@ public class NodeTransformer
             }
 
             transformCompilationUnit_r(tree, node, 
-                node instanceof Node.Scope ? (Node.Scope)node : scope,
+                node instanceof Scope ? (Scope)node : scope,
                 createScopeObjects);
         }
     }
 
-    protected void visitNew(Node node, ScriptOrFnNode tree) {
+    protected void visitNew(Node node, ScriptNode tree) {
     }
 
-    protected void visitCall(Node node, ScriptOrFnNode tree) {
+    protected void visitCall(Node node, ScriptNode tree) {
     }
     
     protected Node visitLet(boolean createWith, Node parent, Node previous, 
@@ -502,13 +507,13 @@ public class NodeTransformer
                             body);
                     }
                     // We're removing the LETEXPR, so move the symbols
-                    Node.Scope.joinScopes((Node.Scope)current,
-                                          (Node.Scope)scopeNode);
+                    Scope.joinScopes((Scope)current,
+                                          (Scope)scopeNode);
                     current = c.getFirstChild(); // should be a NAME, checked below
                 }
                 if (current.getType() != Token.NAME) throw Kit.codeBug();
                 Node stringNode = Node.newString(current.getString());
-                stringNode.setScope((Node.Scope)scopeNode);
+                stringNode.setScope((Scope)scopeNode);
                 Node init = current.getFirstChild();
                 if (init == null) {
                     init = new Node(Token.VOID, Node.newNumber(0.0));
@@ -520,11 +525,21 @@ public class NodeTransformer
                 scopeNode.setType(Token.COMMA);
                 result.addChildToBack(scopeNode);
                 scopeNode.addChildToBack(body);
+                if (body instanceof Scope) {
+                    Scope scopeParent = ((Scope) body).getParentScope();
+                    ((Scope) body).setParentScope((Scope)scopeNode);
+                    ((Scope) scopeNode).setParentScope(scopeParent);
+                }
             } else {
                 result.addChildToBack(new Node(Token.EXPR_VOID, newVars));
                 scopeNode.setType(Token.BLOCK);
                 result.addChildToBack(scopeNode);
                 scopeNode.addChildrenToBack(body);
+                if (body instanceof Scope) {
+                    Scope scopeParent = ((Scope) body).getParentScope();
+                    ((Scope) body).setParentScope((Scope)scopeNode);
+                    ((Scope) scopeNode).setParentScope(scopeParent);
+                }
             }
         }
         return result;</diff>
      <filename>src/org/mozilla/javascript/NodeTransformer.java</filename>
    </modified>
    <modified>
      <diff>@@ -31,6 +31,7 @@
  *   Mike McCabe
  *   Milen Nankov
  *   Norris Boyd
+ *   Steve Yegge
  *
  * Alternatively, the contents of this file may be used under the terms of
  * the GNU General Public License Version 2 or later (the &quot;GPL&quot;), in which
@@ -46,63 +47,88 @@
 
 package org.mozilla.javascript;
 
-import java.io.Reader;
+import org.mozilla.javascript.ast.*;  // we use basically every class
+
+import java.io.BufferedReader;
 import java.io.IOException;
+import java.io.Reader;
+
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 /**
- * This class implements the JavaScript parser.
+ * This class implements the JavaScript parser.&lt;p&gt;
+ *
+ * It is based on the SpiderMonkey C source files jsparse.c and jsparse.h in the
+ * jsref package.&lt;p&gt;
  *
- * It is based on the C source files jsparse.c and jsparse.h
- * in the jsref package.
+ * The parser generates an {@link AstRoot} parse tree representing the source
+ * code.  No tree rewriting is permitted at this stage, so that the parse tree
+ * is a faithful representation of the source for frontend processing tools and
+ * IDEs.&lt;p&gt;
+ *
+ * This parser implementation is not intended to be reused after a parse
+ * finishes, and will throw an IllegalStateException() if invoked again.&lt;p&gt;
  *
  * @see TokenStream
  *
  * @author Mike McCabe
  * @author Brendan Eich
  */
-
 public class Parser
 {
+    /**
+     * Maximum number of allowed function or constructor arguments,
+     * to follow SpiderMonkey.
+     */
+    public static final int ARGC_LIMIT = 1 &lt;&lt; 16;
+
     // TokenInformation flags : currentFlaggedToken stores them together
     // with token type
     final static int
-        CLEAR_TI_MASK  = 0xFFFF,   // mask to clear token information bits
-        TI_AFTER_EOL   = 1 &lt;&lt; 16,  // first token of the source line
-        TI_CHECK_LABEL = 1 &lt;&lt; 17;  // indicates to check for label
+        CLEAR_TI_MASK    = 0xFFFF,  // mask to clear token information bits
+        TI_AFTER_EOL     = 1 &lt;&lt; 16, // first token of the source line
+        TI_CHECK_LABEL   = 1 &lt;&lt; 17; // indicates to check for label
 
     CompilerEnvirons compilerEnv;
     private ErrorReporter errorReporter;
+    private IdeErrorReporter errorCollector;
     private String sourceURI;
-    boolean calledByCompileFunction;
+    private char[] sourceChars;
+
+    boolean calledByCompileFunction;  // ugly - set directly by Context
+    private boolean parseFinished;  // set when finished to prevent reuse
 
     private TokenStream ts;
-    private int currentFlaggedToken;
+    private int currentFlaggedToken = Token.EOF;
+    private int currentToken;
     private int syntaxErrorCount;
 
-    private IRFactory nf;
+    private List&lt;Comment&gt; scannedComments;
 
-    private int nestingOfFunction;
+    protected int nestingOfFunction;
+    private LabeledStatement currentLabel;
 
-    private Decompiler decompiler;
-    private String encodedSource;
-
-// The following are per function variables and should be saved/restored
-// during function parsing.
-// XXX Move to separated class?
-    ScriptOrFnNode currentScriptOrFn;
-    Node.Scope currentScope;
-    private int nestingOfWith;
-    private Map&lt;String,Node&gt; labelSet; // map of label names into nodes
-    private ObjArray loopSet;
-    private ObjArray loopAndSwitchSet;
+    // The following are per function variables and should be saved/restored
+    // during function parsing.  See PerFunctionVariables class below.
+    ScriptNode currentScriptOrFn;
+    Scope currentScope;
+    int nestingOfWith;
     private int endFlags;
-// end of per function variables
-    
-    public int getCurrentLineNumber() {
-        return ts.getLineno();
-    }
+    private boolean inForInit;  // bound temporarily during forStatement()
+    private Map&lt;String,LabeledStatement&gt; labelSet;
+    private List&lt;Loop&gt; loopSet;
+    private List&lt;Jump&gt; loopAndSwitchSet;
+    // end of per function variables
+
+    // Lacking 2-token lookahead, labels become a problem.
+    // These vars store the token info of the last matched name,
+    // iff it wasn't the last matched token.
+    private int prevNameTokenStart;
+    private String prevNameTokenString = &quot;&quot;;
+    private int prevNameTokenLineno;
 
     // Exception to unwind
     private static class ParserException extends RuntimeException
@@ -110,75 +136,185 @@ public class Parser
         static final long serialVersionUID = 5882582646773765630L;
     }
 
-    public Parser(CompilerEnvirons compilerEnv, ErrorReporter errorReporter)
-    {
+    public Parser() {
+        this(new CompilerEnvirons());
+    }
+
+    public Parser(CompilerEnvirons compilerEnv) {
+        this(compilerEnv, compilerEnv.getErrorReporter());
+    }
+
+    public Parser(CompilerEnvirons compilerEnv, ErrorReporter errorReporter) {
         this.compilerEnv = compilerEnv;
         this.errorReporter = errorReporter;
+        if (errorReporter instanceof IdeErrorReporter) {
+            errorCollector = (IdeErrorReporter)errorReporter;
+        }
     }
 
-    protected Decompiler createDecompiler(CompilerEnvirons compilerEnv)
-    {
-        return new Decompiler();
+    // Add a strict warning on the last matched token.
+    void addStrictWarning(String messageId, String messageArg) {
+        int beg = -1, end = -1;
+        if (ts != null) {
+            beg = ts.tokenBeg;
+            end = ts.tokenEnd - ts.tokenBeg;
+        }
+        addStrictWarning(messageId, messageArg, beg, end);
     }
 
-    void addStrictWarning(String messageId, String messageArg)
-    {
+    void addStrictWarning(String messageId, String messageArg,
+                          int position, int length) {
         if (compilerEnv.isStrictMode())
-            addWarning(messageId, messageArg);
+            addWarning(messageId, messageArg, position, length);
     }
 
-    void addWarning(String messageId, String messageArg)
+    void addWarning(String messageId, String messageArg) {
+        int beg = -1, end = -1;
+        if (ts != null) {
+            beg = ts.tokenBeg;
+            end = ts.tokenEnd - ts.tokenBeg;
+        }
+        addWarning(messageId, messageArg, beg, end);
+    }
+
+    void addWarning(String messageId, int position, int length) {
+        addWarning(messageId, null, position, length);
+    }
+
+    void addWarning(String messageId, String messageArg,
+                    int position, int length)
     {
-        String message = ScriptRuntime.getMessage1(messageId, messageArg);
+        String message = lookupMessage(messageId, messageArg);
         if (compilerEnv.reportWarningAsError()) {
-            ++syntaxErrorCount;
-            errorReporter.error(message, sourceURI, ts.getLineno(),
-                                ts.getLine(), ts.getOffset());
-        } else
+            addError(messageId, messageArg, position, length);
+        } else if (errorCollector != null) {
+            int lineOffset = position - lineBeginningFor(position);
+            errorCollector.warning(message, sourceURI, position, length);
+        } else {
             errorReporter.warning(message, sourceURI, ts.getLineno(),
                                   ts.getLine(), ts.getOffset());
+        }
     }
 
-    void addError(String messageId)
-    {
-        ++syntaxErrorCount;
-        String message = ScriptRuntime.getMessage0(messageId);
-        errorReporter.error(message, sourceURI, ts.getLineno(),
-                            ts.getLine(), ts.getOffset());
+    void addError(String messageId) {
+        addError(messageId, ts.tokenBeg, ts.tokenEnd - ts.tokenBeg);
+    }
+
+    void addError(String messageId, int position, int length) {
+        addError(messageId, null, position, length);
+    }
+
+    void addError(String messageId, String messageArg) {
+        addError(messageId, messageArg, ts.tokenBeg,
+                 ts.tokenEnd - ts.tokenBeg);
     }
 
-    void addError(String messageId, String messageArg)
+    void addError(String messageId, String messageArg, int position, int length)
     {
         ++syntaxErrorCount;
-        String message = ScriptRuntime.getMessage1(messageId, messageArg);
-        errorReporter.error(message, sourceURI, ts.getLineno(),
-                            ts.getLine(), ts.getOffset());
+        String message = lookupMessage(messageId, messageArg);
+        if (errorCollector != null) {
+            int lineno = lineFor(position);
+            int lineOffset = position - lineBeginningFor(position);
+            errorCollector.error(message, sourceURI, position, length);
+        } else {
+            int lineno = 1, offset = 1;
+            String line = &quot;&quot;;
+            if (ts != null) {  // happens in some regression tests
+                lineno = ts.getLineno();
+                line = ts.getLine();
+                offset = ts.getOffset();
+            }
+            errorReporter.error(message, sourceURI, lineno, line, offset);
+        }
     }
 
-    RuntimeException reportError(String messageId)
+    String lookupMessage(String messageId) {
+        return lookupMessage(messageId, null);
+    }
+
+    String lookupMessage(String messageId, String messageArg) {
+        return messageArg == null
+            ? ScriptRuntime.getMessage0(messageId)
+            : ScriptRuntime.getMessage1(messageId, messageArg);
+    }
+
+    void reportError(String messageId) {
+        if (ts == null) {  // happens in some regression tests
+            reportError(messageId, 1, 1);
+        } else {
+            reportError(messageId, ts.tokenBeg, ts.tokenEnd - ts.tokenBeg);
+        }
+    }
+
+    void reportError(String messageId, int position, int length)
     {
-        addError(messageId);
+        addError(messageId, position, length);
+
+        if (!compilerEnv.recoverFromErrors()) {
+            throw new ParserException();
+        }
+    }
+
+    // Computes the absolute end offset of node N.
+    // Use with caution!  Assumes n.getPosition() is -absolute-, which
+    // is only true before the node is added to its parent.
+    private int getNodeEnd(AstNode n) {
+        return n.getPosition() + n.getLength();
+    }
 
-        // Throw a ParserException exception to unwind the recursive descent
-        // parse.
-        throw new ParserException();
+    private void recordComment() {
+        if (scannedComments == null) {
+            scannedComments = new ArrayList&lt;Comment&gt;();
+        }
+        scannedComments.add(new Comment(ts.tokenBeg,
+                                        ts.getTokenLength(),
+                                        ts.commentType));
     }
 
+    // Returns the next token without consuming it.
+    // If previous token was consumed, calls scanner to get new token.
+    // If previous token was -not- consumed, returns it (idempotent).
+    //
+    // This function will not return a newline (Token.EOL - instead, it
+    // gobbles newlines until it finds a non-newline token, and flags
+    // that token as appearing just after a newline.
+    //
+    // This function will also not return a Token.COMMENT.  Instead, it
+    // records comments in the scannedComments list.  If the token
+    // returned by this function immediately follows a jsdoc comment,
+    // the token is flagged as such.
+    //
+    // Note that this function always returned the un-flagged token!
+    // The flags, if any, are saved in currentFlaggedToken.
     private int peekToken()
         throws IOException
     {
-        int tt = currentFlaggedToken;
-        if (tt == Token.EOF) {
-            tt = ts.getToken();
+        // By far the most common case:  last token hasn't been consumed,
+        // so return already-peeked token.
+        if (currentFlaggedToken != Token.EOF) {
+            return currentToken;
+        }
+
+        int tt = ts.getToken();
+        boolean sawEOL = false;
+
+        // process comments and whitespace
+        while (tt == Token.EOL || tt == Token.COMMENT) {
             if (tt == Token.EOL) {
-                do {
-                    tt = ts.getToken();
-                } while (tt == Token.EOL);
-                tt |= TI_AFTER_EOL;
+                sawEOL = true;
+            } else {
+                sawEOL = false;
+                if (compilerEnv.isRecordingComments()) {
+                    recordComment();
+                }
             }
-            currentFlaggedToken = tt;
+            tt = ts.getToken();
         }
-        return tt &amp; CLEAR_TI_MASK;
+
+        currentToken = tt;
+        currentFlaggedToken = tt | (sawEOL ? TI_AFTER_EOL : 0);
+        return currentToken;  // return unflagged token
     }
 
     private int peekFlaggedToken()
@@ -188,8 +324,7 @@ public class Parser
         return currentFlaggedToken;
     }
 
-    private void consumeToken()
-    {
+    private void consumeToken() {
         currentFlaggedToken = Token.EOF;
     }
 
@@ -213,14 +348,18 @@ public class Parser
     private boolean matchToken(int toMatch)
         throws IOException
     {
-        int tt = peekToken();
-        if (tt != toMatch) {
+        if (peekToken() != toMatch) {
             return false;
         }
         consumeToken();
         return true;
     }
 
+    // Returns Token.EOL if the current token follows a newline, else returns
+    // the current token.  Used in situations where we don't consider certain
+    // token types valid if they are preceded by a newline.  One example is the
+    // postfix ++ or -- operator, which has to be on the same line as its
+    // operand.
     private int peekTokenOrEOL()
         throws IOException
     {
@@ -232,162 +371,148 @@ public class Parser
         return tt;
     }
 
-    private void setCheckForLabel()
+    private boolean mustMatchToken(int toMatch, String messageId)
+        throws IOException
     {
-        if ((currentFlaggedToken &amp; CLEAR_TI_MASK) != Token.NAME)
-            throw Kit.codeBug();
-        currentFlaggedToken |= TI_CHECK_LABEL;
+        return mustMatchToken(toMatch, messageId, ts.tokenBeg,
+                              ts.tokenEnd - ts.tokenBeg);
     }
 
-    private void mustMatchToken(int toMatch, String messageId)
-        throws IOException, ParserException
+    private boolean mustMatchToken(int toMatch, String msgId, int pos, int len)
+        throws IOException
     {
-        if (!matchToken(toMatch)) {
-            reportError(messageId);
+        if (matchToken(toMatch)) {
+            return true;
         }
+        reportError(msgId, pos, len);
+        return false;
     }
 
-    private void mustHaveXML()
-    {
+    private void mustHaveXML() {
         if (!compilerEnv.isXmlAvailable()) {
             reportError(&quot;msg.XML.not.available&quot;);
         }
     }
 
-    public String getEncodedSource()
-    {
-        return encodedSource;
-    }
-
-    public boolean eof()
-    {
+    public boolean eof() {
         return ts.eof();
     }
 
-    boolean insideFunction()
-    {
+    boolean insideFunction() {
         return nestingOfFunction != 0;
     }
-    
-    void pushScope(Node node) {
-        Node.Scope scopeNode = (Node.Scope) node;
-        if (scopeNode.getParentScope() != null) throw Kit.codeBug();
-        scopeNode.setParent(currentScope);
-        currentScope = scopeNode;
+
+    void pushScope(Scope scope) {
+        Scope parent = scope.getParentScope();
+        // During codegen, parent scope chain may already be initialized,
+        // in which case we just need to set currentScope variable.
+        if (parent != null) {
+            if (parent != currentScope)
+                codeBug();
+        } else {
+            currentScope.addChildScope(scope);
+        }
+        currentScope = scope;
     }
-    
+
     void popScope() {
         currentScope = currentScope.getParentScope();
     }
 
-    private Node enterLoop(Node loopLabel, boolean doPushScope)
-    {
-        Node loop = nf.createLoopNode(loopLabel, ts.getLineno());
-        if (loopSet == null) {
-            loopSet = new ObjArray();
-            if (loopAndSwitchSet == null) {
-                loopAndSwitchSet = new ObjArray();
-            }
+    private void enterLoop(Loop loop) {
+        if (loopSet == null)
+            loopSet = new ArrayList&lt;Loop&gt;();
+        loopSet.add(loop);
+        if (loopAndSwitchSet == null)
+            loopAndSwitchSet = new ArrayList&lt;Jump&gt;();
+        loopAndSwitchSet.add(loop);
+        pushScope(loop);
+        if (currentLabel != null) {
+            currentLabel.setStatement(loop);
+            currentLabel.getFirstLabel().setLoop(loop);
         }
-        loopSet.push(loop);
-        loopAndSwitchSet.push(loop);
-        if (doPushScope) {
-            pushScope(loop);
-        }
-        return loop;
     }
 
-    private void exitLoop(boolean doPopScope)
-    {
-        loopSet.pop();
-        loopAndSwitchSet.pop();
-        if (doPopScope) {
-            popScope();
-        }
+    private void exitLoop() {
+        loopSet.remove(loopSet.size() - 1);
+        loopAndSwitchSet.remove(loopAndSwitchSet.size() - 1);
+        popScope();
     }
 
-    private Node enterSwitch(Node switchSelector, int lineno)
-    {
-        Node switchNode = nf.createSwitch(switchSelector, lineno);
-        if (loopAndSwitchSet == null) {
-            loopAndSwitchSet = new ObjArray();
-        }
-        loopAndSwitchSet.push(switchNode);
-        return switchNode;
+    private void enterSwitch(SwitchStatement node) {
+        if (loopAndSwitchSet == null)
+            loopAndSwitchSet = new ArrayList&lt;Jump&gt;();
+        loopAndSwitchSet.add(node);
     }
 
-    private void exitSwitch()
-    {
-        loopAndSwitchSet.pop();
+    private void exitSwitch() {
+        loopAndSwitchSet.remove(loopAndSwitchSet.size() - 1);
     }
 
-    /*
-     * Build a parse tree from the given sourceString.
+    /**
+     * Builds a parse tree from the given source string.
      *
-     * @return an Object representing the parsed
-     * program.  If the parse fails, null will be returned.  (The
-     * parse failure will result in a call to the ErrorReporter from
-     * CompilerEnvirons.)
+     * @return an {@link AstRoot} object representing the parsed program.  If
+     * the parse fails, {@code null} will be returned.  (The parse failure will
+     * result in a call to the {@link ErrorReporter} from
+     * {@link CompilerEnvirons}.)
      */
-    public ScriptOrFnNode parse(String sourceString,
-                                String sourceURI, int lineno)
+    public AstRoot parse(String sourceString, String sourceURI, int lineno)
     {
+        if (parseFinished) throw new IllegalStateException(&quot;parser reused&quot;);
         this.sourceURI = sourceURI;
+        if (compilerEnv.isIdeMode()) {
+            this.sourceChars = sourceString.toCharArray();
+        }
         this.ts = new TokenStream(this, null, sourceString, lineno);
         try {
             return parse();
-        } catch (IOException ex) {
+        } catch (IOException iox) {
             // Should never happen
             throw new IllegalStateException();
+        } finally {
+            parseFinished = true;
         }
     }
 
-    /*
-     * Build a parse tree from the given sourceString.
-     *
-     * @return an Object representing the parsed
-     * program.  If the parse fails, null will be returned.  (The
-     * parse failure will result in a call to the ErrorReporter from
-     * CompilerEnvirons.)
+    /**
+     * Builds a parse tree from the given sourcereader.
+     * @see #parse(String,String,int)
+     * @throws IOException if the {@link Reader} encounters an error
      */
-    public ScriptOrFnNode parse(Reader sourceReader,
-                                String sourceURI, int lineno)
+    public AstRoot parse(Reader sourceReader, String sourceURI, int lineno)
         throws IOException
     {
-        this.sourceURI = sourceURI;
-        this.ts = new TokenStream(this, sourceReader, null, lineno);
-        return parse();
+        if (parseFinished) throw new IllegalStateException(&quot;parser reused&quot;);
+        if (compilerEnv.isIdeMode()) {
+            return parse(readFully(sourceReader), sourceURI, lineno);
+        }
+        try {
+            this.sourceURI = sourceURI;
+            ts = new TokenStream(this, sourceReader, null, lineno);
+            return parse();
+        } finally {
+            parseFinished = true;
+        }
     }
 
-    private ScriptOrFnNode parse()
-        throws IOException
+    private AstRoot parse() throws IOException
     {
-        this.decompiler = createDecompiler(compilerEnv);
-        this.nf = new IRFactory(this);
-        currentScriptOrFn = nf.createScript();
-        currentScope = currentScriptOrFn;
-        int sourceStartOffset = decompiler.getCurrentOffset();
-        this.encodedSource = null;
-        decompiler.addToken(Token.SCRIPT);
-
-        this.currentFlaggedToken = Token.EOF;
-        this.syntaxErrorCount = 0;
-
-        int baseLineno = ts.getLineno();  // line number where source starts
+        int pos = 0;
+        AstRoot root = new AstRoot(pos);
+        currentScope = currentScriptOrFn = root;
 
-        /* so we have something to add nodes to until
-         * we've collected all the source */
-        Node pn = nf.createLeaf(Token.BLOCK);
+        int baseLineno = ts.lineno;  // line number where source starts
+        int end = pos;  // in case source is empty
 
         try {
             for (;;) {
                 int tt = peekToken();
-
                 if (tt &lt;= Token.EOF) {
                     break;
                 }
 
-                Node n;
+                AstNode n;
                 if (tt == Token.FUNCTION) {
                     consumeToken();
                     try {
@@ -400,54 +525,53 @@ public class Parser
                 } else {
                     n = statement();
                 }
-                nf.addChildToBack(pn, n);
+                end = getNodeEnd(n);
+                root.addChildToBack(n);
+                n.setParent(root);
             }
         } catch (StackOverflowError ex) {
-            String msg = ScriptRuntime.getMessage0(
-                &quot;msg.too.deep.parser.recursion&quot;);
-            throw Context.reportRuntimeError(msg, sourceURI,
-                                             ts.getLineno(), null, 0);
+            String msg = lookupMessage(&quot;msg.too.deep.parser.recursion&quot;);
+            if (!compilerEnv.isIdeMode())
+                throw Context.reportRuntimeError(msg, sourceURI,
+                                                 ts.lineno, null, 0);
         }
 
         if (this.syntaxErrorCount != 0) {
             String msg = String.valueOf(this.syntaxErrorCount);
-            msg = ScriptRuntime.getMessage1(&quot;msg.got.syntax.errors&quot;, msg);
-            throw errorReporter.runtimeError(msg, sourceURI, baseLineno,
-                                             null, 0);
-        }
-
-        currentScriptOrFn.setSourceName(sourceURI);
-        currentScriptOrFn.setBaseLineno(baseLineno);
-        currentScriptOrFn.setEndLineno(ts.getLineno());
-
-        int sourceEndOffset = decompiler.getCurrentOffset();
-        currentScriptOrFn.setEncodedSourceBounds(sourceStartOffset,
-                                                 sourceEndOffset);
-
-        nf.initScript(currentScriptOrFn, pn);
-
-        if (compilerEnv.isGeneratingSource()) {
-            encodedSource = decompiler.getEncodedSource();
+            msg = lookupMessage(&quot;msg.got.syntax.errors&quot;, msg);
+            if (!compilerEnv.isIdeMode())
+                throw errorReporter.runtimeError(msg, sourceURI, baseLineno,
+                                                 null, 0);
+        }
+
+        // add comments to root in lexical order
+        if (scannedComments != null) {
+            // If we find a comment beyond end of our last statement or
+            // function, extend the root bounds to the end of that comment.
+            int last = scannedComments.size() - 1;
+            end = Math.max(end, getNodeEnd(scannedComments.get(last)));
+            for (Comment c : scannedComments) {
+                root.addComment(c);
+            }
         }
-        this.decompiler = null; // It helps GC
 
-        return currentScriptOrFn;
+        root.setLength(end - pos);
+        root.setSourceName(sourceURI);
+        root.setBaseLineno(baseLineno);
+        root.setEndLineno(ts.lineno);
+        return root;
     }
 
-    /*
-     * The C version of this function takes an argument list,
-     * which doesn't seem to be needed for tree generation...
-     * it'd only be useful for checking argument hiding, which
-     * I'm not doing anyway...
-     */
-    private Node parseFunctionBody()
+    private Block parseFunctionBody()
         throws IOException
     {
+        mustMatchToken(Token.LC, &quot;msg.no.brace.body&quot;);
         ++nestingOfFunction;
-        Node pn = nf.createBlock(ts.getLineno());
+        int pos = ts.tokenBeg;
+        Block pn = new Block(pos);  // starts at LC position
         try {
             bodyLoop: for (;;) {
-                Node n;
+                AstNode n;
                 int tt = peekToken();
                 switch (tt) {
                   case Token.ERROR:
@@ -463,7 +587,7 @@ public class Parser
                     n = statement();
                     break;
                 }
-                nf.addChildToBack(pn, n);
+                pn.addStatement(n);
             }
         } catch (ParserException e) {
             // Ignore it
@@ -471,36 +595,82 @@ public class Parser
             --nestingOfFunction;
         }
 
+        int end = ts.tokenEnd;
+        if (mustMatchToken(Token.RC, &quot;msg.no.brace.after.body&quot;))
+            end = ts.tokenEnd;
+        pn.setLength(end - pos);
         return pn;
     }
 
-    private Node function(int functionType)
-        throws IOException, ParserException
+    private void parseFunctionParams(FunctionNode fnNode)
+        throws IOException
     {
-        int syntheticType = functionType;
-        int baseLineno = ts.getLineno();  // line number where source starts
+        if (matchToken(Token.RP)) {
+            fnNode.setRp(ts.tokenBeg - fnNode.getPosition());
+            return;
+        }
+        // Would prefer not to call createDestructuringAssignment until codegen,
+        // but the symbol definitions have to happen now, before body is parsed.
+        Node destructuring = null;
+        do {
+            int tt = peekToken();
+            if (tt == Token.LB || tt == Token.LC) {
+                AstNode expr = primaryExpr();
+                markDestructuring(expr);
+                fnNode.addParam(expr);
+                // Destructuring assignment for parameters: add a dummy
+                // parameter name, and add a statement to the body to initialize
+                // variables from the destructuring assignment
+                if (destructuring == null) {
+                    destructuring = new Node(Token.COMMA);
+                }
+                String pname = currentScriptOrFn.getNextTempName();
+                defineSymbol(Token.LP, pname, false);
+                Node nameNode = createName(pname);
+                Node assign = createDestructuringAssignment(Token.VAR,
+                                                            expr, nameNode);
+                destructuring.addChildToBack(assign);
+            } else {
+                if (mustMatchToken(Token.NAME, &quot;msg.no.parm&quot;)) {
+                    fnNode.addParam(createNameNode());
+                    defineSymbol(Token.LP, ts.getString());
+                } else {
+                    fnNode.addParam(makeErrorNode());
+                }
+            }
+        } while (matchToken(Token.COMMA));
+
+        if (destructuring != null) {
+            fnNode.putProp(Node.DESTRUCTURING_PARAMS, destructuring);
+        }
+
+        if (mustMatchToken(Token.RP, &quot;msg.no.paren.after.parms&quot;)) {
+            fnNode.setRp(ts.tokenBeg - fnNode.getPosition());
+        }
+    }
+
+    private FunctionNode function(int type)
+        throws IOException
+    {
+        int syntheticType = type;
+        int baseLineno = ts.lineno;  // line number where source starts
+        int functionSourceStart = ts.tokenBeg;  // start of &quot;function&quot; kwd
+        Name name = null;
+        AstNode memberExprNode = null;
 
-        int functionSourceStart = decompiler.markFunctionStart(functionType);
-        String name;
-        Node memberExprNode = null;
         if (matchToken(Token.NAME)) {
-            name = ts.getString();
-            decompiler.addName(name);
+            name = createNameNode(true, Token.NAME);
             if (!matchToken(Token.LP)) {
                 if (compilerEnv.isAllowMemberExprAsFunctionName()) {
-                    // Extension to ECMA: if 'function &lt;name&gt;' does not follow
-                    // by '(', assume &lt;name&gt; starts memberExpr
-                    Node memberExprHead = nf.createName(name);
-                    name = &quot;&quot;;
+                    AstNode memberExprHead = name;
+                    name = null;
                     memberExprNode = memberExprTail(false, memberExprHead);
                 }
                 mustMatchToken(Token.LP, &quot;msg.no.paren.parms&quot;);
             }
         } else if (matchToken(Token.LP)) {
-            // Anonymous function
-            name = &quot;&quot;;
+            // Anonymous function:  leave name as null
         } else {
-            name = &quot;&quot;;
             if (compilerEnv.isAllowMemberExprAsFunctionName()) {
                 // Note that memberExpr can not start with '(' like
                 // in function (1+2).toString(), because 'function (' already
@@ -509,211 +679,166 @@ public class Parser
             }
             mustMatchToken(Token.LP, &quot;msg.no.paren.parms&quot;);
         }
+        int lpPos = currentToken == Token.LP ? ts.tokenBeg : -1;
 
         if (memberExprNode != null) {
             syntheticType = FunctionNode.FUNCTION_EXPRESSION;
-        } 
-        
-        if (syntheticType != FunctionNode.FUNCTION_EXPRESSION &amp;&amp; 
-            name.length() &gt; 0)
-        {
+        }
+
+        if (syntheticType != FunctionNode.FUNCTION_EXPRESSION
+            &amp;&amp; name != null &amp;&amp; name.length() &gt; 0) {
             // Function statements define a symbol in the enclosing scope
-            defineSymbol(Token.FUNCTION, false, name);
+            defineSymbol(Token.FUNCTION, name.getIdentifier());
         }
 
-        boolean nested = insideFunction();
+        FunctionNode fnNode = new FunctionNode(functionSourceStart, name);
+        fnNode.setFunctionType(type);
+        if (lpPos != -1)
+            fnNode.setLp(lpPos - functionSourceStart);
 
-        FunctionNode fnNode = nf.createFunction(name);
-        if (nested || nestingOfWith &gt; 0) {
+        if (insideFunction() || nestingOfWith &gt; 0) {
             // 1. Nested functions are not affected by the dynamic scope flag
-            // as dynamic scope is already a parent of their scope.
+            //    as dynamic scope is already a parent of their scope.
             // 2. Functions defined under the with statement also immune to
-            // this setup, in which case dynamic scope is ignored in favor
-            // of with object.
-            fnNode.itsIgnoreDynamicScope = true;
-        }
-        int functionIndex = currentScriptOrFn.addFunction(fnNode);
-
-        int functionSourceEnd;
-
-        ScriptOrFnNode savedScriptOrFn = currentScriptOrFn;
-        currentScriptOrFn = fnNode;
-        Node.Scope savedCurrentScope = currentScope;
-        currentScope = fnNode;
-        int savedNestingOfWith = nestingOfWith;
-        nestingOfWith = 0;
-        Map&lt;String,Node&gt; savedLabelSet = labelSet;
-        labelSet = null;
-        ObjArray savedLoopSet = loopSet;
-        loopSet = null;
-        ObjArray savedLoopAndSwitchSet = loopAndSwitchSet;
-        loopAndSwitchSet = null;
-        int savedFunctionEndFlags = endFlags;
-        endFlags = 0;
+            //    this setup, in which case dynamic scope is ignored in favor
+            //    of the with object.
+            fnNode.setIgnoreDynamicScope();
+        }
 
-        Node destructuring = null;
-        Node body;
+        PerFunctionVariables savedVars = new PerFunctionVariables(fnNode);
         try {
-            decompiler.addToken(Token.LP);
-            if (!matchToken(Token.RP)) {
-                boolean first = true;
-                do {
-                    if (!first)
-                        decompiler.addToken(Token.COMMA);
-                    first = false;
-                    int tt = peekToken();
-                    if (tt == Token.LB || tt == Token.LC) {
-                        // Destructuring assignment for parameters: add a 
-                        // dummy parameter name, and add a statement to the
-                        // body to initialize variables from the destructuring
-                        // assignment
-                        if (destructuring == null) {
-                            destructuring = new Node(Token.COMMA);
-                        }
-                        String parmName = currentScriptOrFn.getNextTempName();
-                        defineSymbol(Token.LP, false, parmName);
-                        destructuring.addChildToBack(
-                            nf.createDestructuringAssignment(Token.VAR,
-                                primaryExpr(), nf.createName(parmName)));
-                    } else {
-                        mustMatchToken(Token.NAME, &quot;msg.no.parm&quot;);
-                        String s = ts.getString();
-                        defineSymbol(Token.LP, false, s);
-                        decompiler.addName(s);
-                    }
-                } while (matchToken(Token.COMMA));
-
-                mustMatchToken(Token.RP, &quot;msg.no.paren.after.parms&quot;);
+            parseFunctionParams(fnNode);
+            fnNode.setBody(parseFunctionBody());
+            fnNode.setEncodedSourceBounds(functionSourceStart, ts.tokenEnd);
+            fnNode.setLength(ts.tokenEnd - functionSourceStart);
+
+            if (compilerEnv.isStrictMode()
+                &amp;&amp; !fnNode.getBody().hasConsistentReturnUsage()) {
+                String msg = (name != null &amp;&amp; name.length() &gt; 0)
+                           ? &quot;msg.no.return.value&quot;
+                           : &quot;msg.anon.no.return.value&quot;;
+                addStrictWarning(msg, name.getIdentifier());
             }
-            decompiler.addToken(Token.RP);
 
-            mustMatchToken(Token.LC, &quot;msg.no.brace.body&quot;);
-            decompiler.addEOL(Token.LC);
-            body = parseFunctionBody();
-            if (destructuring != null) {
-                body.addChildToFront(
-                    new Node(Token.EXPR_VOID, destructuring, ts.getLineno()));
+            // Function expressions define a name only in the body of the
+            // function, and only if not hidden by a parameter name
+            if (syntheticType == FunctionNode.FUNCTION_EXPRESSION
+                &amp;&amp; name != null &amp;&amp; name.length() &gt; 0
+                &amp;&amp; currentScope.getSymbol(name.getIdentifier()) == null) {
+                defineSymbol(Token.FUNCTION, name.getIdentifier());
             }
-            mustMatchToken(Token.RC, &quot;msg.no.brace.after.body&quot;);
+        } finally {
+            savedVars.restore();
+        }
 
-            if (compilerEnv.isStrictMode() &amp;&amp; !body.hasConsistentReturnUsage())
-            {
-              String msg = name.length() &gt; 0 ? &quot;msg.no.return.value&quot;
-                                             : &quot;msg.anon.no.return.value&quot;;
-              addStrictWarning(msg, name);
-            }
-            
-            if (syntheticType == FunctionNode.FUNCTION_EXPRESSION &amp;&amp;
-                name.length() &gt; 0 &amp;&amp; currentScope.getSymbol(name) == null) 
-            {
-                // Function expressions define a name only in the body of the 
-                // function, and only if not hidden by a parameter name
-                defineSymbol(Token.FUNCTION, false, name);
-            }
-            
-            decompiler.addToken(Token.RC);
-            functionSourceEnd = decompiler.markFunctionEnd(functionSourceStart);
-            if (functionType != FunctionNode.FUNCTION_EXPRESSION) {
-                // Add EOL only if function is not part of expression
-                // since it gets SEMI + EOL from Statement in that case
-                decompiler.addToken(Token.EOL);
+        if (memberExprNode != null) {
+            Kit.codeBug();
+            fnNode.setMemberExprNode(memberExprNode);  // rewrite later
+            /* old code:
+            if (memberExprNode != null) {
+                pn = nf.createAssignment(Token.ASSIGN, memberExprNode, pn);
+                if (functionType != FunctionNode.FUNCTION_EXPRESSION) {
+                    // XXX check JScript behavior: should it be createExprStatement?
+                    pn = nf.createExprStatementNoReturn(pn, baseLineno);
+                }
             }
-        }
-        finally {
-            endFlags = savedFunctionEndFlags;
-            loopAndSwitchSet = savedLoopAndSwitchSet;
-            loopSet = savedLoopSet;
-            labelSet = savedLabelSet;
-            nestingOfWith = savedNestingOfWith;
-            currentScriptOrFn = savedScriptOrFn;
-            currentScope = savedCurrentScope;
+            */
         }
 
-        fnNode.setEncodedSourceBounds(functionSourceStart, functionSourceEnd);
         fnNode.setSourceName(sourceURI);
         fnNode.setBaseLineno(baseLineno);
-        fnNode.setEndLineno(ts.getLineno());
+        fnNode.setEndLineno(ts.lineno);
 
-        Node pn = nf.initFunction(fnNode, functionIndex, body, syntheticType);
-        if (memberExprNode != null) {
-            pn = nf.createAssignment(Token.ASSIGN, memberExprNode, pn);
-            if (functionType != FunctionNode.FUNCTION_EXPRESSION) {
-                // XXX check JScript behavior: should it be createExprStatement?
-                pn = nf.createExprStatementNoReturn(pn, baseLineno);
-            }
+        // Set the parent scope.  Needed for finding undeclared vars.
+        // Have to wait until after parsing the function to set its parent
+        // scope, since defineSymbol needs the defining-scope check to stop
+        // at the function boundary when checking for redeclarations.
+        if (compilerEnv.isIdeMode()) {
+            fnNode.setParentScope(currentScope);
         }
-        return pn;
+        return fnNode;
     }
 
-    private Node statements(Node scope)
-        throws IOException
-    {
-        Node pn = scope != null ? scope : nf.createBlock(ts.getLineno());
+    // This function does not match the closing RC: the caller matches
+    // the RC so it can provide a suitable error message if not matched.
+    // This means it's up to the caller to set the length of the node to
+    // include the closing RC.  The node start pos is set to the
+    // absolute buffer start position, and the caller should fix it up
+    // to be relative to the parent node.  All children of this block
+    // node are given relative start positions and correct lengths.
+
+    private AstNode statements(AstNode parent) throws IOException {
+        if (currentToken != Token.LC  // assertion can be invalid in bad code
+            &amp;&amp; !compilerEnv.isIdeMode()) codeBug();
+        int pos = ts.tokenBeg;
+        AstNode block = parent != null ? parent : new Block(pos);
+        block.setLineno(ts.lineno);
 
         int tt;
         while ((tt = peekToken()) &gt; Token.EOF &amp;&amp; tt != Token.RC) {
-            nf.addChildToBack(pn, statement());
+            block.addChild(statement());
         }
-
-        return pn;
+        block.setLength(ts.tokenBeg - pos);
+        return block;
     }
 
-    private Node condition()
-        throws IOException, ParserException
-    {
-        mustMatchToken(Token.LP, &quot;msg.no.paren.cond&quot;);
-        decompiler.addToken(Token.LP);
-        Node pn = expr(false);
-        mustMatchToken(Token.RP, &quot;msg.no.paren.after.cond&quot;);
-        decompiler.addToken(Token.RP);
+    private AstNode statements() throws IOException {
+        return statements(null);
+    }
 
-        // Report strict warning on code like &quot;if (a = 7) ...&quot;. Suppress the
-        // warning if the condition is parenthesized, like &quot;if ((a = 7)) ...&quot;.
-        if (pn.getProp(Node.PARENTHESIZED_PROP) == null &amp;&amp;
-            (pn.getType() == Token.SETNAME || pn.getType() == Token.SETPROP ||
-             pn.getType() == Token.SETELEM))
-        {
-            addStrictWarning(&quot;msg.equal.as.assign&quot;, &quot;&quot;);
-        }
-        return pn;
+    private class ConditionData {
+        AstNode condition;
+        int lp = -1;
+        int rp = -1;
     }
 
-    // match a NAME; return null if no match.
-    private Node matchJumpLabelName()
-        throws IOException, ParserException
+    // parse and return a parenthesized expression
+    private ConditionData condition()
+        throws IOException
     {
-        Node label = null;
+        ConditionData data = new ConditionData();
 
-        int tt = peekTokenOrEOL();
-        if (tt == Token.NAME) {
-            consumeToken();
-            String name = ts.getString();
-            decompiler.addName(name);
-            if (labelSet != null) {
-                label = labelSet.get(name);
-            }
-            if (label == null) {
-                reportError(&quot;msg.undef.label&quot;);
-            }
-        }
+        if (mustMatchToken(Token.LP, &quot;msg.no.paren.cond&quot;))
+            data.lp = ts.tokenBeg;
 
-        return label;
+        data.condition = expr();
+
+        if (mustMatchToken(Token.RP, &quot;msg.no.paren.after.cond&quot;))
+            data.rp = ts.tokenBeg;
+
+        // Report strict warning on code like &quot;if (a = 7) ...&quot;. Suppress the
+        // warning if the condition is parenthesized, like &quot;if ((a = 7)) ...&quot;.
+        if (data.condition instanceof Assignment) {
+            addStrictWarning(&quot;msg.equal.as.assign&quot;, &quot;&quot;,
+                             data.condition.getPosition(),
+                             data.condition.getLength());
+        }
+        return data;
     }
 
-    private Node statement()
+    private AstNode statement()
         throws IOException
     {
+        int pos = ts.tokenBeg;
         try {
-            Node pn = statementHelper(null);
+            AstNode pn = statementHelper();
             if (pn != null) {
-                if (compilerEnv.isStrictMode() &amp;&amp; !pn.hasSideEffects())
-                    addStrictWarning(&quot;msg.no.side.effects&quot;, &quot;&quot;);
+                if (compilerEnv.isStrictMode() &amp;&amp; !pn.hasSideEffects()) {
+                    int beg = pn.getPosition();
+                    beg = Math.max(beg, lineBeginningFor(beg));
+                    addStrictWarning(pn instanceof EmptyExpression
+                                     ? &quot;msg.extra.trailing.semi&quot;
+                                     : &quot;msg.no.side.effects&quot;,
+                                     &quot;&quot;, beg, nodeEnd(pn) - beg);
+                }
                 return pn;
             }
-        } catch (ParserException e) { }
+        } catch (ParserException e) {
+            // an ErrorNode was added to the ErrorReporter
+        }
 
-        // skip to end of statement
-        int lineno = ts.getLineno();
+        // error:  skip ahead to a probable statement boundary
+        int lineno = ts.lineno;
         guessingStatementEnd: for (;;) {
             int tt = peekTokenOrEOL();
             consumeToken();
@@ -725,550 +850,652 @@ public class Parser
                 break guessingStatementEnd;
             }
         }
-        return nf.createExprStatement(nf.createName(&quot;error&quot;), lineno);
+        // We don't make error nodes explicitly part of the tree;
+        // they get added to the ErrorReporter.  May need to do
+        // something different here.
+        return new EmptyExpression(pos, ts.tokenBeg - pos);
     }
 
-    private Node statementHelper(Node statementLabel)
-        throws IOException, ParserException
+    private AstNode statementHelper()
+        throws IOException
     {
-        Node pn = null;
-        int tt = peekToken();
+        // If the statement is set, then it's been told its label by now.
+        if (currentLabel != null &amp;&amp; currentLabel.getStatement() != null)
+            currentLabel = null;
+
+        AstNode pn = null;
+        int tt = peekToken(), pos = ts.tokenBeg;
 
         switch (tt) {
-          case Token.IF: {
-            consumeToken();
+          case Token.IF:
+              return ifStatement();
 
-            decompiler.addToken(Token.IF);
-            int lineno = ts.getLineno();
-            Node cond = condition();
-            decompiler.addEOL(Token.LC);
-            Node ifTrue = statement();
-            Node ifFalse = null;
-            if (matchToken(Token.ELSE)) {
-                decompiler.addToken(Token.RC);
-                decompiler.addToken(Token.ELSE);
-                decompiler.addEOL(Token.LC);
-                ifFalse = statement();
-            }
-            decompiler.addEOL(Token.RC);
-            pn = nf.createIf(cond, ifTrue, ifFalse, lineno);
-            return pn;
-          }
+          case Token.SWITCH:
+              return switchStatement();
 
-          case Token.SWITCH: {
-            consumeToken();
+          case Token.WHILE:
+              return whileLoop();
 
-            decompiler.addToken(Token.SWITCH);
-            int lineno = ts.getLineno();
-            mustMatchToken(Token.LP, &quot;msg.no.paren.switch&quot;);
-            decompiler.addToken(Token.LP);
-            pn = enterSwitch(expr(false), lineno);
-            try {
-                mustMatchToken(Token.RP, &quot;msg.no.paren.after.switch&quot;);
-                decompiler.addToken(Token.RP);
-                mustMatchToken(Token.LC, &quot;msg.no.brace.switch&quot;);
-                decompiler.addEOL(Token.LC);
-
-                boolean hasDefault = false;
-                switchLoop: for (;;) {
-                    tt = nextToken();
-                    Node caseExpression;
-                    switch (tt) {
-                      case Token.RC:
+          case Token.DO:
+              return doLoop();
+
+          case Token.FOR:
+              return forLoop();
+
+          case Token.TRY:
+              return tryStatement();
+
+          case Token.THROW:
+              pn = throwStatement();
+              break;
+
+          case Token.BREAK:
+              pn = breakStatement();
+              break;
+
+          case Token.CONTINUE:
+              pn = continueStatement();
+              break;
+
+          case Token.WITH:
+              return withStatement();
+
+          case Token.CONST:
+          case Token.VAR:
+              consumeToken();
+              int lineno = ts.lineno;
+              pn = variables(currentToken, ts.tokenBeg);
+              pn.setLineno(lineno);
+              break;
+
+          case Token.LET:
+              pn = letStatement();
+              if (pn instanceof VariableDeclaration
+                  &amp;&amp; peekToken() == Token.SEMI)
+                  break;
+              return pn;
+
+          case Token.RETURN:
+          case Token.YIELD:
+              pn = returnOrYield(tt, false);
+              break;
+
+          case Token.DEBUGGER:
+              consumeToken();
+              pn = new KeywordLiteral(ts.tokenBeg,
+                                      ts.tokenEnd - ts.tokenBeg, tt);
+              pn.setLineno(ts.lineno);
+              break;
+
+          case Token.LC:
+              return block();
+
+          case Token.ERROR:
+              consumeToken();
+              return makeErrorNode();
+
+          case Token.SEMI:
+              consumeToken();
+              pos = ts.tokenBeg;
+              pn = new EmptyExpression(pos, ts.tokenEnd - pos);
+              pn.setLineno(ts.lineno);
+              return pn;
+
+          case Token.FUNCTION:
+              consumeToken();
+              return function(FunctionNode.FUNCTION_EXPRESSION_STATEMENT);
+
+          case Token.DEFAULT :
+              pn = defaultXmlNamespace();
+              break;
+
+          case Token.NAME:
+              pn = nameOrLabel();
+              if (pn instanceof ExpressionStatement)
+                  break;
+              return pn;  // LabeledStatement
+
+          default:
+              pn = new ExpressionStatement(expr(), !insideFunction());
+              break;
+        }
+
+        autoInsertSemicolon(pn);
+        return pn;
+    }
+
+    private void autoInsertSemicolon(AstNode pn) throws IOException {
+        int ttFlagged = peekFlaggedToken();
+        int pos = pn.getPosition();
+        switch (ttFlagged &amp; CLEAR_TI_MASK) {
+          case Token.SEMI:
+              // Consume ';' as a part of expression
+              consumeToken();
+              // extend the node bounds to include the semicolon.
+              pn.setLength(ts.tokenEnd - pos);
+              break;
+          case Token.ERROR:
+          case Token.EOF:
+          case Token.RC:
+              // Autoinsert ;
+              warnMissingSemi(pos, nodeEnd(pn));
+              break;
+          default:
+              if ((ttFlagged &amp; TI_AFTER_EOL) == 0) {
+                  // Report error if no EOL or autoinsert ; otherwise
+                  reportError(&quot;msg.no.semi.stmt&quot;);
+              } else {
+                  warnMissingSemi(pos, nodeEnd(pn));
+              }
+              break;
+        }
+    }
+
+    private IfStatement ifStatement()
+        throws IOException
+    {
+        if (currentToken != Token.IF) codeBug();
+        consumeToken();
+        int pos = ts.tokenBeg, lineno = ts.lineno, elsePos = -1;
+        ConditionData data = condition();
+        AstNode ifTrue = statement(), ifFalse = null;
+        if (matchToken(Token.ELSE)) {
+            elsePos = ts.tokenBeg - pos;
+            ifFalse = statement();
+        }
+        int end = getNodeEnd(ifFalse != null ? ifFalse : ifTrue);
+        IfStatement pn = new IfStatement(pos, end - pos);
+        pn.setCondition(data.condition);
+        pn.setParens(data.lp - pos, data.rp - pos);
+        pn.setThenPart(ifTrue);
+        pn.setElsePart(ifFalse);
+        pn.setElsePosition(elsePos);
+        pn.setLineno(lineno);
+        return pn;
+    }
+
+    private SwitchStatement switchStatement()
+        throws IOException
+    {
+        if (currentToken != Token.SWITCH) codeBug();
+        consumeToken();
+        int pos = ts.tokenBeg;
+
+        SwitchStatement pn = new SwitchStatement(pos);
+        if (mustMatchToken(Token.LP, &quot;msg.no.paren.switch&quot;))
+            pn.setLp(ts.tokenBeg - pos);
+        pn.setLineno(ts.lineno);
+
+        AstNode discriminant = expr();
+        pn.setExpression(discriminant);
+        enterSwitch(pn);
+
+        try {
+            if (mustMatchToken(Token.RP, &quot;msg.no.paren.after.switch&quot;))
+                pn.setRp(ts.tokenBeg - pos);
+
+            mustMatchToken(Token.LC, &quot;msg.no.brace.switch&quot;);
+
+            boolean hasDefault = false;
+            int tt;
+            switchLoop: for (;;) {
+                tt = nextToken();
+                int casePos = ts.tokenBeg;
+                AstNode caseExpression = null;
+                switch (tt) {
+                    case Token.RC:
+                        pn.setLength(ts.tokenEnd - pos);
                         break switchLoop;
 
-                      case Token.CASE:
-                        decompiler.addToken(Token.CASE);
-                        caseExpression = expr(false);
+                    case Token.CASE:
+                        caseExpression = expr();
                         mustMatchToken(Token.COLON, &quot;msg.no.colon.case&quot;);
-                        decompiler.addEOL(Token.COLON);
                         break;
 
-                      case Token.DEFAULT:
+                    case Token.DEFAULT:
                         if (hasDefault) {
                             reportError(&quot;msg.double.switch.default&quot;);
                         }
-                        decompiler.addToken(Token.DEFAULT);
                         hasDefault = true;
                         caseExpression = null;
                         mustMatchToken(Token.COLON, &quot;msg.no.colon.case&quot;);
-                        decompiler.addEOL(Token.COLON);
                         break;
 
-                      default:
+                    default:
                         reportError(&quot;msg.bad.switch&quot;);
                         break switchLoop;
-                    }
-
-                    Node block = nf.createLeaf(Token.BLOCK);
-                    while ((tt = peekToken()) != Token.RC
-                           &amp;&amp; tt != Token.CASE
-                           &amp;&amp; tt != Token.DEFAULT
-                           &amp;&amp; tt != Token.EOF)
-                    {
-                        nf.addChildToBack(block, statement());
-                    }
-
-                    // caseExpression == null =&gt; add default label
-                    nf.addSwitchCase(pn, caseExpression, block);
                 }
-                decompiler.addEOL(Token.RC);
-                nf.closeSwitch(pn);
-            } finally {
-                exitSwitch();
-            }
-            return pn;
-          }
 
-          case Token.WHILE: {
-            consumeToken();
-            decompiler.addToken(Token.WHILE);
-
-            Node loop = enterLoop(statementLabel, true);
-            try {
-                Node cond = condition();
-                decompiler.addEOL(Token.LC);
-                Node body = statement();
-                decompiler.addEOL(Token.RC);
-                pn = nf.createWhile(loop, cond, body);
-            } finally {
-                exitLoop(true);
-            }
-            return pn;
-          }
-
-          case Token.DO: {
-            consumeToken();
-            decompiler.addToken(Token.DO);
-            decompiler.addEOL(Token.LC);
+                SwitchCase caseNode = new SwitchCase(casePos);
+                caseNode.setExpression(caseExpression);
+                caseNode.setLength(ts.tokenEnd - pos);  // include colon
 
-            Node loop = enterLoop(statementLabel, true);
-            try {
-                Node body = statement();
-                decompiler.addToken(Token.RC);
-                mustMatchToken(Token.WHILE, &quot;msg.no.while.do&quot;);
-                decompiler.addToken(Token.WHILE);
-                Node cond = condition();
-                pn = nf.createDoWhile(loop, body, cond);
-            } finally {
-                exitLoop(true);
+                while ((tt = peekToken()) != Token.RC
+                       &amp;&amp; tt != Token.CASE
+                       &amp;&amp; tt != Token.DEFAULT
+                       &amp;&amp; tt != Token.EOF)
+                {
+                    caseNode.addStatement(statement());  // updates length
+                }
+                pn.addCase(caseNode);
             }
-            // Always auto-insert semicolon to follow SpiderMonkey:
-            // It is required by ECMAScript but is ignored by the rest of
-            // world, see bug 238945
-            matchToken(Token.SEMI);
-            decompiler.addEOL(Token.SEMI);
-            return pn;
-          }
+        } finally {
+            exitSwitch();
+        }
+        return pn;
+    }
 
-          case Token.FOR: {
-            consumeToken();
-            boolean isForEach = false;
-            decompiler.addToken(Token.FOR);
+    private WhileLoop whileLoop()
+        throws IOException
+    {
+        if (currentToken != Token.WHILE) codeBug();
+        consumeToken();
+        int pos = ts.tokenBeg;
+        WhileLoop pn = new WhileLoop(pos);
+        pn.setLineno(ts.lineno);
+        enterLoop(pn);
+        try {
+            ConditionData data = condition();
+            pn.setCondition(data.condition);
+            pn.setParens(data.lp - pos, data.rp - pos);
+            AstNode body = statement();
+            pn.setLength(getNodeEnd(body) - pos);
+            pn.setBody(body);
+        } finally {
+            exitLoop();
+        }
+        return pn;
+    }
 
-            Node loop = enterLoop(statementLabel, true);
-            try {
-                Node init;  // Node init is also foo in 'foo in object'
-                Node cond;  // Node cond is also object in 'foo in object'
-                Node incr = null;
-                Node body;
-                int declType = -1;
-
-                // See if this is a for each () instead of just a for ()
-                if (matchToken(Token.NAME)) {
-                    decompiler.addName(ts.getString());
-                    if (ts.getString().equals(&quot;each&quot;)) {
-                        isForEach = true;
-                    } else {
-                        reportError(&quot;msg.no.paren.for&quot;);
-                    }
-                }
+    private DoLoop doLoop()
+        throws IOException
+    {
+        if (currentToken != Token.DO) codeBug();
+        consumeToken();
+        int pos = ts.tokenBeg, end;
+        DoLoop pn = new DoLoop(pos);
+        pn.setLineno(ts.lineno);
+        enterLoop(pn);
+        try {
+            AstNode body = statement();
+            mustMatchToken(Token.WHILE, &quot;msg.no.while.do&quot;);
+            pn.setWhilePosition(ts.tokenBeg - pos);
+            ConditionData data = condition();
+            pn.setCondition(data.condition);
+            pn.setParens(data.lp - pos, data.rp - pos);
+            end = getNodeEnd(body);
+            pn.setBody(body);
+        } finally {
+            exitLoop();
+        }
+        // Always auto-insert semicolon to follow SpiderMonkey:
+        // It is required by ECMAScript but is ignored by the rest of
+        // world, see bug 238945
+        if (matchToken(Token.SEMI)) {
+            end = ts.tokenEnd;
+        }
+        pn.setLength(end - pos);
+        return pn;
+    }
 
-                mustMatchToken(Token.LP, &quot;msg.no.paren.for&quot;);
-                decompiler.addToken(Token.LP);
-                tt = peekToken();
-                if (tt == Token.SEMI) {
-                    init = nf.createLeaf(Token.EMPTY);
+    private Loop forLoop()
+        throws IOException
+    {
+        if (currentToken != Token.FOR) codeBug();
+        consumeToken();
+        int forPos = ts.tokenBeg, lineno = ts.lineno;
+        boolean isForEach = false, isForIn = false;
+        int eachPos = -1, inPos = -1, lp = -1, rp = -1;
+        AstNode init = null;  // init is also foo in 'foo in object'
+        AstNode cond = null;  // cond is also object in 'foo in object'
+        AstNode incr = null;
+        Loop pn = null;
+
+        Scope tempScope = new Scope();
+        pushScope(tempScope);  // decide below what AST class to use
+        try {
+            // See if this is a for each () instead of just a for ()
+            if (matchToken(Token.NAME)) {
+                if (&quot;each&quot;.equals(ts.getString())) {
+                    isForEach = true;
+                    eachPos = ts.tokenBeg - forPos;
                 } else {
-                    if (tt == Token.VAR || tt == Token.LET) {
-                        // set init to a var list or initial
-                        consumeToken();    // consume the token
-                        decompiler.addToken(tt);
-                        init = variables(true, tt);
-                        declType = tt;
-                    }
-                    else {
-                        init = expr(true);
-                    }
+                    reportError(&quot;msg.no.paren.for&quot;);
                 }
+            }
 
-                if (matchToken(Token.IN)) {
-                    decompiler.addToken(Token.IN);
-                    // 'cond' is the object over which we're iterating
-                    cond = expr(false);
-                } else {  // ordinary for loop
-                    mustMatchToken(Token.SEMI, &quot;msg.no.semi.for&quot;);
-                    decompiler.addToken(Token.SEMI);
-                    if (peekToken() == Token.SEMI) {
-                        // no loop condition
-                        cond = nf.createLeaf(Token.EMPTY);
-                    } else {
-                        cond = expr(false);
-                    }
+            if (mustMatchToken(Token.LP, &quot;msg.no.paren.for&quot;))
+                lp = ts.tokenBeg - forPos;
+            int tt = peekToken();
 
-                    mustMatchToken(Token.SEMI, &quot;msg.no.semi.for.cond&quot;);
-                    decompiler.addToken(Token.SEMI);
-                    if (peekToken() == Token.RP) {
-                        incr = nf.createLeaf(Token.EMPTY);
-                    } else {
-                        incr = expr(false);
-                    }
+            init = forLoopInit(tt);
+
+            if (matchToken(Token.IN)) {
+                isForIn = true;
+                inPos = ts.tokenBeg - forPos;
+                cond = expr();  // object over which we're iterating
+            } else {  // ordinary for-loop
+                mustMatchToken(Token.SEMI, &quot;msg.no.semi.for&quot;);
+                if (peekToken() == Token.SEMI) {
+                    // no loop condition
+                    cond = new EmptyExpression(ts.tokenBeg, 1);
+                } else {
+                    cond = expr();
                 }
 
-                mustMatchToken(Token.RP, &quot;msg.no.paren.for.ctrl&quot;);
-                decompiler.addToken(Token.RP);
-                decompiler.addEOL(Token.LC);
-                body = statement();
-                decompiler.addEOL(Token.RC);
-
-                if (incr == null) {
-                    // cond could be null if 'in obj' got eaten
-                    // by the init node.
-                    pn = nf.createForIn(declType, loop, init, cond, body,
-                                        isForEach);
+                mustMatchToken(Token.SEMI, &quot;msg.no.semi.for.cond&quot;);
+                int tmpPos = ts.tokenEnd;
+                if (peekToken() == Token.RP) {
+                    incr = new EmptyExpression(tmpPos, 1);
                 } else {
-                    pn = nf.createFor(loop, init, cond, incr, body);
+                    incr = expr();
                 }
-            } finally {
-                exitLoop(true);
             }
-            return pn;
-          }
 
-          case Token.TRY: {
-            consumeToken();
-            int lineno = ts.getLineno();
+            if (mustMatchToken(Token.RP, &quot;msg.no.paren.for.ctrl&quot;))
+                rp = ts.tokenBeg - forPos;
 
-            Node tryblock;
-            Node catchblocks = null;
-            Node finallyblock = null;
-
-            decompiler.addToken(Token.TRY);
-            if (peekToken() != Token.LC) {
-                reportError(&quot;msg.no.brace.try&quot;);
-            }
-            decompiler.addEOL(Token.LC);
-            tryblock = statement();
-            decompiler.addEOL(Token.RC);
-
-            catchblocks = nf.createLeaf(Token.BLOCK);
-
-            boolean sawDefaultCatch = false;
-            int peek = peekToken();
-            if (peek == Token.CATCH) {
-                while (matchToken(Token.CATCH)) {
-                    if (sawDefaultCatch) {
-                        reportError(&quot;msg.catch.unreachable&quot;);
+            if (isForIn) {
+                ForInLoop fis = new ForInLoop(forPos);
+                if (init instanceof VariableDeclaration) {
+                    // check that there was only one variable given
+                    if (((VariableDeclaration)init).getVariables().size() &gt; 1) {
+                        reportError(&quot;msg.mult.index&quot;);
                     }
-                    decompiler.addToken(Token.CATCH);
-                    mustMatchToken(Token.LP, &quot;msg.no.paren.catch&quot;);
-                    decompiler.addToken(Token.LP);
-
-                    mustMatchToken(Token.NAME, &quot;msg.bad.catchcond&quot;);
-                    String varName = ts.getString();
-                    decompiler.addName(varName);
-
-                    Node catchCond = null;
-                    if (matchToken(Token.IF)) {
-                        decompiler.addToken(Token.IF);
-                        catchCond = expr(false);
-                    } else {
-                        sawDefaultCatch = true;
-                    }
-
-                    mustMatchToken(Token.RP, &quot;msg.bad.catchcond&quot;);
-                    decompiler.addToken(Token.RP);
-                    mustMatchToken(Token.LC, &quot;msg.no.brace.catchblock&quot;);
-                    decompiler.addEOL(Token.LC);
-
-                    nf.addChildToBack(catchblocks,
-                        nf.createCatch(varName, catchCond,
-                                       statements(null),
-                                       ts.getLineno()));
-
-                    mustMatchToken(Token.RC, &quot;msg.no.brace.after.body&quot;);
-                    decompiler.addEOL(Token.RC);
                 }
-            } else if (peek != Token.FINALLY) {
-                mustMatchToken(Token.FINALLY, &quot;msg.try.no.catchfinally&quot;);
-            }
-
-            if (matchToken(Token.FINALLY)) {
-                decompiler.addToken(Token.FINALLY);
-                decompiler.addEOL(Token.LC);
-                finallyblock = statement();
-                decompiler.addEOL(Token.RC);
+                fis.setIterator(init);
+                fis.setIteratedObject(cond);
+                fis.setInPosition(inPos);
+                fis.setIsForEach(isForEach);
+                fis.setEachPosition(eachPos);
+                pn = fis;
+            } else {
+                ForLoop fl = new ForLoop(forPos);
+                fl.setInitializer(init);
+                fl.setCondition(cond);
+                fl.setIncrement(incr);
+                pn = fl;
             }
 
-            pn = nf.createTryCatchFinally(tryblock, catchblocks,
-                                          finallyblock, lineno);
+            // replace temp scope with the new loop object
+            currentScope.replaceWith(pn);
+            popScope();
 
-            return pn;
-          }
+            // We have to parse the body -after- creating the loop node,
+            // so that the loop node appears in the loopSet, allowing
+            // break/continue statements to find the enclosing loop.
+            enterLoop(pn);
+            try {
+                AstNode body = statement();
+                pn.setLength(getNodeEnd(body) - forPos);
+                pn.setBody(body);
+            } finally {
+                exitLoop();
+            }
 
-          case Token.THROW: {
-            consumeToken();
-            if (peekTokenOrEOL() == Token.EOL) {
-                // ECMAScript does not allow new lines before throw expression,
-                // see bug 256617
-                reportError(&quot;msg.bad.throw.eol&quot;);
+        } finally {
+            if (currentScope == tempScope) {
+                popScope();
             }
+        }
+        pn.setParens(lp, rp);
+        pn.setLineno(lineno);
+        return pn;
+    }
 
-            int lineno = ts.getLineno();
-            decompiler.addToken(Token.THROW);
-            pn = nf.createThrow(expr(false), lineno);
-            break;
-          }
+    private AstNode forLoopInit(int tt) throws IOException {
+        try {
+            inForInit = true;  // checked by variables() and relExpr()
+            AstNode init = null;
+            if (tt == Token.SEMI) {
+                init = new EmptyExpression(ts.tokenBeg, 1);
+            } else if (tt == Token.VAR || tt == Token.LET) {
+                consumeToken();
+                init = variables(tt, ts.tokenBeg);
+            } else {
+                init = expr();
+                markDestructuring(init);
+            }
+            return init;
+        } finally {
+            inForInit = false;
+        }
+    }
 
-          case Token.BREAK: {
-            consumeToken();
-            int lineno = ts.getLineno();
+    private TryStatement tryStatement()
+        throws IOException
+    {
+        if (currentToken != Token.TRY) codeBug();
+        consumeToken();
+        int tryPos = ts.tokenBeg, lineno = ts.lineno, finallyPos = -1;
+        if (peekToken() != Token.LC) {
+            reportError(&quot;msg.no.brace.try&quot;);
+        }
+        AstNode tryBlock = statement();
+        int tryEnd = getNodeEnd(tryBlock);
 
-            decompiler.addToken(Token.BREAK);
+        List&lt;CatchClause&gt; clauses = null;
 
-            // matchJumpLabelName only matches if there is one
-            Node breakStatement = matchJumpLabelName();
-            if (breakStatement == null) {
-                if (loopAndSwitchSet == null || loopAndSwitchSet.size() == 0) {
-                    reportError(&quot;msg.bad.break&quot;);
-                    return null;
+        boolean sawDefaultCatch = false;
+        int peek = peekToken();
+        if (peek == Token.CATCH) {
+            while (matchToken(Token.CATCH)) {
+                if (sawDefaultCatch) {
+                    reportError(&quot;msg.catch.unreachable&quot;);
                 }
-                breakStatement = (Node)loopAndSwitchSet.peek();
-            }
-            pn = nf.createBreak(breakStatement, lineno);
-            break;
-          }
-
-          case Token.CONTINUE: {
-            consumeToken();
-            int lineno = ts.getLineno();
+                int catchPos = ts.tokenBeg, lp = -1, rp = -1, guardPos = -1;
+                if (mustMatchToken(Token.LP, &quot;msg.no.paren.catch&quot;))
+                    lp = ts.tokenBeg;
 
-            decompiler.addToken(Token.CONTINUE);
+                mustMatchToken(Token.NAME, &quot;msg.bad.catchcond&quot;);
+                Name varName = createNameNode();
 
-            Node loop;
-            // matchJumpLabelName only matches if there is one
-            Node label = matchJumpLabelName();
-            if (label == null) {
-                if (loopSet == null || loopSet.size() == 0) {
-                    reportError(&quot;msg.continue.outside&quot;);
-                    return null;
-                }
-                loop = (Node)loopSet.peek();
-            } else {
-                loop = nf.getLabelLoop(label);
-                if (loop == null) {
-                    reportError(&quot;msg.continue.nonloop&quot;);
-                    return null;
+                AstNode catchCond = null;
+                if (matchToken(Token.IF)) {
+                    guardPos = ts.tokenBeg;
+                    catchCond = expr();
+                } else {
+                    sawDefaultCatch = true;
                 }
-            }
-            pn = nf.createContinue(loop, lineno);
-            break;
-          }
 
-          case Token.WITH: {
-            consumeToken();
-
-            decompiler.addToken(Token.WITH);
-            int lineno = ts.getLineno();
-            mustMatchToken(Token.LP, &quot;msg.no.paren.with&quot;);
-            decompiler.addToken(Token.LP);
-            Node obj = expr(false);
-            mustMatchToken(Token.RP, &quot;msg.no.paren.after.with&quot;);
-            decompiler.addToken(Token.RP);
-            decompiler.addEOL(Token.LC);
-
-            ++nestingOfWith;
-            Node body;
-            try {
-                body = statement();
-            } finally {
-                --nestingOfWith;
+                if (mustMatchToken(Token.RP, &quot;msg.bad.catchcond&quot;))
+                    rp = ts.tokenBeg;
+                mustMatchToken(Token.LC, &quot;msg.no.brace.catchblock&quot;);
+
+                Block catchBlock = (Block)statements();
+                tryEnd = getNodeEnd(catchBlock);
+                CatchClause catchNode = new CatchClause(catchPos);
+                catchNode.setVarName(varName);
+                catchNode.setCatchCondition(catchCond);
+                catchNode.setBody(catchBlock);
+                if (guardPos != -1) {
+                    catchNode.setIfPosition(guardPos - catchPos);
+                }
+                catchNode.setParens(lp, rp);
+
+                if (mustMatchToken(Token.RC, &quot;msg.no.brace.after.body&quot;))
+                    tryEnd = ts.tokenEnd;
+                catchNode.setLength(tryEnd - catchPos);
+                if (clauses == null)
+                    clauses = new ArrayList&lt;CatchClause&gt;();
+                clauses.add(catchNode);
             }
+        } else if (peek != Token.FINALLY) {
+            mustMatchToken(Token.FINALLY, &quot;msg.try.no.catchfinally&quot;);
+        }
 
-            decompiler.addEOL(Token.RC);
+        AstNode finallyBlock = null;
+        if (matchToken(Token.FINALLY)) {
+            finallyPos = ts.tokenBeg;
+            finallyBlock = statement();
+            tryEnd = getNodeEnd(finallyBlock);
+        }
 
-            pn = nf.createWith(obj, body, lineno);
-            return pn;
-          }
+        TryStatement pn = new TryStatement(tryPos, tryEnd - tryPos);
+        pn.setTryBlock(tryBlock);
+        pn.setCatchClauses(clauses);
+        pn.setFinallyBlock(finallyBlock);
+        pn.setFinallyPosition(finallyPos);
+        pn.setLineno(lineno);
+        return pn;
+    }
 
-          case Token.CONST:
-          case Token.VAR: {
-            consumeToken();
-            decompiler.addToken(tt);
-            pn = variables(false, tt);
-            break;
-          }
-          
-          case Token.LET: {
-            consumeToken();
-            decompiler.addToken(Token.LET);
-            if (peekToken() == Token.LP) {
-                return let(true);
-            } else {
-                pn = variables(false, tt);
-                if (peekToken() == Token.SEMI)
-                    break;
-                return pn;
-            }
-          }
+    private ThrowStatement throwStatement()
+        throws IOException
+    {
+        if (currentToken != Token.THROW) codeBug();
+        consumeToken();
+        int pos = ts.tokenBeg, lineno = ts.lineno;
+        if (peekTokenOrEOL() == Token.EOL) {
+            // ECMAScript does not allow new lines before throw expression,
+            // see bug 256617
+            reportError(&quot;msg.bad.throw.eol&quot;);
+        }
+        AstNode expr = expr();
+        ThrowStatement pn = new ThrowStatement(pos, getNodeEnd(expr), expr);
+        pn.setLineno(lineno);
+        return pn;
+    }
 
-          case Token.RETURN: 
-          case Token.YIELD: {
-            pn = returnOrYield(tt, false);
-            break;
-          }
+    // If we match a NAME, consume the token and return the statement
+    // with that label.  If the name does not match an existing label,
+    // reports an error.  Returns the labeled statement node, or null if
+    // the peeked token was not a name.  Side effect:  sets scanner token
+    // information for the label identifier (tokenBeg, tokenEnd, etc.)
 
-          case Token.DEBUGGER:
-            consumeToken();
-            decompiler.addToken(Token.DEBUGGER);
-            pn = nf.createDebugger(ts.getLineno());
-            break;
+    private LabeledStatement matchJumpLabelName()
+        throws IOException
+    {
+        LabeledStatement label = null;
 
-          case Token.LC:
+        if (peekTokenOrEOL() == Token.NAME) {
             consumeToken();
-            if (statementLabel != null) {
-                decompiler.addToken(Token.LC);
+            if (labelSet != null) {
+                label = labelSet.get(ts.getString());
             }
-            Node scope = nf.createScopeNode(Token.BLOCK, ts.getLineno());
-            pushScope(scope);
-            try {
-                statements(scope);
-                mustMatchToken(Token.RC, &quot;msg.no.brace.block&quot;);
-                if (statementLabel != null) {
-                    decompiler.addEOL(Token.RC);
-                }
-                return scope;
-            } finally {
-                popScope();
+            if (label == null) {
+                reportError(&quot;msg.undef.label&quot;);
             }
+        }
 
-          case Token.ERROR:
-            // Fall thru, to have a node for error recovery to work on
-          case Token.SEMI:
-            consumeToken();
-            pn = nf.createLeaf(Token.EMPTY);
-            return pn;
-
-          case Token.FUNCTION: {
-            consumeToken();
-            pn = function(FunctionNode.FUNCTION_EXPRESSION_STATEMENT);
-            return pn;
-          }
+        return label;
+    }
 
-          case Token.DEFAULT :
-            consumeToken();
-            mustHaveXML();
+    private BreakStatement breakStatement()
+        throws IOException
+    {
+        if (currentToken != Token.BREAK) codeBug();
+        consumeToken();
+        int lineno = ts.lineno, pos = ts.tokenBeg, end = ts.tokenEnd;
+        Name breakLabel = null;
+        if (peekTokenOrEOL() == Token.NAME) {
+            breakLabel = createNameNode();
+            end = getNodeEnd(breakLabel);
+        }
+
+        // matchJumpLabelName only matches if there is one
+        LabeledStatement labels = matchJumpLabelName();
+        // always use first label as target
+        Jump breakTarget = labels == null ? null : labels.getFirstLabel();
+
+        if (breakTarget == null &amp;&amp; breakLabel == null) {
+            if (loopAndSwitchSet == null || loopAndSwitchSet.size() == 0) {
+                if (breakLabel == null) {
+                    reportError(&quot;msg.bad.break&quot;, pos, end - pos);
+                }
+            } else {
+                breakTarget = loopAndSwitchSet.get(loopAndSwitchSet.size() - 1);
+            }
+        }
 
-            decompiler.addToken(Token.DEFAULT);
-            int nsLine = ts.getLineno();
+        BreakStatement pn = new BreakStatement(pos, end - pos);
+        pn.setBreakLabel(breakLabel);
+        // can be null if it's a bad break in error-recovery mode
+        if (breakTarget != null)
+            pn.setBreakTarget(breakTarget);
+        pn.setLineno(lineno);
+        return pn;
+    }
 
-            if (!(matchToken(Token.NAME)
-                  &amp;&amp; ts.getString().equals(&quot;xml&quot;)))
-            {
-                reportError(&quot;msg.bad.namespace&quot;);
+    private ContinueStatement continueStatement()
+        throws IOException
+    {
+        if (currentToken != Token.CONTINUE) codeBug();
+        consumeToken();
+        int lineno = ts.lineno, pos = ts.tokenBeg, end = ts.tokenEnd;
+        Name label = null;
+        if (peekTokenOrEOL() == Token.NAME) {
+            label = createNameNode();
+            end = getNodeEnd(label);
+        }
+
+        // matchJumpLabelName only matches if there is one
+        LabeledStatement labels = matchJumpLabelName();
+        Loop target = null;
+        if (labels == null &amp;&amp; label == null) {
+            if (loopSet == null || loopSet.size() == 0) {
+                reportError(&quot;msg.continue.outside&quot;);
+            } else {
+                target = loopSet.get(loopSet.size() - 1);
             }
-            decompiler.addName(&quot; xml&quot;);
-
-            if (!(matchToken(Token.NAME)
-                  &amp;&amp; ts.getString().equals(&quot;namespace&quot;)))
-            {
-                reportError(&quot;msg.bad.namespace&quot;);
+        } else {
+            if (labels == null || !(labels.getStatement() instanceof Loop)) {
+                reportError(&quot;msg.continue.nonloop&quot;, pos, end - pos);
             }
-            decompiler.addName(&quot; namespace&quot;);
+            target = labels == null ? null : (Loop)labels.getStatement();
+        }
 
-            if (!matchToken(Token.ASSIGN)) {
-                reportError(&quot;msg.bad.namespace&quot;);
-            }
-            decompiler.addToken(Token.ASSIGN);
+        ContinueStatement pn = new ContinueStatement(pos, end - pos);
+        if (target != null)  // can be null in error-recovery mode
+            pn.setTarget(target);
+        pn.setLabel(label);
+        pn.setLineno(lineno);
+        return pn;
+    }
 
-            Node expr = expr(false);
-            pn = nf.createDefaultNamespace(expr, nsLine);
-            break;
+    private WithStatement withStatement()
+        throws IOException
+    {
+        if (currentToken != Token.WITH) codeBug();
+        consumeToken();
+        int lineno = ts.lineno, pos = ts.tokenBeg, lp = -1, rp = -1;
+        if (mustMatchToken(Token.LP, &quot;msg.no.paren.with&quot;))
+            lp = ts.tokenBeg;
 
-          case Token.NAME: {
-            int lineno = ts.getLineno();
-            String name = ts.getString();
-            setCheckForLabel();
-            pn = expr(false);
-            if (pn.getType() != Token.LABEL) {
-                pn = nf.createExprStatement(pn, lineno);
-            } else {
-                // Parsed the label: push back token should be
-                // colon that primaryExpr left untouched.
-                if (peekToken() != Token.COLON) Kit.codeBug();
-                consumeToken();
-                // depend on decompiling lookahead to guess that that
-                // last name was a label.
-                decompiler.addName(name);
-                decompiler.addEOL(Token.COLON);
-
-                if (labelSet == null) {
-                    labelSet = new HashMap&lt;String,Node&gt;();
-                } else if (labelSet.containsKey(name)) {
-                    reportError(&quot;msg.dup.label&quot;);
-                }
+        AstNode obj = expr();
 
-                boolean firstLabel;
-                if (statementLabel == null) {
-                    firstLabel = true;
-                    statementLabel = pn;
-                } else {
-                    // Discard multiple label nodes and use only
-                    // the first: it allows to simplify IRFactory
-                    firstLabel = false;
-                }
-                labelSet.put(name, statementLabel);
-                try {
-                    pn = statementHelper(statementLabel);
-                } finally {
-                    labelSet.remove(name);
-                }
-                if (firstLabel) {
-                    pn = nf.createLabeledStatement(statementLabel, pn);
-                }
-                return pn;
-            }
-            break;
-          }
+        if (mustMatchToken(Token.RP, &quot;msg.no.paren.after.with&quot;))
+            rp = ts.tokenBeg;
 
-          default: {
-            int lineno = ts.getLineno();
-            pn = expr(false);
-            pn = nf.createExprStatement(pn, lineno);
-            break;
-          }
+        ++nestingOfWith;
+        AstNode body;
+        try {
+            body = statement();
+        } finally {
+            --nestingOfWith;
         }
 
-        int ttFlagged = peekFlaggedToken();
-        switch (ttFlagged &amp; CLEAR_TI_MASK) {
-          case Token.SEMI:
-            // Consume ';' as a part of expression
-            consumeToken();
-            break;
-          case Token.ERROR:
-          case Token.EOF:
-          case Token.RC:
-            // Autoinsert ;
-            break;
-          default:
-            if ((ttFlagged &amp; TI_AFTER_EOL) == 0) {
-                // Report error if no EOL or autoinsert ; otherwise
-                reportError(&quot;msg.no.semi.stmt&quot;);
-            }
-            break;
-        }
-        decompiler.addEOL(Token.SEMI);
+        WithStatement pn = new WithStatement(pos, getNodeEnd(body) - pos);
+        pn.setExpression(obj);
+        pn.setStatement(body);
+        pn.setParens(lp, rp);
+        pn.setLineno(lineno);
+        return pn;
+    }
 
+    private AstNode letStatement()
+        throws IOException
+    {
+        if (currentToken != Token.LET) codeBug();
+        consumeToken();
+        int lineno = ts.lineno, pos = ts.tokenBeg;
+        AstNode pn;
+        if (peekToken() == Token.LP) {
+            pn = let(true, pos);
+        } else {
+            pn = variables(Token.LET, pos);  // else, e.g.: let x=6, y=7;
+        }
+        pn.setLineno(lineno);
         return pn;
     }
 
@@ -1277,393 +1504,511 @@ public class Parser
      * @param before bits before change
      * @param after bits after change
      * @param mask mask for bits
-     * @return true if all the bits in the mask are set in &quot;after&quot; but not 
-     *              &quot;before&quot;
+     * @return {@code true} if all the bits in the mask are set in &quot;after&quot;
+     *          but not in &quot;before&quot;
      */
-    private static final boolean nowAllSet(int before, int after, int mask)
-    {
+    private static final boolean nowAllSet(int before, int after, int mask) {
         return ((before &amp; mask) != mask) &amp;&amp; ((after &amp; mask) == mask);
     }
-    
-    private Node returnOrYield(int tt, boolean exprContext)
-        throws IOException, ParserException
+
+    private AstNode returnOrYield(int tt, boolean exprContext)
+        throws IOException
     {
         if (!insideFunction()) {
             reportError(tt == Token.RETURN ? &quot;msg.bad.return&quot;
                                            : &quot;msg.bad.yield&quot;);
         }
         consumeToken();
-        decompiler.addToken(tt);
-        int lineno = ts.getLineno();
+        int lineno = ts.lineno, pos = ts.tokenBeg, end = ts.tokenEnd;
 
-        Node e;
-        /* This is ugly, but we don't want to require a semicolon. */
+        AstNode e = null;
+        // This is ugly, but we don't want to require a semicolon.
         switch (peekTokenOrEOL()) {
-          case Token.SEMI:
-          case Token.RC:
-          case Token.EOF:
-          case Token.EOL:
-          case Token.ERROR:
-          case Token.RB:
-          case Token.RP:
-          case Token.YIELD:
-            e = null;
+          case Token.SEMI: case Token.RC:  case Token.RB:    case Token.RP:
+          case Token.EOF:  case Token.EOL: case Token.ERROR: case Token.YIELD:
             break;
           default:
-            e = expr(false);
-            break;
+            e = expr();
+            end = getNodeEnd(e);
         }
 
         int before = endFlags;
-        Node ret;
+        AstNode ret;
 
         if (tt == Token.RETURN) {
-            if (e == null ) {
-                endFlags |= Node.END_RETURNS;
-            } else {
-                endFlags |= Node.END_RETURNS_VALUE;
-            }
-            ret = nf.createReturn(e, lineno);
-            
+            endFlags |= e == null ? Block.END_RETURNS : Block.END_RETURNS_VALUE;
+            ret = new ReturnStatement(pos, end - pos, e);
+
             // see if we need a strict mode warning
-            if (nowAllSet(before, endFlags, 
-                          Node.END_RETURNS|Node.END_RETURNS_VALUE))
-            {
-                addStrictWarning(&quot;msg.return.inconsistent&quot;, &quot;&quot;);
-            }
+            if (nowAllSet(before, endFlags,
+                          Block.END_RETURNS|Block.END_RETURNS_VALUE))
+                addStrictWarning(&quot;msg.return.inconsistent&quot;, &quot;&quot;, pos, end - pos);
         } else {
-            endFlags |= Node.END_YIELDS;
-            ret = nf.createYield(e, lineno);
-            if (!exprContext)
-                ret = new Node(Token.EXPR_VOID, ret, lineno);
+            if (!insideFunction())
+                reportError(&quot;msg.bad.yield&quot;);
+            endFlags |= Block.END_YIELDS;
+            ret = new Yield(pos, end - pos, e);
+            setRequiresActivation();
+            setIsGenerator();
+            if (!exprContext) {
+                ret = new ExpressionStatement(ret);
+            }
         }
 
         // see if we are mixing yields and value returns.
-        if (nowAllSet(before, endFlags, 
-                      Node.END_YIELDS|Node.END_RETURNS_VALUE))
-        {
-            String name = ((FunctionNode)currentScriptOrFn).getFunctionName();
-            if (name.length() == 0)
+        if (insideFunction()
+            &amp;&amp; nowAllSet(before, endFlags,
+                         Block.END_YIELDS|Block.END_RETURNS_VALUE)) {
+            Name name = ((FunctionNode)currentScriptOrFn).getFunctionName();
+            if (name == null || name.length() == 0)
                 addError(&quot;msg.anon.generator.returns&quot;, &quot;&quot;);
             else
-                addError(&quot;msg.generator.returns&quot;, name);
+                addError(&quot;msg.generator.returns&quot;, name.getIdentifier());
         }
 
+        ret.setLineno(lineno);
         return ret;
     }
 
+    private AstNode block()
+        throws IOException
+    {
+        if (currentToken != Token.LC) codeBug();
+        consumeToken();
+        int pos = ts.tokenBeg;
+        Scope block = new Scope(pos);
+        block.setLineno(ts.lineno);
+        pushScope(block);
+        try {
+            statements(block);
+            mustMatchToken(Token.RC, &quot;msg.no.brace.block&quot;);
+            block.setLength(ts.tokenEnd - pos);
+            return block;
+        } finally {
+            popScope();
+        }
+    }
+
+    private AstNode defaultXmlNamespace()
+        throws IOException
+    {
+        if (currentToken != Token.DEFAULT) codeBug();
+        consumeToken();
+        mustHaveXML();
+        setRequiresActivation();
+        int lineno = ts.lineno, pos = ts.tokenBeg;
+
+        if (!(matchToken(Token.NAME) &amp;&amp; &quot;xml&quot;.equals(ts.getString()))) {
+            reportError(&quot;msg.bad.namespace&quot;);
+        }
+        if (!(matchToken(Token.NAME) &amp;&amp; &quot;namespace&quot;.equals(ts.getString()))) {
+            reportError(&quot;msg.bad.namespace&quot;);
+        }
+        if (!matchToken(Token.ASSIGN)) {
+            reportError(&quot;msg.bad.namespace&quot;);
+        }
+
+        AstNode e = expr();
+        UnaryExpression dxmln = new UnaryExpression(pos, getNodeEnd(e) - pos);
+        dxmln.setOperator(Token.DEFAULTNAMESPACE);
+        dxmln.setOperand(e);
+        dxmln.setLineno(lineno);
+
+        ExpressionStatement es = new ExpressionStatement(dxmln, true);
+        return es;
+    }
+
+    private void recordLabel(Label label, LabeledStatement bundle)
+        throws IOException
+    {
+        // current token should be colon that primaryExpr left untouched
+        if (peekToken() != Token.COLON) codeBug();
+        consumeToken();
+        String name = label.getName();
+        if (labelSet == null) {
+            labelSet = new HashMap&lt;String,LabeledStatement&gt;();
+        } else {
+            LabeledStatement ls = labelSet.get(name);
+            if (ls != null) {
+                if (compilerEnv.isIdeMode()) {
+                    Label dup = ls.getLabelByName(name);
+                    reportError(&quot;msg.dup.label&quot;,
+                                dup.getAbsolutePosition(), dup.getLength());
+                }
+                reportError(&quot;msg.dup.label&quot;,
+                            label.getPosition(), label.getLength());
+            }
+        }
+        bundle.addLabel(label);
+        labelSet.put(name, bundle);
+    }
+
+    /**
+     * Found a name in a statement context.  If it's a label, we gather
+     * up any following labels and the next non-label statement into a
+     * {@link LabeledStatement} &quot;bundle&quot; and return that.  Otherwise we parse
+     * an expression and return it wrapped in an {@link ExpressionStatement}.
+     */
+    private AstNode nameOrLabel()
+        throws IOException
+    {
+        if (currentToken != Token.NAME) throw codeBug();
+        int lineno = ts.lineno, pos = ts.tokenBeg, end = ts.tokenEnd;
+
+        // set check for label and call down to primaryExpr
+        currentFlaggedToken |= TI_CHECK_LABEL;
+        AstNode expr = expr();
+
+        if (expr.getType() != Token.LABEL) {
+            return new ExpressionStatement(expr, !insideFunction());
+        }
+
+        LabeledStatement bundle = new LabeledStatement(pos);
+        recordLabel((Label)expr, bundle);
+
+        // look for more labels
+        AstNode stmt = null;
+        while (peekToken() == Token.NAME) {
+            currentFlaggedToken |= TI_CHECK_LABEL;
+            expr = expr();
+            if (expr.getType() != Token.LABEL) {
+                stmt = new ExpressionStatement(expr, !insideFunction());
+                autoInsertSemicolon(stmt);
+                break;
+            }
+            recordLabel((Label)expr, bundle);
+        }
+
+        // no more labels; now parse the labeled statement
+        try {
+            currentLabel = bundle;
+            if (stmt == null) {
+                stmt = statementHelper();
+            }
+        } finally {
+            currentLabel = null;
+            // remove the labels for this statement from the global set
+            for (Label lb : bundle.getLabels()) {
+                labelSet.remove(lb.getName());
+            }
+        }
+
+        bundle.setLength(getNodeEnd(stmt) - pos);
+        bundle.setStatement(stmt);
+        return bundle;
+    }
+
     /**
      * Parse a 'var' or 'const' statement, or a 'var' init list in a for
      * statement.
-     * @param inFor true if we are currently in the midst of the init
-     * clause of a for.
      * @param declType A token value: either VAR, CONST, or LET depending on
      * context.
-     * @return The parsed statement
-     * @throws IOException
-     * @throws ParserException
+     * @param pos the position where the node should start.  It's sometimes
+     * the var/const/let keyword, and other times the beginning of the first
+     * token in the first variable declaration.
+     * @return the parsed variable list
      */
-    private Node variables(boolean inFor, int declType)
-        throws IOException, ParserException
+    private VariableDeclaration variables(int declType, int pos)
+        throws IOException
     {
-        Node result = nf.createVariables(declType, ts.getLineno());
-        boolean first = true;
+        int end;
+        VariableDeclaration pn = new VariableDeclaration(pos);
+        pn.setType(declType);
+        pn.setLineno(ts.lineno);
+        // Example:
+        // var foo = {a: 1, b: 2}, bar = [3, 4];
+        // var {b: s2, a: s1} = foo, x = 6, y, [s3, s4] = bar;
         for (;;) {
-            Node destructuring = null;
-            String s = null;
-            int tt = peekToken();
+            AstNode destructuring = null;
+            Name name = null;
+            int tt = peekToken(), kidPos = ts.tokenBeg;
+            end = ts.tokenEnd;
+
             if (tt == Token.LB || tt == Token.LC) {
                 // Destructuring assignment, e.g., var [a,b] = ...
                 destructuring = primaryExpr();
+                end = getNodeEnd(destructuring);
+                if (!(destructuring instanceof DestructuringForm))
+                    reportError(&quot;msg.bad.assign.left&quot;, kidPos, end - kidPos);
+                markDestructuring(destructuring);
             } else {
                 // Simple variable name
                 mustMatchToken(Token.NAME, &quot;msg.bad.var&quot;);
-                s = ts.getString();
-    
-                if (!first)
-                    decompiler.addToken(Token.COMMA);
-                first = false;
-    
-                decompiler.addName(s);
-                defineSymbol(declType, inFor, s);
+                name = createNameNode();
+                defineSymbol(declType, ts.getString(), inForInit);
             }
-    
-            Node init = null;
+
+            AstNode init = null;
             if (matchToken(Token.ASSIGN)) {
-                decompiler.addToken(Token.ASSIGN);
-                init = assignExpr(inFor);
+                init = assignExpr();
+                end = getNodeEnd(init);
             }
-    
+
+            VariableInitializer vi = new VariableInitializer(kidPos, end);
             if (destructuring != null) {
-                if (init == null) {
-                    if (!inFor)
-                        reportError(&quot;msg.destruct.assign.no.init&quot;);
-                    nf.addChildToBack(result, destructuring);
-                } else {
-                    nf.addChildToBack(result,
-                        nf.createDestructuringAssignment(declType,
-                            destructuring, init));
+                if (init == null &amp;&amp; !inForInit) {
+                    reportError(&quot;msg.destruct.assign.no.init&quot;);
                 }
+                vi.setTarget(destructuring);
             } else {
-                Node name = nf.createName(s);
-                if (init != null)
-                    nf.addChildToBack(name, init);
-                nf.addChildToBack(result, name);
+                vi.setTarget(name);
             }
-    
+            vi.setInitializer(init);
+            vi.setType(declType);
+            pn.addVariable(vi);
+
             if (!matchToken(Token.COMMA))
                 break;
         }
-        return result;
+        pn.setLength(end - pos);
+        return pn;
     }
 
-    
-    private Node let(boolean isStatement)
-        throws IOException, ParserException
+    // have to pass in 'let' kwd position to compute kid offsets properly
+    private AstNode let(boolean isStatement, int pos)
+        throws IOException
     {
-        mustMatchToken(Token.LP, &quot;msg.no.paren.after.let&quot;);
-        decompiler.addToken(Token.LP);
-        Node result = nf.createScopeNode(Token.LET, ts.getLineno());
-        pushScope(result);
+        LetNode pn = new LetNode(pos);
+        pn.setLineno(ts.lineno);
+        if (mustMatchToken(Token.LP, &quot;msg.no.paren.after.let&quot;))
+            pn.setLp(ts.tokenBeg - pos);
+        pushScope(pn);
         try {
-              Node vars = variables(false, Token.LET);
-              nf.addChildToBack(result, vars);
-              mustMatchToken(Token.RP, &quot;msg.no.paren.let&quot;);
-              decompiler.addToken(Token.RP);
-              if (isStatement &amp;&amp; peekToken() == Token.LC) {
-                  // let statement
-                  consumeToken();
-                  decompiler.addEOL(Token.LC);
-                  nf.addChildToBack(result, statements(null));
-                  mustMatchToken(Token.RC, &quot;msg.no.curly.let&quot;);
-                  decompiler.addToken(Token.RC);
-              } else {
-                  // let expression
-                  result.setType(Token.LETEXPR);
-                  nf.addChildToBack(result, expr(false));
-                  if (isStatement) {
-                      // let expression in statement context
-                      result = nf.createExprStatement(result, ts.getLineno());
-                  }
-              }
+            VariableDeclaration vars = variables(Token.LET, ts.tokenBeg);
+            pn.setVariables(vars);
+            if (mustMatchToken(Token.RP, &quot;msg.no.paren.let&quot;)) {
+                pn.setRp(ts.tokenBeg - pos);
+            }
+            if (isStatement &amp;&amp; peekToken() == Token.LC) {
+                // let statement
+                consumeToken();
+                int beg = ts.tokenBeg;  // position stmt at LC
+                AstNode stmt = statements();
+                mustMatchToken(Token.RC, &quot;msg.no.curly.let&quot;);
+                stmt.setLength(ts.tokenEnd - beg);
+                pn.setLength(ts.tokenEnd - pos);
+                pn.setBody(stmt);
+                pn.setType(Token.LET);
+            } else {
+                // let expression
+                AstNode expr = expr();
+                pn.setLength(getNodeEnd(expr) - pos);
+                pn.setBody(expr);
+                if (isStatement) {
+                    // let expression in statement context
+                    ExpressionStatement es =
+                            new ExpressionStatement(pn, !insideFunction());
+                    es.setLineno(pn.getLineno());
+                    return es;
+                }
+            }
         } finally {
             popScope();
         }
-        return result;
+        return pn;
     }
-    
-    void defineSymbol(int declType, boolean ignoreNotInBlock, String name) {
-        Node.Scope definingScope = currentScope.getDefiningScope(name);
-        Node.Scope.Symbol symbol = definingScope != null 
-                                  ? definingScope.getSymbol(name)
-                                  : null;
-        boolean error = false;
-        if (symbol != null &amp;&amp; (symbol.declType == Token.CONST ||
-            declType == Token.CONST))
-        {
-            error = true;
-        } else {
-            switch (declType) {
-              case Token.LET:
-                if (symbol != null &amp;&amp; definingScope == currentScope) {
-                    error = symbol.declType == Token.LET;
-                }
-                int currentScopeType = currentScope.getType();
-                if (!ignoreNotInBlock &amp;&amp; 
-                    ((currentScopeType == Token.LOOP) ||
-                     (currentScopeType == Token.IF)))
-                {
-                    addError(&quot;msg.let.decl.not.in.block&quot;);
-                }
-                currentScope.putSymbol(name, 
-                    new Node.Scope.Symbol(declType, name));
-                break;
-                
-              case Token.VAR:
-              case Token.CONST:
-              case Token.FUNCTION:
-                if (symbol != null) {
-                    if (symbol.declType == Token.VAR)
-                        addStrictWarning(&quot;msg.var.redecl&quot;, name);
-                    else if (symbol.declType == Token.LP) {
-                        addStrictWarning(&quot;msg.var.hides.arg&quot;, name);
-                    }
-                } else {
-                    currentScriptOrFn.putSymbol(name, 
-                        new Node.Scope.Symbol(declType, name));
-                }
-                break;
-                
-              case Token.LP:
-                if (symbol != null) {
-                    // must be duplicate parameter. Second parameter hides the 
-                    // first, so go ahead and add the second pararameter
-                    addWarning(&quot;msg.dup.parms&quot;, name);
-                }
-                currentScriptOrFn.putSymbol(name, 
-                    new Node.Scope.Symbol(declType, name));
-                break;
-                
-              default:
-                throw Kit.codeBug();
+
+    void defineSymbol(int declType, String name) {
+        defineSymbol(declType, name, false);
+    }
+
+    void defineSymbol(int declType, String name, boolean ignoreNotInBlock) {
+        if (name == null) {
+            if (compilerEnv.isIdeMode()) {  // be robust in IDE-mode
+                return;
+            } else {
+                codeBug();
             }
         }
-        if (error) {
-            addError(symbol.declType == Token.CONST ? &quot;msg.const.redecl&quot; :
-                     symbol.declType == Token.LET ? &quot;msg.let.redecl&quot; :
-                     symbol.declType == Token.VAR ? &quot;msg.var.redecl&quot; :
-                     symbol.declType == Token.FUNCTION ? &quot;msg.fn.redecl&quot; :
+        Scope definingScope = currentScope.getDefiningScope(name);
+        Symbol symbol = definingScope != null
+                        ? definingScope.getSymbol(name)
+                        : null;
+        int symDeclType = symbol != null ? symbol.getDeclType() : -1;
+        if (symbol != null
+            &amp;&amp; (symDeclType == Token.CONST
+                || declType == Token.CONST
+                || (definingScope == currentScope &amp;&amp; symDeclType == Token.LET)))
+        {
+            addError(symDeclType == Token.CONST ? &quot;msg.const.redecl&quot; :
+                     symDeclType == Token.LET ? &quot;msg.let.redecl&quot; :
+                     symDeclType == Token.VAR ? &quot;msg.var.redecl&quot; :
+                     symDeclType == Token.FUNCTION ? &quot;msg.fn.redecl&quot; :
                      &quot;msg.parm.redecl&quot;, name);
+            return;
+        }
+        switch (declType) {
+          case Token.LET:
+              if (!ignoreNotInBlock &amp;&amp;
+                  ((currentScope.getType() == Token.IF) ||
+                   currentScope instanceof Loop)) {
+                  addError(&quot;msg.let.decl.not.in.block&quot;);
+                  return;
+              }
+              currentScope.putSymbol(new Symbol(declType, name));
+              return;
+
+          case Token.VAR:
+          case Token.CONST:
+          case Token.FUNCTION:
+              if (symbol != null) {
+                  if (symDeclType == Token.VAR)
+                      addStrictWarning(&quot;msg.var.redecl&quot;, name);
+                  else if (symDeclType == Token.LP) {
+                      addStrictWarning(&quot;msg.var.hides.arg&quot;, name);
+                  }
+              } else {
+                  currentScriptOrFn.putSymbol(new Symbol(declType, name));
+              }
+              return;
+
+          case Token.LP:
+              if (symbol != null) {
+                  // must be duplicate parameter. Second parameter hides the
+                  // first, so go ahead and add the second pararameter
+                  addWarning(&quot;msg.dup.parms&quot;, name);
+              }
+              currentScriptOrFn.putSymbol(new Symbol(declType, name));
+              return;
+
+          default:
+              throw codeBug();
         }
     }
 
-    private Node expr(boolean inForInit)
-        throws IOException, ParserException
+    private AstNode expr()
+        throws IOException
     {
-        Node pn = assignExpr(inForInit);
+        AstNode pn = assignExpr();
+        int pos = pn.getPosition();
         while (matchToken(Token.COMMA)) {
-            decompiler.addToken(Token.COMMA);
+            int opPos = ts.tokenBeg;
             if (compilerEnv.isStrictMode() &amp;&amp; !pn.hasSideEffects())
-                addStrictWarning(&quot;msg.no.side.effects&quot;, &quot;&quot;);
-            if (peekToken() == Token.YIELD) {
-              reportError(&quot;msg.yield.parenthesized&quot;);
-            }
-            pn = nf.createBinary(Token.COMMA, pn, assignExpr(inForInit));
+                addStrictWarning(&quot;msg.no.side.effects&quot;, &quot;&quot;,
+                                 pos, nodeEnd(pn) - pos);
+            if (peekToken() == Token.YIELD)
+                reportError(&quot;msg.yield.parenthesized&quot;);
+            pn = new InfixExpression(Token.COMMA, pn, assignExpr(), opPos);
         }
         return pn;
     }
 
-    private Node assignExpr(boolean inForInit)
-        throws IOException, ParserException
+    private AstNode assignExpr()
+        throws IOException
     {
         int tt = peekToken();
         if (tt == Token.YIELD) {
-            consumeToken();
             return returnOrYield(tt, true);
         }
-        Node pn = condExpr(inForInit);
-
+        AstNode pn = condExpr();
         tt = peekToken();
         if (Token.FIRST_ASSIGN &lt;= tt &amp;&amp; tt &lt;= Token.LAST_ASSIGN) {
             consumeToken();
-            decompiler.addToken(tt);
-            pn = nf.createAssignment(tt, pn, assignExpr(inForInit));
+            markDestructuring(pn);
+            int opPos = ts.tokenBeg;
+            pn = new Assignment(tt, pn, assignExpr(), opPos);
         }
-
         return pn;
     }
 
-    private Node condExpr(boolean inForInit)
-        throws IOException, ParserException
+    private AstNode condExpr()
+        throws IOException
     {
-        Node pn = orExpr(inForInit);
-
+        AstNode pn = orExpr();
         if (matchToken(Token.HOOK)) {
-            decompiler.addToken(Token.HOOK);
-            Node ifTrue = assignExpr(false);
-            mustMatchToken(Token.COLON, &quot;msg.no.colon.cond&quot;);
-            decompiler.addToken(Token.COLON);
-            Node ifFalse = assignExpr(inForInit);
-            return nf.createCondExpr(pn, ifTrue, ifFalse);
+            int qmarkPos = ts.tokenBeg, colonPos = -1;
+            AstNode ifTrue = assignExpr();
+            if (mustMatchToken(Token.COLON, &quot;msg.no.colon.cond&quot;))
+                colonPos = ts.tokenBeg;
+            AstNode ifFalse = assignExpr();
+            int beg = pn.getPosition(), len = getNodeEnd(ifFalse) - beg;
+            ConditionalExpression ce = new ConditionalExpression(beg, len);
+            ce.setTestExpression(pn);
+            ce.setTrueExpression(ifTrue);
+            ce.setFalseExpression(ifFalse);
+            ce.setQuestionMarkPosition(qmarkPos - beg);
+            ce.setColonPosition(colonPos - beg);
+            pn = ce;
         }
-
         return pn;
     }
 
-    private Node orExpr(boolean inForInit)
-        throws IOException, ParserException
+    private AstNode orExpr()
+        throws IOException
     {
-        Node pn = andExpr(inForInit);
+        AstNode pn = andExpr();
         if (matchToken(Token.OR)) {
-            decompiler.addToken(Token.OR);
-            pn = nf.createBinary(Token.OR, pn, orExpr(inForInit));
+            int opPos = ts.tokenBeg;
+            pn = new InfixExpression(Token.OR, pn, orExpr(), opPos);
         }
-
         return pn;
     }
 
-    private Node andExpr(boolean inForInit)
-        throws IOException, ParserException
+    private AstNode andExpr()
+        throws IOException
     {
-        Node pn = bitOrExpr(inForInit);
+        AstNode pn = bitOrExpr();
         if (matchToken(Token.AND)) {
-            decompiler.addToken(Token.AND);
-            pn = nf.createBinary(Token.AND, pn, andExpr(inForInit));
+            int opPos = ts.tokenBeg;
+            pn = new InfixExpression(Token.AND, pn, andExpr(), opPos);
         }
-
         return pn;
     }
 
-    private Node bitOrExpr(boolean inForInit)
-        throws IOException, ParserException
+    private AstNode bitOrExpr()
+        throws IOException
     {
-        Node pn = bitXorExpr(inForInit);
+        AstNode pn = bitXorExpr();
         while (matchToken(Token.BITOR)) {
-            decompiler.addToken(Token.BITOR);
-            pn = nf.createBinary(Token.BITOR, pn, bitXorExpr(inForInit));
+            int opPos = ts.tokenBeg;
+            pn = new InfixExpression(Token.BITOR, pn, bitXorExpr(), opPos);
         }
         return pn;
     }
 
-    private Node bitXorExpr(boolean inForInit)
-        throws IOException, ParserException
+    private AstNode bitXorExpr()
+        throws IOException
     {
-        Node pn = bitAndExpr(inForInit);
+        AstNode pn = bitAndExpr();
         while (matchToken(Token.BITXOR)) {
-            decompiler.addToken(Token.BITXOR);
-            pn = nf.createBinary(Token.BITXOR, pn, bitAndExpr(inForInit));
+            int opPos = ts.tokenBeg;
+            pn = new InfixExpression(Token.BITXOR, pn, bitAndExpr(), opPos);
         }
         return pn;
     }
 
-    private Node bitAndExpr(boolean inForInit)
-        throws IOException, ParserException
+    private AstNode bitAndExpr()
+        throws IOException
     {
-        Node pn = eqExpr(inForInit);
+        AstNode pn = eqExpr();
         while (matchToken(Token.BITAND)) {
-            decompiler.addToken(Token.BITAND);
-            pn = nf.createBinary(Token.BITAND, pn, eqExpr(inForInit));
+            int opPos = ts.tokenBeg;
+            pn = new InfixExpression(Token.BITAND, pn, eqExpr(), opPos);
         }
         return pn;
     }
 
-    private Node eqExpr(boolean inForInit)
-        throws IOException, ParserException
+    private AstNode eqExpr()
+        throws IOException
     {
-        Node pn = relExpr(inForInit);
+        AstNode pn = relExpr();
         for (;;) {
-            int tt = peekToken();
+            int tt = peekToken(), opPos = ts.tokenBeg;
             switch (tt) {
               case Token.EQ:
               case Token.NE:
               case Token.SHEQ:
               case Token.SHNE:
                 consumeToken();
-                int decompilerToken = tt;
                 int parseToken = tt;
                 if (compilerEnv.getLanguageVersion() == Context.VERSION_1_2) {
                     // JavaScript 1.2 uses shallow equality for == and != .
-                    // In addition, convert === and !== for decompiler into
-                    // == and != since the decompiler is supposed to show
-                    // canonical source and in 1.2 ===, !== are allowed
-                    // only as an alias to ==, !=.
-                    switch (tt) {
-                      case Token.EQ:
+                    if (tt == Token.EQ)
                         parseToken = Token.SHEQ;
-                        break;
-                      case Token.NE:
+                    else if (tt == Token.NE)
                         parseToken = Token.SHNE;
-                        break;
-                      case Token.SHEQ:
-                        decompilerToken = Token.EQ;
-                        break;
-                      case Token.SHNE:
-                        decompilerToken = Token.NE;
-                        break;
-                    }
                 }
-                decompiler.addToken(decompilerToken);
-                pn = nf.createBinary(parseToken, pn, relExpr(inForInit));
+                pn = new InfixExpression(parseToken, pn, relExpr(), opPos);
                 continue;
             }
             break;
@@ -1671,12 +2016,12 @@ public class Parser
         return pn;
     }
 
-    private Node relExpr(boolean inForInit)
-        throws IOException, ParserException
+    private AstNode relExpr()
+        throws IOException
     {
-        Node pn = shiftExpr();
+        AstNode pn = shiftExpr();
         for (;;) {
-            int tt = peekToken();
+            int tt = peekToken(), opPos = ts.tokenBeg;
             switch (tt) {
               case Token.IN:
                 if (inForInit)
@@ -1688,8 +2033,7 @@ public class Parser
               case Token.GE:
               case Token.GT:
                 consumeToken();
-                decompiler.addToken(tt);
-                pn = nf.createBinary(tt, pn, shiftExpr());
+                pn = new InfixExpression(tt, pn, shiftExpr(), opPos);
                 continue;
             }
             break;
@@ -1697,19 +2041,18 @@ public class Parser
         return pn;
     }
 
-    private Node shiftExpr()
-        throws IOException, ParserException
+    private AstNode shiftExpr()
+        throws IOException
     {
-        Node pn = addExpr();
+        AstNode pn = addExpr();
         for (;;) {
-            int tt = peekToken();
+            int tt = peekToken(), opPos = ts.tokenBeg;
             switch (tt) {
               case Token.LSH:
               case Token.URSH:
               case Token.RSH:
                 consumeToken();
-                decompiler.addToken(tt);
-                pn = nf.createBinary(tt, pn, addExpr());
+                pn = new InfixExpression(tt, pn, addExpr(), opPos);
                 continue;
             }
             break;
@@ -1717,791 +2060,1396 @@ public class Parser
         return pn;
     }
 
-    private Node addExpr()
-        throws IOException, ParserException
+    private AstNode addExpr()
+        throws IOException
     {
-        Node pn = mulExpr();
+        AstNode pn = mulExpr();
         for (;;) {
-            int tt = peekToken();
+            int tt = peekToken(), opPos = ts.tokenBeg;
             if (tt == Token.ADD || tt == Token.SUB) {
                 consumeToken();
-                decompiler.addToken(tt);
-                // flushNewLines
-                pn = nf.createBinary(tt, pn, mulExpr());
+                pn = new InfixExpression(tt, pn, mulExpr(), opPos);
                 continue;
             }
             break;
         }
-
         return pn;
     }
 
-    private Node mulExpr()
-        throws IOException, ParserException
+    private AstNode mulExpr()
+        throws IOException
     {
-        Node pn = unaryExpr();
+        AstNode pn = unaryExpr();
         for (;;) {
-            int tt = peekToken();
+            int tt = peekToken(), opPos = ts.tokenBeg;
             switch (tt) {
               case Token.MUL:
               case Token.DIV:
               case Token.MOD:
                 consumeToken();
-                decompiler.addToken(tt);
-                pn = nf.createBinary(tt, pn, unaryExpr());
+                pn = new InfixExpression(tt, pn, unaryExpr(), opPos);
                 continue;
             }
             break;
         }
-
         return pn;
     }
 
-    private Node unaryExpr()
-        throws IOException, ParserException
+    private AstNode unaryExpr()
+        throws IOException
     {
-        int tt;
-
-        tt = peekToken();
+        int tt = peekToken();
 
         switch(tt) {
-        case Token.VOID:
-        case Token.NOT:
-        case Token.BITNOT:
-        case Token.TYPEOF:
-            consumeToken();
-            decompiler.addToken(tt);
-            return nf.createUnary(tt, unaryExpr());
-
-        case Token.ADD:
-            consumeToken();
-            // Convert to special POS token in decompiler and parse tree
-            decompiler.addToken(Token.POS);
-            return nf.createUnary(Token.POS, unaryExpr());
-
-        case Token.SUB:
-            consumeToken();
-            // Convert to special NEG token in decompiler and parse tree
-            decompiler.addToken(Token.NEG);
-            return nf.createUnary(Token.NEG, unaryExpr());
-
-        case Token.INC:
-        case Token.DEC:
-            consumeToken();
-            decompiler.addToken(tt);
-            return nf.createIncDec(tt, false, memberExpr(true));
-
-        case Token.DELPROP:
-            consumeToken();
-            decompiler.addToken(Token.DELPROP);
-            return nf.createUnary(Token.DELPROP, unaryExpr());
+          case Token.VOID:
+          case Token.NOT:
+          case Token.BITNOT:
+          case Token.TYPEOF:
+              consumeToken();
+              return new UnaryExpression(tt, ts.tokenBeg, unaryExpr());
+
+          case Token.ADD:
+              consumeToken();
+              // Convert to special POS token in parse tree
+              return new UnaryExpression(Token.POS, ts.tokenBeg, unaryExpr());
+
+          case Token.SUB:
+              consumeToken();
+              // Convert to special NEG token in parse tree
+              return new UnaryExpression(Token.NEG, ts.tokenBeg, unaryExpr());
+
+          case Token.INC:
+          case Token.DEC:
+              consumeToken();
+              UnaryExpression expr = new UnaryExpression(tt, ts.tokenBeg,
+                                                         memberExpr(true));
+              checkBadIncDec(expr);
+              return expr;
+
+          case Token.DELPROP:
+              consumeToken();
+              return new UnaryExpression(tt, ts.tokenBeg, unaryExpr());
 
-        case Token.ERROR:
-            consumeToken();
-            break;
-
-        // XML stream encountered in expression.
-        case Token.LT:
-            if (compilerEnv.isXmlAvailable()) {
-                consumeToken();
-                Node pn = xmlInitializer();
-                return memberExprTail(true, pn);
-            }
-            // Fall thru to the default handling of RELOP
+          case Token.ERROR:
+              consumeToken();
+              return makeErrorNode();
 
-        default:
-            Node pn = memberExpr(true);
+          case Token.LT:
+              // XML stream encountered in expression.
+              if (compilerEnv.isXmlAvailable()) {
+                  consumeToken();
+                  return memberExprTail(true, xmlInitializer());
+              }
+              // Fall thru to the default handling of RELOP
 
-            // Don't look across a newline boundary for a postfix incop.
-            tt = peekTokenOrEOL();
-            if (tt == Token.INC || tt == Token.DEC) {
-                consumeToken();
-                decompiler.addToken(tt);
-                return nf.createIncDec(tt, true, pn);
-            }
-            return pn;
+          default:
+              AstNode pn = memberExpr(true);
+              // Don't look across a newline boundary for a postfix incop.
+              tt = peekTokenOrEOL();
+              if (!(tt == Token.INC || tt == Token.DEC)) {
+                  return pn;
+              }
+              consumeToken();
+              UnaryExpression uexpr =
+                      new UnaryExpression(tt, ts.tokenBeg, pn, true);
+              checkBadIncDec(uexpr);
+              return uexpr;
         }
-        return nf.createName(&quot;error&quot;); // Only reached on error.Try to continue.
-
     }
 
-    private Node xmlInitializer() throws IOException
+    private AstNode xmlInitializer()
+        throws IOException
     {
-        int tt = ts.getFirstXMLToken();
+        if (currentToken != Token.LT) codeBug();
+        int pos = ts.tokenBeg, tt = ts.getFirstXMLToken();
         if (tt != Token.XML &amp;&amp; tt != Token.XMLEND) {
             reportError(&quot;msg.syntax&quot;);
-            return null;
+            return makeErrorNode();
         }
 
-        /* Make a NEW node to append to. */
-        Node pnXML = nf.createLeaf(Token.NEW);
-
-        String xml = ts.getString();
-        boolean fAnonymous = xml.trim().startsWith(&quot;&lt;&gt;&quot;);
+        XmlLiteral pn = new XmlLiteral(pos);
+        pn.setLineno(ts.lineno);
 
-        Node pn = nf.createName(fAnonymous ? &quot;XMLList&quot; : &quot;XML&quot;);
-        nf.addChildToBack(pnXML, pn);
-
-        pn = null;
-        Node expr;
         for (;;tt = ts.getNextXMLToken()) {
             switch (tt) {
-            case Token.XML:
-                xml = ts.getString();
-                decompiler.addName(xml);
-                mustMatchToken(Token.LC, &quot;msg.syntax&quot;);
-                decompiler.addToken(Token.LC);
-                expr = (peekToken() == Token.RC)
-                    ? nf.createString(&quot;&quot;)
-                    : expr(false);
-                mustMatchToken(Token.RC, &quot;msg.syntax&quot;);
-                decompiler.addToken(Token.RC);
-                if (pn == null) {
-                    pn = nf.createString(xml);
-                } else {
-                    pn = nf.createBinary(Token.ADD, pn, nf.createString(xml));
-                }
-                if (ts.isXMLAttribute()) {
-                    /* Need to put the result in double quotes */
-                    expr = nf.createUnary(Token.ESCXMLATTR, expr);
-                    Node prepend = nf.createBinary(Token.ADD,
-                                                   nf.createString(&quot;\&quot;&quot;),
-                                                   expr);
-                    expr = nf.createBinary(Token.ADD,
-                                           prepend,
-                                           nf.createString(&quot;\&quot;&quot;));
-                } else {
-                    expr = nf.createUnary(Token.ESCXMLTEXT, expr);
-                }
-                pn = nf.createBinary(Token.ADD, pn, expr);
-                break;
-            case Token.XMLEND:
-                xml = ts.getString();
-                decompiler.addName(xml);
-                if (pn == null) {
-                    pn = nf.createString(xml);
-                } else {
-                    pn = nf.createBinary(Token.ADD, pn, nf.createString(xml));
-                }
+              case Token.XML:
+                  pn.addFragment(new XmlString(ts.tokenBeg, ts.getString()));
+                  mustMatchToken(Token.LC, &quot;msg.syntax&quot;);
+                  int beg = ts.tokenBeg;
+                  AstNode expr = (peekToken() == Token.RC)
+                                 ? new EmptyExpression(beg, ts.tokenEnd - beg)
+                                 : expr();
+                  mustMatchToken(Token.RC, &quot;msg.syntax&quot;);
+                  XmlExpression xexpr = new XmlExpression(beg, expr);
+                  xexpr.setIsXmlAttribute(ts.isXMLAttribute());
+                  xexpr.setLength(ts.tokenEnd - beg);
+                  pn.addFragment(xexpr);
+                  break;
+
+              case Token.XMLEND:
+                  pn.addFragment(new XmlString(ts.tokenBeg, ts.getString()));
+                  return pn;
 
-                nf.addChildToBack(pnXML, pn);
-                return pnXML;
-            default:
-                reportError(&quot;msg.syntax&quot;);
-                return null;
+              default:
+                  reportError(&quot;msg.syntax&quot;);
+                  return makeErrorNode();
             }
         }
     }
 
-    private void argumentList(Node listNode)
-        throws IOException, ParserException
+    private List&lt;AstNode&gt; argumentList()
+        throws IOException
     {
-        boolean matched;
-        matched = matchToken(Token.RP);
-        if (!matched) {
-            boolean first = true;
-            do {
-                if (!first)
-                    decompiler.addToken(Token.COMMA);
-                first = false;
-                if (peekToken() == Token.YIELD) {
-                    reportError(&quot;msg.yield.parenthesized&quot;);
-                }
-                nf.addChildToBack(listNode, assignExpr(false));
-            } while (matchToken(Token.COMMA));
+        if (matchToken(Token.RP))
+            return null;
 
-            mustMatchToken(Token.RP, &quot;msg.no.paren.arg&quot;);
-        }
-        decompiler.addToken(Token.RP);
+        List&lt;AstNode&gt; result = new ArrayList&lt;AstNode&gt;();
+        do {
+            if (peekToken() == Token.YIELD)
+                reportError(&quot;msg.yield.parenthesized&quot;);
+            result.add(assignExpr());
+        } while (matchToken(Token.COMMA));
+
+        mustMatchToken(Token.RP, &quot;msg.no.paren.arg&quot;);
+        return result;
     }
 
-    private Node memberExpr(boolean allowCallSyntax)
-        throws IOException, ParserException
+    /**
+     * Parse a new-expression, or if next token isn't {@link Token.NEW},
+     * a primary expression.
+     * @param allowCallSyntax passed down to {@link #memberExprTail}
+     */
+    private AstNode memberExpr(boolean allowCallSyntax)
+        throws IOException
     {
-        int tt;
+        int tt = peekToken(), lineno = ts.lineno;
+        AstNode pn;
 
-        Node pn;
-
-        /* Check for new expressions. */
-        tt = peekToken();
-        if (tt == Token.NEW) {
-            /* Eat the NEW token. */
+        if (tt != Token.NEW) {
+            pn = primaryExpr();
+        } else {
             consumeToken();
-            decompiler.addToken(Token.NEW);
+            int pos = ts.tokenBeg;
+            NewExpression nx = new NewExpression(pos);
 
-            /* Make a NEW node to append to. */
-            pn = nf.createCallOrNew(Token.NEW, memberExpr(false));
+            AstNode target = memberExpr(false);
+            int end = getNodeEnd(target);
+            nx.setTarget(target);
 
+            int lp = -1;
             if (matchToken(Token.LP)) {
-                decompiler.addToken(Token.LP);
-                /* Add the arguments to pn, if any are supplied. */
-                argumentList(pn);
+                lp = ts.tokenBeg;
+                List&lt;AstNode&gt; args = argumentList();
+                if (args != null &amp;&amp; args.size() &gt; ARGC_LIMIT)
+                    reportError(&quot;msg.too.many.constructor.args&quot;);
+                int rp = ts.tokenBeg;
+                end = ts.tokenEnd;
+                if (args != null)
+                    nx.setArguments(args);
+                nx.setParens(lp - pos, rp - pos);
             }
 
-            /* XXX there's a check in the C source against
-             * &quot;too many constructor arguments&quot; - how many
-             * do we claim to support?
-             */
-
-            /* Experimental syntax:  allow an object literal to follow a new expression,
-             * which will mean a kind of anonymous class built with the JavaAdapter.
-             * the object literal will be passed as an additional argument to the constructor.
-             */
-            tt = peekToken();
-            if (tt == Token.LC) {
-                nf.addChildToBack(pn, primaryExpr());
+            // Experimental syntax: allow an object literal to follow a new
+            // expression, which will mean a kind of anonymous class built with
+            // the JavaAdapter.  the object literal will be passed as an
+            // additional argument to the constructor.
+            if (matchToken(Token.LC)) {
+                ObjectLiteral initializer = objectLiteral();
+                end = getNodeEnd(initializer);
+                nx.setInitializer(initializer);
             }
-        } else {
-            pn = primaryExpr();
+            nx.setLength(end - pos);
+            pn = nx;
         }
-
-        return memberExprTail(allowCallSyntax, pn);
+        AstNode tail = memberExprTail(allowCallSyntax, pn);
+        tail.setLineno(lineno);
+        return tail;
     }
 
-    private Node memberExprTail(boolean allowCallSyntax, Node pn)
-        throws IOException, ParserException
+    /**
+     * Parse any number of &quot;(expr)&quot;, &quot;[expr]&quot; &quot;.expr&quot;, &quot;..expr&quot;,
+     * or &quot;.(expr)&quot; constructs trailing the passed expression.
+     * @param pn the non-null parent node
+     * @return the outermost (lexically last occurring) expression,
+     * which will have the passed parent node as a descendant
+     */
+    private AstNode memberExprTail(boolean allowCallSyntax, AstNode pn)
+        throws IOException
     {
+        // we no longer return null for errors, so this won't be null
+        if (pn == null) codeBug();
+        int pos = pn.getPosition(), lineno = ts.lineno;
       tailLoop:
         for (;;) {
             int tt = peekToken();
             switch (tt) {
-
               case Token.DOT:
               case Token.DOTDOT:
-                {
-                    int memberTypeFlags;
-                    String s;
-
-                    consumeToken();
-                    decompiler.addToken(tt);
-                    memberTypeFlags = 0;
-                    if (tt == Token.DOTDOT) {
-                        mustHaveXML();
-                        memberTypeFlags = Node.DESCENDANTS_FLAG;
-                    }
-                    if (!compilerEnv.isXmlAvailable()) {
-                        mustMatchToken(Token.NAME, &quot;msg.no.name.after.dot&quot;);
-                        s = ts.getString();
-                        decompiler.addName(s);
-                        pn = nf.createPropertyGet(pn, null, s, memberTypeFlags);
-                        break;
-                    }
-
-                    tt = nextToken();
-                    switch (tt) {
-                    
-                      // needed for generator.throw();
-                      case Token.THROW:
-                        decompiler.addName(&quot;throw&quot;);
-                        pn = propertyName(pn, &quot;throw&quot;, memberTypeFlags);
-                        break;
-
-                      // handles: name, ns::name, ns::*, ns::[expr]
-                      case Token.NAME:
-                        s = ts.getString();
-                        decompiler.addName(s);
-                        pn = propertyName(pn, s, memberTypeFlags);
-                        break;
-
-                      // handles: *, *::name, *::*, *::[expr]
-                      case Token.MUL:
-                        decompiler.addName(&quot;*&quot;);
-                        pn = propertyName(pn, &quot;*&quot;, memberTypeFlags);
-                        break;
-
-                      // handles: '@attr', '@ns::attr', '@ns::*', '@ns::*',
-                      //          '@::attr', '@::*', '@*', '@*::attr', '@*::*'
-                      case Token.XMLATTR:
-                        decompiler.addToken(Token.XMLATTR);
-                        pn = attributeAccess(pn, memberTypeFlags);
-                        break;
-
-                      default:
-                        reportError(&quot;msg.no.name.after.dot&quot;);
-                    }
-                }
-                break;
+                  pn = propertyAccess(tt, pn);
+                  break;
 
               case Token.DOTQUERY:
-                consumeToken();
-                mustHaveXML();
-                decompiler.addToken(Token.DOTQUERY);
-                pn = nf.createDotQuery(pn, expr(false), ts.getLineno());
-                mustMatchToken(Token.RP, &quot;msg.no.paren&quot;);
-                decompiler.addToken(Token.RP);
-                break;
+                  consumeToken();
+                  int opPos = ts.tokenBeg, rp = -1;
+                  mustHaveXML();
+                  setRequiresActivation();
+                  AstNode filter = expr();
+                  int end = getNodeEnd(filter);
+                  if (mustMatchToken(Token.RP, &quot;msg.no.paren&quot;)) {
+                      rp = ts.tokenBeg;
+                      end = ts.tokenEnd;
+                  }
+                  XmlDotQuery q = new XmlDotQuery(pos, end - pos);
+                  q.setLeft(pn);
+                  q.setRight(filter);
+                  q.setOperatorPosition(opPos);
+                  q.setRp(rp - pos);
+                  pn = q;
+                  break;
 
               case Token.LB:
-                consumeToken();
-                decompiler.addToken(Token.LB);
-                pn = nf.createElementGet(pn, null, expr(false), 0);
-                mustMatchToken(Token.RB, &quot;msg.no.bracket.index&quot;);
-                decompiler.addToken(Token.RB);
-                break;
+                  consumeToken();
+                  int lb = ts.tokenBeg, rb = -1;
+                  AstNode expr = expr();
+                  end = getNodeEnd(expr);
+                  if (mustMatchToken(Token.RB, &quot;msg.no.bracket.index&quot;)) {
+                      rb = ts.tokenBeg;
+                      end = ts.tokenEnd;
+                  }
+                  ElementGet g = new ElementGet(pos, end - pos);
+                  g.setTarget(pn);
+                  g.setElement(expr);
+                  g.setParens(lb, rb);
+                  pn = g;
+                  break;
 
               case Token.LP:
-                if (!allowCallSyntax) {
-                    break tailLoop;
-                }
-                consumeToken();
-                decompiler.addToken(Token.LP);
-                pn = nf.createCallOrNew(Token.CALL, pn);
-                /* Add the arguments to pn, if any are supplied. */
-                argumentList(pn);
-                break;
+                  if (!allowCallSyntax) {
+                      break tailLoop;
+                  }
+                  consumeToken();
+                  checkCallRequiresActivation(pn);
+                  FunctionCall f = new FunctionCall(pos);
+                  f.setTarget(pn);
+                  f.setLp(ts.tokenBeg - pos);
+                  List&lt;AstNode&gt; args = argumentList();
+                  if (args != null &amp;&amp; args.size() &gt; ARGC_LIMIT)
+                      reportError(&quot;msg.too.many.function.args&quot;);
+                  f.setArguments(args);
+                  f.setRp(ts.tokenBeg - pos);
+                  f.setLength(ts.tokenEnd - pos);
+                  pn = f;
+                  break;
 
               default:
-                break tailLoop;
+                  break tailLoop;
             }
         }
+        pn.setLineno(lineno);
         return pn;
     }
 
-    /*
-     * Xml attribute expression:
-     *   '@attr', '@ns::attr', '@ns::*', '@ns::*', '@*', '@*::attr', '@*::*'
+    /**
+     * Handles any construct following a &quot;.&quot; or &quot;..&quot; operator.
+     * @param pn the left-hand side (target) of the operator.  Never null.
+     * @return a PropertyGet, XmlMemberGet, or ErrorNode
+     */
+    private AstNode propertyAccess(int tt, AstNode pn)
+            throws IOException
+    {
+        if (pn == null) codeBug();
+        int memberTypeFlags = 0, lineno = ts.lineno, dotPos = ts.tokenBeg;
+        consumeToken();
+
+        if (tt == Token.DOTDOT) {
+            mustHaveXML();
+            memberTypeFlags = XmlRef.DESCENDANTS_FLAG;
+        }
+
+        if (!compilerEnv.isXmlAvailable()) {
+            mustMatchToken(Token.NAME, &quot;msg.no.name.after.dot&quot;);
+            Name name = createNameNode(true, Token.GETPROP);
+            PropertyGet pg = new PropertyGet(pn, name, dotPos);
+            pg.setLineno(lineno);
+            return pg;
+        }
+
+        AstNode ref = null;  // right side of . or .. operator
+
+        switch (nextToken()) {
+          case Token.THROW:
+              // needed for generator.throw();
+              saveNameTokenData(ts.tokenBeg, &quot;throw&quot;, ts.lineno);
+              ref = propertyName(-1, &quot;throw&quot;, memberTypeFlags);
+              break;
+
+          case Token.NAME:
+              // handles: name, ns::name, ns::*, ns::[expr]
+              ref = propertyName(-1, ts.getString(), memberTypeFlags);
+              break;
+
+          case Token.MUL:
+              // handles: *, *::name, *::*, *::[expr]
+              saveNameTokenData(ts.tokenBeg, &quot;*&quot;, ts.lineno);
+              ref = propertyName(-1, &quot;*&quot;, memberTypeFlags);
+              break;
+
+          case Token.XMLATTR:
+              // handles: '@attr', '@ns::attr', '@ns::*', '@ns::*',
+              //          '@::attr', '@::*', '@*', '@*::attr', '@*::*'
+              ref = attributeAccess();
+              break;
+
+          default:
+              reportError(&quot;msg.no.name.after.dot&quot;);
+              return makeErrorNode();
+        }
+
+        boolean xml = ref instanceof XmlRef;
+        InfixExpression result = xml ? new XmlMemberGet() : new PropertyGet();
+        if (xml &amp;&amp; tt == Token.DOT)
+            result.setType(Token.DOT);
+        int pos = pn.getPosition();
+        result.setPosition(pos);
+        result.setLength(getNodeEnd(ref) - pos);
+        result.setOperatorPosition(dotPos - pos);
+        result.setLineno(lineno);
+        result.setLeft(pn);  // do this after setting position
+        result.setRight(ref);
+        return result;
+    }
+
+    /**
+     * Xml attribute expression:&lt;p&gt;
+     *   {@code @attr}, {@code @ns::attr}, {@code @ns::*}, {@code @ns::*},
+     *   {@code @*}, {@code @*::attr}, {@code @*::*}, {@code @ns::[expr]},
+     *   {@code @*::[expr]}, {@code @[expr]} &lt;p&gt;
+     * Called if we peeked an '@' token.
      */
-    private Node attributeAccess(Node pn, int memberTypeFlags)
+    private AstNode attributeAccess()
         throws IOException
     {
-        memberTypeFlags |= Node.ATTRIBUTE_FLAG;
-        int tt = nextToken();
+        int tt = nextToken(), atPos = ts.tokenBeg;
 
         switch (tt) {
           // handles: @name, @ns::name, @ns::*, @ns::[expr]
           case Token.NAME:
-            {
-                String s = ts.getString();
-                decompiler.addName(s);
-                pn = propertyName(pn, s, memberTypeFlags);
-            }
-            break;
+              return propertyName(atPos, ts.getString(), 0);
 
           // handles: @*, @*::name, @*::*, @*::[expr]
           case Token.MUL:
-            decompiler.addName(&quot;*&quot;);
-            pn = propertyName(pn, &quot;*&quot;, memberTypeFlags);
-            break;
+              saveNameTokenData(ts.tokenBeg, &quot;*&quot;, ts.lineno);
+              return propertyName(atPos, &quot;*&quot;, 0);
 
           // handles @[expr]
           case Token.LB:
-            decompiler.addToken(Token.LB);
-            pn = nf.createElementGet(pn, null, expr(false), memberTypeFlags);
-            mustMatchToken(Token.RB, &quot;msg.no.bracket.index&quot;);
-            decompiler.addToken(Token.RB);
-            break;
+              return xmlElemRef(atPos, null, -1);
 
           default:
-            reportError(&quot;msg.no.name.after.xmlAttr&quot;);
-            pn = nf.createPropertyGet(pn, null, &quot;?&quot;, memberTypeFlags);
-            break;
+              reportError(&quot;msg.no.name.after.xmlAttr&quot;);
+              return makeErrorNode();
         }
-
-        return pn;
     }
 
     /**
-     * Check if :: follows name in which case it becomes qualified name
+     * Check if :: follows name in which case it becomes a qualified name.
+     *
+     * @param atPos a natural number if we just read an '@' token, else -1
+     *
+     * @param s the name or string that was matched (an identifier, &quot;throw&quot; or
+     * &quot;*&quot;).
+     *
+     * @param memberTypeFlags flags tracking whether we're a '.' or '..' child
+     *
+     * @return an XmlRef node if it's an attribute access, a child of a
+     * '..' operator, or the name is followed by ::.  For a plain name,
+     * returns a Name node.  Returns an ErrorNode for malformed XML
+     * expressions.  (For now - might change to return a partial XmlRef.)
      */
-    private Node propertyName(Node pn, String name, int memberTypeFlags)
-        throws IOException, ParserException
+    private AstNode propertyName(int atPos, String s, int memberTypeFlags)
+        throws IOException
     {
-        String namespace = null;
+        int pos = atPos != -1 ? atPos : ts.tokenBeg, lineno = ts.lineno;
+        int colonPos = -1;
+        Name name = createNameNode(true, currentToken);
+        Name ns = null;
+
         if (matchToken(Token.COLONCOLON)) {
-            decompiler.addToken(Token.COLONCOLON);
-            namespace = name;
+            ns = name;
+            colonPos = ts.tokenBeg;
 
-            int tt = nextToken();
-            switch (tt) {
+            switch (nextToken()) {
               // handles name::name
               case Token.NAME:
-                name = ts.getString();
-                decompiler.addName(name);
-                break;
+                  name = createNameNode();
+                  break;
 
               // handles name::*
               case Token.MUL:
-                decompiler.addName(&quot;*&quot;);
-                name = &quot;*&quot;;
-                break;
+                  saveNameTokenData(ts.tokenBeg, &quot;*&quot;, ts.lineno);
+                  name = createNameNode(false, -1);
+                  break;
 
-              // handles name::[expr]
+              // handles name::[expr] or *::[expr]
               case Token.LB:
-                decompiler.addToken(Token.LB);
-                pn = nf.createElementGet(pn, namespace, expr(false),
-                                         memberTypeFlags);
-                mustMatchToken(Token.RB, &quot;msg.no.bracket.index&quot;);
-                decompiler.addToken(Token.RB);
-                return pn;
+                  return xmlElemRef(atPos, ns, colonPos);
 
               default:
-                reportError(&quot;msg.no.name.after.coloncolon&quot;);
-                name = &quot;?&quot;;
+                  reportError(&quot;msg.no.name.after.coloncolon&quot;);
+                  return makeErrorNode();
             }
         }
 
-        pn = nf.createPropertyGet(pn, namespace, name, memberTypeFlags);
-        return pn;
+        if (ns == null &amp;&amp; memberTypeFlags == 0 &amp;&amp; atPos == -1) {
+            return name;
+        }
+
+        XmlPropRef ref = new XmlPropRef(pos, getNodeEnd(name) - pos);
+        ref.setAtPos(atPos);
+        ref.setNamespace(ns);
+        ref.setColonPos(colonPos);
+        ref.setPropName(name);
+        ref.setLineno(lineno);
+        return ref;
     }
 
-    private Node arrayComprehension(String arrayName, Node expr)
-        throws IOException, ParserException
+    /**
+     * Parse the [expr] portion of an xml element reference, e.g.
+     * @[expr], @*::[expr], or ns::[expr].
+     */
+    private XmlElemRef xmlElemRef(int atPos, Name namespace, int colonPos)
+        throws IOException
     {
-        if (nextToken() != Token.FOR)
-            throw Kit.codeBug(); // shouldn't be here if next token isn't 'for'
-        decompiler.addName(&quot; &quot;); // space after array literal expr
-        decompiler.addToken(Token.FOR);
-        boolean isForEach = false;
-        if (matchToken(Token.NAME)) {
-            decompiler.addName(ts.getString());
-            if (ts.getString().equals(&quot;each&quot;)) {
-                isForEach = true;
-            } else {
-                reportError(&quot;msg.no.paren.for&quot;);
-            }
+        int lb = ts.tokenBeg, rb = -1, pos = atPos != -1 ? atPos : lb;
+        AstNode expr = expr();
+        int end = getNodeEnd(expr);
+        if (mustMatchToken(Token.RB, &quot;msg.no.bracket.index&quot;)) {
+            rb = ts.tokenBeg;
+            end = ts.tokenEnd;
+        }
+        XmlElemRef ref = new XmlElemRef(pos, end - pos);
+        ref.setNamespace(namespace);
+        ref.setColonPos(colonPos);
+        ref.setAtPos(atPos);
+        ref.setExpression(expr);
+        ref.setBrackets(lb, rb);
+        return ref;
+    }
+
+    private AstNode primaryExpr()
+        throws IOException
+    {
+        int ttFlagged = nextFlaggedToken();
+        int tt = ttFlagged &amp; CLEAR_TI_MASK;
+
+        switch(tt) {
+          case Token.FUNCTION:
+              return function(FunctionNode.FUNCTION_EXPRESSION);
+
+          case Token.LB:
+              return arrayLiteral();
+
+          case Token.LC:
+              return objectLiteral();
+
+          case Token.LET:
+              return let(false, ts.tokenBeg);
+
+          case Token.LP:
+              return parenExpr();
+
+          case Token.XMLATTR:
+              mustHaveXML();
+              return attributeAccess();
+
+          case Token.NAME:
+              return name(ttFlagged, tt);
+
+          case Token.NUMBER:
+              return new NumberLiteral(ts.tokenBeg,
+                                       ts.getString(),
+                                       ts.getNumber());
+          case Token.STRING:
+              return createStringLiteral();
+
+          case Token.DIV:
+          case Token.ASSIGN_DIV:
+              // Got / or /= which in this context means a regexp
+              ts.readRegExp(tt);
+              int pos = ts.tokenBeg, end = ts.tokenEnd;
+              RegExpLiteral re = new RegExpLiteral(pos, end - pos);
+              re.setValue(ts.getString());
+              re.setFlags(ts.readAndClearRegExpFlags());
+              return re;
+
+          case Token.NULL:
+          case Token.THIS:
+          case Token.FALSE:
+          case Token.TRUE:
+              pos = ts.tokenBeg; end = ts.tokenEnd;
+              return new KeywordLiteral(pos, end - pos, tt);
+
+          case Token.RESERVED:
+              reportError(&quot;msg.reserved.id&quot;);
+              break;
+
+          case Token.ERROR:
+              // the scanner or one of its subroutines reported the error.
+              break;
+
+          case Token.EOF:
+              reportError(&quot;msg.unexpected.eof&quot;);
+              break;
+
+          default:
+              reportError(&quot;msg.syntax&quot;);
+              break;
         }
-        mustMatchToken(Token.LP, &quot;msg.no.paren.for&quot;);
-        decompiler.addToken(Token.LP);
-        String name;
-        int tt = peekToken();
-        if (tt == Token.LB || tt == Token.LC) {
-            // handle destructuring assignment
-            name = currentScriptOrFn.getNextTempName();
-            defineSymbol(Token.LP, false, name);
-            expr = nf.createBinary(Token.COMMA,
-                nf.createAssignment(Token.ASSIGN, primaryExpr(), 
-                                    nf.createName(name)),
-                expr);
-        } else if (tt == Token.NAME) {
-            consumeToken();
-            name = ts.getString();
-            decompiler.addName(name);
-        } else {
-            reportError(&quot;msg.bad.var&quot;);
-            return nf.createNumber(0);
-        }
-
-        Node init = nf.createName(name);
-        // Define as a let since we want the scope of the variable to
-        // be restricted to the array comprehension
-        defineSymbol(Token.LET, false, name);
-        
-        mustMatchToken(Token.IN, &quot;msg.in.after.for.name&quot;);
-        decompiler.addToken(Token.IN);
-        Node iterator = expr(false);
-        mustMatchToken(Token.RP, &quot;msg.no.paren.for.ctrl&quot;);
-        decompiler.addToken(Token.RP);
-        
-        Node body;
-        tt = peekToken();
-        if (tt == Token.FOR) {
-            body = arrayComprehension(arrayName, expr);
+        // should only be reachable in IDE/error-recovery mode
+        return makeErrorNode();
+    }
+
+    private AstNode parenExpr() throws IOException {
+        boolean wasInForInit = inForInit;
+        inForInit = false;
+        try {
+            AstNode e = expr();
+            ParenthesizedExpression pn = new ParenthesizedExpression(e);
+            mustMatchToken(Token.RP, &quot;msg.no.paren&quot;);
+            pn.setLength(ts.tokenEnd - pn.getPosition());
+            return pn;
+        } finally {
+            inForInit = wasInForInit;
+        }
+    }
+
+    private AstNode name(int ttFlagged, int tt) throws IOException {
+        String nameString = ts.getString();
+        int namePos = ts.tokenBeg, nameLineno = ts.lineno;
+        if (0 != (ttFlagged &amp; TI_CHECK_LABEL) &amp;&amp; peekToken() == Token.COLON) {
+            // Do not consume colon.  It is used as an unwind indicator
+            // to return to statementHelper.
+            Label label = new Label(namePos, ts.tokenEnd - namePos);
+            label.setName(nameString);
+            label.setLineno(ts.lineno);
+            return label;
+        }
+        // Not a label.  Unfortunately peeking the next token to check for
+        // a colon has biffed ts.tokenBeg, ts.tokenEnd.  We store the name's
+        // bounds in instance vars and createNameNode uses them.
+        saveNameTokenData(namePos, nameString, nameLineno);
+
+        if (compilerEnv.isXmlAvailable()) {
+            return propertyName(-1, nameString, 0);
         } else {
-            Node call = nf.createCallOrNew(Token.CALL,
-                nf.createPropertyGet(nf.createName(arrayName), null,
-                                     &quot;push&quot;, 0));
-            call.addChildToBack(expr);
-            body = new Node(Token.EXPR_VOID, call, ts.getLineno());
-            if (tt == Token.IF) {
+            return createNameNode(true, Token.NAME);
+        }
+    }
+
+    /**
+     * May return an {@link ArrayLiteral} or {@link ArrayComprehension}.
+     */
+    private AstNode arrayLiteral()
+        throws IOException
+    {
+        if (currentToken != Token.LB) codeBug();
+        int pos = ts.tokenBeg, end = ts.tokenEnd;
+        List&lt;AstNode&gt; elements = new ArrayList&lt;AstNode&gt;();
+        ArrayLiteral pn = new ArrayLiteral(pos);
+        boolean after_lb_or_comma = true;
+        int afterComma = -1;
+        int skipCount = 0;
+        for (;;) {
+            int tt = peekToken();
+            if (tt == Token.COMMA) {
                 consumeToken();
-                decompiler.addToken(Token.IF);
-                int lineno = ts.getLineno();
-                Node cond = condition();
-                body = nf.createIf(cond, body, null, lineno);
+                afterComma = ts.tokenEnd;
+                if (!after_lb_or_comma) {
+                    after_lb_or_comma = true;
+                } else {
+                    elements.add(new EmptyExpression(ts.tokenBeg, 1));
+                    skipCount++;
+                }
+            } else if (tt == Token.RB) {
+                consumeToken();
+                // for ([a,] in obj) is legal, but for ([a] in obj) is
+                // not since we have both key and value supplied. The
+                // trick is that [a,] and [a] are equivalent in other
+                // array literal contexts. So we calculate a special
+                // length value just for destructuring assignment.
+                end = ts.tokenEnd;
+                pn.setDestructuringLength(elements.size() +
+                                          (after_lb_or_comma ? 1 : 0));
+                pn.setSkipCount(skipCount);
+                if (afterComma != -1)
+                    warnTrailingComma(&quot;msg.array.trailing.comma&quot;,
+                                      pos, elements, afterComma);
+                break;
+            } else if (tt == Token.FOR &amp;&amp; !after_lb_or_comma
+                       &amp;&amp; elements.size() == 1) {
+                return arrayComprehension(elements.get(0), pos);
+            } else if (tt == Token.EOF) {
+                end = ts.tokenBeg;
+                break;
+            } else {
+                if (!after_lb_or_comma) {
+                    reportError(&quot;msg.no.bracket.arg&quot;);
+                }
+                elements.add(assignExpr());
+                after_lb_or_comma = false;
+                afterComma = -1;
             }
-            mustMatchToken(Token.RB, &quot;msg.no.bracket.arg&quot;);
-            decompiler.addToken(Token.RB);
         }
+        for (AstNode e : elements) {
+            pn.addElement(e);
+        }
+        pn.setLength(end - pos);
+        return pn;
+    }
 
-        Node loop = enterLoop(null, true);
+    /**
+     * Parse a JavaScript 1.7 Array comprehension.
+     * @param result the first expression after the opening left-bracket
+     * @param pos start of LB token that begins the array comprehension
+     * @return the array comprehension or an error node
+     */
+    private AstNode arrayComprehension(AstNode result, int pos)
+        throws IOException
+    {
+        List&lt;ArrayComprehensionLoop&gt; loops =
+                new ArrayList&lt;ArrayComprehensionLoop&gt;();
+        while (peekToken() == Token.FOR) {
+            loops.add(arrayComprehensionLoop());
+        }
+        int ifPos = -1;
+        ConditionData data = null;
+        if (peekToken() == Token.IF) {
+            consumeToken();
+            ifPos = ts.tokenBeg - pos;
+            data = condition();
+        }
+        mustMatchToken(Token.RB, &quot;msg.no.bracket.arg&quot;);
+        ArrayComprehension pn = new ArrayComprehension(pos, ts.tokenEnd - pos);
+        pn.setResult(result);
+        pn.setLoops(loops);
+        if (data != null) {
+            pn.setIfPosition(ifPos);
+            pn.setFilter(data.condition);
+            pn.setFilterLp(data.lp - pos);
+            pn.setFilterRp(data.rp - pos);
+        }
+        return pn;
+    }
+
+    private ArrayComprehensionLoop arrayComprehensionLoop()
+        throws IOException
+    {
+        if (nextToken() != Token.FOR) codeBug();
+        int pos = ts.tokenBeg;
+        int eachPos = -1, lp = -1, rp = -1, inPos = -1;
+        ArrayComprehensionLoop pn = new ArrayComprehensionLoop(pos);
+
+        pushScope(pn);
         try {
-            return nf.createForIn(Token.LET, loop, init, iterator, body,
-                                  isForEach);
+            if (matchToken(Token.NAME)) {
+                if (ts.getString().equals(&quot;each&quot;)) {
+                    eachPos = ts.tokenBeg - pos;
+                } else {
+                    reportError(&quot;msg.no.paren.for&quot;);
+                }
+            }
+            if (mustMatchToken(Token.LP, &quot;msg.no.paren.for&quot;)) {
+                lp = ts.tokenBeg - pos;
+            }
+
+            AstNode iter = null;
+            switch (peekToken()) {
+              case Token.LB:
+              case Token.LC:
+                  // handle destructuring assignment
+                  iter = primaryExpr();
+                  markDestructuring(iter);
+                  break;
+              case Token.NAME:
+                  consumeToken();
+                  iter = createNameNode();
+                  break;
+              default:
+                  reportError(&quot;msg.bad.var&quot;);
+            }
+
+            // Define as a let since we want the scope of the variable to
+            // be restricted to the array comprehension
+            if (iter.getType() == Token.NAME) {
+                defineSymbol(Token.LET, ts.getString(), true);
+            }
+
+            if (mustMatchToken(Token.IN, &quot;msg.in.after.for.name&quot;))
+                inPos = ts.tokenBeg - pos;
+            AstNode obj = expr();
+            if (mustMatchToken(Token.RP, &quot;msg.no.paren.for.ctrl&quot;))
+                rp = ts.tokenBeg - pos;
+
+            pn.setLength(ts.tokenEnd - pos);
+            pn.setIterator(iter);
+            pn.setIteratedObject(obj);
+            pn.setInPosition(inPos);
+            pn.setEachPosition(eachPos);
+            pn.setIsForEach(eachPos != -1);
+            pn.setParens(lp, rp);
+            return pn;
         } finally {
-            exitLoop(false);
+            popScope();
         }
     }
-    
-    private Node primaryExpr()
-        throws IOException, ParserException
+
+    private ObjectLiteral objectLiteral()
+        throws IOException
     {
-        Node pn;
+        int pos = ts.tokenBeg, lineno = ts.lineno;
+        int afterComma = -1;
+        List&lt;ObjectProperty&gt; elems = new ArrayList&lt;ObjectProperty&gt;();
 
-        int ttFlagged = nextFlaggedToken();
-        int tt = ttFlagged &amp; CLEAR_TI_MASK;
+      commaLoop:
+        for (;;) {
+            int tt = peekToken();
+            switch(tt) {
+              case Token.NAME:
+              case Token.STRING:
+                  afterComma = -1;
+                  consumeToken();
+                  StringLiteral stringProp = null;
+                  if (tt == Token.STRING) {
+                      stringProp = createStringLiteral();
+                  }
+                  Name name = createNameNode();
+                  String prop = ts.getString();
+                  int ppos = ts.tokenBeg, pend = ts.tokenEnd;
+
+                  if ((tt == Token.NAME
+                       &amp;&amp; peekToken() == Token.NAME
+                       &amp;&amp; (&quot;get&quot;.equals(prop) || &quot;set&quot;.equals(prop))))
+                  {
+                      consumeToken();
+                      name = createNameNode();
+                      elems.add(getterSetterProperty(ppos, name,
+                                                     &quot;get&quot;.equals(prop)));
+                  } else {
+                      AstNode pname = stringProp != null ? stringProp : name;
+                      elems.add(plainProperty(pname));
+                  }
+                  break;
 
-        switch(tt) {
+              case Token.NUMBER:
+                  consumeToken();
+                  afterComma = -1;
+                  AstNode nl = new NumberLiteral(ts.tokenBeg,
+                                                 ts.getString(),
+                                                 ts.getNumber());
+                  elems.add(plainProperty(nl));
+                  break;
+
+              case Token.RC:
+                  if (afterComma != -1 &amp;&amp; compilerEnv.getWarnTrailingComma())
+                      warnTrailingComma(&quot;msg.extra.trailing.comma&quot;,
+                                        pos, elems, afterComma);
+                  break commaLoop;
 
-          case Token.FUNCTION:
-            return function(FunctionNode.FUNCTION_EXPRESSION);
-
-          case Token.LB: {
-            ObjArray elems = new ObjArray();
-            int skipCount = 0;
-            int destructuringLen = 0;
-            decompiler.addToken(Token.LB);
-            boolean after_lb_or_comma = true;
-            for (;;) {
-                tt = peekToken();
+              default:
+                  reportError(&quot;msg.bad.prop&quot;);
+                  break;
+            }
 
-                if (tt == Token.COMMA) {
-                    consumeToken();
-                    decompiler.addToken(Token.COMMA);
-                    if (!after_lb_or_comma) {
-                        after_lb_or_comma = true;
-                    } else {
-                        elems.add(null);
-                        ++skipCount;
-                    }
-                } else if (tt == Token.RB) {
-                    consumeToken();
-                    decompiler.addToken(Token.RB);
-                    // for ([a,] in obj) is legal, but for ([a] in obj) is 
-                    // not since we have both key and value supplied. The
-                    // trick is that [a,] and [a] are equivalent in other
-                    // array literal contexts. So we calculate a special
-                    // length value just for destructuring assignment.
-                    destructuringLen = elems.size() + 
-                                       (after_lb_or_comma ? 1 : 0);
-                    break;
-                } else if (skipCount == 0 &amp;&amp; elems.size() == 1 &amp;&amp;
-                           tt == Token.FOR)
-                {
-                    Node scopeNode = nf.createScopeNode(Token.ARRAYCOMP, 
-                                                        ts.getLineno());
-                    String tempName = currentScriptOrFn.getNextTempName();
-                    pushScope(scopeNode);
-                    try {
-                        defineSymbol(Token.LET, false, tempName);
-                        Node expr = (Node) elems.get(0);
-                        Node block = nf.createBlock(ts.getLineno());
-                        Node init = new Node(Token.EXPR_VOID, 
-                            nf.createAssignment(Token.ASSIGN, 
-                                nf.createName(tempName),
-                                nf.createCallOrNew(Token.NEW,
-                                    nf.createName(&quot;Array&quot;))), ts.getLineno());
-                        block.addChildToBack(init);
-                        block.addChildToBack(arrayComprehension(tempName, 
-                            expr));
-                        scopeNode.addChildToBack(block);
-                        scopeNode.addChildToBack(nf.createName(tempName));
-                        return scopeNode;
-                    } finally {
-                        popScope();
-                    }
-                } else {
-                    if (!after_lb_or_comma) {
-                        reportError(&quot;msg.no.bracket.arg&quot;);
-                    }
-                    elems.add(assignExpr(false));
-                    after_lb_or_comma = false;
-                }
+            if (matchToken(Token.COMMA)) {
+                afterComma = ts.tokenEnd;
+            } else {
+                break commaLoop;
             }
-            return nf.createArrayLiteral(elems, skipCount, destructuringLen);
-          }
+        }
 
-          case Token.LC: {
-            ObjArray elems = new ObjArray();
-            decompiler.addToken(Token.LC);
-            if (!matchToken(Token.RC)) {
-
-                boolean first = true;
-            commaloop:
-                do {
-                    Object property;
-
-                    if (!first)
-                        decompiler.addToken(Token.COMMA);
-                    else
-                        first = false;
-
-                    tt = peekToken();
-                    switch(tt) {
-                      case Token.NAME:
-                      case Token.STRING:
-                        consumeToken();
-                        // map NAMEs to STRINGs in object literal context
-                        // but tell the decompiler the proper type
-                        String s = ts.getString();
-                        if (tt == Token.NAME) {
-                            if (s.equals(&quot;get&quot;) &amp;&amp;
-                                peekToken() == Token.NAME) {
-                                decompiler.addToken(Token.GET);
-                                consumeToken();
-                                s = ts.getString();
-                                decompiler.addName(s);
-                                property = ScriptRuntime.getIndexObject(s);
-                                if (!getterSetterProperty(elems, property,
-                                                          true))
-                                    break commaloop;
-                                break;
-                            } else if (s.equals(&quot;set&quot;) &amp;&amp;
-                                       peekToken() == Token.NAME) {
-                                decompiler.addToken(Token.SET);
-                                consumeToken();
-                                s = ts.getString();
-                                decompiler.addName(s);
-                                property = ScriptRuntime.getIndexObject(s);
-                                if (!getterSetterProperty(elems, property,
-                                                          false))
-                                    break commaloop;
-                                break;
-                            }
-                            decompiler.addName(s);
-                        } else {
-                            decompiler.addString(s);
-                        }
-                        property = ScriptRuntime.getIndexObject(s);
-                        plainProperty(elems, property);
-                        break;
+        mustMatchToken(Token.RC, &quot;msg.no.brace.prop&quot;);
+        ObjectLiteral pn = new ObjectLiteral(pos, ts.tokenEnd - pos);
+        pn.setElements(elems);
+        pn.setLineno(lineno);
+        return pn;
+    }
 
-                      case Token.NUMBER:
-                        consumeToken();
-                        double n = ts.getNumber();
-                        decompiler.addNumber(n);
-                        property = ScriptRuntime.getIndexObject(n);
-                        plainProperty(elems, property);
-                        break;
+    private ObjectProperty plainProperty(AstNode property)
+        throws IOException
+    {
+        mustMatchToken(Token.COLON, &quot;msg.no.colon.prop&quot;);
+        ObjectProperty pn = new ObjectProperty();
+        pn.setOperatorPosition(ts.tokenBeg);
+        pn.setLeftAndRight(property, assignExpr());
+        return pn;
+    }
 
-                      case Token.RC:
-                        // trailing comma is OK.
-                        break commaloop;
-                    default:
-                        reportError(&quot;msg.bad.prop&quot;);
-                        break commaloop;
-                    }
-                } while (matchToken(Token.COMMA));
+    private ObjectProperty getterSetterProperty(int pos, AstNode propName,
+                                                boolean isGetter)
+        throws IOException
+    {
+        FunctionNode fn = function(FunctionNode.FUNCTION_EXPRESSION);
+        // We've already parsed the function name, so fn should be anonymous.
+        Name name = fn.getFunctionName();
+        if (name != null &amp;&amp; name.length() != 0) {
+            reportError(&quot;msg.bad.prop&quot;);
+        }
+        ObjectProperty pn = new ObjectProperty(pos);
+        if (isGetter) {
+            pn.setIsGetter();
+        } else {
+            pn.setIsSetter();
+        }
+        int end = getNodeEnd(fn);
+        pn.setLeft(propName);
+        pn.setRight(fn);
+        pn.setLength(end - pos);
+        return pn;
+    }
+
+    private Name createNameNode() {
+        return createNameNode(false, Token.NAME);
+    }
 
-                mustMatchToken(Token.RC, &quot;msg.no.brace.prop&quot;);
+    /**
+     * Create a {@code Name} node using the token info from the
+     * last scanned name.  In some cases we need to either synthesize
+     * a name node, or we lost the name token information by peeking.
+     * If the {@code token} parameter is not {@link Token#NAME}, then
+     * we use token info saved in instance vars.
+     */
+    private Name createNameNode(boolean checkActivation, int token) {
+        int beg = ts.tokenBeg;
+        String s = ts.getString();
+        int lineno = ts.lineno;
+        if (currentToken != Token.NAME) {
+            beg = prevNameTokenStart;
+            s = prevNameTokenString;
+            lineno = prevNameTokenLineno;
+            prevNameTokenStart = 0;
+            prevNameTokenString = &quot;&quot;;
+            prevNameTokenLineno = 0;
+        }
+        if (s == null) {
+            if (compilerEnv.isIdeMode()) {
+                s = &quot;&quot;;
+            } else {
+                codeBug();
             }
-            decompiler.addToken(Token.RC);
-            return nf.createObjectLiteral(elems);
-          }
-          
-          case Token.LET:
-            decompiler.addToken(Token.LET);
-            return let(false);
+        }
+        Name name = new Name(beg, s);
+        name.setLineno(lineno);
+        if (checkActivation) {
+            checkActivationName(s, token);
+        }
+        return name;
+    }
 
-          case Token.LP:
+    private StringLiteral createStringLiteral() {
+        int pos = ts.tokenBeg, end = ts.tokenEnd;
+        StringLiteral s = new StringLiteral(pos, end - pos);
+        s.setValue(ts.getString());
+        s.setQuoteCharacter(ts.getQuoteChar());
+        return s;
+    }
 
-            /* Brendan's IR-jsparse.c makes a new node tagged with
-             * TOK_LP here... I'm not sure I understand why.  Isn't
-             * the grouping already implicit in the structure of the
-             * parse tree?  also TOK_LP is already overloaded (I
-             * think) in the C IR as 'function call.'  */
-            decompiler.addToken(Token.LP);
-            pn = expr(false);
-            pn.putProp(Node.PARENTHESIZED_PROP, Boolean.TRUE);
-            decompiler.addToken(Token.RP);
-            mustMatchToken(Token.RP, &quot;msg.no.paren&quot;);
-            return pn;
+    protected void checkActivationName(String name, int token) {
+        if (!insideFunction()) {
+            return;
+        }
+        boolean activation = false;
+        if (&quot;arguments&quot;.equals(name)
+            || (compilerEnv.getActivationNames() != null
+                &amp;&amp; compilerEnv.getActivationNames().contains(name)))
+        {
+            activation = true;
+        } else if (&quot;length&quot;.equals(name)) {
+            if (token == Token.GETPROP
+                &amp;&amp; compilerEnv.getLanguageVersion() == Context.VERSION_1_2)
+            {
+                // Use of &quot;length&quot; in 1.2 requires an activation object.
+                activation = true;
+            }
+        }
+        if (activation) {
+            setRequiresActivation();
+        }
+    }
 
-          case Token.XMLATTR:
-            mustHaveXML();
-            decompiler.addToken(Token.XMLATTR);
-            pn = attributeAccess(null, 0);
-            return pn;
+    protected void setRequiresActivation() {
+        if (insideFunction()) {
+            ((FunctionNode)currentScriptOrFn).setRequiresActivation();
+        }
+    }
 
-          case Token.NAME: {
-            String name = ts.getString();
-            if ((ttFlagged &amp; TI_CHECK_LABEL) != 0) {
-                if (peekToken() == Token.COLON) {
-                    // Do not consume colon, it is used as unwind indicator
-                    // to return to statementHelper.
-                    // XXX Better way?
-                    return nf.createLabel(ts.getLineno());
-                }
+    private void checkCallRequiresActivation(AstNode pn) {
+        if ((pn.getType() == Token.NAME
+             &amp;&amp; &quot;eval&quot;.equals(((Name)pn).getIdentifier()))
+            || (pn.getType() == Token.GETPROP &amp;&amp;
+                &quot;eval&quot;.equals(((PropertyGet)pn).getProperty().getIdentifier())))
+            setRequiresActivation();
+    }
+
+    protected void setIsGenerator() {
+        if (insideFunction()) {
+            ((FunctionNode)currentScriptOrFn).setIsGenerator();
+        }
+    }
+
+    private void checkBadIncDec(UnaryExpression expr) {
+        AstNode op = removeParens(expr.getOperand());
+        int tt = op.getType();
+        if (!(tt == Token.NAME
+              || tt == Token.GETPROP
+              || tt == Token.GETELEM
+              || tt == Token.GET_REF
+              || tt == Token.CALL))
+            reportError(expr.getType() == Token.INC
+                        ? &quot;msg.bad.incr&quot;
+                        : &quot;msg.bad.decr&quot;);
+    }
+
+    private ErrorNode makeErrorNode() {
+        ErrorNode pn = new ErrorNode(ts.tokenBeg, ts.tokenEnd - ts.tokenBeg);
+        pn.setLineno(ts.lineno);
+        return pn;
+    }
+
+    // Return end of node.  Assumes node does NOT have a parent yet.
+    private int nodeEnd(AstNode node) {
+        return node.getPosition() + node.getLength();
+    }
+
+    private void saveNameTokenData(int pos, String name, int lineno) {
+        prevNameTokenStart = pos;
+        prevNameTokenString = name;
+        prevNameTokenLineno = lineno;
+    }
+
+    /**
+     * Return the file offset of the beginning of the input source line
+     * containing the passed position.
+     *
+     * @param pos an offset into the input source stream.  If the offset
+     * is negative, it's converted to 0, and if it's beyond the end of
+     * the source buffer, the last source position is used.
+     *
+     * @return the offset of the beginning of the line containing pos
+     * (i.e. 1+ the offset of the first preceding newline).  Returns -1
+     * if the {@link CompilerEnvirons} is not set to ide-mode,
+     * and {@link #parse(java.io.Reader,String,int)} was used.
+     */
+    private int lineBeginningFor(int pos) {
+        if (sourceChars == null) {
+            return -1;
+        }
+        if (pos &lt;= 0) {
+            return 0;
+        }
+        char[] buf = sourceChars;
+        if (pos &gt;= buf.length) {
+            pos = buf.length - 1;
+        }
+        while (--pos &gt;= 0) {
+            char c = buf[pos];
+            if (c == '\n' || c == '\r') {
+                return pos + 1; // want position after the newline
             }
+        }
+        return 0;
+    }
 
-            decompiler.addName(name);
-            if (compilerEnv.isXmlAvailable()) {
-                pn = propertyName(null, name, 0);
-            } else {
-                pn = nf.createName(name);
+    /**
+     * Return line number for a source position.    Returns -1 if
+     * the {@link CompilerEnvirons} is not set to ide-mode,
+     * and {@link #parse(java.io.Reader,String,int)} was used.
+     */
+    private int lineFor(int pos) {
+        if (sourceChars == null) {
+            return -1;
+        }
+        char[] buf = sourceChars;
+        int count = 0, len = buf.length;
+        for (int i = 0; i &lt; len; i++) {
+            char c = buf[i];
+            if (c == '\r' || c == '\n')
+                count++;
+            if (i &gt;= pos)
+                break;
+        }
+        return count;
+    }
+
+    private void warnMissingSemi(int pos, int end) {
+        // Should probably change this to be a CompilerEnvirons setting,
+        // with an enum Never, Always, Permissive, where Permissive means
+        // don't warn for 1-line functions like function (s) {return x+2}
+        if (compilerEnv.isStrictMode()) {
+            int beg = Math.max(pos, lineBeginningFor(end));
+            if (end == -1)
+                end = ts.cursor;
+            addStrictWarning(&quot;msg.missing.semi&quot;, &quot;&quot;,
+                             beg, end - beg);
+        }
+    }
+
+    private void warnTrailingComma(String messageId, int pos,
+                                   List elems, int commaPos) {
+        if (compilerEnv.getWarnTrailingComma()) {
+            // back up from comma to beginning of line or array/objlit
+            if (!elems.isEmpty()) {
+                pos = ((AstNode)elems.get(0)).getPosition();
             }
-            return pn;
-          }
+            pos = Math.max(pos, lineBeginningFor(commaPos));
+            addWarning(&quot;msg.extra.trailing.comma&quot;, pos, commaPos - pos);
+        }
+    }
 
-          case Token.NUMBER: {
-            double n = ts.getNumber();
-            decompiler.addNumber(n);
-            return nf.createNumber(n);
-          }
 
-          case Token.STRING: {
-            String s = ts.getString();
-            decompiler.addString(s);
-            return nf.createString(s);
-          }
+    private String readFully(Reader reader) throws IOException {
+        BufferedReader in = new BufferedReader(reader);
+        try {
+            char[] cbuf = new char[1024];
+            StringBuilder sb = new StringBuilder(1024);
+            int bytes_read;
+            while ((bytes_read = in.read(cbuf, 0, 1024)) != -1) {
+                sb.append(cbuf, 0, bytes_read);
+            }
+            return sb.toString();
+        } finally {
+            in.close();
+        }
+    }
 
-          case Token.DIV:
-          case Token.ASSIGN_DIV: {
-            // Got / or /= which should be treated as regexp in fact
-            ts.readRegExp(tt);
-            String flags = ts.regExpFlags;
-            ts.regExpFlags = null;
-            String re = ts.getString();
-            decompiler.addRegexp(re, flags);
-            int index = currentScriptOrFn.addRegexp(re, flags);
-            return nf.createRegExp(index);
-          }
+    // helps reduce clutter in the already-large function() method
+    protected class PerFunctionVariables
+    {
+        private ScriptNode savedCurrentScriptOrFn;
+        private Scope savedCurrentScope;
+        private int savedNestingOfWith;
+        private int savedEndFlags;
+        private boolean savedInForInit;
+        private Map&lt;String,LabeledStatement&gt; savedLabelSet;
+        private List&lt;Loop&gt; savedLoopSet;
+        private List&lt;Jump&gt; savedLoopAndSwitchSet;
 
-          case Token.NULL:
-          case Token.THIS:
-          case Token.FALSE:
-          case Token.TRUE:
-            decompiler.addToken(tt);
-            return nf.createLeaf(tt);
+        PerFunctionVariables(FunctionNode fnNode) {
+            savedCurrentScriptOrFn = Parser.this.currentScriptOrFn;
+            Parser.this.currentScriptOrFn = fnNode;
 
-          case Token.RESERVED:
-            reportError(&quot;msg.reserved.id&quot;);
-            break;
+            savedCurrentScope = Parser.this.currentScope;
+            Parser.this.currentScope = fnNode;
 
-          case Token.ERROR:
-            /* the scanner or one of its subroutines reported the error. */
-            break;
+            savedNestingOfWith = Parser.this.nestingOfWith;
+            Parser.this.nestingOfWith = 0;
 
-          case Token.EOF:
-            reportError(&quot;msg.unexpected.eof&quot;);
-            break;
+            savedLabelSet = Parser.this.labelSet;
+            Parser.this.labelSet = null;
+
+            savedLoopSet = Parser.this.loopSet;
+            Parser.this.loopSet = null;
+
+            savedLoopAndSwitchSet = Parser.this.loopAndSwitchSet;
+            Parser.this.loopAndSwitchSet = null;
+
+            savedEndFlags = Parser.this.endFlags;
+            Parser.this.endFlags = 0;
+
+            savedInForInit = Parser.this.inForInit;
+            Parser.this.inForInit = false;
+        }
+
+        void restore() {
+            Parser.this.currentScriptOrFn = savedCurrentScriptOrFn;
+            Parser.this.currentScope = savedCurrentScope;
+            Parser.this.nestingOfWith = savedNestingOfWith;
+            Parser.this.labelSet = savedLabelSet;
+            Parser.this.loopSet = savedLoopSet;
+            Parser.this.loopAndSwitchSet = savedLoopAndSwitchSet;
+            Parser.this.endFlags = savedEndFlags;
+            Parser.this.inForInit = savedInForInit;
+        }
+    }
 
+    /**
+     * Given a destructuring assignment with a left hand side parsed
+     * as an array or object literal and a right hand side expression,
+     * rewrite as a series of assignments to the variables defined in
+     * left from property accesses to the expression on the right.
+     * @param type declaration type: Token.VAR or Token.LET or -1
+     * @param left array or object literal containing NAME nodes for
+     *        variables to assign
+     * @param right expression to assign from
+     * @return expression that performs a series of assignments to
+     *         the variables defined in left
+     */
+    Node createDestructuringAssignment(int type, Node left, Node right)
+    {
+        String tempName = currentScriptOrFn.getNextTempName();
+        Node result = destructuringAssignmentHelper(type, left, right,
+            tempName);
+        Node comma = result.getLastChild();
+        comma.addChildToBack(createName(tempName));
+        return result;
+    }
+
+    Node destructuringAssignmentHelper(int variableType, Node left,
+                                       Node right, String tempName)
+    {
+        Scope result = createScopeNode(Token.LETEXPR, left.getLineno());
+        result.addChildToFront(new Node(Token.LET,
+            createName(Token.NAME, tempName, right)));
+        try {
+            pushScope(result);
+            defineSymbol(Token.LET, tempName, true);
+        } finally {
+            popScope();
+        }
+        Node comma = new Node(Token.COMMA);
+        result.addChildToBack(comma);
+        List&lt;String&gt; destructuringNames = new ArrayList&lt;String&gt;();
+        boolean empty = true;
+        switch (left.getType()) {
+          case Token.ARRAYLIT:
+              empty = destructuringArray((ArrayLiteral)left,
+                                         variableType, tempName, comma,
+                                         destructuringNames);
+              break;
+          case Token.OBJECTLIT:
+              empty = destructuringObject((ObjectLiteral)left,
+                                          variableType, tempName, comma,
+                                          destructuringNames);
+              break;
+          case Token.GETPROP:
+          case Token.GETELEM:
+              comma.addChildToBack(simpleAssignment(left, createName(tempName)));
+              break;
           default:
-            reportError(&quot;msg.syntax&quot;);
-            break;
+              reportError(&quot;msg.bad.assign.left&quot;);
         }
-        return null;    // should never reach here
+        if (empty) {
+            // Don't want a COMMA node with no children. Just add a zero.
+            comma.addChildToBack(createNumber(0));
+        }
+        result.putProp(Node.DESTRUCTURING_NAMES, destructuringNames);
+        return result;
     }
 
-    private void plainProperty(ObjArray elems, Object property)
-            throws IOException {
-        mustMatchToken(Token.COLON, &quot;msg.no.colon.prop&quot;);
+    boolean destructuringArray(ArrayLiteral array,
+                               int variableType,
+                               String tempName,
+                               Node parent,
+                               List&lt;String&gt; destructuringNames)
+    {
+        boolean empty = true;
+        int setOp = variableType == Token.CONST
+            ? Token.SETCONST : Token.SETNAME;
+        int index = 0;
+        for (AstNode n : array.getElements()) {
+            if (n.getType() == Token.EMPTY) {
+                index++;
+                continue;
+            }
+            Node rightElem = new Node(Token.GETELEM,
+                                      createName(tempName),
+                                      createNumber(index));
+            if (n.getType() == Token.NAME) {
+                String name = n.getString();
+                parent.addChildToBack(new Node(setOp,
+                                              createName(Token.BINDNAME,
+                                                         name, null),
+                                              rightElem));
+                if (variableType != -1) {
+                    defineSymbol(variableType, name, true);
+                    destructuringNames.add(name);
+                }
+            } else {
+                parent.addChildToBack
+                    (destructuringAssignmentHelper
+                     (variableType, n,
+                      rightElem,
+                      currentScriptOrFn.getNextTempName()));
+            }
+            index++;
+            empty = false;
+        }
+        return empty;
+    }
 
-        // OBJLIT is used as ':' in object literal for
-        // decompilation to solve spacing ambiguity.
-        decompiler.addToken(Token.OBJECTLIT);
-        elems.add(property);
-        elems.add(assignExpr(false));
+    boolean destructuringObject(ObjectLiteral node,
+                                int variableType,
+                                String tempName,
+                                Node parent,
+                                List&lt;String&gt; destructuringNames)
+    {
+        boolean empty = true;
+        int setOp = variableType == Token.CONST
+            ? Token.SETCONST : Token.SETNAME;
+
+        for (ObjectProperty prop : node.getElements()) {
+            AstNode id = prop.getLeft();
+            Node rightElem = null;
+            if (id instanceof Name) {
+                Node s = Node.newString(((Name)id).getIdentifier());
+                rightElem = new Node(Token.GETPROP, createName(tempName), s);
+            } else if (id instanceof StringLiteral) {
+                Node s = Node.newString(((StringLiteral)id).getValue());
+                rightElem = new Node(Token.GETPROP, createName(tempName), s);
+            } else if (id instanceof NumberLiteral) {
+                Node s = createNumber((int)((NumberLiteral)id).getNumber());
+                rightElem = new Node(Token.GETELEM, createName(tempName), s);
+            } else {
+                throw codeBug();
+            }
+
+            AstNode value = prop.getRight();
+            if (value.getType() == Token.NAME) {
+                String name = ((Name)value).getIdentifier();
+                parent.addChildToBack(new Node(setOp,
+                                              createName(Token.BINDNAME,
+                                                         name, null),
+                                              rightElem));
+                if (variableType != -1) {
+                    defineSymbol(variableType, name, true);
+                    destructuringNames.add(name);
+                }
+            } else {
+                parent.addChildToBack
+                    (destructuringAssignmentHelper
+                     (variableType, value, rightElem,
+                      currentScriptOrFn.getNextTempName()));
+            }
+            empty = false;
+        }
+        return empty;
     }
 
-    private boolean getterSetterProperty(ObjArray elems, Object property,
-                                         boolean isGetter) throws IOException {
-        Node f = function(FunctionNode.FUNCTION_EXPRESSION);
-        if (f.getType() != Token.FUNCTION) {
-            reportError(&quot;msg.bad.prop&quot;);
-            return false;
+    protected Node createName(String name) {
+        checkActivationName(name, Token.NAME);
+        return Node.newString(Token.NAME, name);
+    }
+
+    protected Node createName(int type, String name, Node child) {
+        Node result = createName(name);
+        result.setType(type);
+        if (child != null)
+            result.addChildToBack(child);
+        return result;
+    }
+
+    protected Node createNumber(double number) {
+        return Node.newNumber(number);
+    }
+
+    /**
+     * Create a node that can be used to hold lexically scoped variable
+     * definitions (via let declarations).
+     *
+     * @param token the token of the node to create
+     * @param lineno line number of source
+     * @return the created node
+     */
+    protected Scope createScopeNode(int token, int lineno) {
+        Scope scope =new Scope();
+        scope.setType(token);
+        scope.setLineno(lineno);
+        return scope;
+    }
+
+    // Quickie tutorial for some of the interpreter bytecodes.
+    //
+    // GETPROP - for normal foo.bar prop access; right side is a name
+    // GETELEM - for normal foo[bar] element access; rhs is an expr
+    // SETPROP - for assignment when left side is a GETPROP
+    // SETELEM - for assignment when left side is a GETELEM
+    // DELPROP - used for delete foo.bar or foo[bar]
+    //
+    // GET_REF, SET_REF, DEL_REF - in general, these mean you're using
+    // get/set/delete on a right-hand side expression (possibly with no
+    // explicit left-hand side) that doesn't use the normal JavaScript
+    // Object (i.e. ScriptableObject) get/set/delete functions, but wants
+    // to provide its own versions instead.  It will ultimately implement
+    // Ref, and currently SpecialRef (for __proto__ etc.) and XmlName
+    // (for E4X XML objects) are the only implementations.  The runtime
+    // notices these bytecodes and delegates get/set/delete to the object.
+    //
+    // BINDNAME:  used in assignments.  LHS is evaluated first to get a
+    // specific object containing the property (&quot;binding&quot; the property
+    // to the object) so that it's always the same object, regardless of
+    // side effects in the RHS.
+
+    protected Node simpleAssignment(Node left, Node right) {
+        int nodeType = left.getType();
+        switch (nodeType) {
+          case Token.NAME:
+              left.setType(Token.BINDNAME);
+              return new Node(Token.SETNAME, left, right);
+
+          case Token.GETPROP:
+          case Token.GETELEM: {
+              Node obj, id;
+              // If it's a PropertyGet or ElementGet, we're in the parse pass.
+              // We could alternately have PropertyGet and ElementGet
+              // override getFirstChild/getLastChild and return the appropriate
+              // field, but that seems just as ugly as this casting.
+              if (left instanceof PropertyGet) {
+                  obj = ((PropertyGet)left).getTarget();
+                  id = ((PropertyGet)left).getProperty();
+              } else if (left instanceof ElementGet) {
+                  obj = ((ElementGet)left).getTarget();
+                  id = ((ElementGet)left).getElement();
+              } else {
+                  // This branch is called during IRFactory transform pass.
+                  obj = left.getFirstChild();
+                  id = left.getLastChild();
+              }
+              int type;
+              if (nodeType == Token.GETPROP) {
+                  type = Token.SETPROP;
+              } else {
+                  type = Token.SETELEM;
+              }
+              return new Node(type, obj, id, right);
+          }
+          case Token.GET_REF: {
+              Node ref = left.getFirstChild();
+              checkMutableReference(ref);
+              return new Node(Token.SET_REF, ref, right);
+          }
         }
-        int fnIndex = f.getExistingIntProp(Node.FUNCTION_PROP);
-        FunctionNode fn = currentScriptOrFn.getFunctionNode(fnIndex);
-        if (fn.getFunctionName().length() != 0) {
-            reportError(&quot;msg.bad.prop&quot;);
-            return false;
+
+        throw codeBug();
+    }
+
+    protected void checkMutableReference(Node n) {
+        int memberTypeFlags = n.getIntProp(Node.MEMBER_TYPE_PROP, 0);
+        if ((memberTypeFlags &amp; Node.DESCENDANTS_FLAG) != 0) {
+            reportError(&quot;msg.bad.assign.left&quot;);
         }
-        elems.add(property);
-        if (isGetter) {
-            elems.add(nf.createUnary(Token.GET, f));
-        } else {
-            elems.add(nf.createUnary(Token.SET, f));
+    }
+
+    // remove any ParenthesizedExpression wrappers
+    protected AstNode removeParens(AstNode node) {
+        while (node instanceof ParenthesizedExpression) {
+            node = ((ParenthesizedExpression)node).getExpression();
         }
-        return true;
+        return node;
+    }
+
+    void markDestructuring(AstNode node) {
+        if (node instanceof DestructuringForm) {
+            ((DestructuringForm)node).setIsDestructuring(true);
+        } else if (node instanceof ParenthesizedExpression) {
+            markDestructuring(((ParenthesizedExpression)node).getExpression());
+        }
+    }
+
+    // throw a failed-assertion with some helpful debugging info
+    private RuntimeException codeBug() 
+        throws RuntimeException
+    {
+        throw Kit.codeBug(&quot;ts.cursor=&quot; + ts.cursor
+                          + &quot;, ts.tokenBeg=&quot; + ts.tokenBeg
+                          + &quot;, currentToken=&quot; + currentToken);
     }
 }</diff>
      <filename>src/org/mozilla/javascript/Parser.java</filename>
    </modified>
    <modified>
      <diff>@@ -54,6 +54,7 @@ import java.text.MessageFormat;
 import java.util.Locale;
 import java.util.ResourceBundle;
 
+import org.mozilla.javascript.ast.FunctionNode;
 import org.mozilla.javascript.xml.XMLObject;
 import org.mozilla.javascript.xml.XMLLib;
 </diff>
      <filename>src/org/mozilla/javascript/ScriptRuntime.java</filename>
    </modified>
    <modified>
      <diff>@@ -56,6 +56,9 @@ package org.mozilla.javascript;
 
 public class Token
 {
+    public static enum CommentType {
+        LINE, BLOCK, JSDOC, HTML
+    }
 
     // debug flags
     public static final boolean printTrees = false;
@@ -258,13 +261,29 @@ public class Token
         LETEXPR        = 157,
         WITHEXPR       = 158,
         DEBUGGER       = 159,
-        LAST_TOKEN     = 159;
+        COMMENT        = 160,
+        LAST_TOKEN     = 160;
 
+    /**
+     * Returns a name for the token.  If Rhino is compiled with certain
+     * hardcoded debugging flags in this file, it calls {@code #typeToName};
+     * otherwise it returns a string whose value is the token number.
+     */
     public static String name(int token)
     {
         if (!printNames) {
             return String.valueOf(token);
         }
+        return typeToName(token);
+    }
+
+    /**
+     * Always returns a human-readable string for the token name.
+     * For instance, {@link #FINALLY} has the name &quot;FINALLY&quot;.
+     * @param token the token code
+     * @return the actual name for the token code
+     */
+    public static String typeToName(int token) {
         switch (token) {
           case ERROR:           return &quot;ERROR&quot;;
           case EOF:             return &quot;EOF&quot;;
@@ -315,7 +334,7 @@ public class Token
           case TRUE:            return &quot;TRUE&quot;;
           case SHEQ:            return &quot;SHEQ&quot;;
           case SHNE:            return &quot;SHNE&quot;;
-          case REGEXP:          return &quot;OBJECT&quot;;
+          case REGEXP:          return &quot;REGEXP&quot;;
           case BINDNAME:        return &quot;BINDNAME&quot;;
           case THROW:           return &quot;THROW&quot;;
           case RETHROW:         return &quot;RETHROW&quot;;
@@ -426,9 +445,20 @@ public class Token
           case WITHEXPR:        return &quot;WITHEXPR&quot;;
           case LETEXPR:         return &quot;LETEXPR&quot;;
           case DEBUGGER:        return &quot;DEBUGGER&quot;;
+          case COMMENT:         return &quot;COMMENT&quot;;
         }
 
         // Token without name
         throw new IllegalStateException(String.valueOf(token));
     }
+
+    /**
+     * Return true if the passed code is a valid Token constant.
+     * @param code a potential token code
+     * @return true if it's a known token
+     */
+    public static boolean isValidToken(int code) {
+        return code &gt;= ERROR
+                &amp;&amp; code &lt;= LAST_TOKEN;
+    }
 }</diff>
      <filename>src/org/mozilla/javascript/Token.java</filename>
    </modified>
    <modified>
      <diff>@@ -29,6 +29,7 @@
  *   Bob Jervis
  *   Terry Lucas
  *   Milen Nankov
+ *   Steve Yegge
  *
  * Alternatively, the contents of this file may be used under the terms of
  * the GNU General Public License Version 2 or later (the &quot;GPL&quot;), in which
@@ -83,7 +84,7 @@ class TokenStream
             this.sourceString = sourceString;
             this.sourceEnd = sourceString.length();
         }
-        this.sourceCursor = 0;
+        this.sourceCursor = this.cursor = 0;
     }
 
     /* This function uses the cached op, string and number fields in
@@ -295,6 +296,10 @@ class TokenStream
 
     final String getString() { return string; }
 
+    final char getQuoteChar() {
+        return (char) quoteChar;
+    }
+
     final double getNumber() { return number; }
 
     final boolean eof() { return hitEOF; }
@@ -309,9 +314,13 @@ class TokenStream
             for (;;) {
                 c = getChar();
                 if (c == EOF_CHAR) {
+                    tokenBeg = cursor - 1;
+                    tokenEnd = cursor;
                     return Token.EOF;
                 } else if (c == '\n') {
                     dirtyLine = false;
+                    tokenBeg = cursor - 1;
+                    tokenEnd = cursor;
                     return Token.EOL;
                 } else if (!isJSSpace(c)) {
                     if (c != '-') {
@@ -321,6 +330,10 @@ class TokenStream
                 }
             }
 
+            // Assume the token will be 1 char - fixed up below.
+            tokenBeg = cursor - 1;
+            tokenEnd = cursor;
+
             if (c == '@') return Token.XMLATTR;
 
             // identifier/keyword/instanceof?
@@ -400,8 +413,8 @@ class TokenStream
                     // Return the corresponding token if it's a keyword
                     int result = stringToKeyword(str);
                     if (result != Token.EOF) {
-                        if ((result == Token.LET || result == Token.YIELD) &amp;&amp; 
-                            parser.compilerEnv.getLanguageVersion() 
+                        if ((result == Token.LET || result == Token.YIELD) &amp;&amp;
+                            parser.compilerEnv.getLanguageVersion()
                                &lt; Context.VERSION_1_7)
                         {
                             // LET and YIELD are tokens only in 1.7 and later
@@ -496,6 +509,7 @@ class TokenStream
                 }
                 ungetChar(c);
                 String numString = getStringFromBuffer();
+                this.string = numString;
 
                 double dval;
                 if (base == 10 &amp;&amp; !isInteger) {
@@ -522,13 +536,14 @@ class TokenStream
                 // are any escaped characters in the string, we revert to
                 // building it out of a StringBuffer.
 
-                int quoteChar = c;
+                quoteChar = c;
                 stringBufferTop = 0;
 
                 c = getChar();
             strLoop: while (c != quoteChar) {
                     if (c == '\n' || c == EOF_CHAR) {
                         ungetChar(c);
+                        tokenEnd = cursor;
                         parser.addError(&quot;msg.unterminated.string.lit&quot;);
                         return Token.ERROR;
                     }
@@ -678,20 +693,22 @@ class TokenStream
 
             case '=':
                 if (matchChar('=')) {
-                    if (matchChar('='))
+                    if (matchChar('=')) {
                         return Token.SHEQ;
-                    else
+                    } else {
                         return Token.EQ;
+                    }
                 } else {
                     return Token.ASSIGN;
                 }
 
             case '!':
                 if (matchChar('=')) {
-                    if (matchChar('='))
+                    if (matchChar('=')) {
                         return Token.SHNE;
-                    else
+                    } else {
                         return Token.NE;
+                    }
                 } else {
                     return Token.NOT;
                 }
@@ -755,24 +772,37 @@ class TokenStream
             case '/':
                 // is it a // comment?
                 if (matchChar('/')) {
+                    tokenBeg = cursor - 2;
                     skipLine();
-                    continue retry;
+                    commentType = Token.CommentType.LINE;
+                    return Token.COMMENT;
                 }
+                // is it a /* or /** comment?
                 if (matchChar('*')) {
                     boolean lookForSlash = false;
+                    tokenBeg = cursor - 2;
+                    if (matchChar('*')) {
+                        lookForSlash = true;
+                        commentType = Token.CommentType.JSDOC;
+                    } else {
+                        commentType = Token.CommentType.BLOCK;
+                    }
                     for (;;) {
                         c = getChar();
                         if (c == EOF_CHAR) {
+                            tokenEnd = cursor - 1;
                             parser.addError(&quot;msg.unterminated.comment&quot;);
-                            return Token.ERROR;
+                            return Token.COMMENT;
                         } else if (c == '*') {
                             lookForSlash = true;
                         } else if (c == '/') {
                             if (lookForSlash) {
-                                continue retry;
+                                tokenEnd = cursor;
+                                return Token.COMMENT;
                             }
                         } else {
                             lookForSlash = false;
+                            tokenEnd = cursor;
                         }
                     }
                 }
@@ -808,10 +838,11 @@ class TokenStream
                 } else if (matchChar('-')) {
                     if (!dirtyLine) {
                         // treat HTML end-comment after possible whitespace
-                        // after line start as comment-utill-eol
+                        // after line start as comment-until-eol
                         if (matchChar('&gt;')) {
                             skipLine();
-                            continue retry;
+                            commentType = Token.CommentType.HTML;
+                            return Token.COMMENT;
                         }
                     }
                     c = Token.DEC;
@@ -868,6 +899,7 @@ class TokenStream
     void readRegExp(int startToken)
         throws IOException
     {
+        int start = tokenBeg;
         stringBufferTop = 0;
         if (startToken == Token.ASSIGN_DIV) {
             // Miss-scanned /=
@@ -881,7 +913,10 @@ class TokenStream
         while ((c = getChar()) != '/' || inCharSet) {
             if (c == '\n' || c == EOF_CHAR) {
                 ungetChar(c);
-                throw parser.reportError(&quot;msg.unterminated.re.lit&quot;);
+                tokenEnd = cursor - 1;
+                this.string = new String(stringBuffer, 0, stringBufferTop);
+                parser.reportError(&quot;msg.unterminated.re.lit&quot;);
+                return;
             }
             if (c == '\\') {
                 addToString(c);
@@ -902,12 +937,15 @@ class TokenStream
                 addToString('i');
             else if (matchChar('m'))
                 addToString('m');
+            else if (matchChar('y'))  // FireFox 3
+                addToString('y');
             else
                 break;
         }
+        tokenEnd = start + stringBufferTop + 2;  // include slashes
 
         if (isAlpha(peekChar())) {
-            throw parser.reportError(&quot;msg.invalid.re.flag&quot;);
+            parser.reportError(&quot;msg.invalid.re.flag&quot;);
         }
 
         this.string = new String(stringBuffer, 0, reEnd);
@@ -915,6 +953,12 @@ class TokenStream
                                       stringBufferTop - reEnd);
     }
 
+    String readAndClearRegExpFlags() {
+        String flags = this.regExpFlags;
+        this.regExpFlags = null;
+        return flags;
+    }
+
     boolean isXMLAttribute()
     {
         return xmlIsAttribute;
@@ -931,6 +975,7 @@ class TokenStream
 
     int getNextXMLToken() throws IOException
     {
+        tokenBeg = cursor;
         stringBufferTop = 0; // remember the XML
 
         for (int c = getChar(); c != EOF_CHAR; c = getChar()) {
@@ -1073,6 +1118,7 @@ class TokenStream
             }
         }
 
+        tokenEnd = cursor;
         stringBufferTop = 0; // throw away the string in progress
         this.string = null;
         parser.addError(&quot;msg.XML.bad.form&quot;);
@@ -1196,6 +1242,7 @@ class TokenStream
 
     private String getStringFromBuffer()
     {
+        tokenEnd = cursor;
         return new String(stringBuffer, 0, stringBufferTop);
     }
 
@@ -1217,12 +1264,14 @@ class TokenStream
         if (ungetCursor != 0 &amp;&amp; ungetBuffer[ungetCursor - 1] == '\n')
             Kit.codeBug();
         ungetBuffer[ungetCursor++] = c;
+        cursor--;
     }
-    
+
     private boolean matchChar(int test) throws IOException
     {
         int c = getCharIgnoreLineEnd();
         if (c == test) {
+            tokenEnd = cursor;
             return true;
         } else {
             ungetCharIgnoreLineEnd(c);
@@ -1240,6 +1289,7 @@ class TokenStream
     private int getChar() throws IOException
     {
         if (ungetCursor != 0) {
+            cursor++;
             return ungetBuffer[--ungetCursor];
         }
 
@@ -1250,6 +1300,7 @@ class TokenStream
                     hitEOF = true;
                     return EOF_CHAR;
                 }
+                cursor++;
                 c = sourceString.charAt(sourceCursor++);
             } else {
                 if (sourceCursor == sourceEnd) {
@@ -1258,6 +1309,7 @@ class TokenStream
                         return EOF_CHAR;
                     }
                 }
+                cursor++;
                 c = sourceBuffer[sourceCursor++];
             }
 
@@ -1288,10 +1340,11 @@ class TokenStream
             return c;
         }
     }
-    
+
     private int getCharIgnoreLineEnd() throws IOException
     {
         if (ungetCursor != 0) {
+            cursor++;
             return ungetBuffer[--ungetCursor];
         }
 
@@ -1302,6 +1355,7 @@ class TokenStream
                     hitEOF = true;
                     return EOF_CHAR;
                 }
+                cursor++;
                 c = sourceString.charAt(sourceCursor++);
             } else {
                 if (sourceCursor == sourceEnd) {
@@ -1310,6 +1364,7 @@ class TokenStream
                         return EOF_CHAR;
                     }
                 }
+                cursor++;
                 c = sourceBuffer[sourceCursor++];
             }
 
@@ -1330,20 +1385,25 @@ class TokenStream
             return c;
         }
     }
-    
+
     private void ungetCharIgnoreLineEnd(int c)
     {
         ungetBuffer[ungetCursor++] = c;
+        cursor--;
     }
-    
+
     private void skipLine() throws IOException
     {
         // skip to end of line
         int c;
         while ((c = getChar()) != EOF_CHAR &amp;&amp; c != '\n') { }
         ungetChar(c);
+        tokenEnd = cursor;
     }
 
+    /**
+     * Returns the offset into the current line.
+     */
     final int getOffset()
     {
         int n = sourceCursor - lineStart;
@@ -1422,6 +1482,42 @@ class TokenStream
         return true;
     }
 
+    /**
+     * Return the current position of the scanner cursor.
+     */
+    public int getCursor() {
+        return cursor;
+    }
+
+    /**
+     * Return the absolute source offset of the last scanned token.
+     */
+    public int getTokenBeg() {
+        return tokenBeg;
+    }
+
+    /**
+     * Return the absolute source end-offset of the last scanned token.
+     */
+    public int getTokenEnd() {
+        return tokenEnd;
+    }
+
+    /**
+     * Return tokenEnd - tokenBeg
+     */
+    public int getTokenLength() {
+        return tokenEnd - tokenBeg;
+    }
+
+    /**
+     * Return the type of the last scanned comment.
+     * @return type of last scanned comment, or 0 if none have been scanned.
+     */
+    public Token.CommentType getCommentType() {
+        return commentType;
+    }
+
     // stuff other than whitespace since start of line
     private boolean dirtyLine;
 
@@ -1434,6 +1530,9 @@ class TokenStream
     private String string = &quot;&quot;;
     private double number;
 
+    // delimiter for last string literal scanned
+    private int quoteChar;
+
     private char[] stringBuffer = new char[128];
     private int stringBufferTop;
     private ObjToIntMap allStrings = new ObjToIntMap(50);
@@ -1445,14 +1544,29 @@ class TokenStream
     private boolean hitEOF = false;
 
     private int lineStart = 0;
-    private int lineno;
     private int lineEndChar = -1;
+    int lineno;
 
     private String sourceString;
     private Reader sourceReader;
     private char[] sourceBuffer;
     private int sourceEnd;
-    private int sourceCursor;
+
+    // sourceCursor is an index into a small buffer that keeps a
+    // sliding window of the source stream.
+    int sourceCursor;
+
+    // cursor is a monotonically increasing index into the original
+    // source stream, tracking exactly how far scanning has progressed.
+    // Its value is the index of the next character to be scanned.
+    int cursor;
+
+    // Record start and end positions of last scanned token.
+    int tokenBeg;
+    int tokenEnd;
+
+    // Type of last comment scanned.
+    Token.CommentType commentType;
 
     // for xml tokenizer
     private boolean xmlIsAttribute;</diff>
      <filename>src/org/mozilla/javascript/TokenStream.java</filename>
    </modified>
    <modified>
      <diff>@@ -39,6 +39,7 @@
 package org.mozilla.javascript.optimizer;
 
 import org.mozilla.javascript.*;
+import org.mozilla.javascript.ast.Jump;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -198,7 +199,7 @@ class Block
             if ( (blockEndNodeType == Token.IFNE)
                         || (blockEndNodeType == Token.IFEQ)
                                 || (blockEndNodeType == Token.GOTO) ) {
-                Node target = ((Node.Jump)blockEndNode).target;
+                Node target = ((Jump)blockEndNode).target;
                 FatBlock branchTargetBlock = theTargetBlocks.get(target);
                 target.putProp(Node.TARGETBLOCK_PROP,
                                            branchTargetBlock.realBlock);</diff>
      <filename>src/org/mozilla/javascript/optimizer/Block.java</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,6 @@
-/* ***** BEGIN LICENSE BLOCK *****
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0
  *
  * The contents of this file are subject to the Mozilla Public License Version
@@ -37,6 +39,9 @@
 package org.mozilla.javascript.optimizer;
 
 import org.mozilla.javascript.*;
+import org.mozilla.javascript.ast.AstRoot;
+import org.mozilla.javascript.ast.FunctionNode;
+import org.mozilla.javascript.ast.ScriptNode;
 
 /**
  * Generates class files from script sources.
@@ -157,9 +162,15 @@ public class ClassCompiler
                                         int lineno,
                                         String mainClassName)
     {
-        Parser p = new Parser(compilerEnv, compilerEnv.getErrorReporter());
-        ScriptOrFnNode tree = p.parse(source, sourceLocation, lineno);
-        String encodedSource = p.getEncodedSource();
+        Parser p = new Parser(compilerEnv);
+        AstRoot ast = p.parse(source, sourceLocation, lineno);
+        IRFactory irf = new IRFactory(compilerEnv);
+        ScriptNode tree = irf.transformTree(ast);
+
+        // release reference to original parse tree &amp; parser
+        irf = null;
+        ast = null;
+        p = null;
 
         Class&lt;?&gt; superClass = getTargetExtends();
         Class&lt;?&gt;[] interfaces = getTargetImplements();
@@ -175,7 +186,7 @@ public class ClassCompiler
         codegen.setMainMethodClass(mainMethodClassName);
         byte[] scriptClassBytes
             = codegen.compileToClassFile(compilerEnv, scriptClassName,
-                                         tree, encodedSource,
+                                         tree, tree.getEncodedSource(),
                                          false);
 
         if (isPrimary) {
@@ -185,7 +196,7 @@ public class ClassCompiler
         ObjToIntMap functionNames = new ObjToIntMap(functionCount);
         for (int i = 0; i != functionCount; ++i) {
             FunctionNode ofn = tree.getFunctionNode(i);
-            String name = ofn.getFunctionName();
+            String name = ofn.getName();
             if (name != null &amp;&amp; name.length() != 0) {
                 functionNames.put(name, ofn.getParamCount());
             }</diff>
      <filename>src/org/mozilla/javascript/optimizer/ClassCompiler.java</filename>
    </modified>
    <modified>
      <diff>@@ -44,6 +44,9 @@
 package org.mozilla.javascript.optimizer;
 
 import org.mozilla.javascript.*;
+import org.mozilla.javascript.ast.FunctionNode;
+import org.mozilla.javascript.ast.Jump;
+import org.mozilla.javascript.ast.ScriptNode;
 import org.mozilla.classfile.*;
 import java.util.*;
 import java.lang.reflect.Constructor;
@@ -78,7 +81,7 @@ public class Codegen implements Evaluator
     }
 
     public Object compile(CompilerEnvirons compilerEnv,
-                          ScriptOrFnNode tree,
+                          ScriptNode tree,
                           String encodedSource,
                           boolean returnFunction)
     {
@@ -156,7 +159,7 @@ public class Codegen implements Evaluator
 
     byte[] compileToClassFile(CompilerEnvirons compilerEnv,
                               String mainClassName,
-                              ScriptOrFnNode scriptOrFn,
+                              ScriptNode scriptOrFn,
                               String encodedSource,
                               boolean returnFunction)
     {
@@ -172,7 +175,7 @@ public class Codegen implements Evaluator
             scriptOrFn = scriptOrFn.getFunctionNode(0);
         }
 
-        initScriptOrFnNodesData(scriptOrFn);
+        initScriptNodesData(scriptOrFn);
 
         this.mainClassName = mainClassName;
         this.mainClassSignature
@@ -186,7 +189,7 @@ public class Codegen implements Evaluator
     }
     
     private RuntimeException reportClassFileFormatException(
-        ScriptOrFnNode scriptOrFn,
+        ScriptNode scriptOrFn,
         String message)
     {
         String msg = scriptOrFn instanceof FunctionNode
@@ -197,7 +200,7 @@ public class Codegen implements Evaluator
             scriptOrFn.getLineno(), null, 0);
     }
 
-    private void transform(ScriptOrFnNode tree)
+    private void transform(ScriptNode tree)
     {
         initOptFunctions_r(tree);
 
@@ -217,7 +220,7 @@ public class Codegen implements Evaluator
                     if (ofn.fnode.getFunctionType()
                         == FunctionNode.FUNCTION_STATEMENT)
                     {
-                        String name = ofn.fnode.getFunctionName();
+                        String name = ofn.fnode.getName();
                         if (name.length() != 0) {
                             if (possibleDirectCalls == null) {
                                 possibleDirectCalls = new HashMap&lt;String,OptFunctionNode&gt;();
@@ -242,7 +245,7 @@ public class Codegen implements Evaluator
         }
     }
 
-    private static void initOptFunctions_r(ScriptOrFnNode scriptOrFn)
+    private static void initOptFunctions_r(ScriptNode scriptOrFn)
     {
         for (int i = 0, N = scriptOrFn.getFunctionCount(); i != N; ++i) {
             FunctionNode fn = scriptOrFn.getFunctionNode(i);
@@ -251,13 +254,13 @@ public class Codegen implements Evaluator
         }
     }
 
-    private void initScriptOrFnNodesData(ScriptOrFnNode scriptOrFn)
+    private void initScriptNodesData(ScriptNode scriptOrFn)
     {
         ObjArray x = new ObjArray();
-        collectScriptOrFnNodes_r(scriptOrFn, x);
+        collectScriptNodes_r(scriptOrFn, x);
 
         int count = x.size();
-        scriptOrFnNodes = new ScriptOrFnNode[count];
+        scriptOrFnNodes = new ScriptNode[count];
         x.toArray(scriptOrFnNodes);
 
         scriptOrFnIndexes = new ObjToIntMap(count);
@@ -266,13 +269,13 @@ public class Codegen implements Evaluator
         }
     }
 
-    private static void collectScriptOrFnNodes_r(ScriptOrFnNode n,
+    private static void collectScriptNodes_r(ScriptNode n,
                                                  ObjArray x)
     {
         x.add(n);
         int nestedCount = n.getFunctionCount();
         for (int i = 0; i != nestedCount; ++i) {
-            collectScriptOrFnNodes_r(n.getFunctionNode(i), x);
+            collectScriptNodes_r(n.getFunctionNode(i), x);
         }
     }
 
@@ -314,7 +317,7 @@ public class Codegen implements Evaluator
 
         int count = scriptOrFnNodes.length;
         for (int i = 0; i != count; ++i) {
-            ScriptOrFnNode n = scriptOrFnNodes[i];
+            ScriptNode n = scriptOrFnNodes[i];
 
             BodyCodegen bodygen = new BodyCodegen();
             bodygen.cfw = cfw;
@@ -414,7 +417,7 @@ public class Codegen implements Evaluator
         cfw.stopMethod((short)(firstLocal + 1));
     }
 
-    static boolean isGenerator(ScriptOrFnNode node)
+    static boolean isGenerator(ScriptNode node)
     {
         return (node.getType() == Token.FUNCTION ) &amp;&amp;
                 ((FunctionNode)node).isGenerator();
@@ -469,7 +472,7 @@ public class Codegen implements Evaluator
         int endlabel = cfw.acquireLabel();
 
         for (int i = 0; i &lt; scriptOrFnNodes.length; i++) {
-            ScriptOrFnNode n = scriptOrFnNodes[i];
+            ScriptNode n = scriptOrFnNodes[i];
             cfw.markTableSwitchCase(startSwitch, i, (short)6);
             if (isGenerator(n)) {
                 String type = &quot;(&quot; +
@@ -558,7 +561,7 @@ public class Codegen implements Evaluator
         }
 
         for (int i = 0; i != end; ++i) {
-            ScriptOrFnNode n = scriptOrFnNodes[i];
+            ScriptNode n = scriptOrFnNodes[i];
             if (generateSwitch) {
                 if (i == 0) {
                     cfw.markTableSwitchDefault(switchStart);
@@ -859,7 +862,7 @@ public class Codegen implements Evaluator
             }
 
             for (int i = 0; i != count; ++i) {
-                ScriptOrFnNode n = scriptOrFnNodes[i];
+                ScriptNode n = scriptOrFnNodes[i];
                 if (i == 0) {
                     if (count &gt; 1) {
                         cfw.markTableSwitchDefault(switchStart);
@@ -877,7 +880,7 @@ public class Codegen implements Evaluator
                     if (n.getType() == Token.SCRIPT) {
                         cfw.addPush(&quot;&quot;);
                     } else {
-                        String name = ((FunctionNode)n).getFunctionName();
+                        String name = ((FunctionNode)n).getName();
                         cfw.addPush(name);
                     }
                     cfw.add(ByteCode.ARETURN);
@@ -1015,7 +1018,7 @@ public class Codegen implements Evaluator
         cfw.markLabel(doInit);
 
         for (int i = 0; i != scriptOrFnNodes.length; ++i) {
-            ScriptOrFnNode n = scriptOrFnNodes[i];
+            ScriptNode n = scriptOrFnNodes[i];
             int regCount = n.getRegexpCount();
             for (int j = 0; j != regCount; ++j) {
                 String reFieldName = getCompiledRegexpName(n, j);
@@ -1086,7 +1089,7 @@ public class Codegen implements Evaluator
         cfw.stopMethod((short)0);
     }
 
-    void pushRegExpArray(ClassFileWriter cfw, ScriptOrFnNode n,
+    void pushRegExpArray(ClassFileWriter cfw, ScriptNode n,
                          int contextArg, int scopeArg)
     {
         int regexpCount = n.getRegexpCount();
@@ -1217,7 +1220,7 @@ public class Codegen implements Evaluator
                 &quot;instance&quot;, &quot;Ljava/lang/Object;&quot;);
     }
 
-    int getIndex(ScriptOrFnNode n)
+    int getIndex(ScriptNode n)
     {
         return scriptOrFnIndexes.getExisting(n);
     }
@@ -1227,17 +1230,17 @@ public class Codegen implements Evaluator
         return &quot;_dt&quot; + i;
     }
 
-    String getDirectCtorName(ScriptOrFnNode n)
+    String getDirectCtorName(ScriptNode n)
     {
         return &quot;_n&quot;+getIndex(n);
     }
 
-    String getBodyMethodName(ScriptOrFnNode n)
+    String getBodyMethodName(ScriptNode n)
     {
         return &quot;_c&quot;+getIndex(n);
     }
 
-    String getBodyMethodSignature(ScriptOrFnNode n)
+    String getBodyMethodSignature(ScriptNode n)
     {
         StringBuffer sb = new StringBuffer();
         sb.append('(');
@@ -1263,7 +1266,7 @@ public class Codegen implements Evaluator
         return &quot;_i&quot;+getIndex(ofn.fnode);
     }
 
-    String getCompiledRegexpName(ScriptOrFnNode n, int regexpIndex)
+    String getCompiledRegexpName(ScriptNode n, int regexpIndex)
     {
         return &quot;_re&quot;+getIndex(n)+&quot;_&quot;+regexpIndex;
     }
@@ -1310,7 +1313,7 @@ public class Codegen implements Evaluator
     private CompilerEnvirons compilerEnv;
 
     private ObjArray directCallTargets;
-    ScriptOrFnNode[] scriptOrFnNodes;
+    ScriptNode[] scriptOrFnNodes;
     private ObjToIntMap scriptOrFnIndexes;
 
     private String mainMethodClass = DEFAULT_MAIN_METHOD_CLASS;
@@ -1587,7 +1590,7 @@ class BodyCodegen
                 epilogueLabel = cfw.acquireLabel();
             }
 
-            ArrayList&lt;Node&gt; targets = ((FunctionNode)scriptOrFn).getResumptionPoints();
+            List&lt;Node&gt; targets = ((FunctionNode)scriptOrFn).getResumptionPoints();
             if (targets != null) {
                 // get resumption point
                 generateGetGeneratorResumptionPoint();
@@ -1803,7 +1806,7 @@ class BodyCodegen
             // generate locals initialization
             Map&lt;Node,int[]&gt; liveLocals = ((FunctionNode)scriptOrFn).getLiveLocals();
             if (liveLocals != null) {
-                ArrayList&lt;Node&gt; nodes = ((FunctionNode)scriptOrFn).getResumptionPoints();
+                List&lt;Node&gt; nodes = ((FunctionNode)scriptOrFn).getResumptionPoints();
                 for (int i = 0; i &lt; nodes.size(); i++) {
                     Node node = nodes.get(i);
                     int[] live = liveLocals.get(node);
@@ -1970,7 +1973,7 @@ class BodyCodegen
               }
 
               case Token.TRY:
-                visitTryCatchFinally((Node.Jump)node, child);
+                visitTryCatchFinally((Jump)node, child);
                 break;
 
               case Token.CATCH_SCOPE:
@@ -2045,7 +2048,7 @@ class BodyCodegen
               case Token.SWITCH:
                 if (compilerEnv.isGenerateObserverCount())
                     addInstructionCount();
-                visitSwitch((Node.Jump)node, child);
+                visitSwitch((Jump)node, child);
                 break;
 
               case Token.ENTERWITH:
@@ -2139,7 +2142,7 @@ class BodyCodegen
               case Token.IFNE:
                 if (compilerEnv.isGenerateObserverCount())
                     addInstructionCount(); 
-                visitGoto((Node.Jump)node, type, child);
+                visitGoto((Jump)node, type, child);
                 break;
 
               case Token.FINALLY:
@@ -3072,7 +3075,7 @@ class BodyCodegen
         return labelId;
     }
 
-    private void visitGoto(Node.Jump node, int type, Node child)
+    private void visitGoto(Jump node, int type, Node child)
     {
         Node target = node.target;
         if (type == Token.IFEQ || type == Token.IFNE) {
@@ -3664,7 +3667,7 @@ Else pass the JS object in the aReg and 0.0 in the dReg.
         cfw.addLineNumberEntry((short)itsLineNumber);
     }
 
-    private void visitTryCatchFinally(Node.Jump node, Node child)
+    private void visitTryCatchFinally(Jump node, Node child)
     {
         /* Save the variable object, in case there are with statements
          * enclosed by the try block and we catch some exception.
@@ -3862,7 +3865,7 @@ Else pass the JS object in the aReg and 0.0 in the dReg.
         return true;
     }
 
-    private void visitSwitch(Node.Jump switchNode, Node child)
+    private void visitSwitch(Jump switchNode, Node child)
     {
         // See comments in IRFactory.createSwitch() for description
         // of SWITCH node
@@ -3872,9 +3875,9 @@ Else pass the JS object in the aReg and 0.0 in the dReg.
         short selector = getNewWordLocal();
         cfw.addAStore(selector);
 
-        for (Node.Jump caseNode = (Node.Jump)child.getNext();
+        for (Jump caseNode = (Jump)child.getNext();
              caseNode != null;
-             caseNode = (Node.Jump)caseNode.getNext())
+             caseNode = (Jump)caseNode.getNext())
         {
             if (caseNode.getType() != Token.CASE)
                 throw Codegen.badTree();
@@ -4993,7 +4996,7 @@ Else pass the JS object in the aReg and 0.0 in the dReg.
     ClassFileWriter cfw;
     Codegen codegen;
     CompilerEnvirons compilerEnv;
-    ScriptOrFnNode scriptOrFn;
+    ScriptNode scriptOrFn;
     public int scriptOrFnIndex;
     private int savedCodeOffset;
 </diff>
      <filename>src/org/mozilla/javascript/optimizer/Codegen.java</filename>
    </modified>
    <modified>
      <diff>@@ -40,6 +40,8 @@
 package org.mozilla.javascript.optimizer;
 
 import org.mozilla.javascript.*;
+import org.mozilla.javascript.ast.FunctionNode;
+import org.mozilla.javascript.ast.ScriptNode;
 
 final class OptFunctionNode
 {
@@ -49,13 +51,13 @@ final class OptFunctionNode
         fnode.setCompilerData(this);
     }
 
-    static OptFunctionNode get(ScriptOrFnNode scriptOrFn, int i)
+    static OptFunctionNode get(ScriptNode scriptOrFn, int i)
     {
         FunctionNode fnode = scriptOrFn.getFunctionNode(i);
         return (OptFunctionNode)fnode.getCompilerData();
     }
 
-    static OptFunctionNode get(ScriptOrFnNode scriptOrFn)
+    static OptFunctionNode get(ScriptNode scriptOrFn)
     {
         return (OptFunctionNode)scriptOrFn.getCompilerData();
     }</diff>
      <filename>src/org/mozilla/javascript/optimizer/OptFunctionNode.java</filename>
    </modified>
    <modified>
      <diff>@@ -39,6 +39,7 @@
 package org.mozilla.javascript.optimizer;
 
 import org.mozilla.javascript.*;
+import org.mozilla.javascript.ast.ScriptNode;
 import java.util.Map;
 
 /**
@@ -57,18 +58,18 @@ class OptTransformer extends NodeTransformer {
     }
 
     @Override
-    protected void visitNew(Node node, ScriptOrFnNode tree) {
+    protected void visitNew(Node node, ScriptNode tree) {
         detectDirectCall(node, tree);
         super.visitNew(node, tree);
     }
 
     @Override
-    protected void visitCall(Node node, ScriptOrFnNode tree) {
+    protected void visitCall(Node node, ScriptNode tree) {
         detectDirectCall(node, tree);
         super.visitCall(node, tree);
     }
 
-    private void detectDirectCall(Node node, ScriptOrFnNode tree)
+    private void detectDirectCall(Node node, ScriptNode tree)
     {
         if (tree.getType() == Token.FUNCTION) {
             Node left = node.getFirstChild();</diff>
      <filename>src/org/mozilla/javascript/optimizer/OptTransformer.java</filename>
    </modified>
    <modified>
      <diff>@@ -40,6 +40,7 @@
 package org.mozilla.javascript.optimizer;
 
 import org.mozilla.javascript.*;
+import org.mozilla.javascript.ast.ScriptNode;
 
 class Optimizer
 {
@@ -50,7 +51,7 @@ class Optimizer
 
     // It is assumed that (NumberType | AnyType) == AnyType
 
-    void optimize(ScriptOrFnNode scriptOrFn)
+    void optimize(ScriptNode scriptOrFn)
     {
         //  run on one function at a time for now
         int functionCount = scriptOrFn.getFunctionCount();</diff>
      <filename>src/org/mozilla/javascript/optimizer/Optimizer.java</filename>
    </modified>
    <modified>
      <diff>@@ -326,6 +326,9 @@ msg.no.paren.after.cond =\
 msg.no.semi.stmt =\
     missing ; before statement
 
+msg.missing.semi =\
+    missing ; after statement
+
 msg.no.name.after.dot =\
     missing name after . operator
 
@@ -479,12 +482,24 @@ msg.XML.not.available =\
 msg.too.deep.parser.recursion =\
     Too deep recursion while parsing
 
+msg.too.many.constructor.args =\
+    Too many constructor arguments
+
+msg.too.many.function.args =\
+    Too many function arguments
+
 msg.no.side.effects =\
     Code has no side effects
 
+msg.extra.trailing.semi =\
+    Extraneous trailing semicolon
+
 msg.extra.trailing.comma =\
     Trailing comma is not legal in an ECMA-262 object initializer
 
+msg.trailing.array.comma =\
+    Trailing comma in array literal has different cross-browser behavior
+
 msg.equal.as.assign =\
     Test for equality (==) mistyped as assignment (=)?
 </diff>
      <filename>src/org/mozilla/javascript/resources/Messages.properties</filename>
    </modified>
    <modified>
      <diff>@@ -1,50 +1,55 @@
-package org.mozilla.javascript.drivers;
-
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-
-import junit.framework.TestCase;
-
-import org.mozilla.javascript.Context;
-import org.mozilla.javascript.ContextFactory;
-import org.mozilla.javascript.Scriptable;
-
-public class JsTestsBase extends TestCase {
-    private int optimizationLevel;
-    
-    public void setOptimizationLevel(int level) {
-        this.optimizationLevel = level;
-    }
-  
-    public void runJsTest(Context cx, Scriptable shared, String name, String source) {
-        // create a lightweight top-level scope
-        Scriptable scope = cx.newObject(shared);
-        scope.setPrototype(shared);
-        System.out.print(name + &quot;: &quot;);
-        Object result = cx.evaluateString(scope, source,
-                &quot;jstest input&quot;, 1, null);
-        assertTrue(result != null);
-        assertTrue(&quot;success&quot;.equals(result));
-        System.out.println(&quot;passed&quot;);
-    }
-    
-    public void runJsTests(File[] tests) throws IOException {
-        ContextFactory factory = ContextFactory.getGlobal();
-        Context cx = factory.enterContext();
-        try {
-            cx.setOptimizationLevel(this.optimizationLevel);
-            Scriptable shared = cx.initStandardObjects();
-            for (File f : tests) {
-                int length = (int) f.length(); // don't worry about very long
-                                               // files
-                char[] buf = new char[length];
-                new FileReader(f).read(buf, 0, length);
-                String session = new String(buf);
-                runJsTest(cx, shared, f.getName(), session);
-            }
-        } finally {
-            Context.exit();
-        }
-    }
-}
+package org.mozilla.javascript.drivers;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.ContextFactory;
+import org.mozilla.javascript.Scriptable;
+
+public class JsTestsBase extends TestCase {
+    private int optimizationLevel;
+    
+    public void setOptimizationLevel(int level) {
+        this.optimizationLevel = level;
+    }
+  
+    public void runJsTest(Context cx, Scriptable shared, String name, String source) {
+        // create a lightweight top-level scope
+        Scriptable scope = cx.newObject(shared);
+        scope.setPrototype(shared);
+        System.out.print(name + &quot;: &quot;);
+        Object result;
+        try {
+            result = cx.evaluateString(scope, source, &quot;jstest input&quot;, 1, null);
+        } catch (RuntimeException e) {
+            System.out.println(&quot;FAILED&quot;);
+            throw e;
+        }
+        assertTrue(result != null);
+        assertTrue(&quot;success&quot;.equals(result));
+        System.out.println(&quot;passed&quot;);
+    }
+    
+    public void runJsTests(File[] tests) throws IOException {
+        ContextFactory factory = ContextFactory.getGlobal();
+        Context cx = factory.enterContext();
+        try {
+            cx.setOptimizationLevel(this.optimizationLevel);
+            Scriptable shared = cx.initStandardObjects();
+            for (File f : tests) {
+                int length = (int) f.length(); // don't worry about very long
+                                               // files
+                char[] buf = new char[length];
+                new FileReader(f).read(buf, 0, length);
+                String session = new String(buf);
+                runJsTest(cx, shared, f.getName(), session);
+            }
+        } finally {
+            Context.exit();
+        }
+    }
+}</diff>
      <filename>testsrc/org/mozilla/javascript/drivers/JsTestsBase.java</filename>
    </modified>
    <modified>
      <diff>@@ -1,37 +1,37 @@
-package org.mozilla.javascript.tests;
-
-import junit.framework.TestCase;
-
-import org.mozilla.javascript.*;
-
-/**
- * See https://bugzilla.mozilla.org/show_bug.cgi?id=419940
- * @author Norris Boyd
- */
-public class Bug419940Test extends TestCase {
-    final static int value = 12;
-
-    public static abstract class BaseFoo {
-        public abstract int doSomething();
-    }
-    public static class Foo extends BaseFoo {
-        @Override
-        public int doSomething() {
-           return value;
-        }
-    }
-   
-  public void testAdapter() {
-      String source = 
-          &quot;(new JavaAdapter(&quot; + Foo.class.getName() + &quot;, {})).doSomething();&quot;;
-
-      Context cx = ContextFactory.getGlobal().enterContext();
-      try {
-          Scriptable scope = cx.initStandardObjects();
-          Object result = cx.evaluateString(scope, source, &quot;source&quot;, 1, null);
-          assertEquals(new Integer(value), result);
-      } finally {
-          Context.exit();
-      }
-  }  
-}
+package org.mozilla.javascript.tests;
+
+import junit.framework.TestCase;
+
+import org.mozilla.javascript.*;
+
+/**
+ * See https://bugzilla.mozilla.org/show_bug.cgi?id=419940
+ * @author Norris Boyd
+ */
+public class Bug419940Test extends TestCase {
+    final static int value = 12;
+
+    public static abstract class BaseFoo {
+        public abstract int doSomething();
+    }
+    public static class Foo extends BaseFoo {
+        @Override
+        public int doSomething() {
+           return value;
+        }
+    }
+   
+  public void testAdapter() {
+      String source = 
+          &quot;(new JavaAdapter(&quot; + Foo.class.getName() + &quot;, {})).doSomething();&quot;;
+
+      Context cx = ContextFactory.getGlobal().enterContext();
+      try {
+          Scriptable scope = cx.initStandardObjects();
+          Object result = cx.evaluateString(scope, source, &quot;source&quot;, 1, null);
+          assertEquals(new Integer(value), result);
+      } finally {
+          Context.exit();
+      }
+  }  
+}</diff>
      <filename>testsrc/org/mozilla/javascript/tests/Bug419940Test.java</filename>
    </modified>
    <modified>
      <diff>@@ -1,88 +1,88 @@
-package org.mozilla.javascript.tests;
-
-import org.mozilla.javascript.*;
-import junit.framework.TestCase;
-
-/**
- * Example of defining global functions.
- * 
- * @author Norris Boyd
- */
-public class DefineFunctionPropertiesTest extends TestCase {
-    ScriptableObject global;
-    static Object key = &quot;DefineFunctionPropertiesTest&quot;;
-    
-    /**
-     * Demonstrates how to create global functions in JavaScript
-     * from static methods defined in Java.
-     */
-    @Override
-    public void setUp() {
-        Context cx = Context.enter();
-        try {
-            global = cx.initStandardObjects();
-            String[] names = { &quot;f&quot;, &quot;g&quot; };
-            global.defineFunctionProperties(names,
-                    DefineFunctionPropertiesTest.class,
-                    ScriptableObject.DONTENUM);
-        } finally {
-            Context.exit();
-        }
-    }
-    
-    /**
-     * Simple global function that doubles its input.
-     */
-    public static int f(int a) {
-        return a * 2;
-    }
-    
-    /**
-     * Simple test: call 'f' defined above
-     */
-    public void testSimpleFunction() {
-        Context cx = Context.enter();
-        try {
-            Object result = cx.evaluateString(global, &quot;f(7) + 1&quot;,
-                    &quot;test source&quot;, 1, null);
-            assertEquals(15.0, result);
-        } finally {
-            Context.exit();
-        }
-    }
-    
-    /**
-     * More complicated example: this form of call allows variable
-     * argument lists, and allows access to the 'this' object. For 
-     * a global function, the 'this' object is the global object.
-     * In this case we look up a value that we associated with the global
-     * object using {@link ScriptableObject#getAssociatedValue(Object)}.
-     */
-    public static Object g(Context cx, Scriptable thisObj, Object[] args,
-            Function funObj)
-    {
-        Object arg = args.length &gt; 0 ? args[0] : Undefined.instance;
-        Object privateValue = Undefined.instance;
-        if (thisObj instanceof ScriptableObject) {
-            privateValue = 
-                ((ScriptableObject) thisObj).getAssociatedValue(key);
-        }
-        return arg.toString() + privateValue;
-    }
-    
-    /**
-     * Associate a value with the global scope and call function 'g' 
-     * defined above.
-     */
-    public void testPrivateData() {
-        Context cx = Context.enter();
-        try {
-            global.associateValue(key, &quot;bar&quot;);
-            Object result = cx.evaluateString(global, &quot;g('foo');&quot;,
-                    &quot;test source&quot;, 1, null);
-            assertEquals(&quot;foobar&quot;, result);
-        } finally {
-            Context.exit();
-        }
-    }
-}
+package org.mozilla.javascript.tests;
+
+import org.mozilla.javascript.*;
+import junit.framework.TestCase;
+
+/**
+ * Example of defining global functions.
+ * 
+ * @author Norris Boyd
+ */
+public class DefineFunctionPropertiesTest extends TestCase {
+    ScriptableObject global;
+    static Object key = &quot;DefineFunctionPropertiesTest&quot;;
+    
+    /**
+     * Demonstrates how to create global functions in JavaScript
+     * from static methods defined in Java.
+     */
+    @Override
+    public void setUp() {
+        Context cx = Context.enter();
+        try {
+            global = cx.initStandardObjects();
+            String[] names = { &quot;f&quot;, &quot;g&quot; };
+            global.defineFunctionProperties(names,
+                    DefineFunctionPropertiesTest.class,
+                    ScriptableObject.DONTENUM);
+        } finally {
+            Context.exit();
+        }
+    }
+    
+    /**
+     * Simple global function that doubles its input.
+     */
+    public static int f(int a) {
+        return a * 2;
+    }
+    
+    /**
+     * Simple test: call 'f' defined above
+     */
+    public void testSimpleFunction() {
+        Context cx = Context.enter();
+        try {
+            Object result = cx.evaluateString(global, &quot;f(7) + 1&quot;,
+                    &quot;test source&quot;, 1, null);
+            assertEquals(15.0, result);
+        } finally {
+            Context.exit();
+        }
+    }
+    
+    /**
+     * More complicated example: this form of call allows variable
+     * argument lists, and allows access to the 'this' object. For 
+     * a global function, the 'this' object is the global object.
+     * In this case we look up a value that we associated with the global
+     * object using {@link ScriptableObject#getAssociatedValue(Object)}.
+     */
+    public static Object g(Context cx, Scriptable thisObj, Object[] args,
+            Function funObj)
+    {
+        Object arg = args.length &gt; 0 ? args[0] : Undefined.instance;
+        Object privateValue = Undefined.instance;
+        if (thisObj instanceof ScriptableObject) {
+            privateValue = 
+                ((ScriptableObject) thisObj).getAssociatedValue(key);
+        }
+        return arg.toString() + privateValue;
+    }
+    
+    /**
+     * Associate a value with the global scope and call function 'g' 
+     * defined above.
+     */
+    public void testPrivateData() {
+        Context cx = Context.enter();
+        try {
+            global.associateValue(key, &quot;bar&quot;);
+            Object result = cx.evaluateString(global, &quot;g('foo');&quot;,
+                    &quot;test source&quot;, 1, null);
+            assertEquals(&quot;foobar&quot;, result);
+        } finally {
+            Context.exit();
+        }
+    }
+}</diff>
      <filename>testsrc/org/mozilla/javascript/tests/DefineFunctionPropertiesTest.java</filename>
    </modified>
    <modified>
      <diff>@@ -1,39 +1,39 @@
-package org.mozilla.javascript.tests;
-
-import java.io.File;
-import java.io.FileFilter;
-import java.io.IOException;
-
-import org.mozilla.javascript.drivers.FileUtils;
-import org.mozilla.javascript.drivers.JsTestsBase;
-
-public class JsTestsTest extends JsTestsBase {
-    static final String baseDirectory = &quot;testsrc&quot; + File.separator + &quot;jstests&quot;;
-
-    static final String jstestsExtension = &quot;.jstest&quot;;
-
-    public void runJsTests() throws IOException {
-        File[] tests = FileUtils.recursiveListFiles(new File(baseDirectory),
-                new FileFilter() {
-                  public boolean accept(File f) {
-                      return f.getName().endsWith(jstestsExtension);
-                  }
-                });
-        runJsTests(tests);
-    }
-    
-    public void testJsTestsInterpreted() throws IOException {
-        setOptimizationLevel(-1);
-        runJsTests();
-    }
-    
-    public void testJsTestsCompiled() throws IOException {
-        setOptimizationLevel(0);
-        runJsTests();
-    }
-    
-    public void testJsTestsOptimized() throws IOException {
-        setOptimizationLevel(9);
-        runJsTests();
-    }
-}
+package org.mozilla.javascript.tests;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+
+import org.mozilla.javascript.drivers.FileUtils;
+import org.mozilla.javascript.drivers.JsTestsBase;
+
+public class JsTestsTest extends JsTestsBase {
+    static final String baseDirectory = &quot;testsrc&quot; + File.separator + &quot;jstests&quot;;
+
+    static final String jstestsExtension = &quot;.jstest&quot;;
+
+    public void runJsTests() throws IOException {
+        File[] tests = FileUtils.recursiveListFiles(new File(baseDirectory),
+                new FileFilter() {
+                  public boolean accept(File f) {
+                      return f.getName().endsWith(jstestsExtension);
+                  }
+                });
+        runJsTests(tests);
+    }
+    
+    public void testJsTestsInterpreted() throws IOException {
+        setOptimizationLevel(-1);
+        runJsTests();
+    }
+    
+    public void testJsTestsCompiled() throws IOException {
+        setOptimizationLevel(0);
+        runJsTests();
+    }
+    
+    public void testJsTestsOptimized() throws IOException {
+        setOptimizationLevel(9);
+        runJsTests();
+    }
+}</diff>
      <filename>testsrc/org/mozilla/javascript/tests/JsTestsTest.java</filename>
    </modified>
    <modified>
      <diff>@@ -16,6 +16,9 @@ import org.mozilla.javascript.Callable;
 public class ObserveInstructionCountTest extends TestCase {
     // Custom Context to store execution time.
     static class MyContext extends Context {
+        MyContext(ContextFactory factory) {
+            super(factory);
+        }
         int quota;
     }
     
@@ -31,7 +34,7 @@ public class ObserveInstructionCountTest extends TestCase {
         @Override
         protected Context makeContext()
         {
-            MyContext cx = new MyContext();
+            MyContext cx = new MyContext(this);
             // Make Rhino runtime call observeInstructionCount
             // each 500 bytecode instructions (if we're really enforcing
             // a quota of 2000, we could set this closer to 2000)</diff>
      <filename>testsrc/org/mozilla/javascript/tests/ObserveInstructionCountTest.java</filename>
    </modified>
    <modified>
      <diff>@@ -30,7 +30,7 @@ public class StrictModeApiTest extends TestCase {
         } 
         return super.hasFeature(cx, featureIndex); 
     }
-  };
+  }
   
   public void testStrictModeError() {
     contextFactory = new MyContextFactory();
@@ -38,7 +38,7 @@ public class StrictModeApiTest extends TestCase {
     try {
         global = cx.initStandardObjects();
         try {
-            runScript(&quot;({}.nonexistent)&quot;);
+            runScript(&quot;({}.nonexistent);&quot;);
             fail();
         } catch (EvaluatorException e) {
             assertTrue(e.getMessage().startsWith(&quot;Reference to undefined property&quot;));</diff>
      <filename>testsrc/org/mozilla/javascript/tests/StrictModeApiTest.java</filename>
    </modified>
  </modified>
  <removed type="array">
    <removed>
      <filename>src/org/mozilla/javascript/FunctionNode.java</filename>
    </removed>
    <removed>
      <filename>src/org/mozilla/javascript/ScriptOrFnNode.java</filename>
    </removed>
  </removed>
  <parents type="array">
    <parent>
      <id>cf29ac34566c71d6fd5d999cbfa26b968f058500</id>
    </parent>
  </parents>
  <author>
    <name>nboyd%atg.com</name>
    <email>nboyd%atg.com</email>
  </author>
  <url>http://github.com/earl/rhino-mirror/commit/757fa3e85312a929c48528723f342e068682b786</url>
  <id>757fa3e85312a929c48528723f342e068682b786</id>
  <committed-date>2008-11-05T13:37:20-08:00</committed-date>
  <authored-date>2008-11-05T13:37:20-08:00</authored-date>
  <message>Landing Steve Yegge's new Abstract Syntax Tree (AST) API changes.</message>
  <tree>b8eb1b50343aff46096e5a68454daf228afb2002</tree>
  <committer>
    <name>nboyd%atg.com</name>
    <email>nboyd%atg.com</email>
  </committer>
</commit>
