js/src/frontend/BinSource.yaml
author Stars <starsmarsjupitersaturn@protonmail.com>
Fri, 06 Jul 2018 08:40:50 -0600
changeset 427703 795f30107e2a31c04163a53397c4ed693b235e25
parent 423619 4c8cb4420363029571ff80cab93b28bbe986fc72
child 428288 13ae808f48b763ae225c7a2ff6cdf32e1f8f1534
permissions -rw-r--r--
Bug 1466428 - BinSource-auto.cpp: argument name 'YYY' in comment does not match parameter name 'XXX'. Auto generated by rust generator and the yaml file, these comments were pointing to old parameters which have now been updated to have the correct generated source file. r=arai MozReview-Commit-ID: CjNIY85s5K9

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# Rules for generating BinSource-auto.cpp
cpp:
    header: |
        /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
        * vim: set ts=8 sts=4 et sw=4 tw=99:
        * This Source Code Form is subject to the terms of the Mozilla Public
        * License, v. 2.0. If a copy of the MPL was not distributed with this
        * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

        // To generate this file, see the documentation in
        // js/src/frontend/binsource/README.md.

        #include "mozilla/ArrayUtils.h"
        #include "mozilla/Casting.h"
        #include "mozilla/Maybe.h"
        #include "mozilla/Move.h"
        #include "mozilla/PodOperations.h"
        #include "mozilla/Vector.h"

        #include "frontend/BinSource-macros.h"
        #include "frontend/BinSource.h"
        #include "frontend/BinTokenReaderTester.h"
        #include "frontend/FullParseHandler.h"
        #include "frontend/Parser.h"
        #include "frontend/SharedContext.h"

        #include "vm/RegExpObject.h"

        #include "frontend/ParseContext-inl.h"
        #include "frontend/ParseNode-inl.h"

        namespace js {
        namespace frontend {

        // Compare a bunch of `uint8_t` values (as returned by the tokenizer_) with
        // a string literal (and ONLY a string literal).
        template<typename Tok, size_t N>
        bool operator==(const typename Tok::Chars& left, const char (&right)[N]) {
            return Tok::equals(left, right);
        }

    footer: |

        // Force class instantiation.
        // This ensures that the symbols are built, without having to export all our
        // code (and its baggage of #include and macros) in the header.
        template class BinASTParser<BinTokenReaderMultipart>;
        template class BinASTParser<BinTokenReaderTester>;

        } // namespace frontend
        } // namespace js



hpp:
    # Rules for generating BinSource-class.h
    class:
        header: |
            /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
            * vim: set ts=8 sts=4 et sw=4 tw=99:
            * This Source Code Form is subject to the terms of the Mozilla Public
            * License, v. 2.0. If a copy of the MPL was not distributed with this
            * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

            // To generate this file, see the documentation in
            // js/src/frontend/binsource/README.md.

            // This file is meant to be included from the declaration
            // of class `BinASTParser`. The include may be public or private.

    # Rules for generating BinToken.h
    tokens:
        kind:
            doc: |
                /**
                 * The different kinds of Binary AST nodes, as per the specifications of
                 * Binary AST.
                 *
                 * These kinds match roughly with the `ParseNodeKind` used internally.
                 *
                 * Usage:
                 *
                 * ```c++
                 * #define WITH_KIND(CPP_NAME, SPEC_NAME) ...
                 * FOR_EACH_BIN_KIND(WITH_KIND)
                 * ```
                 *
                 *
                 * (sorted by alphabetical order)
                 */
        field:
            doc: |
                /**
                 * The different fields of Binary AST nodes, as per the specifications of
                 * Binary AST.
                 *
                 * Usage:
                 *
                 * ```c++
                 * #define WITH_FIELD(CPP_NAME, SPEC_NAME) ...
                 * FOR_EACH_BIN_FIELD(WITH_FIELD)
                 * ```
                 *
                 * (sorted by alphabetical order)
                 */
        field:
            doc: |
                /**
                 * The different variants of Binary AST string enums, as per
                 * the specifications of Binary AST, as a single macro and
                 * `enum class`.
                 *
                 * Separate enum classes are also defined in BinSource-auto.h.
                 *
                 * Usage:
                 *
                 * ```c++
                 * #define WITH_VARIANT(CPP_NAME, SPEC_NAME) ...
                 * FOR_EACH_BIN_VARIANT(WITH_VARIANT)
                 * ```
                 *
                 * (sorted by alphabetical order)
                 */

        header: |
            /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
             * vim: set ts=8 sts=4 et sw=4 tw=99:
             * This Source Code Form is subject to the terms of the Mozilla Public
             * License, v. 2.0. If a copy of the MPL was not distributed with this
             * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


            // To generate this file, see the documentation in
            // js/src/frontend/binsource/README.md.

            #ifndef frontend_BinToken_h
            #define frontend_BinToken_h

            #include <stddef.h>

            /**
             * Definition of Binary AST tokens.
             *
             * In the Binary AST world, an AST is composed of nodes, where a node is
             * defined by:
             * - a Kind (see `BinKind`);
             * - a list of fields, where each field is:
             *    - a Name (see `BinField`);
             *    - a Value, which may be either a node or a primitive value.
             *
             * The mapping between Kind and list of fields is determined entirely by
             * the grammar of Binary AST. The mapping between (Kind, Name) and the
             * structure of Value is also determined entirely by the grammar of
             * Binary AST.
             *
             * As per the specifications of Binary AST, kinds may be added as the
             * language grows, but never removed. The mapping between Kind and list
             * of fields may also change to add new fields or make some fields optional,
             * but may never remove a field. Finally, the mapping between (Kind, Name)
             * and the structure of Value may be modified to add new possible values,
             * but never to remove a value.
             *
             * A Binary AST parser must be able to fail gracefully when confronted with
             * unknown Kinds or Names.
             */

            namespace js {
            namespace frontend {
        footer: |

            /**
             * Return a string describing a `BinKind`.
             */
            const char* describeBinKind(const BinKind& kind);

            /**
             * Return a string describing a `BinField`.
             */
            const char* describeBinField(const BinField& kind);

            /**
             * Return a string describing a `BinVariant`.
             */
            const char* describeBinVariant(const BinVariant& kind);

            } // namespace frontend
            } // namespace js

            #endif // frontend_BinToken_h

