js/src/frontend/BinSource.yaml
author Tooru Fujisawa <arai_a@mac.com>
Tue, 30 Oct 2018 09:49:41 +0900
changeset 502591 a4fb56d2819e636de6c7ae756028e537efae48d0
parent 501790 c22d2d5b7af664565e888b039badd8157d80264c
child 504948 391d7669e9b28c4475ead6f5f6771dfa1fd05deb
permissions -rw-r--r--
Bug 1497784 - Drop support for lexical declaration in BinAST for now. r=Yoric

# 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/.

parser:
    class-template:
        template<typename Tok>
    class-name:
        BinASTParser<Tok>
    type-ok:
        ParseNode*
    default-value:
        nullptr
    list:
        append: |
            result->appendWithoutOrderAssumption(item);

# 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/ParseNode.h"
        #include "frontend/Parser.h"
        #include "frontend/SharedContext.h"

        #include "vm/RegExpObject.h"

        #include "frontend/ParseContext-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::Arguments, tokenizer_->pos(start)));
    append:
        factory_.addList(/* list = */ result, /* kid = */ item);

ArrayExpression:
    build: |
        if (elements->empty()) {
            elements->setHasNonConstInitializer();
        }
        auto result = elements;

AssertedBlockScope:
    type-ok:
        Ok
    init: |
        const auto scopeKind = AssertedScopeKind::Block;
    fields:
        declaredNames:
            extra-args: scopeKind
        hasDirectEval:
            after: |
                if (hasDirectEval) {
                    parseContext_->sc()->setHasDirectEval();
                    parseContext_->sc()->setBindingsAccessedDynamically();
                }
    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();

AssertedBoundName:
    type-ok:
        Ok
    extra-params: AssertedScopeKind scopeKind
    extra-args: scopeKind
    init: |
        const bool allowDuplicateName = false;
    fields:
        isCaptured:
             after: |
                ParseContext::Scope* scope;
                DeclarationKind declKind;
                MOZ_TRY(getBoundScope(scopeKind, scope, declKind));
    build: |
        MOZ_TRY(addScopeName(scopeKind, name, scope, declKind, isCaptured, allowDuplicateName));
        auto result = Ok();

AssertedDeclaredName:
    inherits: AssertedBoundName
    fields:
        isCaptured:
            after: |
                ParseContext::Scope* scope;
                DeclarationKind declKind;
                MOZ_TRY(getDeclaredScope(scopeKind, kind_, scope, declKind));

AssertedMaybePositionalParameterName:
    inherits: AssertedBoundName
    extra-params: |
        AssertedScopeKind scopeKind,
        MutableHandle<GCVector<JSAtom*>> positionalParams
    extra-args: |
        scopeKind, positionalParams
    sum-arms:
        AssertedRestParameterName:
            disabled: true
        AssertedParameterName:
            disabled: true

AssertedPositionalParameterName:
    inherits: AssertedMaybePositionalParameterName
    init: |
        bool allowDuplicateName = !parseContext_->sc()->strict();
    fields:
        name:
            after: |
                // `positionalParams` vector can be shorter than the actual
                // parameter length. Resize on demand.
                // (see also ListOfAssertedMaybePositionalParameterName)
                size_t prevLength = positionalParams.get().length();
                if (index >= prevLength) {
                    // This is implementation limit, which is not in the spec.
                    if (index >= ARGNO_LIMIT - 1) {
                        return raiseError("AssertedPositionalParameterName.index is too big");
                    }
                    size_t newLength = index + 1;
                    BINJS_TRY(positionalParams.get().resize(newLength));
                    for (uint32_t i = prevLength; i < newLength; i++) {
                        positionalParams.get()[i] = nullptr;
                    }
                }

                if (positionalParams.get()[index]) {
                    return raiseError("AssertedPositionalParameterName has duplicate entry for the same index");
                }
                positionalParams.get()[index] = name;

AssertedBoundNamesScope:
    inherits: AssertedBlockScope
    init: |
        const auto scopeKind = AssertedScopeKind::Catch;
    fields:
        boundNames:
            extra-args: scopeKind

AssertedParameterScope:
    inherits: AssertedBlockScope
    extra-params: |
        MutableHandle<GCVector<JSAtom*>> positionalParams
    extra-args: |
        positionalParams
    init: |
        const auto scopeKind = AssertedScopeKind::Parameter;
    fields:
        isSimpleParameterList:
            after: |
                (void) isSimpleParameterList;
        paramNames:
            extra-args: scopeKind, positionalParams

