Merge.
authorDavid Anderson <danderson@mozilla.com>
Thu, 11 Mar 2010 11:41:14 -0800
changeset 40234 9c0125dd3d3b5b1878f3d58b6126d16a3dce3887
parent 40233 887a3e05324c70444925aed9ce591c598a6f212b (current diff)
parent 40232 471665e9c47f29befc1c2037504e54f20d02af81 (diff)
child 40236 8102de796d51514831dde167d5a100c43741227e
push id12610
push userrsayre@mozilla.com
push dateMon, 05 Apr 2010 17:26:41 +0000
treeherdermozilla-central@1942c0b4e101 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone1.9.3a2pre
Merge.
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -490,16 +490,20 @@ RecycleFuncNameKids(JSParseNode *pn, JST
         }
         break;
 
       default:
         JS_ASSERT(PN_TYPE(pn) == TOK_FUNCTION);
     }
 }
 
+/*
+ * Allocate a JSParseNode from tc's node freelist or, failing that, from cx's
+ * temporary arena.
+ */
 static JSParseNode *
 NewOrRecycledNode(JSTreeContext *tc)
 {
     JSParseNode *pn, *pn2;
 
     pn = tc->compiler->nodeList;
     if (!pn) {
         JSContext *cx = tc->compiler->context;
@@ -564,74 +568,36 @@ NewOrRecycledNode(JSTreeContext *tc)
 #endif
         pn->pn_used = pn->pn_defn = false;
         memset(&pn->pn_u, 0, sizeof pn->pn_u);
         pn->pn_next = NULL;
     }
     return pn;
 }
 
-static inline void
-InitParseNode(JSParseNode *pn, JSTokenType type, JSOp op, JSParseNodeArity arity)
-{
-    pn->pn_type = type;
-    pn->pn_op = op;
-    pn->pn_arity = arity;
-    pn->pn_parens = false;
-    JS_ASSERT(!pn->pn_used);
-    JS_ASSERT(!pn->pn_defn);
-    pn->pn_next = pn->pn_link = NULL;
-}
-
-/*
- * Allocate a JSParseNode from tc's node freelist or, failing that, from cx's
- * temporary arena.
- */
-static JSParseNode *
-NewParseNode(JSParseNodeArity arity, JSTreeContext *tc)
+/* used only by static create methods of subclasses */
+
+JSParseNode *
+JSParseNode::create(JSParseNodeArity arity, JSTreeContext *tc)
 {
     JSParseNode *pn;
     JSToken *tp;
 
     pn = NewOrRecycledNode(tc);
     if (!pn)
         return NULL;
     tp = &CURRENT_TOKEN(&tc->compiler->tokenStream);
-    InitParseNode(pn, tp->type, JSOP_NOP, arity);
+    pn->init(tp->type, JSOP_NOP, arity);
     pn->pn_pos = tp->pos;
     return pn;
 }
 