Arguments:
    init:
        BINJS_TRY_DECL(result, factory_.newList(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
    append:
        factory_.addList(/* list = */ result, /* kid = */ item);

ArrayExpression:
    build:
        auto result = elements;

AssertedBlockScope:
    type-ok:
        Ok
    build: |
        if (hasDirectEval && parseContext_->isFunctionBox() && !parseContext_->sc()->strict()) {
            // In non-strict mode code, direct calls to eval can
            // add variables to the call object.
            parseContext_->functionBox()->setHasExtensibleScope();
        }
        auto result = Ok();
    fields:
        capturedNames:
            block:
                replace: |
                    MOZ_TRY(parseAndUpdateCapturedNames(kind));
        hasDirectEval:
            after: |
                if (hasDirectEval) {
                    parseContext_->sc()->setHasDirectEval();
                    parseContext_->sc()->setBindingsAccessedDynamically();
                }
        lexicallyDeclaredNames:
            block:
                replace:
                    MOZ_TRY(parseAndUpdateScopeNames(*parseContext_->innermostScope(), DeclarationKind::Let));

AssertedParameterScope:
    inherits: AssertedBlockScope
    fields:
        parameterNames:
            block:
                replace: |
                    ParseContext::Statement* inStatement = parseContext_->innermostStatement();

                    // If we are in a `CatchClause`, the binding is a implicit CatchParameter
                    // and it goes into the innermost scope. Otherwise, we're in a function,
                    // so it goes in the function scope.
                    if (inStatement && inStatement->kind() == StatementKind::Catch)
                        MOZ_TRY(parseAndUpdateScopeNames(*parseContext_->innermostScope(), DeclarationKind::CatchParameter));
                    else
                        MOZ_TRY(parseAndUpdateScopeNames(parseContext_->functionScope(), DeclarationKind::PositionalFormalParameter));

AssertedVarScope:
    inherits: AssertedBlockScope
    fields:
        varDeclaredNames:
            block:
                replace:
                    MOZ_TRY(parseAndUpdateScopeNames(parseContext_->varScope(), DeclarationKind::Var));

AssignmentExpression:
    build: |
        BINJS_TRY_DECL(result, factory_.newAssignment(ParseNodeKind::Assign, binding, expression));

AssignmentTargetIdentifier:
    build: |
        if (!IsIdentifier(name))
            return raiseError("Invalid identifier");
        BINJS_TRY(usedNames_.noteUse(cx_, name, parseContext_->scriptId(), parseContext_->innermostScope()->id()));
        BINJS_TRY_DECL(result, factory_.newName(name->asPropertyName(), tokenizer_->pos(start), cx_));

BindingIdentifier:
    build: |
        if (!IsIdentifier(name))
            return raiseError("Invalid identifier");
        BINJS_TRY_DECL(result, factory_.newName(name->asPropertyName(), tokenizer_->pos(start), cx_));

BinaryExpression:
    build: |
        ParseNodeKind pnk;
        switch (operator_) {
          case BinaryOperator::Comma:
            pnk = ParseNodeKind::Comma;
            break;
          case BinaryOperator::LogicalOr:
            pnk = ParseNodeKind::Or;
            break;
          case BinaryOperator::LogicalAnd:
            pnk = ParseNodeKind::And;
            break;
          case BinaryOperator::BitOr:
            pnk = ParseNodeKind::BitOr;
            break;
          case BinaryOperator::BitXor:
            pnk = ParseNodeKind::BitXor;
            break;
          case BinaryOperator::BitAnd:
            pnk = ParseNodeKind::BitAnd;
            break;
          case BinaryOperator::Eq:
            pnk = ParseNodeKind::Eq;
            break;
          case BinaryOperator::Neq:
            pnk = ParseNodeKind::Ne;
            break;
          case BinaryOperator::StrictEq:
            pnk = ParseNodeKind::StrictEq;
            break;
          case BinaryOperator::StrictNeq:
            pnk = ParseNodeKind::StrictNe;
            break;
          case BinaryOperator::LessThan:
            pnk = ParseNodeKind::Lt;
            break;
          case BinaryOperator::LeqThan:
            pnk = ParseNodeKind::Le;
            break;
          case BinaryOperator::GreaterThan:
            pnk = ParseNodeKind::Gt;
            break;
          case BinaryOperator::GeqThan:
            pnk = ParseNodeKind::Ge;
            break;
          case BinaryOperator::In:
            pnk = ParseNodeKind::In;
            break;
          case BinaryOperator::Instanceof:
            pnk = ParseNodeKind::InstanceOf;
            break;
          case BinaryOperator::Lsh:
            pnk = ParseNodeKind::Lsh;
            break;
          case BinaryOperator::Rsh:
            pnk = ParseNodeKind::Rsh;
            break;
          case BinaryOperator::Ursh:
            pnk = ParseNodeKind::Ursh;
            break;
          case BinaryOperator::Plus:
            pnk = ParseNodeKind::Add;
            break;
          case BinaryOperator::Minus:
            pnk = ParseNodeKind::Sub;
            break;
          case BinaryOperator::Mul:
            pnk = ParseNodeKind::Star;
            break;
          case BinaryOperator::Div:
            pnk = ParseNodeKind::Div;
            break;
          case BinaryOperator::Mod:
            pnk = ParseNodeKind::Mod;
            break;
          case BinaryOperator::Pow:
            pnk = ParseNodeKind::Pow;
            break;
        }

        ParseNode* result;
        if (left->isKind(pnk) &&
            pnk != ParseNodeKind::Pow /* ParseNodeKind::Pow is not left-associative */)
        {
            // Regroup left-associative operations into lists.
            left->appendWithoutOrderAssumption(right);
            result = left;
        } else {
            BINJS_TRY_DECL(list, factory_.newList(pnk, tokenizer_->pos(start)));

            list->appendWithoutOrderAssumption(left);
            list->appendWithoutOrderAssumption(right);
            result = list;
        }

Block:
    init: |
        ParseContext::Statement stmt(parseContext_, StatementKind::Block);
        ParseContext::Scope currentScope(cx_, parseContext_, usedNames_);
        BINJS_TRY(currentScope.init(parseContext_));
    build: |
        MOZ_TRY(checkClosedVars(currentScope));
        BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, currentScope, alloc_, parseContext_));
        BINJS_TRY_DECL(result, factory_.newLexicalScope(*bindings, statements));