AssertedScriptGlobalScope:
    inherits: AssertedBlockScope
    init: |
        const auto scopeKind = AssertedScopeKind::Global;

AssertedVarScope:
    inherits: AssertedBlockScope
    init: |
        const auto scopeKind = AssertedScopeKind::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->template as<ListNode>().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;

        // Try to optimize funcall and funapply at the bytecode level
        if (PropertyName* prop = factory_.maybeDottedProperty(callee)) {
            if (prop == cx_->names().apply) {
                op = JSOP_FUNAPPLY;
                if (parseContext_->isFunctionBox())
                    parseContext_->functionBox()->usesApply = true;
            } else if (prop == cx_->names().call) {
                op = JSOP_FUNCALL;
            }
        }

        // Check for direct calls to `eval`.
        if (factory_.isEvalName(callee, cx_)) {
            if (!parseContext_->varScope().lookupDeclaredNameForAdd(cx_->names().eval)
             && !parseContext_->innermostScope()->lookupDeclaredNameForAdd(cx_->names().eval))
            {
                // This is a direct call to `eval`.
                if (!parseContext_->sc()->hasDirectEval()) {
                    return raiseMissingDirectEvalInAssertedScope();
                }

                op = parseContext_->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL;
            }
        }

        BINJS_TRY_DECL(result, factory_.newCall(callee, arguments));
        result->setOp(op);


CatchClause:
    type-ok:
        LexicalScopeNode*
    init: |
        ParseContext::Statement stmt(parseContext_, StatementKind::Catch);
        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, 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, tokenizer_->offset()));

