Bug 695752 - Part 3 - class ParseNodeAllocator. r=luke.
authorJason Orendorff <jorendorff@mozilla.com>
Wed, 19 Oct 2011 16:45:05 -0500
changeset 79637 477f20a61cbaab6d9070a19fc1a7857c2e5b2f8b
parent 79636 7e5a4585d4658122de696a13601fd54eb9b92a72
child 79638 56ec5e954858b976485d9f4de4a070708470c364
push id506
push userclegnitto@mozilla.com
push dateWed, 09 Nov 2011 02:03:18 +0000
treeherdermozilla-aurora@63587fc7bb93 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs695752
milestone10.0a1
Bug 695752 - Part 3 - class ParseNodeAllocator. r=luke.
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeGenerator.h
js/src/frontend/FoldConstants.cpp
js/src/frontend/ParseNode.cpp
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -201,17 +201,17 @@ BytecodeCompiler::compileScript(JSContex
 
         if (!EmitTree(cx, &cg, pn))
             goto out;
 
 #if JS_HAS_XML_SUPPORT
         if (!pn->isKind(TOK_SEMI) || !pn->pn_kid || !TreeTypeIsXML(pn->pn_kid->getKind()))
             onlyXML = false;
 #endif
-        RecycleTree(pn, &cg);
+        cg.freeTree(pn);
     }
 
 #if JS_HAS_XML_SUPPORT
     /*
      * Prevent XML data theft via <script src="http://victim.com/foo.xml">.
      * For background, see:
      *
      * https://bugzilla.mozilla.org/show_bug.cgi?id=336551
--- a/js/src/frontend/BytecodeGenerator.h
+++ b/js/src/frontend/BytecodeGenerator.h
@@ -498,16 +498,18 @@ struct TreeContext {                /* t
 
     void noteHasExtensibleScope() {
         flags |= TCF_FUN_EXTENSIBLE_SCOPE;
     }
 
     bool hasExtensibleScope() const {
         return flags & TCF_FUN_EXTENSIBLE_SCOPE;
     }
+
+    ParseNode *freeTree(ParseNode *pn) { return parser->freeTree(pn); }
 };
 
 /*
  * Return true if we need to check for conditions that elicit
  * JSOPTION_STRICT warnings or strict mode errors.
  */
 inline bool TreeContext::needStrictChecks() {
     return parser->context->hasStrictOption() || inStrictMode();
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -208,19 +208,19 @@ FoldBinaryNumeric(JSContext *cx, JSOp op
         }
         break;
 
       default:;
     }
 
     /* Take care to allow pn1 or pn2 to alias pn. */
     if (pn1 != pn)
-        RecycleTree(pn1, tc);
+        tc->freeTree(pn1);
     if (pn2 != pn)
-        RecycleTree(pn2, tc);
+        tc->freeTree(pn2);
     pn->setKind(TOK_NUMBER);
     pn->setOp(JSOP_DOUBLE);
     pn->setArity(PN_NULLARY);
     pn->pn_dval = d;
     return JS_TRUE;
 }
 
 #if JS_HAS_XML_SUPPORT
