WIP: UniqueNode draft
authorDavid Teller <dteller@mozilla.com>
Mon, 28 Aug 2017 16:07:55 +0200
changeset 654877 401bd84830199441e9d6039bbce7b6dbb3c1ba94
parent 654876 3fac8532f22b68b25212e9f611ecfa56e8704183
child 654878 3a3348f73f1bbfc17b837c4a34d90377f63f3274
push id76717
push userdteller@mozilla.com
push dateTue, 29 Aug 2017 11:36:57 +0000
milestone57.0a1
WIP: UniqueNode MozReview-Commit-ID: 3UltW3VjoiY
js/src/frontend/BinSource.cpp
js/src/frontend/BinSource.h
js/src/shell/js.cpp
--- a/js/src/frontend/BinSource.cpp
+++ b/js/src/frontend/BinSource.cpp
@@ -43,49 +43,51 @@ NewEmptyBindingData(JSContext* cx, LifoA
 const std::string BINJS_SCOPE = "BINJS:Scope";
 const std::string BINJS_VAR_NAME = "BINJS:VarDeclaredNames";
 const std::string BINJS_LET_NAME = "BINJS:LetDeclaredNames";
 const std::string BINJS_CONST_NAME = "BINJS:ConstDeclaredNames";
 const std::string BINJS_CAPTURED_NAME = "BINJS:CapturedNames";
 const std::string BINJS_DIRECT_EVAL = "BINJS:HasDirectEval";
 
 
-
 class BinParseContext: public ParseContext {
 public:
     BinParseContext(ASTReader*, GlobalSharedContext*, Directives*);
 };
 
 bool
-ASTReader::parse(char* start, char* stop, js::UniquePtr<ParseNode>& out) {
+ASTReader::parse(char* start, char* stop, ParseNode** out) {
     SimpleTokenReader reader(this->cx);
     reader.init(start, stop, nullptr);
 
 #if 0
     Directives directives(options().strictOption);
     GlobalSharedContext globalsc(this->cx, ScopeKind::Global,
                                  directives, options().extraWarningsOption);
     BinParseContext globalpc(this, &globalsc, /* newDirectives = */ nullptr);
     if (!globalpc.init()) {
         return false;
     }
 #endif // 0
 
-    if (!this->parseProgram(&reader, out)) {
+    UniqueNode result(nullptr, this->nodeFree);
+    if (!this->parseProgram(&reader, result)) {
         return false;
     }
 
     if (!reader.uninit()) {
         return this->raiseError("parse()");
     }
+
+    *out = result.release();
     return true;
 }
 
 bool
-ASTReader::parseProgram(SimpleTokenReader* reader, js::UniquePtr<ParseNode>& out) {
+ASTReader::parseProgram(SimpleTokenReader* reader, UniqueNode& out) {
     fprintf(stderr, "ASTReader::parseProgram\n");
     if (out) {
         return this->raiseAlreadyParsed("Program");
     }
     BinKind kind;
     SimpleTokenReader::BinFields fields(this->cx);
     SimpleTokenReader sub(this->cx);
 
@@ -100,17 +102,17 @@ ASTReader::parseProgram(SimpleTokenReade
     if (!this->parseBlockStatementAux(&sub, kind, fields, out)) {
         return false;
     }
 
     return true;
 }
 
 bool
-ASTReader::parseBlockStatement(SimpleTokenReader* reader, js::UniquePtr<ParseNode>& out) {
+ASTReader::parseBlockStatement(SimpleTokenReader* reader, UniqueNode& out) {
     fprintf(stderr, "ASTReader::parseBlockStatement\n");
     if (out) {
         // Already parsed.
         return this->raiseAlreadyParsed("BlockStatement");
     }
     BinKind kind;
     SimpleTokenReader::BinFields fields(this->cx);
     SimpleTokenReader sub(this->cx);
@@ -180,32 +182,29 @@ ASTReader::parseScope(SimpleTokenReader*
                     return false;
                 }
                 break;
             default:
                 return this->raiseInvalidField("Scope", field);
         }
     }
 
-    return true;
+   return true;
 }
 
 bool
 ASTReader::parseBlockStatementAux(SimpleTokenReader* reader,
     const BinKind name,
     const SimpleTokenReader::BinFields& fields,
-    js::UniquePtr<ParseNode>& out)
+    UniqueNode& out)
 {
     fprintf(stderr, "ASTReader::parseBlockStatementAux (%lu fields)\n", fields.length());
-    js::UniquePtr<ParseNode> body;
-    js::UniquePtr<ParseNode> directives; // Ignored
+    UniqueNode body(nullptr, this->nodeFree);
+    UniqueNode directives(nullptr, this->nodeFree); // Ignored
     ScopeData scope(this->cx);
-    if (!scope.init()) {
-        return this->raiseOOM();
-    }
 
     for (auto field: fields) {
         fprintf(stderr, "ASTReader::parseBlockStatementAux => field %d\n", field);
         switch (field) {
             case BinField::binjs_scope:
                 if (!this->parseScope(reader, scope)) {
                     return false;
                 }
@@ -254,17 +253,17 @@ ASTReader::parseBlockStatementAux(Simple
         bool isCaptured = scope.capturedNames->has(name);
         BindingName binding(atom, isCaptured);
         PodCopy(cursor, &binding, 1); // FIXME: Why does this work?
         cursor++;
         bindings->length++;
     }
     bindings->constStart = scope.letNames->length();
 
-    js::UniquePtr<ParseNode> result(new_<LexicalScopeNode>(bindings.get(), body.get()));
+    UniqueNode result(new_<LexicalScopeNode>(bindings.get(), body.get()), this->nodeFree);
     if (!result) {
         return this->raiseOOM();
     }
     Unused << bindings.release();
     Unused << body.release();
     out = Move(result);
     // FIXME: Validate capturedNames, etc.
     return true;
@@ -274,16 +273,19 @@ bool
 ASTReader::parseStringSet(SimpleTokenReader* reader, MutableHandle<mozilla::Maybe<NameBag>> out) {
     if (out.get()) {
         return this->raiseAlreadyParsed("{String}");
     }
     uint32_t length;
     SimpleTokenReader sub(this->cx);
 
     NameBag result(this->cx);
+    if (!result.init()) {
+        return this->raiseOOM();
+    }
 
     if (!reader->readList(length, sub)) {
         return this->raiseTokenError("{String}");
     }
 
     for (uint32_t i = 0; i < length; ++i) {
         RootedString string(this->cx);
         if (!this->readString(&sub, &string)) {
@@ -327,49 +329,49 @@ ASTReader::parseStringList(SimpleTokenRe
         }
     }
 
     out.set(Move(Some(Move(result))));
     return true;
 }
 
 bool
-ASTReader::parseStatementList(SimpleTokenReader* reader, js::UniquePtr<ParseNode>& out) {
+ASTReader::parseStatementList(SimpleTokenReader* reader, UniqueNode& out) {
     fprintf(stderr, "ASTReader::parseStatementList\n");
     if (out) {
         return this->raiseAlreadyParsed("[Statement]");
     }
     uint32_t length;
     SimpleTokenReader sub(this->cx);
 
-    js::UniquePtr<ParseNode> result(new_<ListNode>(PNK_STATEMENTLIST, TokenPos(0, 0)));
+    UniqueNode result(new_<ListNode>(PNK_STATEMENTLIST, TokenPos(0, 0)), this->nodeFree);
     if (!result) {
         return this->raiseOOM();
     }
 
     if (!reader->readList(length, sub)) {
         return this->raiseTokenError("[Statement]");
     }
 
     for (uint32_t i = 0; i < length; ++i) {
-        js::UniquePtr<ParseNode> statement;
+        UniqueNode statement(nullptr, this->nodeFree);
         if (!this->parseStatement(&sub, statement)) {
             return false;
         }
 
         result->append(statement.release()); // `result` knows how to deallocate `statement`.
     }
 
     result.swap(out);
 
     return true;
 }
 
 bool
-ASTReader::parseStatement(SimpleTokenReader* reader, js::UniquePtr<ParseNode>& out) {
+ASTReader::parseStatement(SimpleTokenReader* reader, UniqueNode& out) {
     if (out) {
         return this->raiseAlreadyParsed("Statement");
     }
     BinKind kind;
     SimpleTokenReader::BinFields fields(this->cx);
     SimpleTokenReader sub(this->cx);
 
     if (!reader->taggedTuple(kind, fields, &sub)) {
@@ -377,50 +379,50 @@ ASTReader::parseStatement(SimpleTokenRea
     }
     if (kind == BinKind::binjs_null) {
         out = nullptr;
         return true;
     }
 
     switch (kind) {
         case BinKind::empty_statement: {
-            js::UniquePtr<ParseNode> result(new_<NullaryNode>(PNK_NOP, TokenPos(0, 0)));
+            UniqueNode result(new_<NullaryNode>(PNK_NOP, TokenPos(0, 0)), this->nodeFree);
             if (!result) {
                 return this->raiseOOM();
             }
             out = Move(result);
             break;
         }
         case BinKind::block_statement: {
-            js::UniquePtr<ParseNode> body;
+            UniqueNode body(nullptr, this->nodeFree);
             if (!this->parseBlockStatementAux(&sub, kind, fields, body)) {
                 return false;
             }
             out = Move(body);
             break;
         }
         case BinKind::expression_statement: {
-            js::UniquePtr<ParseNode> body;
+            UniqueNode body(nullptr, this->nodeFree);
             if (!this->parseExpressionStatementAux(&sub, kind, fields, body)) {
                 return false;
             }
             out = Move(body);
             break;
         }
         case BinKind::debugger_statement: {
-            js::UniquePtr<ParseNode> result(new_<DebuggerStatement>(TokenPos(0, 0)));
+            UniqueNode result(new_<DebuggerStatement>(TokenPos(0, 0)), this->nodeFree);
             if (!result) {
                 return this->raiseOOM();
             }
             out = Move(result);
             break;
         }
         case BinKind::with_statement: {
-            js::UniquePtr<ParseNode> body;
-            js::UniquePtr<ParseNode> expr;
+            UniqueNode body(nullptr, this->nodeFree);
+            UniqueNode expr(nullptr, this->nodeFree);
             for (auto field: fields) {
                 switch (field) {
                     case BinField::body:
                         if (!this->parseStatement(&sub, body)) {
                             return false;
                         }
                         break;
                     case BinField::object:
@@ -431,54 +433,54 @@ ASTReader::parseStatement(SimpleTokenRea
                     default:
                         return this->raiseInvalidField("WithStatement", field);
                 }
             }
             if (!body || !expr) {
                 return this->raiseMissingField("WithStatement");
             }
 
-            js::UniquePtr<ParseNode> result(new_<BinaryNode>(PNK_WITH, JSOP_NOP, TokenPos(0, 0),
-                                            expr.get(), body.get()));
+            UniqueNode result(new_<BinaryNode>(PNK_WITH, JSOP_NOP, TokenPos(0, 0),
+                                            expr.get(), body.get()), this->nodeFree);
             if (!result) {
                 return this->raiseOOM();
             }
             Unused << expr.release();
             Unused << body.release();
 
             out = Move(result);
             break;
         }
         case BinKind::return_statement: {
-            js::UniquePtr<ParseNode> arg;
+            UniqueNode arg(nullptr, this->nodeFree);
             for (auto field: fields) {
                 switch (field) {
                     case BinField::argument:
                         if (!this->parseExpression(&sub, arg)) { // FIXME: Make sure that it works with no expression.
                             return false;
                         }
                         break;
                     default:
                         return this->raiseInvalidField("ReturnStatement", field);
                 }
             }
 
             // `arg` is optional, so we don't check whether it's `nullptr`.
-            js::UniquePtr<ParseNode> result(new_<UnaryNode>(PNK_RETURN, JSOP_RETURN, TokenPos(0, 0), arg.get()));
+            UniqueNode result(new_<UnaryNode>(PNK_RETURN, JSOP_RETURN, TokenPos(0, 0), arg.get()), this->nodeFree);
             if (!result) {
                 return this->raiseOOM();
             }
 
             Unused << arg.release();
             out = Move(result);
             break;
         }
         case BinKind::labeled_statement: {
             Rooted<PropertyName*> label(this->cx);
-            js::UniquePtr<ParseNode> body;
+            UniqueNode body(nullptr, this->nodeFree);
 
             for (auto field: fields) {
                 switch (field) {
                     case BinField::label:
                         if (!this->readString(&sub, &label)) {
                             return false;
                         }
                         break;
@@ -491,17 +493,17 @@ ASTReader::parseStatement(SimpleTokenRea
                         return this->raiseInvalidField("LabeledStatement", field);
                 }
             }
 
             if (!label || !body) {
                 return this->raiseMissingField("LabeledStatement");
             }
 
-            js::UniquePtr<ParseNode> result(new_<LabeledStatement>(label.get(), body.get(), 0));
+            UniqueNode result(new_<LabeledStatement>(label.get(), body.get(), 0), this->nodeFree);
             if (!result) {
                 return this->raiseOOM();
             }
 
             Unused << body.release();
             out = Move(result);
             break;
     }
@@ -522,37 +524,37 @@ ASTReader::parseStatement(SimpleTokenRea
             }
         }
 
         if (!label) {
             return this->raiseMissingField("ContinueStatement");
         }
 
         if (kind == BinKind::break_statement) {
-            js::UniquePtr<ParseNode> result(new_<BreakStatement>(label.get(), TokenPos(0, 0)));
+            UniqueNode result(new_<BreakStatement>(label.get(), TokenPos(0, 0)), this->nodeFree);
             if (!result) {
                 return this->raiseOOM();
             }
             out = Move(result);
         } else {
-            js::UniquePtr<ParseNode> result(new_<ContinueStatement>(label.get(), TokenPos(0, 0)));
+            UniqueNode result(new_<ContinueStatement>(label.get(), TokenPos(0, 0)), this->nodeFree);
             if (!result) {
                 return this->raiseOOM();
             }
             out = Move(result);
         }
 
         break;
 
     }
     case BinKind::if_statement: {
 
-        js::UniquePtr<ParseNode> test;
-        js::UniquePtr<ParseNode> consequent;
-        js::UniquePtr<ParseNode> alternate; // Optional
+        UniqueNode test(nullptr, this->nodeFree);
+        UniqueNode consequent(nullptr, this->nodeFree);
+        UniqueNode alternate(nullptr, this->nodeFree); // Optional
 
         for (auto field: fields) {
             switch (field) {
                 case BinField::test: {
                     if (!this->parseExpression(&sub, test)) {
                         return false;
                     }
                     break;
@@ -574,32 +576,32 @@ ASTReader::parseStatement(SimpleTokenRea
             }
         }
 
         if (!test || !consequent) {
             // Do not test `alternate`, since that value is optional.
             return this->raiseMissingField("IfStatement");
         }
 
-        js::UniquePtr<ParseNode> result(new_<TernaryNode>(PNK_IF, JSOP_NOP, test.get(), consequent.get(), alternate.get()));
+        UniqueNode result(new_<TernaryNode>(PNK_IF, JSOP_NOP, test.get(), consequent.get(), alternate.get()), this->nodeFree);
         if (!result) {
             return this->raiseOOM();
         }
 
         Unused << test.release();
         Unused << consequent.release();
         Unused << alternate.release();
 
         out = Move(result);
         break;
     }
     case BinKind::switch_statement: {
 
-        js::UniquePtr<ParseNode> discriminant;
-        js::UniquePtr<ParseNode> cases;
+        UniqueNode discriminant(nullptr, this->nodeFree);
+        UniqueNode cases(nullptr, this->nodeFree);
 
         for (auto field: fields) {
             switch (field) {
                 case BinField::discriminant: {
                     if (!this->parseExpression(&sub, discriminant)) {
                         return false;
                     }
                     break;
@@ -614,30 +616,30 @@ ASTReader::parseStatement(SimpleTokenRea
                     return this->raiseInvalidField("SwitchStatement", field);
             }
         }
 
         if (!discriminant || !cases) {
             return this->raiseMissingField("SwtichStatement");
         }
 
-        js::UniquePtr<ParseNode> result(new_<BinaryNode>(PNK_SWITCH, JSOP_NOP, TokenPos(0, 0), discriminant.get(), cases.get()));
+        UniqueNode result(new_<BinaryNode>(PNK_SWITCH, JSOP_NOP, TokenPos(0, 0), discriminant.get(), cases.get()), this->nodeFree);
         if (!result) {
             return this->raiseOOM();
         }
 
         Unused << discriminant.release();
         Unused << cases.release();
         out = Move(result);
         break;
     }
     case BinKind::switch_case: {
 
-        js::UniquePtr<ParseNode> test;
-        js::UniquePtr<ParseNode> consequent;
+        UniqueNode test(nullptr, this->nodeFree);
+        UniqueNode consequent(nullptr, this->nodeFree);
 
         for (auto field: fields) {
             switch (field) {
                 case BinField::test:
                     if (!this->parseExpression(&sub, test)) {
                         return false;
                     }
                     break;
@@ -646,61 +648,61 @@ ASTReader::parseStatement(SimpleTokenRea
                         return false;
                     }
                     break;
                 default:
                     return this->raiseInvalidField("SwitchCase", field);
             }
         }
 
-        js::UniquePtr<ParseNode> result(new_<CaseClause>(test.get(), consequent.get(), 0));
+        UniqueNode result(new_<CaseClause>(test.get(), consequent.get(), 0), this->nodeFree);
         if (!result) {
             return this->raiseOOM();
         }
 
         Unused << test.release();
         Unused << consequent.release();
         out = Move(result);
         break;
 
     }
 
     case BinKind::throw_statement: {
 
-        js::UniquePtr<ParseNode> arg;
+        UniqueNode arg(nullptr, this->nodeFree);
         for (auto field: fields) {
             if (field == BinField::arguments) {
                 if (!this->parseExpression(&sub, arg)) {
                     return false;
                 }
             } else {
                 return this->raiseInvalidField("ThrowStatement", field);
             }
         }
 
         if (!arg) {
             return this->raiseMissingField("ThrowStatement");
         }
 
-        js::UniquePtr<ParseNode> result(new_<UnaryNode>(PNK_THROW, JSOP_THROW, TokenPos(0, 0), arg.get()));
+        UniqueNode result(new_<UnaryNode>(PNK_THROW, JSOP_THROW, TokenPos(0, 0), arg.get()), this->nodeFree);
         if (!result) {
             return this->raiseOOM();
         }
 
         Unused << arg.release();
         out = Move(result);
         break;
 
     }
 
     case BinKind::try_statement: {
 
-        js::UniquePtr<ParseNode> block;
-        js::UniquePtr<ParseNode> handler;
-        js::UniquePtr<ParseNode> finalizer;
+        UniqueNode block(nullptr, this->nodeFree);
+        UniqueNode handler(nullptr, this->nodeFree);
+        UniqueNode finalizer(nullptr, this->nodeFree);
 
         for (auto field: fields) {
             switch (field) {
                 case BinField::block:
                     if (!this->parseBlockStatement(&sub, block)) {
                         return false;
                     }
                     break;
@@ -718,35 +720,35 @@ ASTReader::parseStatement(SimpleTokenRea
                     return this->raiseInvalidField("TryStatement", field);
             }
         }
 
         if (!block || !handler) { // `finalizer` is optional
             return this->raiseMissingField("TryStatement");
         }
 
-        js::UniquePtr<ParseNode> result(new_<TernaryNode>(PNK_TRY, JSOP_NOP, block.get(), handler.get(), finalizer.get(), TokenPos(0, 0)));
+        UniqueNode result(new_<TernaryNode>(PNK_TRY, JSOP_NOP, block.get(), handler.get(), finalizer.get(), TokenPos(0, 0)), this->nodeFree);
         if (!result) {
             return this->raiseOOM();
         }
 
         Unused << block.release();
         Unused << handler.release();
         Unused << finalizer.release();
 
         out = Move(result);
         break;
 
     }
 
     case BinKind::while_statement: MOZ_FALLTHROUGH;
     case BinKind::do_while_statement: {
 
-        js::UniquePtr<ParseNode> test;
-        js::UniquePtr<ParseNode> body;
+        UniqueNode test(nullptr, this->nodeFree);
+        UniqueNode body(nullptr, this->nodeFree);
 
         for (auto field: fields) {
             switch (field) {
                 case BinField::test:
                     if (!this->parseExpression(&sub, test)) {
                         return false;
                     }
                     break;
@@ -760,46 +762,43 @@ ASTReader::parseStatement(SimpleTokenRea
             }
         }
 
         if (!test || !body) {
             return this->raiseMissingField("DoWhileStatement");
         }
 
         if (kind == BinKind::while_statement) {
-            js::UniquePtr<ParseNode> result(new_<BinaryNode>(PNK_WHILE, JSOP_NOP, TokenPos(0, 0), body.get(), test.get()));
+            UniqueNode result(new_<BinaryNode>(PNK_WHILE, JSOP_NOP, TokenPos(0, 0), body.get(), test.get()), this->nodeFree);
 
             if (!result) {
                 return this->raiseOOM();
             }
             out = Move(result);
         } else {
-            js::UniquePtr<ParseNode> result(new_<BinaryNode>(PNK_DOWHILE, JSOP_NOP, TokenPos(0, 0), body.get(), test.get()));
+            UniqueNode result(new_<BinaryNode>(PNK_DOWHILE, JSOP_NOP, TokenPos(0, 0), body.get(), test.get()), this->nodeFree);
 
             if (!result) {
                 return this->raiseOOM();
             }
             out = Move(result);
         }
 
         Unused << test.release();
         Unused << body.release();
         break;
     }
 
     case BinKind::for_statement: {
 
-        js::UniquePtr<ParseNode> init; // Optional
-        js::UniquePtr<ParseNode> test; // Optional
-        js::UniquePtr<ParseNode> update; // Optional
+        UniqueNode init(nullptr, this->nodeFree); // Optional
+        UniqueNode test(nullptr, this->nodeFree); // Optional
+        UniqueNode update(nullptr, this->nodeFree); // Optional
         ScopeData scope(this->cx); // Optional
-        if (!scope.init()) {
-            return this->raiseOOM();
-        }
-        js::UniquePtr<ParseNode> body; // Required
+        UniqueNode body(nullptr, this->nodeFree); // Required
 
         for (auto field: fields) {
             switch (field) {
                 case BinField::init: {
                     // This can be either a VarDecl or an Expression.
                     SimpleTokenReader::BinFields subFields(this->cx);
                     SimpleTokenReader subSub(this->cx);
                     BinKind subKind;
@@ -849,47 +848,44 @@ ASTReader::parseStatement(SimpleTokenRea
                     return this->raiseInvalidField("ForStatement", field);
             }
         }
 
         if (!body) {
             return this->raiseMissingField("ForStatement");
         }
 
-        js::UniquePtr<ParseNode> forHead(new_<TernaryNode>(PNK_FORHEAD, JSOP_NOP, init.get(), test.get(), update.get(), TokenPos(0, 0)));
+        UniqueNode forHead(new_<TernaryNode>(PNK_FORHEAD, JSOP_NOP, init.get(), test.get(), update.get(), TokenPos(0, 0)), this->nodeFree);
         if (!forHead) {
             return this->raiseOOM();
         }
         Unused << init.release();
         Unused << update.release();
         Unused << test.release();
 
-        js::UniquePtr<ParseNode> result(new_<BinaryNode>(PNK_FOR, JSOP_NOP, TokenPos(0, 0),
-                                          forHead.get(), body.get()));
+        UniqueNode result(new_<BinaryNode>(PNK_FOR, JSOP_NOP, TokenPos(0, 0),
+                                          forHead.get(), body.get()), this->nodeFree);
 
         if (!result) {
             return this->raiseOOM();
         }
 
         Unused << forHead.release();
         Unused << body.release();
 
         out = Move(result);
         break;
     }
 
     case BinKind::for_in_statement: {
 
-        js::UniquePtr<ParseNode> left;
-        js::UniquePtr<ParseNode> right;
-        js::UniquePtr<ParseNode> body;
+        UniqueNode left(nullptr, this->nodeFree);
+        UniqueNode right(nullptr, this->nodeFree);
+        UniqueNode body(nullptr, this->nodeFree);
         ScopeData scope(this->cx); // Optional
-        if (!scope.init()) {
-            return this->raiseOOM();
-        }
 
         for (auto field: fields) {
             switch (field) {
                 case BinField::left: {
                     // This can be either a VarDecl or a Pattern.
                     SimpleTokenReader::BinFields subFields(this->cx);
                     SimpleTokenReader subSub(this->cx);
                     BinKind subKind;
@@ -933,40 +929,40 @@ ASTReader::parseStatement(SimpleTokenRea
                     return this->raiseInvalidField("ForInStatement", field);
             }
         }
 
         if (!left || !right || !body) {
             return this->raiseMissingField("ForInStatement");
         }
 
-        js::UniquePtr<ParseNode> forHead(new_<TernaryNode>(PNK_FORIN, JSOP_NOP, left.get(), nullptr, right.get(), TokenPos(0, 0)));
+        UniqueNode forHead(new_<TernaryNode>(PNK_FORIN, JSOP_NOP, left.get(), nullptr, right.get(), TokenPos(0, 0)), this->nodeFree);
         if (!forHead) {
             return this->raiseOOM();
         }
 
         Unused << left.release();
         Unused << right.release();
 
-        js::UniquePtr<ParseNode> result(new_<BinaryNode>(PNK_FOR, JSOP_ITER, TokenPos(0, 0),
-                                          forHead.get(), body.get()));
+        UniqueNode result(new_<BinaryNode>(PNK_FOR, JSOP_ITER, TokenPos(0, 0),
+                                          forHead.get(), body.get()), this->nodeFree);
         if (!result) {
             return this->raiseOOM();
         }
 
         Unused << forHead.release();
         Unused << body.release();
 
         out = Move(result);
         break;
 
     }
 
     case BinKind::function_declaration: {
-        js::UniquePtr<ParseNode> result;
+        UniqueNode result(nullptr, this->nodeFree);
         if (!this->parseFunctionAux(&sub, kind, fields, result)) {
             return false;
         }
 
         out = Move(result);
         break;
 
     }
@@ -978,31 +974,28 @@ ASTReader::parseStatement(SimpleTokenRea
     default:
         return this->raiseInvalidKind("Statement", kind);
     }
 
     return true;
 }
 
 bool
-ASTReader::parseFunctionAux(SimpleTokenReader* reader, const BinKind kind, const SimpleTokenReader::BinFields& fields, js::UniquePtr<ParseNode>& out) {
+ASTReader::parseFunctionAux(SimpleTokenReader* reader, const BinKind kind, const SimpleTokenReader::BinFields& fields, UniqueNode& out) {
     MOZ_ASSERT(kind == BinKind::function_declaration || kind == BinKind::function_expression || kind == BinKind::object_method);
     if (out) {
         return this->raiseAlreadyParsed("Function");
     }
 
-    js::UniquePtr<ParseNode> id;
-    js::UniquePtr<ParseNode> params;
-    js::UniquePtr<ParseNode> body;
+    UniqueNode id(nullptr, this->nodeFree);
+    UniqueNode params(nullptr, this->nodeFree);
+    UniqueNode body(nullptr, this->nodeFree);
     ScopeData scope(this->cx);
-    if (!scope.init()) {
-        return this->raiseOOM();
-    }
 
-    js::UniquePtr<ParseNode> key; // Methods only
+    UniqueNode key(nullptr, this->nodeFree); // Methods only
     Maybe<bool> computed;     // Methods only
     Maybe<std::string> method_kind; // Methods only
 
     for (auto field: fields) {
         switch (field) {
             case BinField::id:
                 if (!this->parsePattern(reader, id)) {
                     return false;
@@ -1110,60 +1103,60 @@ ASTReader::parseFunctionAux(SimpleTokenR
 
     MOZ_ASSERT(body->getKind() == PNK_STATEMENTLIST); // FIXME: If that's not the case, promote to PNK_STATEMENTLIST
     params->append(body.release());
 
     const JSOp op =
         kind == BinKind::object_method
         ? JSOP_LAMBDA
         : JSOP_NOP;
-    js::UniquePtr<ParseNode> function(new_<CodeNode>(PNK_FUNCTION, op, TokenPos(0, 0)));
+    UniqueNode function(new_<CodeNode>(PNK_FUNCTION, op, TokenPos(0, 0)), this->nodeFree);
     if (!function) {
         return this->raiseOOM();
     }
 
     function->pn_funbox = funbox;
     function->pn_body   = params.release();
 
-    js::UniquePtr<ParseNode> result;
+    UniqueNode result(nullptr, this->nodeFree);
     if (kind == BinKind::object_method) {
         JSOp op;
         if (*method_kind == "init") {
             op = JSOP_INITPROP;
         } else if (*method_kind == "get") {
             op = JSOP_INITPROP_GETTER;
         } else if (*method_kind == "set") {
             op = JSOP_INITPROP_SETTER;
         } else {
             return this->raiseInvalidEnum("ObjectMethod", *method_kind);
         }
-        result = js::UniquePtr<ParseNode>(this->new_<BinaryNode>(PNK_COLON, op, key.get(), function.get()));
+        result = UniqueNode(this->new_<BinaryNode>(PNK_COLON, op, key.get(), function.get()), this->nodeFree);
         if (!result) {
             return this->raiseOOM();
         }
 
         Unused << key.release();
         Unused << function.release();
     } else {
         result = Move(function);
     }
 
     out = Move(result);
     return true;
 }
 
 bool
-ASTReader::parseVariableDeclarationAux(SimpleTokenReader* reader, const BinKind name, const SimpleTokenReader::BinFields& fields, js::UniquePtr<ParseNode>& out) {
-    if (!out) {
+ASTReader::parseVariableDeclarationAux(SimpleTokenReader* reader, const BinKind name, const SimpleTokenReader::BinFields& fields, UniqueNode& out) {
+    if (out) {
         return this->raiseAlreadyParsed("VariableDeclaration");
     }
 
     ParseNodeKind kind = PNK_LIMIT;
     JSOp op = JSOP_NOP;
-    js::UniquePtr<ParseNode> result;
+    UniqueNode result(nullptr, this->nodeFree);
 
     for (auto field: fields) {
         switch (field) {
             case BinField::kind: {
                 Maybe<std::string> kindName;
                 if (!this->readString(reader, kindName)) {
                     return false;
                 }
@@ -1197,30 +1190,30 @@ ASTReader::parseVariableDeclarationAux(S
                 if (!reader->readList(length, sub)) {
                     return this->raiseTokenError("VariableDeclaration");
                 }
 
                 if (length == 0) {
                     return this->raiseEmpty("VariableDeclaration");
                 }
 
-                js::UniquePtr<ParseNode> root(new_<ListNode>(PNK_LIMIT /*Placeholder*/, JSOP_NOP/*Placeholder*/, TokenPos(0, 0)));
+                UniqueNode root(new_<ListNode>(PNK_NOP /*Placeholder*/, JSOP_NOP/*Placeholder*/, TokenPos(0, 0)), this->nodeFree);
                 if (!root) {
                     return this->raiseOOM();
                 }
 
-                js::UniquePtr<ParseNode> first;
+                UniqueNode first(nullptr, this->nodeFree);
                 if (!this->parseVariableDeclarator(&sub, first)) {
                     return this->raiseOOM();
                 }
                 root->initList(first.release());
 
 
                 for (uint32_t i = 1; i < length; ++i) {
-                    js::UniquePtr<ParseNode> current;
+                    UniqueNode current(nullptr, this->nodeFree);
                     if (!this->parseVariableDeclarator(&sub, current)) {
                         return false;
                     }
                     if (!current) {
                         return this->raiseMissingField("VariableDeclaration");
                     }
                     root->append(current.release());
                 }
@@ -1234,47 +1227,49 @@ ASTReader::parseVariableDeclarationAux(S
     }
 
     if (!result || kind == PNK_LIMIT) {
         return this->raiseMissingField("VariableDeclaration");
     }
 
     result->setKind(kind);
     result->setOp(op);
+
+    MOZ_ASSERT(!result->isKind(PNK_NOP));
     out = Move(result);
 
     return true;
 }
 
 
 bool
-ASTReader::parseExpression(SimpleTokenReader* reader, js::UniquePtr<ParseNode>& out) {
+ASTReader::parseExpression(SimpleTokenReader* reader, UniqueNode& out) {
     if (out) {
         return this->raiseAlreadyParsed("Expression");
     }
 
     SimpleTokenReader::BinFields fields(this->cx);
     SimpleTokenReader sub(this->cx);
     BinKind kind;
 
     if (!reader->taggedTuple(kind, fields, &sub)) {
         return this->raiseTokenError("Expression");
     }
 
     return this->parseExpressionAux(&sub, kind, fields, out);
 }
 
 bool
-ASTReader::parseExpressionStatementAux(SimpleTokenReader* reader, const BinKind kind, const SimpleTokenReader::BinFields& fields, js::UniquePtr<ParseNode>& out) {
+ASTReader::parseExpressionStatementAux(SimpleTokenReader* reader, const BinKind kind, const SimpleTokenReader::BinFields& fields, UniqueNode& out) {
     MOZ_ASSERT(kind == BinKind::expression_statement);
     if (out) {
         return this->raiseAlreadyParsed("ExpressionStatement");
     }
 
-    js::UniquePtr<ParseNode> result;
+    UniqueNode result(nullptr, this->nodeFree);
     for (auto field: fields) {
         switch (field) {
             case BinField::expression:
                 if (!this->parseExpression(reader, result)) {
                     return false;
                 }
                 break;
             default:
@@ -1286,35 +1281,35 @@ ASTReader::parseExpressionStatementAux(S
         return this->raiseMissingField("ExpressionStatement");
     }
 
     out = Move(result);
     return true;
 }
 
 bool
-ASTReader::parseVariableDeclarator(SimpleTokenReader* reader, js::UniquePtr<ParseNode>& out) {
+ASTReader::parseVariableDeclarator(SimpleTokenReader* reader, UniqueNode& out) {
     if (out) {
         return this->raiseAlreadyParsed("VariableDeclarator");
     }
 
     BinKind kind;
     SimpleTokenReader::BinFields fields(this->cx);
     SimpleTokenReader sub(this->cx);
 
     if (!reader->taggedTuple(kind, fields, &sub)) {
         return this->raiseTokenError("VariableDeclarator");
     }
 
     if (kind != BinKind::variable_declarator) {
         return this->raiseInvalidKind("VariableDeclarator", kind);
     }
 
-    js::UniquePtr<ParseNode> id;
-    js::UniquePtr<ParseNode> init; // Optional.
+    UniqueNode id(nullptr, this->nodeFree);
+    UniqueNode init(nullptr, this->nodeFree); // Optional.
     for (auto field: fields) {
         switch (field) {
             case BinField::id:
                 if (!this->parsePattern(&sub, id)) {
                     return false;
                 }
                 break;
             case BinField::init:
@@ -1326,125 +1321,125 @@ ASTReader::parseVariableDeclarator(Simpl
                 return this->raiseInvalidField("VariableDeclarator", field);
         }
     }
 
     if (!id) {
         return this->raiseMissingField("VariableDeclarator");
     }
 
-    js::UniquePtr<ParseNode> result;
+    UniqueNode result(nullptr, this->nodeFree);
 
     // FIXME: Documentation in ParseNode is clearly obsolete.
     if (id->isKind(PNK_NAME)) {
         // var foo [= bar]
         RootedAtom atom(cx, id->pn_atom);
-        result = js::UniquePtr<ParseNode>(new_<NameNode>(PNK_NAME, JSOP_NOP, atom, TokenPos(0, 0)));
+        result = UniqueNode(new_<NameNode>(PNK_NAME, JSOP_NOP, atom, TokenPos(0, 0)), this->nodeFree);
         if (!result) {
             return this->raiseOOM();
         }
 
         if (init) {
             result->pn_expr = init.release();
         }
     } else {
         // var pattern = bar
         if (!init) {
             // Here, `init` is required.
             return this->raiseMissingField("VariableDeclarator");
         }
-        result = js::UniquePtr<ParseNode>(new_<BinaryNode>(PNK_ASSIGN, JSOP_NOP, TokenPos(0, 0), nullptr, nullptr));
+        result = UniqueNode(new_<BinaryNode>(PNK_ASSIGN, JSOP_NOP, TokenPos(0, 0), nullptr, nullptr), this->nodeFree);
         if (!result) {
             return this->raiseOOM();
         }
 
         result->pn_left = id.release();
         result->pn_right = init.release();
     }
 
     out = Move(result);
     return true;
 }
 
 bool
-ASTReader::parseExpressionOrElisionList(SimpleTokenReader* reader, js::UniquePtr<ParseNode>& out) {
+ASTReader::parseExpressionOrElisionList(SimpleTokenReader* reader, UniqueNode& out) {
     if (out) {
         return this->raiseAlreadyParsed("[Expression|null]");
     }
 
     uint32_t length;
     SimpleTokenReader sub(this->cx);
 
     if (!reader->readList(length, sub)) {
         return this->raiseTokenError("[Expression|null]");
     }
 
-    js::UniquePtr<ParseNode> result(new_<ListNode>(PNK_ARRAY, TokenPos(0, 0)));
+    UniqueNode result(new_<ListNode>(PNK_ARRAY, TokenPos(0, 0)), this->nodeFree);
     if (!result) {
         return this->raiseOOM();
     }
 
     for (uint32_t i = 0; i < length; ++i) {
-        js::UniquePtr<ParseNode> expr;
+        UniqueNode expr(nullptr, this->nodeFree);
         if (!this->parseExpression(&sub, expr)) {
             return false;
         }
 
         if (!expr) {
-            expr = js::UniquePtr<ParseNode>(new_<NullaryNode>(PNK_ELISION, TokenPos(0, 0)));
+            expr = UniqueNode(new_<NullaryNode>(PNK_ELISION, TokenPos(0, 0)), this->nodeFree);
             if (!expr) {
                 return this->raiseOOM();
             }
         }
 
         result->append(expr.release());
     }
 
     out = Move(result);
     return true;
 }
 
 
 bool
-ASTReader::parseSwitchCaseList(SimpleTokenReader* reader, js::UniquePtr<ParseNode>& out) {
+ASTReader::parseSwitchCaseList(SimpleTokenReader* reader, UniqueNode& out) {
     if (out) {
         return this->raiseAlreadyParsed("[SwitchCase]");
     }
 
     uint32_t length;
     SimpleTokenReader sub(this->cx);
 
     if (!reader->readList(length, sub)) {
         return this->raiseTokenError("[SwitchCase]");
     }
 
-    js::UniquePtr<ParseNode> result(new_<ListNode>(PNK_ARRAY, TokenPos(0, 0)));
+    UniqueNode result(new_<ListNode>(PNK_ARRAY, TokenPos(0, 0)), this->nodeFree);
     if (!result) {
         return this->raiseOOM();
     }
 
     for (uint32_t i = 0; i < length; ++i) {
-        js::UniquePtr<ParseNode> case_;
+        UniqueNode case_(nullptr, this->nodeFree);
         if (!this->parseSwitchCase(&sub, case_)) {
             return false;
         }
 
         if (!case_) {
             return this->raiseEmpty("[SwitchCase]");
         }
 
         result->append(case_.release());
     }
 
     out = Move(result);
     return true;
 }
 
 bool
-ASTReader::parseExpressionAux(SimpleTokenReader* reader, const BinKind kind, const SimpleTokenReader::BinFields& fields, js::UniquePtr<ParseNode>& out) {
+ASTReader::parseExpressionAux(SimpleTokenReader* reader, const BinKind kind, const SimpleTokenReader::BinFields& fields, UniqueNode& out) {
         switch (kind) {
             case BinKind::binjs_null:
                 return true;
             case BinKind::identifier: {
                 Rooted<PropertyName*> id(this->cx);
                 for (auto field: fields) {
                     switch (field) {
                         case BinField::name:
@@ -1456,17 +1451,17 @@ ASTReader::parseExpressionAux(SimpleToke
                             return this->raiseInvalidField("Identifier", field);
                     }
                 }
 
                 if (!id) {
                     return this->raiseMissingField("Identifier");
                 }
 
-                js::UniquePtr<ParseNode> result(new_<NameNode>(PNK_NAME, JSOP_GETNAME, id, TokenPos(0, 0)));
+                UniqueNode result(new_<NameNode>(PNK_NAME, JSOP_GETNAME, id, TokenPos(0, 0)), this->nodeFree);
                 if (!result) {
                     return this->raiseOOM();
                 }
 
                 out = Move(result);
                 break;
             }
             case BinKind::boolean_literal: {
@@ -1482,30 +1477,30 @@ ASTReader::parseExpressionAux(SimpleToke
                             return this->raiseInvalidField("BooleanLiteral", field);
                     }
                 }
 
                 if (!value) {
                     return this->raiseMissingField("BooleanLiteral");
                 }
 
-                js::UniquePtr<ParseNode> result(new_<BooleanLiteral>(*value, TokenPos(0, 0)));
+                UniqueNode result(new_<BooleanLiteral>(*value, TokenPos(0, 0)), this->nodeFree);
                 if (!result) {
                     return this->raiseOOM();
                 }
 
                 out = Move(result);
                 break;
             }
             case BinKind::directive_literal:
                 // A directive is not truly a literal, so this doesn't make sense
                 // here.
                 return this->raiseError("DirectiveLiteral (not truly a literal)");
             case BinKind::null_literal: {
-                js::UniquePtr<ParseNode> result(new_<NullLiteral>(TokenPos(0, 0)));
+                UniqueNode result(new_<NullLiteral>(TokenPos(0, 0)), this->nodeFree);
                 if (!result) {
                     return this->raiseOOM();
                 }
                 out = Move(result);
                 break;
             }
             case BinKind::numeric_literal: {
                 Maybe<double> value;
@@ -1520,17 +1515,17 @@ ASTReader::parseExpressionAux(SimpleToke
                             return this->raiseInvalidField("NumericLiteral", field);
                     }
                 }
 
                 if (!value) {
                     return this->raiseMissingField("NumericLiteral");
                 }
 
-                js::UniquePtr<ParseNode> result(new_<NullaryNode>(PNK_NUMBER, TokenPos(0, 0)));
+                UniqueNode result(new_<NullaryNode>(PNK_NUMBER, TokenPos(0, 0)), this->nodeFree);
                 if (!result) {
                     return this->raiseOOM();
                 }
                 result->initNumber(*value, DecimalPoint::HasDecimal); // FIXME: Is the DecimalPoint important?
                 out = Move(result);
                 break;
             }
             case BinKind::regexp_literal: {
@@ -1588,17 +1583,17 @@ ASTReader::parseExpressionAux(SimpleToke
                     return this->raiseOOM();
                 }
 
 
                 ObjectBox* objbox = this->newObjectBox(reobj);
                 if (!objbox) {
                     return this->raiseOOM();
                 }
-                js::UniquePtr<ParseNode> result(new_<RegExpLiteral>(objbox, TokenPos(0, 0)));
+                UniqueNode result(new_<RegExpLiteral>(objbox, TokenPos(0, 0)), this->nodeFree);
                 if (!result) {
                     // FIXME: I *think* that `objbox` is traced.
                     return this->raiseOOM();
                 }
 
                 out = Move(result);
                 break;
             }
@@ -1615,83 +1610,83 @@ ASTReader::parseExpressionAux(SimpleToke
                             return this->raiseInvalidField("StringLiteral", field);
                     }
                 }
 
                 if (!value) {
                     return this->raiseMissingField("StringLiteral");
                 }
 
-                js::UniquePtr<ParseNode> result(new_<NullaryNode>(PNK_STRING, JSOP_NOP, TokenPos(0, 0), value));
+                UniqueNode result(new_<NullaryNode>(PNK_STRING, JSOP_NOP, TokenPos(0, 0), value), this->nodeFree);
                 if (!result) {
                     return this->raiseOOM();
                 }
 
                 out = Move(result);
                 break;
             }
             case BinKind::this_expression: {
-                js::UniquePtr<ParseNode> thisName;
+                UniqueNode thisName(nullptr, this->nodeFree);
                 if (this->thisBinding() == ThisBinding::Function) {
-                    thisName = js::UniquePtr<ParseNode>(new_<NameNode>(PNK_NAME, JSOP_GETNAME, this->cx->names().dotThis, TokenPos(0, 0)));
+                    thisName = UniqueNode(new_<NameNode>(PNK_NAME, JSOP_GETNAME, this->cx->names().dotThis, TokenPos(0, 0)), this->nodeFree);
                     if (!thisName) {
                         return this->raiseOOM();
                     }
                 }
 
-                js::UniquePtr<ParseNode> result(new_<ThisLiteral>(TokenPos(0, 0), thisName.get()));
+                UniqueNode result(new_<ThisLiteral>(TokenPos(0, 0), thisName.get()), this->nodeFree);
                 if (!result) {
                     return this->raiseOOM();
                 }
                 Unused << thisName.release();
 
                 out = Move(result);
                 break;
             }
             case BinKind::array_expression: {
-                js::UniquePtr<ParseNode> result;
+                UniqueNode result(nullptr, this->nodeFree);
                 if (!this->parseExpressionOrElisionList(reader, result)) {
                     return false;
                 }
 
                 result->setKind(PNK_ARRAY);
                 result->setOp(JSOP_NEWINIT);
                 out = Move(result);
                 break;
             }
             case BinKind::object_expression: {
-                js::UniquePtr<ParseNode> result;
+                UniqueNode result(nullptr, this->nodeFree);
                 if (!this->parseKeyValueList(reader, result)) {
                     return false;
                 }
 
                 for (ParseNode* iter = result.get(); iter != nullptr; iter = iter->pn_next) {
                     MOZ_ASSERT(iter->isKind(PNK_COLON));
                     MOZ_ASSERT(iter->pn_left != nullptr);
                     MOZ_ASSERT(iter->pn_right != nullptr);
                 }
 
                 result->setKind(PNK_OBJECT);
                 result->setOp(JSOP_NEWINIT);
                 out = Move(result);
                 break;
             }
             case BinKind::function_expression: {
-                js::UniquePtr<ParseNode> result;
+                UniqueNode result(nullptr, this->nodeFree);
                 if (!this->parseFunctionAux(reader, kind, fields, result)) {
                     return false;
                 }
                 result->setOp(JSOP_LAMBDA);
                 out = Move(result);
                 break;
             }
             case BinKind::unary_expression:
             case BinKind::update_expression: {
 
-                js::UniquePtr<ParseNode> expr;
+                UniqueNode expr(nullptr, this->nodeFree);
                 Maybe<std::string> operation;
                 Maybe<bool> prefix; // FIXME: Ignored for unary_expression?
 
                 for (auto field: fields) {
                     switch (field) {
                         case BinField::operator_:
                             if (!this->readString(reader, operation)) {
                                 return false;
@@ -1771,30 +1766,30 @@ ASTReader::parseExpressionAux(SimpleToke
                         } else {
                             pnk = PNK_POSTDECREMENT;
                         }
                     } else {
                         return this->raiseInvalidEnum("UpdateOperator", *operation);
                     }
                 }
 
-                js::UniquePtr<ParseNode> result(new_<UnaryNode>(pnk, op, TokenPos(0, 0), expr.get()));
+                UniqueNode result(new_<UnaryNode>(pnk, op, TokenPos(0, 0), expr.get()), this->nodeFree);
                 if (!result) {
                     return this->raiseOOM();
                 }
 
                 Unused << expr.release();
                 out = Move(result);
                 break;
             }
             case BinKind::binary_expression: MOZ_FALLTHROUGH;
             case BinKind::logical_expression: {
 
-                js::UniquePtr<ParseNode> left;
-                js::UniquePtr<ParseNode> right;
+                UniqueNode left(nullptr, this->nodeFree);
+                UniqueNode right(nullptr, this->nodeFree);
                 Maybe<std::string> operation;
                 for (auto field: fields) {
                     switch (field) {
                         case BinField::left:
                             if (!this->parseExpression(reader, left)) {
                                 return false;
                             }
                             break;
@@ -1890,29 +1885,29 @@ ASTReader::parseExpressionAux(SimpleToke
                     op = JSOP_OR;
                 } else if (*operation == "&&" ) {
                     pnk = PNK_AND;
                     op = JSOP_AND;
                 } else {
                     return this->raiseInvalidEnum("BinaryOperator | LogicalOperator", *operation);
                 }
 
-                js::UniquePtr<ParseNode> list(new_<ListNode>(pnk, JSOP_NOP, TokenPos(0, 0)));
+                UniqueNode list(new_<ListNode>(pnk, JSOP_NOP, TokenPos(0, 0)), this->nodeFree);
                 if (!list) {
                     return this->raiseOOM();
                 }
                 list->makeEmpty();
                 list->append(left.release());
                 list->append(right.release());
                 out = Move(list);
                 break;
             }
             case BinKind::assignment_expression: {
-                js::UniquePtr<ParseNode> left;
-                js::UniquePtr<ParseNode> right;
+                UniqueNode left(nullptr, this->nodeFree);
+                UniqueNode right(nullptr, this->nodeFree);
                 Maybe<std::string> operation;
                 for (auto field: fields) {
                     switch (field) {
                         case BinField::left:
                             if (!this->parseExpression(reader, left)) {
                                 return false;
                             }
                             break;
@@ -1974,29 +1969,29 @@ ASTReader::parseExpressionAux(SimpleToke
                     op = JSOP_BITXOR;
                 } else if (*operation == "&=") {
                     pnk = PNK_BITANDASSIGN;
                     op = JSOP_BITAND;
                 } else {
                     return this->raiseInvalidEnum("AssignmentOperator", *operation);
                 }
 
-                js::UniquePtr<ParseNode> list(new_<BinaryNode>(pnk, JSOP_NOP, TokenPos(0, 0), left.get(), right.get()));
+                UniqueNode list(new_<BinaryNode>(pnk, JSOP_NOP, TokenPos(0, 0), left.get(), right.get()), this->nodeFree);
                 if (!list) {
                     return this->raiseOOM();
                 }
                 Unused << left.release();
                 Unused << right.release();
 
                 out = Move(list);
                 break;
             }
             case BinKind::member_expression: {
-                js::UniquePtr<ParseNode> object;
-                js::UniquePtr<ParseNode> property;
+                UniqueNode object(nullptr, this->nodeFree);
+                UniqueNode property(nullptr, this->nodeFree);
                 Maybe<bool> isComputed;
 
                 for (auto field: fields) {
                     switch (field) {
                         case BinField::object:
                             if (!this->parseExpression(reader, object)) {
                                 return false;
                             }
@@ -2015,40 +2010,40 @@ ASTReader::parseExpressionAux(SimpleToke
                             return this->raiseInvalidField("MemberExpression", field);
                     }
                 }
 
                 if (!object || !property || !isComputed) {
                     return this->raiseMissingField("MemberExpression");
                 }
 
-                js::UniquePtr<ParseNode> result;
+                UniqueNode result(nullptr, this->nodeFree);
                 if (*isComputed) {
                     if (!property->isKind(PNK_NAME)) {
                         return this->raiseError("MemberExpression (computed implies name)");
                     }
                     PropertyName* name = property->pn_atom->asPropertyName();
-                    result = js::UniquePtr<ParseNode>(new_<PropertyAccess>(object.get(), name, 0, 0));
+                    result = UniqueNode(new_<PropertyAccess>(object.get(), name, 0, 0), this->nodeFree);
                 } else {
-                    result = js::UniquePtr<ParseNode>(new_<PropertyByValue>(object.get(), property.get(), 0, 0));
+                    result = UniqueNode(new_<PropertyByValue>(object.get(), property.get(), 0, 0), this->nodeFree);
                 }
                 if (!result) {
                     return this->raiseOOM();
                 }
                 out = Move(result);
 
 
                 Unused << object.release();
                 Unused << property.release();
                 break;
             }
             case BinKind::conditional_expression: {
-                js::UniquePtr<ParseNode> test;
-                js::UniquePtr<ParseNode> alternate;
-                js::UniquePtr<ParseNode> consequent;
+                UniqueNode test(nullptr, this->nodeFree);
+                UniqueNode alternate(nullptr, this->nodeFree);
+                UniqueNode consequent(nullptr, this->nodeFree);
 
                 for (auto field: fields) {
                     switch (field) {
                         case BinField::test:
                             if (!this->parseExpression(reader, test)) {
                                 return false;
                             }
                             break;
@@ -2066,32 +2061,32 @@ ASTReader::parseExpressionAux(SimpleToke
                             return this->raiseInvalidField("ConditionalExpression", field);
                     }
                 }
 
                 if (!test || !alternate || !consequent) {
                     return this->raiseMissingField("ConditionalExpression");
                 }
 
-                js::UniquePtr<ParseNode> result(new_<ConditionalExpression>(test.get(), consequent.get(), alternate.get()));
+                UniqueNode result(new_<ConditionalExpression>(test.get(), consequent.get(), alternate.get()), this->nodeFree);
                 if (!result) {
                     return this->raiseOOM();
                 }
 
                 Unused << test.release();
                 Unused << alternate.release();
                 Unused << consequent.release();
 
                 out = Move(result);
                 break;
             }
             case BinKind::call_expression: MOZ_FALLTHROUGH;
             case BinKind::new_expression: {
-                js::UniquePtr<ParseNode> callee;
-                js::UniquePtr<ParseNode> arguments;
+                UniqueNode callee(nullptr, this->nodeFree);
+                UniqueNode arguments(nullptr, this->nodeFree);
 
                 for (auto field: fields) {
                     switch (field) {
                         case BinField::callee:
                             if (!this->parseExpression(reader, callee)) {
                                 return false;
                             }
                             break;
@@ -2108,25 +2103,25 @@ ASTReader::parseExpressionAux(SimpleToke
                 if (!callee || !arguments) {
                     return this->raiseMissingField("NewExpression");
                 }
 
                 ParseNodeKind pnk =
                     kind == BinKind::call_expression
                     ? PNK_CALL
                     : PNK_NEW;
-                js::UniquePtr<ParseNode> result(new_<ListNode>(pnk, TokenPos(0, 0)));
+                UniqueNode result(new_<ListNode>(pnk, TokenPos(0, 0)), this->nodeFree);
                 result->initList(arguments.release());
                 result->prepend(callee.release());
 
                 out = Move(result);
                 break;
             }
             case BinKind::sequence_expression: {
-                js::UniquePtr<ParseNode> sequence;
+                UniqueNode sequence(nullptr, this->nodeFree);
 
                 for (auto field: fields) {
                     switch (field) {
                         case BinField::expressions:
                             if (!this->parseExpressionOrElisionList(reader, sequence)) {
                                 return false;
                             }
                             break;
@@ -2146,17 +2141,17 @@ ASTReader::parseExpressionAux(SimpleToke
             default:
                 return this->raiseInvalidKind("Expression", kind);
         }
 
         return true;
 }
 
 bool
-ASTReader::parseDirectiveList(SimpleTokenReader* reader, js::UniquePtr<ParseNode>& out) {
+ASTReader::parseDirectiveList(SimpleTokenReader* reader, UniqueNode& out) {
     fprintf(stderr, "ASTReader::parseDirectiveList\n");
     if (out) {
         return this->raiseAlreadyParsed("[Directive]");
     }
 
     uint32_t length;
     SimpleTokenReader sub(this->cx);
     if (!reader->readList(length, sub)) {
@@ -2172,35 +2167,35 @@ ASTReader::parseDirectiveList(SimpleToke
             return this->raiseEmpty("[Directive]");
         }
     }
 
     return true;
 }
 
 bool
-ASTReader::parseSwitchCase(SimpleTokenReader* reader, js::UniquePtr<ParseNode>& out) {
+ASTReader::parseSwitchCase(SimpleTokenReader* reader, UniqueNode& out) {
     if (out) {
         return this->raiseAlreadyParsed("SwitchCase");
     }
 
     BinKind kind;
     SimpleTokenReader::BinFields fields(this->cx);
     SimpleTokenReader sub(this->cx);
 
     if (!reader->taggedTuple(kind, fields, &sub)) {
         return this->raiseTokenError("SwitchCase");
     }
 
     if (kind != BinKind::switch_case) {
         return this->raiseInvalidKind("SwitchCase", kind);
     }
 
-    js::UniquePtr<ParseNode> test; // Optional.
-    js::UniquePtr<ParseNode> statements; // Required.
+    UniqueNode test(nullptr, this->nodeFree); // Optional.
+    UniqueNode statements(nullptr, this->nodeFree); // Required.
 
     for (auto field: fields) {
         switch (field) {
             case BinField::test:
                 if (!this->parseExpression(&sub, test)) {
                     return false;
                 }
                 break;
@@ -2214,32 +2209,32 @@ ASTReader::parseSwitchCase(SimpleTokenRe
         }
     }
 
     if (!statements) {
         return this->raiseMissingField("SwitchCase");
     }
     MOZ_ASSERT(statements->isKind(PNK_STATEMENTLIST));
 
-    js::UniquePtr<ParseNode> result;
+    UniqueNode result(nullptr, this->nodeFree);
 
-    result = js::UniquePtr<ParseNode>(new_<CaseClause>(test.get(), statements.get(), 0));
+    result = UniqueNode(new_<CaseClause>(test.get(), statements.get(), 0), this->nodeFree);
     if (!result) {
         return this->raiseOOM();
     }
 
     Unused << test.release();
     Unused << statements.release();
 
     out = Move(result);
     return true;
 }
 
 bool
-ASTReader::parseCatchClause(SimpleTokenReader* reader, js::UniquePtr<ParseNode>& out) {
+ASTReader::parseCatchClause(SimpleTokenReader* reader, UniqueNode& out) {
     if (out) {
         return this->raiseAlreadyParsed("CatchClause");
     }
 
 
     BinKind kind;
     SimpleTokenReader::BinFields fields(this->cx);
     SimpleTokenReader sub(this->cx);
@@ -2247,18 +2242,18 @@ ASTReader::parseCatchClause(SimpleTokenR
     if (!reader->taggedTuple(kind, fields, &sub)) {
         return this->raiseTokenError("CatchClause");
     }
 
     if (kind != BinKind::catch_clause) {
         return this->raiseInvalidKind("CatchClause", kind);
     }
 
-    js::UniquePtr<ParseNode> param;
-    js::UniquePtr<ParseNode> body;
+    UniqueNode param(nullptr, this->nodeFree);
+    UniqueNode body(nullptr, this->nodeFree);
 
     for (auto field: fields) {
         switch (field) {
             case BinField::param:
                 if (!this->parsePattern(&sub, param)) {
                     return false;
                 }
                 break;
@@ -2271,93 +2266,93 @@ ASTReader::parseCatchClause(SimpleTokenR
                 return this->raiseInvalidField("CatchClause", field);
         }
     }
 
     if (!param || !body) {
         return this->raiseMissingField("CatchClause");
     }
 
-    js::UniquePtr<ParseNode> catchClause(new_<TernaryNode>(PNK_CATCH, JSOP_NOP, param.get(), nullptr, body.get()));
+    UniqueNode catchClause(new_<TernaryNode>(PNK_CATCH, JSOP_NOP, param.get(), nullptr, body.get()), this->nodeFree);
     if (!catchClause) {
         return this->raiseOOM();
     }
 
     Unused << param.release();
     Unused << body.release();
 
-    js::UniquePtr<ParseNode> lexNode(new_<LexicalScopeNode>(/*FIXME: Implement*/nullptr, catchClause.get()));
+    UniqueNode lexNode(new_<LexicalScopeNode>(/*FIXME: Implement*/nullptr, catchClause.get()), this->nodeFree);
     if (!lexNode) {
         return this->raiseOOM();
     }
     Unused << catchClause.release();
 
-    js::UniquePtr<ParseNode> catchList(new_<ListNode>(PNK_CATCHLIST, TokenPos(0, 0)));// FIXME: We need a PNK_CATCHLIST of a single PNK_CATCH
+    UniqueNode catchList(new_<ListNode>(PNK_CATCHLIST, TokenPos(0, 0)), this->nodeFree);// FIXME: We need a PNK_CATCHLIST of a single PNK_CATCH
     if (!catchList) {
         return this->raiseOOM();
     }
 
     catchList->append(catchClause.release());
 
     out = Move(catchList);
     return true;
 }
 
 bool
-ASTReader::parsePatternList(SimpleTokenReader* reader, js::UniquePtr<ParseNode>& out) {
+ASTReader::parsePatternList(SimpleTokenReader* reader, UniqueNode& out) {
     if (out) {
         return this->raiseAlreadyParsed("[Pattern]");
     }
 
-    js::UniquePtr<ParseNode> result(this->new_<ListNode>(PNK_PARAMSBODY,
-        TokenPos(0, 0)));
+    UniqueNode result(this->new_<ListNode>(PNK_PARAMSBODY,
+        TokenPos(0, 0)), this->nodeFree);
 
     uint32_t length;
     SimpleTokenReader sub(this->cx);
 
     if (!reader->readList(length, sub)) {
         return this->raiseTokenError("[Pattern]");
     }
 
     for (uint32_t i = 0; i < length; ++i) {
-        js::UniquePtr<ParseNode> pattern;
+        UniqueNode pattern(nullptr, this->nodeFree);
         if (!this->parsePattern(&sub, pattern)) {
             return false;
         }
 
         if (!pattern) {
             return this->raiseEmpty("[Pattern]");
         }
 
         result->append(pattern.release());
     }
 
     out = Move(result);
     return true;
 }
 
 bool
-ASTReader::parsePattern(SimpleTokenReader* reader, js::UniquePtr<ParseNode>& out) {
+ASTReader::parsePattern(SimpleTokenReader* reader, UniqueNode& out) {
     if (out) {
         return this->raiseAlreadyParsed("Pattern");
     }
 
     BinKind kind;
     SimpleTokenReader::BinFields fields(this->cx);
     SimpleTokenReader sub(this->cx);
 
     if (!reader->taggedTuple(kind, fields, &sub)) {
         return this->raiseTokenError("Pattern");
     }
 
     return this->parsePatternAux(&sub, kind, fields, out);
 }
 
 bool
-ASTReader::parsePatternAux(SimpleTokenReader* reader, const BinKind kind, const SimpleTokenReader::BinFields& fields, js::UniquePtr<ParseNode>& out) {
+ASTReader::parsePatternAux(SimpleTokenReader* reader, const BinKind kind, const SimpleTokenReader::BinFields& fields, UniqueNode& out) {
     if (out) {
         return this->raiseAlreadyParsed("Pattern");
     }
 
     switch (kind) {
         case BinKind::identifier: {
             RootedAtom id(cx);
             for (auto field: fields) {
@@ -2371,17 +2366,17 @@ ASTReader::parsePatternAux(SimpleTokenRe
                         return this->raiseInvalidField("Identifier as Pattern", field);
                 }
             }
 
             if (!id) {
                 return this->raiseMissingField("Identifier as Pattern");
             }
 
-            js::UniquePtr<ParseNode> result(new_<NameNode>(PNK_NAME, JSOP_GETNAME, id, TokenPos(0, 0)));
+            UniqueNode result(new_<NameNode>(PNK_NAME, JSOP_GETNAME, id, TokenPos(0, 0)), this->nodeFree);
             if (!result) {
                 return this->raiseOOM();
             }
 
             out = Move(result);
             return true;
             break;
         }
@@ -2389,33 +2384,33 @@ ASTReader::parsePatternAux(SimpleTokenRe
             // For the moment, we only support identifiers.
             return this->raiseInvalidKind("Pattern", kind);
     }
 
     return true;
 }
 
 bool
-ASTReader::parseKeyValue(SimpleTokenReader* reader, js::UniquePtr<ParseNode>& out) {
+ASTReader::parseKeyValue(SimpleTokenReader* reader, UniqueNode& out) {
     if (out) {
         return this->raiseAlreadyParsed("ObjectMember");
     }
 
     BinKind kind;
     SimpleTokenReader::BinFields fields(this->cx);
     SimpleTokenReader sub(this->cx);
 
     if (!reader->taggedTuple(kind, fields, &sub)) {
         return this->raiseTokenError("ObjectMember");
     }
 
     switch (kind) {
         case BinKind::object_property: {
-            js::UniquePtr<ParseNode> key;
-            js::UniquePtr<ParseNode> value;
+            UniqueNode key(nullptr, this->nodeFree);
+            UniqueNode value(nullptr, this->nodeFree);
             Maybe<bool> computed;
             for (auto field: fields) {
                 switch (field) {
                     case BinField::key:
                         if (!this->parseExpression(&sub, key)) {
                             return false;
                         }
                         break;
@@ -2437,29 +2432,29 @@ ASTReader::parseKeyValue(SimpleTokenRead
             if (!key || !computed || !value) {
                 return this->raiseMissingField("ObjectMember");
             }
             if (!(key->isKind(PNK_NUMBER) || key->isKind(PNK_OBJECT_PROPERTY_NAME)
                 || key->isKind(PNK_STRING) || key->isKind(PNK_COMPUTED_NAME))) {
                 return this->raiseError("ObjectMember key kind");
             }
 
-            js::UniquePtr<ParseNode> result(this->new_<BinaryNode>(PNK_COLON, JSOP_INITPROP, key.get(), value.get()));
+            UniqueNode result(this->new_<BinaryNode>(PNK_COLON, JSOP_INITPROP, key.get(), value.get()), this->nodeFree);
             if (!result) {
                 return this->raiseOOM();
             }
 
             Unused << key.release();
             Unused << value.release();
 
             out = Move(result);
             return true;
         }
         case BinKind::object_method: {
-            js::UniquePtr<ParseNode> result;
+            UniqueNode result(nullptr, this->nodeFree);
             if (!this->parseFunctionAux(&sub, kind, fields, result)) {
                 return false;
             }
             if (!result) {
                 return this->raiseEmpty("ObjectMethod");
             }
 
             MOZ_ASSERT(result->isKind(PNK_COLON));
@@ -2468,27 +2463,27 @@ ASTReader::parseKeyValue(SimpleTokenRead
             return true;
         }
         default:
             return this->raiseInvalidKind("ObjectMember", kind);
     }
 }
 
 bool
-ASTReader::parseKeyValueList(SimpleTokenReader* reader, js::UniquePtr<ParseNode>& out) {
+ASTReader::parseKeyValueList(SimpleTokenReader* reader, UniqueNode& out) {
     uint32_t length;
     SimpleTokenReader sub(this->cx);
 
     if (!reader->readList(length, sub)) {
         return this->raiseTokenError("[ObjectMember]");
     }
 
-    js::UniquePtr<ParseNode> result(this->new_<ListNode>(PNK_LIMIT, TokenPos(0, 0)));
+    UniqueNode result(this->new_<ListNode>(PNK_LIMIT, TokenPos(0, 0)), this->nodeFree);
     for (uint32_t i = 0; i < length; ++i) {
-        js::UniquePtr<ParseNode> keyValue;
+        UniqueNode keyValue(nullptr, this->nodeFree);
         if (!this->parseKeyValue(&sub, keyValue)) {
             return false;
         }
         if (!keyValue) {
             return this->raiseEmpty("[ObjectMember]");
         }
 
         result->append(keyValue.release());
--- a/js/src/frontend/BinSource.h
+++ b/js/src/frontend/BinSource.h
@@ -8,29 +8,46 @@
 
 #include "frontend/BinTokenReader.h"
 #include "frontend/ParseNode.h"
 #include "frontend/SharedContext.h"
 
 namespace js {
 namespace frontend {
 
+class ParseNodeDeleter {
+public:
+    void operator()(ParseNode* ptr) {
+        MOZ_ASSERT(ptr);
+        alloc->freeTree(ptr);
+    }
+    ParseNodeDeleter(ParseNodeAllocator& alloc_)
+        : alloc(&alloc_)
+    { }
+    ParseNodeDeleter(const ParseNodeDeleter& deleter)
+        : alloc(deleter.alloc)
+    { }
+    void operator=(const ParseNodeDeleter&& other)
+    {
+        alloc = Move(other.alloc);
+    }
+protected:
+    ParseNodeAllocator* alloc; // Non-owned.
+};
+
 struct ScopeData MOZ_STACK_CLASS {
     using NameBag = JS::GCHashSet<JSString*>;
     using Names = JS::GCVector<JSString*, 8>;
 public:
     ScopeData(JSContext* cx)
       : letNames(cx)
       , constNames(cx)
       , varNames(cx)
       , capturedNames(cx)
     { }
-    bool init() {
-        return capturedNames.get()->init();
-    }
     bool isSome() const {
         if (hasDirectEval.isSome()) {
             MOZ_ASSERT(letNames.get().isSome());
             MOZ_ASSERT(constNames.get().isSome());
             MOZ_ASSERT(varNames.get().isSome());
             MOZ_ASSERT(capturedNames.get().isSome());
             return true;
         } else {
@@ -47,77 +64,80 @@ public:
     JS::Rooted<mozilla::Maybe<Names>> varNames;
     JS::Rooted<mozilla::Maybe<NameBag>> capturedNames;
 };
 
 class ASTReader: private JS::AutoGCRooter
 {
     using NameBag = JS::GCHashSet<JSString*>;
     using Names = JS::GCVector<JSString*, 8>;
+    using UniqueNode = UniquePtr<ParseNode, ParseNodeDeleter>;
+
 public:
     ASTReader(JSContext* cx_, LifoAlloc& alloc_, const JS::ReadOnlyCompileOptions& options)
         : AutoGCRooter(cx_, BINPARSER)
         , traceListHead(nullptr)
-        , allocator(cx_, alloc_)
         , options_(options)
         , cx(cx_)
         , alloc(alloc_)
+        , nodeAlloc(cx_, alloc_)
+        , nodeFree(nodeAlloc)
         , currentDirectives_(/*strict*/false)
     { }
 
-    bool parse(char* start, char* stop, js::UniquePtr<ParseNode>& out);
+    bool parse(char* start, char* stop, ParseNode** out);
 private:
     bool raiseTokenError(const std::string& kind);
     bool raiseInvalidKind(const std::string& superKind, const BinKind kind);
     bool raiseInvalidField(const std::string& kind, const BinField field);
     bool raiseInvalidEnum(const std::string& kind, const std::string& value);
     bool raiseMissingField(const std::string& kind);
     bool raiseAlreadyParsed(const std::string& kind);
     bool raiseEmpty(const std::string& kind);
     bool raiseOOM();
     bool raiseError(const std::string& method);
 
     // --- Parse full nodes.
 
-    bool parseBlockStatement(SimpleTokenReader* reader, js::UniquePtr<ParseNode>&);
-    bool parseCatchClause(SimpleTokenReader* reader, js::UniquePtr<ParseNode>&);
-    bool parseExpression(SimpleTokenReader* reader, js::UniquePtr<ParseNode>& out);
-    bool parsePattern(SimpleTokenReader* reader, js::UniquePtr<ParseNode>& out);
-    bool parseProgram(SimpleTokenReader* reader, js::UniquePtr<ParseNode>& out);
+    bool parseBlockStatement(SimpleTokenReader* reader, UniqueNode&);
+    bool parseCatchClause(SimpleTokenReader* reader, UniqueNode&);
+    bool parseExpression(SimpleTokenReader* reader, UniqueNode& out);
+    bool parsePattern(SimpleTokenReader* reader, UniqueNode& out);
+    bool parseProgram(SimpleTokenReader* reader, UniqueNode& out);
     bool parseScope(SimpleTokenReader* reader, ScopeData& out);
-    bool parseStatement(SimpleTokenReader* reader, js::UniquePtr<ParseNode>& out);
-    bool parseStatementList(SimpleTokenReader* reader, js::UniquePtr<ParseNode>& out);
-    bool parseSwitchCase(SimpleTokenReader* reader, js::UniquePtr<ParseNode>& out);
-    bool parseVariableDeclarator(SimpleTokenReader* reader, js::UniquePtr<ParseNode>&);
-    bool parseKeyValue(SimpleTokenReader* reader, js::UniquePtr<ParseNode>& out);
+    bool parseStatement(SimpleTokenReader* reader, UniqueNode& out);
+    bool parseStatementList(SimpleTokenReader* reader, UniqueNode& out);
+    bool parseSwitchCase(SimpleTokenReader* reader, UniqueNode& out);
+    bool parseVariableDeclarator(SimpleTokenReader* reader, UniqueNode&);
+    bool parseKeyValue(SimpleTokenReader* reader, UniqueNode& out);
 
     // --- Parse lists of nodes.
 
 
     // Produces a list of expressions. Empty expressions are replaced with PNK_ELISION.
-    bool parseExpressionOrElisionList(SimpleTokenReader* reader, js::UniquePtr<ParseNode>& out);
+    bool parseExpressionOrElisionList(SimpleTokenReader* reader, UniqueNode& out);
 
     bool parseStringList(SimpleTokenReader* reader, MutableHandle<mozilla::Maybe<Names>> out);
     bool parseStringList(SimpleTokenReader* reader, MutableHandle<Names> out);
     bool parseStringSet(SimpleTokenReader* reader, MutableHandle<mozilla::Maybe<NameBag>>);
     bool parseStringSet(SimpleTokenReader* reader, MutableHandle<NameBag>);
-    bool parseSwitchCaseList(SimpleTokenReader* reader, js::UniquePtr<ParseNode>& out);
-    bool parsePatternList(SimpleTokenReader* reader, js::UniquePtr<ParseNode>&);
-    bool parseDirectiveList(SimpleTokenReader* reader, js::UniquePtr<ParseNode>&);
+    bool parseSwitchCaseList(SimpleTokenReader* reader, UniqueNode& out);
+    bool parsePatternList(SimpleTokenReader* reader, UniqueNode&);
+    bool parseDirectiveList(SimpleTokenReader* reader, UniqueNode&);
     // Returns a list of PNK_COLON.
-    bool parseKeyValueList(SimpleTokenReader* reader, js::UniquePtr<ParseNode>&);
+    bool parseKeyValueList(SimpleTokenReader* reader, UniqueNode&);
 
     // --- Parse the contents of a node whose kind has already been determined.
 
-    bool parseFunctionAux(SimpleTokenReader* reader, const BinKind kind, const SimpleTokenReader::BinFields& fields, js::UniquePtr<ParseNode>& out);
-    bool parsePatternAux(SimpleTokenReader* reader, const BinKind kind, const SimpleTokenReader::BinFields& fields, js::UniquePtr<ParseNode>& out);
-    bool parseBlockStatementAux(SimpleTokenReader* reader, const BinKind kind, const SimpleTokenReader::BinFields& fields, js::UniquePtr<ParseNode>& out);
-    bool parseExpressionStatementAux(SimpleTokenReader* reader, const BinKind kind, const SimpleTokenReader::BinFields& fields, js::UniquePtr<ParseNode>& out);
-    bool parseExpressionAux(SimpleTokenReader* reader, const BinKind kind, const SimpleTokenReader::BinFields& fields, js::UniquePtr<ParseNode>& out);
-    bool parseVariableDeclarationAux(SimpleTokenReader* reader, const BinKind kind, const SimpleTokenReader::BinFields& fields, js::UniquePtr<ParseNode>& out);
+    bool parseFunctionAux(SimpleTokenReader* reader, const BinKind kind, const SimpleTokenReader::BinFields& fields, UniqueNode& out);
+    bool parsePatternAux(SimpleTokenReader* reader, const BinKind kind, const SimpleTokenReader::BinFields& fields, UniqueNode& out);
+    bool parseBlockStatementAux(SimpleTokenReader* reader, const BinKind kind, const SimpleTokenReader::BinFields& fields, UniqueNode& out);
+    bool parseExpressionStatementAux(SimpleTokenReader* reader, const BinKind kind, const SimpleTokenReader::BinFields& fields, UniqueNode& out);
+    bool parseExpressionAux(SimpleTokenReader* reader, const BinKind kind, const SimpleTokenReader::BinFields& fields, UniqueNode& out);
+    bool parseVariableDeclarationAux(SimpleTokenReader* reader, const BinKind kind, const SimpleTokenReader::BinFields& fields, UniqueNode& out);
 
     // --- Utilities.
 
     bool readString(SimpleTokenReader* reader, mozilla::Maybe<std::string>&);
     bool readString(SimpleTokenReader* reader, mozilla::Maybe<std::u16string>&);
     bool readString(SimpleTokenReader* reader, MutableHandleString);
     bool readString(SimpleTokenReader* reader, MutableHandleAtom);
     bool readString(SimpleTokenReader* reader, MutableHandle<PropertyName*>);
@@ -141,62 +161,64 @@ private:
     /* List of objects allocated during parsing, for GC tracing. */
     ObjectBox* traceListHead;
     void trace(JSTracer* trc)
     {
         ObjectBox::TraceList(trc, this->traceListHead);
     }
 
 
-    public:
-      ObjectBox* newObjectBox(JSObject* obj)
-      {
-          MOZ_ASSERT(obj);
+public:
+    ObjectBox* newObjectBox(JSObject* obj)
+    {
+        MOZ_ASSERT(obj);
 
-          /*
-           * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
-           * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
-           * arenas containing the entries must be alive until we are done with
-           * scanning, parsing and code generation for the whole script or top-level
-           * function.
-           */
+        /*
+         * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
+         * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
+         * arenas containing the entries must be alive until we are done with
+         * scanning, parsing and code generation for the whole script or top-level
+         * function.
+         */
 
-          ObjectBox* objbox = alloc.new_<ObjectBox>(obj, traceListHead);
-          if (!objbox) {
-              ReportOutOfMemory(this->cx);
-              return nullptr;
+         ObjectBox* objbox = alloc.new_<ObjectBox>(obj, traceListHead);
+         if (!objbox) {
+             ReportOutOfMemory(this->cx);
+             return nullptr;
           }
 
           traceListHead = objbox;
 
           return objbox;
       }
 
 
-    ParseNodeAllocator allocator;
-
-    ParseNode* allocParseNode(size_t size) {
-        MOZ_ASSERT(size == sizeof(ParseNode));
-        return static_cast<ParseNode*>(allocator.allocNode());
-    }
+      ParseNode* allocParseNode(size_t size) {
+          MOZ_ASSERT(size == sizeof(ParseNode));
+          return static_cast<ParseNode*>(nodeAlloc.allocNode());
+      }
 
-    ParseNode* cloneNode(const ParseNode& other) {
-        ParseNode* node = allocParseNode(sizeof(ParseNode));
-        if (!node)
-            return nullptr;
-        mozilla::PodAssign(node, &other);
-        return node;
-    }
+      ParseNode* cloneNode(const ParseNode& other) {
+          ParseNode* node = allocParseNode(sizeof(ParseNode));
+          if (!node)
+              return nullptr;
+           mozilla::PodAssign(node, &other);
+           return node;
+       }
 
-    JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
+       JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
 
 private:
     const ReadOnlyCompileOptions& options_;
     JSContext* cx;
     LifoAlloc& alloc;
+
+    ParseNodeAllocator nodeAlloc;
+    ParseNodeDeleter nodeFree;
+
     Directives currentDirectives_;
     ThisBinding thisBinding_;
 
     // Needs access to AutoGCRooter.
     friend void TraceBinParser(JSTracer* trc, AutoGCRooter* parser);
 };
 
 } // namespace frontend
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4270,31 +4270,31 @@ BinParse(JSContext* cx, unsigned argc, V
     GetArrayBufferViewLengthAndData(obj, &buf_length, &buf_isSharedMemory, &buf_data);
     MOZ_ASSERT(buf_data);
 
     CompileOptions options(cx);
     options.setIntroductionType("js shell bin parse")
            .setFileAndLine("<ArrayBuffer>", 1);
 
     ASTReader reader(cx, cx->tempLifoAlloc(), options);
-    js::UniquePtr<ParseNode> parsed;
+    ParseNode* parsed; // FIXME: Never deallocated?
     char* start = reinterpret_cast<char*>(buf_data);
     char* end   = start + buf_length;
 
     fprintf(stderr, "DEBUG: Parse starts\n");
-    if (!reader.parse(start, end, parsed)) {
+    if (!reader.parse(start, end, &parsed)) {
         fprintf(stderr, "DEBUG: Parse failed\n");
         MOZ_CRASH();
         return false;
     }
     fprintf(stderr, "DEBUG: Parse succeeded\n");
 
 #ifdef DEBUG
     fprintf(stderr, "<DUMP BEGINS>\n");
-    DumpParseTree(parsed.get());
+    DumpParseTree(parsed);
     fputc('\n', stderr);
     fprintf(stderr, "<DUMP ENDS>\n");
 #endif
 
     args.rval().setUndefined();
     return true;
 }