BreakStatement:
    fields:
        label:
            block:
                replace: |
                    RootedAtom label(cx_);
                    MOZ_TRY_VAR(label, tokenizer_->readMaybeAtom());

    build: |
        if (label) {
            if (!IsIdentifier(label))
                return raiseError("Invalid identifier");

            auto validity = parseContext_->checkBreakStatement(label->asPropertyName());

            if (validity.isErr()) {
                switch (validity.unwrapErr()) {
                case ParseContext::BreakStatementError::ToughBreak:
                    return raiseError(kind, "Not in a loop");
                case ParseContext::BreakStatementError::LabelNotFound:
                    return raiseError(kind, "Label not found");
                }
            }
        }
        BINJS_TRY_DECL(result, factory_.newBreakStatement(label ? label->asPropertyName() : nullptr, tokenizer_->pos(start)));

CallExpression:
    build: |
        auto op = JSOP_CALL;
        // Check for direct calls to `eval`.
        if (factory_.isEvalName(callee, cx_)) {
            if (!parseContext_->varScope().lookupDeclaredNameForAdd(callee->name())
             && !parseContext_->innermostScope()->lookupDeclaredNameForAdd(callee->name())) {
                // This is a direct call to `eval`.
                if (!parseContext_->sc()->hasDirectEval())
                    return raiseMissingDirectEvalInAssertedScope();

                op = parseContext_->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL;
            }
        }
        auto result = arguments;
        result->setKind(ParseNodeKind::Call);
        result->prepend(callee);
        result->setOp(op);