@@ -298,17 +298,17 @@ FoldXMLConstants(JSContext *cx, ParseNod
                 if (accum)
                     FileEscapedString(stdout, accum, 0);
                 else
                     fputs("NULL", stdout);
                 fputc('\n', stdout);
 #endif
             } else if (accum && pn1 != pn2) {
                 while (pn1->pn_next != pn2) {
-                    pn1 = RecycleTree(pn1, tc);
+                    pn1 = tc->freeTree(pn1);
                     --pn->pn_count;
                 }
                 pn1->setKind(TOK_XMLTEXT);
                 pn1->setOp(JSOP_STRING);
                 pn1->setArity(PN_NULLARY);
                 pn1->pn_atom = js_AtomizeString(cx, accum);
                 if (!pn1->pn_atom)
                     return JS_FALSE;
@@ -351,17 +351,17 @@ FoldXMLConstants(JSContext *cx, ParseNod
         if (str) {
             accum = js_ConcatStrings(cx, accum, str);
             if (!accum)
                 return JS_FALSE;
         }
 
         JS_ASSERT(*pnp == pn1);
         while (pn1->pn_next) {
-            pn1 = RecycleTree(pn1, tc);
+            pn1 = tc->freeTree(pn1);
             --pn->pn_count;
         }
         pn1->setKind(TOK_XMLTEXT);
         pn1->setOp(JSOP_STRING);
         pn1->setArity(PN_NULLARY);
         pn1->pn_atom = js_AtomizeString(cx, accum);
         if (!pn1->pn_atom)
             return JS_FALSE;
@@ -483,17 +483,17 @@ FoldConstants(JSContext *cx, ParseNode *
         pn2 = pn->pn_kid2;
         pn3 = pn->pn_kid3;
         if (pn1 && !FoldConstants(cx, pn1, tc, pn->isKind(TOK_IF)))
             return false;
         if (pn2) {
             if (!FoldConstants(cx, pn2, tc, pn->isKind(TOK_FORHEAD)))
                 return false;
             if (pn->isKind(TOK_FORHEAD) && pn2->isOp(JSOP_TRUE)) {
-                RecycleTree(pn2, tc);
+                tc->freeTree(pn2);
                 pn->pn_kid2 = NULL;
             }
         }
         if (pn3 && !FoldConstants(cx, pn3, tc))
             return false;
         break;
 
       case PN_BINARY:
@@ -606,19 +606,19 @@ FoldConstants(JSContext *cx, ParseNode *
              * empty statement, which does not decompile, even when labeled).
              * NB: pn must be a TOK_IF as TOK_HOOK can never have a null kid
              * or an empty statement for a child.
              */
             pn->setKind(TOK_LC);
             pn->setArity(PN_LIST);
             pn->makeEmpty();
         }
-        RecycleTree(pn2, tc);
+        tc->freeTree(pn2);
         if (pn3 && pn3 != pn2)
-            RecycleTree(pn3, tc);
+            tc->freeTree(pn3);
         break;
 
       case TOK_OR:
       case TOK_AND:
         if (inCond) {
             if (pn->isArity(PN_LIST)) {
                 ParseNode **pnp = &pn->pn_head;
                 JS_ASSERT(*pnp == pn1);
@@ -626,52 +626,52 @@ FoldConstants(JSContext *cx, ParseNode *
                     Truthiness t = Boolish(pn1);
                     if (t == Unknown) {
                         pnp = &pn1->pn_next;
                         continue;
                     }
                     if ((t == Truthy) == pn->isKind(TOK_OR)) {
                         for (pn2 = pn1->pn_next; pn2; pn2 = pn3) {
                             pn3 = pn2->pn_next;
-                            RecycleTree(pn2, tc);
+                            tc->freeTree(pn2);
                             --pn->pn_count;
                         }
                         pn1->pn_next = NULL;
                         break;
                     }
                     JS_ASSERT((t == Truthy) == pn->isKind(TOK_AND));
                     if (pn->pn_count == 1)
                         break;
                     *pnp = pn1->pn_next;
-                    RecycleTree(pn1, tc);
+                    tc->freeTree(pn1);
                     --pn->pn_count;
                 } while ((pn1 = *pnp) != 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;
                     JS_ASSERT(!pn2->pn_next);
                     pn->setArity(PN_BINARY);
                     pn->pn_left = pn1;
                     pn->pn_right = pn2;
                 } else if (pn->pn_count == 1) {
                     pn->become(pn1);
-                    RecycleTree(pn1, tc);
+                    tc->freeTree(pn1);
                 }
             } else {
                 Truthiness t = Boolish(pn1);
                 if (t != Unknown) {
                     if ((t == Truthy) == pn->isKind(TOK_OR)) {
-                        RecycleTree(pn2, tc);
+                        tc->freeTree(pn2);
                         pn->become(pn1);
                     } else {
                         JS_ASSERT((t == Truthy) == pn->isKind(TOK_AND));
-                        RecycleTree(pn1, tc);
+                        tc->freeTree(pn1);
                         pn->become(pn2);
                     }
                 }
             }
         }
         break;
 
       case TOK_ASSIGN:
@@ -718,17 +718,17 @@ FoldConstants(JSContext *cx, ParseNode *
             chars[length] = 0;
             JSString *str = js_NewString(cx, chars, length);
             if (!str) {
                 cx->free_(chars);
                 return false;
             }
 
             /* Fill the buffer, advancing chars and recycling kids as we go. */
-            for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) {
+            for (pn2 = pn1; pn2; pn2 = tc->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. */
@@ -756,18 +756,18 @@ FoldConstants(JSContext *cx, ParseNode *
             if (!str)
                 return false;
             pn->pn_atom = js_AtomizeString(cx, str);
             if (!pn->pn_atom)
                 return false;
             pn->setKind(TOK_STRING);
             pn->setOp(JSOP_STRING);
             pn->setArity(PN_NULLARY);
-            RecycleTree(pn1, tc);
-            RecycleTree(pn2, tc);
+            tc->freeTree(pn1);
+            tc->freeTree(pn2);
             break;
         }
 
         /* Can't concatenate string literals, let's try numbers. */
         goto do_binary_op;
 
       case TOK_STAR:
       case TOK_SHOP:
@@ -838,22 +838,22 @@ FoldConstants(JSContext *cx, ParseNode *
               default:
                 /* Return early to dodge the common TOK_NUMBER code. */
                 return true;
             }
             pn->setKind(TOK_NUMBER);
             pn->setOp(JSOP_DOUBLE);
             pn->setArity(PN_NULLARY);
             pn->pn_dval = d;
-            RecycleTree(pn1, tc);
+            tc->freeTree(pn1);
         } else if (pn1->isKind(TOK_PRIMARY)) {
             if (pn->isOp(JSOP_NOT) && (pn1->isOp(JSOP_TRUE) || pn1->isOp(JSOP_FALSE))) {
                 pn->become(pn1);
                 pn->setOp(pn->isOp(JSOP_TRUE) ? JSOP_FALSE : JSOP_TRUE);
-                RecycleTree(pn1, tc);
+                tc->freeTree(pn1);
             }
         }
         break;
 
 #if JS_HAS_XML_SUPPORT
       case TOK_XMLELEM:
       case TOK_XMLLIST:
       case TOK_XMLPTAGC:
@@ -877,34 +877,34 @@ FoldConstants(JSContext *cx, ParseNode *
             ObjectBox *xmlbox = tc->parser->newObjectBox(&v.toObject());
             if (!xmlbox)
                 return false;
 
             pn->setKind(TOK_XMLNAME);
             pn->setOp(JSOP_OBJECT);
             pn->setArity(PN_NULLARY);
             pn->pn_objbox = xmlbox;
-            RecycleTree(pn1, tc);
+            tc->freeTree(pn1);
         }
         break;
 #endif /* JS_HAS_XML_SUPPORT */
 
       default:;
     }
 
     if (inCond) {
         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.
              */
-            PrepareNodeForMutation(pn, tc);
+            tc->parser->allocator.prepareNodeForMutation(pn);
             pn->setKind(TOK_PRIMARY);
             pn->setOp(t == Truthy ? JSOP_TRUE : JSOP_FALSE);
             pn->setArity(PN_NULLARY);
         }
     }
 
     return true;
 }
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -35,19 +35,16 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "frontend/ParseNode.h"
 