ComputedMemberExpression:
    build: |
        BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, tokenizer_->offset()));

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");
        }

        ParseNode* result;
        if (name->template is<NameNode>() && name->template as<NameNode>().atom() == cx_->names().proto) {
            BINJS_TRY_VAR(result, factory_.newUnary(ParseNodeKind::MutateProto, start, expression));
        } else {
            BINJS_TRY_VAR(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

LazyFunctionDeclaration:
    init: |
        const auto syntax = FunctionSyntaxKind::Statement;
    inherits: LazyFunctionExpression

FunctionExpressionContents:
    type-ok:
        Ok
    extra-params: |
        uint32_t funLength,
        ListNode** paramsOut,
        ListNode** bodyOut
    extra-args: |
        funLength, paramsOut, bodyOut
    fields:
        isThisCaptured:
            after: |
                // TODO: Use this in BinASTParser::buildFunction.
                (void) isThisCaptured;
        isFunctionNameCaptured:
            after: |
                // Per spec, isFunctionNameCaptured can be true for anonymous
                // function.  Check isFunctionNameCaptured only for named
                // function.
                if (parseContext_->functionBox()->function()->isNamedLambda() &&
                    isFunctionNameCaptured)
                {
                    captureFunctionName();
                }
        parameterScope:
            before: |
                Rooted<GCVector<JSAtom*>> positionalParams(cx_, GCVector<JSAtom*>(cx_));
            extra-args: |
                &positionalParams
        params:
            after: |
                MOZ_TRY(checkFunctionLength(funLength));
                MOZ_TRY(checkPositionalParameterIndices(positionalParams, params));
    build: |
        *paramsOut = params;
        *bodyOut = body;
        auto result = Ok();

FunctionOrMethodContents:
    inherits: FunctionExpressionContents

GetterContents:
    inherits: FunctionExpressionContents
    fields:
        body:
            before: |
                BINJS_TRY_DECL(params, new_<ListNode>(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));

SetterContents:
    inherits: FunctionExpressionContents
    fields:
        param:
            after: |
              BINJS_TRY_DECL(params, new_<ListNode>(ParseNodeKind::ParamsBody, param->pn_pos));
              factory_.addList(params, param);
              MOZ_TRY(checkPositionalParameterIndices(positionalParams, params));

EagerFunctionExpression:
    init: |
        const auto syntax = FunctionSyntaxKind::Expression;
    fields:
        isGenerator:
            after: |
                if (isGenerator) {
                    return raiseError("Generator is not supported in this preview release");
                }
        isAsync:
            after: |
                if (isAsync) {
                    return raiseError("Async function is not supported in this preview release");
                }
        contents:
            before: |
                BINJS_MOZ_TRY_DECL(funbox, buildFunctionBox(
                    isGenerator ? GeneratorKind::Generator
                                : GeneratorKind::NotGenerator,
                    isAsync ? FunctionAsyncKind::AsyncFunction
                            : FunctionAsyncKind::SyncFunction,
                    syntax,
                    (syntax != FunctionSyntaxKind::Setter &&
                     syntax != FunctionSyntaxKind::Getter) ? name : nullptr));

                forceStrictIfNecessary(funbox, directives);

                // 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_));
                ListNode* params;
                ListNode* body;
            extra-args: |
                length, &params, &body
            after: |
                MOZ_TRY(prependDirectivesToBody(body, directives));
    build: |
        BINJS_TRY_DECL(lexicalScopeData, NewLexicalScopeData(cx_, lexicalScope, alloc_, parseContext_));
        BINJS_TRY_DECL(bodyScope, factory_.newLexicalScope(*lexicalScopeData, body));
        BINJS_MOZ_TRY_DECL(result, buildFunction(start, kind, name, params, bodyScope, funbox));

LazyFunctionExpression:
    init: |
        const auto syntax = FunctionSyntaxKind::Expression;
    fields:
        isGenerator:
            after: |
                if (isGenerator) {
                    return raiseError("Generator is not supported in this preview release");
                }
        isAsync:
            after: |
                if (isAsync) {
                    return raiseError("Async function is not supported in this preview release");
                }
        contents:
            block:
                replace:
                    // Don't parse the contents until we delazify.
    build: |
        BINJS_MOZ_TRY_DECL(funbox, buildFunctionBox(
            isGenerator ? GeneratorKind::Generator
                        : GeneratorKind::NotGenerator,
            isAsync ? FunctionAsyncKind::AsyncFunction
                    : FunctionAsyncKind::SyncFunction,
            syntax, name));

        forceStrictIfNecessary(funbox, directives);

        RootedFunction fun(cx_, funbox->function());

        // TODO: This will become incorrect in the face of ES6 features.
        fun->setArgCount(length);

        auto skipStart = contentsSkip.startOffset();
        BINJS_TRY_DECL(lazy, LazyScript::Create(cx_, fun, sourceObject_, parseContext_->closedOverBindingsForLazy(), parseContext_->innerFunctionsForLazy,
                                                skipStart, skipStart + contentsSkip.length(),
                                                skipStart, 0, skipStart, ParseGoal::Script));

        if (funbox->strict()) {
            lazy->setStrict();
        }
        lazy->setIsBinAST();
        funbox->function()->initLazyScript(lazy);

        BINJS_MOZ_TRY_DECL(result, makeEmptyFunctionNode(skipStart, kind, funbox));

EagerGetter:
    init: |
        const auto syntax = FunctionSyntaxKind::Setter;
        const bool isGenerator = false;
        const bool isAsync = false;
        const auto accessorType = AccessorType::Getter;
        const uint32_t length = 0;
    inherits: EagerMethod

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

EagerSetter:
    init: |
        const auto syntax = FunctionSyntaxKind::Setter;
        const bool isGenerator = false;
        const bool isAsync = false;
        const auto accessorType = AccessorType::Setter;
    inherits: EagerMethod

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->template as<NameNode>().atom()->asPropertyName()));
        ParseNodeKind pnk;
        switch (kind_) {
          case VariableDeclarationKind::Var:
            pnk = ParseNodeKind::Var;
            break;
          case VariableDeclarationKind::Let:
            return raiseError("Let is not supported in this preview release");
          case VariableDeclarationKind::Const:
            return raiseError("Const is not supported in this preview release");
        }
        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)));
        ParseNode* result;
        BINJS_TRY_VAR(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:
    type-ok:
        ListNode*
    build: |
        auto result = items;
        if (rest) {
            return raiseError("Rest parameter is not supported in this preview release");
        }

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)));
        ParseNode* result;
        BINJS_TRY_VAR(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:
    inherits: ListOfStatement

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));

ListOfAssertedBoundName:
    extra-params: AssertedScopeKind scopeKind
    extra-args: scopeKind
    type-ok:
        Ok
    init: |
        (void) start;
        auto result = Ok();
    append: |
        // Nothing to do here.