CatchClause:
    init: |
        ParseContext::Statement stmt(parseContext_, StatementKind::Catch);
        ParseContext::Scope currentScope(cx_, parseContext_, usedNames_);
        BINJS_TRY(currentScope.init(parseContext_));
    build: |
        BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, currentScope, alloc_, parseContext_));
        BINJS_TRY_DECL(result, factory_.newLexicalScope(*bindings, body));
        BINJS_TRY(factory_.setupCatchScope(result, binding, body));

CompoundAssignmentExpression:
    build: |
        ParseNodeKind pnk;
        switch (operator_){
          case CompoundAssignmentOperator::PlusAssign:
            pnk = ParseNodeKind::AddAssign;
            break;
          case CompoundAssignmentOperator::MinusAssign:
            pnk = ParseNodeKind::SubAssign;
            break;
          case CompoundAssignmentOperator::MulAssign:
            pnk = ParseNodeKind::MulAssign;
            break;
          case CompoundAssignmentOperator::DivAssign:
            pnk = ParseNodeKind::DivAssign;
            break;
          case CompoundAssignmentOperator::ModAssign:
            pnk = ParseNodeKind::ModAssign;
            break;
          case CompoundAssignmentOperator::PowAssign:
            pnk = ParseNodeKind::PowAssign;
            break;
          case CompoundAssignmentOperator::LshAssign:
            pnk = ParseNodeKind::LshAssign;
            break;
          case CompoundAssignmentOperator::RshAssign:
            pnk = ParseNodeKind::RshAssign;
            break;
          case CompoundAssignmentOperator::UrshAssign:
            pnk = ParseNodeKind::UrshAssign;
            break;
          case CompoundAssignmentOperator::BitOrAssign:
            pnk = ParseNodeKind::BitOrAssign;
            break;
          case CompoundAssignmentOperator::BitXorAssign:
            pnk = ParseNodeKind::BitXorAssign;
            break;
          case CompoundAssignmentOperator::BitAndAssign:
            pnk = ParseNodeKind::BitAndAssign;
            break;
        }
        BINJS_TRY_DECL(result, factory_.newAssignment(pnk, binding, expression));

ComputedMemberAssignmentTarget:
    build: |
        BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, start));

ComputedMemberExpression:
    build: |
        BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, start));

ConditionalExpression:
    build: |
        BINJS_TRY_DECL(result, factory_.newConditional(test, consequent, alternate));

ContinueStatement:
    fields:
        label:
            block:
                replace: |
                    RootedAtom label(cx_);
                    MOZ_TRY_VAR(label, tokenizer_->readMaybeAtom());

    build: |
        if (label) {
            if (!IsIdentifier(label))
                return raiseError("ContinueStatement - Label MUST be an identifier");

            auto validity = parseContext_->checkContinueStatement(label ? label->asPropertyName() : nullptr);
            if (validity.isErr()) {
                switch (validity.unwrapErr()) {
                  case ParseContext::ContinueStatementError::NotInALoop:
                    return raiseError(kind, "Not in a loop");
                  case ParseContext::ContinueStatementError::LabelNotFound:
                    return raiseError(kind, "Label not found");
                }
            }
        }

        BINJS_TRY_DECL(result, factory_.newContinueStatement(label ? label->asPropertyName() : nullptr, tokenizer_->pos(start)));

DataProperty:
    build: |
        if (!factory_.isUsableAsObjectPropertyName(name))
            return raiseError("DataProperty key kind");

        BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, expression, AccessorType::None));

Directive:
    build: |
        TokenPos pos = tokenizer_->pos(start);
        BINJS_TRY_DECL(result, factory_.newStringLiteral(rawValue, pos));

DoWhileStatement:
    init:
        ParseContext::Statement stmt(parseContext_, StatementKind::DoLoop);
    build:
        BINJS_TRY_DECL(result, factory_.newDoWhileStatement(body, test, tokenizer_->pos(start)));

EagerFunctionDeclaration:
    init: |
        const auto syntax = FunctionSyntaxKind::Statement;
    inherits: EagerFunctionExpression

EagerFunctionExpression:
    init: |
        const auto syntax = FunctionSyntaxKind::Expression;
    fields:
        parameterScope:
            before: |
                BINJS_MOZ_TRY_DECL(funbox, buildFunctionBox(
                    isGenerator ? GeneratorKind::Generator
                                : GeneratorKind::NotGenerator,
                    isAsync ? FunctionAsyncKind::AsyncFunction
                            : FunctionAsyncKind::SyncFunction,
                    syntax, name));

                // Push a new ParseContext. It will be used to parse `scope`, the arguments, the function.
                BinParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
                BINJS_TRY(funpc.init());
                parseContext_->functionScope().useAsVarScope(parseContext_);
                MOZ_ASSERT(parseContext_->isFunctionBox());

                ParseContext::Scope lexicalScope(cx_, parseContext_, usedNames_);
                BINJS_TRY(lexicalScope.init(parseContext_));
    build: |
        BINJS_TRY_DECL(lexicalScopeData, NewLexicalScopeData(cx_, lexicalScope, alloc_, parseContext_));
        BINJS_TRY_VAR(body, factory_.newLexicalScope(*lexicalScopeData, body));
        BINJS_MOZ_TRY_DECL(result, buildFunction(start, kind, name, params, body, funbox));