-#include "frontend/BytecodeGenerator.h"
-#include "frontend/Parser.h"
-
 #include "jsscriptinlines.h"
 
 #include "frontend/ParseMaps-inl.h"
 #include "frontend/ParseNode-inl.h"
 
 using namespace js;
 
 /*
@@ -149,40 +146,40 @@ FunctionBox::shouldUnbrand(uintN methods
                 return true;
         }
     }
     return false;
 }
 
 /* Add |node| to |parser|'s free node list. */
 void
-AddNodeToFreeList(ParseNode *pn, Parser *parser)
+ParseNodeAllocator::freeNode(ParseNode *pn)
 {
     /* Catch back-to-back dup recycles. */
-    JS_ASSERT(pn != parser->nodeList);
+    JS_ASSERT(pn != freelist);
 
     /* 
      * It's too hard to clear these nodes from the AtomDefnMaps, etc. that
      * hold references to them, so we never free them. It's our caller's job to
      * recognize and process these, since their children do need to be dealt
      * with.
      */
     JS_ASSERT(!pn->isUsed());
     JS_ASSERT(!pn->isDefn());
 
     if (pn->isArity(PN_NAMESET) && pn->pn_names.hasMap())
-        pn->pn_names.releaseMap(parser->context);
+        pn->pn_names.releaseMap(cx);
 
 #ifdef DEBUG
     /* Poison the node, to catch attempts to use it without initializing it. */
     memset(pn, 0xab, sizeof(*pn));
 #endif
 
-    pn->pn_next = parser->nodeList;
-    parser->nodeList = pn;
+    pn->pn_next = freelist;
+    freelist = pn;
 }
 
 /*
  * A work pool of ParseNodes. The work pool is a stack, chained together
  * by nodes' pn_next fields. We use this to avoid creating deep C++ stacks
  * when recycling deep parse trees.
  *
  * Since parse nodes are probably allocated in something close to the order
@@ -214,18 +211,18 @@ class NodeStack {
     ParseNode *top;
 };
 
 /*
  * Push the children of |pn| on |stack|. Return true if |pn| itself could be
  * safely recycled, or false if it must be cleaned later (pn_used and pn_defn
  * nodes, and all function nodes; see comments for
  * js::Parser::cleanFunctionList). Some callers want to free |pn|; others
- * (PrepareNodeForMutation) don't care about |pn|, and just need to take care
- * of its children.
+ * (js::ParseNodeAllocator::prepareNodeForMutation) don't care about |pn|, and
+ * just need to take care of its children.
  */
 static bool
 PushNodeChildren(ParseNode *pn, NodeStack *stack)
 {
     switch (pn->getArity()) {
       case PN_FUNC:
         /*
          * Function nodes are linked into the function box tree, and may appear
@@ -297,17 +294,17 @@ PushNodeChildren(ParseNode *pn, NodeStac
 }
 
 /*
  * Prepare |pn| to be mutated in place into a new kind of node. Recycle all
  * |pn|'s recyclable children (but not |pn| itself!), and disconnect it from
  * metadata structures (the function box tree).
  */
 void
-PrepareNodeForMutation(ParseNode *pn, TreeContext *tc)
+ParseNodeAllocator::prepareNodeForMutation(ParseNode *pn)
 {
     if (!pn->isArity(PN_NULLARY)) {
         if (pn->isArity(PN_FUNC)) {
             /*
              * Since this node could be turned into anything, we can't
              * ensure it won't be subsequently recycled, so we must
              * disconnect it from the funbox tree entirely.
              *
@@ -328,103 +325,91 @@ PrepareNodeForMutation(ParseNode *pn, Tr
         PushNodeChildren(pn, &stack);
         /*
          * For each node on the work stack, push its children on the work stack,
          * and free the node if we can.
          */
         while (!stack.empty()) {
             pn = stack.pop();
             if (PushNodeChildren(pn, &stack))
-                AddNodeToFreeList(pn, tc->parser);
+                freeNode(pn);
         }
     }
 }
 
 /*
  * Return the nodes in the subtree |pn| to the parser's free node list, for
  * reallocation.
  *
  * Note that all functions in |pn| that are not enclosed by other functions
  * in |pn| must be direct children of |tc|, because we only clean up |tc|'s
  * function and method lists. You must not reach into a function and
  * recycle some part of it (unless you've updated |tc|->functionList, the
  * way js_FoldConstants does).
  */
 ParseNode *
-RecycleTree(ParseNode *pn, TreeContext *tc)
+ParseNodeAllocator::freeTree(ParseNode *pn)
 {
     if (!pn)
         return NULL;
 
     ParseNode *savedNext = pn->pn_next;
 
     NodeStack stack;
     for (;;) {
         if (PushNodeChildren(pn, &stack))
-            AddNodeToFreeList(pn, tc->parser);
+            freeNode(pn);
         if (stack.empty())
             break;
         pn = stack.pop();
     }
 
     return savedNext;
 }
 
 /*
  * Allocate a ParseNode from parser's node freelist or, failing that, from
  * cx's temporary arena.
  */
 void *
-AllocNodeUninitialized(Parser *parser)
+ParseNodeAllocator::allocNode()
 {
-    if (ParseNode *pn = parser->nodeList) {
-        parser->nodeList = pn->pn_next;
+    if (ParseNode *pn = freelist) {
+        freelist = pn->pn_next;
         return pn;
     }
 
-    JSContext *cx = parser->context;
     void *p = cx->tempLifoAlloc().alloc(sizeof (ParseNode));
     if (!p)
         js_ReportOutOfMemory(cx);
     return p;
 }
 
 /* used only by static create methods of subclasses */
 
 ParseNode *
 ParseNode::create(ParseNodeArity arity, TreeContext *tc)
 {
-    const Token &tok = tc->parser->tokenStream.currentToken();
-    return create(arity, tok.type, JSOP_NOP, tok.pos, tc);
-}
-
-ParseNode *
-ParseNode::create(ParseNodeArity arity, TokenKind type, JSOp op, const TokenPos &pos,
-                  TreeContext *tc)
-{
-    void *p = AllocNodeUninitialized(tc->parser);
-    if (!p)
-        return NULL;
-    return new(p) ParseNode(type, op, arity, pos);
+    Parser *parser = tc->parser;
+    const Token &tok = parser->tokenStream.currentToken();
+    return parser->new_<ParseNode>(tok.type, JSOP_NOP, arity, tok.pos);
 }
 
 ParseNode *
 ParseNode::newBinaryOrAppend(TokenKind tt, JSOp op, ParseNode *left, ParseNode *right,
                              TreeContext *tc)
 {
     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_EmitTree recursion.
      */
-    if (left->isKind(tt) &&
-        left->isOp(op) &&
-        (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
+    if (left->isKind(tt) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
         if (left->pn_arity != PN_LIST) {
             ParseNode *pn1 = left->pn_left, *pn2 = left->pn_right;
             left->setArity(PN_LIST);
             left->pn_parens = false;
             left->initList(pn1);
             left->append(pn2);
             if (tt == TOK_PLUS) {
                 if (pn1->isKind(TOK_STRING))
@@ -453,20 +438,21 @@ ParseNode::newBinaryOrAppend(TokenKind t
      * 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 (tt == TOK_PLUS &&
         left->isKind(TOK_NUMBER) &&
         right->isKind(TOK_NUMBER) &&
-        tc->parser->foldConstants) {
+        tc->parser->foldConstants)
+    {
         left->pn_dval += right->pn_dval;
         left->pn_pos.end = right->pn_pos.end;
-        RecycleTree(right, tc);
+        tc->freeTree(right);
         return left;
     }
 
     return tc->parser->new_<BinaryNode>(tt, op, left, right);
 }
 
 NameNode *
 NameNode::create(JSAtom *atom, TreeContext *tc)
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -452,26 +452,21 @@ struct ParseNode {
         pn_parens = false;
         JS_ASSERT(!pn_used);
         JS_ASSERT(!pn_defn);
         pn_names.init();
         pn_next = pn_link = NULL;
     }
 
     static ParseNode *create(ParseNodeArity arity, TreeContext *tc);
-    static ParseNode *create(ParseNodeArity arity, TokenKind type, JSOp op,
-                             const TokenPos &pos, TreeContext *tc);
 
   public:
     static ParseNode *newBinaryOrAppend(TokenKind tt, JSOp op, ParseNode *left, ParseNode *right,
                                         TreeContext *tc);
 
-    static ParseNode *newTernary(TokenKind tt, JSOp op, ParseNode *kid1, ParseNode *kid2,
-                                 ParseNode *kid3, TreeContext *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().
      */
     ParseNode *expr() const {
         JS_ASSERT(!pn_used);
@@ -755,43 +750,31 @@ struct FunctionNode : public ParseNode {
     static inline FunctionNode *create(TreeContext *tc) {
         return (FunctionNode *)ParseNode::create(PN_FUNC, tc);
     }
 };
 
 struct NameNode : public ParseNode {
     static NameNode *create(JSAtom *atom, TreeContext *tc);
 
-    void inline initCommon(TreeContext *tc);
+    inline void initCommon(TreeContext *tc);
 };
 
 struct NameSetNode : public ParseNode {
     static inline NameSetNode *create(TreeContext *tc) {
         return (NameSetNode *)ParseNode::create(PN_NAMESET, tc);
     }
 };
 
 struct LexicalScopeNode : public ParseNode {
     static inline LexicalScopeNode *create(TreeContext *tc) {
         return (LexicalScopeNode *)ParseNode::create(PN_NAME, tc);
     }
 };
 
-void *
-AllocNodeUninitialized(Parser *parser);
-
-void
-AddNodeToFreeList(ParseNode *pn, Parser *parser);
-
-void
-PrepareNodeForMutation(ParseNode *pn, TreeContext *tc);
-
-ParseNode *
-RecycleTree(ParseNode *pn, TreeContext *tc);
-
 ParseNode *
 CloneLeftHandSide(ParseNode *opn, TreeContext *tc);
 
 /*
  * 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
  * a TOK_VAR list may be a Definition instead of a ParseNode. The pn_defn
@@ -960,16 +943,30 @@ struct Definition : public ParseNode
         if (isConst())
             return CONST;
         if (isLet())
             return LET;
         return VAR;
     }
 };
 
+class ParseNodeAllocator {
+  public:
+    explicit ParseNodeAllocator(JSContext *cx) : cx(cx), freelist(NULL) {}
+
+    void *allocNode();
+    void freeNode(ParseNode *pn);
+    ParseNode *freeTree(ParseNode *pn);
+    void prepareNodeForMutation(ParseNode *pn);
+
+  private:
+    JSContext *cx;
+    ParseNode *freelist;
+};
+
 inline bool
 ParseNode::test(uintN flag) const
 {
     JS_ASSERT(pn_defn || pn_arity == PN_FUNC || pn_arity == PN_NAME);
 #ifdef DEBUG
     if ((flag & (PND_ASSIGNED | PND_FUNARG)) && pn_defn && !(pn_dflags & flag)) {
         for (ParseNode *pn = ((Definition *) this)->dn_uses; pn; pn = pn->pn_link) {
             JS_ASSERT(!pn->pn_defn);
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -118,17 +118,17 @@ using namespace js::frontend;
 
 Parser::Parser(JSContext *cx, JSPrincipals *prin, StackFrame *cfp, bool foldConstants)
   : AutoGCRooter(cx, PARSER),
     context(cx),
     tokenStream(cx),
     principals(NULL),
     callerFrame(cfp),
     callerVarObj(cfp ? &cfp->varObj() : NULL),
-    nodeList(NULL),
+    allocator(cx),
     functionCount(0),
     traceListHead(NULL),
     tc(NULL),
     keepAtoms(cx->runtime),
     foldConstants(foldConstants)
 {
     cx->activeCompilations++;
     PodArrayZero(tempFreeList);
@@ -274,19 +274,19 @@ Parser::trace(JSTracer *trc)
  *   are deleted and should be recycled once we get here.
  *
  * - Mutated: funbox->node is NULL; the contents of the node itself could
  *   be anything. When we mutate a function node into some other kind of
  *   node, we lose all indication that the node was ever part of the
  *   function box tree; it could later be recycled, reallocated, and turned
  *   into anything at all. (Fortunately, method list members never get
  *   mutated, so we don't have to worry about that case.)
- *   PrepareNodeForMutation clears the node's function box's node pointer,
- *   disconnecting it entirely from the function box tree, and marking the
- *   function box to be trimmed out.
+ *   ParseNodeAllocator::prepareNodeForMutation clears the node's function
+ *   box's node pointer, disconnecting it entirely from the function box tree,
+ *   and marking the function box to be trimmed out.
  */
 void
 Parser::cleanFunctionList(FunctionBox **funboxHead)
 {
     FunctionBox **link = funboxHead;
     while (FunctionBox *box = *link) {
         if (!box->node) {
             /*
@@ -295,17 +295,17 @@ Parser::cleanFunctionList(FunctionBox **
              */
             *link = box->siblings;
         } else if (!box->node->pn_funbox) {
             /*
              * This funbox's parse node is ready to be recycled. Drop the box, recycle
              * the node, and stay at the same link.
              */
             *link = box->siblings;
-            AddNodeToFreeList(box->node, this);
+            allocator.freeNode(box->node);
         } else {
             /* The function is still live. */
 
             /* First, remove nodes for deleted functions from our methods list. */
             {
                 ParseNode **methodLink = &box->methods;
                 while (ParseNode *method = *methodLink) {
                     /* Method nodes are never rewritten in place to be other kinds of nodes. */
@@ -830,17 +830,17 @@ MakeDefIntoUse(Definition *dn, ParseNode
                 return NULL;
             //pn->dn_uses = lhs;
             dn = (Definition *) lhs;
         }
 
         dn->setOp((js_CodeSpec[dn->getOp()].format & JOF_SET) ? JSOP_SETNAME : JSOP_NAME);
     } else if (dn->kind() == Definition::FUNCTION) {
         JS_ASSERT(dn->isOp(JSOP_NOP));
-        PrepareNodeForMutation(dn, tc);
+        tc->parser->prepareNodeForMutation(dn);
         dn->setKind(TOK_NAME);
         dn->setArity(PN_NAME);
         dn->pn_atom = atom;
     }
 
     /* Now make dn no longer a definition, rather a use of pn. */
     JS_ASSERT(dn->isKind(TOK_NAME));
     JS_ASSERT(dn->isArity(PN_NAME));
@@ -2070,17 +2070,17 @@ Parser::functionDef(PropertyName *funNam
                  * closing brace.  See bug 640075.
                  */
                 fn->pn_pos.end = pn->pn_pos.end;
 
                 fn->pn_body = NULL;
                 fn->pn_cookie.makeFree();
 
                 tc->lexdeps->remove(funName);
-                RecycleTree(pn, tc);
+                freeTree(pn);
                 pn = fn;
             }
 
             if (!Define(pn, funName, tc))
                 return NULL;
         }
 
         /*
@@ -6248,17 +6248,17 @@ Parser::memberExpr(JSBool allowCallSynta
                 PopStatement(tc);
             }
 
             /* Check both tt and pn_type, to distinguish |x.(y)| and |x.y::z| from |x.y|. */
             if (tt == TOK_NAME && pn3->isKind(TOK_NAME)) {
                 pn2->setOp(JSOP_GETPROP);
                 pn2->pn_expr = pn;
                 pn2->pn_atom = pn3->pn_atom;
-                RecycleTree(pn3, tc);
+                freeTree(pn3);
             } else {
                 if (tt == TOK_LP) {
                     pn2->setKind(TOK_FILTER);
                     pn2->setOp(JSOP_FILTER);
 
                     /* A filtering predicate is like a with statement. */
                     tc->flags |= TCF_FUN_HEAVYWEIGHT;
                 } else if (TokenKindIsXML(pn3->getKind())) {
@@ -6874,17 +6874,17 @@ Parser::xmlElementOrList(JSBool allowLis
             return NULL;
         tokenStream.matchToken(TOK_XMLSPACE);
 
         tt = tokenStream.getToken();
         if (tt == TOK_XMLPTAGC) {
             /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */
             if (pn2->isKind(TOK_XMLSTAGO)) {
                 pn->makeEmpty();
-                RecycleTree(pn, tc);
+                freeTree(pn);
                 pn = pn2;
             } else {
                 JS_ASSERT(pn2->isKind(TOK_XMLNAME) ||
                           pn2->isKind(TOK_LC));
                 pn->initList(pn2);
                 if (!XML_FOLDABLE(pn2))
                     pn->pn_xflags |= PNX_CANTFOLD;
             }
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -101,17 +101,17 @@ struct Parser : private AutoGCRooter
 {
     JSContext           *const context; /* FIXME Bug 551291: use AutoGCRooter::context? */
     void                *tempFreeList[NUM_TEMP_FREELISTS];
     TokenStream         tokenStream;
     void                *tempPoolMark;  /* initial JSContext.tempPool mark */
     JSPrincipals        *principals;    /* principals associated with source */
     StackFrame          *const callerFrame;  /* scripted caller frame for eval and dbgapi */
     JSObject            *const callerVarObj; /* callerFrame's varObj */
-    ParseNode           *nodeList;      /* list of recyclable parse-node structs */
+    ParseNodeAllocator  allocator;
     uint32              functionCount;  /* number of functions in current unit */
     ObjectBox           *traceListHead; /* list of parsed object for GC tracing */
     TreeContext         *tc;            /* innermost tree context (stack-allocated) */
 
     /* Root atoms and objects allocated for the parsed tree. */
     AutoKeepAtoms       keepAtoms;
 
     /* Perform constant-folding; must be true when interfacing with the emitter. */
@@ -178,20 +178,24 @@ struct Parser : private AutoGCRooter
     /*
      * Report a parse (compile) error.
      */
     inline bool reportErrorNumber(ParseNode *pn, uintN flags, uintN errorNumber, ...);
 
   private:
     void *allocParseNode(size_t size) {
         JS_ASSERT(size == sizeof(ParseNode));
-        return AllocNodeUninitialized(this);
+        return allocator.allocNode();
     }
 
   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(allocParseNode,);
 
   private:
     /*
      * JS parsers, from lowest to highest precedence.
      *
      * Each parser must be called during the dynamic scope of a TreeContext
      * object, pointed to by this->tc.