Bug 835587 - Add syntax only mode to parser, r=jorendorff.
authorBrian Hackett <bhackett1024@gmail.com>
Tue, 26 Feb 2013 08:41:57 -0700
changeset 123020 c92816f3028c79db4a16a4b5f7bb7d746080602a
parent 123019 7bdc94c723f30754739a793af634686d4cf1a80e
child 123021 01b282dacd663bedf5c73e075fb40b3845f44e85
push id24372
push useremorley@mozilla.com
push dateWed, 27 Feb 2013 13:22:59 +0000
treeherdermozilla-central@0a91da5f5eab [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs835587
milestone22.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 835587 - Add syntax only mode to parser, r=jorendorff.
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeCompiler.h
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/FoldConstants.cpp
js/src/frontend/FoldConstants.h
js/src/frontend/FullParseHandler.h
js/src/frontend/ParseNode-inl.h
js/src/frontend/ParseNode.cpp
js/src/frontend/ParseNode.h
js/src/frontend/Parser-inl.h
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/frontend/SharedContext.h
js/src/frontend/SyntaxParseHandler.h
js/src/frontend/TokenStream.cpp
js/src/frontend/TokenStream.h
js/src/gc/RootMarking.cpp
js/src/jsapi.cpp
js/src/jsprvtd.h
js/src/jsreflect.cpp
js/src/shell/js.cpp
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -12,16 +12,17 @@
 #include "frontend/BytecodeEmitter.h"
 #include "frontend/FoldConstants.h"
 #include "frontend/NameFunctions.h"
 #include "vm/GlobalObject.h"
 
 #include "jsinferinlines.h"
 
 #include "frontend/ParseMaps-inl.h"
+#include "frontend/ParseNode-inl.h"
 #include "frontend/Parser-inl.h"
 #include "frontend/SharedContext-inl.h"
 
 using namespace js;
 using namespace js::frontend;
 
 static bool
 CheckLength(JSContext *cx, size_t length)
@@ -42,22 +43,22 @@ SetSourceMap(JSContext *cx, TokenStream 
     if (tokenStream.hasSourceMap()) {
         if (!ss->setSourceMap(cx, tokenStream.releaseSourceMap(), script->filename))
             return false;
     }
     return true;
 }
 
 static bool
-CheckArgumentsWithinEval(JSContext *cx, Parser &parser, HandleFunction fun)
+CheckArgumentsWithinEval(JSContext *cx, Parser<FullParseHandler> &parser, HandleFunction fun)
 {
     if (fun->hasRest()) {
         // It's an error to use |arguments| in a function that has a rest
         // parameter.
-        parser.reportError(NULL, JSMSG_ARGUMENTS_AND_REST);
+        parser.report(ParseError, false, NULL, JSMSG_ARGUMENTS_AND_REST);
         return false;
     }
 
     // Force construction of arguments objects for functions that use
     // |arguments| within an eval.
     RootedScript script(cx, fun->nonLazyScript());
     if (script->argumentsHasVarBinding()) {
         if (!JSScript::argumentsOptimizationFailed(cx, script))
@@ -114,24 +115,24 @@ frontend::CompileScript(JSContext *cx, H
         break;
       case CompileOptions::LAZY_SOURCE:
         ss->setSourceRetrievable();
         break;
       case CompileOptions::NO_SOURCE:
         break;
     }
 
-    Parser parser(cx, options, chars, length, /* foldConstants = */ true);
+    Parser<FullParseHandler> parser(cx, options, chars, length, /* foldConstants = */ true);
     if (!parser.init())
         return UnrootedScript(NULL);
     parser.sct = sct;
 
     GlobalSharedContext globalsc(cx, scopeChain, StrictModeFromContext(cx));
 
-    ParseContext pc(&parser, &globalsc, staticLevel, /* bodyid = */ 0);
+    ParseContext<FullParseHandler> pc(&parser, &globalsc, staticLevel, /* bodyid = */ 0);
     if (!pc.init())
         return UnrootedScript(NULL);
 
     bool savedCallerFun =
         options.compileAndGo &&
         evalCaller &&
         (evalCaller->function() || evalCaller->savedCallerFun);
     Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun,
@@ -180,20 +181,19 @@ frontend::CompileScript(JSContext *cx, H
             JSFunction *fun = evalCaller->functionOrCallerFunction();
             ObjectBox *funbox = parser.newFunctionBox(fun, &pc, fun->strict());
             if (!funbox)
                 return UnrootedScript(NULL);
             bce.objectList.add(funbox);
         }
     }
 
-    TokenStream &tokenStream = parser.tokenStream;
     bool canHaveDirectives = true;
     for (;;) {
-        TokenKind tt = tokenStream.peekToken(TSF_OPERAND);
+        TokenKind tt = parser.tokenStream.peekToken(TSF_OPERAND);
         if (tt <= TOK_EOF) {
             if (tt == TOK_EOF)
                 break;
             JS_ASSERT(tt == TOK_ERROR);
             return UnrootedScript(NULL);
         }
 
         ParseNode *pn = parser.statement();
@@ -208,20 +208,20 @@ frontend::CompileScript(JSContext *cx, H
         if (!FoldConstants(cx, &pn, &parser))
             return UnrootedScript(NULL);
         if (!NameFunctions(cx, pn))
             return UnrootedScript(NULL);
 
         if (!EmitTree(cx, &bce, pn))
             return UnrootedScript(NULL);
 
-        parser.freeTree(pn);
+        parser.handler.freeTree(pn);
     }
 
-    if (!SetSourceMap(cx, tokenStream, ss, script))
+    if (!SetSourceMap(cx, parser.tokenStream, ss, script))
         return UnrootedScript(NULL);
 
     if (evalCaller && evalCaller->functionOrCallerFunction()) {
         // Watch for uses of 'arguments' within the evaluated script, both as
         // free variables and as variables redeclared with 'var'.
         RootedFunction fun(cx, evalCaller->functionOrCallerFunction());
         HandlePropertyName arguments = cx->names().arguments;
         for (AtomDefnRange r = pc.lexdeps->all(); !r.empty(); r.popFront()) {
@@ -268,16 +268,56 @@ frontend::CompileScript(JSContext *cx, H
     bce.tellDebuggerAboutCompiledScript(cx);
 
     if (sct == &mysct && !sct->complete())
         return UnrootedScript(NULL);
 
     return script;
 }
 
+bool
+frontend::ParseScript(JSContext *cx, HandleObject scopeChain,
+                      const CompileOptions &options, StableCharPtr chars, size_t length)
+{
+    if (!CheckLength(cx, length))
+        return false;
+
+    Parser<SyntaxParseHandler> parser(cx, options, chars.get(), length, /* foldConstants = */ false);
+    if (!parser.init()) {
+        cx->clearPendingException();
+        return false;
+    }
+
+    GlobalSharedContext globalsc(cx, scopeChain, StrictModeFromContext(cx));
+
+    ParseContext<SyntaxParseHandler> pc(&parser, &globalsc, 0, /* bodyid = */ 0);
+    if (!pc.init()) {
+        cx->clearPendingException();
+        return false;
+    }
+
+    for (;;) {
+        TokenKind tt = parser.tokenStream.peekToken(TSF_OPERAND);
+        if (tt <= TOK_EOF) {
+            if (tt == TOK_EOF)
+                break;
+            JS_ASSERT(tt == TOK_ERROR);
+            cx->clearPendingException();
+            return false;
+        }
+
+        if (!parser.statement()) {
+            cx->clearPendingException();
+            return false;
+        }
+    }
+
+    return true;
+}
+
 // Compile a JS function body, which might appear as the value of an event
 // handler attribute in an HTML <INPUT> tag, or in a Function() constructor.
 bool
 frontend::CompileFunctionBody(JSContext *cx, HandleFunction fun, CompileOptions options,
                               const AutoNameVector &formals, const jschar *chars, size_t length)
 {
     if (!CheckLength(cx, length))
         return false;
@@ -288,35 +328,35 @@ frontend::CompileFunctionBody(JSContext 
     SourceCompressionToken sct(cx);
     JS_ASSERT(options.sourcePolicy != CompileOptions::LAZY_SOURCE);
     if (options.sourcePolicy == CompileOptions::SAVE_SOURCE) {
         if (!ss->setSourceCopy(cx, chars, length, true, &sct))
             return false;
     }
 
     options.setCompileAndGo(false);
-    Parser parser(cx, options, chars, length, /* foldConstants = */ true);
+    Parser<FullParseHandler> parser(cx, options, chars, length, /* foldConstants = */ true);
     if (!parser.init())
         return false;
     parser.sct = &sct;
 
     JS_ASSERT(fun);
 
     fun->setArgCount(formals.length());
 
     /* FIXME: make Function format the source for a function definition. */
-    ParseNode *fn = CodeNode::create(PNK_FUNCTION, &parser);
+    ParseNode *fn = CodeNode::create(PNK_FUNCTION, &parser.handler);
     if (!fn)
         return false;
 
     fn->pn_body = NULL;
     fn->pn_funbox = NULL;
     fn->pn_cookie.makeFree();
 
-    ParseNode *argsbody = ListNode::create(PNK_ARGSBODY, &parser);
+    ParseNode *argsbody = ListNode::create(PNK_ARGSBODY, &parser.handler);
     if (!argsbody)
         return false;
     argsbody->setOp(JSOP_NOP);
     argsbody->makeEmpty();
     fn->pn_body = argsbody;
 
     Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), false, options,
                                                   /* staticLevel = */ 0, ss,
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -15,15 +15,19 @@ namespace frontend {
 
 UnrootedScript
 CompileScript(JSContext *cx, HandleObject scopeChain, HandleScript evalCaller,
               const CompileOptions &options, const jschar *chars, size_t length,
               JSString *source_ = NULL, unsigned staticLevel = 0,
               SourceCompressionToken *extraSct = NULL);
 
 bool
+ParseScript(JSContext *cx, HandleObject scopeChain,
+            const CompileOptions &options, StableCharPtr chars, size_t length);
+
+bool
 CompileFunctionBody(JSContext *cx, HandleFunction fun, CompileOptions options,
                     const AutoNameVector &formals, const jschar *chars, size_t length);
 
 } /* namespace frontend */
 } /* namespace js */
 
 #endif /* BytecodeCompiler_h__ */
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -81,17 +81,18 @@ struct frontend::StmtInfoBCE : public St
     }
 
     ptrdiff_t &guardJump() {
         JS_ASSERT(type == STMT_TRY || type == STMT_FINALLY);
         return continues;
     }
 };
 
-BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent, Parser *parser, SharedContext *sc,
+BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent,
+                                 Parser<FullParseHandler> *parser, SharedContext *sc,
                                  HandleScript script, HandleScript evalCaller, bool hasGlobalScope,
                                  unsigned lineno, bool selfHostingMode)
   : sc(sc),
     parent(parent),
     script(sc->context, script),
     prolog(sc->context, lineno),
     main(sc->context, lineno),
     current(&main),
@@ -1660,37 +1661,37 @@ BytecodeEmitter::tellDebuggerAboutCompil
     }
 }
 
 bool
 BytecodeEmitter::reportError(ParseNode *pn, unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
-    bool result = tokenStream()->reportCompileErrorNumberVA(pn, JSREPORT_ERROR, errorNumber, args);
+    bool result = tokenStream()->reportCompileErrorNumberVA(pn->pn_pos, JSREPORT_ERROR, errorNumber, args);
     va_end(args);
     return result;
 }
 
 bool
 BytecodeEmitter::reportStrictWarning(ParseNode *pn, unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
-    bool result = tokenStream()->reportStrictWarningErrorNumberVA(pn, errorNumber, args);
+    bool result = tokenStream()->reportStrictWarningErrorNumberVA(pn->pn_pos, errorNumber, args);
     va_end(args);
     return result;
 }
 
 bool
 BytecodeEmitter::reportStrictModeError(ParseNode *pn, unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
-    bool result = tokenStream()->reportStrictModeErrorNumberVA(pn, sc->strict, errorNumber, args);
+    bool result = tokenStream()->reportStrictModeErrorNumberVA(pn->pn_pos, sc->strict, errorNumber, args);
     va_end(args);
     return result;
 }
 
 static bool
 EmitNameOp(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool callContext)
 {
     JSOp op;
@@ -1938,39 +1939,39 @@ EmitNameIncDec(JSContext *cx, ParseNode 
     if (post && Emit1(cx, bce, JSOP_POP) < 0)       // RESULT
         return false;
 
     UpdateDecomposeLength(bce, start);
 
     return true;
 }
 
-static bool
-EmitElemOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
+bool
+frontend::EmitElemOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
 {
     ParseNode *left, *right;
 
     if (pn->isArity(PN_NAME)) {
         /*
          * Set left and right so pn appears to be a PNK_ELEM node, instead of
          * a PNK_DOT node. See the PNK_FOR/IN case in EmitTree, and
          * EmitDestructuringOps nearer below. In the destructuring case, the
          * base expression (pn_expr) of the name may be null, which means we
          * have to emit a JSOP_BINDNAME.
          */
         left = pn->maybeExpr();
         if (!left) {
-            left = NullaryNode::create(PNK_STRING, bce->parser);
+            left = NullaryNode::create(PNK_STRING, &bce->parser->handler);
             if (!left)
                 return false;
             left->setOp(JSOP_BINDNAME);
             left->pn_pos = pn->pn_pos;
             left->pn_atom = pn->pn_atom;
         }
-        right = NullaryNode::create(PNK_STRING, bce->parser);
+        right = NullaryNode::create(PNK_STRING, &bce->parser->handler);
         if (!right)
             return false;
         right->setOp(JSOP_STRING);
         right->pn_pos = pn->pn_pos;
         right->pn_atom = pn->pn_atom;
     } else {
         JS_ASSERT(pn->isArity(PN_BINARY));
         left = pn->pn_left;
@@ -2174,16 +2175,22 @@ EmitSwitch(JSContext *cx, BytecodeEmitte
         pn2 = pn2->expr();
     }
 #endif
 
     uint32_t caseCount = pn2->pn_count;
     uint32_t tableLength = 0;
     ScopedJSFreePtr<ParseNode*> table(NULL);
 
+    if (caseCount > JS_BIT(16)) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
+                             JSMSG_TOO_MANY_CASES);
+        return false;
+    }
+
     if (caseCount == 0 ||
         (caseCount == 1 &&
          (hasDefault = (pn2->pn_head->isKind(PNK_DEFAULT))))) {
         caseCount = 0;
         low = 0;
         high = -1;
     } else {
         bool ok = true;
@@ -4798,16 +4805,23 @@ EmitCallOrNew(JSContext *cx, BytecodeEmi
      * JSOP_{SET,INIT}PROP.
      *
      * Then (or in a call case that has no explicit reference-base
      * object) we emit JSOP_UNDEFINED to produce the undefined |this|
      * value required for calls (which non-strict mode functions
      * will box into the global object).
      */
     uint32_t argc = pn->pn_count - 1;
+
+    if (argc >= ARGC_LIMIT) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
+                             callop ? JSMSG_TOO_MANY_FUN_ARGS : JSMSG_TOO_MANY_CON_ARGS);
+        return false;
+    }
+
     bool emitArgs = true;
     ParseNode *pn2 = pn->pn_head;
     switch (pn2->getKind()) {
       case PNK_NAME:
         if (bce->selfHostingMode && pn2->name() == cx->names().callFunction)
         {
             /*
              * Special-casing of callFunction to emit bytecode that directly
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -88,17 +88,18 @@ struct BytecodeEmitter
             // Start them off moderately large, to avoid repeated resizings
             // early on.
             code.reserve(1024);
             notes.reserve(1024);
         }
     };
     EmitSection prolog, main, *current;
 
-    Parser          *const parser;  /* the parser */
+    /* the parser */
+    Parser<FullParseHandler> *const parser;
 
     HandleScript    evalCaller;     /* scripted caller info for eval and dbgapi */
 
     StmtInfoBCE     *topStmt;       /* top of statement info stack */
     StmtInfoBCE     *topScopeStmt;  /* top lexical scope statement */
     Rooted<StaticBlockObject *> blockChain;
                                     /* compile time block scope chain */
 
@@ -138,17 +139,17 @@ struct BytecodeEmitter
                                            the field |selfHostingMode| in Parser.h for details. */
 
     /*
      * Note that BytecodeEmitters are magic: they own the arena "top-of-stack"
      * space above their tempMark points. This means that you cannot alloc from
      * tempLifoAlloc and save the pointer beyond the next BytecodeEmitter
      * destruction.
      */
-    BytecodeEmitter(BytecodeEmitter *parent, Parser *parser, SharedContext *sc,
+    BytecodeEmitter(BytecodeEmitter *parent, Parser<FullParseHandler> *parser, SharedContext *sc,
                     HandleScript script, HandleScript evalCaller, bool hasGlobalScope,
                     unsigned lineno, bool selfHostingMode = false);
     bool init();
 
     ~BytecodeEmitter();
 
     bool isAliasedName(ParseNode *pn);
 
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -103,17 +103,17 @@ FoldType(JSContext *cx, ParseNode *pn, P
 
 /*
  * Fold two numeric constants.  Beware that pn1 and pn2 are recycled, unless
  * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
  * a successful call to this function.
  */
 static bool
 FoldBinaryNumeric(JSContext *cx, JSOp op, ParseNode *pn1, ParseNode *pn2,
-                  ParseNode *pn, Parser *parser)
+                  ParseNode *pn, Parser<FullParseHandler> *parser)
 {
     double d, d2;
     int32_t i, j;
 
     JS_ASSERT(pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER));
     d = pn1->pn_dval;
     d2 = pn2->pn_dval;
     switch (op) {
@@ -169,20 +169,16 @@ FoldBinaryNumeric(JSContext *cx, JSOp op
             d = js_fmod(d, d2);
         }
         break;
 
       default:;
     }
 
     /* Take care to allow pn1 or pn2 to alias pn. */
-    if (pn1 != pn)
-        parser->freeTree(pn1);
-    if (pn2 != pn)
-        parser->freeTree(pn2);
     pn->setKind(PNK_NUMBER);
     pn->setOp(JSOP_DOUBLE);
     pn->setArity(PN_NULLARY);
     pn->pn_dval = d;
     return true;
 }
 
 // Remove a ParseNode, **pnp, from a parse tree, putting another ParseNode,
@@ -239,19 +235,24 @@ Boolish(ParseNode *pn)
       case JSOP_FALSE:
         return Falsy;
 
       default:
         return Unknown;
     }
 }
 
+namespace js {
+namespace frontend {
+
+template <>
 bool
-frontend::FoldConstants(JSContext *cx, ParseNode **pnp, Parser *parser, bool inGenexpLambda,
-                        bool inCond)
+FoldConstants<FullParseHandler>(JSContext *cx, ParseNode **pnp,
+                                Parser<FullParseHandler> *parser,
+                                bool inGenexpLambda, bool inCond)
 {
     ParseNode *pn = *pnp;
     ParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
 
     JS_CHECK_RECURSION(cx, return false);
 
     switch (pn->getArity()) {
       case PN_CODE:
@@ -296,17 +297,17 @@ frontend::FoldConstants(JSContext *cx, P
                 return false;
         }
         pn1 = pn->pn_kid1;
 
         if (pn->pn_kid2) {
             if (!FoldConstants(cx, &pn->pn_kid2, parser, inGenexpLambda, pn->isKind(PNK_FORHEAD)))
                 return false;
             if (pn->isKind(PNK_FORHEAD) && pn->pn_kid2->isOp(JSOP_TRUE)) {
-                parser->freeTree(pn->pn_kid2);
+                parser->handler.freeTree(pn->pn_kid2);
                 pn->pn_kid2 = NULL;
             }
         }
         pn2 = pn->pn_kid2;
 
         if (pn->pn_kid3) {
             if (!FoldConstants(cx, &pn->pn_kid3, parser, inGenexpLambda))
                 return false;
@@ -422,17 +423,17 @@ frontend::FoldConstants(JSContext *cx, P
              * NB: pn must be a PNK_IF as PNK_CONDITIONAL can never have a null
              * kid or an empty statement for a child.
              */
             pn->setKind(PNK_STATEMENTLIST);
             pn->setArity(PN_LIST);
             pn->makeEmpty();
         }
         if (pn3 && pn3 != pn2)
-            parser->freeTree(pn3);
+            parser->handler.freeTree(pn3);
         break;
 
       case PNK_OR:
       case PNK_AND:
         if (inCond) {
             if (pn->isArity(PN_LIST)) {
                 ParseNode **listp = &pn->pn_head;
                 JS_ASSERT(*listp == pn1);
@@ -441,27 +442,27 @@ frontend::FoldConstants(JSContext *cx, P
                     Truthiness t = Boolish(pn1);
                     if (t == Unknown) {
                         listp = &pn1->pn_next;
                         continue;
                     }
                     if ((t == Truthy) == pn->isKind(PNK_OR)) {
                         for (pn2 = pn1->pn_next; pn2; pn2 = pn3) {
                             pn3 = pn2->pn_next;
-                            parser->freeTree(pn2);
+                            parser->handler.freeTree(pn2);
                             --pn->pn_count;
                         }
                         pn1->pn_next = NULL;
                         break;
                     }
                     JS_ASSERT((t == Truthy) == pn->isKind(PNK_AND));
                     if (pn->pn_count == 1)
                         break;
                     *listp = pn1->pn_next;
-                    parser->freeTree(pn1);
+                    parser->handler.freeTree(pn1);
                     --pn->pn_count;
                 } while ((pn1 = *listp) != NULL);
 
                 // We may have to change arity from LIST to BINARY.
                 pn1 = pn->pn_head;
                 if (pn->pn_count == 2) {
                     pn2 = pn1->pn_next;
                     pn1->pn_next = NULL;
@@ -478,22 +479,22 @@ frontend::FoldConstants(JSContext *cx, P
                     for (; pn1; pn2 = pn1, pn1 = pn1->pn_next)
                         ;
                     pn->pn_tail = &pn2->pn_next;
                 }
             } else {
                 Truthiness t = Boolish(pn1);
                 if (t != Unknown) {
                     if ((t == Truthy) == pn->isKind(PNK_OR)) {
-                        parser->freeTree(pn2);
+                        parser->handler.freeTree(pn2);
                         ReplaceNode(pnp, pn1);
                         pn = pn1;
                     } else {
                         JS_ASSERT((t == Truthy) == pn->isKind(PNK_AND));
-                        parser->freeTree(pn1);
+                        parser->handler.freeTree(pn1);
                         ReplaceNode(pnp, pn2);
                         pn = pn2;
                     }
                 }
             }
         }
         break;
 
@@ -550,17 +551,17 @@ frontend::FoldConstants(JSContext *cx, P
             chars[length] = 0;
             JSString *str = js_NewString<CanGC>(cx, chars, length);
             if (!str) {
                 js_free(chars);
                 return false;
             }
 
             /* Fill the buffer, advancing chars and recycling kids as we go. */
-            for (pn2 = pn1; pn2; pn2 = parser->freeTree(pn2)) {
+            for (pn2 = pn1; pn2; pn2 = parser->handler.freeTree(pn2)) {
                 JSAtom *atom = pn2->pn_atom;
                 size_t length2 = atom->length();
                 js_strncpy(chars, atom->chars(), length2);
                 chars += length2;
             }
             JS_ASSERT(*chars == 0);
 
             /* Atomize the result string and mutate pn to refer to it. */
@@ -586,18 +587,18 @@ frontend::FoldConstants(JSContext *cx, P
             if (!str)
                 return false;
             pn->pn_atom = AtomizeString<CanGC>(cx, str);
             if (!pn->pn_atom)
                 return false;
             pn->setKind(PNK_STRING);
             pn->setOp(JSOP_STRING);
             pn->setArity(PN_NULLARY);
-            parser->freeTree(pn1);
-            parser->freeTree(pn2);
+            parser->handler.freeTree(pn1);
+            parser->handler.freeTree(pn2);
             break;
         }
 
         /* Can't concatenate string literals, let's try numbers. */
         goto do_binary_op;
 
       case PNK_SUB:
       case PNK_STAR:
@@ -681,17 +682,17 @@ frontend::FoldConstants(JSContext *cx, P
               default:
                 /* Return early to dodge the common PNK_NUMBER code. */
                 return true;
             }
             pn->setKind(PNK_NUMBER);
             pn->setOp(JSOP_DOUBLE);
             pn->setArity(PN_NULLARY);
             pn->pn_dval = d;
-            parser->freeTree(pn1);
+            parser->handler.freeTree(pn1);
         } else if (pn1->isKind(PNK_TRUE) || pn1->isKind(PNK_FALSE)) {
             if (pn->isOp(JSOP_NOT)) {
                 ReplaceNode(pnp, pn1);
                 pn = pn1;
                 if (pn->isKind(PNK_TRUE)) {
                     pn->setKind(PNK_FALSE);
                     pn->setOp(JSOP_FALSE);
                 } else {
@@ -709,22 +710,34 @@ frontend::FoldConstants(JSContext *cx, P
         Truthiness t = Boolish(pn);
         if (t != Unknown) {
             /*
              * We can turn function nodes into constant nodes here, but mutating function
              * nodes is tricky --- in particular, mutating a function node that appears on
              * a method list corrupts the method list. However, methods are M's in
              * statements of the form 'this.foo = M;', which we never fold, so we're okay.
              */
-            parser->allocator.prepareNodeForMutation(pn);
+            parser->handler.prepareNodeForMutation(pn);
             if (t == Truthy) {
                 pn->setKind(PNK_TRUE);
                 pn->setOp(JSOP_TRUE);
             } else {
                 pn->setKind(PNK_FALSE);
                 pn->setOp(JSOP_FALSE);
             }
             pn->setArity(PN_NULLARY);
         }
     }
 
     return true;
 }
+
+template <>
+bool
+FoldConstants<SyntaxParseHandler>(JSContext *cx, SyntaxParseHandler::Node *pnp,
+                                  Parser<SyntaxParseHandler> *parser,
+                                  bool inGenexpLambda, bool inCond)
+{
+    return true;
+}
+
+} /* namespace frontend */
+} /* namespace js */
--- a/js/src/frontend/FoldConstants.h
+++ b/js/src/frontend/FoldConstants.h
@@ -21,16 +21,18 @@ namespace frontend {
 // the same node (unchanged or modified in place) or a new node.
 //
 // Usage:
 //    pn = parser->statement();
 //    if (!pn)
 //        return false;
 //    if (!FoldConstants(cx, &pn, parser))
 //        return false;
+template <typename ParseHandler>
 bool
-FoldConstants(JSContext *cx, ParseNode **pnp, Parser *parser, bool inGenexpLambda = false,
-              bool inCond = false);
+FoldConstants(JSContext *cx, typename ParseHandler::Node *pnp,
+              Parser<ParseHandler> *parser,
+              bool inGenexpLambda = false, bool inCond = false);
 
 } /* namespace frontend */
 } /* namespace js */
 
 #endif /* FoldConstants_h__ */
new file mode 100644
--- /dev/null
+++ b/js/src/frontend/FullParseHandler.h
@@ -0,0 +1,373 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 sw=4 et tw=78:
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef FullParseHandler_h__
+#define FullParseHandler_h__
+
+#include "ParseNode.h"
+#include "SharedContext.h"
+
+namespace js {
+namespace frontend {
+
+class FullParseHandler
+{
+    ParseNodeAllocator allocator;
+    TokenStream &tokenStream;
+    bool foldConstants;
+
+    ParseNode *allocParseNode(size_t size) {
+        JS_ASSERT(size == sizeof(ParseNode));
+        return static_cast<ParseNode *>(allocator.allocNode());
+    }
+
+    ParseNode *cloneNode(const ParseNode &other) {
+        ParseNode *node = allocParseNode(sizeof(ParseNode));
+        if (!node)
+            return NULL;
+        PodAssign(node, &other);
+        return node;
+    }
+
+  public:
+    /* new_ methods for creating parse nodes. These report OOM on context. */
+    JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
+
+    typedef ParseNode *Node;
+    typedef Definition *DefinitionNode;
+
+    FullParseHandler(JSContext *cx, TokenStream &tokenStream, bool foldConstants)
+      : allocator(cx),
+        tokenStream(tokenStream),
+        foldConstants(foldConstants)
+    {}
+
+    static Definition *null() { return NULL; }
+
+    ParseNode *freeTree(ParseNode *pn) { return allocator.freeTree(pn); }
+    void prepareNodeForMutation(ParseNode *pn) { return allocator.prepareNodeForMutation(pn); }
+    const Token &currentToken() { return tokenStream.currentToken(); }
+
+    ParseNode *newName(PropertyName *name, ParseContext<FullParseHandler> *pc,
+                       ParseNodeKind kind = PNK_NAME) {
+        ParseNode *pn = NameNode::create(kind, name, this, pc);
+        if (!pn)
+            return NULL;
+        pn->setOp(JSOP_NAME);
+        return pn;
+    }
+    ParseNode *newAtom(ParseNodeKind kind, JSAtom *atom, JSOp op = JSOP_NOP) {
+        ParseNode *pn = NullaryNode::create(kind, this);
+        if (!pn)
+            return NULL;
+        pn->setOp(op);
+        pn->pn_atom = atom;
+        return pn;
+    }
+    ParseNode *newNumber(double value, DecimalPoint decimalPoint = NoDecimal) {
+        ParseNode *pn = NullaryNode::create(PNK_NUMBER, this);
+        if (!pn)
+            return NULL;
+        pn->initNumber(value, decimalPoint);
+        return pn;
+    }
+    ParseNode *newNumber(const Token &tok) {
+        return newNumber(tok.number(), tok.decimalPoint());
+    }
+    ParseNode *newBooleanLiteral(bool cond, const TokenPos &pos) {
+        return new_<BooleanLiteral>(cond, pos);
+    }
+    ParseNode *newThisLiteral(const TokenPos &pos) {
+        return new_<ThisLiteral>(pos);
+    }
+    ParseNode *newNullLiteral(const TokenPos &pos) {
+        return new_<NullLiteral>(pos);
+    }
+    ParseNode *newConditional(ParseNode *cond, ParseNode *thenExpr, ParseNode *elseExpr) {
+        return new_<ConditionalExpression>(cond, thenExpr, elseExpr);
+    }
+
+    ParseNode *newNullary(ParseNodeKind kind) {
+        return NullaryNode::create(kind, this);
+    }
+
+    ParseNode *newUnary(ParseNodeKind kind, ParseNode *kid, JSOp op = JSOP_NOP) {
+        return new_<UnaryNode>(kind, op, kid->pn_pos, kid);
+    }
+    ParseNode *newUnary(ParseNodeKind kind, JSOp op = JSOP_NOP) {
+        return new_<UnaryNode>(kind, op, tokenStream.currentToken().pos, (ParseNode *) NULL);
+    }
+    void setUnaryKid(ParseNode *pn, ParseNode *kid) {
+        pn->pn_kid = kid;
+        pn->pn_pos.end = kid->pn_pos.end;
+    }
+
+    ParseNode *newBinary(ParseNodeKind kind, JSOp op = JSOP_NOP) {
+        return new_<BinaryNode>(kind, op, tokenStream.currentToken().pos,
+                                (ParseNode *) NULL, (ParseNode *) NULL);
+    }
+    ParseNode *newBinary(ParseNodeKind kind, ParseNode *left,
+                         JSOp op = JSOP_NOP) {
+        return new_<BinaryNode>(kind, op, left->pn_pos, left, (ParseNode *) NULL);
+    }
+    ParseNode *newBinary(ParseNodeKind kind, ParseNode *left, ParseNode *right,
+                         JSOp op = JSOP_NOP) {
+        TokenPos pos = TokenPos::make(left->pn_pos.begin, right->pn_pos.end);
+        return new_<BinaryNode>(kind, op, pos, left, right);
+    }
+    ParseNode *newBinaryOrAppend(ParseNodeKind kind, ParseNode *left, ParseNode *right,
+                                 JSOp op = JSOP_NOP) {
+        return ParseNode::newBinaryOrAppend(kind, op, left, right, this, foldConstants);
+    }
+    void setBinaryRHS(ParseNode *pn, ParseNode *rhs) {
+        JS_ASSERT(pn->isArity(PN_BINARY));
+        pn->pn_right = rhs;
+        pn->pn_pos.end = rhs->pn_pos.end;
+    }
+
+    ParseNode *newTernary(ParseNodeKind kind,
+                          ParseNode *first, ParseNode *second, ParseNode *third,
+                          JSOp op = JSOP_NOP) {
+        return new_<TernaryNode>(kind, op, first, second, third);
+    }
+
+    ParseNode *newBreak(PropertyName *label, const TokenPtr &begin, const TokenPtr &end) {
+        return new_<BreakStatement>(label, begin, end);
+    }
+    ParseNode *newContinue(PropertyName *label, const TokenPtr &begin, const TokenPtr &end) {
+        return new_<ContinueStatement>(label, begin, end);
+    }
+    ParseNode *newDebuggerStatement(const TokenPos &pos) {
+        return new_<DebuggerStatement>(pos);
+    }
+    ParseNode *newPropertyAccess(ParseNode *pn, PropertyName *name, const TokenPtr &end) {
+        return new_<PropertyAccess>(pn, name, pn->pn_pos.begin, end);
+    }
+    ParseNode *newPropertyByValue(ParseNode *pn, ParseNode *kid, const TokenPtr &end) {
+        return new_<PropertyByValue>(pn, kid, pn->pn_pos.begin, end);
+    }
+
+    inline bool addCatchBlock(ParseNode *catchList, ParseNode *letBlock,
+                              ParseNode *catchName, ParseNode *catchGuard, ParseNode *catchBody);
+
+    inline void morphNameIntoLabel(ParseNode *name, ParseNode *statement);
+
+    inline void setLeaveBlockResult(ParseNode *block, ParseNode *kid, bool leaveBlockExpr);
+
+    inline void setLastFunctionArgumentDefault(ParseNode *funcpn, ParseNode *pn);
+    inline ParseNode *newFunctionDefinition();
+    void setFunctionBody(ParseNode *pn, ParseNode *kid) {
+        pn->pn_body = kid;
+    }
+    void setFunctionBox(ParseNode *pn, FunctionBox *funbox) {
+        pn->pn_funbox = funbox;
+    }
+    bool isOperationWithoutParens(ParseNode *pn, ParseNodeKind kind) {
+        return pn->isKind(kind) && !pn->isInParens();
+    }
+
+    inline void noteLValue(ParseNode *pn);
+    inline bool finishInitializerAssignment(ParseNode *pn, ParseNode *init, JSOp op);
+
+    void setBeginPosition(ParseNode *pn, ParseNode *oth) {
+        setBeginPosition(pn, oth->pn_pos.begin);
+    }
+    void setBeginPosition(ParseNode *pn, const TokenPtr &begin) {
+        pn->pn_pos.begin = begin;
+        JS_ASSERT(pn->pn_pos.begin <= pn->pn_pos.end);
+    }
+
+    void setEndPosition(ParseNode *pn, ParseNode *oth) {
+        setEndPosition(pn, oth->pn_pos.end);
+    }
+    void setEndPosition(ParseNode *pn, const TokenPtr &end) {
+        pn->pn_pos.end = end;
+        JS_ASSERT(pn->pn_pos.begin <= pn->pn_pos.end);
+    }
+
+    TokenPos getPosition(ParseNode *pn) {
+        return pn->pn_pos;
+    }
+
+    ParseNode *newList(ParseNodeKind kind, ParseNode *kid = NULL, JSOp op = JSOP_NOP) {
+        ParseNode *pn = ListNode::create(kind, this);
+        if (!pn)
+            return NULL;
+        pn->setOp(op);
+        pn->makeEmpty();
+        if (kid) {
+            pn->pn_pos.begin = kid->pn_pos.begin;
+            pn->append(kid);
+        }
+        return pn;
+    }
+    void addList(ParseNode *pn, ParseNode *kid) {
+        pn->append(kid);
+    }
+
+    void setOp(ParseNode *pn, JSOp op) {
+        pn->setOp(op);
+    }
+    void setBlockId(ParseNode *pn, unsigned blockid) {
+        pn->pn_blockid = blockid;
+    }
+    void setFlag(ParseNode *pn, unsigned flag) {
+        pn->pn_dflags |= flag;
+    }
+    void setListFlag(ParseNode *pn, unsigned flag) {
+        JS_ASSERT(pn->isArity(PN_LIST));
+        pn->pn_xflags |= flag;
+    }
+    ParseNode *setInParens(ParseNode *pn) {
+        pn->setInParens(true);
+        return pn;
+    }
+    void setPrologue(ParseNode *pn) {
+        pn->pn_prologue = true;
+    }
+
+    bool isConstant(ParseNode *pn) {
+        return pn->isConstant();
+    }
+    PropertyName *isName(ParseNode *pn) {
+        return pn->isKind(PNK_NAME) ? pn->pn_atom->asPropertyName() : NULL;
+    }
+    PropertyName *isGetProp(ParseNode *pn) {
+        return pn->isOp(JSOP_GETPROP) ? pn->pn_atom->asPropertyName() : NULL;
+    }
+    JSAtom *isStringExprStatement(ParseNode *pn, TokenPos *pos) {
+        if (JSAtom *atom = pn->isStringExprStatement()) {
+            *pos = pn->pn_pos;
+            return atom;
+        }
+        return NULL;
+    }
+    bool isEmptySemicolon(ParseNode *pn) {
+        return pn->isKind(PNK_SEMI) && !pn->pn_kid;
+    }
+
+    inline ParseNode *makeAssignment(ParseNode *pn, ParseNode *rhs);
+};
+
+inline bool
+FullParseHandler::addCatchBlock(ParseNode *catchList, ParseNode *letBlock,
+                                ParseNode *catchName, ParseNode *catchGuard, ParseNode *catchBody)
+{
+    ParseNode *catchpn = newTernary(PNK_CATCH, catchName, catchGuard, catchBody);
+    if (!catchpn)
+        return false;
+
+    catchList->append(letBlock);
+    letBlock->pn_expr = catchpn;
+    return true;
+}
+
+inline void
+FullParseHandler::morphNameIntoLabel(ParseNode *name, ParseNode *statement)
+{
+    name->setKind(PNK_COLON);
+    name->pn_pos.end = statement->pn_pos.end;
+    name->pn_expr = statement;
+}
+
+inline void
+FullParseHandler::setLeaveBlockResult(ParseNode *block, ParseNode *kid, bool leaveBlockExpr)
+{
+    JS_ASSERT(block->isOp(JSOP_LEAVEBLOCK));
+    if (leaveBlockExpr)
+        block->setOp(JSOP_LEAVEBLOCKEXPR);
+    block->pn_expr = kid;
+}
+
+inline void
+FullParseHandler::setLastFunctionArgumentDefault(ParseNode *funcpn, ParseNode *defaultValue)
+{
+    ParseNode *arg = funcpn->pn_body->last();
+    arg->pn_dflags |= PND_DEFAULT;
+    arg->pn_expr = defaultValue;
+}
+
+inline ParseNode *
+FullParseHandler::newFunctionDefinition()
+{
+    ParseNode *pn = CodeNode::create(PNK_FUNCTION, this);
+    if (!pn)
+        return NULL;
+    pn->pn_body = NULL;
+    pn->pn_funbox = NULL;
+    pn->pn_cookie.makeFree();
+    pn->pn_dflags = 0;
+    return pn;
+}
+
+inline void
+FullParseHandler::noteLValue(ParseNode *pn)
+{
+    if (pn->isUsed())
+        pn->pn_lexdef->pn_dflags |= PND_ASSIGNED;
+
+    pn->pn_dflags |= PND_ASSIGNED;
+}
+
+inline bool
+FullParseHandler::finishInitializerAssignment(ParseNode *pn, ParseNode *init, JSOp op)
+{
+    if (pn->isUsed()) {
+        pn = makeAssignment(pn, init);
+        if (!pn)
+            return false;
+    } else {
+        pn->pn_expr = init;
+    }
+
+    pn->setOp((pn->pn_dflags & PND_BOUND)
+              ? JSOP_SETLOCAL
+              : (op == JSOP_DEFCONST)
+              ? JSOP_SETCONST
+              : JSOP_SETNAME);
+
+    noteLValue(pn);
+
+    /* The declarator's position must include the initializer. */
+    pn->pn_pos.end = init->pn_pos.end;
+    return true;
+}
+
+inline ParseNode *
+FullParseHandler::makeAssignment(ParseNode *pn, ParseNode *rhs)
+{
+    ParseNode *lhs = cloneNode(*pn);
+    if (!lhs)
+        return NULL;
+
+    if (pn->isUsed()) {
+        Definition *dn = pn->pn_lexdef;
+        ParseNode **pnup = &dn->dn_uses;
+
+        while (*pnup != pn)
+            pnup = &(*pnup)->pn_link;
+        *pnup = lhs;
+        lhs->pn_link = pn->pn_link;
+        pn->pn_link = NULL;
+    }
+
+    pn->setKind(PNK_ASSIGN);
+    pn->setOp(JSOP_NOP);
+    pn->setArity(PN_BINARY);
+    pn->setInParens(false);
+    pn->setUsed(false);
+    pn->setDefn(false);
+    pn->pn_left = lhs;
+    pn->pn_right = rhs;
+    pn->pn_pos.end = rhs->pn_pos.end;
+    return lhs;
+}
+
+} // frontend
+} // js
+
+#endif /* FullParseHandler_h__ */
--- a/js/src/frontend/ParseNode-inl.h
+++ b/js/src/frontend/ParseNode-inl.h
@@ -38,38 +38,18 @@ ParseNode::name() const
 
 inline JSAtom *
 ParseNode::atom() const
 {
     JS_ASSERT(isKind(PNK_MODULE) || isKind(PNK_STRING));
     return isKind(PNK_MODULE) ? pn_modulebox->module()->atom() : pn_atom;
 }
 
-inline bool
-ParseNode::isConstant()
-{
-    switch (pn_type) {
-      case PNK_NUMBER:
-      case PNK_STRING:
-      case PNK_NULL:
-      case PNK_FALSE:
-      case PNK_TRUE:
-        return true;
-      case PNK_ARRAY:
-      case PNK_OBJECT:
-        return isOp(JSOP_NEWINIT) && !(pn_xflags & PNX_NONCONST);
-      default:
-        return false;
-    }
-}
-
-struct ParseContext;
-
 inline void
-NameNode::initCommon(ParseContext *pc)
+NameNode::initCommon(ParseContext<FullParseHandler> *pc)
 {
     pn_expr = NULL;
     pn_cookie.makeFree();
     pn_dflags = (!pc->topStmt || pc->topStmt->type == STMT_BLOCK)
                 ? PND_BLOCKCHILD
                 : 0;
     pn_blockid = pc->blockid();
 }
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -249,36 +249,37 @@ ParseNodeAllocator::allocNode()
     if (!p)
         js_ReportOutOfMemory(cx);
     return p;
 }
 
 /* used only by static create methods of subclasses */
 
 ParseNode *
-ParseNode::create(ParseNodeKind kind, ParseNodeArity arity, Parser *parser)
+ParseNode::create(ParseNodeKind kind, ParseNodeArity arity, FullParseHandler *handler)
 {
-    const Token &tok = parser->tokenStream.currentToken();
-    return parser->new_<ParseNode>(kind, JSOP_NOP, arity, tok.pos);
+    const Token &tok = handler->currentToken();
+    return handler->new_<ParseNode>(kind, JSOP_NOP, arity, tok.pos);
 }
 
 ParseNode *
-ParseNode::append(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right, Parser *parser)
+ParseNode::append(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right,
+                  FullParseHandler *handler)
 {
     if (!left || !right)
         return NULL;
 
     JS_ASSERT(left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC));
 
     ListNode *list;
     if (left->pn_arity == PN_LIST) {
         list = &left->as<ListNode>();
     } else {
         ParseNode *pn1 = left->pn_left, *pn2 = left->pn_right;
-        list = parser->new_<ListNode>(kind, op, pn1);
+        list = handler->new_<ListNode>(kind, op, pn1);
         if (!list)
             return NULL;
         list->append(pn2);
         if (kind == PNK_ADD) {
             if (pn1->isKind(PNK_STRING))
                 list->pn_xflags |= PNX_STRCAT;
             else if (!pn1->isKind(PNK_NUMBER))
                 list->pn_xflags |= PNX_CANTFOLD;
@@ -298,56 +299,56 @@ ParseNode::append(ParseNodeKind kind, JS
             list->pn_xflags |= PNX_CANTFOLD;
     }
 
     return list;
 }
 
 ParseNode *
 ParseNode::newBinaryOrAppend(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right,
-                             Parser *parser)
+                             FullParseHandler *handler, bool foldConstants)
 {
     if (!left || !right)
         return NULL;
 
     /*
      * Flatten a left-associative (left-heavy) tree of a given operator into
      * a list to reduce js::FoldConstants and js::frontend::EmitTree recursion.
      */
     if (left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC))
-        return append(kind, op, left, right, parser);
+        return append(kind, op, left, right, handler);
 
     /*
      * Fold constant addition immediately, to conserve node space and, what's
      * more, so js::FoldConstants never sees mixed addition and concatenation
      * operations with more than one leading non-string operand in a PN_LIST
      * generated for expressions such as 1 + 2 + "pt" (which should evaluate
      * to "3pt", not "12pt").
      */
     if (kind == PNK_ADD &&
         left->isKind(PNK_NUMBER) &&
         right->isKind(PNK_NUMBER) &&
-        parser->foldConstants)
+        foldConstants)
     {
         left->pn_dval += right->pn_dval;
         left->pn_pos.end = right->pn_pos.end;
-        parser->freeTree(right);
+        handler->freeTree(right);
         return left;
     }
 
-    return parser->new_<BinaryNode>(kind, op, left, right);
+    return handler->new_<BinaryNode>(kind, op, left, right);
 }
 
-// Nb: unlike most functions that are passed a Parser, this one gets a
-// SharedContext passed in separately, because in this case |pc| may not equal
-// |parser->pc|.
+// Note: the parse context passed into this may not equal the associated
+// parser's current context.
 NameNode *
-NameNode::create(ParseNodeKind kind, JSAtom *atom, Parser *parser, ParseContext *pc)
+NameNode::create(ParseNodeKind kind, JSAtom *atom, FullParseHandler *handler,
+                 ParseContext<FullParseHandler> *pc)
 {
-    ParseNode *pn = ParseNode::create(kind, PN_NAME, parser);
+    ParseNode *pn = ParseNode::create(kind, PN_NAME, handler);
     if (pn) {
         pn->pn_atom = atom;
         ((NameNode *)pn)->initCommon(pc);
     }
     return (NameNode *)pn;
 }
 
 const char *
@@ -356,98 +357,100 @@ Definition::kindString(Kind kind)
     static const char *table[] = {
         js_var_str, js_const_str, js_let_str, js_function_str, "argument", "unknown"
     };
 
     JS_ASSERT(unsigned(kind) <= unsigned(ARG));
     return table[kind];
 }
 
+namespace js {
+namespace frontend {
+
 #if JS_HAS_DESTRUCTURING
 
 /*
  * This function assumes the cloned tree is for use in the same statement and
  * binding context as the original tree.
  */
-static ParseNode *
-CloneParseTree(ParseNode *opn, Parser *parser)
+template <>
+ParseNode *
+Parser<FullParseHandler>::cloneParseTree(ParseNode *opn)
 {
-    ParseContext *pc = parser->pc;
+    JS_CHECK_RECURSION(context, return NULL);
 
-    JS_CHECK_RECURSION(pc->sc->context, return NULL);
-
-    ParseNode *pn = parser->new_<ParseNode>(opn->getKind(), opn->getOp(), opn->getArity(),
+    ParseNode *pn = handler.new_<ParseNode>(opn->getKind(), opn->getOp(), opn->getArity(),
                                             opn->pn_pos);
     if (!pn)
         return NULL;
     pn->setInParens(opn->isInParens());
     pn->setDefn(opn->isDefn());
     pn->setUsed(opn->isUsed());
 
     switch (pn->getArity()) {
 #define NULLCHECK(e)    JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO
 
       case PN_CODE:
         if (pn->getKind() == PNK_MODULE) {
             JS_NOT_REACHED("module nodes cannot be cloned");
             return NULL;
         } else {
             NULLCHECK(pn->pn_funbox =
-                      parser->newFunctionBox(opn->pn_funbox->function(), pc, opn->pn_funbox->strict));
-            NULLCHECK(pn->pn_body = CloneParseTree(opn->pn_body, parser));
+                      newFunctionBox(opn->pn_funbox->function(), pc, opn->pn_funbox->strict));
+            NULLCHECK(pn->pn_body = cloneParseTree(opn->pn_body));
             pn->pn_cookie = opn->pn_cookie;
             pn->pn_dflags = opn->pn_dflags;
             pn->pn_blockid = opn->pn_blockid;
         }
         break;
 
       case PN_LIST:
         pn->makeEmpty();
         for (ParseNode *opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) {
             ParseNode *pn2;
-            NULLCHECK(pn2 = CloneParseTree(opn2, parser));
+            NULLCHECK(pn2 = cloneParseTree(opn2));
             pn->append(pn2);
         }
         pn->pn_xflags = opn->pn_xflags;
         break;
 
       case PN_TERNARY:
-        NULLCHECK(pn->pn_kid1 = CloneParseTree(opn->pn_kid1, parser));
-        NULLCHECK(pn->pn_kid2 = CloneParseTree(opn->pn_kid2, parser));
-        NULLCHECK(pn->pn_kid3 = CloneParseTree(opn->pn_kid3, parser));
+        NULLCHECK(pn->pn_kid1 = cloneParseTree(opn->pn_kid1));
+        NULLCHECK(pn->pn_kid2 = cloneParseTree(opn->pn_kid2));
+        NULLCHECK(pn->pn_kid3 = cloneParseTree(opn->pn_kid3));
         break;
 
       case PN_BINARY:
-        NULLCHECK(pn->pn_left = CloneParseTree(opn->pn_left, parser));
+        NULLCHECK(pn->pn_left = cloneParseTree(opn->pn_left));
         if (opn->pn_right != opn->pn_left)
-            NULLCHECK(pn->pn_right = CloneParseTree(opn->pn_right, parser));
+            NULLCHECK(pn->pn_right = cloneParseTree(opn->pn_right));
         else
             pn->pn_right = pn->pn_left;
         pn->pn_iflags = opn->pn_iflags;
         break;
 
       case PN_UNARY:
-        NULLCHECK(pn->pn_kid = CloneParseTree(opn->pn_kid, parser));
+        NULLCHECK(pn->pn_kid = cloneParseTree(opn->pn_kid));
         pn->pn_hidden = opn->pn_hidden;
         break;
 
       case PN_NAME:
         // PN_NAME could mean several arms in pn_u, so copy the whole thing.
         pn->pn_u = opn->pn_u;
         if (opn->isUsed()) {
             /*
              * The old name is a use of its pn_lexdef. Make the clone also be a
              * use of that definition.
              */
             Definition *dn = pn->pn_lexdef;
 
             pn->pn_link = dn->dn_uses;
             dn->dn_uses = pn;
         } else if (opn->pn_expr) {
-            NULLCHECK(pn->pn_expr = CloneParseTree(opn->pn_expr, parser));
+            NULLCHECK(pn->pn_expr = cloneParseTree(opn->pn_expr));
 
             /*
              * If the old name is a definition, the new one has pn_defn set.
              * Make the old name a use of the new node.
              */
             if (opn->isDefn()) {
                 opn->setDefn(false);
                 LinkUseToDef(opn, (Definition *) pn);
@@ -471,20 +474,21 @@ CloneParseTree(ParseNode *opn, Parser *p
  *   for (var/const/let TARGET in EXPR)
  *
  * opn must be the pn_head of a node produced by Parser::variables, so its form
  * is known to be LHS = NAME | [LHS] | {id:LHS}.
  *
  * The cloned tree is for use only in the same statement and binding context as
  * the original tree.
  */
+template <>
 ParseNode *
-frontend::CloneLeftHandSide(ParseNode *opn, Parser *parser)
+Parser<FullParseHandler>::cloneLeftHandSide(ParseNode *opn)
 {
-    ParseNode *pn = parser->new_<ParseNode>(opn->getKind(), opn->getOp(), opn->getArity(),
+    ParseNode *pn = handler.new_<ParseNode>(opn->getKind(), opn->getOp(), opn->getArity(),
                                             opn->pn_pos);
     if (!pn)
         return NULL;
     pn->setInParens(opn->isInParens());
     pn->setDefn(opn->isDefn());
     pn->setUsed(opn->isUsed());
 
 #if JS_HAS_DESTRUCTURING
@@ -492,29 +496,29 @@ frontend::CloneLeftHandSide(ParseNode *o
         JS_ASSERT(opn->isKind(PNK_ARRAY) || opn->isKind(PNK_OBJECT));
         pn->makeEmpty();
         for (ParseNode *opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) {
             ParseNode *pn2;
             if (opn->isKind(PNK_OBJECT)) {
                 JS_ASSERT(opn2->isArity(PN_BINARY));
                 JS_ASSERT(opn2->isKind(PNK_COLON));
 
-                ParseNode *tag = CloneParseTree(opn2->pn_left, parser);
+                ParseNode *tag = cloneParseTree(opn2->pn_left);
                 if (!tag)
                     return NULL;
-                ParseNode *target = CloneLeftHandSide(opn2->pn_right, parser);
+                ParseNode *target = cloneLeftHandSide(opn2->pn_right);
                 if (!target)
                     return NULL;
 
-                pn2 = parser->new_<BinaryNode>(PNK_COLON, JSOP_INITPROP, opn2->pn_pos, tag, target);
+                pn2 = handler.new_<BinaryNode>(PNK_COLON, JSOP_INITPROP, opn2->pn_pos, tag, target);
             } else if (opn2->isArity(PN_NULLARY)) {
                 JS_ASSERT(opn2->isKind(PNK_COMMA));
-                pn2 = CloneParseTree(opn2, parser);
+                pn2 = cloneParseTree(opn2);
             } else {
-                pn2 = CloneLeftHandSide(opn2, parser);
+                pn2 = cloneLeftHandSide(opn2);
             }
 
             if (!pn2)
                 return NULL;
             pn->append(pn2);
         }
         pn->pn_xflags = opn->pn_xflags;
         return pn;
@@ -541,16 +545,19 @@ frontend::CloneLeftHandSide(ParseNode *o
             pn->setDefn(false);
 
             LinkUseToDef(pn, (Definition *) opn);
         }
     }
     return pn;
 }
 
+} /* namespace frontend */
+} /* namespace js */
+
 #ifdef DEBUG
 
 static const char *parseNodeNames[] = {
 #define STRINGIFY(name) #name,
     FOR_EACH_PARSE_NODE_KIND(STRINGIFY)
 #undef STRINGIFY
 };
 
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -13,18 +13,21 @@
 #include "jsscript.h"
 
 #include "frontend/ParseMaps.h"
 #include "frontend/TokenStream.h"
 
 namespace js {
 namespace frontend {
 
+template <typename ParseHandler>
 struct ParseContext;
 
+class FullParseHandler;
+
 /*
  * Indicates a location in the stack that an upvar value can be retrieved from
  * as a two tuple of (level, slot).
  *
  * Some existing client code uses the level value as a delta, or level "skip"
  * quantity. We could probably document that through use of more types at some
  * point in the future.
  */
@@ -565,34 +568,34 @@ struct ParseNode {
         pn_op = op;
         pn_arity = arity;
         pn_parens = false;
         JS_ASSERT(!pn_used);
         JS_ASSERT(!pn_defn);
         pn_next = pn_link = NULL;
     }
 
-    static ParseNode *create(ParseNodeKind kind, ParseNodeArity arity, Parser *parser);
+    static ParseNode *create(ParseNodeKind kind, ParseNodeArity arity, FullParseHandler *handler);
 
   public:
     /*
      * Append right to left, forming a list node.  |left| must have the given
      * kind and op, and op must be left-associative.
      */
     static ParseNode *
-    append(ParseNodeKind tt, JSOp op, ParseNode *left, ParseNode *right, Parser *parser);
+    append(ParseNodeKind tt, JSOp op, ParseNode *left, ParseNode *right, FullParseHandler *handler);
 
     /*
      * Either append right to left, if left meets the conditions necessary to
      * append (see append), or form a binary node whose children are right and
      * left.
      */
     static ParseNode *
     newBinaryOrAppend(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right,
-                      Parser *parser);
+                      FullParseHandler *handler, bool foldConstants);
 
     inline PropertyName *name() const;
     inline JSAtom *atom() const;
 
     /*
      * The pn_expr and lexdef members are arms of an unsafe union. Unless you
      * know exactly what you're doing, use only the following methods to access
      * them. For less overhead and assertions for protection, use pn->expr()
@@ -683,44 +686,25 @@ struct ParseNode {
      * This considers only the node and its children, not its context. After
      * parsing, check the node's pn_prologue flag to see if it is indeed part of
      * a directive prologue.
      *
      * Note that a Directive Prologue can contain statements that cannot
      * themselves be directives (string literals that include escape sequences
      * or escaped newlines, say). This member function returns true for such
      * nodes; we use it to determine the extent of the prologue.
-     * isEscapeFreeStringLiteral, below, checks whether the node itself could be
-     * a directive.
      */
-    bool isStringExprStatement() const {
+    JSAtom *isStringExprStatement() const {
         if (getKind() == PNK_SEMI) {
             JS_ASSERT(pn_arity == PN_UNARY);
             ParseNode *kid = pn_kid;
-            return kid && kid->getKind() == PNK_STRING && !kid->pn_parens;
+            if (kid && kid->getKind() == PNK_STRING && !kid->pn_parens)
+                return kid->pn_atom;
         }
-        return false;
-    }
-
-    /*
-     * Return true if this node, known to be an unparenthesized string literal,
-     * could be the string of a directive in a Directive Prologue. Directive
-     * strings never contain escape sequences or line continuations.
-     */
-    bool isEscapeFreeStringLiteral() const {
-        JS_ASSERT(isKind(PNK_STRING) && !pn_parens);
-
-        /*
-         * If the string's length in the source code is its length as a value,
-         * accounting for the quotes, then it must not contain any escape
-         * sequences or line continuations.
-         */
-        JSString *str = pn_atom;
-        return (pn_pos.begin.lineno == pn_pos.end.lineno &&
-                pn_pos.begin.index + str->length() + 2 == pn_pos.end.index);
+        return NULL;
     }
 
     inline bool test(unsigned flag) const;
 
     bool isLet() const          { return test(PND_LET); }
     bool isConst() const        { return test(PND_CONST); }
     bool isBlockChild() const   { return test(PND_BLOCKCHILD); }
     bool isPlaceholder() const  { return test(PND_PLACEHOLDER); }
@@ -762,21 +746,21 @@ struct ParseNode {
      * must be non-empty for correct PN_LAST usage -- this is asserted!
      */
     ParseNode *last() const {
         JS_ASSERT(pn_arity == PN_LIST);
         JS_ASSERT(pn_count != 0);
         return (ParseNode *)(uintptr_t(pn_tail) - offsetof(ParseNode, pn_next));
     }
 
-    void initNumber(const Token &tok) {
+    void initNumber(double value, DecimalPoint decimalPoint) {
         JS_ASSERT(pn_arity == PN_NULLARY);
         JS_ASSERT(getKind() == PNK_NUMBER);
-        pn_u.number.value = tok.number();
-        pn_u.number.decimalPoint = tok.decimalPoint();
+        pn_u.number.value = value;
+        pn_u.number.decimalPoint = decimalPoint;
     }
 
     void makeEmpty() {
         JS_ASSERT(pn_arity == PN_LIST);
         pn_head = NULL;
         pn_tail = &pn_head;
         pn_count = 0;
         pn_xflags = 0;
@@ -828,18 +812,18 @@ struct ParseNode {
 
 #ifdef DEBUG
     void dump();
     void dump(int indent);
 #endif
 };
 
 struct NullaryNode : public ParseNode {
-    static inline NullaryNode *create(ParseNodeKind kind, Parser *parser) {
-        return (NullaryNode *) ParseNode::create(kind, PN_NULLARY, parser);
+    static inline NullaryNode *create(ParseNodeKind kind, FullParseHandler *handler) {
+        return (NullaryNode *) ParseNode::create(kind, PN_NULLARY, handler);
     }
 
     static bool test(const ParseNode &node) {
         return node.isArity(PN_NULLARY);
     }
 
 #ifdef DEBUG
     void dump();
@@ -848,18 +832,18 @@ struct NullaryNode : public ParseNode {
 
 struct UnaryNode : public ParseNode {
     UnaryNode(ParseNodeKind kind, JSOp op, const TokenPos &pos, ParseNode *kid)
       : ParseNode(kind, op, PN_UNARY, pos)
     {
         pn_kid = kid;
     }
 
-    static inline UnaryNode *create(ParseNodeKind kind, Parser *parser) {
-        return (UnaryNode *) ParseNode::create(kind, PN_UNARY, parser);
+    static inline UnaryNode *create(ParseNodeKind kind, FullParseHandler *handler) {
+        return (UnaryNode *) ParseNode::create(kind, PN_UNARY, handler);
     }
 
     static bool test(const ParseNode &node) {
         return node.isArity(PN_UNARY);
     }
 
 #ifdef DEBUG
     void dump(int indent);
@@ -876,18 +860,18 @@ struct BinaryNode : public ParseNode {
 
     BinaryNode(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right)
       : ParseNode(kind, op, PN_BINARY, TokenPos::box(left->pn_pos, right->pn_pos))
     {
         pn_left = left;
         pn_right = right;
     }
 
-    static inline BinaryNode *create(ParseNodeKind kind, Parser *parser) {
-        return (BinaryNode *) ParseNode::create(kind, PN_BINARY, parser);
+    static inline BinaryNode *create(ParseNodeKind kind, FullParseHandler *handler) {
+        return (BinaryNode *) ParseNode::create(kind, PN_BINARY, handler);
     }
 
     static bool test(const ParseNode &node) {
         return node.isArity(PN_BINARY);
     }
 
 #ifdef DEBUG
     void dump(int indent);
@@ -900,18 +884,18 @@ struct TernaryNode : public ParseNode {
                   TokenPos::make((kid1 ? kid1 : kid2 ? kid2 : kid3)->pn_pos.begin,
                                  (kid3 ? kid3 : kid2 ? kid2 : kid1)->pn_pos.end))
     {
         pn_kid1 = kid1;
         pn_kid2 = kid2;
         pn_kid3 = kid3;
     }
 
-    static inline TernaryNode *create(ParseNodeKind kind, Parser *parser) {
-        return (TernaryNode *) ParseNode::create(kind, PN_TERNARY, parser);
+    static inline TernaryNode *create(ParseNodeKind kind, FullParseHandler *handler) {
+        return (TernaryNode *) ParseNode::create(kind, PN_TERNARY, handler);
     }
 
     static bool test(const ParseNode &node) {
         return node.isArity(PN_TERNARY);
     }
 
 #ifdef DEBUG
     void dump(int indent);
@@ -922,60 +906,61 @@ struct ListNode : public ParseNode
 {
     ListNode(ParseNodeKind kind, JSOp op, ParseNode *kid)
       : ParseNode(kind, op, PN_LIST)
     {
         pn_pos = kid->pn_pos;
         initList(kid);
     }
 
-    static inline ListNode *create(ParseNodeKind kind, Parser *parser) {
-        return (ListNode *) ParseNode::create(kind, PN_LIST, parser);
+    static inline ListNode *create(ParseNodeKind kind, FullParseHandler *handler) {
+        return (ListNode *) ParseNode::create(kind, PN_LIST, handler);
     }
 
     static bool test(const ParseNode &node) {
         return node.isArity(PN_LIST);
     }
 
 #ifdef DEBUG
     void dump(int indent);
 #endif
 };
 
 struct CodeNode : public ParseNode {
-    static inline CodeNode *create(ParseNodeKind kind, Parser *parser) {
-        return (CodeNode *) ParseNode::create(kind, PN_CODE, parser);
+    static inline CodeNode *create(ParseNodeKind kind, FullParseHandler *handler) {
+        return (CodeNode *) ParseNode::create(kind, PN_CODE, handler);
     }
 
     static bool test(const ParseNode &node) {
         return node.isArity(PN_CODE);
     }
 
 #ifdef DEBUG
     void dump(int indent);
 #endif
 };
 
 struct NameNode : public ParseNode {
-    static NameNode *create(ParseNodeKind kind, JSAtom *atom, Parser *parser, ParseContext *pc);
+    static NameNode *create(ParseNodeKind kind, JSAtom *atom,
+                            FullParseHandler *handler, ParseContext<FullParseHandler> *pc);
 
-    inline void initCommon(ParseContext *pc);
+    inline void initCommon(ParseContext<FullParseHandler> *pc);
 
     static bool test(const ParseNode &node) {
         return node.isArity(PN_NAME);
     }
 
 #ifdef DEBUG
     void dump(int indent);
 #endif
 };
 
 struct LexicalScopeNode : public ParseNode {
-    static inline LexicalScopeNode *create(ParseNodeKind kind, Parser *parser) {
-        return (LexicalScopeNode *) ParseNode::create(kind, PN_NAME, parser);
+    static inline LexicalScopeNode *create(ParseNodeKind kind, FullParseHandler *handler) {
+        return (LexicalScopeNode *) ParseNode::create(kind, PN_NAME, handler);
     }
 };
 
 class LoopControlStatement : public ParseNode {
   protected:
     LoopControlStatement(ParseNodeKind kind, PropertyName *label,
                          const TokenPtr &begin, const TokenPtr &end)
       : ParseNode(kind, JSOP_NOP, PN_NULLARY, TokenPos::make(begin, end))
@@ -1117,19 +1102,16 @@ class PropertyByValue : public ParseNode
                     const TokenPtr &begin, const TokenPtr &end)
       : ParseNode(PNK_ELEM, JSOP_GETELEM, PN_BINARY, TokenPos::make(begin, end))
     {
         pn_u.binary.left = lhs;
         pn_u.binary.right = propExpr;
     }
 };
 
-ParseNode *
-CloneLeftHandSide(ParseNode *opn, Parser *parser);
-
 #ifdef DEBUG
 void DumpParseTree(ParseNode *pn, int indent = 0);
 #endif
 
 /*
  * js::Definition is a degenerate subtype of the PN_FUNC and PN_NAME variants
  * of js::ParseNode, allocated only for function, var, const, and let
  * declarations that define truly lexical bindings. This means that a child of
@@ -1296,16 +1278,34 @@ inline Definition *
 ParseNode::resolve()
 {
     if (isDefn())
         return (Definition *)this;
     JS_ASSERT(lexdef()->isDefn());
     return (Definition *)lexdef();
 }
 
+inline bool
+ParseNode::isConstant()
+{
+    switch (pn_type) {
+      case PNK_NUMBER:
+      case PNK_STRING:
+      case PNK_NULL:
+      case PNK_FALSE:
+      case PNK_TRUE:
+        return true;
+      case PNK_ARRAY:
+      case PNK_OBJECT:
+        return isOp(JSOP_NEWINIT) && !(pn_xflags & PNX_NONCONST);
+      default:
+        return false;
+    }
+}
+
 inline void
 LinkUseToDef(ParseNode *pn, Definition *dn)
 {
     JS_ASSERT(!pn->isUsed());
     JS_ASSERT(!pn->isDefn());
     JS_ASSERT(pn != dn->dn_uses);
     pn->pn_link = dn->dn_uses;
     dn->dn_uses = pn;
@@ -1330,12 +1330,21 @@ class ObjectBox {
 
     ObjectBox *traceLink;
     ObjectBox *emitLink;
 
     ObjectBox(JSFunction *function, ObjectBox *traceLink);
     ObjectBox(Module *module, ObjectBox *traceLink);
 };
 
+enum ParseReportKind {
+    ParseError,
+    ParseWarning,
+    ParseStrictWarning,
+    ParseStrictError
+};
+
+enum FunctionSyntaxKind { Expression, Statement };
+
 } /* namespace frontend */
 } /* namespace js */
 
 #endif /* ParseNode_h__ */
--- a/js/src/frontend/Parser-inl.h
+++ b/js/src/frontend/Parser-inl.h
@@ -8,74 +8,107 @@
 #ifndef Parser_inl_h__
 #define Parser_inl_h__
 
 #include "frontend/Parser.h"
 
 namespace js {
 namespace frontend {
 
+template <typename ParseHandler>
 inline unsigned
-ParseContext::blockid()
+ParseContext<ParseHandler>::blockid()
 {
     return topStmt ? topStmt->blockid : bodyid;
 }
 
+template <typename ParseHandler>
 inline bool
-ParseContext::atBodyLevel()
+ParseContext<ParseHandler>::atBodyLevel()
 {
     return !topStmt;
 }
 
+template <typename ParseHandler>
 inline
-ParseContext::ParseContext(Parser *prs, SharedContext *sc, unsigned staticLevel, uint32_t bodyid)
+ParseContext<ParseHandler>::ParseContext(Parser<ParseHandler> *prs, SharedContext *sc,
+                                      unsigned staticLevel, uint32_t bodyid)
   : sc(sc),
     bodyid(0),           // initialized in init()
     blockidGen(bodyid),  // used to set |bodyid| and subsequently incremented in init()
     topStmt(NULL),
     topScopeStmt(NULL),
     blockChain(prs->context),
     staticLevel(staticLevel),
     parenDepth(0),
     yieldCount(0),
-    blockNode(NULL),
+    blockNode(ParseHandler::null()),
     decls_(prs->context),
     args_(prs->context),
     vars_(prs->context),
-    yieldNode(NULL),
+    yieldNode(ParseHandler::null()),
     parserPC(&prs->pc),
     lexdeps(prs->context),
     parent(prs->pc),
     funcStmts(NULL),
     funHasReturnExpr(false),
     funHasReturnVoid(false),
     parsingForInit(false),
     parsingWith(prs->pc ? prs->pc->parsingWith : false), // inherit from parent context
     inDeclDestructuring(false),
     funBecameStrict(false)
 {
     prs->pc = this;
 }
 
+template <typename ParseHandler>
 inline bool
-ParseContext::init()
+ParseContext<ParseHandler>::init()
 {
     if (!frontend::GenerateBlockId(this, this->bodyid))
         return false;
 
     return decls_.init() && lexdeps.ensureMap(sc->context);
 }
 
+template <typename ParseHandler>
 inline
-ParseContext::~ParseContext()
+ParseContext<ParseHandler>::~ParseContext()
 {
     // |*parserPC| pointed to this object.  Now that this object is about to
     // die, make |*parserPC| point to this object's parent.
     JS_ASSERT(*parserPC == this);
     *parserPC = this->parent;
     js_delete(funcStmts);
 }
 
+/*
+ * Check that it is permitted to introduce a binding for atom.  Strict mode
+ * forbids introducing new definitions for 'eval', 'arguments', or for any
+ * strict mode reserved keyword.  Use pn for reporting error locations, or use
+ * pc's token stream if pn is NULL.
+ */
+template <typename ParseHandler>
+static bool
+CheckStrictBinding(JSContext *cx, ParseHandler *handler, ParseContext<ParseHandler> *pc,
+                   HandlePropertyName name, ParseNode *pn)
+{
+    if (!pc->sc->needStrictChecks())
+        return true;
+
+    if (name == cx->names().eval ||
+        name == cx->names().arguments ||
+        FindKeyword(name->charsZ(), name->length()))
+    {
+        JSAutoByteString bytes;
+        if (!js_AtomToPrintableString(cx, name, &bytes))
+            return false;
+        return handler->report(ParseStrictError, pn, JSMSG_BAD_BINDING, bytes.ptr());
+    }
+
+    return true;
+}
+
 } // namespace frontend
 } // namespace js
 
 #endif // Parser_inl_h__
 
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -55,64 +55,70 @@
 #include "frontend/Parser-inl.h"
 #include "frontend/SharedContext-inl.h"
 
 #include "vm/NumericConversions.h"
 #include "vm/RegExpObject-inl.h"
 
 using namespace js;
 using namespace js::gc;
-using namespace js::frontend;
+
+namespace js {
+namespace frontend {
 
 typedef Rooted<StaticBlockObject*> RootedStaticBlockObject;
 typedef Handle<StaticBlockObject*> HandleStaticBlockObject;
 
 typedef MutableHandle<PropertyName*> MutableHandlePropertyName;
 
 /*
  * Insist that the next token be of type tt, or report errno and return null.
  * NB: this macro uses cx and ts from its lexical environment.
  */
 #define MUST_MATCH_TOKEN_WITH_FLAGS(tt, errno, __flags)                                     \
     JS_BEGIN_MACRO                                                                          \
         if (tokenStream.getToken((__flags)) != tt) {                                        \
-            reportError(NULL, errno);                                                       \
-            return NULL;                                                                    \
+            report(ParseError, false, null(), errno);                                       \
+            return null();                                                                  \
         }                                                                                   \
     JS_END_MACRO
 #define MUST_MATCH_TOKEN(tt, errno) MUST_MATCH_TOKEN_WITH_FLAGS(tt, errno, 0)
 
+template <typename ParseHandler>
 bool
-StrictModeGetter::get() const
-{
-    return parser->pc->sc->strict;
-}
-
-bool
-frontend::GenerateBlockId(ParseContext *pc, uint32_t &blockid)
+GenerateBlockId(ParseContext<ParseHandler> *pc, uint32_t &blockid)
 {
     if (pc->blockidGen == JS_BIT(20)) {
         JS_ReportErrorNumber(pc->sc->context, js_GetErrorMessage, NULL, JSMSG_NEED_DIET, "program");
         return false;
     }
     JS_ASSERT(pc->blockidGen < JS_BIT(20));
     blockid = pc->blockidGen++;
     return true;
 }
 
+template bool
+GenerateBlockId(ParseContext<SyntaxParseHandler> *pc, uint32_t &blockid);
+
+template bool
+GenerateBlockId(ParseContext<FullParseHandler> *pc, uint32_t &blockid);
+
+template <typename ParseHandler>
 static void
-PushStatementPC(ParseContext *pc, StmtInfoPC *stmt, StmtType type)
+PushStatementPC(ParseContext<ParseHandler> *pc, StmtInfoPC *stmt, StmtType type)
 {
     stmt->blockid = pc->blockid();
     PushStatement(pc, stmt, type);
 }
 
 // See comment on member function declaration.
+template <>
 bool
-ParseContext::define(JSContext *cx, HandlePropertyName name, ParseNode *pn, Definition::Kind kind)
+ParseContext<FullParseHandler>::define(JSContext *cx, HandlePropertyName name,
+                                       ParseNode *pn, Definition::Kind kind)
 {
     JS_ASSERT(!pn->isUsed());
     JS_ASSERT_IF(pn->isDefn(), pn->isPlaceholder());
 
     Definition *prevDef = NULL;
     if (kind == Definition::LET)
         prevDef = decls_.lookupFirst(name);
     else
@@ -180,41 +186,51 @@ ParseContext::define(JSContext *cx, Hand
         }
         if (!decls_.addUnique(name, dn))
             return false;
         break;
 
       case Definition::LET:
         dn->setOp(JSOP_GETLOCAL);
         dn->pn_dflags |= (PND_LET | PND_BOUND);
-        JS_ASSERT(dn->pn_cookie.level() == staticLevel); /* see BindLet */
+        JS_ASSERT(dn->pn_cookie.level() == staticLevel); /* see bindLet */
         if (!decls_.addShadow(name, dn))
             return false;
         break;
 
       case Definition::PLACEHOLDER:
       case Definition::NAMED_LAMBDA:
         JS_NOT_REACHED("unexpected kind");
         break;
     }
 
     return true;
 }
 
+template <>
+bool
+ParseContext<SyntaxParseHandler>::define(JSContext *cx, HandlePropertyName name, Node pn,
+                                         Definition::Kind kind)
+{
+    return true;
+}
+
+template <typename ParseHandler>
 void
-ParseContext::prepareToAddDuplicateArg(Definition *prevDecl)
+ParseContext<ParseHandler>::prepareToAddDuplicateArg(Definition *prevDecl)
 {
     JS_ASSERT(prevDecl->kind() == Definition::ARG);
     JS_ASSERT(decls_.lookupFirst(prevDecl->name()) == prevDecl);
     JS_ASSERT(!prevDecl->isClosed());
     decls_.remove(prevDecl->name());
 }
 
+template <typename ParseHandler>
 void
-ParseContext::updateDecl(JSAtom *atom, ParseNode *pn)
+ParseContext<ParseHandler>::updateDecl(JSAtom *atom, Node pn)
 {
     Definition *oldDecl = decls_.lookupFirst(atom);
 
     pn->setDefn(true);
     Definition *newDecl = (Definition *)pn;
     decls_.updateFirst(atom, newDecl);
 
     if (!sc->isFunctionBox()) {
@@ -233,25 +249,27 @@ ParseContext::updateDecl(JSAtom *atom, P
     } else {
         JS_ASSERT(JOF_OPTYPE(oldDecl->getOp()) == JOF_LOCAL);
         newDecl->setOp(JSOP_GETLOCAL);
         JS_ASSERT(vars_[oldDecl->pn_cookie.slot()] == oldDecl);
         vars_[oldDecl->pn_cookie.slot()] = newDecl;
     }
 }
 
+template <typename ParseHandler>
 void
-ParseContext::popLetDecl(JSAtom *atom)
+ParseContext<ParseHandler>::popLetDecl(JSAtom *atom)
 {
     JS_ASSERT(decls_.lookupFirst(atom)->isLet());
     decls_.remove(atom);
 }
 
+template <typename ParseHandler>
 static void
-AppendPackedBindings(const ParseContext *pc, const DeclVector &vec, Binding *dst)
+AppendPackedBindings(const ParseContext<ParseHandler> *pc, const DeclVector &vec, Binding *dst)
 {
     for (unsigned i = 0; i < vec.length(); ++i, ++dst) {
         Definition *dn = vec[i];
         PropertyName *name = dn->name();
 
         BindingKind kind;
         switch (dn->kind()) {
           case Definition::VAR:
@@ -278,18 +296,19 @@ AppendPackedBindings(const ParseContext 
         bool aliased = dn->isClosed() ||
                        (pc->sc->bindingsAccessedDynamically() &&
                         pc->decls().lookupFirst(name) == dn);
 
         *dst = Binding(name, kind, aliased);
     }
 }
 
+template <typename ParseHandler>
 bool
-ParseContext::generateFunctionBindings(JSContext *cx, InternalHandle<Bindings*> bindings) const
+ParseContext<ParseHandler>::generateFunctionBindings(JSContext *cx, InternalHandle<Bindings*> bindings) const
 {
     JS_ASSERT(sc->isFunctionBox());
 
     unsigned count = args_.length() + vars_.length();
     Binding *packedBindings = cx->tempLifoAlloc().newArrayUninitialized<Binding>(count);
     if (!packedBindings) {
         js_ReportOutOfMemory(cx);
         return false;
@@ -306,54 +325,85 @@ ParseContext::generateFunctionBindings(J
 
     FunctionBox *funbox = sc->asFunctionBox();
     if (bindings->hasAnyAliasedBindings() || funbox->hasExtensibleScope())
         funbox->function()->setIsHeavyweight();
 
     return true;
 }
 
-Parser::Parser(JSContext *cx, const CompileOptions &options,
-               const jschar *chars, size_t length, bool foldConstants)
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::report(ParseReportKind kind, bool strict, Node pn, unsigned errorNumber, ...)
+{
+    TokenPos pos = pn ? handler.getPosition(pn) : tokenStream.currentToken().pos;
+
+    va_list args;
+    va_start(args, errorNumber);
+    bool result = false;
+    switch (kind) {
+      case ParseError:
+        result = tokenStream.reportCompileErrorNumberVA(pos, JSREPORT_ERROR, errorNumber, args);
+        break;
+      case ParseWarning:
+        result = tokenStream.reportCompileErrorNumberVA(pos, JSREPORT_WARNING, errorNumber, args);
+        break;
+      case ParseStrictWarning:
+        result = tokenStream.reportStrictWarningErrorNumberVA(pos, errorNumber, args);
+        break;
+      case ParseStrictError:
+        result = tokenStream.reportStrictModeErrorNumberVA(pos, strict, errorNumber, args);
+        break;
+    }
+    va_end(args);
+    return result;
+}
+
+template <typename ParseHandler>
+Parser<ParseHandler>::Parser(JSContext *cx, const CompileOptions &options,
+                             const jschar *chars, size_t length, bool foldConstants)
   : AutoGCRooter(cx, PARSER),
     context(cx),
-    strictModeGetter(thisForCtor()),
-    tokenStream(cx, options, chars, length, &strictModeGetter),
+    tokenStream(cx, options, chars, length, thisForCtor()),
     tempPoolMark(NULL),
-    allocator(cx),
     traceListHead(NULL),
     pc(NULL),
     sct(NULL),
     keepAtoms(cx->runtime),
     foldConstants(foldConstants),
     compileAndGo(options.compileAndGo),
-    selfHostingMode(options.selfHostingMode)
+    selfHostingMode(options.selfHostingMode),
+    unknownResult(false),
+    handler(cx, tokenStream, foldConstants)
 {
     cx->activeCompilations++;
 }
 
+template <typename ParseHandler>
 bool
-Parser::init()
+Parser<ParseHandler>::init()
 {
     if (!context->ensureParseMapPool())
         return false;
 
     tempPoolMark = context->tempLifoAlloc().mark();
     return true;
 }
 
-Parser::~Parser()
+template <typename ParseHandler>
+Parser<ParseHandler>::~Parser()
 {
     JSContext *cx = context;
     cx->tempLifoAlloc().release(tempPoolMark);
     cx->activeCompilations--;
 }
 
+template <typename ParseHandler>
 ObjectBox *
-Parser::newObjectBox(JSObject *obj)
+Parser<ParseHandler>::newObjectBox(JSObject *obj)
 {
     JS_ASSERT(obj && !IsPoisonedPtr(obj));
 
     /*
      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
      * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
      * arenas containing the entries must be alive until we are done with
      * scanning, parsing and code generation for the whole script or top-level
@@ -366,18 +416,19 @@ Parser::newObjectBox(JSObject *obj)
         return NULL;
     }
 
     traceListHead = objbox;
 
     return objbox;
 }
 
+template <typename ParseHandler>
 FunctionBox::FunctionBox(JSContext *cx, ObjectBox* traceListHead, JSFunction *fun,
-                         ParseContext *outerpc, bool strict)
+                         ParseContext<ParseHandler> *outerpc, bool strict)
   : ObjectBox(fun, traceListHead),
     SharedContext(cx, strict),
     bindings(),
     bufStart(0),
     bufEnd(0),
     ndefaults(0),
     inWith(false),                  // initialized below
     inGenexpLambda(false),
@@ -421,18 +472,20 @@ FunctionBox::FunctionBox(JSContext *cx, 
         // In this case, the inner anonymous function needs to inherit the
         // setting of |inWith| from the outer one.
         FunctionBox *parent = outerpc->sc->asFunctionBox();
         if (parent && parent->inWith)
             inWith = true;
     }
 }
 
+template <typename ParseHandler>
 FunctionBox *
-Parser::newFunctionBox(JSFunction *fun, ParseContext *outerpc, bool strict)
+Parser<ParseHandler>::newFunctionBox(JSFunction *fun,
+                                     ParseContext<ParseHandler> *outerpc, bool strict)
 {
     JS_ASSERT(fun && !IsPoisonedPtr(fun));
 
     /*
      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
      * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
      * arenas containing the entries must be alive until we are done with
      * scanning, parsing and code generation for the whole script or top-level
@@ -445,24 +498,26 @@ Parser::newFunctionBox(JSFunction *fun, 
         return NULL;
     }
 
     traceListHead = funbox;
 
     return funbox;
 }
 
-ModuleBox::ModuleBox(JSContext *cx, ObjectBox *traceListHead, Module *module, ParseContext *pc)
+ModuleBox::ModuleBox(JSContext *cx, ObjectBox *traceListHead, Module *module,
+                     ParseContext<FullParseHandler> *pc)
     : ObjectBox(module, traceListHead),
       SharedContext(cx, true)
 {
 }
 
+template <>
 ModuleBox *
-Parser::newModuleBox(Module *module, ParseContext *outerpc)
+Parser<FullParseHandler>::newModuleBox(Module *module, ParseContext<FullParseHandler> *outerpc)
 {
     JS_ASSERT(module && !IsPoisonedPtr(module));
 
     /*
      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
      * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
      * arenas containing the entries must be alive until we are done with
      * scanning, parsing and code generation for the whole script or top-level
@@ -475,75 +530,68 @@ Parser::newModuleBox(Module *module, Par
         return NULL;
     }
 
     traceListHead = modulebox;
 
     return modulebox;
 }
 
+template <typename ParseHandler>
 void
-Parser::trace(JSTracer *trc)
+Parser<ParseHandler>::trace(JSTracer *trc)
 {
     traceListHead->trace(trc);
 }
 
-static bool
-GenerateBlockIdForStmtNode(ParseNode *pn, ParseContext *pc)
-{
-    JS_ASSERT(pc->topStmt);
-    JS_ASSERT(pc->topStmt->maybeScope());
-    JS_ASSERT(pn->isKind(PNK_STATEMENTLIST) || pn->isKind(PNK_LEXICALSCOPE));
-    if (!GenerateBlockId(pc, pc->topStmt->blockid))
-        return false;
-    pn->pn_blockid = pc->topStmt->blockid;
-    return true;
-}
-
 /*
  * Parse a top-level JS script.
  */
-ParseNode *
-Parser::parse(JSObject *chain)
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::parse(JSObject *chain)
 {
     /*
      * Protect atoms from being collected by a GC activation, which might
      * - nest on this thread due to out of memory (the so-called "last ditch"
      *   GC attempted within js_NewGCThing), or
      * - run for any reason on another thread if this thread is suspended on
      *   an object lock before it finishes generating bytecode into a script
      *   protected from the GC by a root or a stack frame reference.
      */
     GlobalSharedContext globalsc(context, chain, StrictModeFromContext(context));
-    ParseContext globalpc(this, &globalsc, /* staticLevel = */ 0, /* bodyid = */ 0);
+    ParseContext<ParseHandler> globalpc(this, &globalsc, /* staticLevel = */ 0, /* bodyid = */ 0);
     if (!globalpc.init())
-        return NULL;
-
-    ParseNode *pn = statements();
+        return null();
+
+    Node pn = statements();
     if (pn) {
         if (!tokenStream.matchToken(TOK_EOF)) {
-            reportError(NULL, JSMSG_SYNTAX_ERROR);
-            pn = NULL;
-        } else if (foldConstants) {
+            report(ParseError, false, null(), JSMSG_SYNTAX_ERROR);
+            return null();
+        }
+        if (foldConstants) {
             if (!FoldConstants(context, &pn, this))
-                pn = NULL;
+                return null();
         }
     }
     return pn;
 }
 
 /*
  * Insist on a final return before control flows out of pn.  Try to be a bit
  * smart about loops: do {...; return e2;} while(0) at the end of a function
  * that contains an early return e1 will get a strict warning.  Similarly for
  * iloops: while (true){...} is treated as though ... returns.
  */
-#define ENDS_IN_OTHER   0
-#define ENDS_IN_RETURN  1
-#define ENDS_IN_BREAK   2
+enum {
+    ENDS_IN_OTHER = 0,
+    ENDS_IN_RETURN = 1,
+    ENDS_IN_BREAK = 2
+};
 
 static int
 HasFinalReturn(ParseNode *pn)
 {
     ParseNode *pn2, *pn3;
     unsigned rv, rv2, hasDefault;
 
     switch (pn->getKind()) {
@@ -650,172 +698,156 @@ HasFinalReturn(ParseNode *pn)
             return ENDS_IN_OTHER;
         return HasFinalReturn(pn->pn_right);
 
       default:
         return ENDS_IN_OTHER;
     }
 }
 
-static bool
-ReportBadReturn(JSContext *cx, Parser *parser, ParseNode *pn, Parser::Reporter reporter,
-                unsigned errnum, unsigned anonerrnum)
+static int
+HasFinalReturn(SyntaxParseHandler::Node pn)
+{
+    return ENDS_IN_RETURN;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::reportBadReturn(Node pn, ParseReportKind kind,
+                                      unsigned errnum, unsigned anonerrnum)
 {
     JSAutoByteString name;
-    JSAtom *atom = parser->pc->sc->asFunctionBox()->function()->atom();
+    JSAtom *atom = pc->sc->asFunctionBox()->function()->atom();
     if (atom) {
-        if (!js_AtomToPrintableString(cx, atom, &name))
+        if (!js_AtomToPrintableString(context, atom, &name))
             return false;
     } else {
         errnum = anonerrnum;
     }
-    return (parser->*reporter)(pn, errnum, name.ptr());
+    return report(kind, pc->sc->strict, pn, errnum, name.ptr());
 }
 
-static bool
-CheckFinalReturn(JSContext *cx, Parser *parser, ParseNode *pn)
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::checkFinalReturn(Node pn)
 {
-    JS_ASSERT(parser->pc->sc->isFunctionBox());
+    JS_ASSERT(pc->sc->isFunctionBox());
     return HasFinalReturn(pn) == ENDS_IN_RETURN ||
-           ReportBadReturn(cx, parser, pn, &Parser::reportStrictWarning,
+           reportBadReturn(pn, ParseStrictWarning,
                            JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
 }
 
 /*
  * Check that it is permitted to assign to lhs.  Strict mode code may not
  * assign to 'eval' or 'arguments'.
  */
-static bool
-CheckStrictAssignment(JSContext *cx, Parser *parser, ParseNode *lhs)
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::checkStrictAssignment(Node lhs)
 {
-    if (parser->pc->sc->needStrictChecks() && lhs->isKind(PNK_NAME)) {
-        JSAtom *atom = lhs->pn_atom;
-        if (atom == cx->names().eval || atom == cx->names().arguments) {
-            JSAutoByteString name;
-            if (!js_AtomToPrintableString(cx, atom, &name) ||
-                !parser->reportStrictModeError(lhs, JSMSG_DEPRECATED_ASSIGN, name.ptr()))
-            {
-                return false;
-            }
+    if (!pc->sc->needStrictChecks())
+        return true;
+
+    JSAtom *atom = handler.isName(lhs);
+    if (!atom)
+        return true;
+
+    if (atom == context->names().eval || atom == context->names().arguments) {
+        JSAutoByteString name;
+        if (!js_AtomToPrintableString(context, atom, &name) ||
+            !report(ParseStrictError, pc->sc->strict, lhs,
+                    JSMSG_DEPRECATED_ASSIGN, name.ptr()))
+        {
+            return false;
         }
     }
     return true;
 }
 
 /*
  * Check that it is permitted to introduce a binding for atom.  Strict mode
  * forbids introducing new definitions for 'eval', 'arguments', or for any
  * strict mode reserved keyword.  Use pn for reporting error locations, or use
  * pc's token stream if pn is NULL.
  */
+template <typename ParseHandler>
 bool
-CheckStrictBinding(JSContext *cx, Parser *parser, HandlePropertyName name, ParseNode *pn)
+Parser<ParseHandler>::checkStrictBinding(HandlePropertyName name, Node pn)
 {
-    if (!parser->pc->sc->needStrictChecks())
+    if (!pc->sc->needStrictChecks())
         return true;
 
-    if (name == cx->names().eval ||
-        name == cx->names().arguments ||
+    if (name == context->names().eval ||
+        name == context->names().arguments ||
         FindKeyword(name->charsZ(), name->length()))
     {
         JSAutoByteString bytes;
-        if (!js_AtomToPrintableString(cx, name, &bytes))
+        if (!js_AtomToPrintableString(context, name, &bytes))
             return false;
-        return parser->reportStrictModeError(pn, JSMSG_BAD_BINDING, bytes.ptr());
+        return report(ParseStrictError, pc->sc->strict, pn,
+                      JSMSG_BAD_BINDING, bytes.ptr());
     }
 
     return true;
 }
 
+template <>
+bool
+Parser<FullParseHandler>::defineArg(ParseNode *funcpn, HandlePropertyName name,
+                                    bool disallowDuplicateArgs, Definition **duplicatedArg);
+
+template <>
 ParseNode *
-Parser::standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals, HandleScript script,
-                               ParseNode *fn, FunctionBox **funbox, bool strict, bool *becameStrict)
+Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals,
+                                                 HandleScript script, Node fn, FunctionBox **funbox,
+                                                 bool strict, bool *becameStrict)
 {
     if (becameStrict)
         *becameStrict = false;
 
     *funbox = newFunctionBox(fun, /* outerpc = */ NULL, strict);
     if (!funbox)
-        return NULL;
-
-    fn->pn_funbox = *funbox;
-
-    ParseContext funpc(this, *funbox, 0, /* staticLevel = */ 0);
+        return null();
+    handler.setFunctionBox(fn, *funbox);
+
+    ParseContext<FullParseHandler> funpc(this, *funbox, 0, /* staticLevel = */ 0);
     if (!funpc.init())
-        return NULL;
+        return null();
 
     for (unsigned i = 0; i < formals.length(); i++) {
-        if (!DefineArg(this, fn, formals[i]))
-            return NULL;
+        if (!defineArg(fn, formals[i]))
+            return null();
     }
 
     ParseNode *pn = functionBody(StatementListBody);
     if (!pn) {
         if (becameStrict && pc->funBecameStrict)
             *becameStrict = true;
-        return NULL;
+        return null();
     }
 
     if (!tokenStream.matchToken(TOK_EOF)) {
-        reportError(NULL, JSMSG_SYNTAX_ERROR);
-        return NULL;
+        report(ParseError, false, null(), JSMSG_SYNTAX_ERROR);
+        return null();
     }
 
     if (!FoldConstants(context, &pn, this))
-        return NULL;
+        return null();
 
     InternalHandle<Bindings*> bindings(script, &script->bindings);
     if (!funpc.generateFunctionBindings(context, bindings))
-        return NULL;
+        return null();
 
     return pn;
 }
 
-ParseNode *
-Parser::functionBody(FunctionBodyType type)
+template <>
+bool
+Parser<FullParseHandler>::checkFunctionArguments()
 {
-    JS_ASSERT(pc->sc->isFunctionBox());
-    JS_ASSERT(!pc->funHasReturnExpr && !pc->funHasReturnVoid);
-
-    ParseNode *pn;
-    if (type == StatementListBody) {
-        pn = statements();
-    } else {
-        JS_ASSERT(type == ExpressionBody);
-        JS_ASSERT(JS_HAS_EXPR_CLOSURES);
-
-        pn = UnaryNode::create(PNK_RETURN, this);
-        if (pn) {
-            pn->pn_kid = assignExpr();
-            if (!pn->pn_kid) {
-                pn = NULL;
-            } else {
-                if (pc->sc->asFunctionBox()->isGenerator()) {
-                    ReportBadReturn(context, this, pn, &Parser::reportError,
-                                    JSMSG_BAD_GENERATOR_RETURN,
-                                    JSMSG_BAD_ANON_GENERATOR_RETURN);
-                    pn = NULL;
-                } else {
-                    pn->setOp(JSOP_RETURN);
-                    pn->pn_pos.end = pn->pn_kid->pn_pos.end;
-                }
-            }
-        }
-    }
-
-    if (!pn)
-        return NULL;
-
-    /* Check for falling off the end of a function that returns a value. */
-    if (context->hasStrictOption() && pc->funHasReturnExpr &&
-        !CheckFinalReturn(context, this, pn))
-    {
-        pn = NULL;
-    }
-
     /* Time to implement the odd semantics of 'arguments'. */
     HandlePropertyName arguments = context->names().arguments;
 
     /*
      * Non-top-level functions use JSOP_DEFFUN which is a dynamic scope
      * operation which means it aliases any bindings with the same name.
      * Due to the implicit declaration mechanism (below), 'arguments' will not
      * have decls and, even if it did, they will not be noted as closed in the
@@ -838,45 +870,45 @@ Parser::functionBody(FunctionBodyType ty
      * the function body.
      */
     for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) {
         if (r.front().key() == arguments) {
             Definition *dn = r.front().value();
             pc->lexdeps->remove(arguments);
             dn->pn_dflags |= PND_IMPLICITARGUMENTS;
             if (!pc->define(context, arguments, dn, Definition::VAR))
-                return NULL;
+                return false;
             break;
         }
     }
 
     /*
      * Report error if both rest parameters and 'arguments' are used. Do this
      * check before adding artificial 'arguments' below.
      */
     Definition *maybeArgDef = pc->decls().lookupFirst(arguments);
     bool argumentsHasBinding = !!maybeArgDef;
     bool argumentsHasLocalBinding = maybeArgDef && maybeArgDef->kind() != Definition::ARG;
     bool hasRest = pc->sc->asFunctionBox()->function()->hasRest();
     if (hasRest && argumentsHasLocalBinding) {
-        reportError(NULL, JSMSG_ARGUMENTS_AND_REST);
-        return NULL;
+        report(ParseError, false, NULL, JSMSG_ARGUMENTS_AND_REST);
+        return false;
     }
 
     /*
      * Even if 'arguments' isn't explicitly mentioned, dynamic name lookup
      * forces an 'arguments' binding. The exception is that functions with rest
      * parameters are free from 'arguments'.
      */
     if (!argumentsHasBinding && pc->sc->bindingsAccessedDynamically() && !hasRest) {
-        ParseNode *pn = NameNode::create(PNK_NAME, arguments, this, pc);
+        ParseNode *pn = NameNode::create(PNK_NAME, arguments, &handler, pc);
         if (!pn)
-            return NULL;
+            return false;
         if (!pc->define(context, arguments, pn, Definition::VAR))
-            return NULL;
+            return false;
         argumentsHasBinding = true;
         argumentsHasLocalBinding = true;
     }
 
     /*
      * Now that all possible 'arguments' bindings have been added, note whether
      * 'arguments' has a local binding and whether it unconditionally needs an
      * arguments object. (Also see the flags' comments in ContextFlags.)
@@ -911,40 +943,86 @@ Parser::functionBody(FunctionBodyType ty
          * to any mutation we create it eagerly whenever parameters are (or
          * might, in the case of calls to eval) be assigned.
          */
         if (pc->sc->needStrictChecks()) {
             for (AtomDefnListMap::Range r = pc->decls().all(); !r.empty(); r.popFront()) {
                 DefinitionList &dlist = r.front().value();
                 for (DefinitionList::Range dr = dlist.all(); !dr.empty(); dr.popFront()) {
                     Definition *dn = dr.front();
-                    if (dn->kind() == Definition::ARG && dn->isAssigned()) {
+                    if (dn->kind() == Definition::ARG && dn->isAssigned())
                         funbox->setDefinitelyNeedsArgsObj();
-                        goto exitLoop;
-                    }
                 }
             }
             /* Watch for mutation of arguments through e.g. eval(). */
             if (pc->sc->bindingsAccessedDynamically())
                 funbox->setDefinitelyNeedsArgsObj();
-          exitLoop: ;
         }
     }
 
+    return true;
+}
+
+template <>
+bool
+Parser<SyntaxParseHandler>::checkFunctionArguments()
+{
+    return true;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::functionBody(FunctionBodyType type)
+{
+    JS_ASSERT(pc->sc->isFunctionBox());
+    JS_ASSERT(!pc->funHasReturnExpr && !pc->funHasReturnVoid);
+
+    Node pn;
+    if (type == StatementListBody) {
+        pn = statements();
+        if (!pn)
+            return null();
+    } else {
+        JS_ASSERT(type == ExpressionBody);
+        JS_ASSERT(JS_HAS_EXPR_CLOSURES);
+
+        Node kid = assignExpr();
+        if (!kid)
+            return null();
+
+        pn = handler.newUnary(PNK_RETURN, kid, JSOP_RETURN);
+        if (!pn)
+            return null();
+
+        if (pc->sc->asFunctionBox()->isGenerator()) {
+            reportBadReturn(pn, ParseError,
+                            JSMSG_BAD_GENERATOR_RETURN,
+                            JSMSG_BAD_ANON_GENERATOR_RETURN);
+            return null();
+        }
+    }
+
+    /* Check for falling off the end of a function that returns a value. */
+    if (context->hasStrictOption() && pc->funHasReturnExpr && !checkFinalReturn(pn))
+        return null();
+
+    if (!checkFunctionArguments())
+        return null();
+
     return pn;
 }
 
 // Create a placeholder Definition node for |atom|.
 // Nb: unlike most functions that are passed a Parser, this one gets a
 // SharedContext passed in separately, because in this case |pc| may not equal
 // |parser->pc|.
 static Definition *
-MakePlaceholder(ParseNode *pn, Parser *parser, ParseContext *pc)
+MakePlaceholder(ParseNode *pn, FullParseHandler *handler, ParseContext<FullParseHandler> *pc)
 {
-    Definition *dn = (Definition *) NameNode::create(PNK_NAME, pn->pn_atom, parser, pc);
+    Definition *dn = (Definition *) NameNode::create(PNK_NAME, pn->pn_atom, handler, pc);
     if (!dn)
         return NULL;
 
     dn->setOp(JSOP_NOP);
     dn->setDefn(true);
     dn->pn_dflags |= PND_PLACEHOLDER;
     return dn;
 }
@@ -960,52 +1038,28 @@ ForgetUse(ParseNode *pn)
     ParseNode **pnup = &pn->lexdef()->dn_uses;
     ParseNode *pnu;
     while ((pnu = *pnup) != pn)
         pnup = &pnu->pn_link;
     *pnup = pn->pn_link;
     pn->setUsed(false);
 }
 
-static ParseNode *
-MakeAssignment(ParseNode *pn, ParseNode *rhs, Parser *parser)
+static void
+ForgetUse(SyntaxParseHandler::Node pn)
 {
-    ParseNode *lhs = parser->cloneNode(*pn);
-    if (!lhs)
-        return NULL;
-
-    if (pn->isUsed()) {
-        Definition *dn = pn->pn_lexdef;
-        ParseNode **pnup = &dn->dn_uses;
-
-        while (*pnup != pn)
-            pnup = &(*pnup)->pn_link;
-        *pnup = lhs;
-        lhs->pn_link = pn->pn_link;
-        pn->pn_link = NULL;
-    }
-
-    pn->setKind(PNK_ASSIGN);
-    pn->setOp(JSOP_NOP);
-    pn->setArity(PN_BINARY);
-    pn->setInParens(false);
-    pn->setUsed(false);
-    pn->setDefn(false);
-    pn->pn_left = lhs;
-    pn->pn_right = rhs;
-    pn->pn_pos.end = rhs->pn_pos.end;
-    return lhs;
 }
 
 /* See comment for use in Parser::functionDef. */
-static bool
-MakeDefIntoUse(Definition *dn, ParseNode *pn, JSAtom *atom, Parser *parser)
+template <>
+bool
+Parser<FullParseHandler>::makeDefIntoUse(Definition *dn, ParseNode *pn, JSAtom *atom)
 {
     /* Turn pn into a definition. */
-    parser->pc->updateDecl(atom, pn);
+    pc->updateDecl(atom, pn);
 
     /* Change all uses of dn to be uses of pn. */
     for (ParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
         JS_ASSERT(pnu->isUsed());
         JS_ASSERT(!pnu->isDefn());
         pnu->pn_lexdef = (Definition *) pn;
         pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
     }
@@ -1025,30 +1079,30 @@ MakeDefIntoUse(Definition *dn, ParseNode
      *     assertEq(g(), 2);
      *   }
      *
      * both asserts are valid.
      */
     if (dn->getKind() == PNK_FUNCTION) {
         JS_ASSERT(dn->functionIsHoisted());
         pn->dn_uses = dn->pn_link;
-        parser->prepareNodeForMutation(dn);
+        handler.prepareNodeForMutation(dn);
         dn->setKind(PNK_NOP);
         dn->setArity(PN_NULLARY);
         return true;
     }
 
     /*
      * If dn is arg, or in [var, const, let] and has an initializer, then we
      * must rewrite it to be an assignment node, whose freshly allocated
      * left-hand side becomes a use of pn.
      */
     if (dn->canHaveInitializer()) {
         if (ParseNode *rhs = dn->expr()) {
-            ParseNode *lhs = MakeAssignment(dn, rhs, parser);
+            ParseNode *lhs = handler.makeAssignment(dn, rhs);
             if (!lhs)
                 return false;
             pn->dn_uses = lhs;
             dn->pn_link = NULL;
             dn = (Definition *) lhs;
         }
     }
 
@@ -1067,57 +1121,57 @@ MakeDefIntoUse(Definition *dn, ParseNode
 
 /*
  * Parameter block types for the several Binder functions.  We use a common
  * helper function signature in order to share code among destructuring and
  * simple variable declaration parsers.  In the destructuring case, the binder
  * function is called indirectly from the variable declaration parser by way
  * of CheckDestructuring and its friends.
  */
-typedef bool
-(*Binder)(JSContext *cx, BindData *data, HandlePropertyName name, Parser *parser);
-
-static bool
-BindLet(JSContext *cx, BindData *data, HandlePropertyName name, Parser *parser);
-
-static bool
-BindVarOrConst(JSContext *cx, BindData *data, HandlePropertyName name, Parser *parser);
-
-struct frontend::BindData {
+
+template <typename ParseHandler>
+struct BindData
+{
     BindData(JSContext *cx) : let(cx) {}
 
-    ParseNode       *pn;        /* name node for definition processing and
-                                   error source coordinates */
+    typedef bool
+    (*Binder)(JSContext *cx, BindData *data, HandlePropertyName name, Parser<ParseHandler> *parser);
+
+    /* name node for definition processing and error source coordinates */
+    typename ParseHandler::Node pn;
+
     JSOp            op;         /* prolog bytecode or nop */
     Binder          binder;     /* binder, discriminates u */
 
     struct LetData {
         LetData(JSContext *cx) : blockObj(cx) {}
         VarContext varContext;
         RootedStaticBlockObject blockObj;
         unsigned   overflow;
     } let;
 
     void initLet(VarContext varContext, StaticBlockObject &blockObj, unsigned overflow) {
-        this->pn = NULL;
+        this->pn = ParseHandler::null();
         this->op = JSOP_NOP;
-        this->binder = BindLet;
+        this->binder = Parser<ParseHandler>::bindLet;
         this->let.varContext = varContext;
         this->let.blockObj = &blockObj;
         this->let.overflow = overflow;
     }
 
     void initVarOrConst(JSOp op) {
         this->op = op;
-        this->binder = BindVarOrConst;
+        this->binder = Parser<ParseHandler>::bindVarOrConst;
     }
 };
 
+template <typename ParseHandler>
 JSFunction *
-Parser::newFunction(ParseContext *pc, HandleAtom atom, FunctionSyntaxKind kind)
+Parser<ParseHandler>::newFunction(ParseContext<ParseHandler> *pc, HandleAtom atom,
+                                  FunctionSyntaxKind kind)
 {
     JS_ASSERT_IF(kind == Statement, atom != NULL);
 
     /*
      * Find the global compilation context in order to pre-set the newborn
      * function's parent slot to pc->sc->asGlobal()->scopeChain. If the global
      * context is a compile-and-go one, we leave the pre-set parent intact;
      * otherwise we clear parent and proto.
@@ -1180,23 +1234,23 @@ DeoptimizeUsesWithin(Definition *dn, con
 }
 
 /*
  * Beware: this function is called for functions nested in other functions or
  * global scripts but not for functions compiled through the Function
  * constructor or JSAPI. To always execute code when a function has finished
  * parsing, use Parser::functionBody.
  */
-static bool
-LeaveFunction(ParseNode *fn, Parser *parser, HandlePropertyName funName,
-              FunctionSyntaxKind kind = Expression)
+template <>
+bool
+Parser<FullParseHandler>::leaveFunction(ParseNode *fn, HandlePropertyName funName,
+                                        FunctionSyntaxKind kind)
 {
-    JSContext *cx = parser->context;
-    ParseContext *funpc = parser->pc;
-    ParseContext *pc = funpc->parent;
+    ParseContext<FullParseHandler> *funpc = pc;
+    ParseContext<FullParseHandler> *pc = funpc->parent;
     pc->blockidGen = funpc->blockidGen;
 
     FunctionBox *funbox = fn->pn_funbox;
     JS_ASSERT(funbox == funpc->sc->asFunctionBox());
 
     if (!pc->topStmt || pc->topStmt->type == STMT_BLOCK)
         fn->pn_dflags |= PND_BLOCKCHILD;
 
@@ -1204,17 +1258,17 @@ LeaveFunction(ParseNode *fn, Parser *par
     if (funpc->lexdeps->count()) {
         for (AtomDefnRange r = funpc->lexdeps->all(); !r.empty(); r.popFront()) {
             JSAtom *atom = r.front().key();
             Definition *dn = r.front().value();
             JS_ASSERT(dn->isPlaceholder());
 
             if (atom == funName && kind == Expression) {
                 dn->setOp(JSOP_CALLEE);
-                if (!dn->pn_cookie.set(cx, funpc->staticLevel,
+                if (!dn->pn_cookie.set(context, funpc->staticLevel,
                                        UpvarCookie::CALLEE_SLOT))
                     return false;
                 dn->pn_dflags |= PND_BOUND;
                 JS_ASSERT(dn->kind() == Definition::NAMED_LAMBDA);
 
                 /*
                  * Since 'dn' is a placeholder, it has not been defined in the
                  * ParseContext and hence we must manually flag a closed-over
@@ -1262,17 +1316,17 @@ LeaveFunction(ParseNode *fn, Parser *par
                      *   function f() { x; { function g() { x; } let x; } }
                      *
                      * Here, the 'let' must not capture all the uses of f's
                      * lexdep entry for x, but it must capture the x node
                      * referred to from g's TOK_UPVARS node.  Always turning
                      * inherited lexdeps into uses of a new outer definition
                      * allows us to handle both these cases in a natural way.
                      */
-                    outer_dn = MakePlaceholder(dn, parser, pc);
+                    outer_dn = MakePlaceholder(dn, &handler, pc);
                     if (!outer_dn || !pc->lexdeps->add(p, atom, outer_dn))
                         return false;
                 }
             }
 
             /*
              * Insert dn's uses list at the front of outer_dn's list.
              *
@@ -1304,308 +1358,317 @@ LeaveFunction(ParseNode *fn, Parser *par
 
             /* Mark the outer dn as escaping. */
             outer_dn->pn_dflags |= PND_CLOSED;
         }
     }
 
     InternalHandle<Bindings*> bindings =
         InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
-    if (!funpc->generateFunctionBindings(cx, bindings))
+    if (!funpc->generateFunctionBindings(context, bindings))
         return false;
 
-    funpc->lexdeps.releaseMap(cx);
+    funpc->lexdeps.releaseMap(context);
+    return true;
+}
+
+template <>
+bool
+Parser<SyntaxParseHandler>::leaveFunction(Node fn, HandlePropertyName funName,
+                                          FunctionSyntaxKind kind)
+{
+    pc->lexdeps.releaseMap(context);
     return true;
 }
 
 /*
- * DefineArg is called for both the arguments of a regular function definition
+ * defineArg is called for both the arguments of a regular function definition
  * and the arguments specified by the Function constructor.
  *
  * The 'disallowDuplicateArgs' bool indicates whether the use of another
  * feature (destructuring or default arguments) disables duplicate arguments.
  * (ECMA-262 requires us to support duplicate parameter names, but, for newer
  * features, we consider the code to have "opted in" to higher standards and
  * forbid duplicates.)
  *
  * If 'duplicatedArg' is non-null, then DefineArg assigns to it any previous
  * argument with the same name. The caller may use this to report an error when
  * one of the abovementioned features occurs after a duplicate.
  */
+template <>
 bool
-frontend::DefineArg(Parser *parser, ParseNode *funcpn, HandlePropertyName name,
-                    bool disallowDuplicateArgs, Definition **duplicatedArg)
+Parser<FullParseHandler>::defineArg(ParseNode *funcpn, HandlePropertyName name,
+                                    bool disallowDuplicateArgs, Definition **duplicatedArg)
 {
-    JSContext *cx = parser->context;
-    ParseContext *pc = parser->pc;
     SharedContext *sc = pc->sc;
 
     /* Handle duplicate argument names. */
     if (Definition *prevDecl = pc->decls().lookupFirst(name)) {
         /*
          * Strict-mode disallows duplicate args. We may not know whether we are
          * in strict mode or not (since the function body hasn't been parsed).
          * In such cases, reportStrictModeError will queue up the potential
          * error and return 'true'.
          */
         if (sc->needStrictChecks()) {
             JSAutoByteString bytes;
-            if (!js_AtomToPrintableString(cx, name, &bytes))
+            if (!js_AtomToPrintableString(context, name, &bytes))
                 return false;
-            if (!parser->reportStrictModeError(prevDecl, JSMSG_DUPLICATE_FORMAL, bytes.ptr()))
+            if (!report(ParseStrictError, pc->sc->strict, prevDecl,
+                        JSMSG_DUPLICATE_FORMAL, bytes.ptr()))
+            {
                 return false;
+            }
         }
 
         if (disallowDuplicateArgs) {
-            parser->reportError(prevDecl, JSMSG_BAD_DUP_ARGS);
+            report(ParseError, false, prevDecl, JSMSG_BAD_DUP_ARGS);
             return false;
         }
 
         if (duplicatedArg)
             *duplicatedArg = prevDecl;
 
         /* ParseContext::define assumes and asserts prevDecl is not in decls. */
         pc->prepareToAddDuplicateArg(prevDecl);
     }
 
-    ParseNode *argpn = NameNode::create(PNK_NAME, name, parser, parser->pc);
+    ParseNode *argpn = handler.newName(name, pc);
     if (!argpn)
         return false;
 
-    if (!CheckStrictBinding(parser->context, parser, name, argpn))
+    if (!checkStrictBinding(name, argpn))
         return false;
 
     funcpn->pn_body->append(argpn);
-    return parser->pc->define(parser->context, name, argpn, Definition::ARG);
+    return pc->define(context, name, argpn, Definition::ARG);
+}
+
+template <>
+bool
+Parser<SyntaxParseHandler>::defineArg(Node funcpn, HandlePropertyName name,
+                                      bool disallowDuplicateArgs, DefinitionNode *duplicatedArg)
+{
+    return true;
 }
 
 #if JS_HAS_DESTRUCTURING
-static bool
-BindDestructuringArg(JSContext *cx, BindData *data, HandlePropertyName name, Parser *parser)
+template <typename ParseHandler>
+/* static */ bool
+Parser<ParseHandler>::bindDestructuringArg(JSContext *cx, BindData<ParseHandler> *data,
+                                           HandlePropertyName name, Parser<ParseHandler> *parser)
 {
-    ParseContext *pc = parser->pc;
+    ParseContext<ParseHandler> *pc = parser->pc;
     JS_ASSERT(pc->sc->isFunctionBox());
 
     if (pc->decls().lookupFirst(name)) {
-        parser->reportError(NULL, JSMSG_BAD_DUP_ARGS);
+        parser->report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS);
         return false;
     }
 
-    if (!CheckStrictBinding(cx, parser, name, data->pn))
+    if (!parser->checkStrictBinding(name, data->pn))
         return false;
 
     return pc->define(cx, name, data->pn, Definition::VAR);
 }
 #endif /* JS_HAS_DESTRUCTURING */
 
+template <typename ParseHandler>
 bool
-Parser::functionArguments(ParseNode **listp, ParseNode* funcpn, bool &hasRest)
+Parser<ParseHandler>::functionArguments(Node *listp, Node funcpn, bool &hasRest)
 {
     if (tokenStream.getToken() != TOK_LP) {
-        reportError(NULL, JSMSG_PAREN_BEFORE_FORMAL);
+        report(ParseError, false, null(), JSMSG_PAREN_BEFORE_FORMAL);
         return false;
     }
 
     FunctionBox *funbox = pc->sc->asFunctionBox();
     funbox->bufStart = tokenStream.offsetOfToken(tokenStream.currentToken());
 
     hasRest = false;
 
-    ParseNode *argsbody = ListNode::create(PNK_ARGSBODY, this);
+    Node argsbody = handler.newList(PNK_ARGSBODY);
     if (!argsbody)
         return false;
-    argsbody->setOp(JSOP_NOP);
-    argsbody->makeEmpty();
-
-    funcpn->pn_body = argsbody;
+    handler.setFunctionBody(funcpn, argsbody);
 
     if (!tokenStream.matchToken(TOK_RP)) {
         bool hasDefaults = false;
-        Definition *duplicatedArg = NULL;
+        DefinitionNode duplicatedArg = null();
         bool destructuringArg = false;
 #if JS_HAS_DESTRUCTURING
-        ParseNode *list = NULL;
+        Node list = null();
 #endif
 
         do {
             if (hasRest) {
-                reportError(NULL, JSMSG_PARAMETER_AFTER_REST);
+                report(ParseError, false, null(), JSMSG_PARAMETER_AFTER_REST);
                 return false;
             }
             switch (TokenKind tt = tokenStream.getToken()) {
 #if JS_HAS_DESTRUCTURING
               case TOK_LB:
               case TOK_LC:
               {
                 /* See comment below in the TOK_NAME case. */
                 if (duplicatedArg) {
-                    reportError(duplicatedArg, JSMSG_BAD_DUP_ARGS);
+                    report(ParseError, false, duplicatedArg, JSMSG_BAD_DUP_ARGS);
                     return false;
                 }
 
                 if (hasDefaults) {
-                    reportError(NULL, JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT);
+                    report(ParseError, false, null(), JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT);
                     return false;
                 }
 
                 destructuringArg = true;
 
                 /*
                  * A destructuring formal parameter turns into one or more
                  * local variables initialized from properties of a single
                  * anonymous positional parameter, so here we must tweak our
                  * binder and its data.
                  */
-                BindData data(context);
-                data.pn = NULL;
+                BindData<ParseHandler> data(context);
+                data.pn = ParseHandler::null();
                 data.op = JSOP_DEFVAR;
-                data.binder = BindDestructuringArg;
-                ParseNode *lhs = destructuringExpr(&data, tt);
+                data.binder = bindDestructuringArg;
+                Node lhs = destructuringExpr(&data, tt);
                 if (!lhs)
                     return false;
 
                 /*
                  * Synthesize a destructuring assignment from the single
                  * anonymous positional parameter into the destructuring
                  * left-hand-side expression and accumulate it in list.
                  */
                 HandlePropertyName name = context->names().empty;
-                ParseNode *rhs = NameNode::create(PNK_NAME, name, this, this->pc);
+                Node rhs = handler.newName(name, pc);
                 if (!rhs)
                     return false;
 
                 if (!pc->define(context, name, rhs, Definition::ARG))
                     return false;
 
-                ParseNode *item = new_<BinaryNode>(PNK_ASSIGN, JSOP_NOP, lhs->pn_pos, lhs, rhs);
+                Node item = handler.newBinary(PNK_ASSIGN, lhs, rhs);
                 if (!item)
                     return false;
                 if (list) {
-                    list->append(item);
+                    handler.addList(list, item);
                 } else {
-                    list = ListNode::create(PNK_VAR, this);
+                    list = handler.newList(PNK_VAR, item);
                     if (!list)
                         return false;
-                    list->initList(item);
                     *listp = list;
                 }
                 break;
               }
 #endif /* JS_HAS_DESTRUCTURING */
 
               case TOK_TRIPLEDOT:
               {
                 hasRest = true;
                 tt = tokenStream.getToken();
                 if (tt != TOK_NAME) {
                     if (tt != TOK_ERROR)
-                        reportError(NULL, JSMSG_NO_REST_NAME);
+                        report(ParseError, false, null(), JSMSG_NO_REST_NAME);
                     return false;
                 }
                 /* Fall through */
               }
 
               case TOK_NAME:
               {
                 RootedPropertyName name(context, tokenStream.currentToken().name());
                 bool disallowDuplicateArgs = destructuringArg || hasDefaults;
-                if (!DefineArg(this, funcpn, name, disallowDuplicateArgs, &duplicatedArg))
+                if (!defineArg(funcpn, name, disallowDuplicateArgs, &duplicatedArg))
                     return false;
 
                 if (tokenStream.matchToken(TOK_ASSIGN)) {
                     if (hasRest) {
-                        reportError(NULL, JSMSG_REST_WITH_DEFAULT);
+                        report(ParseError, false, null(), JSMSG_REST_WITH_DEFAULT);
                         return false;
                     }
                     if (duplicatedArg) {
-                        reportError(duplicatedArg, JSMSG_BAD_DUP_ARGS);
+                        report(ParseError, false, duplicatedArg, JSMSG_BAD_DUP_ARGS);
                         return false;
                     }
                     hasDefaults = true;
-                    ParseNode *def_expr = assignExprWithoutYield(JSMSG_YIELD_IN_DEFAULT);
+                    Node def_expr = assignExprWithoutYield(JSMSG_YIELD_IN_DEFAULT);
                     if (!def_expr)
                         return false;
-                    ParseNode *arg = funcpn->pn_body->last();
-                    arg->pn_dflags |= PND_DEFAULT;
-                    arg->pn_expr = def_expr;
+                    handler.setLastFunctionArgumentDefault(funcpn, def_expr);
                     funbox->ndefaults++;
                 } else if (!hasRest && hasDefaults) {
-                    reportError(NULL, JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT);
+                    report(ParseError, false, null(), JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT);
                     return false;
                 }
 
                 break;
               }
 
               default:
-                reportError(NULL, JSMSG_MISSING_FORMAL);
+                report(ParseError, false, null(), JSMSG_MISSING_FORMAL);
                 /* FALL THROUGH */
               case TOK_ERROR:
                 return false;
             }
         } while (tokenStream.matchToken(TOK_COMMA));
 
         if (tokenStream.getToken() != TOK_RP) {
-            reportError(NULL, JSMSG_PAREN_AFTER_FORMAL);
+            report(ParseError, false, null(), JSMSG_PAREN_AFTER_FORMAL);
             return false;
         }
     }
 
     return true;
 }
 
-ParseNode *
-Parser::functionDef(HandlePropertyName funName, const TokenStream::Position &start,
-                    FunctionType type, FunctionSyntaxKind kind)
+template <>
+bool
+Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
+                                                  ParseNode **pn_, FunctionSyntaxKind kind)
 {
-    JS_ASSERT_IF(kind == Statement, funName);
-
-    /* Make a TOK_FUNCTION node. */
-    ParseNode *pn = CodeNode::create(PNK_FUNCTION, this);
-    if (!pn)
-        return NULL;
-    pn->pn_body = NULL;
-    pn->pn_funbox = NULL;
-    pn->pn_cookie.makeFree();
-    pn->pn_dflags = 0;
+    ParseNode *&pn = *pn_;
 
     /* Function statements add a binding to the enclosing scope. */
     bool bodyLevel = pc->atBodyLevel();
+
     if (kind == Statement) {
         /*
          * Handle redeclaration and optimize cases where we can statically bind the
          * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup).
          */
         if (Definition *dn = pc->decls().lookupFirst(funName)) {
             JS_ASSERT(!dn->isUsed());
             JS_ASSERT(dn->isDefn());
 
             if (context->hasStrictOption() || dn->kind() == Definition::CONST) {
                 JSAutoByteString name;
-                Reporter reporter = (dn->kind() != Definition::CONST)
-                                    ? &Parser::reportStrictWarning
-                                    : &Parser::reportError;
+                ParseReportKind reporter = (dn->kind() != Definition::CONST)
+                                           ? ParseStrictWarning
+                                           : ParseError;
                 if (!js_AtomToPrintableString(context, funName, &name) ||
-                    !(this->*reporter)(NULL, JSMSG_REDECLARED_VAR, Definition::kindString(dn->kind()),
-                                       name.ptr()))
+                    !report(reporter, false, NULL, JSMSG_REDECLARED_VAR,
+                            Definition::kindString(dn->kind()), name.ptr()))
                 {
-                    return NULL;
+                    return false;
                 }
             }
 
             /*
              * Body-level function statements are effectively variable
              * declarations where the initialization is hoisted to the
              * beginning of the block. This means that any other variable
              * declaration with the same name is really just an assignment to
              * the function's binding (which is mutable), so turn any existing
              * declaration into a use.
              */
-            if (bodyLevel && !MakeDefIntoUse(dn, pn, funName, this))
-                return NULL;
+            if (bodyLevel && !makeDefIntoUse(dn, pn, funName))
+                return false;
         } else if (bodyLevel) {
             /*
              * If this function was used before it was defined, claim the
              * pre-created definition node for this function that primaryExpr
              * put in pc->lexdeps on first forward reference, and recycle pn.
              */
             if (Definition *fn = pc->lexdeps.lookupDefn(funName)) {
                 JS_ASSERT(fn->isDefn());
@@ -1613,22 +1676,22 @@ Parser::functionDef(HandlePropertyName f
                 fn->setArity(PN_CODE);
                 fn->pn_pos.begin = pn->pn_pos.begin;
                 fn->pn_pos.end = pn->pn_pos.end;
 
                 fn->pn_body = NULL;
                 fn->pn_cookie.makeFree();
 
                 pc->lexdeps->remove(funName);
-                freeTree(pn);
+                handler.freeTree(pn);
                 pn = fn;
             }
 
             if (!pc->define(context, funName, pn, Definition::VAR))
-                return NULL;
+                return false;
         }
 
         /*
          * As a SpiderMonkey-specific extension, non-body-level function
          * statements (e.g., functions in an "if" or "while" block) are
          * dynamically bound when control flow reaches the statement. The
          * emitter normally emits functions in two passes (see PNK_ARGSBODY).
          * To distinguish
@@ -1651,137 +1714,87 @@ Parser::functionDef(HandlePropertyName f
              * Instead of setting bindingsAccessedDynamically, which would be
              * overly conservative, remember the names of all function
              * statements and mark any bindings with the same as aliased at the
              * end of functionBody.
              */
             if (!pc->funcStmts) {
                 pc->funcStmts = context->new_<FuncStmtSet>(context);
                 if (!pc->funcStmts || !pc->funcStmts->init())
-                    return NULL;
+                    return false;
             }
             if (!pc->funcStmts->put(funName))
-                return NULL;
+                return false;
         }
 
         /* No further binding (in BindNameToSlot) is needed for functions. */
         pn->pn_dflags |= PND_BOUND;
     } else {
         /* A function expression does not introduce any binding. */
         pn->setOp(JSOP_LAMBDA);
     }
 
+    return true;
+}
+
+template <>
+bool
+Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
+                                                    Node *pn, FunctionSyntaxKind kind)
+{
+    return true;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream::Position &start,
+                                  FunctionType type, FunctionSyntaxKind kind)
+{
+    JS_ASSERT_IF(kind == Statement, funName);
+
+    /* Make a TOK_FUNCTION node. */
+    Node pn = handler.newFunctionDefinition();
+    if (!pn)
+        return null();
+
+    if (!checkFunctionDefinition(funName, &pn, kind))
+        return null();
+
     RootedFunction fun(context, newFunction(pc, funName, kind));
     if (!fun)
-        return NULL;
+        return null();
 
     // If the outer scope is strict, immediately parse the function in strict
     // mode. Otherwise, we parse it normally. If we see a "use strict"
     // directive, we backup and reparse it as strict.
-    pn->pn_body = NULL;
+    handler.setFunctionBody(pn, null());
     bool initiallyStrict = pc->sc->strict;
     bool becameStrict;
     if (!functionArgsAndBody(pn, fun, funName, type, kind, initiallyStrict, &becameStrict)) {
         if (initiallyStrict || !becameStrict || tokenStream.hadError())
-            return NULL;
+            return null();
 
         // Reparse the function in strict mode.
         tokenStream.seek(start);
         if (funName && tokenStream.getToken() == TOK_ERROR)
-            return NULL;
-        pn->pn_body = NULL;
+            return null();
+        handler.setFunctionBody(pn, null());
         if (!functionArgsAndBody(pn, fun, funName, type, kind, true))
-            return NULL;
+            return null();
     }
 
     return pn;
 }
 
+template <>
 bool
-Parser::functionArgsAndBody(ParseNode *pn, HandleFunction fun, HandlePropertyName funName, FunctionType type,
-                            FunctionSyntaxKind kind, bool strict, bool *becameStrict)
+Parser<FullParseHandler>::finishFunctionDefinition(ParseNode *pn, FunctionBox *funbox,
+                                                   ParseNode *prelude, ParseNode *body,
+                                                   ParseContext<FullParseHandler> *outerpc)
 {
-    if (becameStrict)
-        *becameStrict = false;
-    ParseContext *outerpc = pc;
-
-    // Create box for fun->object early to protect against last-ditch GC.
-    FunctionBox *funbox = newFunctionBox(fun, pc, strict);
-    if (!funbox)
-        return false;
-
-    /* Initialize early for possible flags mutation via destructuringExpr. */
-    ParseContext funpc(this, funbox, outerpc->staticLevel + 1, outerpc->blockidGen);
-    if (!funpc.init())
-        return false;
-
-    /* Now parse formal argument list and compute fun->nargs. */
-    ParseNode *prelude = NULL;
-    bool hasRest;
-    if (!functionArguments(&prelude, pn, hasRest))
-        return false;
-
-    fun->setArgCount(funpc.numArgs());
-    if (funbox->ndefaults)
-        fun->setHasDefaults();
-    if (hasRest)
-        fun->setHasRest();
-
-    if (type == Getter && fun->nargs > 0) {
-        reportError(NULL, JSMSG_ACCESSOR_WRONG_ARGS, "getter", "no", "s");
-        return false;
-    }
-    if (type == Setter && fun->nargs != 1) {
-        reportError(NULL, JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
-        return false;
-    }
-
-    FunctionBodyType bodyType = StatementListBody;
-#if JS_HAS_EXPR_CLOSURES
-    if (tokenStream.getToken(TSF_OPERAND) != TOK_LC) {
-        tokenStream.ungetToken();
-        bodyType = ExpressionBody;
-        fun->setIsExprClosure();
-    }
-#else
-    if (!tokenStream.matchToken(TOK_LC)) {
-        reportError(NULL, JSMSG_CURLY_BEFORE_BODY);
-        return false;
-    }
-#endif
-
-    ParseNode *body = functionBody(bodyType);
-    if (!body) {
-        // Notify the caller if this function was discovered to be strict.
-        if (becameStrict && pc->funBecameStrict)
-            *becameStrict = true;
-        return false;
-    }
-
-    if (funName && !CheckStrictBinding(context, this, funName, pn))
-        return false;
-
-#if JS_HAS_EXPR_CLOSURES
-    if (bodyType == StatementListBody) {
-#endif
-        if (!tokenStream.matchToken(TOK_RC)) {
-            reportError(NULL, JSMSG_CURLY_AFTER_BODY);
-            return false;
-        }
-        funbox->bufEnd = tokenStream.offsetOfToken(tokenStream.currentToken()) + 1;
-#if JS_HAS_EXPR_CLOSURES
-    } else {
-        // We shouldn't call endOffset if the tokenizer got an error.
-        if (tokenStream.hadError())
-            return false;
-        funbox->bufEnd = tokenStream.endOffset(tokenStream.currentToken());
-        if (kind == Statement && !MatchOrInsertSemicolon(context, &tokenStream))
-            return false;
-    }
-#endif
     pn->pn_pos.end = tokenStream.currentToken().pos.end;
 
     /*
      * Fruit of the poisonous tree: if a closure contains a dynamic name access
      * (eval, with, etc), we consider the parent to do the same. The reason is
      * that the deoptimizing effects of dynamic name access apply equally to
      * parents: any local can be read at runtime.
      */
@@ -1796,134 +1809,252 @@ Parser::functionArgsAndBody(ParseNode *p
      * comma expression that we synthesized to body. If the body is a return
      * node, we must make a special PNK_SEQ node, to prepend the destructuring
      * code without bracing the decompilation of the function body.
      */
     if (prelude) {
         if (!body->isArity(PN_LIST)) {
             ParseNode *block;
 
-            block = ListNode::create(PNK_SEQ, this);
+            block = ListNode::create(PNK_SEQ, &handler);
             if (!block)
                 return false;
             block->pn_pos = body->pn_pos;
             block->initList(body);
 
             body = block;
         }
 
-        ParseNode *item = UnaryNode::create(PNK_SEMI, this);
+        ParseNode *item = UnaryNode::create(PNK_SEMI, &handler);
         if (!item)
             return false;
 
         item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
         item->pn_kid = prelude;
         item->pn_next = body->pn_head;
         body->pn_head = item;
         if (body->pn_tail == &body->pn_head)
             body->pn_tail = &item->pn_next;
         ++body->pn_count;
         body->pn_xflags |= PNX_DESTRUCT;
     }
 #endif
 
-    /*
-     * If any nested function scope does a dynamic scope access, all enclosing
-     * scopes may be accessed dynamically.
-     */
-    if (funbox->bindingsAccessedDynamically())
-        outerpc->sc->setBindingsAccessedDynamically();
-    if (funbox->hasDebuggerStatement())
-        outerpc->sc->setHasDebuggerStatement();
-
     pn->pn_funbox = funbox;
     pn->pn_body->append(body);
     pn->pn_body->pn_pos = body->pn_pos;
     pn->pn_blockid = outerpc->blockid();
 
-    if (!LeaveFunction(pn, this, funName, kind))
-        return false;
-
+    return true;
+}
+
+template <>
+bool
+Parser<SyntaxParseHandler>::finishFunctionDefinition(Node pn, FunctionBox *funbox,
+                                                     Node prelude, Node body,
+                                                     ParseContext<SyntaxParseHandler> *outerpc)
+{
     return true;
 }
 
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun,
+                                          HandlePropertyName funName, FunctionType type,
+                                          FunctionSyntaxKind kind, bool strict, bool *becameStrict)
+{
+    if (becameStrict)
+        *becameStrict = false;
+    ParseContext<ParseHandler> *outerpc = pc;
+
+    // Create box for fun->object early to protect against last-ditch GC.
+    FunctionBox *funbox = newFunctionBox(fun, pc, strict);
+    if (!funbox)
+        return false;
+
+    /* Initialize early for possible flags mutation via destructuringExpr. */
+    ParseContext<ParseHandler> funpc(this, funbox, outerpc->staticLevel + 1, outerpc->blockidGen);
+    if (!funpc.init())
+        return false;
+
+    /* Now parse formal argument list and compute fun->nargs. */
+    Node prelude = null();
+    bool hasRest;
+    if (!functionArguments(&prelude, pn, hasRest))
+        return false;
+
+    fun->setArgCount(funpc.numArgs());
+    if (funbox->ndefaults)
+        fun->setHasDefaults();
+    if (hasRest)
+        fun->setHasRest();
+
+    if (type == Getter && fun->nargs > 0) {
+        report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "getter", "no", "s");
+        return false;
+    }
+    if (type == Setter && fun->nargs != 1) {
+        report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
+        return false;
+    }
+
+    FunctionBodyType bodyType = StatementListBody;
+#if JS_HAS_EXPR_CLOSURES
+    if (tokenStream.getToken(TSF_OPERAND) != TOK_LC) {
+        tokenStream.ungetToken();
+        bodyType = ExpressionBody;
+        fun->setIsExprClosure();
+    }
+#else
+    if (!tokenStream.matchToken(TOK_LC)) {
+        report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY);
+        return false;
+    }
+#endif
+
+    Node body = functionBody(bodyType);
+    if (!body) {
+        // Notify the caller if this function was discovered to be strict.
+        if (becameStrict && pc->funBecameStrict)
+            *becameStrict = true;
+        return false;
+    }
+
+    if (funName && !checkStrictBinding(funName, pn))
+        return false;
+
+#if JS_HAS_EXPR_CLOSURES
+    if (bodyType == StatementListBody) {
+#endif
+        if (!tokenStream.matchToken(TOK_RC)) {
+            report(ParseError, false, null(), JSMSG_CURLY_AFTER_BODY);
+            return false;
+        }
+        funbox->bufEnd = tokenStream.offsetOfToken(tokenStream.currentToken()) + 1;
+#if JS_HAS_EXPR_CLOSURES
+    } else {
+        // We shouldn't call endOffset if the tokenizer got an error.
+        if (tokenStream.hadError())
+            return false;
+        funbox->bufEnd = tokenStream.endOffset(tokenStream.currentToken());
+        if (kind == Statement && !MatchOrInsertSemicolon(context, &tokenStream))
+            return false;
+    }
+#endif
+
+    if (!finishFunctionDefinition(pn, funbox, prelude, body, outerpc))
+        return false;
+
+    return leaveFunction(pn, funName, kind);
+}
+
+template <>
 ParseNode *
-Parser::moduleDecl()
+Parser<FullParseHandler>::moduleDecl()
 {
     JS_ASSERT(tokenStream.currentToken().name() == context->runtime->atomState.module);
     if (!((pc->sc->isGlobalSharedContext() || pc->sc->isModuleBox()) && pc->atBodyLevel()))
     {
-        reportError(NULL, JSMSG_MODULE_STATEMENT);
+        report(ParseError, false, NULL, JSMSG_MODULE_STATEMENT);
         return NULL;
     }
 
-    ParseNode *pn = CodeNode::create(PNK_MODULE, this);
+    ParseNode *pn = CodeNode::create(PNK_MODULE, &handler);
     if (!pn)
         return NULL;
     JS_ALWAYS_TRUE(tokenStream.matchToken(TOK_STRING));
     RootedAtom atom(context, tokenStream.currentToken().atom());
     Module *module = js_NewModule(context, atom);
     if (!module)
         return NULL;
     ModuleBox *modulebox = newModuleBox(module, pc);
     if (!modulebox)
         return NULL;
     pn->pn_modulebox = modulebox;
 
-    ParseContext modulepc(this, modulebox, pc->staticLevel + 1, pc->blockidGen);
+    ParseContext<FullParseHandler> modulepc(this, modulebox, pc->staticLevel + 1, pc->blockidGen);
     if (!modulepc.init())
         return NULL;
     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_MODULE);
     pn->pn_body = statements();
     if (!pn->pn_body)
         return NULL;
     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_MODULE);
 
     return pn;
 }
 
-ParseNode *
-Parser::functionStmt()
+template <>
+SyntaxParseHandler::Node
+Parser<SyntaxParseHandler>::moduleDecl()
+{
+    setUnknownResult();
+    return SyntaxParseHandler::NodeFailure;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::functionStmt()
 {
     JS_ASSERT(tokenStream.currentToken().type == TOK_FUNCTION);
     RootedPropertyName name(context);
     if (tokenStream.getToken(TSF_KEYWORD_IS_NAME) == TOK_NAME) {
         name = tokenStream.currentToken().name();
     } else {
         /* Unnamed function expressions are forbidden in statement context. */
-        reportError(NULL, JSMSG_UNNAMED_FUNCTION_STMT);
-        return NULL;
+        report(ParseError, false, null(), JSMSG_UNNAMED_FUNCTION_STMT);
+        return null();
     }
 
     TokenStream::Position start;
     tokenStream.positionAfterLastFunctionKeyword(start);
 
     /* We forbid function statements in strict mode code. */
     if (!pc->atBodyLevel() && pc->sc->needStrictChecks() &&
-        !reportStrictModeError(NULL, JSMSG_STRICT_FUNCTION_STATEMENT))
-        return NULL;
+        !report(ParseStrictError, pc->sc->strict, null(), JSMSG_STRICT_FUNCTION_STATEMENT))
+        return null();
 
     return functionDef(name, start, Normal, Statement);
 }
 
-ParseNode *
-Parser::functionExpr()
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::functionExpr()
 {
     RootedPropertyName name(context);
     JS_ASSERT(tokenStream.currentToken().type == TOK_FUNCTION);
     TokenStream::Position start;
     tokenStream.positionAfterLastFunctionKeyword(start);
     if (tokenStream.getToken(TSF_KEYWORD_IS_NAME) == TOK_NAME)
         name = tokenStream.currentToken().name();
     else
         tokenStream.ungetToken();
     return functionDef(name, start, Normal, Expression);
 }
 
 /*
+ * Return true if this node, known to be an unparenthesized string literal,
+ * could be the string of a directive in a Directive Prologue. Directive
+ * strings never contain escape sequences or line continuations.
+ * isEscapeFreeStringLiteral, below, checks whether the node itself could be
+ * a directive.
+ */
+static inline bool
+IsEscapeFreeStringLiteral(const TokenPos &pos, JSAtom *str)
+{
+    /*
+     * If the string's length in the source code is its length as a value,
+     * accounting for the quotes, then it must not contain any escape
+     * sequences or line continuations.
+     */
+    return (pos.begin.lineno == pos.end.lineno &&
+            pos.begin.index + str->length() + 2 == pos.end.index);
+}
+
+/*
  * Recognize Directive Prologue members and directives. Assuming |pn| is a
  * candidate for membership in a directive prologue, recognize directives and
  * set |pc|'s flags accordingly. If |pn| is indeed part of a prologue, set its
  * |pn_prologue| flag.
  *
  * Note that the following is a strict mode function:
  *
  * function foo() {
@@ -1932,161 +2063,179 @@ Parser::functionExpr()
  *   "use\x20loose"
  *   "use strict"
  * }
  *
  * That is, even though "use\x20loose" can never be a directive, now or in the
  * future (because of the hex escape), the Directive Prologue extends through it
  * to the "use strict" statement, which is indeed a directive.
  */
+template <typename ParseHandler>
 bool
-Parser::maybeParseDirective(ParseNode *pn, bool *cont)
+Parser<ParseHandler>::maybeParseDirective(Node pn, bool *cont)
 {
-    *cont = pn->isStringExprStatement();
+    TokenPos directivePos;
+    JSAtom *directive = handler.isStringExprStatement(pn, &directivePos);
+
+    *cont = !!directive;
     if (!*cont)
         return true;
 
-    ParseNode *string = pn->pn_kid;
-    if (string->isEscapeFreeStringLiteral()) {
+    if (IsEscapeFreeStringLiteral(directivePos, directive)) {
         // Mark this statement as being a possibly legitimate part of a
         // directive prologue, so the bytecode emitter won't warn about it being
         // useless code. (We mustn't just omit the statement entirely yet, as it
         // could be producing the value of an eval or JSScript execution.)
         //
         // Note that even if the string isn't one we recognize as a directive,
         // the emitter still shouldn't flag it as useless, as it could become a
         // directive in the future. We don't want to interfere with people
         // taking advantage of directive-prologue-enabled features that appear
         // in other browsers first.
-        pn->pn_prologue = true;
-
-        JSAtom *directive = string->pn_atom;
+        handler.setPrologue(pn);
+
         if (directive == context->runtime->atomState.useStrict) {
             // We're going to be in strict mode. Note that this scope explicitly
             // had "use strict";
             pc->sc->setExplicitUseStrict();
             if (!pc->sc->strict) {
                 if (pc->sc->isFunctionBox()) {
                     // Request that this function be reparsed as strict.
                     pc->funBecameStrict = true;
                     return false;
                 } else {
                     // We don't reparse global scopes, so we keep track of the
                     // one possible strict violation that could occur in the
                     // directive prologue -- octal escapes -- and complain now.
                     if (tokenStream.sawOctalEscape()) {
-                        reportError(NULL, JSMSG_DEPRECATED_OCTAL);
+                        report(ParseError, false, null(), JSMSG_DEPRECATED_OCTAL);
                         return false;
                     }
                     pc->sc->strict = true;
                 }
             }
         }
     }
     return true;
 }
 
+template <>
+void
+Parser<FullParseHandler>::addStatementToList(ParseNode *pn, ParseNode *kid, bool *hasFunctionStmt)
+{
+    JS_ASSERT(pn->isKind(PNK_STATEMENTLIST));
+
+    if (kid->isKind(PNK_FUNCTION)) {
+        /*
+         * PNX_FUNCDEFS notifies the emitter that the block contains body-
+         * level function definitions that should be processed before the
+         * rest of nodes.
+         *
+         * |hasFunctionStmt| is for the TOK_LC case in Statement. It
+         * is relevant only for function definitions not at body-level,
+         * which we call function statements.
+         */
+        if (pc->atBodyLevel()) {
+            pn->pn_xflags |= PNX_FUNCDEFS;
+        } else {
+            /*
+             * General deoptimization was done in functionDef, here we just
+             * need to tell TOK_LC in Parser::statement to add braces.
+             */
+            JS_ASSERT_IF(pc->sc->isFunctionBox(), pc->sc->asFunctionBox()->hasExtensibleScope());
+            if (hasFunctionStmt)
+                *hasFunctionStmt = true;
+        }
+    }
+
+    pn->append(kid);
+    pn->pn_pos.end = kid->pn_pos.end;
+}
+
+template <>
+void
+Parser<SyntaxParseHandler>::addStatementToList(Node pn, Node kid, bool *hasFunctionStmt)
+{
+}
+
 /*
  * Parse the statements in a block, creating a TOK_LC node that lists the
  * statements' trees.  If called from block-parsing code, the caller must
  * match { before and } after.
  */
-ParseNode *
-Parser::statements(bool *hasFunctionStmt)
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::statements(bool *hasFunctionStmt)
 {
-    JS_CHECK_RECURSION(context, return NULL);
+    JS_CHECK_RECURSION(context, return null());
     if (hasFunctionStmt)
         *hasFunctionStmt = false;
 
-    ParseNode *pn = ListNode::create(PNK_STATEMENTLIST, this);
+    Node pn = handler.newList(PNK_STATEMENTLIST);
     if (!pn)
-        return NULL;
-    pn->makeEmpty();
-    pn->pn_blockid = pc->blockid();
-    ParseNode *saveBlock = pc->blockNode;
+        return null();
+    handler.setBlockId(pn, pc->blockid());
+
+    Node saveBlock = pc->blockNode;
     pc->blockNode = pn;
 
     bool canHaveDirectives = pc->atBodyLevel();
     for (;;) {
         TokenKind tt = tokenStream.peekToken(TSF_OPERAND);
         if (tt <= TOK_EOF || tt == TOK_RC) {
             if (tt == TOK_ERROR) {
                 if (tokenStream.isEOF())
                     tokenStream.setUnexpectedEOF();
-                return NULL;
+                return null();
             }
             break;
         }
-        ParseNode *next = statement();
+        Node next = statement();
         if (!next) {
             if (tokenStream.isEOF())
                 tokenStream.setUnexpectedEOF();
-            return NULL;
+            return null();
         }
 
         if (canHaveDirectives) {
             if (!maybeParseDirective(next, &canHaveDirectives))
-                return NULL;
+                return null();
         }
 
-        if (next->isKind(PNK_FUNCTION)) {
-            /*
-             * PNX_FUNCDEFS notifies the emitter that the block contains body-
-             * level function definitions that should be processed before the
-             * rest of nodes.
-             *
-             * |hasFunctionStmt| is for the TOK_LC case in Statement. It
-             * is relevant only for function definitions not at body-level,
-             * which we call function statements.
-             */
-            if (pc->atBodyLevel()) {
-                pn->pn_xflags |= PNX_FUNCDEFS;
-            } else {
-                /*
-                 * General deoptimization was done in functionDef, here we just
-                 * need to tell TOK_LC in Parser::statement to add braces.
-                 */
-                JS_ASSERT_IF(pc->sc->isFunctionBox(), pc->sc->asFunctionBox()->hasExtensibleScope());
-                if (hasFunctionStmt)
-                    *hasFunctionStmt = true;
-            }
-        }
-        pn->append(next);
+        addStatementToList(pn, next, hasFunctionStmt);
     }
 
     /*
      * Handle the case where there was a let declaration under this block.  If
      * it replaced pc->blockNode with a new block node then we must refresh pn
      * and then restore pc->blockNode.
      */
     if (pc->blockNode != pn)
         pn = pc->blockNode;
     pc->blockNode = saveBlock;
 
-    pn->pn_pos.end = tokenStream.currentToken().pos.end;
-    JS_ASSERT(pn->pn_pos.begin <= pn->pn_pos.end);
+    handler.setEndPosition(pn, tokenStream.currentToken().pos.end);
     return pn;
 }
 
-ParseNode *
-Parser::condition()
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::condition()
 {
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
-    ParseNode *pn = parenExpr();
+    Node pn = parenExpr();
     if (!pn)
-        return NULL;
+        return null();
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
 
     /* Check for (a = b) and warn about possible (a == b) mistype. */
-    JS_ASSERT_IF(pn->isKind(PNK_ASSIGN), pn->isOp(JSOP_NOP));
-    if (pn->isKind(PNK_ASSIGN) &&
-        !pn->isInParens() &&
-        !reportStrictWarning(NULL, JSMSG_EQUAL_AS_ASSIGN))
+    if (handler.isOperationWithoutParens(pn, PNK_ASSIGN) &&
+        !report(ParseStrictWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
     {
-        return NULL;
+        return null();
     }
     return pn;
 }
 
 static bool
 MatchLabel(JSContext *cx, TokenStream *ts, MutableHandlePropertyName label)
 {
     TokenKind tt = ts->peekTokenSameLine(TSF_OPERAND);
@@ -2096,46 +2245,49 @@ MatchLabel(JSContext *cx, TokenStream *t
         (void) ts->getToken();
         label.set(ts->currentToken().name());
     } else {
         label.set(NULL);
     }
     return true;
 }
 
-static bool
-ReportRedeclaration(JSContext *cx, Parser *parser, ParseNode *pn, bool isConst, JSAtom *atom)
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::reportRedeclaration(Node pn, bool isConst, JSAtom *atom)
 {
     JSAutoByteString name;
-    if (js_AtomToPrintableString(cx, atom, &name))
-        parser->reportError(pn, JSMSG_REDECLARED_VAR, isConst ? "const" : "variable", name.ptr());
+    if (js_AtomToPrintableString(context, atom, &name))
+        report(ParseError, false, pn, JSMSG_REDECLARED_VAR, isConst ? "const" : "variable", name.ptr());
     return false;
 }
 
 /*
  * Define a let-variable in a block, let-expression, or comprehension scope. pc
  * must already be in such a scope.
  *
  * Throw a SyntaxError if 'atom' is an invalid name. Otherwise create a
  * property for the new variable on the block object, pc->blockChain;
  * populate data->pn->pn_{op,cookie,defn,dflags}; and stash a pointer to
  * data->pn in a slot of the block object.
  */
-static bool
-BindLet(JSContext *cx, BindData *data, HandlePropertyName name, Parser *parser)
+template <>
+/* static */ bool
+Parser<FullParseHandler>::bindLet(JSContext *cx, BindData<FullParseHandler> *data,
+                                  HandlePropertyName name, Parser<FullParseHandler> *parser)
 {
-    ParseContext *pc = parser->pc;
+    ParseContext<FullParseHandler> *pc = parser->pc;
     ParseNode *pn = data->pn;
-    if (!CheckStrictBinding(cx, parser, name, pn))
+    if (!parser->checkStrictBinding(name, pn))
         return false;
 
     Rooted<StaticBlockObject *> blockObj(cx, data->let.blockObj);
     unsigned blockCount = blockObj->slotCount();
     if (blockCount == JS_BIT(16)) {
-        parser->reportError(pn, data->let.overflow);
+        parser->report(ParseError, false, pn, data->let.overflow);
         return false;
     }
 
     /*
      * Assign block-local index to pn->pn_cookie right away, encoding it as an
      * upvar cookie whose skip tells the current static level. The emitter will
      * adjust the node's slot based on its stack depth model -- and, for global
      * and eval code, js::frontend::CompileScript will adjust the slot
@@ -2147,105 +2299,119 @@ BindLet(JSContext *cx, BindData *data, H
     /*
      * For bindings that are hoisted to the beginning of the block/function,
      * define() right now. Otherwise, delay define until PushLetScope.
      */
     if (data->let.varContext == HoistVars) {
         JS_ASSERT(!pc->atBodyLevel());
         Definition *dn = pc->decls().lookupFirst(name);
         if (dn && dn->pn_blockid == pc->blockid())
-            return ReportRedeclaration(cx, parser, pn, dn->isConst(), name);
+            return parser->reportRedeclaration(pn, dn->isConst(), name);
         if (!pc->define(cx, name, pn, Definition::LET))
             return false;
     }
 
     /*
      * Define the let binding's property before storing pn in the the binding's
      * slot indexed by blockCount off the class-reserved slot base.
      */
     bool redeclared;
     RootedId id(cx, NameToId(name));
     RootedShape shape(cx, StaticBlockObject::addVar(cx, blockObj, id, blockCount, &redeclared));
     if (!shape) {
         if (redeclared)
-            ReportRedeclaration(cx, parser, pn, false, name);
+            parser->reportRedeclaration(pn, false, name);
         return false;
     }
 
     /* Store pn in the static block object. */
     blockObj->setDefinitionParseNode(blockCount, reinterpret_cast<Definition *>(pn));
     return true;
 }
 
-template <class Op>
+template <>
+/* static */ bool
+Parser<SyntaxParseHandler>::bindLet(JSContext *cx, BindData<SyntaxParseHandler> *data,
+                                    HandlePropertyName name, Parser<SyntaxParseHandler> *parser)
+{
+    return true;
+}
+
+template <typename ParseHandler, class Op>
 static inline bool
-ForEachLetDef(JSContext *cx, ParseContext *pc, HandleStaticBlockObject blockObj, Op op)
+ForEachLetDef(JSContext *cx, ParseContext<ParseHandler> *pc,
+              HandleStaticBlockObject blockObj, Op op)
 {
     for (Shape::Range r = blockObj->lastProperty()->all(); !r.empty(); r.popFront()) {
         Shape::Range::AutoRooter rooter(cx, &r);
         Shape &shape = r.front();
 
         /* Beware the destructuring dummy slots. */
         if (JSID_IS_INT(shape.propid()))
             continue;
 
         if (!op(cx, pc, blockObj, shape, JSID_TO_ATOM(shape.propid())))
             return false;
     }
     return true;
 }
 
+template <typename ParseHandler>
 struct PopLetDecl {
-    bool operator()(JSContext *, ParseContext *pc, HandleStaticBlockObject, const Shape &,
-                    JSAtom *atom)
+    bool operator()(JSContext *, ParseContext<ParseHandler> *pc, HandleStaticBlockObject,
+                    const Shape &, JSAtom *atom)
     {
         pc->popLetDecl(atom);
         return true;
     }
 };
 
+template <typename ParseHandler>
 static void
-PopStatementPC(JSContext *cx, ParseContext *pc)
+PopStatementPC(JSContext *cx, ParseContext<ParseHandler> *pc)
 {
     RootedStaticBlockObject blockObj(cx, pc->topStmt->blockObj);
     JS_ASSERT(!!blockObj == (pc->topStmt->isBlockScope));
 
     FinishPopStatement(pc);
 
     if (blockObj) {
         JS_ASSERT(!blockObj->inDictionaryMode());
-        ForEachLetDef(cx, pc, blockObj, PopLetDecl());
+        ForEachLetDef(cx, pc, blockObj, PopLetDecl<ParseHandler>());
         blockObj->resetPrevBlockChainFromParser();
     }
 }
 
+template <typename ParseHandler>
 static inline bool
-OuterLet(ParseContext *pc, StmtInfoPC *stmt, HandleAtom atom)
+OuterLet(ParseContext<ParseHandler> *pc, StmtInfoPC *stmt, HandleAtom atom)
 {
     while (stmt->downScope) {
         stmt = LexicalLookup(pc, atom, NULL, stmt->downScope);
         if (!stmt)
             return false;
         if (stmt->type == STMT_BLOCK)
             return true;
     }
     return false;
 }
 
-static bool
-BindVarOrConst(JSContext *cx, BindData *data, HandlePropertyName name, Parser *parser)
+template <>
+/* static */ bool
+Parser<FullParseHandler>::bindVarOrConst(JSContext *cx, BindData<FullParseHandler> *data,
+                                         HandlePropertyName name, Parser<FullParseHandler> *parser)
 {
-    ParseContext *pc = parser->pc;
+    ParseContext<FullParseHandler> *pc = parser->pc;
     ParseNode *pn = data->pn;
     bool isConstDecl = data->op == JSOP_DEFCONST;
 
     /* Default best op for pn is JSOP_NAME; we'll try to improve below. */
     pn->setOp(JSOP_NAME);
 
-    if (!CheckStrictBinding(cx, parser, name, pn))
+    if (!parser->checkStrictBinding(name, pn))
         return false;
 
     StmtInfoPC *stmt = LexicalLookup(pc, name, NULL, (StmtInfoPC *)NULL);
 
     if (stmt && stmt->type == STMT_WITH) {
         pn->pn_dflags |= PND_DEOPTIMIZED;
         if (pc->sc->isFunctionBox())
             pc->sc->asFunctionBox()->setMightAliasLocals();
@@ -2268,140 +2434,149 @@ BindVarOrConst(JSContext *cx, BindData *
     Definition *dn = defs.front();
     Definition::Kind dn_kind = dn->kind();
     if (dn_kind == Definition::ARG) {
         JSAutoByteString bytes;
         if (!js_AtomToPrintableString(cx, name, &bytes))
             return false;
 
         if (isConstDecl) {
-            parser->reportError(pn, JSMSG_REDECLARED_PARAM, bytes.ptr());
+            parser->report(ParseError, false, pn, JSMSG_REDECLARED_PARAM, bytes.ptr());
             return false;
         }
-        if (!parser->reportStrictWarning(pn, JSMSG_VAR_HIDES_ARG, bytes.ptr()))
+        if (!parser->report(ParseStrictWarning, false, pn, JSMSG_VAR_HIDES_ARG, bytes.ptr()))
             return false;
     } else {
         bool error = (isConstDecl ||
                       dn_kind == Definition::CONST ||
                       (dn_kind == Definition::LET &&
                        (stmt->type != STMT_CATCH || OuterLet(pc, stmt, name))));
 
         if (cx->hasStrictOption()
             ? data->op != JSOP_DEFVAR || dn_kind != Definition::VAR
             : error)
         {
             JSAutoByteString bytes;
-            Parser::Reporter reporter =
-                error ? &Parser::reportError : &Parser::reportStrictWarning;
+            ParseReportKind reporter = error ? ParseError : ParseStrictWarning;
             if (!js_AtomToPrintableString(cx, name, &bytes) ||
-                !(parser->*reporter)(pn, JSMSG_REDECLARED_VAR,
-                                     Definition::kindString(dn_kind), bytes.ptr()))
+                !parser->report(reporter, false, pn, JSMSG_REDECLARED_VAR,
+                                Definition::kindString(dn_kind), bytes.ptr()))
             {
                 return false;
             }
         }
     }
 
     LinkUseToDef(pn, dn);
     return true;
 }
 
-static bool
-MakeSetCall(JSContext *cx, ParseNode *pn, Parser *parser, unsigned msg)
+template <>
+/* static */ bool
+Parser<SyntaxParseHandler>::bindVarOrConst(JSContext *cx, BindData<SyntaxParseHandler> *data,
+                                           HandlePropertyName name,
+                                           Parser<SyntaxParseHandler> *parser)
+{
+    return true;
+}
+
+template <>
+bool
+Parser<FullParseHandler>::makeSetCall(ParseNode *pn, unsigned msg)
 {
     JS_ASSERT(pn->isArity(PN_LIST));
     JS_ASSERT(pn->isOp(JSOP_CALL) || pn->isOp(JSOP_EVAL) ||
               pn->isOp(JSOP_FUNCALL) || pn->isOp(JSOP_FUNAPPLY));
-    if (!parser->reportStrictModeError(pn, msg))
+    if (!report(ParseStrictError, pc->sc->strict, pn, msg))
         return false;
 
     ParseNode *pn2 = pn->pn_head;
     if (pn2->isKind(PNK_FUNCTION) && (pn2->pn_funbox->inGenexpLambda)) {
-        parser->reportError(pn, msg);
+        report(ParseError, false, pn, msg);
         return false;
     }
     pn->pn_xflags |= PNX_SETCALL;
     return true;
 }
 
-static void
-NoteLValue(ParseNode *pn)
+template <>
+bool
+Parser<FullParseHandler>::noteNameUse(ParseNode *pn)
 {
-    if (pn->isUsed())
-        pn->pn_lexdef->pn_dflags |= PND_ASSIGNED;
-
-    pn->pn_dflags |= PND_ASSIGNED;
-}
-
-static bool
-NoteNameUse(ParseNode *pn, Parser *parser)
-{
-    RootedPropertyName name(parser->context, pn->pn_atom->asPropertyName());
-    StmtInfoPC *stmt = LexicalLookup(parser->pc, name, NULL, (StmtInfoPC *)NULL);
-
-    DefinitionList::Range defs = parser->pc->decls().lookupMulti(name);
+    RootedPropertyName name(context, pn->pn_atom->asPropertyName());
+    StmtInfoPC *stmt = LexicalLookup(pc, name, NULL, (StmtInfoPC *)NULL);
+
+    DefinitionList::Range defs = pc->decls().lookupMulti(name);
 
     Definition *dn;
     if (!defs.empty()) {
         dn = defs.front();
     } else {
-        if (AtomDefnAddPtr p = parser->pc->lexdeps->lookupForAdd(name)) {
+        if (AtomDefnAddPtr p = pc->lexdeps->lookupForAdd(name)) {
             dn = p.value();
         } else {
             /*
              * No definition before this use in any lexical scope.
              * Create a placeholder definition node to either:
              * - Be adopted when we parse the real defining
              *   declaration, or
              * - Be left as a free variable definition if we never
              *   see the real definition.
              */
-            dn = MakePlaceholder(pn, parser, parser->pc);
-            if (!dn || !parser->pc->lexdeps->add(p, name, dn))
+            dn = MakePlaceholder(pn, &handler, pc);
+            if (!dn || !pc->lexdeps->add(p, name, dn))
                 return false;
         }
     }
 
     JS_ASSERT(dn->isDefn());
     LinkUseToDef(pn, dn);
 
     if (stmt && stmt->type == STMT_WITH)
         pn->pn_dflags |= PND_DEOPTIMIZED;
 
     return true;
 }
 
+template <>
+bool
+Parser<SyntaxParseHandler>::noteNameUse(Node pn)
+{
+    return true;
+}
+
 #if JS_HAS_DESTRUCTURING
 
-static bool
-BindDestructuringVar(JSContext *cx, BindData *data, ParseNode *pn, Parser *parser)
+template <>
+bool
+Parser<FullParseHandler>::bindDestructuringVar(BindData<FullParseHandler> *data, ParseNode *pn)
 {
     JS_ASSERT(pn->isKind(PNK_NAME));
 
-    RootedPropertyName name(cx, pn->pn_atom->asPropertyName());
+    RootedPropertyName name(context, pn->pn_atom->asPropertyName());
 
     data->pn = pn;
-    if (!data->binder(cx, data, name, parser))
+    if (!data->binder(context, data, name, this))
         return false;
 
     /*
      * Select the appropriate name-setting opcode, respecting eager selection
      * done by the data->binder function.
      */
     if (pn->pn_dflags & PND_BOUND)
         pn->setOp(JSOP_SETLOCAL);
     else if (data->op == JSOP_DEFCONST)
         pn->setOp(JSOP_SETCONST);
     else
         pn->setOp(JSOP_SETNAME);
 
     if (data->op == JSOP_DEFCONST)
         pn->pn_dflags |= PND_CONST;
 
-    NoteLValue(pn);
+    handler.noteLValue(pn);
     return true;
 }
 
 /*
  * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
  * LHS expression except a destructuring initialiser, and R is on the stack.
  * Because R is already evaluated, the usual LHS-specialized bytecodes won't
  * work.  After pushing R[P] we need to evaluate Q's "reference base" QB and
@@ -2413,42 +2588,43 @@ BindDestructuringVar(JSContext *cx, Bind
  * its operands with left-hand side above right-hand side:
  *
  *   [rval, lval, xval]
  *
  * and pops all three values, setting lval[xval] = rval.  But we cannot select
  * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
  * which can be optimized further.  So we select JSOP_SETNAME.
  */
-static bool
-BindDestructuringLHS(JSContext *cx, ParseNode *pn, Parser *parser)
+template <>
+bool
+Parser<FullParseHandler>::bindDestructuringLHS(ParseNode *pn)
 {
     switch (pn->getKind()) {
       case PNK_NAME:
-        NoteLValue(pn);
+        handler.noteLValue(pn);
         /* FALL THROUGH */
 
       case PNK_DOT:
       case PNK_ELEM:
         /*
          * We may be called on a name node that has already been specialized,
          * in the very weird and ECMA-262-required "for (var [x] = i in o) ..."
          * case. See bug 558633.
          */
         if (!(js_CodeSpec[pn->getOp()].format & JOF_SET))
             pn->setOp(JSOP_SETNAME);
         break;
 
       case PNK_CALL:
-        if (!MakeSetCall(cx, pn, parser, JSMSG_BAD_LEFTSIDE_OF_ASS))
+        if (!makeSetCall(pn, JSMSG_BAD_LEFTSIDE_OF_ASS))
             return false;
         break;
 
       default:
-        parser->reportError(pn, JSMSG_BAD_LEFTSIDE_OF_ASS);
+        report(ParseError, false, pn, JSMSG_BAD_LEFTSIDE_OF_ASS);
         return false;
     }
 
     return true;
 }
 
 /*
  * Destructuring patterns can appear in two kinds of contexts:
@@ -2484,77 +2660,78 @@ BindDestructuringLHS(JSContext *cx, Pars
  * CheckDestructuring, we require the pattern's property value
  * positions to be simple names, and define them as appropriate to the
  * context.  For these calls, |data| points to the right sort of
  * BindData.
  *
  * The 'toplevel' is a private detail of the recursive strategy used by
  * CheckDestructuring and callers should use the default value.
  */
-static bool
-CheckDestructuring(JSContext *cx, BindData *data, ParseNode *left, Parser *parser,
-                   bool toplevel = true)
+template <>
+bool
+Parser<FullParseHandler>::checkDestructuring(BindData<FullParseHandler> *data,
+                                             ParseNode *left, bool toplevel)
 {
     bool ok;
 
     if (left->isKind(PNK_ARRAYCOMP)) {
-        parser->reportError(left, JSMSG_ARRAY_COMP_LEFTSIDE);
+        report(ParseError, false, left, JSMSG_ARRAY_COMP_LEFTSIDE);
         return false;
     }
 
-    Rooted<StaticBlockObject *> blockObj(cx);
-    blockObj = data && data->binder == BindLet ? data->let.blockObj.get() : NULL;
+    Rooted<StaticBlockObject *> blockObj(context);
+    blockObj = data && data->binder == bindLet ? data->let.blockObj.get() : NULL;
     uint32_t blockCountBefore = blockObj ? blockObj->slotCount() : 0;
 
     if (left->isKind(PNK_ARRAY)) {
         for (ParseNode *pn = left->pn_head; pn; pn = pn->pn_next) {
             /* Nullary comma is an elision; binary comma is an expression.*/
             if (!pn->isArrayHole()) {
                 if (pn->isKind(PNK_ARRAY) || pn->isKind(PNK_OBJECT)) {
-                    ok = CheckDestructuring(cx, data, pn, parser, false);
+                    ok = checkDestructuring(data, pn, false);
                 } else {
                     if (data) {
                         if (!pn->isKind(PNK_NAME)) {
-                            parser->reportError(pn, JSMSG_NO_VARIABLE_NAME);
+                            report(ParseError, false, pn, JSMSG_NO_VARIABLE_NAME);
                             return false;
                         }
-                        ok = BindDestructuringVar(cx, data, pn, parser);
+                        ok = bindDestructuringVar(data, pn);
                     } else {
-                        ok = BindDestructuringLHS(cx, pn, parser);
+                        ok = bindDestructuringLHS(pn);
                     }
                 }
                 if (!ok)
                     return false;
             }
         }
     } else {
         JS_ASSERT(left->isKind(PNK_OBJECT));
         for (ParseNode *pair = left->pn_head; pair; pair = pair->pn_next) {
             JS_ASSERT(pair->isKind(PNK_COLON));
             ParseNode *pn = pair->pn_right;
 
             if (pn->isKind(PNK_ARRAY) || pn->isKind(PNK_OBJECT)) {
-                ok = CheckDestructuring(cx, data, pn, parser, false);
+                ok = checkDestructuring(data, pn, false);
             } else if (data) {
                 if (!pn->isKind(PNK_NAME)) {
-                    parser->reportError(pn, JSMSG_NO_VARIABLE_NAME);
+                    report(ParseError, false, pn, JSMSG_NO_VARIABLE_NAME);
                     return false;
                 }
-                ok = BindDestructuringVar(cx, data, pn, parser);
+                ok = bindDestructuringVar(data, pn);
             } else {
                 /*
                  * If right and left point to the same node, then this is
                  * destructuring shorthand ({x} = ...). In that case,
                  * identifierName was not used to parse 'x' so 'x' has not been
                  * officially linked to its def or registered in lexdeps. Do
                  * that now.
                  */
-                if (pair->pn_right == pair->pn_left && !NoteNameUse(pn, parser))
+                if (pair->pn_right == pair->pn_left && !noteNameUse(pn))
                     return false;
-                ok = BindDestructuringLHS(cx, pn, parser);
+                ok = bindDestructuringLHS(pn);
             }
             if (!ok)
                 return false;
         }
     }
 
     /*
      * The catch/finally handler implementation in the interpreter assumes
@@ -2576,56 +2753,70 @@ CheckDestructuring(JSContext *cx, BindDa
      * four slots are needed.
      *
      * To satisfy both constraints, we push a dummy slot (and add a
      * corresponding dummy property to the block object) for each initializer
      * that doesn't introduce at least one binding.
      */
     if (toplevel && blockObj && blockCountBefore == blockObj->slotCount()) {
         bool redeclared;
-        RootedId id(cx, INT_TO_JSID(blockCountBefore));
-        if (!StaticBlockObject::addVar(cx, blockObj, id, blockCountBefore, &redeclared))
+        RootedId id(context, INT_TO_JSID(blockCountBefore));
+        if (!StaticBlockObject::addVar(context, blockObj, id, blockCountBefore, &redeclared))
             return false;
         JS_ASSERT(!redeclared);
         JS_ASSERT(blockObj->slotCount() == blockCountBefore + 1);
     }
 
     return true;
 }
 
-ParseNode *
-Parser::destructuringExpr(BindData *data, TokenKind tt)
+template <>
+bool
+Parser<SyntaxParseHandler>::checkDestructuring(BindData<SyntaxParseHandler> *data,
+                                               Node left, bool toplevel)
+{
+    setUnknownResult();
+    return false;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::destructuringExpr(BindData<ParseHandler> *data, TokenKind tt)
 {
     JS_ASSERT(tokenStream.isCurrentTokenType(tt));
 
     pc->inDeclDestructuring = true;
-    ParseNode *pn = primaryExpr(tt);
+    Node pn = primaryExpr(tt);
     pc->inDeclDestructuring = false;
     if (!pn)
-        return NULL;
-    if (!CheckDestructuring(context, data, pn, this))
-        return NULL;
+        return null();
+    if (!checkDestructuring(data, pn))
+        return null();
     return pn;
 }
 
 #endif /* JS_HAS_DESTRUCTURING */
 
-ParseNode *
-Parser::returnOrYield(bool useAssignExpr)
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::returnOrYield(bool useAssignExpr)
 {
     TokenKind tt = tokenStream.currentToken().type;
     if (!pc->sc->isFunctionBox()) {
-        reportError(NULL, JSMSG_BAD_RETURN_OR_YIELD,
-                    (tt == TOK_RETURN) ? js_return_str : js_yield_str);
-        return NULL;
+        report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD,
+               (tt == TOK_RETURN) ? js_return_str : js_yield_str);
+        return null();
     }
 
-    ParseNode *pn = UnaryNode::create((tt == TOK_RETURN) ? PNK_RETURN : PNK_YIELD, this);
+    ParseNodeKind kind = (tt == TOK_RETURN) ? PNK_RETURN : PNK_YIELD;
+    JSOp op = (tt == TOK_RETURN) ? JSOP_RETURN : JSOP_YIELD;
+
+    Node pn = handler.newUnary(kind, op);
     if (!pn)
-        return NULL;
+        return null();
 
 #if JS_HAS_GENERATORS
     if (tt == TOK_YIELD) {
         /*
          * If we're within parens, we won't know if this is a generator expression until we see
          * a |for| token, so we have to delay flagging the current function.
          */
         if (pc->parenDepth == 0) {
@@ -2635,394 +2826,414 @@ Parser::returnOrYield(bool useAssignExpr
             pc->yieldNode = pn;
         }
     }
 #endif
 
     /* This is ugly, but we don't want to require a semicolon. */
     TokenKind tt2 = tokenStream.peekTokenSameLine(TSF_OPERAND);
     if (tt2 == TOK_ERROR)
-        return NULL;
+        return null();
 
     if (tt2 != TOK_EOF && tt2 != TOK_EOL && tt2 != TOK_SEMI && tt2 != TOK_RC
 #if JS_HAS_GENERATORS
         && (tt != TOK_YIELD ||
             (tt2 != tt && tt2 != TOK_RB && tt2 != TOK_RP &&
              tt2 != TOK_COLON && tt2 != TOK_COMMA))
 #endif
         )
     {
-        ParseNode *pn2 = useAssignExpr ? assignExpr() : expr();
+        Node pn2 = useAssignExpr ? assignExpr() : expr();
         if (!pn2)
-            return NULL;
+            return null();
 #if JS_HAS_GENERATORS
         if (tt == TOK_RETURN)
 #endif
             pc->funHasReturnExpr = true;
-        pn->pn_pos.end = pn2->pn_pos.end;
-        pn->pn_kid = pn2;
+        handler.setUnaryKid(pn, pn2);
     } else {
 #if JS_HAS_GENERATORS
         if (tt == TOK_RETURN)
 #endif
             pc->funHasReturnVoid = true;
     }
 
     if (pc->funHasReturnExpr && pc->sc->asFunctionBox()->isGenerator()) {
         /* As in Python (see PEP-255), disallow return v; in generators. */
-        ReportBadReturn(context, this, pn, &Parser::reportError, JSMSG_BAD_GENERATOR_RETURN,
+        reportBadReturn(pn, ParseError, JSMSG_BAD_GENERATOR_RETURN,
                         JSMSG_BAD_ANON_GENERATOR_RETURN);
-        return NULL;
+        return null();
     }
 
     if (context->hasStrictOption() && pc->funHasReturnExpr && pc->funHasReturnVoid &&
-        !ReportBadReturn(context, this, pn, &Parser::reportStrictWarning,
+        !reportBadReturn(pn, ParseStrictWarning,
                          JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE))
     {
-        return NULL;
+        return null();
     }
 
     return pn;
 }
 
-static ParseNode *
-PushLexicalScope(JSContext *cx, Parser *parser, HandleStaticBlockObject blockObj, StmtInfoPC *stmt)
+template <>
+ParseNode *
+Parser<FullParseHandler>::pushLexicalScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt)
 {
     JS_ASSERT(blockObj);
-
-    ParseNode *pn = LexicalScopeNode::create(PNK_LEXICALSCOPE, parser);
+    ParseNode *pn = LexicalScopeNode::create(PNK_LEXICALSCOPE, &handler);
     if (!pn)
-        return NULL;
-
-    ObjectBox *blockbox = parser->newObjectBox(blockObj);
+        return null();
+
+    ObjectBox *blockbox = newObjectBox(blockObj);
     if (!blockbox)
-        return NULL;
-
-    ParseContext *pc = parser->pc;
+        return null();
 
     PushStatementPC(pc, stmt, STMT_BLOCK);
     blockObj->initPrevBlockChainFromParser(pc->blockChain);
     FinishPushBlockScope(pc, stmt, *blockObj.get());
 
     pn->setOp(JSOP_LEAVEBLOCK);
     pn->pn_objbox = blockbox;
     pn->pn_cookie.makeFree();
     pn->pn_dflags = 0;
     if (!GenerateBlockId(pc, stmt->blockid))
-        return NULL;
+        return null();
     pn->pn_blockid = stmt->blockid;
     return pn;
 }
 
-static ParseNode *
-PushLexicalScope(JSContext *cx, Parser *parser, StmtInfoPC *stmt)
+template <>
+SyntaxParseHandler::Node
+Parser<SyntaxParseHandler>::pushLexicalScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt)
 {
-    RootedStaticBlockObject blockObj(cx, StaticBlockObject::create(cx));
+    setUnknownResult();
+    return SyntaxParseHandler::NodeFailure;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::pushLexicalScope(StmtInfoPC *stmt)
+{
+    RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context));
     if (!blockObj)
-        return NULL;
-
-    return PushLexicalScope(cx, parser, blockObj, stmt);
+        return null();
+
+    return pushLexicalScope(blockObj, stmt);
 }
 
 #if JS_HAS_BLOCK_SCOPE
 
 struct AddLetDecl
 {
     uint32_t blockid;
 
     AddLetDecl(uint32_t blockid) : blockid(blockid) {}
 
-    bool operator()(JSContext *cx, ParseContext *pc, HandleStaticBlockObject blockObj,
-                    const Shape &shape, JSAtom *)
-        {
+    bool operator()(JSContext *cx, ParseContext<FullParseHandler> *pc,
+                    HandleStaticBlockObject blockObj, const Shape &shape, JSAtom *)
+    {
         ParseNode *def = (ParseNode *) blockObj->getSlot(shape.slot()).toPrivate();
         def->pn_blockid = blockid;
         RootedPropertyName name(cx, def->name());
         return pc->define(cx, name, def, Definition::LET);
     }
 };
 
-static ParseNode *
-PushLetScope(JSContext *cx, Parser *parser, HandleStaticBlockObject blockObj, StmtInfoPC *stmt)
+template <>
+ParseNode *
+Parser<FullParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt)
 {
     JS_ASSERT(blockObj);
-    ParseNode *pn = PushLexicalScope(cx, parser, blockObj, stmt);
+    ParseNode *pn = pushLexicalScope(blockObj, stmt);
     if (!pn)
-        return NULL;
+        return null();
 
     /* Tell codegen to emit JSOP_ENTERLETx (not JSOP_ENTERBLOCK). */
     pn->pn_dflags |= PND_LET;
 
     /* Populate the new scope with decls found in the head with updated blockid. */
-    if (!ForEachLetDef(cx, parser->pc, blockObj, AddLetDecl(stmt->blockid)))
-        return NULL;
+    if (!ForEachLetDef(context, pc, blockObj, AddLetDecl(stmt->blockid)))
+        return null();
 
     return pn;
 }
 
+template <>
+SyntaxParseHandler::Node
+Parser<SyntaxParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt)
+{
+    setUnknownResult();
+    return SyntaxParseHandler::NodeFailure;
+}
+
 /*
  * Parse a let block statement or let expression (determined by 'letContext').
  * In both cases, bindings are not hoisted to the top of the enclosing block
  * and thus must be carefully injected between variables() and the let body.
  */
-ParseNode *
-Parser::letBlock(LetContext letContext)
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::letBlock(LetContext letContext)
 {
     JS_ASSERT(tokenStream.currentToken().type == TOK_LET);
 
-    ParseNode *pnlet = BinaryNode::create(PNK_LET, this);
-    if (!pnlet)
-        return NULL;
-
     RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context));
     if (!blockObj)
-        return NULL;
+        return null();
+
+    TokenPtr begin = tokenStream.currentToken().pos.begin;
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET);
 
-    ParseNode *vars = variables(PNK_LET, blockObj, DontHoistVars);
+    Node vars = variables(PNK_LET, blockObj, DontHoistVars);
     if (!vars)
-        return NULL;
+        return null();
 
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET);
 
     StmtInfoPC stmtInfo(context);
-    ParseNode *block = PushLetScope(context, this, blockObj, &stmtInfo);
+    Node block = pushLetScope(blockObj, &stmtInfo);
     if (!block)
-        return NULL;
-
-    pnlet->pn_left = vars;
-    pnlet->pn_right = block;
-
-    ParseNode *ret;
+        return null();
+
+    Node pnlet = handler.newBinary(PNK_LET, vars, block);
+    if (!pnlet)
+        return null();
+    handler.setBeginPosition(pnlet, begin);
+
+    Node ret;
     if (letContext == LetStatement && !tokenStream.matchToken(TOK_LC, TSF_OPERAND)) {
         /*
          * Strict mode eliminates a grammar ambiguity with unparenthesized
          * LetExpressions in an ExpressionStatement. If followed immediately
          * by an arguments list, it's ambiguous whether the let expression
          * is the callee or the call is inside the let expression body.
          *
          * See bug 569464.
          */
-        if (!reportStrictModeError(pnlet, JSMSG_STRICT_CODE_LET_EXPR_STMT))
-            return NULL;
+        if (!report(ParseStrictError, pc->sc->strict, pnlet,
+                    JSMSG_STRICT_CODE_LET_EXPR_STMT))
+        {
+            return null();
+        }
 
         /*
          * If this is really an expression in let statement guise, then we
          * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
          * the return value of the expression.
          */
-        ParseNode *semi = UnaryNode::create(PNK_SEMI, this);
-        if (!semi)
-            return NULL;
-
-        semi->pn_kid = pnlet;
-        semi->pn_pos = pnlet->pn_pos;
+        Node semi = handler.newUnary(PNK_SEMI, pnlet);
 
         letContext = LetExpresion;
         ret = semi;
     } else {
         ret = pnlet;
     }
 
+    Node expr;
     if (letContext == LetStatement) {
-        JS_ASSERT(block->getOp() == JSOP_LEAVEBLOCK);
-        block->pn_expr = statements();
-        if (!block->pn_expr)
-            return NULL;
+        expr = statements();
+        if (!expr)
+            return null();
         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
     } else {
         JS_ASSERT(letContext == LetExpresion);
-        block->setOp(JSOP_LEAVEBLOCKEXPR);
-        block->pn_expr = assignExpr();
-        if (!block->pn_expr)
-            return NULL;
+        expr = assignExpr();
+        if (!expr)
+            return null();
     }
-
-    ret->pn_pos.begin = pnlet->pn_pos.begin = pnlet->pn_left->pn_pos.begin;
-    ret->pn_pos.end = pnlet->pn_pos.end = pnlet->pn_right->pn_pos.end;
+    handler.setLeaveBlockResult(block, expr, letContext != LetStatement);
+
+    handler.setBeginPosition(ret, vars);
+    handler.setEndPosition(ret, expr);
+
+    handler.setBeginPosition(pnlet, vars);
+    handler.setEndPosition(pnlet, expr);
 
     PopStatementPC(context, pc);
     return ret;
 }
 
 #endif /* JS_HAS_BLOCK_SCOPE */
 
+template <typename ParseHandler>
 static bool
-PushBlocklikeStatement(StmtInfoPC *stmt, StmtType type, ParseContext *pc)
+PushBlocklikeStatement(StmtInfoPC *stmt, StmtType type, ParseContext<ParseHandler> *pc)
 {
     PushStatementPC(pc, stmt, type);
     return GenerateBlockId(pc, stmt->blockid);
 }
 
-static ParseNode *
-NewBindingNode(JSAtom *atom, Parser *parser, VarContext varContext = HoistVars)
+template <>
+ParseNode *
+Parser<FullParseHandler>::newBindingNode(PropertyName *name, VarContext varContext)
 {
-    ParseContext *pc = parser->pc;
-
     /*
      * If this name is being injected into an existing block/function, see if
      * it has already been declared or if it resolves an outstanding lexdep.
      * Otherwise, this is a let block/expr that introduces a new scope and thus
      * shadows existing decls and doesn't resolve existing lexdeps. Duplicate
-     * names are caught by BindLet.
+     * names are caught by bindLet.
      */
     if (varContext == HoistVars) {
-        if (AtomDefnPtr p = pc->lexdeps->lookup(atom)) {
+        if (AtomDefnPtr p = pc->lexdeps->lookup(name)) {
             ParseNode *lexdep = p.value();
             JS_ASSERT(lexdep->isPlaceholder());
             if (lexdep->pn_blockid >= pc->blockid()) {
                 lexdep->pn_blockid = pc->blockid();
                 pc->lexdeps->remove(p);
-                lexdep->pn_pos = parser->tokenStream.currentToken().pos;
+                lexdep->pn_pos = tokenStream.currentToken().pos;
                 return lexdep;
             }
         }
     }
 
     /* Make a new node for this declarator name (or destructuring pattern). */
-    JS_ASSERT(parser->tokenStream.currentToken().type == TOK_NAME);
-    return NameNode::create(PNK_NAME, atom, parser, parser->pc);
+    JS_ASSERT(tokenStream.currentToken().type == TOK_NAME);
+    return handler.newName(name, pc);
 }
 
-ParseNode *
-Parser::switchStatement()
+template <>
+SyntaxParseHandler::Node
+Parser<SyntaxParseHandler>::newBindingNode(PropertyName *name, VarContext varContext)
+{
+    setUnknownResult();
+    return SyntaxParseHandler::NodeFailure;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::switchStatement()
 {
     JS_ASSERT(tokenStream.currentToken().type == TOK_SWITCH);
-    ParseNode *pn = BinaryNode::create(PNK_SWITCH, this);
-    if (!pn)
-        return NULL;
+
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
 
-    /* pn1 points to the switch's discriminant. */
-    ParseNode *pn1 = parenExpr();
-    if (!pn1)
-        return NULL;
+    Node discriminant = parenExpr();
+    if (!discriminant)
+        return null();
 
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
 
-    /*
-     * NB: we must push stmtInfo before calling GenerateBlockIdForStmtNode
-     * because that function states pc->topStmt->blockid.
-     */
     StmtInfoPC stmtInfo(context);
     PushStatementPC(pc, &stmtInfo, STMT_SWITCH);
 
-    /* pn2 is a list of case nodes. The default case has pn_left == NULL */
-    ParseNode *pn2 = ListNode::create(PNK_STATEMENTLIST, this);
-    if (!pn2)
-        return NULL;
-    pn2->makeEmpty();
-    if (!GenerateBlockIdForStmtNode(pn2, pc))
-        return NULL;
-    ParseNode *saveBlock = pc->blockNode;
-    pc->blockNode = pn2;
+    if (!GenerateBlockId(pc, pc->topStmt->blockid))
+        return null();
+
+    /* The default case has pn_left == NULL */
+    Node caseList = handler.newList(PNK_STATEMENTLIST);
+    if (!caseList)
+        return null();
+    handler.setBlockId(caseList, pc->blockid());
+
+    Node saveBlock = pc->blockNode;
+    pc->blockNode = caseList;
 
     bool seenDefault = false;
     TokenKind tt;
     while ((tt = tokenStream.getToken()) != TOK_RC) {
-        ParseNode *pn3;
+        Node casepn;
         switch (tt) {
           case TOK_DEFAULT:
             if (seenDefault) {
-                reportError(NULL, JSMSG_TOO_MANY_DEFAULTS);
-                return NULL;
+                report(ParseError, false, null(), JSMSG_TOO_MANY_DEFAULTS);
+                return null();
             }
             seenDefault = true;
-            pn3 = BinaryNode::create(PNK_DEFAULT, this);
-            if (!pn3)
-                return NULL;
+            casepn = handler.newBinary(PNK_DEFAULT);
+            if (!casepn)
+                return null();
             break;
 
           case TOK_CASE:
           {
-            pn3 = BinaryNode::create(PNK_CASE, this);
-            if (!pn3)
-                return NULL;
-            pn3->pn_left = expr();
-            if (!pn3->pn_left)
-                return NULL;
+            Node left = expr();
+            if (!left)
+                return null();
+            casepn = handler.newBinary(PNK_CASE, left);
+            if (!casepn)
+                return null();
             break;
           }
 
           case TOK_ERROR:
-            return NULL;
+            return null();
 
           default:
-            reportError(NULL, JSMSG_BAD_SWITCH);
-            return NULL;
+            report(ParseError, false, null(), JSMSG_BAD_SWITCH);
+            return null();
         }
 
-        pn2->append(pn3);
-        if (pn2->pn_count == JS_BIT(16)) {
-            reportError(NULL, JSMSG_TOO_MANY_CASES);
-            return NULL;
-        }
+        handler.addList(caseList, casepn);
 
         MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
 
-        ParseNode *pn4 = ListNode::create(PNK_STATEMENTLIST, this);
-        if (!pn4)
-            return NULL;
-        pn4->makeEmpty();
+        Node body = handler.newList(PNK_STATEMENTLIST);
+        if (!body)
+            return null();
+        handler.setBlockId(body, pc->blockid());
+
         while ((tt = tokenStream.peekToken(TSF_OPERAND)) != TOK_RC &&
                tt != TOK_CASE && tt != TOK_DEFAULT) {
             if (tt == TOK_ERROR)
-                return NULL;
-            ParseNode *pn5 = statement();
-            if (!pn5)
-                return NULL;
-            pn4->pn_pos.end = pn5->pn_pos.end;
-            pn4->append(pn5);
+                return null();
+            Node stmt = statement();
+            if (!stmt)
+                return null();
+            handler.addList(body, stmt);
         }
 
-        /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
-        if (pn4->pn_head)
-            pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin;
-        pn3->pn_pos.end = pn4->pn_pos.end;
-        pn3->pn_right = pn4;
+        handler.setBinaryRHS(casepn, body);
     }
 
     /*
      * Handle the case where there was a let declaration in any case in
      * the switch body, but not within an inner block.  If it replaced
-     * pc->blockNode with a new block node then we must refresh pn2 and
+     * pc->blockNode with a new block node then we must refresh caseList and
      * then restore pc->blockNode.
      */
-    if (pc->blockNode != pn2)
-        pn2 = pc->blockNode;
+    if (pc->blockNode != caseList)
+        caseList = pc->blockNode;
     pc->blockNode = saveBlock;
+
     PopStatementPC(context, pc);
 
-    pn->pn_pos.end = pn2->pn_pos.end = tokenStream.currentToken().pos.end;
-    pn->pn_left = pn1;
-    pn->pn_right = pn2;
+    Node pn = handler.newBinary(PNK_SWITCH, discriminant, caseList);
+    if (!pn)
+        return null();
+
+    handler.setEndPosition(pn, tokenStream.currentToken().pos.end);
+    handler.setEndPosition(caseList, tokenStream.currentToken().pos.end);
     return pn;
 }
 
+template <typename ParseHandler>
 bool
-Parser::matchInOrOf(bool *isForOfp)
+Parser<ParseHandler>::matchInOrOf(bool *isForOfp)
 {
     if (tokenStream.matchToken(TOK_IN)) {
         *isForOfp = false;
         return true;
     }
     if (tokenStream.matchToken(TOK_NAME)) {
         if (tokenStream.currentToken().name() == context->names().of) {
             *isForOfp = true;
             return true;
         }
         tokenStream.ungetToken();
     }
     return false;
 }
 
-static bool
-IsValidForStatementLHS(ParseNode *pn1, JSVersion version, bool forDecl, bool forEach, bool forOf)
+template <>
+bool
+Parser<FullParseHandler>::isValidForStatementLHS(ParseNode *pn1, JSVersion version,
+                                                 bool forDecl, bool forEach, bool forOf)
 {
     if (forDecl) {
         if (pn1->pn_count > 1)
             return false;
         if (pn1->isOp(JSOP_DEFCONST))
             return false;
 #if JS_HAS_DESTRUCTURING
         // In JS 1.7 only, for (var [K, V] in EXPR) has a special meaning.
@@ -3058,31 +3269,33 @@ IsValidForStatementLHS(ParseNode *pn1, J
         return true;
 #endif
 
       default:
         return false;
     }
 }
 
+template <>
 ParseNode *
-Parser::forStatement()
+Parser<FullParseHandler>::forStatement()
 {
     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
 
-    /* A FOR node is binary, left is loop control and right is the body. */
-    ParseNode *pn = BinaryNode::create(PNK_FOR, this);
-    if (!pn)
-        return NULL;
-
     StmtInfoPC forStmt(context);
     PushStatementPC(pc, &forStmt, STMT_FOR_LOOP);
 
+    /* A FOR node is binary, left is loop control and right is the body. */
+    ParseNode *pn = BinaryNode::create(PNK_FOR, &handler);
+    if (!pn)
+        return null();
+
     pn->setOp(JSOP_ITER);
     pn->pn_iflags = 0;
+
     if (allowsForEachIn() && tokenStream.matchToken(TOK_NAME)) {
         if (tokenStream.currentToken().name() == context->names().each)
             pn->pn_iflags = JSITER_FOREACH;
         else
             tokenStream.ungetToken();
     }
 
     TokenPos lp_pos = tokenStream.currentToken().pos;
@@ -3099,18 +3312,18 @@ Parser::forStatement()
 
     /* Set to 'x' in 'for (x ;... ;...)' or 'for (x in ...)'. */
     ParseNode *pn1;
 
     {
         TokenKind tt = tokenStream.peekToken(TSF_OPERAND);
         if (tt == TOK_SEMI) {
             if (pn->pn_iflags & JSITER_FOREACH) {
-                reportError(pn, JSMSG_BAD_FOR_EACH_LOOP);
-                return NULL;
+                report(ParseError, false, null(), JSMSG_BAD_FOR_EACH_LOOP);
+                return null();
             }
 
             pn1 = NULL;
         } else {
             /*
              * Set pn1 to a var list or an initializing expression.
              *
              * Set the parsingForInit flag during parsing of the first clause
@@ -3133,27 +3346,27 @@ Parser::forStatement()
             else if (tt == TOK_LET) {
                 (void) tokenStream.getToken();
                 if (tokenStream.peekToken() == TOK_LP) {
                     pn1 = letBlock(LetExpresion);
                 } else {
                     forDecl = true;
                     blockObj = StaticBlockObject::create(context);
                     if (!blockObj)
-                        return NULL;
+                        return null();
                     pn1 = variables(PNK_LET, blockObj, DontHoistVars);
                 }
             }
 #endif
             else {
                 pn1 = expr();
             }
             pc->parsingForInit = false;
             if (!pn1)
-                return NULL;
+                return null();
         }
     }
 
     JS_ASSERT_IF(forDecl, pn1->isArity(PN_LIST));
     JS_ASSERT(!!blockObj == (forDecl && pn1->isOp(JSOP_NOP)));
 
     const TokenPos pos = tokenStream.currentToken().pos;
 
@@ -3179,26 +3392,26 @@ Parser::forStatement()
          * receives the enumeration value each iteration, and pn3 is the rhs of
          * 'in'.
          */
         forStmt.type = STMT_FOR_IN_LOOP;
 
         /* Set pn_iflags and rule out invalid combinations. */
         if (forOf && pn->pn_iflags != 0) {
             JS_ASSERT(pn->pn_iflags == JSITER_FOREACH);
-            reportError(NULL, JSMSG_BAD_FOR_EACH_LOOP);
-            return NULL;
+            report(ParseError, false, null(), JSMSG_BAD_FOR_EACH_LOOP);
+            return null();
         }
         pn->pn_iflags |= (forOf ? JSITER_FOR_OF : JSITER_ENUMERATE);
 
         /* Check that the left side of the 'in' or 'of' is valid. */
         bool forEach = bool(pn->pn_iflags & JSITER_FOREACH);
-        if (!IsValidForStatementLHS(pn1, versionNumber(), forDecl, forEach, forOf)) {
-            reportError(pn1, JSMSG_BAD_FOR_LEFTSIDE);
-            return NULL;
+        if (!isValidForStatementLHS(pn1, versionNumber(), forDecl, forEach, forOf)) {
+            report(ParseError, false, pn1, JSMSG_BAD_FOR_LEFTSIDE);
+            return null();
         }
 
         /*
          * After the following if-else, pn2 will point to the name or
          * destructuring pattern on in's left. pn1 will point to the decl, if
          * any, else NULL. Note that the "declaration with initializer" case
          * rewrites the loop-head, moving the decl and setting pn1 to NULL.
          */
@@ -3218,36 +3431,35 @@ Parser::forStatement()
                  * Declaration with initializer.
                  *
                  * Rewrite 'for (<decl> x = i in o)' where <decl> is 'var' or
                  * 'const' to hoist the initializer or the entire decl out of
                  * the loop head.
                  */
 #if JS_HAS_BLOCK_SCOPE
                 if (blockObj) {
-                    reportError(pn2, JSMSG_INVALID_FOR_IN_INIT);
-                    return NULL;
+                    report(ParseError, false, pn2, JSMSG_INVALID_FOR_IN_INIT);
+                    return null();
                 }
 #endif /* JS_HAS_BLOCK_SCOPE */
 
-                ParseNode *pnseq = ListNode::create(PNK_SEQ, this);
+                ParseNode *pnseq = handler.newList(PNK_SEQ, pn1);
                 if (!pnseq)
-                    return NULL;
+                    return null();
 
                 /*
                  * All of 'var x = i' is hoisted above 'for (x in o)',
                  * so clear PNX_FORINVAR.
                  *
                  * Request JSOP_POP here since the var is for a simple
                  * name (it is not a destructuring binding's left-hand
                  * side) and it has an initializer.
                  */
                 pn1->pn_xflags &= ~PNX_FORINVAR;
                 pn1->pn_xflags |= PNX_POPVAR;
-                pnseq->initList(pn1);
                 pn1 = NULL;
 
 #if JS_HAS_DESTRUCTURING
                 if (pn2->isKind(PNK_ASSIGN)) {
                     pn2 = pn2->pn_left;
                     JS_ASSERT(pn2->isKind(PNK_ARRAY) || pn2->isKind(PNK_OBJECT) ||
                               pn2->isKind(PNK_NAME));
                 }
@@ -3258,53 +3470,53 @@ Parser::forStatement()
             }
         } else {
             /* Not a declaration. */
             JS_ASSERT(!blockObj);
             pn2 = pn1;
             pn1 = NULL;
 
             if (!setAssignmentLhsOps(pn2, JSOP_NOP))
-                return NULL;
+                return null();
         }
 
         pn3 = expr();
         if (!pn3)
-            return NULL;
+            return null();
 
         if (blockObj) {
             /*
              * Now that the pn3 has been parsed, push the let scope. To hold
              * the blockObj for the emitter, wrap the TOK_LEXICALSCOPE node
              * created by PushLetScope around the for's initializer. This also
              * serves to indicate the let-decl to the emitter.
              */
-            ParseNode *block = PushLetScope(context, this, blockObj, &letStmt);
+            ParseNode *block = pushLetScope(blockObj, &letStmt);
             if (!block)
-                return NULL;
+                return null();
             letStmt.isForLetBlock = true;
             block->pn_expr = pn1;
             block->pn_pos = pn1->pn_pos;
             pn1 = block;
         }
 
         if (forDecl) {
             /*
              * pn2 is part of a declaration. Make a copy that can be passed to
              * EmitAssignment. Take care to do this after PushLetScope.
              */
-            pn2 = CloneLeftHandSide(pn2, this);
+            pn2 = cloneLeftHandSide(pn2);
             if (!pn2)
-                return NULL;
+                return null();
         }
 
         switch (pn2->getKind()) {
           case PNK_NAME:
             /* Beware 'for (arguments in ...)' with or without a 'var'. */
-            NoteLValue(pn2);
+            handler.noteLValue(pn2);
             break;
 
 #if JS_HAS_DESTRUCTURING
           case PNK_ASSIGN:
             JS_NOT_REACHED("forStatement TOK_ASSIGN");
             break;
 
           case PNK_ARRAY:
@@ -3319,85 +3531,85 @@ Parser::forStatement()
                     pn->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
             }
             break;
 #endif
 
           default:;
         }
 
-        forHead = TernaryNode::create(PNK_FORIN, this);
+        forHead = TernaryNode::create(PNK_FORIN, &handler);
         if (!forHead)
-            return NULL;
+            return null();
     } else {
         if (blockObj) {
             /*
              * Desugar 'for (let A; B; C) D' into 'let (A) { for (; B; C) D }'
              * to induce the correct scoping for A.
              */
-            ParseNode *block = PushLetScope(context, this, blockObj, &letStmt);
+            ParseNode *block = pushLetScope(blockObj, &letStmt);
             if (!block)
-                return NULL;
+                return null();
             letStmt.isForLetBlock = true;
 
-            ParseNode *let = new_<BinaryNode>(PNK_LET, JSOP_NOP, pos, pn1, block);
+            ParseNode *let = handler.newBinary(PNK_LET, pn1, block);
             if (!let)
-                return NULL;
+                return null();
 
             pn1 = NULL;
             block->pn_expr = pn;
             forParent = let;
         }
 
         if (pn->pn_iflags & JSITER_FOREACH) {
-            reportError(pn, JSMSG_BAD_FOR_EACH_LOOP);
-            return NULL;
+            report(ParseError, false, pn, JSMSG_BAD_FOR_EACH_LOOP);
+            return null();
         }
         pn->setOp(JSOP_NOP);
 
         /* Parse the loop condition or null into pn2. */
         MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
         if (tokenStream.peekToken(TSF_OPERAND) == TOK_SEMI) {
             pn2 = NULL;
         } else {
             pn2 = expr();
             if (!pn2)
-                return NULL;
+                return null();
         }
 
         /* Parse the update expression or null into pn3. */
         MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
         if (tokenStream.peekToken(TSF_OPERAND) == TOK_RP) {
             pn3 = NULL;
         } else {
             pn3 = expr();
             if (!pn3)
-                return NULL;
+                return null();
         }
 
-        forHead = TernaryNode::create(PNK_FORHEAD, this);
+        forHead = TernaryNode::create(PNK_FORHEAD, &handler);
         if (!forHead)
-            return NULL;
+            return null();
     }
 
     forHead->pn_pos = pos;
     forHead->setOp(JSOP_NOP);
     forHead->pn_kid1 = pn1;
     forHead->pn_kid2 = pn2;
     forHead->pn_kid3 = pn3;
     forHead->pn_pos.begin = lp_pos.begin;
     forHead->pn_pos.end   = tokenStream.currentToken().pos.end;
     pn->pn_left = forHead;
 
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
 
     /* Parse the loop body. */
     ParseNode *body = statement();
     if (!body)
-        return NULL;
+        return null();
 
     /* Record the absolute line number for source note emission. */
     pn->pn_pos.end = body->pn_pos.end;
     pn->pn_right = body;
 
     if (forParent) {
         forParent->pn_pos.begin = pn->pn_pos.begin;
         forParent->pn_pos.end = pn->pn_pos.end;
@@ -3406,20 +3618,31 @@ Parser::forStatement()
 #if JS_HAS_BLOCK_SCOPE
     if (blockObj)
         PopStatementPC(context, pc);
 #endif
     PopStatementPC(context, pc);
     return forParent ? forParent : pn;
 }
 
-ParseNode *
-Parser::tryStatement()
+template <>
+SyntaxParseHandler::Node
+Parser<SyntaxParseHandler>::forStatement()
+{
+    // XXX bug 835587 for statement needs a rewrite for syntax parsing.
+    setUnknownResult();
+    return SyntaxParseHandler::NodeFailure;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::tryStatement()
 {
     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_TRY));
+    TokenPtr begin = tokenStream.currentToken().pos.begin;
 
     /*
      * try nodes are ternary.
      * kid1 is the try statement
      * kid2 is the catch node list or null
      * kid3 is the finally statement
      *
      * catch nodes are ternary.
@@ -3428,225 +3651,228 @@ Parser::tryStatement()
      * kid3 is the catch block
      *
      * catch lvalue nodes are either:
      *   TOK_NAME for a single identifier
      *   TOK_RB or TOK_RC for a destructuring left-hand side
      *
      * finally nodes are TOK_LC statement lists.
      */
-    ParseNode *pn = TernaryNode::create(PNK_TRY, this);
-    if (!pn)
-        return NULL;
-    pn->setOp(JSOP_NOP);
 
     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
     StmtInfoPC stmtInfo(context);
     if (!PushBlocklikeStatement(&stmtInfo, STMT_TRY, pc))
-        return NULL;
-    pn->pn_kid1 = statements();
-    if (!pn->pn_kid1)
-        return NULL;
+        return null();
+    Node innerBlock = statements();
+    if (!innerBlock)
+        return null();
     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
     PopStatementPC(context, pc);
 
-    ParseNode *lastCatch;
-    ParseNode *catchList = NULL;
+    bool hasUnconditionalCatch = false;
+    Node catchList = null();
     TokenKind tt = tokenStream.getToken();
     if (tt == TOK_CATCH) {
-        catchList = ListNode::create(PNK_CATCHLIST, this);
+        catchList = handler.newList(PNK_CATCH);
         if (!catchList)
-            return NULL;
-        catchList->makeEmpty();
-        lastCatch = NULL;
+            return null();
 
         do {
-            ParseNode *pnblock;
-            BindData data(context);
+            Node pnblock;
+            BindData<ParseHandler> data(context);
 
             /* Check for another catch after unconditional catch. */
-            if (lastCatch && !lastCatch->pn_kid2) {
-                reportError(NULL, JSMSG_CATCH_AFTER_GENERAL);
-                return NULL;
+            if (hasUnconditionalCatch) {
+                report(ParseError, false, null(), JSMSG_CATCH_AFTER_GENERAL);
+                return null();
             }
 
             /*
              * Create a lexical scope node around the whole catch clause,
              * including the head.
              */
-            pnblock = PushLexicalScope(context, this, &stmtInfo);
+            pnblock = pushLexicalScope(&stmtInfo);
             if (!pnblock)
-                return NULL;
+                return null();
             stmtInfo.type = STMT_CATCH;
 
             /*
              * Legal catch forms are:
              *   catch (lhs)
              *   catch (lhs if <boolean_expression>)
              * where lhs is a name or a destructuring left-hand side.
              * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
              */
-            ParseNode *pn2 = TernaryNode::create(PNK_CATCH, this);
-            if (!pn2)
-                return NULL;
-            pnblock->pn_expr = pn2;
             MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
 
             /*
              * Contrary to ECMA Ed. 3, the catch variable is lexically
              * scoped, not a property of a new Object instance.  This is
              * an intentional change that anticipates ECMA Ed. 4.
              */
             data.initLet(HoistVars, *pc->blockChain, JSMSG_TOO_MANY_CATCH_VARS);
-            JS_ASSERT(data.let.blockObj && data.let.blockObj == pnblock->pn_objbox->object);
+            JS_ASSERT(data.let.blockObj);
 
             tt = tokenStream.getToken();
-            ParseNode *pn3;
+            Node catchName;
             switch (tt) {
 #if JS_HAS_DESTRUCTURING
               case TOK_LB:
               case TOK_LC:
-                pn3 = destructuringExpr(&data, tt);
-                if (!pn3)
-                    return NULL;
+                catchName = destructuringExpr(&data, tt);
+                if (!catchName)
+                    return null();
                 break;
 #endif
 
               case TOK_NAME:
               {
                 RootedPropertyName label(context, tokenStream.currentToken().name());
-                pn3 = NewBindingNode(label, this);
-                if (!pn3)
-                    return NULL;
-                data.pn = pn3;
+                catchName = newBindingNode(label);
+                if (!catchName)
+                    return null();
+                data.pn = catchName;
                 if (!data.binder(context, &data, label, this))
-                    return NULL;
+                    return null();
                 break;
               }
 
               default:
-                reportError(NULL, JSMSG_CATCH_IDENTIFIER);
-                return NULL;
+                report(ParseError, false, null(), JSMSG_CATCH_IDENTIFIER);
+                return null();
             }
 
-            pn2->pn_kid1 = pn3;
+            Node catchGuard = null();
 #if JS_HAS_CATCH_GUARD
             /*
              * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
              * to avoid conflicting with the JS2/ECMAv4 type annotation
              * catchguard syntax.
              */
             if (tokenStream.matchToken(TOK_IF)) {
-                pn2->pn_kid2 = expr();
-                if (!pn2->pn_kid2)
-                    return NULL;
+                catchGuard = expr();
+                if (!catchGuard)
+                    return null();
             }
 #endif
             MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
 
             MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
-            pn2->pn_kid3 = statements();
-            if (!pn2->pn_kid3)
-                return NULL;
+            Node catchBody = statements();
+            if (!catchBody)
+                return null();
             MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
             PopStatementPC(context, pc);
 
-            pnblock->pn_pos.end = pn2->pn_pos.end = tokenStream.currentToken().pos.end;
-
-            catchList->append(pnblock);
-            lastCatch = pn2;
+            if (!catchGuard)
+                hasUnconditionalCatch = true;
+
+            if (!handler.addCatchBlock(catchList, pnblock, catchName, catchGuard, catchBody))
+                return null();
+            handler.setEndPosition(catchList, tokenStream.currentToken().pos.end);
+            handler.setEndPosition(pnblock, tokenStream.currentToken().pos.end);
+
             tt = tokenStream.getToken(TSF_OPERAND);
         } while (tt == TOK_CATCH);
     }
-    pn->pn_kid2 = catchList;
+
+    Node finallyBlock = null();
 
     if (tt == TOK_FINALLY) {
         MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
         if (!PushBlocklikeStatement(&stmtInfo, STMT_FINALLY, pc))
-            return NULL;
-        pn->pn_kid3 = statements();
-        if (!pn->pn_kid3)
-            return NULL;
+            return null();
+        finallyBlock = statements();
+        if (!finallyBlock)
+            return null();
         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
         PopStatementPC(context, pc);
     } else {
         tokenStream.ungetToken();
     }
-    if (!catchList && !pn->pn_kid3) {
-        reportError(NULL, JSMSG_CATCH_OR_FINALLY);
-        return NULL;
+    if (!catchList && !finallyBlock) {
+        report(ParseError, false, null(), JSMSG_CATCH_OR_FINALLY);
+        return null();
     }
-    pn->pn_pos.end = (pn->pn_kid3 ? pn->pn_kid3 : catchList)->pn_pos.end;
+
+    Node pn = handler.newTernary(PNK_TRY, innerBlock, catchList, finallyBlock);
+    if (!pn)
+        return null();
+
+    handler.setBeginPosition(pn, begin);
+    handler.setEndPosition(pn, finallyBlock ? finallyBlock : catchList);
     return pn;
 }
 
-ParseNode *
-Parser::withStatement()
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::withStatement()
 {
     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_WITH));
+    TokenPtr begin = tokenStream.currentToken().pos.begin;
 
     // In most cases, we want the constructs forbidden in strict mode code to be
     // a subset of those that JSOPTION_STRICT warns about, and we should use
     // reportStrictModeError.  However, 'with' is the sole instance of a
     // construct that is forbidden in strict mode code, but doesn't even merit a
     // warning under JSOPTION_STRICT.  See
     // https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1.
-    if (pc->sc->strict && !reportStrictModeError(NULL, JSMSG_STRICT_CODE_WITH))
-        return NULL;
-
-    ParseNode *pn = BinaryNode::create(PNK_WITH, this);
-    if (!pn)
-        return NULL;
+    if (pc->sc->strict && !report(ParseStrictError, true, null(), JSMSG_STRICT_CODE_WITH))
+        return null();
+
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
-    ParseNode *pn2 = parenExpr();
-    if (!pn2)
-        return NULL;
+    Node objectExpr = parenExpr();
+    if (!objectExpr)
+        return null();
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
-    pn->pn_left = pn2;
 
     bool oldParsingWith = pc->parsingWith;
     pc->parsingWith = true;
 
     StmtInfoPC stmtInfo(context);
     PushStatementPC(pc, &stmtInfo, STMT_WITH);
-    pn2 = statement();
-    if (!pn2)
-        return NULL;
+    Node innerBlock = statement();
+    if (!innerBlock)
+        return null();
     PopStatementPC(context, pc);
 
-    pn->pn_pos.end = pn2->pn_pos.end;
-    pn->pn_right = pn2;
-
     pc->sc->setBindingsAccessedDynamically();
     pc->parsingWith = oldParsingWith;
 
     /*
      * Make sure to deoptimize lexical dependencies inside the |with|
      * to safely optimize binding globals (see bug 561923).
      */
     for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) {
         Definition *defn = r.front().value();
         Definition *lexdep = defn->resolve();
-        DeoptimizeUsesWithin(lexdep, pn->pn_pos);
+        DeoptimizeUsesWithin(lexdep, TokenPos::make(begin, tokenStream.currentToken().pos.begin));
     }
 
+    Node pn = handler.newBinary(PNK_WITH, objectExpr, innerBlock);
+    if (!pn)
+        return null();
+
+    handler.setBeginPosition(pn, begin);
+    handler.setEndPosition(pn, innerBlock);
     return pn;
 }
 
 #if JS_HAS_BLOCK_SCOPE
+template <>
 ParseNode *
-Parser::letStatement()
+Parser<FullParseHandler>::letStatement()
 {
     ParseNode *pn;
     do {
         /* Check for a let statement or let expression. */
         if (tokenStream.peekToken() == TOK_LP) {
             pn = letBlock(LetStatement);
             if (!pn)
-                return NULL;
+                return null();
 
             JS_ASSERT(pn->isKind(PNK_LET) || pn->isKind(PNK_SEMI));
             if (pn->isKind(PNK_LET) && pn->pn_expr->getOp() == JSOP_LEAVEBLOCK)
                 return pn;
 
             /* Let expressions require automatic semicolon insertion. */
             JS_ASSERT(pn->isKind(PNK_SEMI) || pn->isOp(JSOP_NOP));
             break;
@@ -3660,31 +3886,31 @@ Parser::letStatement()
          * find this scope statement and use the same block object.
          *
          * If we are the first let declaration in this block (i.e., when the
          * enclosing maybe-scope StmtInfoPC isn't yet a scope statement) then
          * we also need to set pc->blockNode to be our TOK_LEXICALSCOPE.
          */
         StmtInfoPC *stmt = pc->topStmt;
         if (stmt && (!stmt->maybeScope() || stmt->isForLetBlock)) {
-            reportError(NULL, JSMSG_LET_DECL_NOT_IN_BLOCK);
-            return NULL;
+            report(ParseError, false, null(), JSMSG_LET_DECL_NOT_IN_BLOCK);
+            return null();
         }
 
         if (stmt && stmt->isBlockScope) {
             JS_ASSERT(pc->blockChain == stmt->blockObj);
         } else {
             if (pc->atBodyLevel()) {
                 /*
                  * ES4 specifies that let at top level and at body-block scope
                  * does not shadow var, so convert back to var.
                  */
                 pn = variables(PNK_VAR);
                 if (!pn)
-                    return NULL;
+                    return null();
                 pn->pn_xflags |= PNX_POPVAR;
                 break;
             }
 
             /*
              * Some obvious assertions here, but they may help clarify the
              * situation. This stmt is not yet a scope, so it must not be a
              * catch block (catch is a lexical scope by definition).
@@ -3695,21 +3921,21 @@ Parser::letStatement()
                       stmt->type == STMT_SWITCH ||
                       stmt->type == STMT_TRY ||
                       stmt->type == STMT_FINALLY);
             JS_ASSERT(!stmt->downScope);
 
             /* Convert the block statement into a scope statement. */
             StaticBlockObject *blockObj = StaticBlockObject::create(context);
             if (!blockObj)
-                return NULL;
+                return null();
 
             ObjectBox *blockbox = newObjectBox(blockObj);
             if (!blockbox)
-                return NULL;
+                return null();
 
             /*
              * Insert stmt on the pc->topScopeStmt/stmtInfo.downScope linked
              * list stack, if it isn't already there.  If it is there, but it
              * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
              * block.
              */
             stmt->isBlockScope = true;
@@ -3721,182 +3947,186 @@ Parser::letStatement()
             stmt->blockObj = blockObj;
 
 #ifdef DEBUG
             ParseNode *tmp = pc->blockNode;
             JS_ASSERT(!tmp || !tmp->isKind(PNK_LEXICALSCOPE));
 #endif
 
             /* Create a new lexical scope node for these statements. */
-            ParseNode *pn1 = LexicalScopeNode::create(PNK_LEXICALSCOPE, this);
+            ParseNode *pn1 = LexicalScopeNode::create(PNK_LEXICALSCOPE, &handler);
             if (!pn1)
-                return NULL;
+                return null();
 
             pn1->setOp(JSOP_LEAVEBLOCK);
             pn1->pn_pos = pc->blockNode->pn_pos;
             pn1->pn_objbox = blockbox;
             pn1->pn_expr = pc->blockNode;
             pn1->pn_blockid = pc->blockNode->pn_blockid;
             pc->blockNode = pn1;
         }
 
         pn = variables(PNK_LET, pc->blockChain, HoistVars);
         if (!pn)
-            return NULL;
+            return null();
         pn->pn_xflags = PNX_POPVAR;
     } while (0);
 
     /* Check termination of this primitive statement. */
     return MatchOrInsertSemicolon(context, &tokenStream) ? pn : NULL;
 }
-#endif
-
-ParseNode *
-Parser::expressionStatement()
+
+template <>
+SyntaxParseHandler::Node
+Parser<SyntaxParseHandler>::letStatement()
+{
+    setUnknownResult();
+    return SyntaxParseHandler::NodeFailure;
+}
+
+#endif // JS_HAS_BLOCK_SCOPE
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::expressionStatement()
 {
     tokenStream.ungetToken();
-    ParseNode *pn2 = expr();
+    Node pn2 = expr();
     if (!pn2)
-        return NULL;
+        return null();
 
     if (tokenStream.peekToken() == TOK_COLON) {
-        if (!pn2->isKind(PNK_NAME)) {
-            reportError(NULL, JSMSG_BAD_LABEL);
-            return NULL;
+        RootedAtom label(context, handler.isName(pn2));
+        if (!label) {
+            report(ParseError, false, null(), JSMSG_BAD_LABEL);
+            return null();
         }
-        RootedAtom label(context, pn2->pn_atom);
         for (StmtInfoPC *stmt = pc->topStmt; stmt; stmt = stmt->down) {
             if (stmt->type == STMT_LABEL && stmt->label == label) {
-                reportError(NULL, JSMSG_DUPLICATE_LABEL);
-                return NULL;
+                report(ParseError, false, null(), JSMSG_DUPLICATE_LABEL);
+                return null();
             }
         }
         ForgetUse(pn2);
 
         (void) tokenStream.getToken();
 
         /* Push a label struct and parse the statement. */
         StmtInfoPC stmtInfo(context);
         PushStatementPC(pc, &stmtInfo, STMT_LABEL);
         stmtInfo.label = label;
-        ParseNode *pn = statement();
+        Node pn = statement();
         if (!pn)
-            return NULL;
+            return null();
 
         /* Pop the label, set pn_expr, and return early. */
         PopStatementPC(context, pc);
-        pn2->setKind(PNK_COLON);
-        pn2->pn_pos.end = pn->pn_pos.end;
-        pn2->pn_expr = pn;
+
+        handler.morphNameIntoLabel(pn2, pn);
         return pn2;
     }
 
-    ParseNode *pn = UnaryNode::create(PNK_SEMI, this);
-    if (!pn)
-        return NULL;
-    pn->pn_pos = pn2->pn_pos;
-    pn->pn_kid = pn2;
+    Node pn = handler.newUnary(PNK_SEMI, pn2);
 
     /* Check termination of this primitive statement. */
-    return MatchOrInsertSemicolon(context, &tokenStream) ? pn : NULL;
+    return MatchOrInsertSemicolon(context, &tokenStream) ? pn : null();
 }
 
-ParseNode *
-Parser::statement()
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::statement()
 {
-    ParseNode *pn;
-
-    JS_CHECK_RECURSION(context, return NULL);
+    Node pn;
+
+    JS_CHECK_RECURSION(context, return null());
 
     switch (tokenStream.getToken(TSF_OPERAND)) {
       case TOK_FUNCTION:
         return functionStmt();
 
       case TOK_IF:
       {
+        TokenPtr begin = tokenStream.currentToken().pos.begin;
+
         /* An IF node has three kids: condition, then, and optional else. */
-        pn = TernaryNode::create(PNK_IF, this);
-        if (!pn)
-            return NULL;
-        ParseNode *pn1 = condition();
-        if (!pn1)
-            return NULL;
+        Node cond = condition();
+        if (!cond)
+            return null();
 
         StmtInfoPC stmtInfo(context);
         PushStatementPC(pc, &stmtInfo, STMT_IF);
-        ParseNode *pn2 = statement();
-        if (!pn2)
-            return NULL;
-
-        if (pn2->isKind(PNK_SEMI) &&
-            !pn2->pn_kid &&
-            !reportStrictWarning(NULL, JSMSG_EMPTY_CONSEQUENT))
+        Node thenBranch = statement();
+        if (!thenBranch)
+            return null();
+
+        if (handler.isEmptySemicolon(thenBranch) &&
+            !report(ParseStrictWarning, false, null(), JSMSG_EMPTY_CONSEQUENT))
         {
-            return NULL;
+            return null();
         }
 
-        ParseNode *pn3;
+        Node elseBranch;
         if (tokenStream.matchToken(TOK_ELSE, TSF_OPERAND)) {
             stmtInfo.type = STMT_ELSE;
-            pn3 = statement();
-            if (!pn3)
-                return NULL;
-            pn->pn_pos.end = pn3->pn_pos.end;
+            elseBranch = statement();
+            if (!elseBranch)
+                return null();
         } else {
-            pn3 = NULL;
-            pn->pn_pos.end = pn2->pn_pos.end;
+            elseBranch = null();
         }
+
         PopStatementPC(context, pc);
-        pn->pn_kid1 = pn1;
-        pn->pn_kid2 = pn2;
-        pn->pn_kid3 = pn3;
+        pn = handler.newTernary(PNK_IF, cond, thenBranch, elseBranch);
+        if (!pn)
+            return null();
+        handler.setBeginPosition(pn, begin);
         return pn;
       }
 
       case TOK_SWITCH:
         return switchStatement();
 
       case TOK_WHILE:
       {
-        pn = BinaryNode::create(PNK_WHILE, this);
-        if (!pn)
-            return NULL;
+        TokenPtr begin = tokenStream.currentToken().pos.begin;
         StmtInfoPC stmtInfo(context);
         PushStatementPC(pc, &stmtInfo, STMT_WHILE_LOOP);
-        ParseNode *pn2 = condition();
-        if (!pn2)
-            return NULL;
-        pn->pn_left = pn2;
-        ParseNode *pn3 = statement();
-        if (!pn3)
-            return NULL;
+        Node cond = condition();
+        if (!cond)
+            return null();
+        Node body = statement();
+        if (!body)
+            return null();
         PopStatementPC(context, pc);
-        pn->pn_pos.end = pn3->pn_pos.end;
-        pn->pn_right = pn3;
+        pn = handler.newBinary(PNK_WHILE, cond, body);
+        if (!pn)
+            return null();
+        handler.setBeginPosition(pn, begin);
         return pn;
       }
 
       case TOK_DO:
       {
-        pn = BinaryNode::create(PNK_DOWHILE, this);
-        if (!pn)
-            return NULL;
+        TokenPtr begin = tokenStream.currentToken().pos.begin;
         StmtInfoPC stmtInfo(context);
         PushStatementPC(pc, &stmtInfo, STMT_DO_LOOP);
-        ParseNode *pn2 = statement();
-        if (!pn2)
-            return NULL;
-        pn->pn_left = pn2;
+        Node body = statement();
+        if (!body)
+            return null();
         MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
-        ParseNode *pn3 = condition();
-        if (!pn3)
-            return NULL;
+        Node cond = condition();
+        if (!cond)
+            return null();
         PopStatementPC(context, pc);
-        pn->pn_pos.end = pn3->pn_pos.end;
-        pn->pn_right = pn3;
+
+        pn = handler.newBinary(PNK_DOWHILE, body, cond);
+        if (!pn)
+            return null();
+        handler.setBeginPosition(pn, begin);
+
         if (versionNumber() != JSVERSION_ECMA_3) {
             /*
              * All legacy and extended versions must do automatic semicolon
              * insertion after do-while.  See the testcase and discussion in
              * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
              */
             (void) tokenStream.matchToken(TOK_SEMI);
             return pn;
@@ -3907,396 +4137,394 @@ Parser::statement()
       case TOK_FOR:
         return forStatement();
 
       case TOK_TRY:
         return tryStatement();
 
       case TOK_THROW:
       {
-        pn = UnaryNode::create(PNK_THROW, this);
-        if (!pn)
-            return NULL;
+        TokenPtr begin = tokenStream.currentToken().pos.begin;
 
         /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
         TokenKind tt = tokenStream.peekTokenSameLine(TSF_OPERAND);
         if (tt == TOK_ERROR)
-            return NULL;
+            return null();
         if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) {
-            reportError(NULL, JSMSG_SYNTAX_ERROR);
-            return NULL;
+            report(ParseError, false, null(), JSMSG_SYNTAX_ERROR);
+            return null();
         }
 
-        ParseNode *pn2 = expr();
-        if (!pn2)
-            return NULL;
-        pn->pn_pos.end = pn2->pn_pos.end;
-        pn->setOp(JSOP_THROW);
-        pn->pn_kid = pn2;
+        Node pnexp = expr();
+        if (!pnexp)
+            return null();
+
+        pn = handler.newUnary(PNK_THROW, pnexp, JSOP_THROW);
+        if (!pn)
+            return null();
+        handler.setBeginPosition(pn, begin);
         break;
       }
 
       /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
       case TOK_CATCH:
-        reportError(NULL, JSMSG_CATCH_WITHOUT_TRY);
-        return NULL;
+        report(ParseError, false, null(), JSMSG_CATCH_WITHOUT_TRY);
+        return null();
 
       case TOK_FINALLY:
-        reportError(NULL, JSMSG_FINALLY_WITHOUT_TRY);
-        return NULL;
+        report(ParseError, false, null(), JSMSG_FINALLY_WITHOUT_TRY);
+        return null();
 
       case TOK_BREAK:
       {
         TokenPtr begin = tokenStream.currentToken().pos.begin;
         RootedPropertyName label(context);
         if (!MatchLabel(context, &tokenStream, &label))
-            return NULL;
+            return null();
         TokenPtr end = tokenStream.currentToken().pos.end;
-        pn = new_<BreakStatement>(label.get(), begin, end);
+        pn = handler.newBreak(label, begin, end);
         if (!pn)
-            return NULL;
+            return null();
         StmtInfoPC *stmt = pc->topStmt;
         if (label) {
             for (; ; stmt = stmt->down) {
                 if (!stmt) {
-                    reportError(NULL, JSMSG_LABEL_NOT_FOUND);
-                    return NULL;
+                    report(ParseError, false, null(), JSMSG_LABEL_NOT_FOUND);
+                    return null();
                 }
                 if (stmt->type == STMT_LABEL && stmt->label == label)
                     break;
             }
         } else {
             for (; ; stmt = stmt->down) {
                 if (!stmt) {
-                    reportError(NULL, JSMSG_TOUGH_BREAK);
-                    return NULL;
+                    report(ParseError, false, null(), JSMSG_TOUGH_BREAK);
+                    return null();
                 }
                 if (stmt->isLoop() || stmt->type == STMT_SWITCH)
                     break;
             }
         }
         break;
       }
 
       case TOK_CONTINUE:
       {
         TokenPtr begin = tokenStream.currentToken().pos.begin;
         RootedPropertyName label(context);
         if (!MatchLabel(context, &tokenStream, &label))
-            return NULL;
+            return null();
         TokenPtr end = tokenStream.currentToken().pos.begin;
-        pn = new_<ContinueStatement>(label.get(), begin, end);
+        pn = handler.newContinue(label, begin, end);
         if (!pn)
-            return NULL;
+            return null();
         StmtInfoPC *stmt = pc->topStmt;
         if (label) {
             for (StmtInfoPC *stmt2 = NULL; ; stmt = stmt->down) {
                 if (!stmt) {
-                    reportError(NULL, JSMSG_LABEL_NOT_FOUND);
-                    return NULL;
+                    report(ParseError, false, null(), JSMSG_LABEL_NOT_FOUND);
+                    return null();
                 }
                 if (stmt->type == STMT_LABEL) {
                     if (stmt->label == label) {
                         if (!stmt2 || !stmt2->isLoop()) {
-                            reportError(NULL, JSMSG_BAD_CONTINUE);
-                            return NULL;
+                            report(ParseError, false, null(), JSMSG_BAD_CONTINUE);
+                            return null();
                         }
                         break;
                     }
                 } else {
                     stmt2 = stmt;
                 }
             }
         } else {
             for (; ; stmt = stmt->down) {
                 if (!stmt) {
-                    reportError(NULL, JSMSG_BAD_CONTINUE);
-                    return NULL;
+                    report(ParseError, false, null(), JSMSG_BAD_CONTINUE);
+                    return null();
                 }
                 if (stmt->isLoop())
                     break;
             }
         }
         break;
       }
 
       case TOK_WITH:
         return withStatement();
 
       case TOK_VAR:
         pn = variables(PNK_VAR);
         if (!pn)
-            return NULL;
+            return null();
 
         /* Tell js_EmitTree to generate a final POP. */
-        pn->pn_xflags |= PNX_POPVAR;
+        handler.setListFlag(pn, PNX_POPVAR);
         break;
 
       case TOK_CONST:
         pn = variables(PNK_CONST);
         if (!pn)
-            return NULL;
+            return null();
 
         /* Tell js_EmitTree to generate a final POP. */
-        pn->pn_xflags |= PNX_POPVAR;
+        handler.setListFlag(pn, PNX_POPVAR);
         break;
 
 #if JS_HAS_BLOCK_SCOPE
       case TOK_LET:
         return letStatement();
 #endif /* JS_HAS_BLOCK_SCOPE */
 
       case TOK_RETURN:
         pn = returnOrYield(false);
         if (!pn)
-            return NULL;
+            return null();
         break;
 
       case TOK_LC:
       {
         StmtInfoPC stmtInfo(context);
         if (!PushBlocklikeStatement(&stmtInfo, STMT_BLOCK, pc))
-            return NULL;
+            return null();
         bool hasFunctionStmt;
         pn = statements(&hasFunctionStmt);
         if (!pn)
-            return NULL;
+            return null();
 
         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
         PopStatementPC(context, pc);
         return pn;
       }
 
       case TOK_SEMI:
-        pn = UnaryNode::create(PNK_SEMI, this);
-        if (!pn)
-            return NULL;
-        return pn;
+        return handler.newUnary(PNK_SEMI);
 
       case TOK_DEBUGGER:
-        pn = new_<DebuggerStatement>(tokenStream.currentToken().pos);
+        pn = handler.newDebuggerStatement(tokenStream.currentToken().pos);
         if (!pn)
-            return NULL;
+            return null();
         pc->sc->setBindingsAccessedDynamically();
         pc->sc->setHasDebuggerStatement();
         break;
 
       case TOK_ERROR:
-        return NULL;
+        return null();
 
       case TOK_NAME:
         if (tokenStream.currentToken().name() == context->names().module &&
             tokenStream.peekTokenSameLine(TSF_OPERAND) == TOK_STRING)
         {
             return moduleDecl();
         }
 
       default:
         return expressionStatement();
     }
 
     /* Check termination of this primitive statement. */
-    return MatchOrInsertSemicolon(context, &tokenStream) ? pn : NULL;
+    return MatchOrInsertSemicolon(context, &tokenStream) ? pn : null();
 }
 
 /*
  * The 'blockObj' parameter is non-null when parsing the 'vars' in a let
  * expression, block statement, non-top-level let declaration in statement
  * context, and the let-initializer of a for-statement.
  */
-ParseNode *
-Parser::variables(ParseNodeKind kind, StaticBlockObject *blockObj, VarContext varContext)
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::variables(ParseNodeKind kind, StaticBlockObject *blockObj, VarContext varContext)
 {
     /*
      * The four options here are:
      * - PNK_VAR:   We're parsing var declarations.
      * - PNK_CONST: We're parsing const declarations.
      * - PNK_LET:   We are parsing a let declaration.
      * - PNK_CALL:  We are parsing the head of a let block.
      */
     JS_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET || kind == PNK_CALL);
 
-    ParseNode *pn = ListNode::create(kind, this);
+    JSOp op = blockObj ? JSOP_NOP : kind == PNK_VAR ? JSOP_DEFVAR : JSOP_DEFCONST;
+
+    Node pn = handler.newList(kind, null(), op);
     if (!pn)
-        return NULL;
-
-    pn->setOp(blockObj ? JSOP_NOP : kind == PNK_VAR ? JSOP_DEFVAR : JSOP_DEFCONST);
-    pn->makeEmpty();
+        return null();
 
     /*
      * SpiderMonkey const is really "write once per initialization evaluation"
      * var, whereas let is block scoped. ES-Harmony wants block-scoped const so
      * this code will change soon.
      */
-    BindData data(context);
+    BindData<ParseHandler> data(context);
     if (blockObj)
         data.initLet(varContext, *blockObj, JSMSG_TOO_MANY_LOCALS);
     else
-        data.initVarOrConst(pn->getOp());
-
-    ParseNode *pn2;
+        data.initVarOrConst(op);
+
+    Node pn2;
     do {
         TokenKind tt = tokenStream.getToken();
 #if JS_HAS_DESTRUCTURING
         if (tt == TOK_LB || tt == TOK_LC) {
             pc->inDeclDestructuring = true;
             pn2 = primaryExpr(tt);
             pc->inDeclDestructuring = false;
             if (!pn2)
-                return NULL;
-
-            if (!CheckDestructuring(context, &data, pn2, this))
-                return NULL;
+                return null();
+
+            if (!checkDestructuring(&data, pn2))
+                return null();
             bool ignored;
             if (pc->parsingForInit && matchInOrOf(&ignored)) {
                 tokenStream.ungetToken();
-                pn->append(pn2);
+                handler.addList(pn, pn2);
                 continue;
             }
 
             MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
             JS_ASSERT(tokenStream.currentToken().t_op == JSOP_NOP);
 
-            ParseNode *init = assignExpr();
+            Node init = assignExpr();
             if (!init)
-                return NULL;
-
-            pn2 = ParseNode::newBinaryOrAppend(PNK_ASSIGN, JSOP_NOP, pn2, init, this);
+                return null();
+
+            pn2 = handler.newBinaryOrAppend(PNK_ASSIGN, pn2, init);
             if (!pn2)
-                return NULL;
-            pn->append(pn2);
+                return null();
+            handler.addList(pn, pn2);
             continue;
         }
 #endif /* JS_HAS_DESTRUCTURING */
 
         if (tt != TOK_NAME) {
             if (tt != TOK_ERROR)
-                reportError(NULL, JSMSG_NO_VARIABLE_NAME);
-            return NULL;
+                report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
+            return null();
         }
 
         RootedPropertyName name(context, tokenStream.currentToken().name());
-        pn2 = NewBindingNode(name, this, varContext);
+        pn2 = newBindingNode(name, varContext);
         if (!pn2)
-            return NULL;
+            return null();
         if (data.op == JSOP_DEFCONST)
-            pn2->pn_dflags |= PND_CONST;
+            handler.setFlag(pn2, PND_CONST);
         data.pn = pn2;
         if (!data.binder(context, &data, name, this))
-            return NULL;
-        pn->append(pn2);
+            return null();
+        handler.addList(pn, pn2);
 
         if (tokenStream.matchToken(TOK_ASSIGN)) {
             JS_ASSERT(tokenStream.currentToken().t_op == JSOP_NOP);
 
-            ParseNode *init = assignExpr();
+            Node init = assignExpr();
             if (!init)
-                return NULL;
-
-            if (pn2->isUsed()) {
-                pn2 = MakeAssignment(pn2, init, this);
-                if (!pn2)
-                    return NULL;
-            } else {
-                pn2->pn_expr = init;
-            }
-
-            pn2->setOp((pn2->pn_dflags & PND_BOUND)
-                       ? JSOP_SETLOCAL
-                       : (data.op == JSOP_DEFCONST)
-                       ? JSOP_SETCONST
-                       : JSOP_SETNAME);
-
-            NoteLValue(pn2);
-
-            /* The declarator's position must include the initializer. */
-            pn2->pn_pos.end = init->pn_pos.end;
+                return null();
+
+            if (!handler.finishInitializerAssignment(pn2, init, data.op))
+                return null();
         }
     } while (tokenStream.matchToken(TOK_COMMA));
 
-    pn->pn_pos.end = pn->last()->pn_pos.end;
     return pn;
 }
 
+template <>
 ParseNode *
-Parser::expr()
+Parser<FullParseHandler>::expr()
 {
     ParseNode *pn = assignExpr();
     if (pn && tokenStream.matchToken(TOK_COMMA)) {
-        ParseNode *pn2 = ListNode::create(PNK_COMMA, this);
+        ParseNode *pn2 = ListNode::create(PNK_COMMA, &handler);
         if (!pn2)
-            return NULL;
+            return null();
         pn2->pn_pos.begin = pn->pn_pos.begin;
         pn2->initList(pn);
         pn = pn2;
         do {
 #if JS_HAS_GENERATORS
             pn2 = pn->last();
             if (pn2->isKind(PNK_YIELD) && !pn2->isInParens()) {
-                reportError(pn2, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
-                return NULL;
+                report(ParseError, false, pn2, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
+                return null();
             }
 #endif
             pn2 = assignExpr();
             if (!pn2)
-                return NULL;
+                return null();
             pn->append(pn2);
         } while (tokenStream.matchToken(TOK_COMMA));
         pn->pn_pos.end = pn->last()->pn_pos.end;
     }
     return pn;
 }
 
+template <>
+SyntaxParseHandler::Node
+Parser<SyntaxParseHandler>::expr()
+{
+    Node pn = assignExpr();
+    if (pn && tokenStream.matchToken(TOK_COMMA)) {
+        do {
+            if (!assignExpr())
+                return null();
+        } while (tokenStream.matchToken(TOK_COMMA));
+        return SyntaxParseHandler::NodeGeneric;
+    }
+    return pn;
+}
+
 /*
  * For a number of the expression parsers we define an always-inlined version
  * and a never-inlined version (which just calls the always-inlined version).
  * Using the always-inlined version in the hot call-sites givs a ~5% parsing
  * speedup.  These macros help avoid some boilerplate code.
  */
 #define BEGIN_EXPR_PARSER(name)                                               \
-    JS_ALWAYS_INLINE ParseNode *                                              \
-    Parser::name##i()
+    template <typename ParseHandler>                                          \
+    JS_ALWAYS_INLINE typename ParseHandler::Node                              \
+    Parser<ParseHandler>::name##i()
 
 #define END_EXPR_PARSER(name)                                                 \
-    JS_NEVER_INLINE ParseNode *                                               \
-    Parser::name##n() {                                                       \
+    template <typename ParseHandler>                                          \
+    JS_NEVER_INLINE typename ParseHandler::Node                               \
+    Parser<ParseHandler>::name##n() {                                         \
         return name##i();                                                     \
     }
 
 BEGIN_EXPR_PARSER(mulExpr1)
 {
-    ParseNode *pn = unaryExpr();
+    Node pn = unaryExpr();
 
     /*
      * Note: unlike addExpr1() et al, we use getToken() here instead of
      * isCurrentTokenType() because unaryExpr() doesn't leave the TokenStream
      * state one past the end of the unary expression.
      */
     TokenKind tt;
     while (pn && ((tt = tokenStream.getToken()) == TOK_STAR || tt == TOK_DIV || tt == TOK_MOD)) {
         ParseNodeKind kind = (tt == TOK_STAR)
                              ? PNK_STAR
                              : (tt == TOK_DIV)
                              ? PNK_DIV
                              : PNK_MOD;
         JSOp op = tokenStream.currentToken().t_op;
-        pn = ParseNode::newBinaryOrAppend(kind, op, pn, unaryExpr(), this);
+        pn = handler.newBinaryOrAppend(kind, pn, unaryExpr(), op);
     }
     return pn;
 }
 END_EXPR_PARSER(mulExpr1)
 
 BEGIN_EXPR_PARSER(addExpr1)
 {
-    ParseNode *pn = mulExpr1i();
+    Node pn = mulExpr1i();
     while (pn && tokenStream.isCurrentTokenType(TOK_PLUS, TOK_MINUS)) {
         TokenKind tt = tokenStream.currentToken().type;
         JSOp op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;
         ParseNodeKind kind = (tt == TOK_PLUS) ? PNK_ADD : PNK_SUB;
-        pn = ParseNode::newBinaryOrAppend(kind, op, pn, mulExpr1n(), this);
+        pn = handler.newBinaryOrAppend(kind, pn, mulExpr1n(), op);
     }
     return pn;
 }
 END_EXPR_PARSER(addExpr1)
 
 inline ParseNodeKind
 ShiftTokenToParseNodeKind(const Token &token)
 {
@@ -4308,24 +4536,24 @@ ShiftTokenToParseNodeKind(const Token &t
       default:
         JS_ASSERT(token.type == TOK_URSH);
         return PNK_URSH;
     }
 }
 
 BEGIN_EXPR_PARSER(shiftExpr1)
 {
-    ParseNode *left = addExpr1i();
+    Node left = addExpr1i();
     while (left && tokenStream.isCurrentTokenShift()) {
         ParseNodeKind kind = ShiftTokenToParseNodeKind(tokenStream.currentToken());
         JSOp op = tokenStream.currentToken().t_op;
-        ParseNode *right = addExpr1n();
+        Node right = addExpr1n();
         if (!right)
-            return NULL;
-        left = new_<BinaryNode>(kind, op, left, right);
+            return null();
+        left = handler.newBinary(kind, left, right, op);
     }
     return left;
 }
 END_EXPR_PARSER(shiftExpr1)
 
 inline ParseNodeKind
 RelationalTokenToParseNodeKind(const Token &token)
 {
@@ -4350,28 +4578,28 @@ BEGIN_EXPR_PARSER(relExpr1)
 {
     /*
      * Uses of the in operator in shiftExprs are always unambiguous,
      * so unset the flag that prohibits recognizing it.
      */
     bool oldParsingForInit = pc->parsingForInit;
     pc->parsingForInit = false;
 
-    ParseNode *pn = shiftExpr1i();
+    Node pn = shiftExpr1i();
     while (pn &&
            (tokenStream.isCurrentTokenRelational() ||
             /*
              * Recognize the 'in' token as an operator only if we're not
              * currently in the init expr of a for loop.
              */
             (oldParsingForInit == 0 && tokenStream.isCurrentTokenType(TOK_IN)) ||
             tokenStream.isCurrentTokenType(TOK_INSTANCEOF))) {
         ParseNodeKind kind = RelationalTokenToParseNodeKind(tokenStream.currentToken());
         JSOp op = tokenStream.currentToken().t_op;
-        pn = ParseNode::newBinaryOrAppend(kind, op, pn, shiftExpr1n(), this);
+        pn = handler.newBinaryOrAppend(kind, pn, shiftExpr1n(), op);
     }
     /* Restore previous state of parsingForInit flag. */
     pc->parsingForInit |= oldParsingForInit;
 
     return pn;
 }
 END_EXPR_PARSER(relExpr1)
 
@@ -4388,154 +4616,170 @@ EqualityTokenToParseNodeKind(const Token
       default:
         JS_ASSERT(token.type == TOK_NE);
         return PNK_NE;
     }
 }
 
 BEGIN_EXPR_PARSER(eqExpr1)
 {
-    ParseNode *left = relExpr1i();
+    Node left = relExpr1i();
     while (left && tokenStream.isCurrentTokenEquality()) {
         ParseNodeKind kind = EqualityTokenToParseNodeKind(tokenStream.currentToken());
         JSOp op = tokenStream.currentToken().t_op;
-        ParseNode *right = relExpr1n();
+        Node right = relExpr1n();
         if (!right)
-            return NULL;
-        left = new_<BinaryNode>(kind, op, left, right);
+            return null();
+        left = handler.newBinary(kind, left, right, op);
     }
     return left;
 }
 END_EXPR_PARSER(eqExpr1)
 
 BEGIN_EXPR_PARSER(bitAndExpr1)
 {
-    ParseNode *pn = eqExpr1i();
+    Node pn = eqExpr1i();
     while (pn && tokenStream.isCurrentTokenType(TOK_BITAND))
-        pn = ParseNode::newBinaryOrAppend(PNK_BITAND, JSOP_BITAND, pn, eqExpr1n(), this);
+        pn = handler.newBinaryOrAppend(PNK_BITAND, pn, eqExpr1n(), JSOP_BITAND);
     return pn;
 }
 END_EXPR_PARSER(bitAndExpr1)
 
 BEGIN_EXPR_PARSER(bitXorExpr1)
 {
-    ParseNode *pn = bitAndExpr1i();
+    Node pn = bitAndExpr1i();
     while (pn && tokenStream.isCurrentTokenType(TOK_BITXOR))
-        pn = ParseNode::newBinaryOrAppend(PNK_BITXOR, JSOP_BITXOR, pn, bitAndExpr1n(), this);
+        pn = handler.newBinaryOrAppend(PNK_BITXOR, pn, bitAndExpr1n(), JSOP_BITXOR);
     return pn;
 }
 END_EXPR_PARSER(bitXorExpr1)
 
 BEGIN_EXPR_PARSER(bitOrExpr1)
 {
-    ParseNode *pn = bitXorExpr1i();
+    Node pn = bitXorExpr1i();
     while (pn && tokenStream.isCurrentTokenType(TOK_BITOR))
-        pn = ParseNode::newBinaryOrAppend(PNK_BITOR, JSOP_BITOR, pn, bitXorExpr1n(), this);
+        pn = handler.newBinaryOrAppend(PNK_BITOR, pn, bitXorExpr1n(), JSOP_BITOR);
     return pn;
 }
 END_EXPR_PARSER(bitOrExpr1)
 
 BEGIN_EXPR_PARSER(andExpr1)
 {
-    ParseNode *pn = bitOrExpr1i();
+    Node pn = bitOrExpr1i();
     while (pn && tokenStream.isCurrentTokenType(TOK_AND))
-        pn = ParseNode::newBinaryOrAppend(PNK_AND, JSOP_AND, pn, bitOrExpr1n(), this);
+        pn = handler.newBinaryOrAppend(PNK_AND, pn, bitOrExpr1n(), JSOP_AND);
     return pn;
 }
 END_EXPR_PARSER(andExpr1)
 
-JS_ALWAYS_INLINE ParseNode *
-Parser::orExpr1()
+template <typename ParseHandler>
+JS_ALWAYS_INLINE typename ParseHandler::Node
+Parser<ParseHandler>::orExpr1()
 {
-    ParseNode *pn = andExpr1i();
+    Node pn = andExpr1i();
     while (pn && tokenStream.isCurrentTokenType(TOK_OR))
-        pn = ParseNode::newBinaryOrAppend(PNK_OR, JSOP_OR, pn, andExpr1n(), this);
+        pn = handler.newBinaryOrAppend(PNK_OR, pn, andExpr1n(), JSOP_OR);
     return pn;
 }
 
-JS_ALWAYS_INLINE ParseNode *
-Parser::condExpr1()
+template <typename ParseHandler>
+JS_ALWAYS_INLINE typename ParseHandler::Node
+Parser<ParseHandler>::condExpr1()
 {
-    ParseNode *condition = orExpr1();
+    Node condition = orExpr1();
     if (!condition || !tokenStream.isCurrentTokenType(TOK_HOOK))
         return condition;
 
     /*
      * Always accept the 'in' operator in the middle clause of a ternary,
      * where it's unambiguous, even if we might be parsing the init of a
      * for statement.
      */
     bool oldParsingForInit = pc->parsingForInit;
     pc->parsingForInit = false;
-    ParseNode *thenExpr = assignExpr();
+    Node thenExpr = assignExpr();
     pc->parsingForInit = oldParsingForInit;
     if (!thenExpr)
-        return NULL;
+        return null();
 
     MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
 
-    ParseNode *elseExpr = assignExpr();
+    Node elseExpr = assignExpr();
     if (!elseExpr)
-        return NULL;
+        return null();
 
     tokenStream.getToken(); /* read one token past the end */
-    return new_<ConditionalExpression>(condition, thenExpr, elseExpr);
+    return handler.newConditional(condition, thenExpr, elseExpr);
 }
 
+template <>
 bool
-Parser::setAssignmentLhsOps(ParseNode *pn, JSOp op)
+Parser<FullParseHandler>::setAssignmentLhsOps(ParseNode *pn, JSOp op)
 {
     switch (pn->getKind()) {
       case PNK_NAME:
-        if (!CheckStrictAssignment(context, this, pn))
+        if (!checkStrictAssignment(pn))
             return false;
         pn->setOp(pn->isOp(JSOP_GETLOCAL) ? JSOP_SETLOCAL : JSOP_SETNAME);
-        NoteLValue(pn);
+        handler.noteLValue(pn);
         break;
       case PNK_DOT:
         pn->setOp(JSOP_SETPROP);
         break;
       case PNK_ELEM:
         pn->setOp(JSOP_SETELEM);
         break;
 #if JS_HAS_DESTRUCTURING
       case PNK_ARRAY:
       case PNK_OBJECT:
         if (op != JSOP_NOP) {
-            reportError(NULL, JSMSG_BAD_DESTRUCT_ASS);
+            report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_ASS);
             return false;
         }
-        if (!CheckDestructuring(context, NULL, pn, this))
+        if (!checkDestructuring(NULL, pn))
             return false;
         break;
 #endif
       case PNK_CALL:
-        if (!MakeSetCall(context, pn, this, JSMSG_BAD_LEFTSIDE_OF_ASS))
+        if (!makeSetCall(pn, JSMSG_BAD_LEFTSIDE_OF_ASS))
             return false;
         break;
       default:
-        reportError(NULL, JSMSG_BAD_LEFTSIDE_OF_ASS);
+        report(ParseError, false, null(), JSMSG_BAD_LEFTSIDE_OF_ASS);
         return false;
     }
     return true;
 }
 
-ParseNode *
-Parser::assignExpr()
+template <>
+bool
+Parser<SyntaxParseHandler>::setAssignmentLhsOps(Node pn, JSOp op)
 {
-    JS_CHECK_RECURSION(context, return NULL);
+    /* Full syntax checking of valid assignment LHS terms requires a parse tree. */
+    if (pn != SyntaxParseHandler::NodeName && pn != SyntaxParseHandler::NodeLValue) {
+        setUnknownResult();
+        return false;
+    }
+    return checkStrictAssignment(pn);
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::assignExpr()
+{
+    JS_CHECK_RECURSION(context, return null());
 
 #if JS_HAS_GENERATORS
     if (tokenStream.matchToken(TOK_YIELD, TSF_OPERAND))
         return returnOrYield(true);
 #endif
 
-    ParseNode *lhs = condExpr1();
+    Node lhs = condExpr1();
     if (!lhs)
-        return NULL;
+        return null();
 
     ParseNodeKind kind;
     switch (tokenStream.currentToken().type) {
       case TOK_ASSIGN:       kind = PNK_ASSIGN;       break;
       case TOK_ADDASSIGN:    kind = PNK_ADDASSIGN;    break;
       case TOK_SUBASSIGN:    kind = PNK_SUBASSIGN;    break;
       case TOK_BITORASSIGN:  kind = PNK_BITORASSIGN;  break;
       case TOK_BITXORASSIGN: kind = PNK_BITXORASSIGN; break;
@@ -4549,103 +4793,158 @@ Parser::assignExpr()
       default:
         JS_ASSERT(!tokenStream.isCurrentTokenAssignment());
         tokenStream.ungetToken();
         return lhs;
     }
 
     JSOp op = tokenStream.currentToken().t_op;
     if (!setAssignmentLhsOps(lhs, op))
-        return NULL;
-
-    ParseNode *rhs = assignExpr();
+        return null();
+
+    Node rhs = assignExpr();
     if (!rhs)
-        return NULL;
-
-    return ParseNode::newBinaryOrAppend(kind, op, lhs, rhs, this);
+        return null();
+
+    return handler.newBinaryOrAppend(kind, lhs, rhs, op);
 }
 
-static bool
-SetLvalKid(JSContext *cx, Parser *parser, ParseNode *pn, ParseNode *kid,
-           const char *name)
+template <> bool
+Parser<FullParseHandler>::setLvalKid(ParseNode *pn, ParseNode *kid, const char *name)
 {
     if (!kid->isKind(PNK_NAME) &&
         !kid->isKind(PNK_DOT) &&
         (!kid->isKind(PNK_CALL) ||
          (!kid->isOp(JSOP_CALL) && !kid->isOp(JSOP_EVAL) &&
           !kid->isOp(JSOP_FUNCALL) && !kid->isOp(JSOP_FUNAPPLY))) &&
         !kid->isKind(PNK_ELEM))
     {
-        parser->reportError(NULL, JSMSG_BAD_OPERAND, name);
+        report(ParseError, false, null(), JSMSG_BAD_OPERAND, name);
         return false;
     }
-    if (!CheckStrictAssignment(cx, parser, kid))
+    if (!checkStrictAssignment(kid))
         return false;
     pn->pn_kid = kid;
     return true;
 }
 
 static const char incop_name_str[][10] = {"increment", "decrement"};
 
-static bool
-SetIncOpKid(JSContext *cx, Parser *parser, ParseNode *pn, ParseNode *kid,
-            TokenKind tt, bool preorder)
+template <>
+bool
+Parser<FullParseHandler>::setIncOpKid(ParseNode *pn, ParseNode *kid, TokenKind tt, bool preorder)
 {
     JSOp op;
 
-    if (!SetLvalKid(cx, parser, pn, kid, incop_name_str[tt == TOK_DEC]))
+    if (!setLvalKid(pn, kid, incop_name_str[tt == TOK_DEC]))
         return false;
+
     switch (kid->getKind()) {
       case PNK_NAME:
         op = (tt == TOK_INC)
              ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)
              : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);
-        NoteLValue(kid);
+        handler.noteLValue(kid);
         break;
 
       case PNK_DOT:
         op = (tt == TOK_INC)
              ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)
              : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
         break;
 
       case PNK_CALL:
-        if (!MakeSetCall(cx, kid, parser, JSMSG_BAD_INCOP_OPERAND))
+        if (!makeSetCall(kid, JSMSG_BAD_INCOP_OPERAND))
             return false;
         /* FALL THROUGH */
       case PNK_ELEM:
         op = (tt == TOK_INC)
              ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
              : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
         break;
 
       default:
         JS_ASSERT(0);
         op = JSOP_NOP;
     }
     pn->setOp(op);
     return true;
 }
 
-ParseNode *
-Parser::unaryOpExpr(ParseNodeKind kind, JSOp op)
+template <>
+bool
+Parser<SyntaxParseHandler>::setIncOpKid(Node pn, Node kid, TokenKind tt, bool preorder)
 {
-    TokenPtr begin = tokenStream.currentToken().pos.begin;
-    ParseNode *kid = unaryExpr();
+    return setAssignmentLhsOps(kid, JSOP_NOP);
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::unaryOpExpr(ParseNodeKind kind, JSOp op)
+{
+    Node kid = unaryExpr();
     if (!kid)
-        return NULL;
-    return new_<UnaryNode>(kind, op, TokenPos::make(begin, kid->pn_pos.end), kid);
+        return null();
+    return handler.newUnary(kind, kid, op);
 }
 
-ParseNode *
-Parser::unaryExpr()
+template <>
+bool
+Parser<FullParseHandler>::checkDeleteExpression(ParseNode **pn_)
 {
-    ParseNode *pn, *pn2;
-
-    JS_CHECK_RECURSION(context, return NULL);
+    ParseNode *&pn = *pn_;
+
+    /*
+     * Under ECMA3, deleting any unary expression is valid -- it simply
+     * returns true. Here we fold constants before checking for a call
+     * expression, in order to rule out delete of a generator expression.
+     */
+    if (foldConstants && !FoldConstants(context, &pn, this))
+        return false;
+    switch (pn->getKind()) {
+      case PNK_CALL:
+        if (!(pn->pn_xflags & PNX_SETCALL)) {
+            /*
+             * Call MakeSetCall to check for errors, but clear PNX_SETCALL
+             * because the optimizer will eliminate the useless delete.
+             */
+            if (!makeSetCall(pn, JSMSG_BAD_DELETE_OPERAND))
+                return false;
+            pn->pn_xflags &= ~PNX_SETCALL;
+        }
+        break;
+      case PNK_NAME:
+        if (!report(ParseStrictError, pc->sc->strict, pn, JSMSG_DEPRECATED_DELETE_OPERAND))
+            return null();
+        pc->sc->setBindingsAccessedDynamically();
+        pn->pn_dflags |= PND_DEOPTIMIZED;
+        pn->setOp(JSOP_DELNAME);
+        break;
+      default:;
+    }
+    return true;
+}
+
+template <>
+bool
+Parser<SyntaxParseHandler>::checkDeleteExpression(Node *pn)
+{
+    PropertyName *name = handler.isName(*pn);
+    if (name)
+        return report(ParseStrictError, pc->sc->strict, *pn, JSMSG_DEPRECATED_DELETE_OPERAND);
+    return true;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::unaryExpr()
+{
+    Node pn, pn2;
+
+    JS_CHECK_RECURSION(context, return null());
 
     switch (TokenKind tt = tokenStream.getToken(TSF_OPERAND)) {
       case TOK_TYPEOF:
         return unaryOpExpr(PNK_TYPEOF, JSOP_TYPEOF);
       case TOK_VOID:
         return unaryOpExpr(PNK_VOID, JSOP_VOID);
       case TOK_NOT:
         return unaryOpExpr(PNK_NOT, JSOP_NOT);
@@ -4653,90 +4952,65 @@ Parser::unaryExpr()
         return unaryOpExpr(PNK_BITNOT, JSOP_BITNOT);
       case TOK_PLUS:
         return unaryOpExpr(PNK_POS, JSOP_POS);
       case TOK_MINUS:
         return unaryOpExpr(PNK_NEG, JSOP_NEG);
 
       case TOK_INC:
       case TOK_DEC:
-        pn = UnaryNode::create((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT, this);
-        if (!pn)
-            return NULL;
+      {
+        TokenPtr begin = tokenStream.currentToken().pos.begin;
         pn2 = memberExpr(true);
         if (!pn2)
-            return NULL;
-        if (!SetIncOpKid(context, this, pn, pn2, tt, true))
-            return NULL;
-        pn->pn_pos.end = pn2->pn_pos.end;
+            return null();
+        pn = handler.newUnary((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT, pn2);
+        if (!pn)
+            return null();
+        handler.setBeginPosition(pn, begin);
+        if (!setIncOpKid(pn, pn2, tt, true))
+            return null();
         break;
+      }
 
       case TOK_DELETE:
       {
-        pn = UnaryNode::create(PNK_DELETE, this);
-        if (!pn)
-            return NULL;
+        TokenPtr begin = tokenStream.currentToken().pos.begin;
         pn2 = unaryExpr();
         if (!pn2)
-            return NULL;
-        pn->pn_pos.end = pn2->pn_pos.end;
-
-        /*
-         * Under ECMA3, deleting any unary expression is valid -- it simply
-         * returns true. Here we fold constants before checking for a call
-         * expression, in order to rule out delete of a generator expression.
-         */
-        if (foldConstants && !FoldConstants(context, &pn2, this))
-            return NULL;
-        switch (pn2->getKind()) {
-          case PNK_CALL:
-            if (!(pn2->pn_xflags & PNX_SETCALL)) {
-                /*
-                 * Call MakeSetCall to check for errors, but clear PNX_SETCALL
-                 * because the optimizer will eliminate the useless delete.
-                 */
-                if (!MakeSetCall(context, pn2, this, JSMSG_BAD_DELETE_OPERAND))
-                    return NULL;
-                pn2->pn_xflags &= ~PNX_SETCALL;
-            }
-            break;
-          case PNK_NAME:
-            if (!reportStrictModeError(pn, JSMSG_DEPRECATED_DELETE_OPERAND))
-                return NULL;
-            pc->sc->setBindingsAccessedDynamically();
-            pn2->pn_dflags |= PND_DEOPTIMIZED;
-            pn2->setOp(JSOP_DELNAME);
-            break;
-          default:;
-        }
-        pn->pn_kid = pn2;
+            return null();
+
+        if (!checkDeleteExpression(&pn2))
+            return null();
+
+        pn = handler.newUnary(PNK_DELETE, pn2);
+        if (!pn)
+            return null();
+        handler.setBeginPosition(pn, begin);
         break;
       }
       case TOK_ERROR:
-        return NULL;
+        return null();
 
       default:
         tokenStream.ungetToken();
         pn = memberExpr(true);
         if (!pn)
-            return NULL;
+            return null();
 
         /* Don't look across a newline boundary for a postfix incop. */
-        if (tokenStream.onCurrentLine(pn->pn_pos)) {
-            tt = tokenStream.peekTokenSameLine(TSF_OPERAND);
-            if (tt == TOK_INC || tt == TOK_DEC) {
-                tokenStream.consumeKnownToken(tt);
-                pn2 = UnaryNode::create((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT, this);
-                if (!pn2)
-                    return NULL;
-                if (!SetIncOpKid(context, this, pn2, pn, tt, false))
-                    return NULL;
-                pn2->pn_pos.begin = pn->pn_pos.begin;
-                pn = pn2;
-            }
+        tt = tokenStream.peekTokenSameLine(TSF_OPERAND);
+        if (tt == TOK_INC || tt == TOK_DEC) {
+            tokenStream.consumeKnownToken(tt);
+            pn2 = handler.newUnary((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT, pn);
+            if (!pn2)
+                return null();
+            if (!setIncOpKid(pn2, pn, tt, false))
+                return null();
+            pn = pn2;
         }
         break;
     }
     return pn;
 }
 
 #if JS_HAS_GENERATORS
 
@@ -4756,25 +5030,26 @@ Parser::unaryExpr()
  *
  * so the transplanter must adjust static level as well as blockid. E's source
  * coordinates in root->pn_pos are critical to deciding which binding links to
  * preserve and which to cut.
  *
  * NB: This is not a general tree transplanter -- it knows in particular that
  * the one or more bindings induced by V have not yet been created.
  */
-class CompExprTransplanter {
+class CompExprTransplanter
+{
     ParseNode       *root;
-    Parser          *parser;
+    Parser<FullParseHandler> *parser;
     bool            genexp;
     unsigned        adjust;
     HashSet<Definition *> visitedImplicitArguments;
 
   public:
-    CompExprTransplanter(ParseNode *pn, Parser *parser, bool ge, unsigned adj)
+    CompExprTransplanter(ParseNode *pn, Parser<FullParseHandler> *parser, bool ge, unsigned adj)
       : root(pn), parser(parser), genexp(ge), adjust(adj),
         visitedImplicitArguments(parser->context)
     {}
 
     bool init() {
         return visitedImplicitArguments.init();
     }
 
@@ -4793,127 +5068,137 @@ class CompExprTransplanter {
  *
  * The guard will keep track of any |yield| or |arguments| tokens that occur while
  * parsing the body. As soon as the parser reaches the end of the body expression,
  * call endBody() to reset the context's state, and then immediately call:
  *
  * - checkValidBody() if this *did* turn out to be a generator expression
  * - maybeNoteGenerator() if this *did not* turn out to be a generator expression
  */
-class GenexpGuard {
-    Parser          *parser;
-    uint32_t        startYieldCount;
+template <typename ParseHandler>
+class GenexpGuard
+{
+    Parser<ParseHandler> *parser;
+    uint32_t startYieldCount;
+
+    typedef typename ParseHandler::Node Node;
 
   public:
-    explicit GenexpGuard(Parser *parser)
+    explicit GenexpGuard(Parser<ParseHandler> *parser)
       : parser(parser)
     {
-        ParseContext *pc = parser->pc;
+        ParseContext<ParseHandler> *pc = parser->pc;
         if (pc->parenDepth == 0) {
             pc->yieldCount = 0;
-            pc->yieldNode = NULL;
+            pc->yieldNode = ParseHandler::null();
         }
         startYieldCount = pc->yieldCount;
         pc->parenDepth++;
     }
 
     void endBody();
-    bool checkValidBody(ParseNode *pn, unsigned err);
-    bool maybeNoteGenerator(ParseNode *pn);
+    bool checkValidBody(Node pn, unsigned err = JSMSG_BAD_GENEXP_BODY);
+    bool maybeNoteGenerator(Node pn);
 };
 
+template <typename ParseHandler>
 void
-GenexpGuard::endBody()
+GenexpGuard<ParseHandler>::endBody()
 {
     parser->pc->parenDepth--;
 }
 
 /*
  * Check whether a |yield| or |arguments| token has been encountered in the
  * body expression, and if so, report an error.
  *
  * Call this after endBody() when determining that the body *was* in a
  * generator expression.
  */
+template <typename ParseHandler>
 bool
-GenexpGuard::checkValidBody(ParseNode *pn, unsigned err = JSMSG_BAD_GENEXP_BODY)
+GenexpGuard<ParseHandler>::checkValidBody(Node pn, unsigned err)
 {
-    ParseContext *pc = parser->pc;
+    ParseContext<ParseHandler> *pc = parser->pc;
     if (pc->yieldCount > startYieldCount) {
-        ParseNode *errorNode = pc->yieldNode;
+        Node errorNode = pc->yieldNode;
         if (!errorNode)
             errorNode = pn;
-        parser->reportError(errorNode, err, js_yield_str);
+        parser->report(ParseError, false, errorNode, err, js_yield_str);
         return false;
     }
 
     return true;
 }
 
 /*
  * Check whether a |yield| token has been encountered in the body expression,
  * and if so, note that the current function is a generator function.
  *
  * Call this after endBody() when determining that the body *was not* in a
  * generator expression.
  */
+template <typename ParseHandler>
 bool
-GenexpGuard::maybeNoteGenerator(ParseNode *pn)
+GenexpGuard<ParseHandler>::maybeNoteGenerator(Node pn)
 {
-    ParseContext *pc = parser->pc;
+    ParseContext<ParseHandler> *pc = parser->pc;
     if (pc->yieldCount > 0) {
         if (!pc->sc->isFunctionBox()) {
-            parser->reportError(NULL, JSMSG_BAD_RETURN_OR_YIELD, js_yield_str);
+            parser->report(ParseError, false, ParseHandler::null(),
+                           JSMSG_BAD_RETURN_OR_YIELD, js_yield_str);
             return false;
         }
         pc->sc->asFunctionBox()->setIsGenerator();
         if (pc->funHasReturnExpr) {
             /* At the time we saw the yield, we might not have set isGenerator yet. */
-            ReportBadReturn(pc->sc->context, parser, pn, &Parser::reportError,
-                            JSMSG_BAD_GENERATOR_RETURN, JSMSG_BAD_ANON_GENERATOR_RETURN);
+            parser->reportBadReturn(pn, ParseError,
+                                    JSMSG_BAD_GENERATOR_RETURN, JSMSG_BAD_ANON_GENERATOR_RETURN);
             return false;
         }
     }
     return true;
 }
 
 /*
  * Any definitions nested within the comprehension expression of a generator
  * expression must move "down" one static level, which of course increases the
  * upvar-frame-skip count.
  */
+template <typename ParseHandler>
 static bool
-BumpStaticLevel(ParseNode *pn, ParseContext *pc)
+BumpStaticLevel(ParseNode *pn, ParseContext<ParseHandler> *pc)
 {
     if (pn->pn_cookie.isFree())
         return true;
 
     unsigned level = unsigned(pn->pn_cookie.level()) + 1;
     JS_ASSERT(level >= pc->staticLevel);
     return pn->pn_cookie.set(pc->sc->context, level, pn->pn_cookie.slot());
 }
 
+template <typename ParseHandler>
 static bool
-AdjustBlockId(ParseNode *pn, unsigned adjust, ParseContext *pc)
+AdjustBlockId(ParseNode *pn, unsigned adjust, ParseContext<ParseHandler> *pc)
 {
     JS_ASSERT(pn->isArity(PN_LIST) || pn->isArity(PN_CODE) || pn->isArity(PN_NAME));
     if (JS_BIT(20) - pn->pn_blockid <= adjust + 1) {
         JS_ReportErrorNumber(pc->sc->context, js_GetErrorMessage, NULL, JSMSG_NEED_DIET, "program");
         return false;
     }
     pn->pn_blockid += adjust;
     if (pn->pn_blockid >= pc->blockidGen)
         pc->blockidGen = pn->pn_blockid + 1;
     return true;
 }
 
 bool
 CompExprTransplanter::transplant(ParseNode *pn)
 {
-    ParseContext *pc = parser->pc;
+    ParseContext<FullParseHandler> *pc = parser->pc;
 
     if (!pn)
         return true;
 
     switch (pn->getArity()) {
       case PN_LIST:
         for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
             if (!transplant(pn2))
@@ -4990,17 +5275,17 @@ CompExprTransplanter::transplant(ParseNo
                     /*
                      * The variable originally appeared to be a use of a
                      * definition or placeholder outside the generator, but now
                      * we know it is scoped within the comprehension tail's
                      * clauses. Make it (along with any other uses within the
                      * generator) a use of a new placeholder in the generator's
                      * lexdeps.
                      */
-                    Definition *dn2 = MakePlaceholder(pn, parser, parser->pc);
+                    Definition *dn2 = MakePlaceholder(pn, &parser->handler, parser->pc);
                     if (!dn2)
                         return false;
                     dn2->pn_pos = root->pn_pos;
 
                     /*
                      * Change all uses of |dn| that lie within the generator's
                      * |yield| expression into uses of dn2.
                      */
@@ -5064,37 +5349,38 @@ CompExprTransplanter::transplant(ParseNo
  * Starting from a |for| keyword after the first array initialiser element or
  * an expression in an open parenthesis, parse the tail of the comprehension
  * or generator expression signified by this |for| keyword in context.
  *
  * Return null on failure, else return the top-most parse node for the array
  * comprehension or generator expression, with a unary node as the body of the
  * (possibly nested) for-loop, initialized by |kind, op, kid|.
  */
+template <>
 ParseNode *
-Parser::comprehensionTail(ParseNode *kid, unsigned blockid, bool isGenexp,
-                          ParseNodeKind kind, JSOp op)
+Parser<FullParseHandler>::comprehensionTail(ParseNode *kid, unsigned blockid, bool isGenexp,
+                                            ParseNodeKind kind, JSOp op)
 {
     unsigned adjust;
     ParseNode *pn, *pn2, *pn3, **pnp;
     StmtInfoPC stmtInfo(context);
-    BindData data(context);
+    BindData<FullParseHandler> data(context);
     TokenKind tt;
 
     JS_ASSERT(tokenStream.currentToken().type == TOK_FOR);
 
     if (kind == PNK_SEMI) {
         /*
          * Generator expression desugars to an immediately applied lambda that
          * yields the next value from a for-in loop (possibly nested, and with
          * optional if guard). Make pn be the TOK_LC body node.
          */
-        pn = PushLexicalScope(context, this, &stmtInfo);
+        pn = pushLexicalScope(&stmtInfo);
         if (!pn)
-            return NULL;
+            return null();
         adjust = pn->pn_blockid - blockid;
     } else {
         JS_ASSERT(kind == PNK_ARRAYPUSH);
 
         /*
          * Make a parse-node and literal object representing the block scope of
          * this array comprehension. Our caller in primaryExpr, the TOK_LB case
          * aka the array initialiser case, has passed the blockid to claim for
@@ -5102,207 +5388,241 @@ Parser::comprehensionTail(ParseNode *kid
          * here, by calling PushLexicalScope.
          *
          * In the case of a comprehension expression that has nested blocks
          * (e.g., let expressions), we will allocate a higher blockid but then
          * slide all blocks "to the right" to make room for the comprehension's
          * block scope.
          */
         adjust = pc->blockid();
-        pn = PushLexicalScope(context, this, &stmtInfo);
+        pn = pushLexicalScope(&stmtInfo);
         if (!pn)
-            return NULL;
+            return null();
 
         JS_ASSERT(blockid <= pn->pn_blockid);
         JS_ASSERT(blockid < pc->blockidGen);
         JS_ASSERT(pc->bodyid < blockid);
         pn->pn_blockid = stmtInfo.blockid = blockid;
         JS_ASSERT(adjust < blockid);
         adjust = blockid - adjust;
     }
 
     pnp = &pn->pn_expr;
 
     CompExprTransplanter transplanter(kid, this, kind == PNK_SEMI, adjust);
     if (!transplanter.init())
-        return NULL;
+        return null();
 
     if (!transplanter.transplant(kid))
-        return NULL;
+        return null();
 
     JS_ASSERT(pc->blockChain && pc->blockChain == pn->pn_objbox->object);
     data.initLet(HoistVars, *pc->blockChain, JSMSG_ARRAY_INIT_TOO_BIG);
 
     do {
         /*
          * FOR node is binary, left is loop control and right is body.  Use
          * index to count each block-local let-variable on the left-hand side
          * of the in/of.
          */
-        pn2 = BinaryNode::create(PNK_FOR, this);
+        pn2 = BinaryNode::create(PNK_FOR, &handler);
         if (!pn2)
-            return NULL;
+            return null();
 
         pn2->setOp(JSOP_ITER);
         pn2->pn_iflags = JSITER_ENUMERATE;
         if (allowsForEachIn() && tokenStream.matchToken(TOK_NAME)) {
             if (tokenStream.currentToken().name() == context->names().each)
                 pn2->pn_iflags |= JSITER_FOREACH;
             else
                 tokenStream.ungetToken();
         }
         MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
 
-        GenexpGuard guard(this);
+        GenexpGuard<FullParseHandler> guard(this);
 
         RootedPropertyName name(context);
         tt = tokenStream.getToken();
         switch (tt) {
 #if JS_HAS_DESTRUCTURING
           case TOK_LB:
           case TOK_LC:
             pc->inDeclDestructuring = true;
             pn3 = primaryExpr(tt);
             pc->inDeclDestructuring = false;
             if (!pn3)
-                return NULL;
+                return null();
             break;
 #endif
 
           case TOK_NAME:
             name = tokenStream.currentToken().name();
 
             /*
              * Create a name node with pn_op JSOP_NAME.  We can't set pn_op to
              * JSOP_GETLOCAL here, because we don't yet know the block's depth
              * in the operand stack frame.  The code generator computes that,
              * and it tries to bind all names to slots, so we must let it do
              * the deed.
              */
-            pn3 = NewBindingNode(name, this);
+            pn3 = newBindingNode(name);
             if (!pn3)
-                return NULL;
+                return null();
             break;
 
           default:
-            reportError(NULL, JSMSG_NO_VARIABLE_NAME);
+            report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
 
           case TOK_ERROR:
-            return NULL;
+            return null();
         }
 
         bool forOf;
         if (!matchInOrOf(&forOf)) {
-            reportError(NULL, JSMSG_IN_AFTER_FOR_NAME);
-            return NULL;
+            report(ParseError, false, null(), JSMSG_IN_AFTER_FOR_NAME);
+            return null();
         }
         if (forOf) {
             if (pn2->pn_iflags != JSITER_ENUMERATE) {
                 JS_ASSERT(pn2->pn_iflags == (JSITER_FOREACH | JSITER_ENUMERATE));
-                reportError(NULL, JSMSG_BAD_FOR_EACH_LOOP);
-                return NULL;
+                report(ParseError, false, null(), JSMSG_BAD_FOR_EACH_LOOP);
+                return null();
             }
             pn2->pn_iflags = JSITER_FOR_OF;
         }
 
         ParseNode *pn4 = expr();
         if (!pn4)
-            return NULL;
+            return null();
         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
 
         guard.endBody();
 
         if (isGenexp) {
             if (!guard.checkValidBody(pn2))
-                return NULL;
+                return null();
         } else {
             if (!guard.maybeNoteGenerator(pn2))
-                return NULL;
+                return null();
         }
 
         switch (tt) {
 #if JS_HAS_DESTRUCTURING
           case TOK_LB:
           case TOK_LC:
-            if (!CheckDestructuring(context, &data, pn3, this))
-                return NULL;
+            if (!checkDestructuring(&data, pn3))
+                return null();
 
             if (versionNumber() == JSVERSION_1_7 &&
                 !(pn2->pn_iflags & JSITER_FOREACH) &&
                 !forOf)
             {
                 /* Destructuring requires [key, value] enumeration in JS1.7. */
                 if (!pn3->isKind(PNK_ARRAY) || pn3->pn_count != 2) {
-                    reportError(NULL, JSMSG_BAD_FOR_LEFTSIDE);
-                    return NULL;
+                    report(ParseError, false, null(), JSMSG_BAD_FOR_LEFTSIDE);
+                    return null();
                 }
 
                 JS_ASSERT(pn2->isOp(JSOP_ITER));
                 JS_ASSERT(pn2->pn_iflags & JSITER_ENUMERATE);
                 pn2->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
             }
             break;
 #endif
 
           case TOK_NAME:
             data.pn = pn3;
             if (!data.binder(context, &data, name, this))
-                return NULL;
+                return null();
             break;
 
           default:;
         }
 
         /*
          * Synthesize a declaration. Every definition must appear in the parse
          * tree in order for ComprehensionTranslator to work.
          */
-        ParseNode *vars = ListNode::create(PNK_VAR, this);
+        ParseNode *vars = ListNode::create(PNK_VAR, &handler);
         if (!vars)
-            return NULL;
+            return null();
         vars->setOp(JSOP_NOP);
         vars->pn_pos = pn3->pn_pos;
         vars->makeEmpty();
         vars->append(pn3);
         vars->pn_xflags |= PNX_FORINVAR;
 
         /* Definitions can't be passed directly to EmitAssignment as lhs. */
-        pn3 = CloneLeftHandSide(pn3, this);
+        pn3 = cloneLeftHandSide(pn3);
         if (!pn3)
-            return NULL;
-
-        pn2->pn_left = new_<TernaryNode>(PNK_FORIN, JSOP_NOP, vars, pn3, pn4);
+            return null();
+
+        pn2->pn_left = handler.newTernary(PNK_FORIN, vars, pn3, pn4);
         if (!pn2->pn_left)
-            return NULL;
+            return null();
         *pnp = pn2;
         pnp = &pn2->pn_right;
     } while (tokenStream.matchToken(TOK_FOR));
 
     if (tokenStream.matchToken(TOK_IF)) {
-        pn2 = TernaryNode::create(PNK_IF, this);
+        pn2 = TernaryNode::create(PNK_IF, &handler);
         if (!pn2)
-            return NULL;
+            return null();
         pn2->pn_kid1 = condition();
         if (!pn2->pn_kid1)
-            return NULL;
+            return null();
         *pnp = pn2;
         pnp = &pn2->pn_kid2;
     }
 
-    pn2 = UnaryNode::create(kind, this);
+    pn2 = UnaryNode::create(kind, &handler);
     if (!pn2)
-        return NULL;
+        return null();
     pn2->setOp(op);
     pn2->pn_kid = kid;
     *pnp = pn2;
 
     PopStatementPC(context, pc);
     return pn;
 }
 
+template <>
+bool
+Parser<FullParseHandler>::arrayInitializerComprehensionTail(ParseNode *pn)
+{
+    /* Relabel pn as an array comprehension node. */
+    pn->setKind(PNK_ARRAYCOMP);
+
+    /*
+     * Remove the comprehension expression from pn's linked list
+     * and save it via pnexp.  We'll re-install it underneath the
+     * ARRAYPUSH node after we parse the rest of the comprehension.
+     */
+    ParseNode *pnexp = pn->last();
+    JS_ASSERT(pn->pn_count == 1);
+    pn->pn_count = 0;
+    pn->pn_tail = &pn->pn_head;
+    *pn->pn_tail = NULL;
+
+    ParseNode *pntop = comprehensionTail(pnexp, pn->pn_blockid, false,
+                                         PNK_ARRAYPUSH, JSOP_ARRAYPUSH);
+    if (!pntop)
+        return false;
+    pn->append(pntop);
+    return true;
+}
+
+template <>
+bool
+Parser<SyntaxParseHandler>::arrayInitializerComprehensionTail(Node pn)
+{
+    setUnknownResult();
+    return false;
+}
+
 #if JS_HAS_GENERATOR_EXPRS
 
 /*
  * Starting from a |for| keyword after an expression, parse the comprehension
  * tail completing this generator expression. Wrap the expression at kid in a
  * generator function that is immediately called to evaluate to the generator
  * iterator that is the value of this generator expression.
  *
@@ -5310,54 +5630,55 @@ Parser::comprehensionTail(ParseNode *kid
  * application of a generator function that includes the |for| loops and
  * |if| guards, with |kid| as the operand of a |yield| expression as the
  * innermost loop body.
  *
  * Note how unlike Python, we do not evaluate the expression to the right of
  * the first |in| in the chain of |for| heads. Instead, a generator expression
  * is merely sugar for a generator function expression and its application.
  */
+template <>
 ParseNode *
-Parser::generatorExpr(ParseNode *kid)
+Parser<FullParseHandler>::generatorExpr(ParseNode *kid)
 {
     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
 
     /* Create a |yield| node for |kid|. */
-    ParseNode *pn = UnaryNode::create(PNK_YIELD, this);
+    ParseNode *pn = UnaryNode::create(PNK_YIELD, &handler);
     if (!pn)
-        return NULL;
+        return null();
     pn->setOp(JSOP_YIELD);
     pn->setInParens(true);
     pn->pn_pos = kid->pn_pos;
     pn->pn_kid = kid;
     pn->pn_hidden = true;
 
     /* Make a new node for the desugared generator function. */
-    ParseNode *genfn = CodeNode::create(PNK_FUNCTION, this);
+    ParseNode *genfn = CodeNode::create(PNK_FUNCTION, &handler);
     if (!genfn)
-        return NULL;
+        return null();
     genfn->setOp(JSOP_LAMBDA);
     JS_ASSERT(!genfn->pn_body);
     genfn->pn_dflags = 0;
 
     {
-        ParseContext *outerpc = pc;
+        ParseContext<FullParseHandler> *outerpc = pc;
 
         RootedFunction fun(context, newFunction(outerpc, /* atom = */ NullPtr(), Expression));
         if (!fun)
-            return NULL;
+            return null();
 
         /* Create box for fun->object early to protect against last-ditch GC. */
         FunctionBox *genFunbox = newFunctionBox(fun, outerpc, outerpc->sc->strict);
         if (!genFunbox)
-            return NULL;
-
-        ParseContext genpc(this, genFunbox, outerpc->staticLevel + 1, outerpc->blockidGen);
+            return null();
+
+        ParseContext<FullParseHandler> genpc(this, genFunbox, outerpc->staticLevel + 1, outerpc->blockidGen);
         if (!genpc.init())
-            return NULL;
+            return null();
 
         /*
          * We assume conservatively that any deoptimization flags in pc->sc
          * come from the kid. So we propagate these flags into genfn. For code
          * simplicity we also do not detect if the flags were only set in the
          * kid and could be removed from pc->sc.
          */
         genFunbox->anyCxFlags = outerpc->sc->anyCxFlags;
@@ -5366,417 +5687,468 @@ Parser::generatorExpr(ParseNode *kid)
 
         genFunbox->setIsGenerator();
         genFunbox->inGenexpLambda = true;
         genfn->pn_funbox = genFunbox;
         genfn->pn_blockid = genpc.bodyid;
 
         ParseNode *body = comprehensionTail(pn, outerpc->blockid(), true);
         if (!body)
-            return NULL;
+            return null();
         JS_ASSERT(!genfn->pn_body);
         genfn->pn_body = body;
         genfn->pn_pos.begin = body->pn_pos.begin = kid->pn_pos.begin;
         genfn->pn_pos.end = body->pn_pos.end = tokenStream.currentToken().pos.end;
 
         if (AtomDefnPtr p = genpc.lexdeps->lookup(context->names().arguments)) {
             Definition *dn = p.value();
             ParseNode *errorNode = dn->dn_uses ? dn->dn_uses : body;
-            reportError(errorNode, JSMSG_BAD_GENEXP_BODY, js_arguments_str);
-            return NULL;
+            report(ParseError, false, errorNode, JSMSG_BAD_GENEXP_BODY, js_arguments_str);
+            return null();
         }
 
         RootedPropertyName funName(context);
-        if (!LeaveFunction(genfn, this, funName))
-            return NULL;
+        if (!leaveFunction(genfn, funName))
+            return null();
     }
 
     /*
      * Our result is a call expression that invokes the anonymous generator
      * function object.
      */
-    ParseNode *result = ListNode::create(PNK_GENEXP, this);
+    ParseNode *result = ListNode::create(PNK_GENEXP, &handler);
     if (!result)
-        return NULL;
+        return null();
     result->setOp(JSOP_CALL);
     result->pn_pos.begin = genfn->pn_pos.begin;
     result->initList(genfn);
     return result;
 }
 
+template <>
+SyntaxParseHandler::Node
+Parser<SyntaxParseHandler>::generatorExpr(Node kid)
+{
+    setUnknownResult();
+    return SyntaxParseHandler::NodeFailure;
+}
+
 static const char js_generator_str[] = "generator";
 
 #endif /* JS_HAS_GENERATOR_EXPRS */
 #endif /* JS_HAS_GENERATORS */
 
-ParseNode *
-Parser::assignExprWithoutYield(unsigned msg)
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::assignExprWithoutYield(unsigned msg)
 {
 #ifdef JS_HAS_GENERATORS
-    GenexpGuard yieldGuard(this);
+    GenexpGuard<ParseHandler> yieldGuard(this);
 #endif
-    ParseNode *res = assignExpr();
+    Node res = assignExpr();
     yieldGuard.endBody();
     if (res) {
 #ifdef JS_HAS_GENERATORS
-        if (!yieldGuard.checkValidBody(res, msg)) {
-            freeTree(res);
-            res = NULL;
-        }
+        if (!yieldGuard.checkValidBody(res, msg))
+            return null();
 #endif
     }
     return res;
 }
 
+template <typename ParseHandler>
 bool
-Parser::argumentList(ParseNode *listNode)
+Parser<ParseHandler>::argumentList(Node listNode)
 {
     if (tokenStream.matchToken(TOK_RP, TSF_OPERAND))
         return true;
 
-    GenexpGuard guard(this);
+    GenexpGuard<ParseHandler> guard(this);
     bool arg0 = true;
 
     do {
-        ParseNode *argNode = assignExpr();
+        Node argNode = assignExpr();
         if (!argNode)
             return false;
         if (arg0)
             guard.endBody();
 
 #if JS_HAS_GENERATORS
-        if (argNode->isKind(PNK_YIELD) &&
-            !argNode->isInParens() &&
+        if (handler.isOperationWithoutParens(argNode, PNK_YIELD) &&
             tokenStream.peekToken() == TOK_COMMA) {
-            reportError(argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
+            report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
             return false;
         }
 #endif
 #if JS_HAS_GENERATOR_EXPRS
         if (tokenStream.matchToken(TOK_FOR)) {
             if (!guard.checkValidBody(argNode))
                 return false;
             argNode = generatorExpr(argNode);
             if (!argNode)
                 return false;
-            if (listNode->pn_count > 1 ||
-                tokenStream.peekToken() == TOK_COMMA) {
-                reportError(argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
+            if (!arg0 || tokenStream.peekToken() == TOK_COMMA) {
+                report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
                 return false;
             }
         } else
 #endif
         if (arg0 && !guard.maybeNoteGenerator(argNode))
             return false;
 
         arg0 = false;
 
-        listNode->append(argNode);
+        handler.addList(listNode, argNode);
     } while (tokenStream.matchToken(TOK_COMMA));
 
     if (tokenStream.getToken() != TOK_RP) {
-        reportError(NULL, JSMSG_PAREN_AFTER_ARGS);
+        report(ParseError, false, null(), JSMSG_PAREN_AFTER_ARGS);
         return false;
     }
     return true;
 }
 
-ParseNode *
-Parser::memberExpr(bool allowCallSyntax)
+template <>
+PropertyName *
+Parser<FullParseHandler>::foldPropertyByValue(ParseNode *pn)
 {
-    ParseNode *lhs;
-
-    JS_CHECK_RECURSION(context, return NULL);
+    /*
+     * Optimize property name lookups. If the name is a PropertyName,
+     * then make a name-based node so the emitter will use a name-based
+     * bytecode. Otherwise make a node using the property expression
+     * by value. If the node is a string containing an index, convert
+     * it to a number to save work later.
+     */
+
+    uint32_t index;
+    if (foldConstants) {
+        if (pn->isKind(PNK_STRING)) {
+            JSAtom *atom = pn->pn_atom;
+            if (atom->isIndex(&index)) {
+                pn->setKind(PNK_NUMBER);
+                pn->setOp(JSOP_DOUBLE);
+                pn->pn_dval = index;
+            } else {
+                return atom->asPropertyName();
+            }
+        } else if (pn->isKind(PNK_NUMBER)) {
+            double number = pn->pn_dval;
+            if (number != ToUint32(number)) {
+                JSAtom *atom = ToAtom<NoGC>(context, DoubleValue(number));
+                if (!atom)
+                    return NULL;
+                return atom->asPropertyName();
+            }
+        }
+    }
+
+    return NULL;
+}
+
+template <>
+PropertyName *
+Parser<SyntaxParseHandler>::foldPropertyByValue(Node pn)
+{
+    return NULL;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::memberExpr(bool allowCallSyntax)
+{
+    Node lhs;
+
+    JS_CHECK_RECURSION(context, return null());
 
     /* Check for new expression first. */
     TokenKind tt = tokenStream.getToken(TSF_OPERAND);
     if (tt == TOK_NEW) {
-        lhs = ListNode::create(PNK_NEW, this);
+        lhs = handler.newList(PNK_NEW, null(), JSOP_NEW);
         if (!lhs)
-            return NULL;
-        ParseNode *ctorExpr = memberExpr(false);
+            return null();
+
+        Node ctorExpr = memberExpr(false);
         if (!ctorExpr)
-            return NULL;
-        lhs->setOp(JSOP_NEW);
-        lhs->initList(ctorExpr);
-        lhs->pn_pos.begin = ctorExpr->pn_pos.begin;
+            return null();
+
+        handler.addList(lhs, ctorExpr);
 
         if (tokenStream.matchToken(TOK_LP) && !argumentList(lhs))
-            return NULL;
-        if (lhs->pn_count > ARGC_LIMIT) {
-            JS_ReportErrorNumber(context, js_GetErrorMessage, NULL,
-                                 JSMSG_TOO_MANY_CON_ARGS);
-            return NULL;
-        }
-        lhs->pn_pos.end = lhs->last()->pn_pos.end;
+            return null();
     } else {
         lhs = primaryExpr(tt);
         if (!lhs)
-            return NULL;
+            return null();
     }
 
     while ((tt = tokenStream.getToken()) > TOK_EOF) {
-        ParseNode *nextMember;
+        Node nextMember;
         if (tt == TOK_DOT) {
             tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
             if (tt == TOK_ERROR)
-                return NULL;
+                return null();
             if (tt == TOK_NAME) {
                 PropertyName *field = tokenStream.currentToken().name();
-                nextMember = new_<PropertyAccess>(lhs, field,
-                                                  lhs->pn_pos.begin,
-                                                  tokenStream.currentToken().pos.end);
+                TokenPtr end = tokenStream.currentToken().pos.end;
+                nextMember = handler.newPropertyAccess(lhs, field, end);
                 if (!nextMember)
-                    return NULL;
+                    return null();
             } else {
-                reportError(NULL, JSMSG_NAME_AFTER_DOT);
-                return NULL;
+                report(ParseError, false, null(), JSMSG_NAME_AFTER_DOT);
+                return null();
             }
         } else if (tt == TOK_LB) {
-            ParseNode *propExpr = expr();
+            Node propExpr = expr();
             if (!propExpr)
-                return NULL;
+                return null();
 
             MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
-            TokenPtr begin = lhs->pn_pos.begin, end = tokenStream.currentToken().pos.end;
 
             /*
              * Do folding so we don't have roundtrip changes for cases like:
              * function (obj) { return obj["a" + "b"] }
              */
             if (foldConstants && !FoldConstants(context, &propExpr, this))
-                return NULL;
-
-            /*
-             * Optimize property name lookups. If the name is a PropertyName,
-             * then make a name-based node so the emitter will use a name-based
-             * bytecode. Otherwise make a node using the property expression
-             * by value. If the node is a string containing an index, convert
-             * it to a number to save work later.
-             */
-            uint32_t index;
-            PropertyName *name = NULL;
-            if (foldConstants) {
-                if (propExpr->isKind(PNK_STRING)) {
-                    JSAtom *atom = propExpr->pn_atom;
-                    if (atom->isIndex(&index)) {
-                        propExpr->setKind(PNK_NUMBER);
-                        propExpr->setOp(JSOP_DOUBLE);
-                        propExpr->pn_dval = index;
-                    } else {
-                        name = atom->asPropertyName();
-                    }
-                } else if (propExpr->isKind(PNK_NUMBER)) {
-                    double number = propExpr->pn_dval;
-                    if (number != ToUint32(number)) {
-                        JSAtom *atom = ToAtom<CanGC>(context, DoubleValue(number));
-                        if (!atom)
-                            return NULL;
-                        name = atom->asPropertyName();
-                    }
-                }
-            }
-
+                return null();
+
+            PropertyName *name = foldPropertyByValue(propExpr);
+
+            TokenPtr end = tokenStream.currentToken().pos.end;
             if (name)
-                nextMember = new_<PropertyAccess>(lhs, name, begin, end);
+                nextMember = handler.newPropertyAccess(lhs, name, end);
             else
-                nextMember = new_<PropertyByValue>(lhs, propExpr, begin, end);
-            if (!nextMember)
-                return NULL;
-        } else if (allowCallSyntax && tt == TOK_LP) {
-            nextMember = ListNode::create(PNK_CALL, this);
+                nextMember = handler.newPropertyByValue(lhs, propExpr, end);
             if (!nextMember)
-                return NULL;
-            nextMember->setOp(JSOP_CALL);
-
-            if (lhs->isOp(JSOP_NAME)) {
-                if (lhs->pn_atom == context->names().eval) {
+                return null();
+        } else if (allowCallSyntax && tt == TOK_LP) {
+            nextMember = handler.newList(PNK_CALL, null(), JSOP_CALL);
+            if (!nextMember)
+                return null();
+
+            if (JSAtom *atom = handler.isName(lhs)) {
+                if (atom == context->names().eval) {
                     /* Select JSOP_EVAL and flag pc as heavyweight. */
-                    nextMember->setOp(JSOP_EVAL);
+                    handler.setOp(nextMember, JSOP_EVAL);
                     pc->sc->setBindingsAccessedDynamically();
 
                     /*
                      * In non-strict mode code, direct calls to eval can add
                      * variables to the call object.
                      */
                     if (pc->sc->isFunctionBox() && !pc->sc->strict)
                         pc->sc->asFunctionBox()->setHasExtensibleScope();
                 }
-            } else if (lhs->isOp(JSOP_GETPROP)) {
+            } else if (JSAtom *atom = handler.isGetProp(lhs)) {
                 /* Select JSOP_FUNAPPLY given foo.apply(...). */
-                if (lhs->pn_atom == context->names().apply)
-                    nextMember->setOp(JSOP_FUNAPPLY);
-                else if (lhs->pn_atom == context->names().call)
-                    nextMember->setOp(JSOP_FUNCALL);
+                if (atom == context->names().apply)
+                    handler.setOp(nextMember, JSOP_FUNAPPLY);
+                else if (atom == context->names().call)
+                    handler.setOp(nextMember, JSOP_FUNCALL);
             }
 
-            nextMember->initList(lhs);
-            nextMember->pn_pos.begin = lhs->pn_pos.begin;
+            handler.setBeginPosition(nextMember, lhs);
+            handler.addList(nextMember, lhs);
 
             if (!argumentList(nextMember))
-                return NULL;
-            if (nextMember->pn_count > ARGC_LIMIT) {
-                JS_ReportErrorNumber(context, js_GetErrorMessage, NULL,
-                                     JSMSG_TOO_MANY_FUN_ARGS);
-                return NULL;
-            }
-            nextMember->pn_pos.end = tokenStream.currentToken().pos.end;
+                return null();
         } else {
             tokenStream.ungetToken();
             return lhs;
         }
 
         lhs = nextMember;
     }
     if (tt == TOK_ERROR)
-        return NULL;
+        return null();
     return lhs;
 }
 
-ParseNode *
-Parser::bracketedExpr()
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::bracketedExpr()
 {
     /*
      * Always accept the 'in' operator in a parenthesized expression,
      * where it's unambiguous, even if we might be parsing the init of a
      * for statement.
      */
     bool oldParsingForInit = pc->parsingForInit;
     pc->parsingForInit = false;
-    ParseNode *pn = expr();
+    Node pn = expr();
     pc->parsingForInit = oldParsingForInit;
     return pn;
 }
 
-ParseNode *
-Parser::identifierName()
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::identifierName()
 {
     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME));
 
     PropertyName *name = tokenStream.currentToken().name();
-    ParseNode *node = NameNode::create(PNK_NAME, name, this, this->pc);
-    if (!node)
-        return NULL;
-    JS_ASSERT(tokenStream.currentToken().t_op == JSOP_NAME);
-    node->setOp(JSOP_NAME);
-
-    if (!pc->inDeclDestructuring && !NoteNameUse(node, this))
-        return NULL;
-
-    return node;
+    Node pn = handler.newName(name, pc);
+    if (!pn)
+        return null();
+
+    if (!pc->inDeclDestructuring && !noteNameUse(pn))
+        return null();
+
+    return pn;
 }
 
-ParseNode *
-Parser::atomNode(ParseNodeKind kind, JSOp op)
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::atomNode(ParseNodeKind kind, JSOp op)
 {
-    ParseNode *node = NullaryNode::create(kind, this);
-    if (!node)
-        return NULL;
-    node->setOp(op);
-    const Token &tok = tokenStream.currentToken();
-    node->pn_atom = tok.atom();
+    JSAtom *atom = tokenStream.currentToken().atom();
+    Node pn = handler.newAtom(kind, atom, op);
+    if (!pn)
+        return null();
 
     // Large strings are fast to parse but slow to compress. Stop compression on
     // them, so we don't wait for a long time for compression to finish at the
     // end of compilation.
     const size_t HUGE_STRING = 50000;
-    if (sct && sct->active() && kind == PNK_STRING && node->pn_atom->length() >= HUGE_STRING)
+    if (sct && sct->active() && kind == PNK_STRING && atom->length() >= HUGE_STRING)
         sct->abort();
 
-    return node;
+    return pn;
 }
 
+template <>
 ParseNode *
-Parser::primaryExpr(TokenKind tt)
+Parser<FullParseHandler>::newRegExp(const jschar *buf, size_t length, RegExpFlag flags)
+{
+    ParseNode *pn = NullaryNode::create(PNK_REGEXP, &handler);
+    if (!pn)
+        return NULL;
+
+    const StableCharPtr chars(buf, length);
+    RegExpStatics *res = context->regExpStatics();
+
+    Rooted<RegExpObject*> reobj(context);
+    if (context->hasfp())
+        reobj = RegExpObject::create(context, res, chars.get(), length, flags, &tokenStream);
+    else
+        reobj = RegExpObject::createNoStatics(context, chars.get(), length, flags, &tokenStream);
+
+    if (!reobj)
+        return NULL;
+
+    if (!compileAndGo) {
+        if (!JSObject::clearParent(context, reobj))
+            return NULL;
+        if (!JSObject::clearType(context, reobj))
+            return NULL;
+    }
+
+    pn->pn_objbox = newObjectBox(reobj);
+    if (!pn->pn_objbox)
+        return NULL;
+
+    pn->setOp(JSOP_REGEXP);
+    return pn;
+}
+
+template <>
+SyntaxParseHandler::Node
+Parser<SyntaxParseHandler>::newRegExp(const jschar *buf, size_t length, RegExpFlag flags)
+{
+    return SyntaxParseHandler::NodeGeneric;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::primaryExpr(TokenKind tt)
 {
     JS_ASSERT(tokenStream.isCurrentTokenType(tt));
 
-    ParseNode *pn, *pn2, *pn3;
+    Node pn, pn2, pn3;
     JSOp op;
 
-    JS_CHECK_RECURSION(context, return NULL);
+    JS_CHECK_RECURSION(context, return null());
 
     switch (tt) {
       case TOK_FUNCTION:
         pn = functionExpr();
         if (!pn)
-            return NULL;
+            return null();
         break;
 
       case TOK_LB:
       {
-        pn = ListNode::create(PNK_ARRAY, this);
+        pn = handler.newList(PNK_ARRAY, null(), JSOP_NEWINIT);
         if (!pn)
-            return NULL;
-        pn->setOp(JSOP_NEWINIT);
-        pn->makeEmpty();
-
+            return null();
 #if JS_HAS_GENERATORS
-        pn->pn_blockid = pc->blockidGen;
+        handler.setBlockId(pn, pc->blockidGen);
 #endif
+
         if (tokenStream.matchToken(TOK_RB, TSF_OPERAND)) {
             /*
              * Mark empty arrays as non-constant, since we cannot easily
              * determine their type.
              */
-            pn->pn_xflags |= PNX_NONCONST;
+            handler.setListFlag(pn, PNX_NONCONST);
         } else {
-            bool spread = false;
+            bool spread = false, missingTrailingComma = false;
             unsigned index = 0;
             for (; ; index++) {
                 if (index == StackSpace::ARGS_LENGTH_MAX) {
-                    reportError(NULL, JSMSG_ARRAY_INIT_TOO_BIG);
-                    return NULL;
+                    report(ParseError, false, null(), JSMSG_ARRAY_INIT_TOO_BIG);
+                    return null();
                 }
 
                 tt = tokenStream.peekToken(TSF_OPERAND);
                 if (tt == TOK_RB)
                     break;
 
                 if (tt == TOK_COMMA) {
                     /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
                     tokenStream.matchToken(TOK_COMMA);
-                    pn2 = NullaryNode::create(PNK_COMMA, this);
-                    pn->pn_xflags |= PNX_SPECIALARRAYINIT | PNX_NONCONST;
+                    pn2 = handler.newNullary(PNK_COMMA);
+                    if (!pn2)
+                        return null();
+                    handler.setListFlag(pn, PNX_SPECIALARRAYINIT | PNX_NONCONST);
+                } else if (tt == TOK_TRIPLEDOT) {
+                    spread = true;
+                    handler.setListFlag(pn, PNX_SPECIALARRAYINIT | PNX_NONCONST);
+
+                    tokenStream.getToken();
+
+                    Node inner = assignExpr();
+                    if (!inner)
+                        return null();
+
+                    pn2 = handler.newUnary(PNK_SPREAD, inner);
+                    if (!pn2)
+                        return null();
                 } else {
-                    ParseNode *spreadNode = NULL;
-                    if (tt == TOK_TRIPLEDOT) {
-                        spread = true;
-                        spreadNode = UnaryNode::create(PNK_SPREAD, this);
-                        if (!spreadNode)
-                            return NULL;
-                        tokenStream.getToken();
-                    }
                     pn2 = assignExpr();
-                    if (pn2) {
-                        if (foldConstants && !FoldConstants(context, &pn2, this))
-                            return NULL;
-                        if (!pn2->isConstant() || spreadNode)
-                            pn->pn_xflags |= PNX_NONCONST;
-                        if (spreadNode) {
-                            pn->pn_xflags |= PNX_SPECIALARRAYINIT;
-                            spreadNode->pn_kid = pn2;
-                            pn2 = spreadNode;
-                        }
-                    }
+                    if (!pn2)
+                        return null();
+                    if (foldConstants && !FoldConstants(context, &pn2, this))
+                        return null();
+                    if (!handler.isConstant(pn2))
+                        handler.setListFlag(pn, PNX_NONCONST);
                 }
-                if (!pn2)
-                    return NULL;
-                pn->append(pn2);
+                handler.addList(pn, pn2);
 
                 if (tt != TOK_COMMA) {
                     /* If we didn't already match TOK_COMMA in above case. */
-                    if (!tokenStream.matchToken(TOK_COMMA))
+                    if (!tokenStream.matchToken(TOK_COMMA)) {
+                        missingTrailingComma = true;
                         break;
+                    }
                 }
             }
 
 #if JS_HAS_GENERATORS
             /*
-             * At this point, (index == 0 && pn->pn_count != 0) implies one
+             * At this point, (index == 0 && missingTrailingComma) implies one
              * element initialiser was parsed.
              *
              * An array comprehension of the form:
              *
              *   [i * j for (i in o) for (j in p) if (i != j)]
              *
              * translates to roughly the following let expression:
              *
@@ -5806,222 +6178,190 @@ Parser::primaryExpr(TokenKind tt)
              * runtime via JSOP_ENTERBLOCK. A block-local var is accessed by
              * the JSOP_GETLOCAL and JSOP_SETLOCAL ops. These ops have an
              * immediate operand, the local slot's stack index from fp->spbase.
              *
              * The array comprehension iteration step, array.push(i * j) in
              * the example above, is done by <i * j>; JSOP_ARRAYPUSH <array>,
              * where <array> is the index of array's stack slot.
              */
-            if (index == 0 && !spread && pn->pn_count != 0 && tokenStream.matchToken(TOK_FOR)) {
-                ParseNode *pnexp, *pntop;
-
-                /* Relabel pn as an array comprehension node. */
-                pn->setKind(PNK_ARRAYCOMP);
-
-                /*
-                 * Remove the comprehension expression from pn's linked list
-                 * and save it via pnexp.  We'll re-install it underneath the
-                 * ARRAYPUSH node after we parse the rest of the comprehension.
-                 */
-                pnexp = pn->last();
-                JS_ASSERT(pn->pn_count == 1);
-                pn->pn_count = 0;
-                pn->pn_tail = &pn->pn_head;
-                *pn->pn_tail = NULL;
-
-                pntop = comprehensionTail(pnexp, pn->pn_blockid, false,
-                                          PNK_ARRAYPUSH, JSOP_ARRAYPUSH);
-                if (!pntop)
-                    return NULL;
-                pn->append(pntop);
+            if (index == 0 && !spread && tokenStream.matchToken(TOK_FOR) && missingTrailingComma) {
+                if (!arrayInitializerComprehensionTail(pn))
+                    return null();
             }
 #endif /* JS_HAS_GENERATORS */
 
             MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
         }
-        pn->pn_pos.end = tokenStream.currentToken().pos.end;
+        handler.setEndPosition(pn, tokenStream.currentToken().pos.end);
         return pn;
       }
 
       case TOK_LC:
       {
-        ParseNode *pnval;
+        Node pnval;
 
         /*
          * A map from property names we've seen thus far to a mask of property
          * assignment types, stored and retrieved with ALE_SET_INDEX/ALE_INDEX.
          */
         AtomIndexMap seen(context);
 
         enum AssignmentType {
             GET     = 0x1,
             SET     = 0x2,
             VALUE   = 0x4 | GET | SET
         };
 
-        pn = ListNode::create(PNK_OBJECT, this);
+        pn = handler.newList(PNK_OBJECT, null(), JSOP_NEWINIT);
         if (!pn)
-            return NULL;
-        pn->setOp(JSOP_NEWINIT);
-        pn->makeEmpty();
+            return null();
 
         for (;;) {
             JSAtom *atom;
             TokenKind ltok = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
-            TokenPtr begin = tokenStream.currentToken().pos.begin;
             switch (ltok) {
               case TOK_NUMBER:
-                pn3 = NullaryNode::create(PNK_NUMBER, this);
-                if (!pn3)
-                    return NULL;
-                pn3->initNumber(tokenStream.currentToken());
-                atom = ToAtom<CanGC>(context, DoubleValue(pn3->pn_dval));
+                atom = ToAtom<CanGC>(context, DoubleValue(tokenStream.currentToken().number()));
                 if (!atom)
-                    return NULL;
+                    return null();
+                pn3 = handler.newNumber(tokenStream.currentToken());
                 break;
               case TOK_NAME:
                 {
                     atom = tokenStream.currentToken().name();
                     if (atom == context->names().get) {
                         op = JSOP_GETTER;
                     } else if (atom == context->names().set) {
                         op = JSOP_SETTER;
                     } else {
-                        pn3 = NullaryNode::create(PNK_NAME, this);
+                        pn3 = handler.newAtom(PNK_NAME, atom);
                         if (!pn3)
-                            return NULL;
-                        pn3->pn_atom = atom;
+                            return null();
                         break;
                     }
 
                     tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
                     if (tt == TOK_NAME) {
                         atom = tokenStream.currentToken().name();
-                        pn3 = NameNode::create(PNK_NAME, atom, this, this->pc);
+                        pn3 = handler.newName(atom->asPropertyName(), pc);
                         if (!pn3)
-                            return NULL;
+                            return null();
                     } else if (tt == TOK_STRING) {
                         atom = tokenStream.currentToken().atom();
 
                         uint32_t index;
                         if (atom->isIndex(&index)) {
-                            pn3 = NullaryNode::create(PNK_NUMBER, this);
+                            pn3 = handler.newNumber(index);
                             if (!pn3)
-                                return NULL;
-                            pn3->pn_dval = index;
-                            atom = ToAtom<CanGC>(context, DoubleValue(pn3->pn_dval));
+                                return null();
+                            atom = ToAtom<CanGC>(context, DoubleValue(index));
                             if (!atom)
-                                return NULL;
+                                return null();
                         } else {
-                            pn3 = NameNode::create(PNK_STRING, atom, this, this->pc);
+                            pn3 = handler.newName(atom->asPropertyName(), pc, PNK_STRING);
                             if (!pn3)
-                                return NULL;
+                                return null();
                         }
                     } else if (tt == TOK_NUMBER) {
-                        pn3 = NullaryNode::create(PNK_NUMBER, this);
+                        double number = tokenStream.currentToken().number();
+                        atom = ToAtom<CanGC>(context, DoubleValue(number));
+                        if (!atom)
+                            return null();
+                        pn3 = handler.newNumber(tokenStream.currentToken());
                         if (!pn3)
-                            return NULL;
-                        pn3->initNumber(tokenStream.currentToken());
-                        atom = ToAtom<CanGC>(context, DoubleValue(pn3->pn_dval));
-                        if (!atom)
-                            return NULL;
+                            return null();
                     } else {
                         tokenStream.ungetToken();
-                        pn3 = NullaryNode::create(PNK_NAME, this);
+                        pn3 = handler.newAtom(PNK_NAME, atom);
                         if (!pn3)
-                            return NULL;
-                        pn3->pn_atom = atom;
+                            return null();
                         break;
                     }
 
-                    pn->pn_xflags |= PNX_NONCONST;
+                    handler.setListFlag(pn, PNX_NONCONST);
 
                     /* NB: Getter function in { get x(){} } is unnamed. */
                     Rooted<PropertyName*> funName(context, NULL);
                     TokenStream::Position start;
                     tokenStream.tell(&start);
                     pn2 = functionDef(funName, start, op == JSOP_GETTER ? Getter : Setter,
                                       Expression);
                     if (!pn2)
-                        return NULL;
-                    TokenPos pos = {begin, pn2->pn_pos.end};
-                    pn2 = new_<BinaryNode>(PNK_COLON, op, pos, pn3, pn2);
+                        return null();
+                    pn2 = handler.newBinary(PNK_COLON, pn3, pn2, op);
                     goto skip;
                 }
               case TOK_STRING: {
                 atom = tokenStream.currentToken().atom();
                 uint32_t index;
                 if (atom->isIndex(&index)) {
-                    pn3 = NullaryNode::create(PNK_NUMBER, this);
+                    pn3 = handler.newNumber(index);
                     if (!pn3)
-                        return NULL;
-                    pn3->pn_dval = index;
+                        return null();
                 } else {
-                    pn3 = NullaryNode::create(PNK_STRING, this);
+                    pn3 = handler.newAtom(PNK_STRING, atom);
                     if (!pn3)
-                        return NULL;
-                    pn3->pn_atom = atom;
+                        return null();
                 }
                 break;
               }
               case TOK_RC:
                 goto end_obj_init;
               default:
-                reportError(NULL, JSMSG_BAD_PROP_ID);
-                return NULL;
+                report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
+                return null();
             }
 
             op = JSOP_INITPROP;
             tt = tokenStream.getToken();
             if (tt == TOK_COLON) {
                 pnval = assignExpr();
                 if (!pnval)
-                    return NULL;
+                    return null();
 
                 if (foldConstants && !FoldConstants(context, &pnval, this))
-                    return NULL;
+                    return null();
 
                 /*
                  * Treat initializers which mutate __proto__ as non-constant,
                  * so that we can later assume singleton objects delegate to
                  * the default Object.prototype.
                  */
-                if (!pnval->isConstant() || atom == context->names().proto)
-                    pn->pn_xflags |= PNX_NONCONST;
+                if (!handler.isConstant(pnval) || atom == context->names().proto)
+                    handler.setListFlag(pn, PNX_NONCONST);
             }
 #if JS_HAS_DESTRUCTURING_SHORTHAND
             else if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC)) {
                 /*
                  * Support, e.g., |var {x, y} = o| as destructuring shorthand
                  * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
                  */
                 tokenStream.ungetToken();
                 if (!tokenStream.checkForKeyword(atom->charsZ(), atom->length(), NULL, NULL))
-                    return NULL;
-                pn->pn_xflags |= PNX_DESTRUCT | PNX_NONCONST;
+                    return null();
+                handler.setListFlag(pn, PNX_DESTRUCT | PNX_NONCONST);
+                PropertyName *name = handler.isName(pn3);
+                JS_ASSERT(atom);
+                pn3 = handler.newName(name, pc);
+                if (!pn3)
+                    return null();
                 pnval = pn3;
-                JS_ASSERT(pnval->isKind(PNK_NAME));
-                pnval->setArity(PN_NAME);
-                ((NameNode *)pnval)->initCommon(pc);
             }
 #endif
             else {
-                reportError(NULL, JSMSG_COLON_AFTER_ID);
-                return NULL;
+                report(ParseError, false, null(), JSMSG_COLON_AFTER_ID);
+                return null();
             }
 
-            {
-                TokenPos pos = {begin, pnval->pn_pos.end};
-                pn2 = new_<BinaryNode>(PNK_COLON, op, pos, pn3, pnval);
-            }
+            pn2 = handler.newBinary(PNK_COLON, pn3, pnval, op);
           skip:
             if (!pn2)
-                return NULL;
-            pn->append(pn2);
+                return null();
+            handler.addList(pn, pn2);
 
             /*
              * Check for duplicate property names.  Duplicate data properties
              * only conflict in strict mode.  Duplicate getter or duplicate
              * setter halves always conflict.  A data property conflicts with
              * any part of an accessor property.
              */
             AssignmentType assignType;
@@ -6040,180 +6380,155 @@ Parser::primaryExpr(TokenKind tt)
             if (p) {
                 jsatomid index = p.value();
                 AssignmentType oldAssignType = AssignmentType(index);
                 if ((oldAssignType & assignType) &&
                     (oldAssignType != VALUE || assignType != VALUE || pc->sc->needStrictChecks()))
                 {
                     JSAutoByteString name;
                     if (!js_AtomToPrintableString(context, atom, &name))
-                        return NULL;
-
-                    Reporter reporter =
+                        return null();
+
+                    ParseReportKind reportKind =
                         (oldAssignType == VALUE && assignType == VALUE && !pc->sc->needStrictChecks())
-                        ? &Parser::reportWarning
-                        : (pc->sc->needStrictChecks() ? &Parser::reportStrictModeError : &Parser::reportError);
-                    if (!(this->*reporter)(NULL, JSMSG_DUPLICATE_PROPERTY, name.ptr()))
-                        return NULL;
+                        ? ParseWarning
+                        : (pc->sc->needStrictChecks() ? ParseStrictError : ParseError);
+                    if (!report(reportKind, pc->sc->strict, null(),
+                                JSMSG_DUPLICATE_PROPERTY, name.ptr()))
+                    {
+                        return null();
+                    }
                 }
                 p.value() = assignType | oldAssignType;
             } else {
                 if (!seen.add(p, atom, assignType))
-                    return NULL;
+                    return null();
             }
 
             tt = tokenStream.getToken();
             if (tt == TOK_RC)
                 goto end_obj_init;
             if (tt != TOK_COMMA) {
-                reportError(NULL, JSMSG_CURLY_AFTER_LIST);
-                return NULL;
+                report(ParseError, false, null(), JSMSG_CURLY_AFTER_LIST);
+                return null();
             }
         }
 
       end_obj_init:
-        pn->pn_pos.end = tokenStream.currentToken().pos.end;
+        handler.setEndPosition(pn, tokenStream.currentToken().pos.end);
         return pn;
       }
 
 #if JS_HAS_BLOCK_SCOPE
       case TOK_LET:
         pn = letBlock(LetExpresion);
         if (!pn)
-            return NULL;
+            return null();
         break;
 #endif
 
       case TOK_LP:
       {
         bool genexp;
 
         pn = parenExpr(&genexp);
         if (!pn)
-            return NULL;
-        pn->setInParens(true);
+            return null();
+        pn = handler.setInParens(pn);
+
         if (!genexp)
             MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
         break;
       }
 
       case TOK_STRING:
         pn = atomNode(PNK_STRING, JSOP_STRING);
         if (!pn)
-            return NULL;
+            return null();
         break;
 
       case TOK_NAME:
         pn = identifierName();
         break;
 
       case TOK_REGEXP:
-      {
-        pn = NullaryNode::create(PNK_REGEXP, this);
-        if (!pn)
-            return NULL;
-
-        size_t length = tokenStream.getTokenbuf().length();
-        const jschar *chars = tokenStream.getTokenbuf().begin();
-        RegExpFlag flags = tokenStream.currentToken().regExpFlags();
-        RegExpStatics *res = context->regExpStatics();
-
-        Rooted<RegExpObject*> reobj(context);
-        if (context->hasfp())
-            reobj = RegExpObject::create(context, res, chars, length, flags, &tokenStream);
-        else
-            reobj = RegExpObject::createNoStatics(context, chars, length, flags, &tokenStream);
-
-        if (!reobj)
-            return NULL;
-
-        if (!compileAndGo) {
-            if (!JSObject::clearParent(context, reobj))
-                return NULL;
-            if (!JSObject::clearType(context, reobj))
-                return NULL;
-        }
-
-        pn->pn_objbox = newObjectBox(reobj);
-        if (!pn->pn_objbox)
-            return NULL;
-
-        pn->setOp(JSOP_REGEXP);
+        pn = newRegExp(tokenStream.getTokenbuf().begin(),
+                       tokenStream.getTokenbuf().length(),
+                       tokenStream.currentToken().regExpFlags());
         break;
-      }
 
       case TOK_NUMBER:
-        pn = NullaryNode::create(PNK_NUMBER, this);
-        if (!pn)
-            return NULL;
-        pn->setOp(JSOP_DOUBLE);
-        pn->initNumber(tokenStream.currentToken());
+        pn = handler.newNumber(tokenStream.currentToken());
         break;
 
       case TOK_TRUE:
-        return new_<BooleanLiteral>(true, tokenStream.currentToken().pos);
+        return handler.newBooleanLiteral(true, tokenStream.currentToken().pos);
       case TOK_FALSE:
-        return new_<BooleanLiteral>(false, tokenStream.currentToken().pos);
+        return handler.newBooleanLiteral(false, tokenStream.currentToken().pos);
       case TOK_THIS:
-        return new_<ThisLiteral>(tokenStream.currentToken().pos);
+        return handler.newThisLiteral(tokenStream.currentToken().pos);
       case TOK_NULL:
-        return new_<NullLiteral>(tokenStream.currentToken().pos);
+        return handler.newNullLiteral(tokenStream.currentToken().pos);
 
       case TOK_ERROR:
         /* The scanner or one of its subroutines reported the error. */
-        return NULL;
+        return null();
 
       default:
-        reportError(NULL, JSMSG_SYNTAX_ERROR);
-        return NULL;
+        report(ParseError, false, null(), JSMSG_SYNTAX_ERROR);
+        return null();
     }
     return pn;
 }
 
-ParseNode *
-Parser::parenExpr(bool *genexp)
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::parenExpr(bool *genexp)
 {
-    TokenPtr begin;
-    ParseNode *pn;
-
     JS_ASSERT(tokenStream.currentToken().type == TOK_LP);
-    begin = tokenStream.currentToken().pos.begin;
+    TokenPtr begin = tokenStream.currentToken().pos.begin;
 
     if (genexp)
         *genexp = false;
 
-    GenexpGuard guard(this);
-
-    pn = bracketedExpr();
+    GenexpGuard<ParseHandler> guard(this);
+
+    Node pn = bracketedExpr();
     if (!pn)
-        return NULL;
+        return null();
     guard.endBody();
 
 #if JS_HAS_GENERATOR_EXPRS
     if (tokenStream.matchToken(TOK_FOR)) {
         if (!guard.checkValidBody(pn))
-            return NULL;
-        JS_ASSERT(!pn->isKind(PNK_YIELD));
-        if (pn->isKind(PNK_COMMA) && !pn->isInParens()) {
-            reportError(pn->last(), JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
-            return NULL;
+            return null();
+        if (handler.isOperationWithoutParens(pn, PNK_COMMA)) {
+            report(ParseError, false, null(),
+                   JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
+            return null();
         }
         pn = generatorExpr(pn);
         if (!pn)
-            return NULL;
-        pn->pn_pos.begin = begin;
+            return null();
+        handler.setBeginPosition(pn, begin);
         if (genexp) {
             if (tokenStream.getToken() != TOK_RP) {
-                reportError(NULL, JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
-                return NULL;
+                report(ParseError, false, null(),
+                       JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
+                return null();
             }
-            pn->pn_pos.end = tokenStream.currentToken().pos.end;
+            handler.setEndPosition(pn, tokenStream.currentToken().pos.end);
             *genexp = true;
         }
     } else
 #endif /* JS_HAS_GENERATOR_EXPRS */
 
     if (!guard.maybeNoteGenerator(pn))
-        return NULL;
+        return null();
 
     return pn;
 }
 
+template class Parser<FullParseHandler>;
+template class Parser<SyntaxParseHandler>;
+
+} /* namespace frontend */
+} /* namespace js */
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -13,49 +13,53 @@
  */
 #include "jsversion.h"
 #include "jsprvtd.h"
 #include "jspubtd.h"
 #include "jsatom.h"
 #include "jsscript.h"
 #include "jswin.h"
 
+#include "frontend/FoldConstants.h"
+#include "frontend/FullParseHandler.h"
 #include "frontend/ParseMaps.h"
 #include "frontend/ParseNode.h"
 #include "frontend/SharedContext.h"
+#include "frontend/SyntaxParseHandler.h"
 
 namespace js {
 namespace frontend {
 
 struct StmtInfoPC : public StmtInfoBase {
     StmtInfoPC      *down;          /* info for enclosing statement */
     StmtInfoPC      *downScope;     /* next enclosing lexical scope */
 
     uint32_t        blockid;        /* for simplified dominance computation */
 
     StmtInfoPC(JSContext *cx) : StmtInfoBase(cx) {}
 };
 
 typedef HashSet<JSAtom *> FuncStmtSet;
-struct Parser;
 class SharedContext;
 
 typedef Vector<Definition *, 16> DeclVector;
 
 /*
  * The struct ParseContext stores information about the current parsing context,
  * which is part of the parser state (see the field Parser::pc). The current
  * parsing context is either the global context, or the function currently being
  * parsed. When the parser encounters a function definition, it creates a new
  * ParseContext, makes it the new current context, and sets its parent to the
  * context in which it encountered the definition.
  */
+template <typename ParseHandler>
 struct ParseContext                 /* tree context for semantic checks */
 {
     typedef StmtInfoPC StmtInfo;
+    typedef typename ParseHandler::Node Node;
 
     SharedContext   *sc;            /* context shared between parsing and bytecode generation */
 
     uint32_t        bodyid;         /* block number of program/function body */
     uint32_t        blockidGen;     /* preincremented block number generator */
 
     StmtInfoPC      *topStmt;       /* top of statement info stack */
     StmtInfoPC      *topScopeStmt;  /* top lexical scope statement */
@@ -63,17 +67,17 @@ struct ParseContext                 /* t
                                     /* compile time block scope chain */
 
     const unsigned  staticLevel;    /* static compilation unit nesting level */
 
     uint32_t        parenDepth;     /* nesting depth of parens that might turn out
                                        to be generator expressions */
     uint32_t        yieldCount;     /* number of |yield| tokens encountered at
                                        non-zero depth in current paren tree */
-    ParseNode       *blockNode;     /* parse node for a block with let declarations
+    Node            blockNode;      /* parse node for a block with let declarations
                                        (block with its own lexical scope)  */
   private:
     AtomDecls       decls_;         /* function, const, and var declarations */
     DeclVector      args_;          /* argument definitions */
     DeclVector      vars_;          /* var/const definitions */
 
   public:
     const AtomDecls &decls() const {
@@ -111,33 +115,33 @@ struct ParseContext                 /* t
      *    non-placeholder definition.
      *  + If this is a function scope (sc->inFunction), 'pn' is bound to a
      *    particular local/argument slot.
      *  + PND_CONST is set for Definition::COSNT
      *  + Pre-existing uses of pre-existing placeholders have been linked to
      *    'pn' if they are in the scope of 'pn'.
      *  + Pre-existing placeholders in the scope of 'pn' have been removed.
      */
-    bool define(JSContext *cx, HandlePropertyName name, ParseNode *pn, Definition::Kind);
+    bool define(JSContext *cx, HandlePropertyName name, Node pn, Definition::Kind);
 
     /*
      * Let definitions may shadow same-named definitions in enclosing scopes.
      * To represesent this, 'decls' is not a plain map, but actually:
      *   decls :: name -> stack of definitions
      * New bindings are pushed onto the stack, name lookup always refers to the
      * top of the stack, and leaving a block scope calls popLetDecl for each
      * name in the block's scope.
      */
     void popLetDecl(JSAtom *atom);
 
-    /* See the sad story in DefineArg. */
+    /* See the sad story in defineArg. */
     void prepareToAddDuplicateArg(Definition *prevDecl);
 
     /* See the sad story in MakeDefIntoUse. */
-    void updateDecl(JSAtom *atom, ParseNode *newDecl);
+    void updateDecl(JSAtom *atom, Node newDecl);
 
     /*
      * After a function body has been parsed, the parser generates the
      * function's "bindings". Bindings are a data-structure, ultimately stored
      * in the compiled JSScript, that serve three purposes:
      *  - After parsing, the ParseContext is destroyed and 'decls' along with
      *    it. Mostly, the emitter just uses the binding information stored in
      *    the use/def nodes, but the emitter occasionally needs 'bindings' for
@@ -146,17 +150,17 @@ struct ParseContext                 /* t
      *    scope object (js::CallObject) for the function. This shape is used
      *    during dynamic name lookup.
      *  - Sometimes a script's bindings are accessed at runtime to retrieve the
      *    contents of the lexical scope (e.g., from the debugger).
      */
     bool generateFunctionBindings(JSContext *cx, InternalHandle<Bindings*> bindings) const;
 
   public:
-    ParseNode       *yieldNode;     /* parse node for a yield expression that might
+    Node             yieldNode;     /* parse node for a yield expression that might
                                        be an error if we turn out to be inside a
                                        generator expression */
 
   private:
     ParseContext    **parserPC;     /* this points to the Parser's active pc
                                        and holds either |this| or one of
                                        |this|'s descendents */
 
@@ -193,52 +197,63 @@ struct ParseContext                 /* t
     // assignment-like and declaration-like destructuring patterns, and why
     // they need to be treated differently.
     bool            inDeclDestructuring:1;
 
     // True if we are in a function, saw a "use strict" directive, and weren't
     // strict before.
     bool            funBecameStrict:1;
 
-    inline ParseContext(Parser *prs, SharedContext *sc, unsigned staticLevel, uint32_t bodyid);
+    inline ParseContext(Parser<ParseHandler> *prs, SharedContext *sc, unsigned staticLevel, uint32_t bodyid);
     inline ~ParseContext();
 
     inline bool init();
 
     unsigned blockid();
 
     // True if we are at the topmost level of a entire script or function body.
     // For example, while parsing this code we would encounter f1 and f2 at
     // body level, but we would not encounter f3 or f4 at body level:
     //
     //   function f1() { function f2() { } }
     //   if (cond) { function f3() { if (cond) { function f4() { } } } }
     //
     bool atBodyLevel();
 };
 
+template <typename ParseHandler>
 bool
-GenerateBlockId(ParseContext *pc, uint32_t &blockid);
+GenerateBlockId(ParseContext<ParseHandler> *pc, uint32_t &blockid);
 
+template <typename ParseHandler>
 struct BindData;
 
-enum FunctionSyntaxKind { Expression, Statement };
+class CompExprTransplanter;
+
+template <typename ParseHandler>
+class GenexpGuard;
+
+// XXX fix this backdoor.
+bool EmitElemOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce);
+
 enum LetContext { LetExpresion, LetStatement };
 enum VarContext { HoistVars, DontHoistVars };
 
-struct Parser : private AutoGCRooter
+template <typename ParseHandler>
+struct Parser : private AutoGCRooter, public StrictModeGetter
 {
     JSContext           *const context; /* FIXME Bug 551291: use AutoGCRooter::context? */
-    StrictModeGetter    strictModeGetter; /* used by tokenStream to test for strict mode */
     TokenStream         tokenStream;
     void                *tempPoolMark;  /* initial JSContext.tempLifoAlloc mark */
-    ParseNodeAllocator  allocator;
-    ObjectBox           *traceListHead; /* list of parsed object for GC tracing */
 
-    ParseContext        *pc;            /* innermost parse context (stack-allocated) */
+    /* list of parsed objects for GC tracing */
+    ObjectBox *traceListHead;
+
+    /* innermost parse context (stack-allocated) */
+    ParseContext<ParseHandler> *pc;
 
     SourceCompressionToken *sct;        /* compression token for aborting */
 
     /* Root atoms and objects allocated for the parsed tree. */
     AutoKeepAtoms       keepAtoms;
 
     /* Perform constant-folding; must be true when interfacing with the emitter. */
     const bool          foldConstants:1;
@@ -258,17 +273,32 @@ struct Parser : private AutoGCRooter
      * guaranteed to return the original objects, ensuring safe implementation
      * of self-hosted builtins.
      * Additionally, the special syntax _CallFunction(receiver, ...args, fun)
      * is supported, for which bytecode is emitted that invokes |fun| with
      * |receiver| as the this-object and ...args as the arguments..
      */
     const bool          selfHostingMode:1;
 
+    /*
+     * Not all language constructs can be handled during syntax parsing. If it
+     * is not known whether the parse succeeds or fails, this bit is set and
+     * the parse will return false.
+     */
+    bool unknownResult;
+
+    typedef typename ParseHandler::Node Node;
+    typedef typename ParseHandler::DefinitionNode DefinitionNode;
+
   public:
+    /* State specific to the kind of parse being performed. */
+    ParseHandler handler;
+
+    bool report(ParseReportKind kind, bool strict, Node pn, unsigned errorNumber, ...);
+
     Parser(JSContext *cx, const CompileOptions &options,
            const jschar *chars, size_t length, bool foldConstants);
     ~Parser();
 
     friend void AutoGCRooter::trace(JSTracer *trc);
 
     /*
      * Initialize a parser. The compiler owns the arena pool "tops-of-stack"
@@ -279,88 +309,73 @@ struct Parser : private AutoGCRooter
     bool init();
 
     const char *getFilename() const { return tokenStream.getFilename(); }
     JSVersion versionNumber() const { return tokenStream.versionNumber(); }
 
     /*
      * Parse a top-level JS script.
      */
-    ParseNode *parse(JSObject *chain);
+    Node parse(JSObject *chain);
 
     /*
      * Allocate a new parsed object or function container from
      * cx->tempLifoAlloc.
      */
     ObjectBox *newObjectBox(JSObject *obj);
-    ModuleBox *newModuleBox(Module *module, ParseContext *pc);
-    FunctionBox *newFunctionBox(JSFunction *fun, ParseContext *pc, bool strict);
+    ModuleBox *newModuleBox(Module *module, ParseContext<ParseHandler> *pc);
+    FunctionBox *newFunctionBox(JSFunction *fun, ParseContext<ParseHandler> *pc, bool strict);
 
     /*
      * Create a new function object given parse context (pc) and a name (which
      * is optional if this is a function expression).
      */
-    JSFunction *newFunction(ParseContext *pc, HandleAtom atom, FunctionSyntaxKind kind);
+    JSFunction *newFunction(ParseContext<ParseHandler> *pc, HandleAtom atom, FunctionSyntaxKind kind);
 
     void trace(JSTracer *trc);
 
-    /*
-     * Report a parse (compile) error.
-     */
-    inline bool reportError(ParseNode *pn, unsigned errorNumber, ...);
-    inline bool reportUcError(ParseNode *pn, unsigned errorNumber, ...);
-    inline bool reportWarning(ParseNode *pn, unsigned errorNumber, ...);
-    inline bool reportStrictWarning(ParseNode *pn, unsigned errorNumber, ...);
-    inline bool reportStrictModeError(ParseNode *pn, unsigned errorNumber, ...);
-    typedef bool (Parser::*Reporter)(ParseNode *pn, unsigned errorNumber, ...);
+    bool hadUnknownResult() {
+        return unknownResult;
+    }
 
   private:
     Parser *thisForCtor() { return this; }
 
-    ParseNode *allocParseNode(size_t size) {
-        JS_ASSERT(size == sizeof(ParseNode));
-        return static_cast<ParseNode *>(allocator.allocNode());
-    }
-
     /*
      * Create a parse node with the given kind and op using the current token's
      * atom.
      */
-    ParseNode *atomNode(ParseNodeKind kind, JSOp op);
+    Node atomNode(ParseNodeKind kind, JSOp op);
+
+    void setUnknownResult() {
+        unknownResult = true;
+    }
 
   public:
-    ParseNode *freeTree(ParseNode *pn) { return allocator.freeTree(pn); }
-    void prepareNodeForMutation(ParseNode *pn) { return allocator.prepareNodeForMutation(pn); }
-
-    /* new_ methods for creating parse nodes. These report OOM on context. */
-    JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
-
-    ParseNode *cloneNode(const ParseNode &other) {
-        ParseNode *node = allocParseNode(sizeof(ParseNode));
-        if (!node)
-            return NULL;
-        PodAssign(node, &other);
-        return node;
-    }
 
     /* Public entry points for parsing. */
-    ParseNode *statement();
-    bool maybeParseDirective(ParseNode *pn, bool *cont);
+    Node statement();
+    bool maybeParseDirective(Node pn, bool *cont);
 
     // Parse a function, given only its body. Used for the Function constructor.
-    ParseNode *standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals, HandleScript script,
-                                      ParseNode *fn, FunctionBox **funbox, bool strict,
-                                      bool *becameStrict = NULL);
+    Node standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals, HandleScript script,
+                                Node fn, FunctionBox **funbox, bool strict,
+                                bool *becameStrict = NULL);
 
     /*
      * Parse a function body.  Pass StatementListBody if the body is a list of
      * statements; pass ExpressionBody if the body is a single expression.
      */
     enum FunctionBodyType { StatementListBody, ExpressionBody };
-    ParseNode *functionBody(FunctionBodyType type);
+    Node functionBody(FunctionBodyType type);
+
+    virtual bool strictMode()
+    {
+        return pc->sc->strict;
+    }
 
   private:
     /*
      * JS parsers, from lowest to highest precedence.
      *
      * Each parser must be called during the dynamic scope of a ParseContext
      * object, pointed to by this->pc.
      *
@@ -369,152 +384,161 @@ struct Parser : private AutoGCRooter
      * Parsers whose name has a '1' suffix leave the TokenStream state
      * pointing to the token one past the end of the parsed fragment.  For a
      * number of the parsers this is convenient and avoids a lot of
      * unnecessary ungetting and regetting of tokens.
      *
      * Some parsers have two versions:  an always-inlined version (with an 'i'
      * suffix) and a never-inlined version (with an 'n' suffix).
      */
-    ParseNode *moduleDecl();
-    ParseNode *functionStmt();
-    ParseNode *functionExpr();
-    ParseNode *statements(bool *hasFunctionStmt = NULL);
+    Node moduleDecl();
+    Node functionStmt();
+    Node functionExpr();
+    Node statements(bool *hasFunctionStmt = NULL);
 
-    ParseNode *switchStatement();
-    ParseNode *forStatement();
-    ParseNode *tryStatement();
-    ParseNode *withStatement();
+    Node switchStatement();
+    Node forStatement();
+    Node tryStatement();
+    Node withStatement();
 #if JS_HAS_BLOCK_SCOPE
-    ParseNode *letStatement();
+    Node letStatement();
 #endif
-    ParseNode *expressionStatement();
-    ParseNode *variables(ParseNodeKind kind, StaticBlockObject *blockObj = NULL,
-                         VarContext varContext = HoistVars);
-    ParseNode *expr();
-    ParseNode *assignExpr();
-    ParseNode *assignExprWithoutYield(unsigned err);
-    ParseNode *condExpr1();
-    ParseNode *orExpr1();
-    ParseNode *andExpr1i();
-    ParseNode *andExpr1n();
-    ParseNode *bitOrExpr1i();
-    ParseNode *bitOrExpr1n();
-    ParseNode *bitXorExpr1i();
-    ParseNode *bitXorExpr1n();
-    ParseNode *bitAndExpr1i();
-    ParseNode *bitAndExpr1n();
-    ParseNode *eqExpr1i();
-    ParseNode *eqExpr1n();
-    ParseNode *relExpr1i();
-    ParseNode *relExpr1n();
-    ParseNode *shiftExpr1i();
-    ParseNode *shiftExpr1n();
-    ParseNode *addExpr1i();
-    ParseNode *addExpr1n();
-    ParseNode *mulExpr1i();
-    ParseNode *mulExpr1n();
-    ParseNode *unaryExpr();
-    ParseNode *memberExpr(bool allowCallSyntax);
-    ParseNode *primaryExpr(TokenKind tt);
-    ParseNode *parenExpr(bool *genexp = NULL);
+    Node expressionStatement();
+    Node variables(ParseNodeKind kind, StaticBlockObject *blockObj = NULL,
+                   VarContext varContext = HoistVars);
+    Node expr();
+    Node assignExpr();
+    Node assignExprWithoutYield(unsigned err);
+    Node condExpr1();
+    Node orExpr1();
+    Node andExpr1i();
+    Node andExpr1n();
+    Node bitOrExpr1i();
+    Node bitOrExpr1n();
+    Node bitXorExpr1i();
+    Node bitXorExpr1n();
+    Node bitAndExpr1i();
+    Node bitAndExpr1n();
+    Node eqExpr1i();
+    Node eqExpr1n();
+    Node relExpr1i();
+    Node relExpr1n();
+    Node shiftExpr1i();
+    Node shiftExpr1n();
+    Node addExpr1i();
+    Node addExpr1n();
+    Node mulExpr1i();
+    Node mulExpr1n();
+    Node unaryExpr();
+    Node memberExpr(bool allowCallSyntax);
+    Node primaryExpr(TokenKind tt);
+    Node parenExpr(bool *genexp = NULL);
 
     /*
      * Additional JS parsers.
      */
     enum FunctionType { Getter, Setter, Normal };
-    bool functionArguments(ParseNode **list, ParseNode *funcpn, bool &hasRest);
+    bool functionArguments(Node *list, Node funcpn, bool &hasRest);
 
-    ParseNode *functionDef(HandlePropertyName name, const TokenStream::Position &start,
-                           FunctionType type, FunctionSyntaxKind kind);
-    bool functionArgsAndBody(ParseNode *pn, HandleFunction fun, HandlePropertyName funName,
+    Node functionDef(HandlePropertyName name, const TokenStream::Position &start,
+                     FunctionType type, FunctionSyntaxKind kind);
+    bool functionArgsAndBody(Node pn, HandleFunction fun, HandlePropertyName funName,
                              FunctionType type, FunctionSyntaxKind kind, bool strict,
                              bool *becameStrict = NULL);
 
-    ParseNode *unaryOpExpr(ParseNodeKind kind, JSOp op);
+    Node unaryOpExpr(ParseNodeKind kind, JSOp op);
 
-    ParseNode *condition();
-    ParseNode *comprehensionTail(ParseNode *kid, unsigned blockid, bool isGenexp,
-                                 ParseNodeKind kind = PNK_SEMI, JSOp op = JSOP_NOP);
-    ParseNode *generatorExpr(ParseNode *kid);
-    bool argumentList(ParseNode *listNode);
-    ParseNode *bracketedExpr();
-    ParseNode *letBlock(LetContext letContext);
-    ParseNode *returnOrYield(bool useAssignExpr);
-    ParseNode *destructuringExpr(BindData *data, TokenKind tt);
+    Node condition();
+    Node comprehensionTail(Node kid, unsigned blockid, bool isGenexp,
+                               ParseNodeKind kind = PNK_SEMI, JSOp op = JSOP_NOP);
+    bool arrayInitializerComprehensionTail(Node pn);
+    Node generatorExpr(Node kid);
+    bool argumentList(Node listNode);
+    Node bracketedExpr();
+    Node letBlock(LetContext letContext);
+    Node returnOrYield(bool useAssignExpr);
+    Node destructuringExpr(BindData<ParseHandler> *data, TokenKind tt);
 
-    ParseNode *identifierName();
+    Node identifierName();
 
     bool allowsForEachIn() {
 #if !JS_HAS_FOR_EACH_IN
         return false;
 #else
         return versionNumber() >= JSVERSION_1_6;
 #endif
     }
 
-    bool setAssignmentLhsOps(ParseNode *pn, JSOp op);
+    bool setAssignmentLhsOps(Node pn, JSOp op);
     bool matchInOrOf(bool *isForOfp);
+
+    void addStatementToList(Node pn, Node kid, bool *hasFunctionStmt);
+    bool checkFunctionArguments();
+    bool makeDefIntoUse(Definition *dn, Node pn, JSAtom *atom);
+    bool checkFunctionDefinition(HandlePropertyName funName, Node *pn, FunctionSyntaxKind kind);
+    bool finishFunctionDefinition(Node pn, FunctionBox *funbox,
+                                  Node prelude, Node body,
+                                  ParseContext<ParseHandler> *outerpc);
+
+    bool isValidForStatementLHS(Node pn1, JSVersion version,
+                                bool forDecl, bool forEach, bool forOf);
+    bool setLvalKid(Node pn, Node kid, const char *name);
+    bool setIncOpKid(Node pn, Node kid, TokenKind tt, bool preorder);
+    bool checkStrictAssignment(Node lhs);
+    bool checkStrictBinding(HandlePropertyName name, Node pn);
+    bool checkDeleteExpression(Node *pn);
+    bool defineArg(Node funcpn, HandlePropertyName name,
+                   bool disallowDuplicateArgs = false, DefinitionNode *duplicatedArg = NULL);
+    Node pushLexicalScope(StmtInfoPC *stmt);
+    Node pushLexicalScope(Handle<StaticBlockObject*> blockObj, StmtInfoPC *stmt);
+    Node pushLetScope(Handle<StaticBlockObject*> blockObj, StmtInfoPC *stmt);
+    bool noteNameUse(Node pn);
+    Node newRegExp(const jschar *chars, size_t length, RegExpFlag flags);
+    Node newBindingNode(PropertyName *name, VarContext varContext = HoistVars);
+    bool checkDestructuring(BindData<ParseHandler> *data, Node left, bool toplevel = true);
+    bool bindDestructuringVar(BindData<ParseHandler> *data, Node pn);
+    bool bindDestructuringLHS(Node pn);
+    bool makeSetCall(Node pn, unsigned msg);
+    PropertyName *foldPropertyByValue(Node pn);
+    Node cloneLeftHandSide(Node opn);
+    Node cloneParseTree(Node opn);
+
+    static bool
+    bindDestructuringArg(JSContext *cx, BindData<ParseHandler> *data,
+                         HandlePropertyName name, Parser<ParseHandler> *parser);
+
+    static bool
+    bindLet(JSContext *cx, BindData<ParseHandler> *data,
+            HandlePropertyName name, Parser<ParseHandler> *parser);
+
+    static bool
+    bindVarOrConst(JSContext *cx, BindData<ParseHandler> *data,
+                   HandlePropertyName name, Parser<ParseHandler> *parser);
+
+    static DefinitionNode null() { return ParseHandler::null(); }
+
+    bool reportRedeclaration(Node pn, bool isConst, JSAtom *atom);
+    bool reportBadReturn(Node pn, ParseReportKind kind, unsigned errnum, unsigned anonerrnum);
+    bool checkFinalReturn(Node pn);
+
+    bool leaveFunction(Node fn, HandlePropertyName funName,
+                       FunctionSyntaxKind kind = Expression);
+
+    friend class CompExprTransplanter;
+    friend class GenexpGuard<ParseHandler>;
+    friend struct BindData<ParseHandler>;
 };
 
-inline bool
-Parser::reportError(ParseNode *pn, unsigned errorNumber, ...)
-{
-    va_list args;
-    va_start(args, errorNumber);
-    bool result = tokenStream.reportCompileErrorNumberVA(pn, JSREPORT_ERROR, errorNumber, args);
-    va_end(args);
-    return result;
-}
-
-inline bool
-Parser::reportUcError(ParseNode *pn, unsigned errorNumber, ...)
-{
-    va_list args;
-    va_start(args, errorNumber);
-    bool result = tokenStream.reportCompileErrorNumberVA(pn, JSREPORT_UC | JSREPORT_ERROR,
-                                                         errorNumber, args);
-    va_end(args);
-    return result;
-}
+template <>
+ParseNode *
+Parser<FullParseHandler>::expr();
 
-inline bool
-Parser::reportWarning(ParseNode *pn, unsigned errorNumber, ...)
-{
-    va_list args;
-    va_start(args, errorNumber);
-    bool result = tokenStream.reportCompileErrorNumberVA(pn, JSREPORT_WARNING, errorNumber, args);
-    va_end(args);
-    return result;
-}
-
-inline bool
-Parser::reportStrictWarning(ParseNode *pn, unsigned errorNumber, ...)
-{
-    va_list args;
-    va_start(args, errorNumber);
-    bool result = tokenStream.reportStrictWarningErrorNumberVA(pn, errorNumber, args);
-    va_end(args);
-    return result;
-}
-
-inline bool
-Parser::reportStrictModeError(ParseNode *pn, unsigned errorNumber, ...)
-{
-    va_list args;
-    va_start(args, errorNumber);
-    bool result =
-        tokenStream.reportStrictModeErrorNumberVA(pn, pc->sc->strict, errorNumber, args);
-    va_end(args);
-    return result;
-}
-
+template <>
 bool
-DefineArg(Parser *parser, ParseNode *funcpn, HandlePropertyName name,
-          bool disallowDuplicateArgs = false, Definition **duplicatedArg = NULL);
+Parser<FullParseHandler>::setAssignmentLhsOps(ParseNode *pn, JSOp op);
 
 } /* namespace frontend */
 } /* namespace js */
 
 /*
  * Convenience macro to access Parser.tokenStream as a pointer.
  */
 #define TS(p) (&(p)->tokenStream)
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -184,34 +184,36 @@ class GlobalSharedContext : public Share
     JSObject *scopeChain() const { return scopeChain_; }
 };
 
 
 class ModuleBox : public ObjectBox, public SharedContext {
 public:
     Bindings bindings;
 
-    ModuleBox(JSContext *cx, ObjectBox *traceListHead, Module *module, ParseContext *pc);
+    ModuleBox(JSContext *cx, ObjectBox *traceListHead, Module *module,
+              ParseContext<FullParseHandler> *pc);
     ObjectBox *toObjectBox() { return this; }
     Module *module() const { return &object->asModule(); }
 };
 
 class FunctionBox : public ObjectBox, public SharedContext
 {
   public:
     Bindings        bindings;               /* bindings for this function */
     size_t          bufStart;
     size_t          bufEnd;
     uint16_t        ndefaults;
     bool            inWith:1;               /* some enclosing scope is a with-statement */
     bool            inGenexpLambda:1;       /* lambda from generator expression */
 
     FunctionContextFlags funCxFlags;
 
-    FunctionBox(JSContext *cx, ObjectBox* traceListHead, JSFunction *fun, ParseContext *pc,
+    template <typename ParseHandler>
+    FunctionBox(JSContext *cx, ObjectBox* traceListHead, JSFunction *fun, ParseContext<ParseHandler> *pc,
                 bool strict);
 
     ObjectBox *toObjectBox() { return this; }
     JSFunction *function() const { return object->toFunction(); }
 
     bool isGenerator()              const { return funCxFlags.isGenerator; }
     bool mightAliasLocals()         const { return funCxFlags.mightAliasLocals; }
     bool hasExtensibleScope()       const { return funCxFlags.hasExtensibleScope; }
new file mode 100644
--- /dev/null
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 sw=4 et tw=78:
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef SyntaxParseHandler_h__
+#define SyntaxParseHandler_h__
+
+namespace js {
+namespace frontend {
+
+class SyntaxParseHandler
+{
+    /* Remember the last encountered name or string literal during syntax parses. */
+    JSAtom *lastAtom;
+    TokenPos lastStringPos;
+    TokenStream &tokenStream;
+
+  public:
+    enum Node {
+        NodeFailure = 0,
+        NodeGeneric,
+        NodeName,
+        NodeString,
+        NodeStringExprStatement,
+        NodeLValue
+    };
+    typedef Node DefinitionNode;
+
+    SyntaxParseHandler(JSContext *cx, TokenStream &tokenStream, bool foldConstants)
+      : lastAtom(NULL),
+        tokenStream(tokenStream)
+    {}
+
+    static Node null() { return NodeFailure; }
+
+    void trace(JSTracer *trc) {}
+
+    Node newName(PropertyName *name, ParseContext<SyntaxParseHandler> *pc,
+                 ParseNodeKind kind = PNK_NAME) {
+        lastAtom = name;
+        return NodeName;
+    }
+    Node newAtom(ParseNodeKind kind, JSAtom *atom, JSOp op = JSOP_NOP) {
+        if (kind == PNK_STRING) {
+            lastAtom = atom;
+            lastStringPos = tokenStream.currentToken().pos;
+        }
+        return NodeString;
+    }
+    Node newNumber(double value, DecimalPoint decimalPoint = NoDecimal) { return NodeGeneric; }
+    Node newNumber(const Token &tok) { return NodeGeneric; }
+    Node newBooleanLiteral(bool cond, const TokenPos &pos) { return NodeGeneric; }
+    Node newThisLiteral(const TokenPos &pos) { return NodeGeneric; }
+    Node newNullLiteral(const TokenPos &pos) { return NodeGeneric; }
+    Node newConditional(Node cond, Node thenExpr, Node elseExpr) { return NodeGeneric; }
+
+    Node newNullary(ParseNodeKind kind) { return NodeGeneric; }
+
+    Node newUnary(ParseNodeKind kind, Node kid, JSOp op = JSOP_NOP) {
+        if (kind == PNK_SEMI && kid == NodeString)
+            return NodeStringExprStatement;
+        return NodeGeneric;
+    }
+    Node newUnary(ParseNodeKind kind, JSOp op = JSOP_NOP) { return NodeGeneric; }
+    void setUnaryKid(Node pn, Node kid) {}
+
+    Node newBinary(ParseNodeKind kind, JSOp op = JSOP_NOP) { return NodeGeneric; }
+    Node newBinary(ParseNodeKind kind, Node left, JSOp op = JSOP_NOP) { return NodeGeneric; }
+    Node newBinary(ParseNodeKind kind, Node left, Node right, JSOp op = JSOP_NOP) {
+        return NodeGeneric;
+    }
+    Node newBinaryOrAppend(ParseNodeKind kind, Node left, Node right, JSOp op = JSOP_NOP) {
+        return NodeGeneric;
+    }
+    void setBinaryRHS(Node pn, Node rhs) {}
+
+    Node newTernary(ParseNodeKind kind, Node first, Node second, Node third, JSOp op = JSOP_NOP) {
+        return NodeGeneric;
+    }
+
+    Node newBreak(PropertyName *label, const TokenPtr &begin, const TokenPtr &end) {
+        return NodeGeneric;
+    }
+    Node newContinue(PropertyName *label, const TokenPtr &begin, const TokenPtr &end) {
+        return NodeGeneric;
+    }
+    Node newDebuggerStatement(const TokenPos &pos) { return NodeGeneric; }
+    Node newPropertyAccess(Node pn, PropertyName *name, const TokenPtr &end) { return NodeLValue; }
+    Node newPropertyByValue(Node pn, Node kid, const TokenPtr &end) { return NodeLValue; }
+
+    bool addCatchBlock(Node catchList, Node letBlock,
+                       Node catchName, Node catchGuard, Node catchBody) { return true; }
+
+    void morphNameIntoLabel(Node name, Node statement) {}
+    void setLeaveBlockResult(Node block, Node kid, bool leaveBlockExpr) {}
+
+    void setLastFunctionArgumentDefault(Node funcpn, Node pn) {}
+    Node newFunctionDefinition() { return NodeGeneric; }
+    void setFunctionBody(Node pn, Node kid) {}
+    void setFunctionBox(Node pn, FunctionBox *funbox) {}
+    bool isOperationWithoutParens(Node pn, ParseNodeKind kind) {
+        // It is OK to return false here, callers should only use this method
+        // for reporting strict option warnings and parsing code which the
+        // syntax parser does not handle.
+        return false;
+    }
+
+    void noteLValue(Node pn) {}
+    bool finishInitializerAssignment(Node pn, Node init, JSOp op) { return true; }
+
+    void setBeginPosition(Node pn, Node oth) {}
+    void setBeginPosition(Node pn, const TokenPtr &begin) {}
+
+    void setEndPosition(Node pn, Node oth) {}
+    void setEndPosition(Node pn, const TokenPtr &end) {}
+
+    TokenPos getPosition(Node pn) {
+        return tokenStream.currentToken().pos;
+    }
+
+    Node newList(ParseNodeKind kind, Node kid = NodeGeneric, JSOp op = JSOP_NOP) {
+        return NodeGeneric;
+    }
+    void addList(Node pn, Node kid) {}
+
+    void setOp(Node pn, JSOp op) {}
+    void setBlockId(Node pn, unsigned blockid) {}
+    void setFlag(Node pn, unsigned flag) {}
+    void setListFlag(Node pn, unsigned flag) {}
+    Node setInParens(Node pn) {
+        // String literals enclosed by parentheses are ignored during
+        // strict mode parsing.
+        return NodeGeneric;
+    }
+    void setPrologue(Node pn) {}
+
+    bool isConstant(Node pn) { return false; }
+    PropertyName *isName(Node pn) {
+        return (pn == NodeName) ? lastAtom->asPropertyName() : NULL;
+    }
+    PropertyName *isGetProp(Node pn) { return NULL; }
+    JSAtom *isStringExprStatement(Node pn, TokenPos *pos) {
+        if (pn == NodeStringExprStatement) {
+            *pos = lastStringPos;
+            return lastAtom;
+        }
+        return NULL;
+    }
+    bool isEmptySemicolon(Node pn) { return false; }
+
+    Node makeAssignment(Node pn, Node rhs) { return NodeGeneric; }
+};
+
+} // namespace frontend
+} // namespace js
+
+#endif /* SyntaxParseHandler_h__ */
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -416,29 +416,29 @@ TokenStream::seek(const Position &pos)
 void
 TokenStream::positionAfterLastFunctionKeyword(Position &pos)
 {
     JS_ASSERT(lastFunctionKeyword.buf > userbuf.base());
     PodAssign(&pos, &lastFunctionKeyword);
 }
 
 bool
-TokenStream::reportStrictModeErrorNumberVA(ParseNode *pn, bool strictMode, unsigned errorNumber,
+TokenStream::reportStrictModeErrorNumberVA(const TokenPos &pos, bool strictMode, unsigned errorNumber,
                                            va_list args)
 {
     /* In strict mode code, this is an error, not merely a warning. */
     unsigned flags = JSREPORT_STRICT;
     if (strictMode)
         flags |= JSREPORT_ERROR;
     else if (cx->hasStrictOption())
         flags |= JSREPORT_WARNING;
     else
         return true;
  
-    return reportCompileErrorNumberVA(pn, flags, errorNumber, args);
+    return reportCompileErrorNumberVA(pos, flags, errorNumber, args);
 }
 
 void
 CompileError::throwError()
 {
     /*
      * If there's a runtime exception type associated with this error
      * number, set that as the pending exception.  For errors occuring at
@@ -484,35 +484,33 @@ CompileError::~CompileError()
         }
         js_free(report.messageArgs);
     }
 
     PodZero(&report);
 }
 
 bool
-TokenStream::reportCompileErrorNumberVA(ParseNode *pn, unsigned flags, unsigned errorNumber,
+TokenStream::reportCompileErrorNumberVA(const TokenPos &pos, unsigned flags, unsigned errorNumber,
                                         va_list args)
 {
     bool warning = JSREPORT_IS_WARNING(flags);
 
     if (warning && cx->hasWErrorOption()) {
         flags &= ~JSREPORT_WARNING;
         warning = false;
     }
 
     CompileError err(cx);
 
-    const TokenPos *const tp = pn ? &pn->pn_pos : &currentToken().pos;
-
     err.report.flags = flags;
     err.report.errorNumber = errorNumber;
     err.report.filename = filename;
     err.report.originPrincipals = originPrincipals;
-    err.report.lineno = tp->begin.lineno;
+    err.report.lineno = pos.begin.lineno;
 
     err.argumentsType = (flags & JSREPORT_UC) ? ArgumentsAreUnicode : ArgumentsAreASCII;
 
     if (!js_ExpandErrorArguments(cx, js_GetErrorMessage, NULL, errorNumber, &err.message,
                                  &err.report, err.argumentsType, args))
     {
         return false;
     }
@@ -523,31 +521,31 @@ TokenStream::reportCompileErrorNumberVA(
      * the line that T starts on, which makes it hard to print some or all of
      * T's (starting) line for context.
      *
      * So we don't even try, leaving report.linebuf and friends zeroed.  This
      * means that any error involving a multi-line token (eg. an unterminated
      * multi-line string literal) won't have a context printed.
      */
     if (err.report.lineno == lineno) {
-        const jschar *tokptr = linebase + tp->begin.index;
+        const jschar *tokptr = linebase + pos.begin.index;
 
         // We show only a portion (a "window") of the line around the erroneous
         // token -- the first char in the token, plus |windowRadius| chars
         // before it and |windowRadius - 1| chars after it.  This is because
         // lines can be very long and printing the whole line is (a) not that
         // helpful, and (b) can waste a lot of memory.  See bug 634444.
         static const size_t windowRadius = 60;
 
         // Truncate at the front if necessary.
         const jschar *windowBase = (linebase + windowRadius < tokptr)
                                  ? tokptr - windowRadius
                                  : linebase;
         size_t nTrunc = windowBase - linebase;
-        uint32_t windowIndex = tp->begin.index - nTrunc;
+        uint32_t windowIndex = pos.begin.index - nTrunc;
 
         // Find EOL, or truncate at the back if necessary.
         const jschar *windowLimit = userbuf.findEOLMax(tokptr, windowRadius);
         size_t windowLength = windowLimit - windowBase;
         JS_ASSERT(windowLength <= windowRadius * 2);
 
         // Create the windowed strings.
         StringBuffer windowBuf(cx);
@@ -560,63 +558,63 @@ TokenStream::reportCompileErrorNumberVA(
         if (!err.report.uclinebuf)
             return false;
         TwoByteChars tbchars(err.report.uclinebuf, windowLength);
         err.report.linebuf = LossyTwoByteCharsToNewLatin1CharsZ(cx, tbchars).c_str();
         if (!err.report.linebuf)
             return false;
 
         // The lineno check above means we should only see single-line tokens here.
-        JS_ASSERT(tp->begin.lineno == tp->end.lineno);
+        JS_ASSERT(pos.begin.lineno == pos.end.lineno);
         err.report.tokenptr = err.report.linebuf + windowIndex;
         err.report.uctokenptr = err.report.uclinebuf + windowIndex;
     }
 
     err.throwError();
 
     return warning;
 }
 
 bool
 TokenStream::reportStrictModeError(unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
-    bool result = reportStrictModeErrorNumberVA(NULL, strictMode(), errorNumber, args);
+    bool result = reportStrictModeErrorNumberVA(currentToken().pos, strictMode(), errorNumber, args);
     va_end(args);
     return result;
 }
 
 bool
 TokenStream::reportError(unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
-    bool result = reportCompileErrorNumberVA(NULL, JSREPORT_ERROR, errorNumber, args);
+    bool result = reportCompileErrorNumberVA(currentToken().pos, JSREPORT_ERROR, errorNumber, args);
     va_end(args);
     return result;
 }
 
 bool
 TokenStream::reportWarning(unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
-    bool result = reportCompileErrorNumberVA(NULL, JSREPORT_WARNING, errorNumber, args);
+    bool result = reportCompileErrorNumberVA(currentToken().pos, JSREPORT_WARNING, errorNumber, args);
     va_end(args);
     return result;
 }
 
 bool
-TokenStream::reportStrictWarningErrorNumberVA(ParseNode *pn, unsigned errorNumber, va_list args)
+TokenStream::reportStrictWarningErrorNumberVA(const TokenPos &pos, unsigned errorNumber, va_list args)
 {
     if (!cx->hasStrictOption())
         return true;
 
-    return reportCompileErrorNumberVA(NULL, JSREPORT_STRICT | JSREPORT_WARNING, errorNumber, args);
+    return reportCompileErrorNumberVA(pos, JSREPORT_STRICT | JSREPORT_WARNING, errorNumber, args);
 }
 
 /*
  * We have encountered a '\': check for a Unicode escape sequence after it.
  * Return 'true' and the character code value (by value) if we found a
  * Unicode escape sequence.  Otherwise, return 'false'.  In both cases, do not
  * advance along the buffer.
  */
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -374,18 +374,16 @@ enum TokenStreamFlags
      *    </script>
      *
      * It does not cope with malformed comment hiding hacks where --> is hidden
      * by C-style comments, or on a dirty line.  Such cases are already broken.
      */
     TSF_IN_HTML_COMMENT = 0x2000
 };
 
-struct Parser;
-
 struct CompileError {
     JSContext *cx;
     JSErrorReport report;
     char *message;
     ErrorArgumentsType argumentsType;
     CompileError(JSContext *cx)
       : cx(cx), message(NULL), argumentsType(ArgumentsAreUnicode)
     {
@@ -401,25 +399,21 @@ StrictModeFromContext(JSContext *cx)
     return cx->hasOption(JSOPTION_STRICT_MODE);
 }
 
 // Ideally, tokenizing would be entirely independent of context.  But the
 // strict mode flag, which is in SharedContext, affects tokenizing, and
 // TokenStream needs to see it.
 //
 // This class is a tiny back-channel from TokenStream to the strict mode flag
-// that avoids exposing the rest of SharedContext to TokenStream. get()
-// returns the current strictness.
+// that avoids exposing the rest of SharedContext to TokenStream.
 //
 class StrictModeGetter {
-    Parser *parser;
   public:
-    StrictModeGetter(Parser *p) : parser(p) { }
-
-    bool get() const;
+    virtual bool strictMode() = 0;
 };
 
 class TokenStream
 {
     /* Unicode separators that are treated as line terminators, in addition to \n, \r */
     enum {
         LINE_SEPARATOR = 0x2028,
         PARA_SEPARATOR = 0x2029
@@ -484,27 +478,28 @@ class TokenStream
 
     // TokenStream-specific error reporters.
     bool reportError(unsigned errorNumber, ...);
     bool reportWarning(unsigned errorNumber, ...);
 
     // General-purpose error reporters.  You should avoid calling these
     // directly, and instead use the more succinct alternatives (e.g.
     // reportError()) in TokenStream, Parser, and BytecodeEmitter.
-    bool reportCompileErrorNumberVA(ParseNode *pn, unsigned flags, unsigned errorNumber,
+    bool reportCompileErrorNumberVA(const TokenPos &pos, unsigned flags, unsigned errorNumber,
                                     va_list args);
-    bool reportStrictModeErrorNumberVA(ParseNode *pn, bool strictMode, unsigned errorNumber,
+    bool reportStrictModeErrorNumberVA(const TokenPos &pos, bool strictMode, unsigned errorNumber,
                                        va_list args);
-    bool reportStrictWarningErrorNumberVA(ParseNode *pn, unsigned errorNumber, va_list args);
+    bool reportStrictWarningErrorNumberVA(const TokenPos &pos, unsigned errorNumber,
+                                          va_list args);
 
   private:
     // These are private because they should only be called by the tokenizer
     // while tokenizing not by, for example, BytecodeEmitter.
     bool reportStrictModeError(unsigned errorNumber, ...);
-    bool strictMode() const { return strictModeGetter && strictModeGetter->get(); }
+    bool strictMode() const { return strictModeGetter && strictModeGetter->strictMode(); }
 
     void onError();
     static JSAtom *atomize(JSContext *cx, CharBuffer &cb);
     bool putIdentInTokenbuf(const jschar *identStart);
 
     /*
      * Enables flags in the associated tokenstream for the object lifetime.
      * Useful for lexically-scoped flag toggles.
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -380,17 +380,17 @@ inline void
 AutoGCRooter::trace(JSTracer *trc)
 {
     switch (tag_) {
       case JSVAL:
         MarkValueRoot(trc, &static_cast<AutoValueRooter *>(this)->val, "JS::AutoValueRooter.val");
         return;
 
       case PARSER:
-        static_cast<frontend::Parser *>(this)->trace(trc);
+        static_cast<frontend::Parser<frontend::FullParseHandler> *>(this)->trace(trc);
         return;
 
       case IDARRAY: {
         JSIdArray *ida = static_cast<AutoIdArray *>(this)->idArray;
         MarkIdRange(trc, ida->length, ida->vector, "JS::AutoIdArray.idArray");
         return;
       }
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5302,17 +5302,18 @@ JS_BufferIsCompilableUnit(JSContext *cx,
      * Return true on any out-of-memory error, so our caller doesn't try to
      * collect more buffered source.
      */
     result = JS_TRUE;
     exnState = JS_SaveExceptionState(cx);
     {
         CompileOptions options(cx);
         options.setCompileAndGo(false);
-        Parser parser(cx, options, chars, length, /* foldConstants = */ true);
+        Parser<frontend::FullParseHandler> parser(cx, options, chars, length,
+                                                  /* foldConstants = */ true);
         if (parser.init()) {
             older = JS_SetErrorReporter(cx, NULL);
             if (!parser.parse(obj) &&
                 parser.tokenStream.isUnexpectedEOF()) {
                 /*
                  * We ran into an error. If it was because we ran out of
                  * source, we return false so our caller knows to try to
                  * collect more buffered source.
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -155,20 +155,22 @@ namespace frontend {
 struct BytecodeEmitter;
 struct Definition;
 class FunctionBox;
 class ObjectBox;
 struct Token;
 struct TokenPos;
 struct TokenPtr;
 class TokenStream;
-struct Parser;
 class ParseMapPool;
 struct ParseNode;
 
+template <typename ParseHandler>
+struct Parser;
+
 } /* namespace frontend */
 
 namespace analyze {
 
 struct LifetimeVariable;
 class LoopAnalysis;
 class ScriptAnalysis;
 class SlotValue;
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -1452,17 +1452,17 @@ NodeBuilder::function(ASTType type, Toke
 /*
  * Serialization of parse nodes to JavaScript objects.
  *
  * All serialization methods take a non-nullable ParseNode pointer.
  */
 class ASTSerializer
 {
     JSContext           *cx;
-    Parser              *parser;
+    Parser<FullParseHandler> *parser;
     NodeBuilder         builder;
     DebugOnly<uint32_t> lineno;
 
     RawValue unrootedAtomContents(RawAtom atom) {
         return StringValue(atom ? atom : cx->names().empty);
     }
 
     BinaryOperator binop(ParseNodeKind kind, JSOp op);
@@ -1546,17 +1546,17 @@ class ASTSerializer
         , lineno(ln)
 #endif
     {}
 
     bool init(HandleObject userobj) {
         return builder.init(userobj);
     }
 
-    void setParser(Parser *p) {
+    void setParser(Parser<FullParseHandler> *p) {
         parser = p;
     }
 
     bool program(ParseNode *pn, MutableHandleValue dst);
 };
 
 AssignmentOperator
 ASTSerializer::aop(JSOp op)
@@ -2518,17 +2518,17 @@ ASTSerializer::expression(ParseNode *pn,
           return expression(pn->pn_kid, &expr) &&
                  builder.spreadExpression(expr, &pn->pn_pos, dst);
       }
 
       case PNK_OBJECT:
       {
         /* The parser notes any uninitialized properties by setting the PNX_DESTRUCT flag. */
         if (pn->pn_xflags & PNX_DESTRUCT) {
-            parser->reportError(pn, JSMSG_BAD_OBJECT_INIT);
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_OBJECT_INIT);
             return false;
         }
         NodeVector elts(cx);
         if (!elts.reserve(pn->pn_count))
             return false;
 
         for (ParseNode *next = pn->pn_head; next; next = next->pn_next) {
             JS_ASSERT(pn->pn_pos.encloses(next->pn_pos));
@@ -3026,17 +3026,17 @@ reflect_parse(JSContext *cx, uint32_t ar
     JSStableString *stable = src->ensureStable(cx);
     if (!stable)
         return JS_FALSE;
 
     const StableCharPtr chars = stable->chars();
     size_t length = stable->length();
     CompileOptions options(cx);
     options.setFileAndLine(filename, lineno);
-    Parser parser(cx, options, chars.get(), length, /* foldConstants = */ false);
+    Parser<FullParseHandler> parser(cx, options, chars.get(), length, /* foldConstants = */ false);
     if (!parser.init())
         return JS_FALSE;
 
     serialize.setParser(&parser);
 
     ParseNode *pn = parser.parse(NULL);
     if (!pn)
         return JS_FALSE;
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3059,47 +3059,93 @@ Compile(JSContext *cx, unsigned argc, js
     return ok;
 }
 
 static JSBool
 Parse(JSContext *cx, unsigned argc, jsval *vp)
 {
     using namespace js::frontend;
 
-    if (argc < 1) {
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (args.length() < 1) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
-                             "compile", "0", "s");
+                             "parse", "0", "s");
         return false;
     }
-    jsval arg0 = JS_ARGV(cx, vp)[0];
-    if (!JSVAL_IS_STRING(arg0)) {
-        const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, arg0));
+    if (!args[0].isString()) {
+        const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, args[0]));
         JS_ReportError(cx, "expected string to parse, got %s", typeName);
         return false;
     }
 
-    JSString *scriptContents = JSVAL_TO_STRING(arg0);
+    JSString *scriptContents = args[0].toString();
     CompileOptions options(cx);
     options.setFileAndLine("<string>", 1)
            .setCompileAndGo(false);
-    Parser parser(cx, options,
-                  JS_GetStringCharsZ(cx, scriptContents),
-                  JS_GetStringLength(scriptContents),
-                  /* foldConstants = */ true);
+    Parser<FullParseHandler> parser(cx, options,
+                                    JS_GetStringCharsZ(cx, scriptContents),
+                                    JS_GetStringLength(scriptContents),
+                                    /* foldConstants = */ true);
     if (!parser.init())
         return false;
 
     ParseNode *pn = parser.parse(NULL);
     if (!pn)
         return false;
 #ifdef DEBUG
     DumpParseTree(pn);
     fputc('\n', stderr);
 #endif
-    JS_SET_RVAL(cx, vp, JSVAL_VOID);
+    args.rval().setUndefined();
+    return true;
+}
+
+static JSBool
+SyntaxParse(JSContext *cx, unsigned argc, jsval *vp)
+{
+    using namespace js::frontend;
+
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (args.length() < 1) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
+                             "parse", "0", "s");
+        return false;
+    }
+    if (!args[0].isString()) {
+        const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, args[0]));
+        JS_ReportError(cx, "expected string to parse, got %s", typeName);
+        return false;
+    }
+
+    JSString *scriptContents = args[0].toString();
+    CompileOptions options(cx);
+    options.setFileAndLine("<string>", 1)
+           .setCompileAndGo(false);
+
+    const jschar *chars = JS_GetStringCharsZ(cx, scriptContents);
+    size_t length = JS_GetStringLength(scriptContents);
+    Parser<frontend::SyntaxParseHandler> parser(cx, options, chars, length,
+                                                /* foldConstants = */ false);
+    if (!parser.init())
+        return false;
+
+    bool succeeded = parser.parse(NULL);
+    if (cx->isExceptionPending())
+        return false;
+
+    if (!succeeded && !parser.hadUnknownResult()) {
+        // If no exception is posted, either there was an OOM or a language
+        // feature unhandled by the syntax parser was encountered.
+        JS_ASSERT(cx->runtime->hadOutOfMemory);
+        return false;
+    }
+
+    args.rval().setBoolean(succeeded);
     return true;
 }
 
 struct FreeOnReturn
 {
     JSContext *cx;
     const char *ptr;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
@@ -3698,16 +3744,20 @@ static JSFunctionSpecWithHelp shell_func
     JS_FN_HELP("compile", Compile, 1, 0,
 "compile(code)",
 "  Compiles a string to bytecode, potentially throwing."),
 
     JS_FN_HELP("parse", Parse, 1, 0,
 "parse(code)",
 "  Parses a string, potentially throwing."),
 
+    JS_FN_HELP("syntaxParse", SyntaxParse, 1, 0,
+"syntaxParse(code)",
+"  Check the syntax of a string, returning success value"),
+
     JS_FN_HELP("timeout", Timeout, 1, 0,
 "timeout([seconds], [func])",
 "  Get/Set the limit in seconds for the execution time for the current context.\n"
 "  A negative value (default) means that the execution time is unlimited.\n"
 "  If a second argument is provided, it will be invoked when the timer elapses.\n"),
 
     JS_FN_HELP("elapsed", Elapsed, 0, 0,
 "elapsed()",