EagerGetter:
    fields:
        name:
            after: |
                BINJS_MOZ_TRY_DECL(funbox, buildFunctionBox(
                    GeneratorKind::NotGenerator,
                    FunctionAsyncKind::SyncFunction,
                    FunctionSyntaxKind::Getter, /* name = */ nullptr));

                // Push a new ParseContext. It will be used to parse `scope`, the arguments, the function.
                BinParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
                BINJS_TRY(funpc.init());
                parseContext_->functionScope().useAsVarScope(parseContext_);
                MOZ_ASSERT(parseContext_->isFunctionBox());

                ParseContext::Scope lexicalScope(cx_, parseContext_, usedNames_);
                BINJS_TRY(lexicalScope.init(parseContext_));
    build: |
        BINJS_TRY_DECL(params, new_<ListNode>(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
        BINJS_MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body, funbox));
        BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, AccessorType::Getter));

EagerMethod:
    init: |
        const auto syntax = FunctionSyntaxKind::Method;
    inherits: EagerFunctionExpression
    build: |
        BINJS_MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body, funbox));
        BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, AccessorType::None));

EagerSetter:
    fields:
        name:
            after: |
                BINJS_MOZ_TRY_DECL(funbox, buildFunctionBox(
                    GeneratorKind::NotGenerator,
                    FunctionAsyncKind::SyncFunction,
                    FunctionSyntaxKind::Setter, /* name = */ nullptr));

                // Push a new ParseContext. It will be used to parse `scope`, the arguments, the function.
                BinParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
                BINJS_TRY(funpc.init());
                parseContext_->functionScope().useAsVarScope(parseContext_);
                MOZ_ASSERT(parseContext_->isFunctionBox());

                ParseContext::Scope lexicalScope(cx_, parseContext_, usedNames_);
                BINJS_TRY(lexicalScope.init(parseContext_));
    build: |
        BINJS_TRY_DECL(params, new_<ListNode>(ParseNodeKind::ParamsBody, param->pn_pos));
        factory_.addList(params, param);
        BINJS_MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body, funbox));
        BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, AccessorType::Setter));

EmptyStatement:
    build:
        BINJS_TRY_DECL(result, factory_.newEmptyStatement(tokenizer_->pos(start)));

ExpressionStatement:
    build:
        BINJS_TRY_DECL(result, factory_.newExprStatement(expression, tokenizer_->offset()));

ForInOfBinding:
    init:
        AutoVariableDeclarationKind kindGuard(this);
    build: |
        // Restored by `kindGuard`.
        variableDeclarationKind_ = kind_;
        MOZ_TRY(checkBinding(binding->pn_atom->asPropertyName()));
        auto pnk =
            kind_ == VariableDeclarationKind::Let
                ? ParseNodeKind::Let
                : ParseNodeKind::Var;
        BINJS_TRY_DECL(result, factory_.newDeclarationList(pnk, tokenizer_->pos(start)));
        factory_.addList(result, binding);



ForInStatement:
    init: |
        ParseContext::Statement stmt(parseContext_, StatementKind::ForInLoop);

        // Implicit scope around the `for`, used to store `for (let x in  ...)`
        // or `for (const x in ...)`-style declarations. Detail on the
        // declaration is stored as part of `scope`.
        ParseContext::Scope scope(cx_, parseContext_, usedNames_);
        BINJS_TRY(scope.init(parseContext_));
    build: |
        BINJS_TRY_DECL(forHead, factory_.newForInOrOfHead(ParseNodeKind::ForIn, left, right, tokenizer_->pos(start)));
        BINJS_TRY_DECL(result, factory_.newForStatement(start, forHead, body, /*flags*/ 0));

        if (!scope.isEmpty()) {
            BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, scope, alloc_, parseContext_));
            BINJS_TRY_VAR(result, factory_.newLexicalScope(*bindings, result));
        }

FormalParameters:
    build: |
        auto result = items;
        if (rest) {
            BINJS_TRY_DECL(spread, factory_.newSpread(start, rest));
            factory_.addList(result, spread);
        }

ForStatement:
    init: |
        ParseContext::Statement stmt(parseContext_, StatementKind::ForLoop);

        // Implicit scope around the `for`, used to store `for (let x; ...; ...)`
        // or `for (const x; ...; ...)`-style declarations. Detail on the
        // declaration is stored as part of `BINJS_Scope`.
        ParseContext::Scope scope(cx_, parseContext_, usedNames_);
        BINJS_TRY(scope.init(parseContext_));
    build: |
        BINJS_TRY_DECL(forHead, factory_.newForHead(init, test, update, tokenizer_->pos(start)));
        BINJS_TRY_DECL(result, factory_.newForStatement(start, forHead, body, /* iflags = */ 0));

        if (!scope.isEmpty()) {
            BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, scope, alloc_, parseContext_));
            BINJS_TRY_VAR(result, factory_.newLexicalScope(*bindings, result));
        }