-static inline void
-InitNameNodeCommon(JSParseNode *pn, JSTreeContext *tc)
-{
-    pn->pn_expr = NULL;
-    pn->pn_cookie = FREE_UPVAR_COOKIE;
-    pn->pn_dflags = tc->atTopLevel() ? PND_TOPLEVEL : 0;
-    if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
-        pn->pn_dflags |= PND_BLOCKCHILD;
-    pn->pn_blockid = tc->blockid();
-}
-
-static JSParseNode *
-NewNameNode(JSContext *cx, JSAtom *atom, JSTreeContext *tc)
-{
-    JSParseNode *pn;
-
-    pn = NewParseNode(PN_NAME, tc);
-    if (pn) {
-        pn->pn_atom = atom;
-        InitNameNodeCommon(pn, tc);
-    }
-    return pn;
-}
-
-static JSParseNode *
-NewBinary(JSTokenType tt, JSOp op, JSParseNode *left, JSParseNode *right,
-          JSTreeContext *tc)
+JSParseNode *
+JSParseNode::newBinaryOrAppend(JSTokenType tt, JSOp op, JSParseNode *left, JSParseNode *right,
+                               JSTreeContext *tc)
 {
     JSParseNode *pn, *pn1, *pn2;
 
     if (!left || !right)
         return NULL;
 
     /*
      * Flatten a left-associative (left-heavy) tree of a given operator into
@@ -682,23 +648,51 @@ NewBinary(JSTokenType tt, JSOp op, JSPar
         left->pn_pos.end = right->pn_pos.end;
         RecycleTree(right, tc);
         return left;
     }
 
     pn = NewOrRecycledNode(tc);
     if (!pn)
         return NULL;
-    InitParseNode(pn, tt, op, PN_BINARY);
+    pn->init(tt, op, PN_BINARY);
     pn->pn_pos.begin = left->pn_pos.begin;
     pn->pn_pos.end = right->pn_pos.end;
     pn->pn_left = left;
     pn->pn_right = right;
-    return pn;
-}
+    return (BinaryNode *)pn;
+}
+
+namespace js {
+
+inline void
+NameNode::initCommon(JSTreeContext *tc)
+{
+    pn_expr = NULL;
+    pn_cookie = FREE_UPVAR_COOKIE;
+    pn_dflags = tc->atTopLevel() ? PND_TOPLEVEL : 0;
+    if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
+        pn_dflags |= PND_BLOCKCHILD;
+    pn_blockid = tc->blockid();
+}
+
+NameNode *
+NameNode::create(JSAtom *atom, JSTreeContext *tc)
+{
+    JSParseNode *pn;
+
+    pn = JSParseNode::create(PN_NAME, tc);
+    if (pn) {
+        pn->pn_atom = atom;
+        ((NameNode *)pn)->initCommon(tc);
+    }
+    return (NameNode *)pn;
+}
+
+} /* namespace js */
 
 #if JS_HAS_GETTER_SETTER
 static JSTokenType
 CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
 {
     JSAtom *atom;
     JSRuntime *rt;
     JSOp op;
@@ -1255,18 +1249,17 @@ CheckStrictAssignment(JSContext *cx, JST
 
 /*
  * Check that it is permitted to introduce a binding for atom.  Strict
  * mode forbids introducing new definitions for 'eval' or 'arguments'.
  * Use pn for reporting error locations, or use tc's token stream if
  * pn is NULL.
  */
 bool
-CheckStrictBinding(JSContext *cx, JSTreeContext *tc, JSAtom *atom, 
-                   JSParseNode *pn)
+CheckStrictBinding(JSContext *cx, JSTreeContext *tc, JSAtom *atom, JSParseNode *pn)
 {
     if (!tc->needStrictChecks())
         return true;
 
     JSAtomState *atomState = &cx->runtime->atomState;
     if (atom == atomState->evalAtom || atom == atomState->argumentsAtom) {
         const char *name = js_AtomToPrintableString(cx, atom);
         if (name)
@@ -1277,17 +1270,17 @@ CheckStrictBinding(JSContext *cx, JSTree
     return true;
 }
 
 /*
  * In strict mode code, all formal parameter names must be distinct. If fun's
  * formals are legit given fun's strictness level, return true. Otherwise,
  * report an error and return false. Use pn for error position reporting,
  * unless we can find something more accurate in tc's decls.
- * 
+ *
  * In some cases the code to parse the argument list will already have noticed
  * the duplication; we could try to use that knowledge instead of re-checking
  * here. But since the strictness of the function's body determines what
  * constraints to apply to the argument list, we can't report the error until
  * after we've parsed the body. And as it turns out, the function's local name
  * list makes it reasonably cheap to find duplicates after the fact.
  */
 static bool
@@ -1354,17 +1347,17 @@ FunctionBody(JSContext *cx, JSTokenStrea
      * later, because we may have not peeked in ts yet, so Statements won't
      * acquire a valid pn->pn_pos.begin from the current token.
      */
     firstLine = ts->lineno;
 #if JS_HAS_EXPR_CLOSURES
     if (CURRENT_TOKEN(ts).type == TOK_LC) {
         pn = Statements(cx, ts, tc);
     } else {
-        pn = NewParseNode(PN_UNARY, tc);
+        pn = UnaryNode::create(tc);
         if (pn) {
             pn->pn_kid = AssignExpr(cx, ts, tc);
             if (!pn->pn_kid) {
                 pn = NULL;
             } else {
                 if (tc->flags & TCF_FUN_IS_GENERATOR) {
                     ReportBadReturn(cx, tc, JSREPORT_ERROR,
                                     JSMSG_BAD_GENERATOR_RETURN,
@@ -1400,18 +1393,17 @@ FunctionBody(JSContext *cx, JSTokenStrea
 
 static JSAtomListElement *
 MakePlaceholder(JSParseNode *pn, JSTreeContext *tc)
 {
     JSAtomListElement *ale = tc->lexdeps.add(tc->compiler, pn->pn_atom);
     if (!ale)
         return NULL;
 
-    JSDefinition *dn = (JSDefinition *)
-        NewNameNode(tc->compiler->context, pn->pn_atom, tc);
+    JSDefinition *dn = (JSDefinition *)NameNode::create(pn->pn_atom, tc);
     if (!dn)
         return NULL;
 
     ALE_SET_DEFN(ale, dn);
     dn->pn_defn = true;
     dn->pn_dflags |= PND_PLACEHOLDER;
     return ale;
 }
@@ -1583,29 +1575,29 @@ DefineArg(JSParseNode *pn, JSAtom *atom,
     if (atom == tc->compiler->context->runtime->atomState.evalAtom)
         tc->flags |= TCF_FUN_PARAM_EVAL;
 
     /*
      * Make an argument definition node, distinguished by being in tc->decls
      * but having TOK_NAME type and JSOP_NOP op. Insert it in a TOK_ARGSBODY
      * list node returned via pn->pn_body.
      */
-    argpn = NewNameNode(tc->compiler->context, atom, tc);
+    argpn = NameNode::create(atom, tc);
     if (!argpn)
         return false;
     JS_ASSERT(PN_TYPE(argpn) == TOK_NAME && PN_OP(argpn) == JSOP_NOP);
 
     /* Arguments are initialized by definition. */
     argpn->pn_dflags |= PND_INITIALIZED;
     if (!Define(argpn, atom, tc))
         return false;
 
     argsbody = pn->pn_body;
     if (!argsbody) {
-        argsbody = NewParseNode(PN_LIST, tc);
+        argsbody = ListNode::create(tc);
         if (!argsbody)
             return false;
         argsbody->pn_type = TOK_ARGSBODY;
         argsbody->pn_op = JSOP_NOP;
         argsbody->makeEmpty();
         pn->pn_body = argsbody;
     }
     argsbody->append(argpn);
@@ -1640,17 +1632,17 @@ JSCompiler::compileFunctionBody(JSContex
     JSCodeGenerator funcg(&jsc, &codePool, &notePool, jsc.tokenStream.lineno);
     funcg.flags |= TCF_IN_FUNCTION;
     funcg.fun = fun;
     if (!GenerateBlockId(&funcg, funcg.bodyid))
         return NULL;
 
     /* FIXME: make Function format the source for a function definition. */
     jsc.tokenStream.tokens[0].type = TOK_NAME;
-    JSParseNode *fn = NewParseNode(PN_FUNC, &funcg);
+    JSParseNode *fn = FunctionNode::create(&funcg);
     if (fn) {
         fn->pn_body = NULL;
         fn->pn_cookie = FREE_UPVAR_COOKIE;
 
         uintN nargs = fun->nargs;
         if (nargs) {
             jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
             if (!names) {
@@ -2023,17 +2015,17 @@ JSCompiler::markFunArgs(JSFunctionBox *f
 
                     JSFunctionBox *afunbox;
                     if (PN_OP(lexdep) == JSOP_CALLEE) {
                         /*
                          * A named function expression will not appear to be a
                          * funarg if it is immediately applied. However, if its
                          * name is used in an escaping function nested within
                          * it, then it must become flagged as a funarg again.
-                         * See bug 545980. 
+                         * See bug 545980.
                          */
                         afunbox = funbox;
                         uintN calleeLevel = UPVAR_FRAME_SKIP(lexdep->pn_cookie);
                         uintN staticLevel = afunbox->level + 1U;
                         while (staticLevel != calleeLevel) {
                             afunbox = afunbox->parent;
                             --staticLevel;
                         }
@@ -2621,17 +2613,17 @@ LeaveFunction(JSParseNode *fn, JSTreeCon
                     return false;
                 ALE_SET_DEFN(outer_ale, ALE_DEFN(ale));
             }
         }
 
         if (funtc->lexdeps.count - foundCallee != 0) {
             JSParseNode *body = fn->pn_body;
 
-            fn->pn_body = NewParseNode(PN_NAMESET, tc);
+            fn->pn_body = NameSetNode::create(tc);
             if (!fn->pn_body)
                 return false;
 
             fn->pn_body->pn_type = TOK_UPVARS;
             fn->pn_body->pn_pos = body->pn_pos;
             if (foundCallee)
                 funtc->lexdeps.remove(tc->compiler, funAtom);
             fn->pn_body->pn_names = funtc->lexdeps;
@@ -2658,17 +2650,17 @@ FunctionDef(JSContext *cx, JSTokenStream
     bool destructuringArg = false;
     JSAtom *duplicatedArg = NULL;
 #endif
 
     /* Make a TOK_FUNCTION node. */
 #if JS_HAS_GETTER_SETTER
     op = CURRENT_TOKEN(ts).t_op;
 #endif
-    pn = NewParseNode(PN_FUNC, tc);
+    pn = FunctionNode::create(tc);
     if (!pn)
         return NULL;
     pn->pn_body = NULL;
     pn->pn_cookie = FREE_UPVAR_COOKIE;
 
     /*
      * If a lambda, give up on JSOP_{GET,CALL}UPVAR usage unless this function
      * is immediately applied (we clear PND_FUNARG if so -- see MemberExpr).
@@ -2855,46 +2847,46 @@ FunctionDef(JSContext *cx, JSTokenStream
                 if (!js_AddLocal(cx, fun, NULL, JSLOCAL_ARG))
                     return NULL;
 
                 /*
                  * Synthesize a destructuring assignment from the single
                  * anonymous positional parameter into the destructuring
                  * left-hand-side expression and accumulate it in list.
                  */
-                rhs = NewNameNode(cx, cx->runtime->atomState.emptyAtom, &funtc);
+                rhs = NameNode::create(cx->runtime->atomState.emptyAtom, &funtc);
                 if (!rhs)
                     return NULL;
                 rhs->pn_type = TOK_NAME;
                 rhs->pn_op = JSOP_GETARG;
                 rhs->pn_cookie = MAKE_UPVAR_COOKIE(funtc.staticLevel, slot);
                 rhs->pn_dflags |= PND_BOUND;
 
-                item = NewBinary(TOK_ASSIGN, JSOP_NOP, lhs, rhs, &funtc);
+                item = JSParseNode::newBinaryOrAppend(TOK_ASSIGN, JSOP_NOP, lhs, rhs, &funtc);
                 if (!item)
                     return NULL;
                 if (!list) {
-                    list = NewParseNode(PN_LIST, &funtc);
+                    list = ListNode::create(&funtc);
                     if (!list)
                         return NULL;
                     list->pn_type = TOK_COMMA;
                     list->makeEmpty();
                 }
                 list->append(item);
                 break;
               }
 #endif /* JS_HAS_DESTRUCTURING */
 
               case TOK_NAME:
               {
                 JSAtom *atom = CURRENT_TOKEN(ts).t_atom;
                 if (!DefineArg(pn, atom, fun->nargs, &funtc))
                     return NULL;
 #ifdef JS_HAS_DESTRUCTURING
-                /* 
+                /*
                  * ECMA-262 requires us to support duplicate parameter names, but if the
                  * parameter list includes destructuring, we consider the code to have
                  * opted in to higher standards, and forbid duplicates. We may see a
                  * destructuring parameter later, so always note duplicates now.
                  *
                  * Duplicates are warned about (strict option) or cause errors (strict
                  * mode code), but we do those tests in one place below, after having
                  * parsed the body.
@@ -2970,27 +2962,27 @@ FunctionDef(JSContext *cx, JSTokenStream
      * scope node, we must make a special TOK_SEQ node, to prepend the formal
      * parameter destructuring code without bracing the decompilation of the
      * function body's lexical scope.
      */
     if (list) {
         if (body->pn_arity != PN_LIST) {
             JSParseNode *block;
 
-            block = NewParseNode(PN_LIST, tc);
+            block = ListNode::create(tc);
             if (!block)
                 return NULL;
             block->pn_type = TOK_SEQ;
             block->pn_pos = body->pn_pos;
             block->initList(body);
 
             body = block;
         }
 
-        item = NewParseNode(PN_UNARY, tc);
+        item = UnaryNode::create(tc);
         if (!item)
             return NULL;
 
         item->pn_type = TOK_SEMI;
         item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
         item->pn_kid = list;
         item->pn_next = body->pn_head;
         body->pn_head = item;
@@ -3029,17 +3021,17 @@ FunctionDef(JSContext *cx, JSTokenStream
     } else if (!funAtom) {
         /*
          * If this anonymous function definition is *not* embedded within a
          * larger expression, we treat it as an expression statement, not as
          * a function declaration -- and not as a syntax error (as ECMA-262
          * Edition 3 would have it).  Backward compatibility must trump all,
          * unless JSOPTION_ANONFUNFIX is set.
          */
-        result = NewParseNode(PN_UNARY, tc);
+        result = UnaryNode::create(tc);
         if (!result)
             return NULL;
         result->pn_type = TOK_SEMI;
         result->pn_pos = pn->pn_pos;
         result->pn_kid = pn;
         op = JSOP_LAMBDA;
     } else if (!topLevel) {
         /*
@@ -3130,17 +3122,17 @@ static JSParseNode *
 Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
 {
     JSParseNode *pn, *pn2, *saveBlock;
     JSTokenType tt;
     bool inDirectivePrologue = tc->atTopLevel();
 
     JS_CHECK_RECURSION(cx, return NULL);
 
-    pn = NewParseNode(PN_LIST, tc);
+    pn = ListNode::create(tc);
     if (!pn)
         return NULL;
     pn->pn_type = TOK_LC;
     pn->makeEmpty();
     pn->pn_blockid = tc->blockid();
     saveBlock = tc->blockNode;
     tc->blockNode = pn;
 
@@ -3439,17 +3431,17 @@ BindVarOrConst(JSContext *cx, BindData *
 
         data->fresh = false;
 
         if (!pn->pn_used) {
             /* Make pnu be a fresh name node that uses dn. */
             JSParseNode *pnu = pn;
 
             if (pn->pn_defn) {
-                pnu = NewNameNode(cx, atom, tc);
+                pnu = NameNode::create(atom, tc);
                 if (!pnu)
                     return JS_FALSE;
             }
 
             LinkUseToDef(pnu, dn, tc);
             pnu->pn_op = JSOP_NAME;
         }
 
@@ -3476,17 +3468,17 @@ BindVarOrConst(JSContext *cx, BindData *
         if (!pn->pn_defn) {
             JSHashEntry **hep;
 
             ale = tc->lexdeps.rawLookup(atom, hep);
             if (ale) {
                 pn = ALE_DEFN(ale);
                 tc->lexdeps.rawRemove(tc->compiler, ale, hep);
             } else {
-                JSParseNode *pn2 = NewNameNode(cx, atom, tc);
+                JSParseNode *pn2 = NameNode::create(atom, tc);
                 if (!pn2)
                     return JS_FALSE;
 
                 /* The token stream may be past the location for pn. */
                 pn2->pn_type = TOK_NAME;
                 pn2->pn_pos = pn->pn_pos;
                 pn = pn2;
             }
@@ -4310,17 +4302,17 @@ ReturnOrYield(JSContext *cx, JSTokenStre
 
     tt = CURRENT_TOKEN(ts).type;
     if (tt == TOK_RETURN && !(tc->flags & TCF_IN_FUNCTION)) {
         js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
                                     JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
         return NULL;
     }
 
-    pn = NewParseNode(PN_UNARY, tc);
+    pn = UnaryNode::create(tc);
     if (!pn)
         return NULL;
 
 #if JS_HAS_GENERATORS
     if (tt == TOK_YIELD)
         tc->flags |= TCF_FUN_IS_GENERATOR;
 #endif
 
@@ -4376,17 +4368,17 @@ ReturnOrYield(JSContext *cx, JSTokenStre
 static JSParseNode *
 PushLexicalScope(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
                  JSStmtInfo *stmt)
 {
     JSParseNode *pn;
     JSObject *obj;
     JSObjectBox *blockbox;
 
-    pn = NewParseNode(PN_NAME, tc);
+    pn = LexicalScopeNode::create(tc);
     if (!pn)
         return NULL;
 
     obj = js_NewBlockObject(cx);
     if (!obj)
         return NULL;
 
     blockbox = tc->compiler->newObjectBox(obj);
@@ -4411,17 +4403,17 @@ static JSParseNode *
 LetBlock(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool statement)
 {
     JSParseNode *pn, *pnblock, *pnlet;
     JSStmtInfo stmtInfo;
 
     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LET);
 
     /* Create the let binary node. */
-    pnlet = NewParseNode(PN_BINARY, tc);
+    pnlet = BinaryNode::create(tc);
     if (!pnlet)
         return NULL;
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET);
 
     /* This is a let block or expression of the form: let (a, b, c) .... */
     pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo);
     if (!pnblock)
@@ -4438,17 +4430,17 @@ LetBlock(JSContext *cx, JSTokenStream *t
 
     ts->flags |= TSF_OPERAND;
     if (statement && !js_MatchToken(cx, ts, TOK_LC)) {
         /*
          * 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.
          */
-        pn = NewParseNode(PN_UNARY, tc);
+        pn = UnaryNode::create(tc);
         if (!pn)
             return NULL;
         pn->pn_type = TOK_SEMI;
         pn->pn_num = -1;
         pn->pn_kid = pnblock;
 
         statement = JS_FALSE;
     }
@@ -4517,17 +4509,17 @@ NewBindingNode(JSAtom *atom, JSTreeConte
                 pn->pn_blockid = tc->blockid();
 
             tc->lexdeps.remove(tc->compiler, atom);
             return pn;
         }
     }
 
     /* Make a new node for this declarator name (or destructuring pattern). */
-    pn = NewNameNode(tc->compiler->context, atom, tc);
+    pn = NameNode::create(atom, tc);
     if (!pn)
         return NULL;
     return pn;
 }
 
 #if JS_HAS_BLOCK_SCOPE
 static bool
 RebindLets(JSParseNode *pn, JSTreeContext *tc)
@@ -4633,17 +4625,17 @@ Statement(JSContext *cx, JSTokenStream *
         ts->flags &= ~TSF_KEYWORD_IS_NAME;
         if (tt == TOK_DBLCOLON)
             goto expression;
 #endif
         return FunctionStmt(cx, ts, tc);
 
       case TOK_IF:
         /* An IF node has three kids: condition, then, and optional else. */
-        pn = NewParseNode(PN_TERNARY, tc);
+        pn = TernaryNode::create(tc);
         if (!pn)
             return NULL;
         pn1 = Condition(cx, ts, tc);
         if (!pn1)
             return NULL;
         js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
         pn2 = Statement(cx, ts, tc);
         if (!pn2)
@@ -4667,17 +4659,17 @@ Statement(JSContext *cx, JSTokenStream *
         pn->pn_kid3 = pn3;
         return pn;
 
       case TOK_SWITCH:
       {
         JSParseNode *pn5, *saveBlock;
         JSBool seenDefault = JS_FALSE;
 
-        pn = NewParseNode(PN_BINARY, tc);
+        pn = BinaryNode::create(tc);
         if (!pn)
             return NULL;
         MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
 
         /* pn1 points to the switch's discriminant. */
         pn1 = ParenExpr(cx, ts, tc, NULL, NULL);
         if (!pn1)
             return NULL;
@@ -4687,17 +4679,17 @@ Statement(JSContext *cx, JSTokenStream *
 
         /*
          * NB: we must push stmtInfo before calling GenerateBlockIdForStmtNode
          * because that function states tc->topStmt->blockid.
          */
         js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
 
         /* pn2 is a list of case nodes. The default case has pn_left == NULL */
-        pn2 = NewParseNode(PN_LIST, tc);
+        pn2 = ListNode::create(tc);
         if (!pn2)
             return NULL;
         pn2->makeEmpty();
         if (!GenerateBlockIdForStmtNode(pn2, tc))
             return NULL;
         saveBlock = tc->blockNode;
         tc->blockNode = pn2;
 
@@ -4708,17 +4700,17 @@ Statement(JSContext *cx, JSTokenStream *
                     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
                                                 JSMSG_TOO_MANY_DEFAULTS);
                     return NULL;
                 }
                 seenDefault = JS_TRUE;
                 /* FALL THROUGH */
 
               case TOK_CASE:
-                pn3 = NewParseNode(PN_BINARY, tc);
+                pn3 = BinaryNode::create(tc);
                 if (!pn3)
                     return NULL;
                 if (tt == TOK_CASE) {
                     pn3->pn_left = Expr(cx, ts, tc);
                     if (!pn3->pn_left)
                         return NULL;
                 }
                 pn2->append(pn3);
@@ -4734,17 +4726,17 @@ Statement(JSContext *cx, JSTokenStream *
 
               default:
                 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
                                             JSMSG_BAD_SWITCH);
                 return NULL;
             }
             MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
 
-            pn4 = NewParseNode(PN_LIST, tc);
+            pn4 = ListNode::create(tc);
             if (!pn4)
                 return NULL;
             pn4->pn_type = TOK_LC;
             pn4->makeEmpty();
             ts->flags |= TSF_OPERAND;
             while ((tt = js_PeekToken(cx, ts)) != TOK_RC &&
                    tt != TOK_CASE && tt != TOK_DEFAULT) {
                 ts->flags &= ~TSF_OPERAND;
@@ -4779,34 +4771,34 @@ Statement(JSContext *cx, JSTokenStream *
 
         pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
         pn->pn_left = pn1;
         pn->pn_right = pn2;
         return pn;
       }
 
       case TOK_WHILE:
-        pn = NewParseNode(PN_BINARY, tc);
+        pn = BinaryNode::create(tc);
         if (!pn)
             return NULL;
         js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
         pn2 = Condition(cx, ts, tc);
         if (!pn2)
             return NULL;
         pn->pn_left = pn2;
         pn2 = Statement(cx, ts, tc);
         if (!pn2)
             return NULL;
         PopStatement(tc);
         pn->pn_pos.end = pn2->pn_pos.end;
         pn->pn_right = pn2;
         return pn;
 
       case TOK_DO:
-        pn = NewParseNode(PN_BINARY, tc);
+        pn = BinaryNode::create(tc);
         if (!pn)
             return NULL;
         js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
         pn2 = Statement(cx, ts, tc);
         if (!pn2)
             return NULL;
         pn->pn_left = pn2;
         MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
@@ -4831,17 +4823,17 @@ Statement(JSContext *cx, JSTokenStream *
       {
         JSParseNode *pnseq = NULL;
 #if JS_HAS_BLOCK_SCOPE
         JSParseNode *pnlet = NULL;
         JSStmtInfo blockInfo;
 #endif
 
         /* A FOR node is binary, left is loop control and right is the body. */
-        pn = NewParseNode(PN_BINARY, tc);
+        pn = BinaryNode::create(tc);
         if (!pn)
             return NULL;
         js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
 
         pn->pn_op = JSOP_ITER;
         pn->pn_iflags = 0;
         if (js_MatchToken(cx, ts, TOK_NAME)) {
             if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom)
@@ -4967,29 +4959,29 @@ Statement(JSContext *cx, JSTokenStream *
                  * 'var' and 'const'.
                  */
                 pn2 = pn1->pn_head;
                 if ((pn2->pn_type == TOK_NAME && pn2->maybeExpr())
 #if JS_HAS_DESTRUCTURING
                     || pn2->pn_type == TOK_ASSIGN
 #endif
                     ) {
-                    pnseq = NewParseNode(PN_LIST, tc);
+                    pnseq = ListNode::create(tc);
                     if (!pnseq)
                         return NULL;
                     pnseq->pn_type = TOK_SEQ;
                     pnseq->pn_pos.begin = pn->pn_pos.begin;
 
 #if JS_HAS_BLOCK_SCOPE
                     if (tt == TOK_LET) {
                         /*
                          * Hoist just the 'i' from 'for (let x = i in o)' to
                          * before the loop, glued together via pnseq.
                          */
-                        pn3 = NewParseNode(PN_UNARY, tc);
+                        pn3 = UnaryNode::create(tc);
                         if (!pn3)
                             return NULL;
                         pn3->pn_type = TOK_SEMI;
                         pn3->pn_op = JSOP_NOP;
 #if JS_HAS_DESTRUCTURING
                         if (pn2->pn_type == TOK_ASSIGN) {
                             pn4 = pn2->pn_right;
                             pn2 = pn1->pn_head = pn2->pn_left;
@@ -5025,17 +5017,17 @@ Statement(JSContext *cx, JSTokenStream *
                         if (pn2->pn_type == TOK_ASSIGN) {
                             pn1 = CloneParseTree(pn2->pn_left, tc);
                             if (!pn1)
                                 return NULL;
                         } else
 #endif
                         {
                             JS_ASSERT(pn2->pn_type == TOK_NAME);
-                            pn1 = NewNameNode(cx, pn2->pn_atom, tc);
+                            pn1 = NameNode::create(pn2->pn_atom, tc);
                             if (!pn1)
                                 return NULL;
                             pn1->pn_type = TOK_NAME;
                             pn1->pn_op = JSOP_NAME;
                             pn1->pn_pos = pn2->pn_pos;
                             if (pn2->pn_defn)
                                 LinkUseToDef(pn1, (JSDefinition *) pn2, tc);
                         }
@@ -5099,17 +5091,17 @@ Statement(JSContext *cx, JSTokenStream *
                 tc->topStmt = save->down;
 #endif
             pn2 = Expr(cx, ts, tc);
 #if JS_HAS_BLOCK_SCOPE
             if (let)
                 tc->topStmt = save;
 #endif
 
-            pn2 = NewBinary(TOK_IN, JSOP_NOP, pn1, pn2, tc);
+            pn2 = JSParseNode::newBinaryOrAppend(TOK_IN, JSOP_NOP, pn1, pn2, tc);
             if (!pn2)
                 return NULL;
             pn->pn_left = pn2;
         } else {
             if (pn->pn_iflags & JSITER_FOREACH)
                 goto bad_for_each;
             pn->pn_op = JSOP_NOP;
 
@@ -5135,17 +5127,17 @@ Statement(JSContext *cx, JSTokenStream *
                 pn3 = NULL;
             } else {
                 pn3 = Expr(cx, ts, tc);
                 if (!pn3)
                     return NULL;
             }
 
             /* Build the FORHEAD node to use as the left kid of pn. */
-            pn4 = NewParseNode(PN_TERNARY, tc);
+            pn4 = TernaryNode::create(tc);
             if (!pn4)
                 return NULL;
             pn4->pn_type = TOK_FORHEAD;
             pn4->pn_op = JSOP_NOP;
             pn4->pn_kid1 = pn1;
             pn4->pn_kid2 = pn2;
             pn4->pn_kid3 = pn3;
             pn->pn_left = pn4;
@@ -5198,34 +5190,34 @@ Statement(JSContext *cx, JSTokenStream *
          * 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.
          */
-        pn = NewParseNode(PN_TERNARY, tc);
+        pn = TernaryNode::create(tc);
         if (!pn)
             return NULL;
         pn->pn_op = JSOP_NOP;
 
         MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
         if (!PushBlocklikeStatement(&stmtInfo, STMT_TRY, tc))
             return NULL;
         pn->pn_kid1 = Statements(cx, ts, tc);
         if (!pn->pn_kid1)
             return NULL;
         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
         PopStatement(tc);
 
         catchList = NULL;
         tt = js_GetToken(cx, ts);
         if (tt == TOK_CATCH) {
-            catchList = NewParseNode(PN_LIST, tc);
+            catchList = ListNode::create(tc);
             if (!catchList)
                 return NULL;
             catchList->pn_type = TOK_RESERVED;
             catchList->makeEmpty();
             lastCatch = NULL;
 
             do {
                 JSParseNode *pnblock;
@@ -5249,17 +5241,17 @@ Statement(JSContext *cx, JSTokenStream *
 
                 /*
                  * 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)
                  */
-                pn2 = NewParseNode(PN_TERNARY, tc);
+                pn2 = TernaryNode::create(tc);
                 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
@@ -5344,17 +5336,17 @@ Statement(JSContext *cx, JSTokenStream *
             js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
                                         JSMSG_CATCH_OR_FINALLY);
             return NULL;
         }
         return pn;
       }
 
       case TOK_THROW:
-        pn = NewParseNode(PN_UNARY, tc);
+        pn = UnaryNode::create(tc);
         if (!pn)
             return NULL;
 
         /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
         ts->flags |= TSF_OPERAND;
         tt = js_PeekTokenSameLine(cx, ts);
         ts->flags &= ~TSF_OPERAND;
         if (tt == TOK_ERROR)
@@ -5380,17 +5372,17 @@ Statement(JSContext *cx, JSTokenStream *
         return NULL;
 
       case TOK_FINALLY:
         js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
                                     JSMSG_FINALLY_WITHOUT_TRY);
         return NULL;
 
       case TOK_BREAK:
-        pn = NewParseNode(PN_NULLARY, tc);
+        pn = NullaryNode::create(tc);
         if (!pn)
             return NULL;
         if (!MatchLabel(cx, ts, pn))
             return NULL;
         stmt = tc->topStmt;
         label = pn->pn_atom;
         if (label) {
             for (; ; stmt = stmt->down) {
@@ -5413,17 +5405,17 @@ Statement(JSContext *cx, JSTokenStream *
                     break;
             }
         }
         if (label)
             pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
         break;
 
       case TOK_CONTINUE:
-        pn = NewParseNode(PN_NULLARY, tc);
+        pn = NullaryNode::create(tc);
         if (!pn)
             return NULL;
         if (!MatchLabel(cx, ts, pn))
             return NULL;
         stmt = tc->topStmt;
         label = pn->pn_atom;
         if (label) {
             for (stmt2 = NULL; ; stmt = stmt->down) {
@@ -5471,17 +5463,17 @@ Statement(JSContext *cx, JSTokenStream *
          * https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1.
          */
         if (tc->flags & TCF_STRICT_MODE_CODE) {
             js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
                                         JSMSG_STRICT_CODE_WITH);
             return NULL;
         }
 
-        pn = NewParseNode(PN_BINARY, tc);
+        pn = BinaryNode::create(tc);
         if (!pn)
             return NULL;
         MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
         pn2 = ParenExpr(cx, ts, tc, NULL, NULL);
         if (!pn2)
             return NULL;
         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
         pn->pn_left = pn2;
@@ -5601,17 +5593,17 @@ Statement(JSContext *cx, JSTokenStream *
             stmt->blockObj = obj;
 
 #ifdef DEBUG
             pn1 = tc->blockNode;
             JS_ASSERT(!pn1 || pn1->pn_type != TOK_LEXICALSCOPE);
 #endif
 
             /* Create a new lexical scope node for these statements. */
-            pn1 = NewParseNode(PN_NAME, tc);
+            pn1 = LexicalScopeNode::create(tc);
             if (!pn1)
                 return NULL;
 
             pn1->pn_type = TOK_LEXICALSCOPE;
             pn1->pn_op = JSOP_LEAVEBLOCK;
             pn1->pn_pos = tc->blockNode->pn_pos;
             pn1->pn_objbox = blockbox;
             pn1->pn_expr = tc->blockNode;
@@ -5657,35 +5649,35 @@ Statement(JSContext *cx, JSTokenStream *
             pn->pn_xflags |= PNX_NEEDBRACES;
         }
         tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_RETURN_FLAGS));
         return pn;
       }
 
       case TOK_EOL:
       case TOK_SEMI:
-        pn = NewParseNode(PN_UNARY, tc);
+        pn = UnaryNode::create(tc);
         if (!pn)
             return NULL;
         pn->pn_type = TOK_SEMI;
         return pn;
 
 #if JS_HAS_DEBUGGER_KEYWORD
       case TOK_DEBUGGER:
-        pn = NewParseNode(PN_NULLARY, tc);
+        pn = NullaryNode::create(tc);
         if (!pn)
             return NULL;
         pn->pn_type = TOK_DEBUGGER;
         tc->flags |= TCF_FUN_HEAVYWEIGHT;
         break;
 #endif /* JS_HAS_DEBUGGER_KEYWORD */
 
 #if JS_HAS_XML_SUPPORT
       case TOK_DEFAULT:
-        pn = NewParseNode(PN_UNARY, tc);
+        pn = UnaryNode::create(tc);
         if (!pn)
             return NULL;
         if (!js_MatchToken(cx, ts, TOK_NAME) ||
             CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.xmlAtom ||
             !js_MatchToken(cx, ts, TOK_NAME) ||
             CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.namespaceAtom ||
             !js_MatchToken(cx, ts, TOK_ASSIGN) ||
             CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
@@ -5752,17 +5744,17 @@ Statement(JSContext *cx, JSTokenStream *
             /* Pop the label, set pn_expr, and return early. */
             PopStatement(tc);
             pn2->pn_type = TOK_COLON;
             pn2->pn_pos.end = pn->pn_pos.end;
             pn2->pn_expr = pn;
             return pn2;
         }
 
-        pn = NewParseNode(PN_UNARY, tc);
+        pn = UnaryNode::create(tc);
         if (!pn)
             return NULL;
         pn->pn_type = TOK_SEMI;
         pn->pn_pos = pn2->pn_pos;
         pn->pn_kid = pn2;
 
         switch (PN_TYPE(pn2)) {
           case TOK_LP:
@@ -5841,17 +5833,17 @@ Variables(JSContext *cx, JSTokenStream *
         while (scopeStmt && !(scopeStmt->flags & SIF_SCOPE)) {
             JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt));
             scopeStmt = scopeStmt->downScope;
         }
         JS_ASSERT(scopeStmt);
     }
 
     data.op = let ? JSOP_NOP : CURRENT_TOKEN(ts).t_op;
-    pn = NewParseNode(PN_LIST, tc);
+    pn = ListNode::create(tc);
     if (!pn)
         return NULL;
     pn->pn_op = data.op;
     pn->makeEmpty();
 
     /*
      * SpiderMonkey const is really "write once per initialization evaluation"
      * var, whereas let is block scoped. ES-Harmony wants block-scoped const so
@@ -5899,17 +5891,17 @@ Variables(JSContext *cx, JSTokenStream *
                 tc->topStmt = save;
                 tc->topScopeStmt = saveScope;
             }
 #endif
 
             if (!init || !UndominateInitializers(pn2, init, tc))
                 return NULL;
 
-            pn2 = NewBinary(TOK_ASSIGN, JSOP_NOP, pn2, init, tc);
+            pn2 = JSParseNode::newBinaryOrAppend(TOK_ASSIGN, JSOP_NOP, pn2, init, tc);
             if (!pn2)
                 return NULL;
             pn->append(pn2);
             continue;
         }
 #endif /* JS_HAS_DESTRUCTURING */
 
         if (tt != TOK_NAME) {
@@ -5994,17 +5986,17 @@ bad_var_init:
 
 static JSParseNode *
 Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
 {
     JSParseNode *pn, *pn2;
 
     pn = AssignExpr(cx, ts, tc);
     if (pn && js_MatchToken(cx, ts, TOK_COMMA)) {
-        pn2 = NewParseNode(PN_LIST, tc);
+        pn2 = ListNode::create(tc);
         if (!pn2)
             return NULL;
         pn2->pn_pos.begin = pn->pn_pos.begin;
         pn2->initList(pn);
         pn = pn2;
         do {
 #if JS_HAS_GENERATORS
             pn2 = pn->last();
@@ -6080,17 +6072,17 @@ AssignExpr(JSContext *cx, JSTokenStream 
         if (op != JSOP_NOP) {
             js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
                                         JSMSG_BAD_DESTRUCT_ASS);
             return NULL;
         }
         rhs = AssignExpr(cx, ts, tc);
         if (!rhs || !CheckDestructuring(cx, NULL, pn, rhs, tc))
             return NULL;
-        return NewBinary(TOK_ASSIGN, op, pn, rhs, tc);
+        return JSParseNode::newBinaryOrAppend(TOK_ASSIGN, op, pn, rhs, tc);
 #endif
       case TOK_LP:
         if (!MakeSetCall(cx, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
             return NULL;
         break;
 #if JS_HAS_XML_SUPPORT
       case TOK_UNARYOP:
         if (pn->pn_op == JSOP_XMLNAME) {
@@ -6117,29 +6109,29 @@ AssignExpr(JSContext *cx, JSTokenStream 
          * side of this assignment, i.e., the initializer.
          */
         if (!dn->isAssigned()) {
             JS_ASSERT(dn->isInitialized());
             dn->pn_pos.end = rhs->pn_pos.end;
         }
     }
 
-    return NewBinary(TOK_ASSIGN, op, pn, rhs, tc);
+    return JSParseNode::newBinaryOrAppend(TOK_ASSIGN, op, pn, rhs, tc);
 }
 
 static JSParseNode *
 CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
 {
     JSParseNode *pn, *pn1, *pn2, *pn3;
     uintN oldflags;
 
     pn = OrExpr(cx, ts, tc);
     if (pn && js_MatchToken(cx, ts, TOK_HOOK)) {
         pn1 = pn;
-        pn = NewParseNode(PN_TERNARY, tc);
+        pn = TernaryNode::create(tc);
         if (!pn)
             return NULL;
         /*
          * 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.
          */
         oldflags = tc->flags;
@@ -6164,78 +6156,77 @@ CondExpr(JSContext *cx, JSTokenStream *t
 
 static JSParseNode *
 OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
 {
     JSParseNode *pn;
 
     pn = AndExpr(cx, ts, tc);
     while (pn && js_MatchToken(cx, ts, TOK_OR))
-        pn = NewBinary(TOK_OR, JSOP_OR, pn, AndExpr(cx, ts, tc), tc);
+        pn = JSParseNode::newBinaryOrAppend(TOK_OR, JSOP_OR, pn, AndExpr(cx, ts, tc), tc);
     return pn;
 }
 
 static JSParseNode *
 AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
 {
     JSParseNode *pn;
 
     pn = BitOrExpr(cx, ts, tc);
     while (pn && js_MatchToken(cx, ts, TOK_AND))
-        pn = NewBinary(TOK_AND, JSOP_AND, pn, BitOrExpr(cx, ts, tc), tc);
+        pn = JSParseNode::newBinaryOrAppend(TOK_AND, JSOP_AND, pn, BitOrExpr(cx, ts, tc), tc);
     return pn;
 }
 
 static JSParseNode *
 BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
 {
     JSParseNode *pn;
 
     pn = BitXorExpr(cx, ts, tc);
     while (pn && js_MatchToken(cx, ts, TOK_BITOR)) {
-        pn = NewBinary(TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc),
-                       tc);
+        pn = JSParseNode::newBinaryOrAppend(TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc), tc);
     }
     return pn;
 }
 
 static JSParseNode *
 BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
 {
     JSParseNode *pn;
 
     pn = BitAndExpr(cx, ts, tc);
     while (pn && js_MatchToken(cx, ts, TOK_BITXOR)) {
-        pn = NewBinary(TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc),
-                       tc);
+        pn = JSParseNode::newBinaryOrAppend(TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc),
+                                            tc);
     }
     return pn;
 }
 
 static JSParseNode *
 BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
 {
     JSParseNode *pn;
 
     pn = EqExpr(cx, ts, tc);
     while (pn && js_MatchToken(cx, ts, TOK_BITAND))
-        pn = NewBinary(TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc), tc);
+        pn = JSParseNode::newBinaryOrAppend(TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc), tc);
     return pn;
 }
 
 static JSParseNode *
 EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
 {
     JSParseNode *pn;
     JSOp op;
 
     pn = RelExpr(cx, ts, tc);
     while (pn && js_MatchToken(cx, ts, TOK_EQOP)) {
         op = CURRENT_TOKEN(ts).t_op;
-        pn = NewBinary(TOK_EQOP, op, pn, RelExpr(cx, ts, tc), tc);
+        pn = JSParseNode::newBinaryOrAppend(TOK_EQOP, op, pn, RelExpr(cx, ts, tc), tc);
     }
     return pn;
 }
 
 static JSParseNode *
 RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
 {
     JSParseNode *pn;
@@ -6255,34 +6246,34 @@ RelExpr(JSContext *cx, JSTokenStream *ts
             /*
              * Recognize the 'in' token as an operator only if we're not
              * currently in the init expr of a for loop.
              */
             (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN)) ||
             js_MatchToken(cx, ts, TOK_INSTANCEOF))) {
         tt = CURRENT_TOKEN(ts).type;
         op = CURRENT_TOKEN(ts).t_op;
-        pn = NewBinary(tt, op, pn, ShiftExpr(cx, ts, tc), tc);
+        pn = JSParseNode::newBinaryOrAppend(tt, op, pn, ShiftExpr(cx, ts, tc), tc);
     }
     /* Restore previous state of inForInit flag. */
     tc->flags |= inForInitFlag;
 
     return pn;
 }
 
 static JSParseNode *
 ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
 {
     JSParseNode *pn;
     JSOp op;
 
     pn = AddExpr(cx, ts, tc);
     while (pn && js_MatchToken(cx, ts, TOK_SHOP)) {
         op = CURRENT_TOKEN(ts).t_op;
-        pn = NewBinary(TOK_SHOP, op, pn, AddExpr(cx, ts, tc), tc);
+        pn = JSParseNode::newBinaryOrAppend(TOK_SHOP, op, pn, AddExpr(cx, ts, tc), tc);
     }
     return pn;
 }
 
 static JSParseNode *
 AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
 {
     JSParseNode *pn;
@@ -6290,17 +6281,17 @@ AddExpr(JSContext *cx, JSTokenStream *ts
     JSOp op;
 
     pn = MulExpr(cx, ts, tc);
     while (pn &&
            (js_MatchToken(cx, ts, TOK_PLUS) ||
             js_MatchToken(cx, ts, TOK_MINUS))) {
         tt = CURRENT_TOKEN(ts).type;
         op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;
-        pn = NewBinary(tt, op, pn, MulExpr(cx, ts, tc), tc);
+        pn = JSParseNode::newBinaryOrAppend(tt, op, pn, MulExpr(cx, ts, tc), tc);
     }
     return pn;
 }
 
 static JSParseNode *
 MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
 {
     JSParseNode *pn;
@@ -6308,17 +6299,17 @@ MulExpr(JSContext *cx, JSTokenStream *ts
     JSOp op;
 
     pn = UnaryExpr(cx, ts, tc);
     while (pn &&
            (js_MatchToken(cx, ts, TOK_STAR) ||
             js_MatchToken(cx, ts, TOK_DIVOP))) {
         tt = CURRENT_TOKEN(ts).type;
         op = CURRENT_TOKEN(ts).t_op;
-        pn = NewBinary(tt, op, pn, UnaryExpr(cx, ts, tc), tc);
+        pn = JSParseNode::newBinaryOrAppend(tt, op, pn, UnaryExpr(cx, ts, tc), tc);
     }
     return pn;
 }
 
 static JSParseNode *
 SetLvalKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
            JSParseNode *pn, JSParseNode *kid, const char *name)
 {
@@ -6401,43 +6392,43 @@ UnaryExpr(JSContext *cx, JSTokenStream *
     ts->flags |= TSF_OPERAND;
     tt = js_GetToken(cx, ts);
     ts->flags &= ~TSF_OPERAND;
 
     switch (tt) {
       case TOK_UNARYOP:
       case TOK_PLUS:
       case TOK_MINUS:
-        pn = NewParseNode(PN_UNARY, tc);
+        pn = UnaryNode::create(tc);
         if (!pn)
             return NULL;
         pn->pn_type = TOK_UNARYOP;      /* PLUS and MINUS are binary */
         pn->pn_op = CURRENT_TOKEN(ts).t_op;
         pn2 = UnaryExpr(cx, ts, tc);
         if (!pn2)
             return NULL;
         pn->pn_pos.end = pn2->pn_pos.end;
         pn->pn_kid = pn2;
         break;
 
       case TOK_INC:
       case TOK_DEC:
-        pn = NewParseNode(PN_UNARY, tc);
+        pn = UnaryNode::create(tc);
         if (!pn)
             return NULL;
         pn2 = MemberExpr(cx, ts, tc, JS_TRUE);
         if (!pn2)
             return NULL;
         if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE))
             return NULL;
         pn->pn_pos.end = pn2->pn_pos.end;
         break;
 
       case TOK_DELETE:
-        pn = NewParseNode(PN_UNARY, tc);
+        pn = UnaryNode::create(tc);
         if (!pn)
             return NULL;
         pn2 = UnaryExpr(cx, ts, tc);
         if (!pn2)
             return NULL;
         pn->pn_pos.end = pn2->pn_pos.end;
 
         /*
@@ -6475,17 +6466,17 @@ UnaryExpr(JSContext *cx, JSTokenStream *
 
         /* Don't look across a newline boundary for a postfix incop. */
         if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
             ts->flags |= TSF_OPERAND;
             tt = js_PeekTokenSameLine(cx, ts);
             ts->flags &= ~TSF_OPERAND;
             if (tt == TOK_INC || tt == TOK_DEC) {
                 (void) js_GetToken(cx, ts);
-                pn2 = NewParseNode(PN_UNARY, tc);
+                pn2 = UnaryNode::create(tc);
                 if (!pn2)
                     return NULL;
                 if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE))
                     return NULL;
                 pn2->pn_pos.begin = pn->pn_pos.begin;
                 pn = pn2;
             }
         }
@@ -6669,18 +6660,17 @@ CompExprTransplanter::transplant(JSParse
                 if (dn->pn_pos < root->pn_pos || dn->isPlaceholder()) {
                     JSAtomListElement *ale = tc->lexdeps.add(tc->compiler, dn->pn_atom);
                     if (!ale)
                         return false;
 
                     if (dn->pn_pos >= root->pn_pos) {
                         tc->parent->lexdeps.remove(tc->compiler, atom);
                     } else {
-                        JSDefinition *dn2 = (JSDefinition *)
-                            NewNameNode(tc->compiler->context, dn->pn_atom, tc);
+                        JSDefinition *dn2 = (JSDefinition *)NameNode::create(dn->pn_atom, tc);
                         if (!dn2)
                             return false;
 
                         dn2->pn_type = dn->pn_type;
                         dn2->pn_pos = root->pn_pos;
                         dn2->pn_defn = true;
                         dn2->pn_dflags |= PND_PLACEHOLDER;
 
@@ -6788,17 +6778,17 @@ ComprehensionTail(JSParseNode *kid, uint
     data.let.overflow = 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.
          */
-        pn2 = NewParseNode(PN_BINARY, tc);
+        pn2 = BinaryNode::create(tc);
         if (!pn2)
             return NULL;
 
         pn2->pn_op = JSOP_ITER;
         pn2->pn_iflags = JSITER_ENUMERATE;
         if (js_MatchToken(cx, ts, TOK_NAME)) {
             if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom)
                 pn2->pn_iflags |= JSITER_FOREACH;
@@ -6877,35 +6867,35 @@ ComprehensionTail(JSParseNode *kid, uint
             data.pn = pn3;
             if (!data.binder(cx, &data, atom, tc))
                 return NULL;
             break;
 
           default:;
         }
 
-        pn2->pn_left = NewBinary(TOK_IN, JSOP_NOP, pn3, pn4, tc);
+        pn2->pn_left = JSParseNode::newBinaryOrAppend(TOK_IN, JSOP_NOP, pn3, pn4, tc);
         if (!pn2->pn_left)
             return NULL;
         *pnp = pn2;
         pnp = &pn2->pn_right;
     } while (js_MatchToken(cx, ts, TOK_FOR));
 
     if (js_MatchToken(cx, ts, TOK_IF)) {
-        pn2 = NewParseNode(PN_TERNARY, tc);
+        pn2 = TernaryNode::create(tc);
         if (!pn2)
             return NULL;
         pn2->pn_kid1 = Condition(cx, ts, tc);
         if (!pn2->pn_kid1)
             return NULL;
         *pnp = pn2;
         pnp = &pn2->pn_kid2;
     }
 
-    pn2 = NewParseNode(PN_UNARY, tc);
+    pn2 = UnaryNode::create(tc);
     if (!pn2)
         return NULL;
     pn2->pn_type = type;
     pn2->pn_op = op;
     pn2->pn_kid = kid;
     *pnp = pn2;
 
     PopStatement(tc);
@@ -6937,17 +6927,17 @@ GeneratorExpr(JSParseNode *pn, JSParseNo
     pn->pn_type = TOK_YIELD;
     pn->pn_op = JSOP_YIELD;
     pn->pn_parens = true;
     pn->pn_pos = kid->pn_pos;
     pn->pn_kid = kid;
     pn->pn_hidden = true;
 
     /* Make a new node for the desugared generator function. */
-    JSParseNode *genfn = NewParseNode(PN_FUNC, tc);
+    JSParseNode *genfn = FunctionNode::create(tc);
     if (!genfn)
         return NULL;
     genfn->pn_type = TOK_FUNCTION;
     genfn->pn_op = JSOP_LAMBDA;
     JS_ASSERT(!genfn->pn_body);
     genfn->pn_dflags = PND_FUNARG;
 
     {
@@ -6993,17 +6983,17 @@ GeneratorExpr(JSParseNode *pn, JSParseNo
         if (!LeaveFunction(genfn, &gentc, tc))
             return NULL;
     }
 
     /*
      * Our result is a call expression that invokes the anonymous generator
      * function object.
      */
-    JSParseNode *result = NewParseNode(PN_LIST, tc);
+    JSParseNode *result = ListNode::create(tc);
     if (!result)
         return NULL;
     result->pn_type = TOK_LP;
     result->pn_op = JSOP_CALL;
     result->pn_pos.begin = genfn->pn_pos.begin;
     result->initList(genfn);
     return result;
 }
@@ -7034,17 +7024,17 @@ ArgumentList(JSContext *cx, JSTokenStrea
                 js_ReportCompileErrorNumber(cx, ts, argNode, JSREPORT_ERROR,
                                             JSMSG_BAD_GENERATOR_SYNTAX,
                                             js_yield_str);
                 return JS_FALSE;
             }
 #endif
 #if JS_HAS_GENERATOR_EXPRS
             if (js_MatchToken(cx, ts, TOK_FOR)) {
-                JSParseNode *pn = NewParseNode(PN_UNARY, tc);
+                JSParseNode *pn = UnaryNode::create(tc);
                 if (!pn)
                     return JS_FALSE;
                 argNode = GeneratorExpr(pn, argNode, tc);
                 if (!argNode)
                     return JS_FALSE;
                 if (listNode->pn_count > 1 ||
                     js_PeekToken(cx, ts) == TOK_COMMA) {
                     js_ReportCompileErrorNumber(cx, ts, argNode, JSREPORT_ERROR,
@@ -7090,17 +7080,17 @@ MemberExpr(JSContext *cx, JSTokenStream 
 
     JS_CHECK_RECURSION(cx, return NULL);
 
     /* Check for new expression first. */
     ts->flags |= TSF_OPERAND;
     tt = js_GetToken(cx, ts);
     ts->flags &= ~TSF_OPERAND;
     if (tt == TOK_NEW) {
-        pn = NewParseNode(PN_LIST, tc);
+        pn = ListNode::create(tc);
         if (!pn)
             return NULL;
         pn2 = MemberExpr(cx, ts, tc, JS_FALSE);
         if (!pn2)
             return NULL;
         pn2 = CheckForImmediatelyAppliedLambda(pn2);
         pn->pn_op = JSOP_NEW;
         pn->initList(pn2);
@@ -7132,17 +7122,17 @@ MemberExpr(JSContext *cx, JSTokenStream 
             pn2->pn_parens = false;
             pn2->pn_kid = pn;
             pn = pn2;
         }
     }
 
     while ((tt = js_GetToken(cx, ts)) > TOK_EOF) {
         if (tt == TOK_DOT) {
-            pn2 = NewNameNode(cx, NULL, tc);
+            pn2 = NameNode::create(NULL, tc);
             if (!pn2)
                 return NULL;
 #if JS_HAS_XML_SUPPORT
             ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME;
             tt = js_GetToken(cx, ts);
             ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
             pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE);
             if (!pn3)
@@ -7180,17 +7170,17 @@ MemberExpr(JSContext *cx, JSTokenStream 
             pn2->pn_op = JSOP_GETPROP;
             pn2->pn_expr = pn;
             pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
 #endif
             pn2->pn_pos.begin = pn->pn_pos.begin;
             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
 #if JS_HAS_XML_SUPPORT
         } else if (tt == TOK_DBLDOT) {
-            pn2 = NewParseNode(PN_BINARY, tc);
+            pn2 = BinaryNode::create(tc);
             if (!pn2)
                 return NULL;
             ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME;
             tt = js_GetToken(cx, ts);
             ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
             pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE);
             if (!pn3)
                 return NULL;
@@ -7206,17 +7196,17 @@ MemberExpr(JSContext *cx, JSTokenStream 
             }
             pn2->pn_op = JSOP_DESCENDANTS;
             pn2->pn_left = pn;
             pn2->pn_right = pn3;
             pn2->pn_pos.begin = pn->pn_pos.begin;
             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
 #endif
         } else if (tt == TOK_LB) {
-            pn2 = NewParseNode(PN_BINARY, tc);
+            pn2 = BinaryNode::create(tc);
             if (!pn2)
                 return NULL;
             pn3 = Expr(cx, ts, tc);
             if (!pn3)
                 return NULL;
 
             MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
             pn2->pn_pos.begin = pn->pn_pos.begin;
@@ -7245,17 +7235,17 @@ MemberExpr(JSContext *cx, JSTokenStream 
                     pn3->pn_op = JSOP_DOUBLE;
                     pn3->pn_dval = index;
                 }
                 pn2->pn_op = JSOP_GETELEM;
                 pn2->pn_left = pn;
                 pn2->pn_right = pn3;
             } while (0);
         } else if (allowCallSyntax && tt == TOK_LP) {
-            pn2 = NewParseNode(PN_LIST, tc);
+            pn2 = ListNode::create(tc);
             if (!pn2)
                 return NULL;
             pn2->pn_op = JSOP_CALL;
 
             pn = CheckForImmediatelyAppliedLambda(pn);
             if (pn->pn_op == JSOP_NAME) {
                 if (pn->pn_atom == cx->runtime->atomState.evalAtom) {
                     /* Select JSOP_EVAL and flag tc as heavyweight. */
@@ -7377,17 +7367,17 @@ EndBracketedExpr(JSContext *cx, JSTokenS
  * We hoist the :: match into callers of QualifiedSuffix, in order to tweak
  * PropertySelector vs. Identifier pn_arity, pn_op, and other members.
  */
 static JSParseNode *
 PropertySelector(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
 {
     JSParseNode *pn;
 
-    pn = NewParseNode(PN_NULLARY, tc);
+    pn = NullaryNode::create(tc);
     if (!pn)
         return NULL;
     if (pn->pn_type == TOK_STAR) {
         pn->pn_type = TOK_ANYNAME;
         pn->pn_op = JSOP_ANYNAME;
         pn->pn_atom = cx->runtime->atomState.starAtom;
     } else {
         JS_ASSERT(pn->pn_type == TOK_NAME);
@@ -7402,17 +7392,17 @@ PropertySelector(JSContext *cx, JSTokenS
 static JSParseNode *
 QualifiedSuffix(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
                 JSTreeContext *tc)
 {
     JSParseNode *pn2, *pn3;
     JSTokenType tt;
 
     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_DBLCOLON);
-    pn2 = NewNameNode(cx, NULL, tc);
+    pn2 = NameNode::create(NULL, tc);
     if (!pn2)
         return NULL;
 
     /* Left operand of :: must be evaluated if it is an identifier. */
     if (pn->pn_op == JSOP_QNAMEPART)
         pn->pn_op = JSOP_NAME;
 
     ts->flags |= TSF_KEYWORD_IS_NAME;
@@ -7466,17 +7456,17 @@ QualifiedIdentifier(JSContext *cx, JSTok
 
 static JSParseNode *
 AttributeIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
 {
     JSParseNode *pn, *pn2;
     JSTokenType tt;
 
     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_AT);
-    pn = NewParseNode(PN_UNARY, tc);
+    pn = UnaryNode::create(tc);
     if (!pn)
         return NULL;
     pn->pn_op = JSOP_TOATTRNAME;
     ts->flags |= TSF_KEYWORD_IS_NAME;
     tt = js_GetToken(cx, ts);
     ts->flags &= ~TSF_KEYWORD_IS_NAME;
     if (tt == TOK_STAR || tt == TOK_NAME) {
         pn2 = QualifiedIdentifier(cx, ts, tc);
@@ -7498,17 +7488,17 @@ AttributeIdentifier(JSContext *cx, JSTok
  */
 static JSParseNode *
 XMLExpr(JSContext *cx, JSTokenStream *ts, JSBool inTag, JSTreeContext *tc)
 {
     JSParseNode *pn, *pn2;
     uintN oldflag;
 
     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LC);
-    pn = NewParseNode(PN_UNARY, tc);
+    pn = UnaryNode::create(tc);
     if (!pn)
         return NULL;
 
     /*
      * Turn off XML tag mode. We save the old value of the flag because it may
      * already be off: XMLExpr is called both from within a tag, and from
      * within text contained in an element, but outside of any start, end, or
      * point tag.
@@ -7533,17 +7523,17 @@ XMLExpr(JSContext *cx, JSTokenStream *ts
  * child of a container tag.
  */
 static JSParseNode *
 XMLAtomNode(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
 {
     JSParseNode *pn;
     JSToken *tp;
 
-    pn = NewParseNode(PN_NULLARY, tc);
+    pn = NullaryNode::create(tc);
     if (!pn)
         return NULL;
     tp = &CURRENT_TOKEN(ts);
     pn->pn_op = tp->t_op;
     pn->pn_atom = tp->t_atom;
     if (tp->type == TOK_XMLPI)
         pn->pn_atom2 = tp->t_atom2;
     return pn;
@@ -7580,17 +7570,17 @@ XMLNameExpr(JSContext *cx, JSTokenStream
             if (!pn2)
                 return NULL;
         }
 
         if (!pn) {
             pn = pn2;
         } else {
             if (!list) {
-                list = NewParseNode(PN_LIST, tc);
+                list = ListNode::create(tc);
                 if (!list)
                     return NULL;
                 list->pn_type = TOK_XMLNAME;
                 list->pn_pos.begin = pn->pn_pos.begin;
                 list->initList(pn);
                 list->pn_xflags = PNX_CANTFOLD;
                 pn = list;
             }
@@ -7647,17 +7637,17 @@ XMLTagContent(JSContext *cx, JSTokenStre
             js_UngetToken(ts);
             break;
         }
 
         pn2 = XMLNameExpr(cx, ts, tc);
         if (!pn2)
             return NULL;
         if (!list) {
-            list = NewParseNode(PN_LIST, tc);
+            list = ListNode::create(tc);
             if (!list)
                 return NULL;
             list->pn_type = tagtype;
             list->pn_pos.begin = pn->pn_pos.begin;
             list->initList(pn);
             pn = list;
         }
         pn->append(pn2);
@@ -7774,17 +7764,17 @@ XMLElementOrList(JSContext *cx, JSTokenS
 {
     JSParseNode *pn, *pn2, *list;
     JSTokenType tt;
     JSAtom *startAtom, *endAtom;
 
     JS_CHECK_RECURSION(cx, return NULL);
 
     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLSTAGO);
-    pn = NewParseNode(PN_LIST, tc);
+    pn = ListNode::create(tc);
     if (!pn)
         return NULL;
 
     ts->flags |= TSF_XMLTAGMODE;
     tt = js_GetToken(cx, ts);
     if (tt == TOK_ERROR)
         return NULL;
 
@@ -7823,17 +7813,17 @@ XMLElementOrList(JSContext *cx, JSTokenS
             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
 
             /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */
             if (pn2->pn_type != TOK_XMLSTAGO) {
                 pn->initList(pn2);
                 if (!XML_FOLDABLE(pn2))
                     pn->pn_xflags |= PNX_CANTFOLD;
                 pn2 = pn;
-                pn = NewParseNode(PN_LIST, tc);
+                pn = ListNode::create(tc);
                 if (!pn)
                     return NULL;
             }
 
             /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */
             pn->pn_type = TOK_XMLELEM;
             pn->pn_pos.begin = pn2->pn_pos.begin;
             pn->initList(pn2);
@@ -7871,17 +7861,17 @@ XMLElementOrList(JSContext *cx, JSTokenS
                                             JSREPORT_UC | JSREPORT_ERROR,
                                             JSMSG_XML_TAG_NAME_MISMATCH,
                                             str->chars());
                 return NULL;
             }
 
             /* Make a TOK_XMLETAGO list with pn2 as its single child. */
             JS_ASSERT(pn2->pn_type == TOK_XMLNAME || pn2->pn_type == TOK_LC);
-            list = NewParseNode(PN_LIST, tc);
+            list = ListNode::create(tc);
             if (!list)
                 return NULL;
             list->pn_type = TOK_XMLETAGO;
             list->initList(pn2);
             pn->append(list);
             if (!XML_FOLDABLE(pn2)) {
                 list->pn_xflags |= PNX_CANTFOLD;
                 pn->pn_xflags |= PNX_CANTFOLD;
@@ -8016,17 +8006,17 @@ PrimaryExpr(JSContext *cx, JSTokenStream
 #endif
 
     switch (tt) {
       case TOK_FUNCTION:
 #if JS_HAS_XML_SUPPORT
         ts->flags |= TSF_KEYWORD_IS_NAME;
         if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
             ts->flags &= ~TSF_KEYWORD_IS_NAME;
-            pn2 = NewParseNode(PN_NULLARY, tc);
+            pn2 = NullaryNode::create(tc);
             if (!pn2)
                 return NULL;
             pn2->pn_type = TOK_FUNCTION;
             pn = QualifiedSuffix(cx, ts, pn2, tc);
             if (!pn)
                 return NULL;
             break;
         }
@@ -8037,17 +8027,17 @@ PrimaryExpr(JSContext *cx, JSTokenStream
             return NULL;
         break;
 
       case TOK_LB:
       {
         JSBool matched;
         jsuint index;
 
-        pn = NewParseNode(PN_LIST, tc);
+        pn = ListNode::create(tc);
         if (!pn)
             return NULL;
         pn->pn_type = TOK_RB;
         pn->pn_op = JSOP_NEWINIT;
         pn->makeEmpty();
 
 #if JS_HAS_GENERATORS
         pn->pn_blockid = tc->blockidGen;
@@ -8070,17 +8060,17 @@ PrimaryExpr(JSContext *cx, JSTokenStream
                 if (tt == TOK_RB) {
                     pn->pn_xflags |= PNX_ENDCOMMA;
                     break;
                 }
 
                 if (tt == TOK_COMMA) {
                     /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
                     js_MatchToken(cx, ts, TOK_COMMA);
-                    pn2 = NewParseNode(PN_NULLARY, tc);
+                    pn2 = NullaryNode::create(tc);
                     pn->pn_xflags |= PNX_HOLEY;
                 } else {
                     pn2 = AssignExpr(cx, ts, tc);
                 }
                 if (!pn2)
                     return NULL;
                 pn->append(pn2);
 
@@ -8177,32 +8167,32 @@ PrimaryExpr(JSContext *cx, JSTokenStream
          * A map from property names we've seen thus far to bit masks.
          * (We use ALE_INDEX/ALE_SET_INDEX).  An atom's mask includes
          * JSPROP_SETTER if we've seen a setter for it, JSPROP_GETTER
          * if we've seen as getter, and both of those if we've just
          * seen an ordinary value.
          */
         JSAutoAtomList seen(tc->compiler);
 
-        pn = NewParseNode(PN_LIST, tc);
+        pn = ListNode::create(tc);
         if (!pn)
             return NULL;
         pn->pn_type = TOK_RC;
         pn->pn_op = JSOP_NEWINIT;
         pn->makeEmpty();
 
         afterComma = JS_FALSE;
         for (;;) {
             JSAtom *atom;
             ts->flags |= TSF_KEYWORD_IS_NAME;
             tt = js_GetToken(cx, ts);
             ts->flags &= ~TSF_KEYWORD_IS_NAME;
             switch (tt) {
               case TOK_NUMBER:
-                pn3 = NewParseNode(PN_NULLARY, tc);
+                pn3 = NullaryNode::create(tc);
                 if (!pn3)
                     return NULL;
                 pn3->pn_dval = CURRENT_TOKEN(ts).t_dval;
                 if (tc->needStrictChecks())
                     atom = js_AtomizeDouble(cx, pn3->pn_dval);
                 else
                     atom = NULL; /* for the compiler */
                 break;
@@ -8220,32 +8210,32 @@ PrimaryExpr(JSContext *cx, JSTokenStream
                     ts->flags |= TSF_KEYWORD_IS_NAME;
                     tt = js_GetToken(cx, ts);
                     ts->flags &= ~TSF_KEYWORD_IS_NAME;
                     if (tt != TOK_NAME) {
                         js_UngetToken(ts);
                         goto property_name;
                     }
                     atom = CURRENT_TOKEN(ts).t_atom;
-                    pn3 = NewNameNode(cx, atom, tc);
+                    pn3 = NameNode::create(atom, tc);
                     if (!pn3)
                         return NULL;
 
                     /* We have to fake a 'function' token here. */
                     CURRENT_TOKEN(ts).t_op = JSOP_NOP;
                     CURRENT_TOKEN(ts).type = TOK_FUNCTION;
                     pn2 = FunctionExpr(cx, ts, tc);
-                    pn2 = NewBinary(TOK_COLON, op, pn3, pn2, tc);
+                    pn2 = JSParseNode::newBinaryOrAppend(TOK_COLON, op, pn3, pn2, tc);
                     goto skip;
                 }
               property_name:
 #endif
               case TOK_STRING:
                 atom = CURRENT_TOKEN(ts).t_atom;
-                pn3 = NewParseNode(PN_NULLARY, tc);
+                pn3 = NullaryNode::create(tc);
                 if (!pn3)
                     return NULL;
                 pn3->pn_atom = atom;
                 break;
               case TOK_RC:
                 goto end_obj_init;
               default:
                 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
@@ -8280,34 +8270,34 @@ PrimaryExpr(JSContext *cx, JSTokenStream
                  * Support, e.g., |var {x, y} = o| as destructuring shorthand
                  * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
                  */
                 js_UngetToken(ts);
                 pn->pn_xflags |= PNX_DESTRUCT;
                 pnval = pn3;
                 if (pnval->pn_type == TOK_NAME) {
                     pnval->pn_arity = PN_NAME;
-                    InitNameNodeCommon(pnval, tc);
+                    ((NameNode *)pnval)->initCommon(tc);
                 }
 #endif
             }
 
-            pn2 = NewBinary(TOK_COLON, op, pn3, pnval, tc);
+            pn2 = JSParseNode::newBinaryOrAppend(TOK_COLON, op, pn3, pnval, tc);
 #if JS_HAS_GETTER_SETTER
           skip:
 #endif
             if (!pn2)
                 return NULL;
             pn->append(pn2);
 
             /*
              * In strict mode code, check for duplicate property names.  Treat
              * getters and setters as distinct attributes of each property.  A
              * plain old value conflicts with a getter or a setter.
-             */ 
+             */
             if (tc->needStrictChecks()) {
                 unsigned attributesMask;
                 if (op == JSOP_INITPROP) {
                     attributesMask = JSPROP_GETTER | JSPROP_SETTER;
                 } else if (op == JSOP_GETTER) {
                     attributesMask = JSPROP_GETTER;
                 } else if (op == JSOP_SETTER) {
                     attributesMask = JSPROP_SETTER;
@@ -8328,17 +8318,17 @@ PrimaryExpr(JSContext *cx, JSTokenStream
                     }
                     ALE_SET_INDEX(ale, attributesMask | ALE_INDEX(ale));
                 } else {
                     ale = seen.add(tc->compiler, atom);
                     if (!ale)
                         return NULL;
                     ALE_SET_INDEX(ale, attributesMask);
                 }
-            }                    
+            }
 
             tt = js_GetToken(cx, ts);
             if (tt == TOK_RC)
                 goto end_obj_init;
             if (tt != TOK_COMMA) {
                 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
                                             JSMSG_CURLY_AFTER_LIST);
                 return NULL;
@@ -8356,17 +8346,17 @@ PrimaryExpr(JSContext *cx, JSTokenStream
         pn = LetBlock(cx, ts, tc, JS_FALSE);
         if (!pn)
             return NULL;
         break;
 #endif
 
 #if JS_HAS_SHARP_VARS
       case TOK_DEFSHARP:
-        pn = NewParseNode(PN_UNARY, tc);
+        pn = UnaryNode::create(tc);
         if (!pn)
             return NULL;
         pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
         ts->flags |= TSF_OPERAND;
         tt = js_GetToken(cx, ts);
         ts->flags &= ~TSF_OPERAND;
         if (tt == TOK_USESHARP || tt == TOK_DEFSHARP ||
 #if JS_HAS_XML_SUPPORT
@@ -8382,17 +8372,17 @@ PrimaryExpr(JSContext *cx, JSTokenStream
         if (!pn->pn_kid)
             return NULL;
         if (!tc->ensureSharpSlots())
             return NULL;
         break;
 
       case TOK_USESHARP:
         /* Check for forward/dangling references at runtime, to allow eval. */
-        pn = NewParseNode(PN_NULLARY, tc);
+        pn = NullaryNode::create(tc);
         if (!pn)
             return NULL;
         if (!tc->ensureSharpSlots())
             return NULL;
         pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
         break;
 #endif /* JS_HAS_SHARP_VARS */
 
@@ -8434,30 +8424,30 @@ PrimaryExpr(JSContext *cx, JSTokenStream
         /* FALL THROUGH */
 #endif
 
 #if JS_HAS_XML_SUPPORT
       case TOK_XMLCDATA:
       case TOK_XMLCOMMENT:
       case TOK_XMLPI:
 #endif
-        pn = NewParseNode(PN_NULLARY, tc);
+        pn = NullaryNode::create(tc);
         if (!pn)
             return NULL;
         pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
 #if JS_HAS_XML_SUPPORT
         if (tt == TOK_XMLPI)
             pn->pn_atom2 = CURRENT_TOKEN(ts).t_atom2;
         else
 #endif
             pn->pn_op = CURRENT_TOKEN(ts).t_op;
         break;
 
       case TOK_NAME:
-        pn = NewNameNode(cx, CURRENT_TOKEN(ts).t_atom, tc);
+        pn = NameNode::create(CURRENT_TOKEN(ts).t_atom, tc);
         if (!pn)
             return NULL;
         JS_ASSERT(CURRENT_TOKEN(ts).t_op == JSOP_NAME);
         pn->pn_op = JSOP_NAME;
 
         if ((tc->flags & (TCF_IN_FUNCTION | TCF_FUN_PARAM_ARGUMENTS)) == TCF_IN_FUNCTION &&
             pn->pn_atom == cx->runtime->atomState.argumentsAtom) {
             /*
@@ -8584,17 +8574,17 @@ PrimaryExpr(JSContext *cx, JSTokenStream
         }
 #endif
         break;
 
       case TOK_REGEXP:
       {
         JSObject *obj;
 
-        pn = NewParseNode(PN_NULLARY, tc);
+        pn = NullaryNode::create(tc);
         if (!pn)
             return NULL;
 
         obj = js_NewRegExpObject(cx, ts,
                                  ts->tokenbuf.begin(),
                                  ts->tokenbuf.length(),
                                  CURRENT_TOKEN(ts).t_reflags);
         if (!obj)
@@ -8608,25 +8598,25 @@ PrimaryExpr(JSContext *cx, JSTokenStream
         if (!pn->pn_objbox)
             return NULL;
 
         pn->pn_op = JSOP_REGEXP;
         break;
       }
 
       case TOK_NUMBER:
-        pn = NewParseNode(PN_NULLARY, tc);
+        pn = NullaryNode::create(tc);
         if (!pn)
             return NULL;
         pn->pn_op = JSOP_DOUBLE;
         pn->pn_dval = CURRENT_TOKEN(ts).t_dval;
         break;
 
       case TOK_PRIMARY:
-        pn = NewParseNode(PN_NULLARY, tc);
+        pn = NullaryNode::create(tc);
         if (!pn)
             return NULL;
         pn->pn_op = CURRENT_TOKEN(ts).t_op;
         break;
 
       case TOK_ERROR:
         /* The scanner or one of its subroutines reported the error. */
         return NULL;
@@ -8665,17 +8655,17 @@ ParenExpr(JSContext *cx, JSTokenStream *
         }
         if (pn->pn_type == TOK_COMMA && !pn->pn_parens) {
             js_ReportCompileErrorNumber(cx, ts, pn->last(), JSREPORT_ERROR,
                                         JSMSG_BAD_GENERATOR_SYNTAX,
                                         js_generator_str);
             return NULL;
         }
         if (!pn1) {
-            pn1 = NewParseNode(PN_UNARY, tc);
+            pn1 = UnaryNode::create(tc);
             if (!pn1)
                 return NULL;
         }
         pn = GeneratorExpr(pn1, pn, tc);
         if (!pn)
             return NULL;
         pn->pn_pos.begin = begin;
         if (genexp) {
--- a/js/src/jsparse.h
+++ b/js/src/jsparse.h
@@ -381,16 +381,33 @@ struct JSParseNode {
 #define pn_objbox       pn_u.name.objbox
 #define pn_expr         pn_u.name.expr
 #define pn_lexdef       pn_u.name.lexdef
 #define pn_names        pn_u.nameset.names
 #define pn_tree         pn_u.nameset.tree
 #define pn_dval         pn_u.dval
 #define pn_atom2        pn_u.apair.atom2
 
+protected:
+    void inline init(JSTokenType type, JSOp op, JSParseNodeArity arity) {
+        pn_type = type;
+        pn_op = op;
+        pn_arity = arity;
+        pn_parens = false;
+        JS_ASSERT(!pn_used);
+        JS_ASSERT(!pn_defn);
+        pn_next = pn_link = NULL;
+    }
+
+    static JSParseNode *create(JSParseNodeArity arity, JSTreeContext *tc);
+
+public:
+    static JSParseNode *newBinaryOrAppend(JSTokenType tt, JSOp op, JSParseNode *left,
+                                          JSParseNode *right, JSTreeContext *tc);
+
     /*
      * 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()
      * and pn->lexdef(). Otherwise, use pn->maybeExpr() and pn->maybeLexDef().
      */
     JSParseNode  *expr() const {
         JS_ASSERT(!pn_used);
@@ -476,17 +493,17 @@ struct JSParseNode {
 
     /* True if pn is a parsenode representing a literal constant. */
     bool isLiteral() const {
         return PN_TYPE(this) == TOK_NUMBER ||
                PN_TYPE(this) == TOK_STRING ||
                (PN_TYPE(this) == TOK_PRIMARY && PN_OP(this) != JSOP_THIS);
     }
 
-    /* 
+    /*
      * True if this statement node could be a member of a Directive
      * Prologue.  Note that the prologue may contain strings that
      * cannot themselves be directives; that's a stricter test.
      * If Statement begins to simplify trees into this form, then
      * we'll need additional flags that we can test here.
      */
     bool isDirectivePrologueMember() const {
         if (PN_TYPE(this) == TOK_SEMI) {
@@ -500,17 +517,18 @@ struct JSParseNode {
     /*
      * True if this node, known to be a Directive Prologue member,
      * could be a directive itself.
      */
     bool isDirective() const {
         JS_ASSERT(isDirectivePrologueMember());
         JSParseNode *kid = pn_kid;
         JSString *str = ATOM_TO_STRING(kid->pn_atom);
-        /* 
+
+        /*
          * Directives must contain no EscapeSequences or LineContinuations.
          * If the string's length in the source code is its length as a value,
          * accounting for the quotes, then it qualifies.
          */
         return (pn_pos.begin.lineno == pn_pos.end.lineno &&
                 pn_pos.begin.index + str->length() + 2 == pn_pos.end.index);
     }
 
@@ -545,16 +563,74 @@ struct JSParseNode {
     void append(JSParseNode *pn) {
         JS_ASSERT(pn_arity == PN_LIST);
         *pn_tail = pn;
         pn_tail = &pn->pn_next;
         pn_count++;
     }
 };
 
+namespace js {
+
+struct NullaryNode : public JSParseNode {
+    static inline NullaryNode *create(JSTreeContext *tc) {
+        return (NullaryNode *)JSParseNode::create(PN_NULLARY, tc);
+    }
+};
+
+struct UnaryNode : public JSParseNode {
+    static inline UnaryNode *create(JSTreeContext *tc) {
+        return (UnaryNode *)JSParseNode::create(PN_UNARY, tc);
+    }
+};
+
+struct BinaryNode : public JSParseNode {
+    static inline BinaryNode *create(JSTreeContext *tc) {
+        return (BinaryNode *)JSParseNode::create(PN_BINARY, tc);
+    }
+};
+
+struct TernaryNode : public JSParseNode {
+    static inline TernaryNode *create(JSTreeContext *tc) {
+        return (TernaryNode *)JSParseNode::create(PN_TERNARY, tc);
+    }
+};
+
+struct ListNode : public JSParseNode {
+    static inline ListNode *create(JSTreeContext *tc) {
+        return (ListNode *)JSParseNode::create(PN_LIST, tc);
+    }
+};
+
+struct FunctionNode : public JSParseNode {
+    static inline FunctionNode *create(JSTreeContext *tc) {
+        return (FunctionNode *)JSParseNode::create(PN_FUNC, tc);
+    }
+};
+
+struct NameNode : public JSParseNode {
+    static NameNode *create(JSAtom *atom, JSTreeContext *tc);
+
+    void inline initCommon(JSTreeContext *tc);
+};
+
+struct NameSetNode : public JSParseNode {
+    static inline NameSetNode *create(JSTreeContext *tc) {
+        return (NameSetNode *)JSParseNode::create(PN_NAMESET, tc);
+    }
+};
+
+struct LexicalScopeNode : public JSParseNode {
+    static inline LexicalScopeNode *create(JSTreeContext *tc) {
+        return (LexicalScopeNode *)JSParseNode::create(PN_NAME, tc);
+    }
+};
+
+} /* namespace js */
+
 /*
  * JSDefinition is a degenerate subtype of the PN_FUNC and PN_NAME variants of
  * JSParseNode, allocated only for function, var, const, and let declarations
  * that define truly lexical bindings. This means that a child of a TOK_VAR
  * list may be a JSDefinition instead of a JSParseNode. The pn_defn bit is set
  * for all JSDefinitions, clear otherwise.
  *
  * Note that not all var declarations are definitions: JS allows multiple var