ListOfAssertedMaybePositionalParameterName:
    inherits: ListOfAssertedBoundName
    extra-params: |
        AssertedScopeKind scopeKind,
        MutableHandle<GCVector<JSAtom*>> positionalParams
    extra-args: |
        scopeKind, positionalParams
    init: |
        (void) start;
        auto result = Ok();
        // This list contains also destructuring parameters, and the number of
        // list items can be greater than the actual parameters, or more than
        // ARGNO_LIMIT even if the number of parameters fits into ARGNO_LIMIT.
        // Also, the number of parameters can be greater than this list's length
        // if one of destructuring parameter is empty.
        //
        // We resize `positionalParams` vector on demand, to keep the vector
        // length match to the known maximum positional parameter index + 1.

ListOfAssertedDeclaredName:
    inherits: ListOfAssertedBoundName

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

ListOfObjectProperty:
    type-ok:
        ListNode*
    init:
        BINJS_TRY_DECL(result, factory_.newObjectLiteral(start));
    append: |
        if (!item->isConstant())
            result->setHasNonConstInitializer();
        result->appendWithoutOrderAssumption(item);

ListOfOptionalSpreadElementOrExpression:
    type-ok:
        ListNode*
    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:
    type-ok:
        ListNode*
    init: |
        BINJS_TRY_DECL(result, new_<ListNode>(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
    append:
        factory_.addList(/* list = */ result, /* kid = */ item);

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


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

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

ListOfVariableDeclarator:
    type-ok:
        ListNode*
    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,
            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: |
        BINJS_TRY_DECL(result, factory_.newNewExpression(tokenizer_->pos(start).begin, callee, arguments));

ObjectExpression:
    build:
        auto result = properties;

OptionalCatchClause:
    type-ok:
        LexicalScopeNode*

Parameter:
    sum-arms:
        BindingIdentifier:
            after: |
                if (!parseContext_->positionalFormalParameterNames().append(result->template as<NameNode>().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:
    fields:
        directives:
            after: |
                forceStrictIfNecessary(parseContext_->sc(), directives);
    build:
        MOZ_TRY(checkClosedVars(parseContext_->varScope()));
        MOZ_TRY(prependDirectivesToBody(/* body = */ statements, directives));
        auto result = statements;

ShorthandProperty:
    build: |
        MOZ_ASSERT(name->isKind(ParseNodeKind::Name));
        MOZ_ASSERT(!factory_.isUsableAsObjectPropertyName(name));
        BINJS_TRY_DECL(propName, factory_.newObjectLiteralPropertyName(name->template as<NameNode>().name(), tokenizer_->pos(start)));

        BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(propName, name, AccessorType::None));
        result->setKind(ParseNodeKind::Shorthand);

SwitchCase:
    type-ok:
        CaseClause*
    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, false));

SwitchStatementWithDefault:
    build: |
        // Concatenate `preDefaultCase`, `defaultCase`, `postDefaultCase`
        auto cases = preDefaultCases;
        factory_.addList(cases, defaultCase);
        ParseNode* iter = postDefaultCases->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, true));

StaticMemberAssignmentTarget:
    init:
        size_t nameStart;
    fields:
        property:
            block:
                before: |
                    nameStart = tokenizer_->offset();
    build: |
        BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), tokenizer_->pos(nameStart)));
        BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name));

StaticMemberExpression:
    init:
        size_t nameStart;
    fields:
        property:
            block:
                before: |
                    nameStart = tokenizer_->offset();
    build: |
        BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), tokenizer_->pos(nameStart)));
        BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name));

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->empty()) {
            return raiseEmpty("VariableDeclaration");
        }

        ParseNodeKind pnk;
        switch (kind_) {
          case VariableDeclarationKind::Var:
            pnk = ParseNodeKind::Var;
            break;
          case VariableDeclarationKind::Let:
            return raiseError("Let is not supported in this preview release");
          case VariableDeclarationKind::Const:
            return raiseError("Const is not supported in this preview release");
        }
        declarators->setKind(pnk);
        auto result = declarators;

VariableDeclarator:
    build: |
        ParseNode* result;
        if (binding->isKind(ParseNodeKind::Name)) {
            // `var foo [= bar]``
            NameNode* bindingNameNode = &binding->template as<NameNode>();
            MOZ_TRY(checkBinding(bindingNameNode->atom()->asPropertyName()));
            result = bindingNameNode;
            if (init) {
                BINJS_TRY(factory_.finishInitializerAssignment(bindingNameNode, 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:
    fields:
        body:
            before: |
                ParseContext::Statement stmt(parseContext_, StatementKind::With);
    build: |
        parseContext_->sc()->setBindingsAccessedDynamically();
        BINJS_TRY_DECL(result, factory_.newWithStatement(start, object, body));