FunctionBody:
    build: |
        BINJS_MOZ_TRY_DECL(result, appendDirectivesToBody(/* body = */ statements, /* directives = */ directives));


IdentifierExpression:
    build: |
        if (!IsIdentifier(name))
            return raiseError("Invalid identifier");
        BINJS_TRY(usedNames_.noteUse(cx_, name, parseContext_->scriptId(), parseContext_->innermostScope()->id()));
        BINJS_TRY_DECL(result, factory_.newName(name->asPropertyName(), tokenizer_->pos(start), cx_));

IfStatement:
    build: |
        BINJS_TRY_DECL(result, factory_.newIfStatement(start, test, consequent, alternate));

LabelledStatement:
    fields:
        label:
            after: |
                if (!IsIdentifier(label))
                    return raiseError("Invalid identifier");
                ParseContext::LabelStatement stmt(parseContext_, label);
    build:
        BINJS_TRY_DECL(result, factory_.newLabeledStatement(label->asPropertyName(), body, start));

ListOfDirective:
    init:
        BINJS_TRY_DECL(result, factory_.newStatementList(tokenizer_->pos(start)));
    append:
        factory_.addStatementToList(result, item);

ListOfObjectProperty:
    init:
        BINJS_TRY_DECL(result, factory_.newObjectLiteral(start));

ListOfOptionalSpreadElementOrExpression:
    init:
        BINJS_TRY_DECL(result, factory_.newArrayLiteral(start));
    append: |
        if (item)
            factory_.addArrayElement(result, item); // Infallible.
        else
            BINJS_TRY(factory_.addElision(result, tokenizer_->pos(start)));

ListOfParameter:
    init: |
        BINJS_TRY_DECL(result, new_<ListNode>(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
    append:
        factory_.addList(/* list = */ result, /* kid = */ item);

ListOfStatement:
    init:
        BINJS_TRY_DECL(result, factory_.newStatementList(tokenizer_->pos(start)));
    append:
        factory_.addStatementToList(result, item);


#ListOfSpreadElementOrExpression:
#    init:
#        BINJS_TRY_DECL(result, new_<ListNode>(ParseNodeKind::ParamsBody, tokenizer_->pos()));
#    append:
#        result->appendWithoutOrderAssumption(item);

ListOfSwitchCase:
    init:
        BINJS_TRY_DECL(result, factory_.newStatementList(tokenizer_->pos(start)));
    append:
        factory_.addCaseStatementToList(result, item);

ListOfVariableDeclarator:
    init: |
        BINJS_TRY_DECL(result, factory_.newDeclarationList(ParseNodeKind::Const /*Placeholder*/,
            tokenizer_->pos(start)));

LiteralBooleanExpression:
    build:
        BINJS_TRY_DECL(result, factory_.newBooleanLiteral(value, tokenizer_->pos(start)));

LiteralNumericExpression:
    build:
        BINJS_TRY_DECL(result, factory_.newNumber(value, DecimalPoint::HasDecimal, tokenizer_->pos(start)));

LiteralNullExpression:
    build:
        BINJS_TRY_DECL(result, factory_.newNullLiteral(tokenizer_->pos(start)));

LiteralPropertyName:
    build: |
        ParseNode* result;
        uint32_t index;
        if (value->isIndex(&index))
            BINJS_TRY_VAR(result, factory_.newNumber(index, NoDecimal, TokenPos(start, tokenizer_->offset())));
        else
            BINJS_TRY_VAR(result, factory_.newObjectLiteralPropertyName(value, tokenizer_->pos(start)));

LiteralRegExpExpression:
    fields:
        flags:
            block:
                replace: |
                    Chars flags(cx_);
                    MOZ_TRY(tokenizer_->readChars(flags));
    build: |
        RegExpFlag reflags = NoFlags;
        for (auto c : flags) {
            if (c == 'g' && !(reflags & GlobalFlag))
                reflags = RegExpFlag(reflags | GlobalFlag);
            else if (c == 'i' && !(reflags & IgnoreCaseFlag))
                reflags = RegExpFlag(reflags | IgnoreCaseFlag);
            else if (c == 'm' && !(reflags & MultilineFlag))
                reflags = RegExpFlag(reflags | MultilineFlag);
            else if (c == 'y' && !(reflags & StickyFlag))
                reflags = RegExpFlag(reflags | StickyFlag);
            else if (c == 'u' && !(reflags & UnicodeFlag))
                reflags = RegExpFlag(reflags | UnicodeFlag);
            else
                return raiseError("Invalid regexp flags");
        }


        Rooted<RegExpObject*> reobj(cx_);
        BINJS_TRY_VAR(reobj, RegExpObject::create(cx_,
            pattern,
            reflags,
            alloc_,
            TenuredObject));

        BINJS_TRY_DECL(result, factory_.newRegExp(reobj, tokenizer_->pos(start), *this));

LiteralStringExpression:
    build:
        BINJS_TRY_DECL(result, factory_.newStringLiteral(value, tokenizer_->pos(start)));

NewExpression:
    build: |
        auto result = arguments;
        result->setKind(ParseNodeKind::New);
        result->prepend(callee);
        result->setOp(JSOP_NEW);

ObjectExpression:
    build:
        auto result = properties;

OptionalAssertedBlockScope:
    type-ok:
        Ok

OptionalAssertedVarScope:
    type-ok:
        Ok

OptionalAssertedParameterScope:
    type-ok:
        Ok

Parameter:
    sum-arms:
        BindingIdentifier:
            after: |
                if (!parseContext_->positionalFormalParameterNames().append(result->pn_atom))
                    return raiseOOM();
                if (parseContext_->isFunctionBox())
                    parseContext_->functionBox()->length++;

ReturnStatement:
    init: |
        if (!parseContext_->isFunctionBox()) {
            // Return statements are permitted only inside functions.
            return raiseInvalidKind("Toplevel Statement", kind);
        }

        parseContext_->functionBox()->usesReturn = true;
    build:
        BINJS_TRY_DECL(result, factory_.newReturnStatement(expression, tokenizer_->pos(start)));

Script:
    build:
        MOZ_TRY(checkClosedVars(parseContext_->varScope()));
        BINJS_MOZ_TRY_DECL(result, appendDirectivesToBody(/* body = */ statements, /* directives = */ directives));

Setter:
    inherits: Method
    init: |
        const auto isAsync = false;
        const auto isGenerator = false;
    build: |
        BINJS_TRY_DECL(params, factory_.newList(ParseNodeKind::ParamsBody, param));
        BINJS_MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body, funbox));
        BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, AccessorType::Setter));

ShorthandProperty:
    build: |
        if (!factory_.isUsableAsObjectPropertyName(name))
            BINJS_TRY_VAR(name, factory_.newObjectLiteralPropertyName(name->name(), tokenizer_->pos(start)));

        BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, name, AccessorType::None));

SwitchCase:
    build: |
        BINJS_TRY_DECL(result, factory_.newCaseOrDefault(start, test, consequent));

SwitchDefault:
    build: |
        BINJS_TRY_DECL(result, factory_.newCaseOrDefault(start, nullptr, consequent));

SwitchStatement:
    build: |
        BINJS_TRY_DECL(scope, factory_.newLexicalScope(nullptr, cases));
        BINJS_TRY_DECL(result, factory_.newSwitchStatement(start, discriminant, scope));

SwitchStatementWithDefault:
    build: |
        // Concatenate `preDefaultCase`, `defaultCase`, `postDefaultCase`
        auto cases = preDefaultCases;
        factory_.addList(cases, defaultCase);
        ParseNode* iter = postDefaultCases->pn_head;
        while (iter) {
            ParseNode* next = iter->pn_next;
            factory_.addList(cases, iter);
            iter = next;
        }
        BINJS_TRY_DECL(scope, factory_.newLexicalScope(nullptr, cases));
        BINJS_TRY_DECL(result, factory_.newSwitchStatement(start, discriminant, scope));

StaticMemberAssignmentTarget:
    build: |
        BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start));

StaticMemberExpression:
    build: |
        BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start));

ThisExpression:
    build: |
        if (parseContext_->isFunctionBox())
            parseContext_->functionBox()->usesThis = true;

        TokenPos pos = tokenizer_->pos(start);
        ParseNode* thisName(nullptr);
        if (parseContext_->sc()->thisBinding() == ThisBinding::Function) {
            HandlePropertyName dotThis = cx_->names().dotThis;
            BINJS_TRY(usedNames_.noteUse(cx_, dotThis, parseContext_->scriptId(), parseContext_->innermostScope()->id()));
            BINJS_TRY_VAR(thisName, factory_.newName(dotThis, pos, cx_));
        }

        BINJS_TRY_DECL(result, factory_.newThisLiteral(pos, thisName));

ThrowStatement:
    build:
        BINJS_TRY_DECL(result, factory_.newThrowStatement(expression, tokenizer_->pos(start)));

TryCatchStatement:
    fields:
        body:
            block:
                declare:
                    ParseNode* body;
                before: |
                    ParseContext::Statement stmt(parseContext_, StatementKind::Try);
                    ParseContext::Scope scope(cx_, parseContext_, usedNames_);
                    BINJS_TRY(scope.init(parseContext_));
    build:
        BINJS_TRY_DECL(result, factory_.newTryStatement(start, body, catchClause, /* finallyBlock = */ nullptr));

TryFinallyStatement:
    fields:
        body:
            block:
                declare:
                    ParseNode* body;
                before: |
                    ParseContext::Statement stmt(parseContext_, StatementKind::Try);
                    ParseContext::Scope scope(cx_, parseContext_, usedNames_);
                    BINJS_TRY(scope.init(parseContext_));
        finalizer:
            block:
                declare:
                    ParseNode* finalizer;
                before: |
                    ParseContext::Statement stmt(parseContext_, StatementKind::Finally);
                    ParseContext::Scope scope(cx_, parseContext_, usedNames_);
                    BINJS_TRY(scope.init(parseContext_));
    build:
        BINJS_TRY_DECL(result, factory_.newTryStatement(start, body, catchClause, finalizer));

UnaryExpression:
    build: |
        ParseNodeKind pnk;
        switch (operator_) {
          case UnaryOperator::Minus:
            pnk = ParseNodeKind::Neg;
            break;
          case UnaryOperator::Plus:
            pnk = ParseNodeKind::Pos;
            break;
          case UnaryOperator::Not:
            pnk = ParseNodeKind::Not;
            break;
          case UnaryOperator::BitNot:
            pnk = ParseNodeKind::BitNot;
            break;
          case UnaryOperator::Typeof: {
            if (operand->isKind(ParseNodeKind::Name))
                pnk = ParseNodeKind::TypeOfName;
            else
                pnk = ParseNodeKind::TypeOfExpr;
            break;
          }
          case UnaryOperator::Void:
            pnk = ParseNodeKind::Void;
            break;
          case UnaryOperator::Delete: {
            switch (operand->getKind()) {
              case ParseNodeKind::Name:
                operand->setOp(JSOP_DELNAME);
                pnk = ParseNodeKind::DeleteName;
                break;
              case ParseNodeKind::Dot:
                pnk = ParseNodeKind::DeleteProp;
                break;
              case ParseNodeKind::Elem:
                pnk = ParseNodeKind::DeleteElem;
                break;
              default:
                pnk = ParseNodeKind::DeleteExpr;
            }
            break;
          }
        }
        BINJS_TRY_DECL(result, factory_.newUnary(pnk, start, operand));

UpdateExpression:
    build: |
        ParseNodeKind pnk;
        switch (operator_) {
          case UpdateOperator::Incr:
            pnk = isPrefix ? ParseNodeKind::PreIncrement
                           : ParseNodeKind::PostIncrement;
            break;
          case UpdateOperator::Decr:
            pnk = isPrefix ? ParseNodeKind::PreDecrement
                           : ParseNodeKind::PostDecrement;
            break;
        }
        BINJS_TRY_DECL(result, factory_.newUnary(pnk, start, operand));

VariableDeclaration:
    init:
        AutoVariableDeclarationKind kindGuard(this);

    fields:
        kind:
            after: |
                // Restored by `kindGuard`.
                variableDeclarationKind_ = kind_;

    build: |
        // By specification, the list may not be empty.
        if (declarators->pn_count == 0)
            return raiseEmpty("VariableDeclaration");

        ParseNodeKind pnk;
        switch (kind_) {
          case VariableDeclarationKind::Var:
            pnk = ParseNodeKind::Var;
            break;
          case VariableDeclarationKind::Let:
            pnk = ParseNodeKind::Let;
            break;
          case VariableDeclarationKind::Const:
            pnk = ParseNodeKind::Const;
            break;
        }
        declarators->setKind(pnk);
        auto result = declarators;

VariableDeclarator:
    build: |
        ParseNode* result;
        if (binding->isKind(ParseNodeKind::Name)) {
            // `var foo [= bar]``
            MOZ_TRY(checkBinding(binding->pn_atom->asPropertyName()));

            BINJS_TRY_VAR(result, factory_.newName(binding->pn_atom->asPropertyName(), tokenizer_->pos(start), cx_));
            if (init)
                result->pn_expr = init;
        } else {
            // `var pattern = bar`
            if (!init) {
                // Here, `init` is required.
                return raiseMissingField("VariableDeclarator (with non-trivial pattern)", BinField::Init);
            }

            MOZ_CRASH("Unimplemented: AssertedScope check for BindingPattern variable declaration");
            BINJS_TRY_VAR(result, factory_.newAssignment(ParseNodeKind::Assign, binding, init));
        }

WhileStatement:
    init:
        ParseContext::Statement stmt(parseContext_, StatementKind::WhileLoop);
    build:
        BINJS_TRY_DECL(result, factory_.newWhileStatement(start, test, body));

WithStatement:
    build:
        BINJS_TRY_DECL(result, factory_.newWithStatement(start, object, body));