diff --git a/benchmark/babel-core/real-case-preset-env-flow/babel-parser-expression.mjs b/benchmark/babel-core/real-case-preset-env-flow/babel-parser-expression.mjs new file mode 100644 index 000000000000..f97fa88c50ca --- /dev/null +++ b/benchmark/babel-core/real-case-preset-env-flow/babel-parser-expression.mjs @@ -0,0 +1,42 @@ +import Benchmark from "benchmark"; +import baseline from "@babel-baseline/core"; +import current from "@babel/core"; +import parser from "@babel-baseline/parser"; +import { report } from "../../util.mjs"; +import { readFileSync } from "fs"; + +const suite = new Benchmark.Suite(); +const fixtureName = "babel-parser-expression.txt"; + +function createInput() { + return parser.parse( + readFileSync(new URL("./" + fixtureName, import.meta.url), { + encoding: "utf-8", + }), + { sourceType: "module", plugins: ["flow"] } + ); +} +function benchCases(name, implementation, options) { + const input = createInput(); + suite.add( + `${name} ${fixtureName}`, + () => { + implementation(input, { + plugins: ["@babel/preset-env", "@babel/preset-flow"], + targets: "ie 11", + configFile: false, + babelrc: false, + ...options, + }); + }, + { + // increase minSamples for accuracy + minSamples: 100, + } + ); +} + +benchCases("baseline", baseline.transformFromAstSync, {}); +benchCases("current", current.transformFromAstSync, {}); + +suite.on("cycle", report).run(); diff --git a/benchmark/babel-core/real-case-preset-env-flow/babel-parser-expression.txt b/benchmark/babel-core/real-case-preset-env-flow/babel-parser-expression.txt new file mode 100644 index 000000000000..a46f3e236c54 --- /dev/null +++ b/benchmark/babel-core/real-case-preset-env-flow/babel-parser-expression.txt @@ -0,0 +1,2938 @@ +// @flow + +// A recursive descent parser operates by defining functions for all +// syntactic elements, and recursively calling those, each function +// advancing the input stream and returning an AST node. Precedence +// of constructs (for example, the fact that `!x[1]` means `!(x[1])` +// instead of `(!x)[1]` is handled by the fact that the parser +// function that parses unary prefix operators is called first, and +// in turn calls the function that parses `[]` subscripts — that +// way, it'll receive the node for `x[1]` already parsed, and wraps +// *that* in the unary operator node. +// +// Acorn uses an [operator precedence parser][opp] to handle binary +// operator precedence, because it is much more compact than using +// the technique outlined above, which uses different, nesting +// functions to specify precedence, for all of the ten binary +// precedence levels that JavaScript defines. +// +// [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser + +import { + tokenCanStartExpression, + tokenIsAssignment, + tokenIsIdentifier, + tokenIsKeyword, + tokenIsKeywordOrIdentifier, + tokenIsOperator, + tokenIsPostfix, + tokenIsPrefix, + tokenIsRightAssociative, + tokenLabelName, + tokenOperatorPrecedence, + tt, + type TokenType, +} from "../tokenizer/types"; +import * as N from "../types"; +import LValParser from "./lval"; +import { + isKeyword, + isReservedWord, + isStrictReservedWord, + isStrictBindReservedWord, + isIdentifierStart, + canBeReservedWord, +} from "../util/identifier"; +import { Position } from "../util/location"; +import * as charCodes from "charcodes"; +import { + BIND_OUTSIDE, + BIND_VAR, + SCOPE_ARROW, + SCOPE_CLASS, + SCOPE_DIRECT_SUPER, + SCOPE_FUNCTION, + SCOPE_SUPER, +} from "../util/scopeflags"; +import { ExpressionErrors } from "./util"; +import { + PARAM_AWAIT, + PARAM_IN, + PARAM_RETURN, + functionFlags, +} from "../util/production-parameter"; +import { + newArrowHeadScope, + newAsyncArrowScope, + newExpressionScope, +} from "../util/expression-scope"; +import { Errors, SourceTypeModuleErrors } from "./error"; +import type { ParsingError } from "./error"; +import { setInnerComments } from "./comments"; +import { cloneIdentifier } from "./node"; + +/*:: +import type { SourceType } from "../options"; +*/ + +const invalidHackPipeBodies = new Map([ + ["ArrowFunctionExpression", "arrow function"], + ["AssignmentExpression", "assignment"], + ["ConditionalExpression", "conditional"], + ["YieldExpression", "yield"], +]); + +export default class ExpressionParser extends LValParser { + // Forward-declaration: defined in statement.js + /*:: + +parseBlock: ( + allowDirectives?: boolean, + createNewLexicalScope?: boolean, + afterBlockParse?: (hasStrictModeDirective: boolean) => void, + ) => N.BlockStatement; + +parseClass: ( + node: N.Class, + isStatement: boolean, + optionalId?: boolean, + ) => N.Class; + +parseDecorators: (allowExport?: boolean) => void; + +parseFunction: ( + node: T, + statement?: number, + allowExpressionBody?: boolean, + isAsync?: boolean, + ) => T; + +parseFunctionParams: (node: N.Function, allowModifiers?: boolean) => void; + +takeDecorators: (node: N.HasDecorators) => void; + +parseBlockOrModuleBlockBody: ( + body: N.Statement[], + directives: ?(N.Directive[]), + topLevel: boolean, + end: TokenType, + afterBlockParse?: (hasStrictModeDirective: boolean) => void + ) => void + +parseProgram: ( + program: N.Program, end: TokenType, sourceType?: SourceType + ) => N.Program + */ + + // For object literal, check if property __proto__ has been used more than once. + // If the expression is a destructuring assignment, then __proto__ may appear + // multiple times. Otherwise, __proto__ is a duplicated key. + + // For record expression, check if property __proto__ exists + + checkProto( + prop: N.ObjectMember | N.SpreadElement, + isRecord: boolean, + protoRef: { used: boolean }, + refExpressionErrors: ?ExpressionErrors + ): void { + if ( + prop.type === "SpreadElement" || + this.isObjectMethod(prop) || + prop.computed || + // $FlowIgnore + prop.shorthand + ) { + return; + } + + const key = prop.key; + // It is either an Identifier or a String/NumericLiteral + const name = key.type === "Identifier" ? key.name : key.value; + + if (name === "__proto__") { + if (isRecord) { + this.raise(key.start, Errors.RecordNoProto); + return; + } + if (protoRef.used) { + if (refExpressionErrors) { + // Store the first redefinition's position, otherwise ignore because + // we are parsing ambiguous pattern + if (refExpressionErrors.doubleProto === -1) { + refExpressionErrors.doubleProto = key.start; + } + } else { + this.raise(key.start, Errors.DuplicateProto); + } + } + + protoRef.used = true; + } + } + + shouldExitDescending(expr: N.Expression, potentialArrowAt: number): boolean { + return ( + expr.type === "ArrowFunctionExpression" && expr.start === potentialArrowAt + ); + } + + // Convenience method to parse an Expression only + getExpression(): N.Expression & N.ParserOutput { + this.enterInitialScopes(); + this.nextToken(); + const expr = this.parseExpression(); + if (!this.match(tt.eof)) { + this.unexpected(); + } + // Unlike parseTopLevel, we need to drain remaining commentStacks + // because the top level node is _not_ Program. + this.finalizeRemainingComments(); + expr.comments = this.state.comments; + expr.errors = this.state.errors; + if (this.options.tokens) { + expr.tokens = this.tokens; + } + return expr; + } + + // ### Expression parsing + + // These nest, from the most general expression type at the top to + // 'atomic', nondivisible expression types at the bottom. Most of + // the functions will simply let the function (s) below them parse, + // and, *if* the syntactic construct they handle is present, wrap + // the AST node that the inner parser gave them in another node. + + // Parse a full expression. + // - `disallowIn` + // is used to forbid the `in` operator (in for loops initialization expressions) + // When `disallowIn` is true, the production parameter [In] is not present. + + // - `refExpressionErrors ` + // provides reference for storing '=' operator inside shorthand + // property assignment in contexts where both object expression + // and object pattern might appear (so it's possible to raise + // delayed syntax error at correct position). + + parseExpression( + disallowIn?: boolean, + refExpressionErrors?: ExpressionErrors + ): N.Expression { + if (disallowIn) { + return this.disallowInAnd(() => + this.parseExpressionBase(refExpressionErrors) + ); + } + return this.allowInAnd(() => this.parseExpressionBase(refExpressionErrors)); + } + + // https://tc39.es/ecma262/#prod-Expression + parseExpressionBase(refExpressionErrors?: ExpressionErrors): N.Expression { + const startPos = this.state.start; + const startLoc = this.state.startLoc; + const expr = this.parseMaybeAssign(refExpressionErrors); + if (this.match(tt.comma)) { + const node = this.startNodeAt(startPos, startLoc); + node.expressions = [expr]; + while (this.eat(tt.comma)) { + node.expressions.push(this.parseMaybeAssign(refExpressionErrors)); + } + this.toReferencedList(node.expressions); + return this.finishNode(node, "SequenceExpression"); + } + return expr; + } + + // Set [~In] parameter for assignment expression + parseMaybeAssignDisallowIn( + refExpressionErrors?: ?ExpressionErrors, + afterLeftParse?: Function + ) { + return this.disallowInAnd(() => + this.parseMaybeAssign(refExpressionErrors, afterLeftParse) + ); + } + + // Set [+In] parameter for assignment expression + parseMaybeAssignAllowIn( + refExpressionErrors?: ?ExpressionErrors, + afterLeftParse?: Function + ) { + return this.allowInAnd(() => + this.parseMaybeAssign(refExpressionErrors, afterLeftParse) + ); + } + + // This method is only used by + // the typescript and flow plugins. + setOptionalParametersError( + refExpressionErrors: ExpressionErrors, + resultError?: ParsingError + ) { + refExpressionErrors.optionalParameters = + resultError?.pos ?? this.state.start; + } + + // Parse an assignment expression. This includes applications of + // operators like `+=`. + // https://tc39.es/ecma262/#prod-AssignmentExpression + parseMaybeAssign( + refExpressionErrors?: ?ExpressionErrors, + afterLeftParse?: Function + ): N.Expression { + const startPos = this.state.start; + const startLoc = this.state.startLoc; + if (this.isContextual(tt._yield)) { + if (this.prodParam.hasYield) { + let left = this.parseYield(); + if (afterLeftParse) { + left = afterLeftParse.call(this, left, startPos, startLoc); + } + return left; + } + } + + let ownExpressionErrors; + if (refExpressionErrors) { + ownExpressionErrors = false; + } else { + refExpressionErrors = new ExpressionErrors(); + ownExpressionErrors = true; + } + const { type } = this.state; + + if (type === tt.parenL || tokenIsIdentifier(type)) { + this.state.potentialArrowAt = this.state.start; + } + + let left = this.parseMaybeConditional(refExpressionErrors); + if (afterLeftParse) { + left = afterLeftParse.call(this, left, startPos, startLoc); + } + if (tokenIsAssignment(this.state.type)) { + const node = this.startNodeAt(startPos, startLoc); + const operator = this.state.value; + node.operator = operator; + + if (this.match(tt.eq)) { + node.left = this.toAssignable(left, /* isLHS */ true); + refExpressionErrors.doubleProto = -1; // reset because double __proto__ is valid in assignment expression + } else { + node.left = left; + } + + if (refExpressionErrors.shorthandAssign >= node.left.start) { + refExpressionErrors.shorthandAssign = -1; // reset because shorthand default was used correctly + } + + this.checkLVal(left, "assignment expression"); + + this.next(); + node.right = this.parseMaybeAssign(); + return this.finishNode(node, "AssignmentExpression"); + } else if (ownExpressionErrors) { + this.checkExpressionErrors(refExpressionErrors, true); + } + + return left; + } + + // Parse a ternary conditional (`?:`) operator. + // https://tc39.es/ecma262/#prod-ConditionalExpression + + parseMaybeConditional(refExpressionErrors: ExpressionErrors): N.Expression { + const startPos = this.state.start; + const startLoc = this.state.startLoc; + const potentialArrowAt = this.state.potentialArrowAt; + const expr = this.parseExprOps(refExpressionErrors); + + if (this.shouldExitDescending(expr, potentialArrowAt)) { + return expr; + } + + return this.parseConditional(expr, startPos, startLoc, refExpressionErrors); + } + + parseConditional( + expr: N.Expression, + startPos: number, + startLoc: Position, + // eslint-disable-next-line no-unused-vars + refExpressionErrors?: ?ExpressionErrors + ): N.Expression { + if (this.eat(tt.question)) { + const node = this.startNodeAt(startPos, startLoc); + node.test = expr; + node.consequent = this.parseMaybeAssignAllowIn(); + this.expect(tt.colon); + node.alternate = this.parseMaybeAssign(); + return this.finishNode(node, "ConditionalExpression"); + } + return expr; + } + + parseMaybeUnaryOrPrivate( + refExpressionErrors?: ExpressionErrors + ): N.Expression | N.PrivateName { + return this.match(tt.privateName) + ? this.parsePrivateName() + : this.parseMaybeUnary(refExpressionErrors); + } + + // Start the precedence parser. + // https://tc39.es/ecma262/#prod-ShortCircuitExpression + + parseExprOps(refExpressionErrors: ExpressionErrors): N.Expression { + const startPos = this.state.start; + const startLoc = this.state.startLoc; + const potentialArrowAt = this.state.potentialArrowAt; + const expr = this.parseMaybeUnaryOrPrivate(refExpressionErrors); + + if (this.shouldExitDescending(expr, potentialArrowAt)) { + return expr; + } + + return this.parseExprOp(expr, startPos, startLoc, -1); + } + + // Parse binary operators with the operator precedence parsing + // algorithm. `left` is the left-hand side of the operator. + // `minPrec` provides context that allows the function to stop and + // defer further parser to one of its callers when it encounters an + // operator that has a lower precedence than the set it is parsing. + + parseExprOp( + left: N.Expression | N.PrivateName, + leftStartPos: number, + leftStartLoc: Position, + minPrec: number + ): N.Expression { + if (this.isPrivateName(left)) { + // https://tc39.es/proposal-private-fields-in-in + // RelationalExpression [In, Yield, Await] + // [+In] PrivateIdentifier in ShiftExpression[?Yield, ?Await] + + const value = this.getPrivateNameSV(left); + const { start } = left; + + if ( + minPrec >= tokenOperatorPrecedence(tt._in) || + !this.prodParam.hasIn || + !this.match(tt._in) + ) { + this.raise(start, Errors.PrivateInExpectedIn, value); + } + + this.classScope.usePrivateName(value, start); + } + + const op = this.state.type; + if (tokenIsOperator(op) && (this.prodParam.hasIn || !this.match(tt._in))) { + let prec = tokenOperatorPrecedence(op); + if (prec > minPrec) { + if (op === tt.pipeline) { + this.expectPlugin("pipelineOperator"); + if (this.state.inFSharpPipelineDirectBody) { + return left; + } + this.checkPipelineAtInfixOperator(left, leftStartPos); + } + const node = this.startNodeAt(leftStartPos, leftStartLoc); + node.left = left; + node.operator = this.state.value; + + const logical = op === tt.logicalOR || op === tt.logicalAND; + const coalesce = op === tt.nullishCoalescing; + + if (coalesce) { + // Handle the precedence of `tt.coalesce` as equal to the range of logical expressions. + // In other words, `node.right` shouldn't contain logical expressions in order to check the mixed error. + prec = tokenOperatorPrecedence(tt.logicalAND); + } + + this.next(); + + if ( + op === tt.pipeline && + this.getPluginOption("pipelineOperator", "proposal") === "minimal" + ) { + if (this.state.type === tt._await && this.prodParam.hasAwait) { + throw this.raise( + this.state.start, + Errors.UnexpectedAwaitAfterPipelineBody + ); + } + } + + node.right = this.parseExprOpRightExpr(op, prec); + this.finishNode( + node, + logical || coalesce ? "LogicalExpression" : "BinaryExpression" + ); + /* this check is for all ?? operators + * a ?? b && c for this example + * when op is coalesce and nextOp is logical (&&), throw at the pos of nextOp that it can not be mixed. + * Symmetrically it also throws when op is logical and nextOp is coalesce + */ + const nextOp = this.state.type; + if ( + (coalesce && (nextOp === tt.logicalOR || nextOp === tt.logicalAND)) || + (logical && nextOp === tt.nullishCoalescing) + ) { + throw this.raise(this.state.start, Errors.MixingCoalesceWithLogical); + } + + return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec); + } + } + return left; + } + + // Helper function for `parseExprOp`. Parse the right-hand side of binary- + // operator expressions, then apply any operator-specific functions. + + parseExprOpRightExpr(op: TokenType, prec: number): N.Expression { + const startPos = this.state.start; + const startLoc = this.state.startLoc; + switch (op) { + case tt.pipeline: + switch (this.getPluginOption("pipelineOperator", "proposal")) { + case "hack": + return this.withTopicBindingContext(() => { + return this.parseHackPipeBody(); + }); + + case "smart": + return this.withTopicBindingContext(() => { + if (this.prodParam.hasYield && this.isContextual(tt._yield)) { + throw this.raise( + this.state.start, + Errors.PipeBodyIsTighter, + this.state.value + ); + } + return this.parseSmartPipelineBodyInStyle( + this.parseExprOpBaseRightExpr(op, prec), + startPos, + startLoc + ); + }); + + case "fsharp": + return this.withSoloAwaitPermittingContext(() => { + return this.parseFSharpPipelineBody(prec); + }); + } + + // Falls through. + default: + return this.parseExprOpBaseRightExpr(op, prec); + } + } + + // Helper function for `parseExprOpRightExpr`. Parse the right-hand side of + // binary-operator expressions without applying any operator-specific functions. + + parseExprOpBaseRightExpr(op: TokenType, prec: number): N.Expression { + const startPos = this.state.start; + const startLoc = this.state.startLoc; + + return this.parseExprOp( + this.parseMaybeUnaryOrPrivate(), + startPos, + startLoc, + tokenIsRightAssociative(op) ? prec - 1 : prec + ); + } + + parseHackPipeBody(): N.Expression { + const { start } = this.state; + + const body = this.parseMaybeAssign(); + + // TODO: Check how to handle type casts in Flow and TS once they are supported + if (invalidHackPipeBodies.has(body.type) && !body.extra?.parenthesized) { + this.raise( + start, + Errors.PipeUnparenthesizedBody, + invalidHackPipeBodies.get(body.type) + ); + } + if (!this.topicReferenceWasUsedInCurrentContext()) { + // A Hack pipe body must use the topic reference at least once. + this.raise(start, Errors.PipeTopicUnused); + } + + return body; + } + + checkExponentialAfterUnary(node: N.AwaitExpression | N.UnaryExpression) { + if (this.match(tt.exponent)) { + this.raise( + node.argument.start, + Errors.UnexpectedTokenUnaryExponentiation + ); + } + } + + // Parse unary operators, both prefix and postfix. + // https://tc39.es/ecma262/#prod-UnaryExpression + parseMaybeUnary( + refExpressionErrors: ?ExpressionErrors, + sawUnary?: boolean + ): N.Expression { + const startPos = this.state.start; + const startLoc = this.state.startLoc; + const isAwait = this.isContextual(tt._await); + + if (isAwait && this.isAwaitAllowed()) { + this.next(); + const expr = this.parseAwait(startPos, startLoc); + if (!sawUnary) this.checkExponentialAfterUnary(expr); + return expr; + } + const update = this.match(tt.incDec); + const node = this.startNode(); + if (tokenIsPrefix(this.state.type)) { + node.operator = this.state.value; + node.prefix = true; + + if (this.match(tt._throw)) { + this.expectPlugin("throwExpressions"); + } + const isDelete = this.match(tt._delete); + this.next(); + + node.argument = this.parseMaybeUnary(null, true); + + this.checkExpressionErrors(refExpressionErrors, true); + + if (this.state.strict && isDelete) { + const arg = node.argument; + + if (arg.type === "Identifier") { + this.raise(node.start, Errors.StrictDelete); + } else if (this.hasPropertyAsPrivateName(arg)) { + this.raise(node.start, Errors.DeletePrivateField); + } + } + + if (!update) { + if (!sawUnary) this.checkExponentialAfterUnary(node); + return this.finishNode(node, "UnaryExpression"); + } + } + + const expr = this.parseUpdate(node, update, refExpressionErrors); + + if (isAwait) { + const { type } = this.state; + const startsExpr = this.hasPlugin("v8intrinsic") + ? tokenCanStartExpression(type) + : tokenCanStartExpression(type) && !this.match(tt.modulo); + if (startsExpr && !this.isAmbiguousAwait()) { + this.raiseOverwrite(startPos, Errors.AwaitNotInAsyncContext); + return this.parseAwait(startPos, startLoc); + } + } + + return expr; + } + + // https://tc39.es/ecma262/#prod-UpdateExpression + parseUpdate( + node: N.Expression, + update: boolean, + refExpressionErrors: ?ExpressionErrors + ): N.Expression { + if (update) { + this.checkLVal(node.argument, "prefix operation"); + return this.finishNode(node, "UpdateExpression"); + } + + const startPos = this.state.start; + const startLoc = this.state.startLoc; + let expr = this.parseExprSubscripts(refExpressionErrors); + if (this.checkExpressionErrors(refExpressionErrors, false)) return expr; + while (tokenIsPostfix(this.state.type) && !this.canInsertSemicolon()) { + const node = this.startNodeAt(startPos, startLoc); + node.operator = this.state.value; + node.prefix = false; + node.argument = expr; + this.checkLVal(expr, "postfix operation"); + this.next(); + expr = this.finishNode(node, "UpdateExpression"); + } + return expr; + } + + // Parse call, dot, and `[]`-subscript expressions. + // https://tc39.es/ecma262/#prod-LeftHandSideExpression + parseExprSubscripts(refExpressionErrors: ?ExpressionErrors): N.Expression { + const startPos = this.state.start; + const startLoc = this.state.startLoc; + const potentialArrowAt = this.state.potentialArrowAt; + const expr = this.parseExprAtom(refExpressionErrors); + + if (this.shouldExitDescending(expr, potentialArrowAt)) { + return expr; + } + + return this.parseSubscripts(expr, startPos, startLoc); + } + + parseSubscripts( + base: N.Expression, + startPos: number, + startLoc: Position, + noCalls?: ?boolean + ): N.Expression { + const state = { + optionalChainMember: false, + maybeAsyncArrow: this.atPossibleAsyncArrow(base), + stop: false, + }; + do { + base = this.parseSubscript(base, startPos, startLoc, noCalls, state); + + // After parsing a subscript, this isn't "async" for sure. + state.maybeAsyncArrow = false; + } while (!state.stop); + return base; + } + + /** + * @param state Set 'state.stop = true' to indicate that we should stop parsing subscripts. + * state.optionalChainMember to indicate that the member is currently in OptionalChain + */ + parseSubscript( + base: N.Expression, + startPos: number, + startLoc: Position, + noCalls: ?boolean, + state: N.ParseSubscriptState + ): N.Expression { + if (!noCalls && this.eat(tt.doubleColon)) { + return this.parseBind(base, startPos, startLoc, noCalls, state); + } else if (this.match(tt.backQuote)) { + return this.parseTaggedTemplateExpression( + base, + startPos, + startLoc, + state + ); + } + + let optional = false; + + if (this.match(tt.questionDot)) { + if (noCalls && this.lookaheadCharCode() === charCodes.leftParenthesis) { + // stop at `?.` when parsing `new a?.()` + state.stop = true; + return base; + } + state.optionalChainMember = optional = true; + this.next(); + } + + if (!noCalls && this.match(tt.parenL)) { + return this.parseCoverCallAndAsyncArrowHead( + base, + startPos, + startLoc, + state, + optional + ); + } else { + const computed = this.eat(tt.bracketL); + if (computed || optional || this.eat(tt.dot)) { + return this.parseMember( + base, + startPos, + startLoc, + state, + computed, + optional + ); + } else { + state.stop = true; + return base; + } + } + } + + // base[?Yield, ?Await] [ Expression[+In, ?Yield, ?Await] ] + // base[?Yield, ?Await] . IdentifierName + // base[?Yield, ?Await] . PrivateIdentifier + // where `base` is one of CallExpression, MemberExpression and OptionalChain + parseMember( + base: N.Expression, + startPos: number, + startLoc: Position, + state: N.ParseSubscriptState, + computed: boolean, + optional: boolean + ): N.OptionalMemberExpression | N.MemberExpression { + const node = this.startNodeAt(startPos, startLoc); + node.object = base; + node.computed = computed; + const privateName = + !computed && this.match(tt.privateName) && this.state.value; + const property = computed + ? this.parseExpression() + : privateName + ? this.parsePrivateName() + : this.parseIdentifier(true); + + if (privateName !== false) { + if (node.object.type === "Super") { + this.raise(startPos, Errors.SuperPrivateField); + } + this.classScope.usePrivateName(privateName, property.start); + } + node.property = property; + + if (computed) { + this.expect(tt.bracketR); + } + + if (state.optionalChainMember) { + node.optional = optional; + return this.finishNode(node, "OptionalMemberExpression"); + } else { + return this.finishNode(node, "MemberExpression"); + } + } + + // https://github.com/tc39/proposal-bind-operator#syntax + parseBind( + base: N.Expression, + startPos: number, + startLoc: Position, + noCalls: ?boolean, + state: N.ParseSubscriptState + ): N.Expression { + const node = this.startNodeAt(startPos, startLoc); + node.object = base; + node.callee = this.parseNoCallExpr(); + state.stop = true; + return this.parseSubscripts( + this.finishNode(node, "BindExpression"), + startPos, + startLoc, + noCalls + ); + } + + // https://tc39.es/ecma262/#prod-CoverCallExpressionAndAsyncArrowHead + // CoverCallExpressionAndAsyncArrowHead + // CallExpression[?Yield, ?Await] Arguments[?Yield, ?Await] + // OptionalChain[?Yield, ?Await] Arguments[?Yield, ?Await] + parseCoverCallAndAsyncArrowHead( + base: N.Expression, + startPos: number, + startLoc: Position, + state: N.ParseSubscriptState, + optional: boolean + ): N.Expression { + const oldMaybeInArrowParameters = this.state.maybeInArrowParameters; + let refExpressionErrors = null; + + this.state.maybeInArrowParameters = true; + this.next(); // eat `(` + + let node = this.startNodeAt(startPos, startLoc); + node.callee = base; + + if (state.maybeAsyncArrow) { + this.expressionScope.enter(newAsyncArrowScope()); + refExpressionErrors = new ExpressionErrors(); + } + + if (state.optionalChainMember) { + node.optional = optional; + } + + if (optional) { + node.arguments = this.parseCallExpressionArguments(tt.parenR); + } else { + node.arguments = this.parseCallExpressionArguments( + tt.parenR, + base.type === "Import", + base.type !== "Super", + node, + refExpressionErrors + ); + } + this.finishCallExpression(node, state.optionalChainMember); + + if (state.maybeAsyncArrow && this.shouldParseAsyncArrow() && !optional) { + state.stop = true; + this.expressionScope.validateAsPattern(); + this.expressionScope.exit(); + node = this.parseAsyncArrowFromCallExpression( + this.startNodeAt(startPos, startLoc), + node + ); + } else { + if (state.maybeAsyncArrow) { + this.checkExpressionErrors(refExpressionErrors, true); + this.expressionScope.exit(); + } + this.toReferencedArguments(node); + } + + this.state.maybeInArrowParameters = oldMaybeInArrowParameters; + + return node; + } + + toReferencedArguments( + node: N.CallExpression | N.OptionalCallExpression, + isParenthesizedExpr?: boolean + ) { + this.toReferencedListDeep(node.arguments, isParenthesizedExpr); + } + + // MemberExpression [?Yield, ?Await] TemplateLiteral[?Yield, ?Await, +Tagged] + // CallExpression [?Yield, ?Await] TemplateLiteral[?Yield, ?Await, +Tagged] + parseTaggedTemplateExpression( + base: N.Expression, + startPos: number, + startLoc: Position, + state: N.ParseSubscriptState + ): N.TaggedTemplateExpression { + const node: N.TaggedTemplateExpression = this.startNodeAt( + startPos, + startLoc + ); + node.tag = base; + node.quasi = this.parseTemplate(true); + if (state.optionalChainMember) { + this.raise(startPos, Errors.OptionalChainingNoTemplate); + } + return this.finishNode(node, "TaggedTemplateExpression"); + } + + atPossibleAsyncArrow(base: N.Expression): boolean { + return ( + base.type === "Identifier" && + base.name === "async" && + this.state.lastTokEnd === base.end && + !this.canInsertSemicolon() && + // check there are no escape sequences, such as \u{61}sync + base.end - base.start === 5 && + base.start === this.state.potentialArrowAt + ); + } + + finishCallExpression( + node: T, + optional: boolean + ): N.Expression { + if (node.callee.type === "Import") { + if (node.arguments.length === 2) { + if (process.env.BABEL_8_BREAKING) { + this.expectPlugin("importAssertions"); + } else { + if (!this.hasPlugin("moduleAttributes")) { + this.expectPlugin("importAssertions"); + } + } + } + if (node.arguments.length === 0 || node.arguments.length > 2) { + this.raise( + node.start, + Errors.ImportCallArity, + this.hasPlugin("importAssertions") || + this.hasPlugin("moduleAttributes") + ? "one or two arguments" + : "one argument" + ); + } else { + for (const arg of node.arguments) { + if (arg.type === "SpreadElement") { + this.raise(arg.start, Errors.ImportCallSpreadArgument); + } + } + } + } + return this.finishNode( + node, + optional ? "OptionalCallExpression" : "CallExpression" + ); + } + + parseCallExpressionArguments( + close: TokenType, + dynamicImport?: boolean, + allowPlaceholder?: boolean, + nodeForExtra?: ?N.Node, + refExpressionErrors?: ?ExpressionErrors + ): $ReadOnlyArray { + const elts = []; + let first = true; + const oldInFSharpPipelineDirectBody = this.state.inFSharpPipelineDirectBody; + this.state.inFSharpPipelineDirectBody = false; + + while (!this.eat(close)) { + if (first) { + first = false; + } else { + this.expect(tt.comma); + if (this.match(close)) { + if ( + dynamicImport && + !this.hasPlugin("importAssertions") && + !this.hasPlugin("moduleAttributes") + ) { + this.raise( + this.state.lastTokStart, + Errors.ImportCallArgumentTrailingComma + ); + } + if (nodeForExtra) { + this.addExtra( + nodeForExtra, + "trailingComma", + this.state.lastTokStart + ); + } + this.next(); + break; + } + } + + elts.push( + this.parseExprListItem(false, refExpressionErrors, allowPlaceholder) + ); + } + + this.state.inFSharpPipelineDirectBody = oldInFSharpPipelineDirectBody; + + return elts; + } + + shouldParseAsyncArrow(): boolean { + return this.match(tt.arrow) && !this.canInsertSemicolon(); + } + + parseAsyncArrowFromCallExpression( + node: N.ArrowFunctionExpression, + call: N.CallExpression + ): N.ArrowFunctionExpression { + this.resetPreviousNodeTrailingComments(call); + this.expect(tt.arrow); + this.parseArrowExpression( + node, + call.arguments, + true, + call.extra?.trailingComma + ); + // mark inner comments of `async()` as inner comments of `async () =>` + setInnerComments(node, call.innerComments); + // mark trailing comments of `async` to be inner comments + setInnerComments(node, call.callee.trailingComments); + return node; + } + + // Parse a no-call expression (like argument of `new` or `::` operators). + // https://tc39.es/ecma262/#prod-MemberExpression + parseNoCallExpr(): N.Expression { + const startPos = this.state.start; + const startLoc = this.state.startLoc; + return this.parseSubscripts(this.parseExprAtom(), startPos, startLoc, true); + } + + // Parse an atomic expression — either a single token that is an + // expression, an expression started by a keyword like `function` or + // `new`, or an expression wrapped in punctuation like `()`, `[]`, + // or `{}`. + + // https://tc39.es/ecma262/#prod-PrimaryExpression + // https://tc39.es/ecma262/#prod-AsyncArrowFunction + // PrimaryExpression + // Super + // Import + // AsyncArrowFunction + + parseExprAtom(refExpressionErrors?: ?ExpressionErrors): N.Expression { + let node; + + const { type } = this.state; + switch (type) { + case tt._super: + return this.parseSuper(); + + case tt._import: + node = this.startNode(); + this.next(); + + if (this.match(tt.dot)) { + return this.parseImportMetaProperty(node); + } + + if (!this.match(tt.parenL)) { + this.raise(this.state.lastTokStart, Errors.UnsupportedImport); + } + return this.finishNode(node, "Import"); + case tt._this: + node = this.startNode(); + this.next(); + return this.finishNode(node, "ThisExpression"); + + case tt._do: { + return this.parseDo(this.startNode(), false); + } + + case tt.slash: + case tt.slashAssign: { + this.readRegexp(); + return this.parseRegExpLiteral(this.state.value); + } + + case tt.num: + return this.parseNumericLiteral(this.state.value); + + case tt.bigint: + return this.parseBigIntLiteral(this.state.value); + + case tt.decimal: + return this.parseDecimalLiteral(this.state.value); + + case tt.string: + return this.parseStringLiteral(this.state.value); + + case tt._null: + return this.parseNullLiteral(); + + case tt._true: + return this.parseBooleanLiteral(true); + case tt._false: + return this.parseBooleanLiteral(false); + + case tt.parenL: { + const canBeArrow = this.state.potentialArrowAt === this.state.start; + return this.parseParenAndDistinguishExpression(canBeArrow); + } + + case tt.bracketBarL: + case tt.bracketHashL: { + return this.parseArrayLike( + this.state.type === tt.bracketBarL ? tt.bracketBarR : tt.bracketR, + /* canBePattern */ false, + /* isTuple */ true, + refExpressionErrors + ); + } + case tt.bracketL: { + return this.parseArrayLike( + tt.bracketR, + /* canBePattern */ true, + /* isTuple */ false, + refExpressionErrors + ); + } + case tt.braceBarL: + case tt.braceHashL: { + return this.parseObjectLike( + this.state.type === tt.braceBarL ? tt.braceBarR : tt.braceR, + /* isPattern */ false, + /* isRecord */ true, + refExpressionErrors + ); + } + case tt.braceL: { + return this.parseObjectLike( + tt.braceR, + /* isPattern */ false, + /* isRecord */ false, + refExpressionErrors + ); + } + case tt._function: + return this.parseFunctionOrFunctionSent(); + + case tt.at: + this.parseDecorators(); + // fall through + case tt._class: + node = this.startNode(); + this.takeDecorators(node); + return this.parseClass(node, false); + + case tt._new: + return this.parseNewOrNewTarget(); + + case tt.backQuote: + return this.parseTemplate(false); + + // BindExpression[Yield] + // :: MemberExpression[?Yield] + case tt.doubleColon: { + node = this.startNode(); + this.next(); + node.object = null; + const callee = (node.callee = this.parseNoCallExpr()); + if (callee.type === "MemberExpression") { + return this.finishNode(node, "BindExpression"); + } else { + throw this.raise(callee.start, Errors.UnsupportedBind); + } + } + + case tt.privateName: { + // Standalone private names are only allowed in "#x in obj" + // expressions, and they are directly handled by callers of + // parseExprOp. If we reach this, the input is always invalid. + // We can throw a better error message and recover, rather than + // just throwing "Unexpected token" (which is the default + // behavior of this big switch statement). + this.raise( + this.state.start, + Errors.PrivateInExpectedIn, + this.state.value + ); + return this.parsePrivateName(); + } + + case tt.moduloAssign: + if ( + this.getPluginOption("pipelineOperator", "proposal") === "hack" && + this.getPluginOption("pipelineOperator", "topicToken") === "%" + ) { + // If we find %= in an expression position, and the Hack-pipes proposal is active, + // then the % could be the topic token (e.g., in x |> %==y or x |> %===y), and so we + // reparse it as %. + // The next readToken() call will start parsing from =. + + this.state.value = "%"; + this.state.type = tt.modulo; + this.state.pos--; + this.state.end--; + this.state.endLoc.column--; + } else { + throw this.unexpected(); + } + + // falls through + case tt.modulo: + case tt.hash: { + const pipeProposal = this.getPluginOption( + "pipelineOperator", + "proposal" + ); + + if (pipeProposal) { + // A pipe-operator proposal is active, + // although its configuration might not match the current token’s type. + node = this.startNode(); + const start = this.state.start; + const tokenType = this.state.type; + + // Consume the current token. + this.next(); + + // If the pipe-operator plugin’s configuration matches the current token’s type, + // then this will return `node`, will have been finished as a topic reference. + // Otherwise, this will throw a `PipeTopicUnconfiguredToken` error. + return this.finishTopicReference( + node, + start, + pipeProposal, + tokenType + ); + } + } + + // fall through + case tt.relational: { + if (this.state.value === "<") { + const lookaheadCh = this.input.codePointAt(this.nextTokenStart()); + if ( + isIdentifierStart(lookaheadCh) || // Element/Type Parameter + lookaheadCh === charCodes.greaterThan // Fragment <> + ) { + this.expectOnePlugin(["jsx", "flow", "typescript"]); + } + } + } + + // fall through + default: + if (tokenIsIdentifier(type)) { + if ( + this.isContextual(tt._module) && + this.lookaheadCharCode() === charCodes.leftCurlyBrace && + !this.hasFollowingLineBreak() + ) { + return this.parseModuleExpression(); + } + const canBeArrow = this.state.potentialArrowAt === this.state.start; + const containsEsc = this.state.containsEsc; + const id = this.parseIdentifier(); + + if ( + !containsEsc && + id.name === "async" && + !this.canInsertSemicolon() + ) { + const { type } = this.state; + if (type === tt._function) { + this.resetPreviousNodeTrailingComments(id); + this.next(); + return this.parseFunction( + this.startNodeAtNode(id), + undefined, + true + ); + } else if (tokenIsIdentifier(type)) { + // If the next token begins with "=", commit to parsing an async + // arrow function. (Peeking ahead for "=" lets us avoid a more + // expensive full-token lookahead on this common path.) + if (this.lookaheadCharCode() === charCodes.equalsTo) { + // although `id` is not used in async arrow unary function, + // we don't need to reset `async`'s trailing comments because + // it will be attached to the upcoming async arrow binding identifier + return this.parseAsyncArrowUnaryFunction( + this.startNodeAtNode(id) + ); + } else { + // Otherwise, treat "async" as an identifier and let calling code + // deal with the current tt.name token. + return id; + } + } else if (type === tt._do) { + this.resetPreviousNodeTrailingComments(id); + return this.parseDo(this.startNodeAtNode(id), true); + } + } + + if ( + canBeArrow && + this.match(tt.arrow) && + !this.canInsertSemicolon() + ) { + this.next(); + return this.parseArrowExpression( + this.startNodeAtNode(id), + [id], + false + ); + } + + return id; + } else { + throw this.unexpected(); + } + } + } + + // This helper method attempts to finish the given `node` + // into a topic-reference node for the given `pipeProposal`. + // See . + // + // The method assumes that any topic token was consumed before it was called. + // + // If the `pipelineOperator` plugin is active, + // and if the given `tokenType` matches the plugin’s configuration, + // then this method will return the finished `node`. + // + // If the `pipelineOperator` plugin is active, + // but if the given `tokenType` does not match the plugin’s configuration, + // then this method will throw a `PipeTopicUnconfiguredToken` error. + finishTopicReference( + node: N.Node, + start: number, + pipeProposal: string, + tokenType: TokenType + ): N.Expression { + if (this.testTopicReferenceConfiguration(pipeProposal, start, tokenType)) { + // The token matches the plugin’s configuration. + // The token is therefore a topic reference. + + // Determine the node type for the topic reference + // that is appropriate for the active pipe-operator proposal. + let nodeType; + if (pipeProposal === "smart") { + nodeType = "PipelinePrimaryTopicReference"; + } else { + // The proposal must otherwise be "hack", + // as enforced by testTopicReferenceConfiguration. + nodeType = "TopicReference"; + } + + if (!this.topicReferenceIsAllowedInCurrentContext()) { + // The topic reference is not allowed in the current context: + // it is outside of a pipe body. + // Raise recoverable errors. + if (pipeProposal === "smart") { + this.raise(start, Errors.PrimaryTopicNotAllowed); + } else { + // In this case, `pipeProposal === "hack"` is true. + this.raise(start, Errors.PipeTopicUnbound); + } + } + + // Register the topic reference so that its pipe body knows + // that its topic was used at least once. + this.registerTopicReference(); + + return this.finishNode(node, nodeType); + } else { + // The token does not match the plugin’s configuration. + throw this.raise( + start, + Errors.PipeTopicUnconfiguredToken, + tokenLabelName(tokenType) + ); + } + } + + // This helper method tests whether the given token type + // matches the pipelineOperator parser plugin’s configuration. + // If the active pipe proposal is Hack style, + // and if the given token is the same as the plugin configuration’s `topicToken`, + // then this is a valid topic reference. + // If the active pipe proposal is smart mix, + // then the topic token must always be `#`. + // If the active pipe proposal is neither (e.g., "minimal" or "fsharp"), + // then an error is thrown. + testTopicReferenceConfiguration( + pipeProposal: string, + start: number, + tokenType: TokenType + ): boolean { + switch (pipeProposal) { + case "hack": { + const pluginTopicToken = this.getPluginOption( + "pipelineOperator", + "topicToken" + ); + return tokenLabelName(tokenType) === pluginTopicToken; + } + case "smart": + return tokenType === tt.hash; + default: + throw this.raise(start, Errors.PipeTopicRequiresHackPipes); + } + } + + // async [no LineTerminator here] AsyncArrowBindingIdentifier[?Yield] [no LineTerminator here] => AsyncConciseBody[?In] + parseAsyncArrowUnaryFunction(node: N.Node): N.ArrowFunctionExpression { + // We don't need to push a new ParameterDeclarationScope here since we are sure + // 1) it is an async arrow, 2) no biding pattern is allowed in params + this.prodParam.enter(functionFlags(true, this.prodParam.hasYield)); + const params = [this.parseIdentifier()]; + this.prodParam.exit(); + if (this.hasPrecedingLineBreak()) { + this.raise(this.state.pos, Errors.LineTerminatorBeforeArrow); + } + this.expect(tt.arrow); + // let foo = async bar => {}; + this.parseArrowExpression(node, params, true); + return node; + } + + // https://github.com/tc39/proposal-do-expressions + // https://github.com/tc39/proposal-async-do-expressions + parseDo(node: N.Node, isAsync: boolean): N.DoExpression { + this.expectPlugin("doExpressions"); + if (isAsync) { + this.expectPlugin("asyncDoExpressions"); + } + node.async = isAsync; + this.next(); // eat `do` + const oldLabels = this.state.labels; + this.state.labels = []; + if (isAsync) { + // AsyncDoExpression : + // async [no LineTerminator here] do Block[~Yield, +Await, ~Return] + this.prodParam.enter(PARAM_AWAIT); + node.body = this.parseBlock(); + this.prodParam.exit(); + } else { + node.body = this.parseBlock(); + } + + this.state.labels = oldLabels; + return this.finishNode(node, "DoExpression"); + } + + // Parse the `super` keyword + parseSuper(): N.Super { + const node = this.startNode(); + this.next(); // eat `super` + if ( + this.match(tt.parenL) && + !this.scope.allowDirectSuper && + !this.options.allowSuperOutsideMethod + ) { + this.raise(node.start, Errors.SuperNotAllowed); + } else if ( + !this.scope.allowSuper && + !this.options.allowSuperOutsideMethod + ) { + this.raise(node.start, Errors.UnexpectedSuper); + } + + if ( + !this.match(tt.parenL) && + !this.match(tt.bracketL) && + !this.match(tt.dot) + ) { + this.raise(node.start, Errors.UnsupportedSuper); + } + + return this.finishNode(node, "Super"); + } + + parseMaybePrivateName( + isPrivateNameAllowed: boolean + ): N.PrivateName | N.Identifier { + const isPrivate = this.match(tt.privateName); + + if (isPrivate) { + if (!isPrivateNameAllowed) { + this.raise(this.state.start + 1, Errors.UnexpectedPrivateField); + } + return this.parsePrivateName(); + } else { + return this.parseIdentifier(true); + } + } + + parsePrivateName(): N.PrivateName { + const node = this.startNode(); + const id = this.startNodeAt( + this.state.start + 1, + // The position is hardcoded because we merge `#` and name into a single + // tt.privateName token + new Position( + this.state.curLine, + this.state.start + 1 - this.state.lineStart + ) + ); + const name = this.state.value; + this.next(); // eat #name; + node.id = this.createIdentifier(id, name); + return this.finishNode(node, "PrivateName"); + } + + parseFunctionOrFunctionSent(): N.FunctionExpression | N.MetaProperty { + const node = this.startNode(); + + // We do not do parseIdentifier here because when parseFunctionOrFunctionSent + // is called we already know that the current token is a "name" with the value "function" + // This will improve perf a tiny little bit as we do not do validation but more importantly + // here is that parseIdentifier will remove an item from the expression stack + // if "function" or "class" is parsed as identifier (in objects e.g.), which should not happen here. + this.next(); // eat `function` + + if (this.prodParam.hasYield && this.match(tt.dot)) { + const meta = this.createIdentifier( + this.startNodeAtNode(node), + "function" + ); + this.next(); // eat `.` + // https://github.com/tc39/proposal-function.sent#syntax-1 + if (this.match(tt._sent)) { + this.expectPlugin("functionSent"); + } else if (!this.hasPlugin("functionSent")) { + // The code wasn't `function.sent` but just `function.`, so a simple error is less confusing. + this.unexpected(); + } + return this.parseMetaProperty(node, meta, "sent"); + } + return this.parseFunction(node); + } + + parseMetaProperty( + node: N.MetaProperty, + meta: N.Identifier, + propertyName: string + ): N.MetaProperty { + node.meta = meta; + + const containsEsc = this.state.containsEsc; + + node.property = this.parseIdentifier(true); + + if (node.property.name !== propertyName || containsEsc) { + this.raise( + node.property.start, + Errors.UnsupportedMetaProperty, + meta.name, + propertyName + ); + } + + return this.finishNode(node, "MetaProperty"); + } + + // https://tc39.es/ecma262/#prod-ImportMeta + parseImportMetaProperty(node: N.MetaProperty): N.MetaProperty { + const id = this.createIdentifier(this.startNodeAtNode(node), "import"); + this.next(); // eat `.` + + if (this.isContextual(tt._meta)) { + if (!this.inModule) { + this.raise(id.start, SourceTypeModuleErrors.ImportMetaOutsideModule); + } + this.sawUnambiguousESM = true; + } + + return this.parseMetaProperty(node, id, "meta"); + } + + parseLiteralAtNode( + value: any, + type: $ElementType, + node: any + ): T { + this.addExtra(node, "rawValue", value); + this.addExtra(node, "raw", this.input.slice(node.start, this.state.end)); + node.value = value; + this.next(); + return this.finishNode(node, type); + } + + parseLiteral(value: any, type: $ElementType): T { + const node = this.startNode(); + return this.parseLiteralAtNode(value, type, node); + } + + parseStringLiteral(value: any) { + return this.parseLiteral(value, "StringLiteral"); + } + + parseNumericLiteral(value: any) { + return this.parseLiteral(value, "NumericLiteral"); + } + + parseBigIntLiteral(value: any) { + return this.parseLiteral(value, "BigIntLiteral"); + } + + parseDecimalLiteral(value: any) { + return this.parseLiteral(value, "DecimalLiteral"); + } + + parseRegExpLiteral(value: { value: any, pattern: string, flags: string }) { + const node = this.parseLiteral( + value.value, + "RegExpLiteral" + ); + node.pattern = value.pattern; + node.flags = value.flags; + return node; + } + + parseBooleanLiteral(value: boolean) { + const node = this.startNode(); + node.value = value; + this.next(); + return this.finishNode(node, "BooleanLiteral"); + } + + parseNullLiteral() { + const node = this.startNode(); + this.next(); + return this.finishNode(node, "NullLiteral"); + } + + // https://tc39.es/ecma262/#prod-CoverParenthesizedExpressionAndArrowParameterList + parseParenAndDistinguishExpression(canBeArrow: boolean): N.Expression { + const startPos = this.state.start; + const startLoc = this.state.startLoc; + + let val; + this.next(); // eat `(` + this.expressionScope.enter(newArrowHeadScope()); + + const oldMaybeInArrowParameters = this.state.maybeInArrowParameters; + const oldInFSharpPipelineDirectBody = this.state.inFSharpPipelineDirectBody; + this.state.maybeInArrowParameters = true; + this.state.inFSharpPipelineDirectBody = false; + + const innerStartPos = this.state.start; + const innerStartLoc = this.state.startLoc; + const exprList = []; + const refExpressionErrors = new ExpressionErrors(); + let first = true; + let spreadStart; + let optionalCommaStart; + + while (!this.match(tt.parenR)) { + if (first) { + first = false; + } else { + this.expect( + tt.comma, + refExpressionErrors.optionalParameters === -1 + ? null + : refExpressionErrors.optionalParameters + ); + if (this.match(tt.parenR)) { + optionalCommaStart = this.state.start; + break; + } + } + + if (this.match(tt.ellipsis)) { + const spreadNodeStartPos = this.state.start; + const spreadNodeStartLoc = this.state.startLoc; + spreadStart = this.state.start; + exprList.push( + this.parseParenItem( + this.parseRestBinding(), + spreadNodeStartPos, + spreadNodeStartLoc + ) + ); + + this.checkCommaAfterRest(charCodes.rightParenthesis); + + break; + } else { + exprList.push( + this.parseMaybeAssignAllowIn(refExpressionErrors, this.parseParenItem) + ); + } + } + + const innerEndPos = this.state.lastTokEnd; + const innerEndLoc = this.state.lastTokEndLoc; + this.expect(tt.parenR); + + this.state.maybeInArrowParameters = oldMaybeInArrowParameters; + this.state.inFSharpPipelineDirectBody = oldInFSharpPipelineDirectBody; + + let arrowNode = this.startNodeAt(startPos, startLoc); + if ( + canBeArrow && + this.shouldParseArrow(exprList) && + (arrowNode = this.parseArrow(arrowNode)) + ) { + this.expressionScope.validateAsPattern(); + this.expressionScope.exit(); + this.parseArrowExpression(arrowNode, exprList, false); + return arrowNode; + } + this.expressionScope.exit(); + + if (!exprList.length) { + this.unexpected(this.state.lastTokStart); + } + if (optionalCommaStart) this.unexpected(optionalCommaStart); + if (spreadStart) this.unexpected(spreadStart); + this.checkExpressionErrors(refExpressionErrors, true); + + this.toReferencedListDeep(exprList, /* isParenthesizedExpr */ true); + if (exprList.length > 1) { + val = this.startNodeAt(innerStartPos, innerStartLoc); + val.expressions = exprList; + // finish node at current location so it can pick up comments after `)` + this.finishNode(val, "SequenceExpression"); + this.resetEndLocation(val, innerEndPos, innerEndLoc); + } else { + val = exprList[0]; + } + + if (!this.options.createParenthesizedExpressions) { + this.addExtra(val, "parenthesized", true); + this.addExtra(val, "parenStart", startPos); + return val; + } + + const parenExpression = this.startNodeAt(startPos, startLoc); + parenExpression.expression = val; + this.finishNode(parenExpression, "ParenthesizedExpression"); + return parenExpression; + } + + // eslint-disable-next-line no-unused-vars -- `params` is used in typescript plugin + shouldParseArrow(params: Array): boolean { + return !this.canInsertSemicolon(); + } + + parseArrow(node: N.ArrowFunctionExpression): ?N.ArrowFunctionExpression { + if (this.eat(tt.arrow)) { + return node; + } + } + + parseParenItem( + node: N.Expression, + startPos: number, // eslint-disable-line no-unused-vars + startLoc: Position // eslint-disable-line no-unused-vars + ): N.Expression { + return node; + } + + parseNewOrNewTarget(): N.NewExpression | N.MetaProperty { + const node = this.startNode(); + this.next(); + if (this.match(tt.dot)) { + // https://tc39.es/ecma262/#prod-NewTarget + const meta = this.createIdentifier(this.startNodeAtNode(node), "new"); + this.next(); + const metaProp = this.parseMetaProperty(node, meta, "target"); + + if (!this.scope.inNonArrowFunction && !this.scope.inClass) { + this.raise(metaProp.start, Errors.UnexpectedNewTarget); + } + + return metaProp; + } + + return this.parseNew(node); + } + + // New's precedence is slightly tricky. It must allow its argument to + // be a `[]` or dot subscript expression, but not a call — at least, + // not without wrapping it in parentheses. Thus, it uses the noCalls + // argument to parseSubscripts to prevent it from consuming the + // argument list. + // https://tc39.es/ecma262/#prod-NewExpression + parseNew(node: N.Expression): N.NewExpression { + node.callee = this.parseNoCallExpr(); + if (node.callee.type === "Import") { + this.raise(node.callee.start, Errors.ImportCallNotNewExpression); + } else if (this.isOptionalChain(node.callee)) { + this.raise(this.state.lastTokEnd, Errors.OptionalChainingNoNew); + } else if (this.eat(tt.questionDot)) { + this.raise(this.state.start, Errors.OptionalChainingNoNew); + } + + this.parseNewArguments(node); + return this.finishNode(node, "NewExpression"); + } + + parseNewArguments(node: N.NewExpression): void { + if (this.eat(tt.parenL)) { + const args = this.parseExprList(tt.parenR); + this.toReferencedList(args); + // $FlowFixMe (parseExprList should be all non-null in this case) + node.arguments = args; + } else { + node.arguments = []; + } + } + + // Parse template expression. + + parseTemplateElement(isTagged: boolean): N.TemplateElement { + const elem = this.startNode(); + if (this.state.value === null) { + if (!isTagged) { + this.raise(this.state.start + 1, Errors.InvalidEscapeSequenceTemplate); + } + } + elem.value = { + raw: this.input + .slice(this.state.start, this.state.end) + .replace(/\r\n?/g, "\n"), + cooked: this.state.value, + }; + this.next(); + elem.tail = this.match(tt.backQuote); + return this.finishNode(elem, "TemplateElement"); + } + + // https://tc39.es/ecma262/#prod-TemplateLiteral + parseTemplate(isTagged: boolean): N.TemplateLiteral { + const node = this.startNode(); + this.next(); + node.expressions = []; + let curElt = this.parseTemplateElement(isTagged); + node.quasis = [curElt]; + while (!curElt.tail) { + this.expect(tt.dollarBraceL); + node.expressions.push(this.parseTemplateSubstitution()); + this.expect(tt.braceR); + node.quasis.push((curElt = this.parseTemplateElement(isTagged))); + } + this.next(); + return this.finishNode(node, "TemplateLiteral"); + } + + // This is overwritten by the TypeScript plugin to parse template types + parseTemplateSubstitution(): N.Expression { + return this.parseExpression(); + } + + // Parse an object literal, binding pattern, or record. + + parseObjectLike( + close: TokenType, + isPattern: boolean, + isRecord?: ?boolean, + refExpressionErrors?: ?ExpressionErrors + ): T { + if (isRecord) { + this.expectPlugin("recordAndTuple"); + } + const oldInFSharpPipelineDirectBody = this.state.inFSharpPipelineDirectBody; + this.state.inFSharpPipelineDirectBody = false; + const propHash: any = Object.create(null); + let first = true; + const node = this.startNode(); + + node.properties = []; + this.next(); + + while (!this.match(close)) { + if (first) { + first = false; + } else { + this.expect(tt.comma); + if (this.match(close)) { + this.addExtra(node, "trailingComma", this.state.lastTokStart); + break; + } + } + + const prop = this.parsePropertyDefinition(isPattern, refExpressionErrors); + if (!isPattern) { + // $FlowIgnore RestElement will never be returned if !isPattern + this.checkProto(prop, isRecord, propHash, refExpressionErrors); + } + + if ( + isRecord && + !this.isObjectProperty(prop) && + prop.type !== "SpreadElement" + ) { + this.raise(prop.start, Errors.InvalidRecordProperty); + } + + // $FlowIgnore + if (prop.shorthand) { + this.addExtra(prop, "shorthand", true); + } + + node.properties.push(prop); + } + + this.next(); + + this.state.inFSharpPipelineDirectBody = oldInFSharpPipelineDirectBody; + let type = "ObjectExpression"; + if (isPattern) { + type = "ObjectPattern"; + } else if (isRecord) { + type = "RecordExpression"; + } + return this.finishNode(node, type); + } + + // Check grammar production: + // IdentifierName *_opt PropertyName + // It is used in `parsePropertyDefinition` to detect AsyncMethod and Accessors + maybeAsyncOrAccessorProp(prop: N.ObjectProperty): boolean { + return ( + !prop.computed && + prop.key.type === "Identifier" && + (this.isLiteralPropertyName() || + this.match(tt.bracketL) || + this.match(tt.star)) + ); + } + + // https://tc39.es/ecma262/#prod-PropertyDefinition + parsePropertyDefinition( + isPattern: boolean, + refExpressionErrors?: ?ExpressionErrors + ): N.ObjectMember | N.SpreadElement | N.RestElement { + let decorators = []; + if (this.match(tt.at)) { + if (this.hasPlugin("decorators")) { + this.raise(this.state.start, Errors.UnsupportedPropertyDecorator); + } + + // we needn't check if decorators (stage 0) plugin is enabled since it's checked by + // the call to this.parseDecorator + while (this.match(tt.at)) { + decorators.push(this.parseDecorator()); + } + } + + const prop = this.startNode(); + let isGenerator = false; + let isAsync = false; + let isAccessor = false; + let startPos; + let startLoc; + + if (this.match(tt.ellipsis)) { + if (decorators.length) this.unexpected(); + if (isPattern) { + this.next(); + // Don't use parseRestBinding() as we only allow Identifier here. + prop.argument = this.parseIdentifier(); + this.checkCommaAfterRest(charCodes.rightCurlyBrace); + return this.finishNode(prop, "RestElement"); + } + + return this.parseSpread(); + } + + if (decorators.length) { + prop.decorators = decorators; + decorators = []; + } + + prop.method = false; + + if (isPattern || refExpressionErrors) { + startPos = this.state.start; + startLoc = this.state.startLoc; + } + + if (!isPattern) { + isGenerator = this.eat(tt.star); + } + + const containsEsc = this.state.containsEsc; + const key = this.parsePropertyName(prop, /* isPrivateNameAllowed */ false); + + if ( + !isPattern && + !isGenerator && + !containsEsc && + this.maybeAsyncOrAccessorProp(prop) + ) { + const keyName = key.name; + // https://tc39.es/ecma262/#prod-AsyncMethod + // https://tc39.es/ecma262/#prod-AsyncGeneratorMethod + if (keyName === "async" && !this.hasPrecedingLineBreak()) { + isAsync = true; + this.resetPreviousNodeTrailingComments(key); + isGenerator = this.eat(tt.star); + this.parsePropertyName(prop, /* isPrivateNameAllowed */ false); + } + // get PropertyName[?Yield, ?Await] () { FunctionBody[~Yield, ~Await] } + // set PropertyName[?Yield, ?Await] ( PropertySetParameterList ) { FunctionBody[~Yield, ~Await] } + if (keyName === "get" || keyName === "set") { + isAccessor = true; + this.resetPreviousNodeTrailingComments(key); + prop.kind = keyName; + if (this.match(tt.star)) { + isGenerator = true; + this.raise(this.state.pos, Errors.AccessorIsGenerator, keyName); + this.next(); + } + this.parsePropertyName(prop, /* isPrivateNameAllowed */ false); + } + } + + this.parseObjPropValue( + prop, + startPos, + startLoc, + isGenerator, + isAsync, + isPattern, + isAccessor, + refExpressionErrors + ); + + return prop; + } + + getGetterSetterExpectedParamCount( + method: N.ObjectMethod | N.ClassMethod + ): number { + return method.kind === "get" ? 0 : 1; + } + + // This exists so we can override within the ESTree plugin + getObjectOrClassMethodParams(method: N.ObjectMethod | N.ClassMethod) { + return method.params; + } + + // get methods aren't allowed to have any parameters + // set methods must have exactly 1 parameter which is not a rest parameter + checkGetterSetterParams(method: N.ObjectMethod | N.ClassMethod): void { + const paramCount = this.getGetterSetterExpectedParamCount(method); + const params = this.getObjectOrClassMethodParams(method); + + const start = method.start; + + if (params.length !== paramCount) { + if (method.kind === "get") { + this.raise(start, Errors.BadGetterArity); + } else { + this.raise(start, Errors.BadSetterArity); + } + } + + if ( + method.kind === "set" && + params[params.length - 1]?.type === "RestElement" + ) { + this.raise(start, Errors.BadSetterRestParameter); + } + } + + // https://tc39.es/ecma262/#prod-MethodDefinition + parseObjectMethod( + prop: N.ObjectMethod, + isGenerator: boolean, + isAsync: boolean, + isPattern: boolean, + isAccessor: boolean + ): ?N.ObjectMethod { + if (isAccessor) { + // isAccessor implies isAsync: false, isPattern: false, isGenerator: false + this.parseMethod( + prop, + // This _should_ be false, but with error recovery, we allow it to be + // set for informational purposes + isGenerator, + /* isAsync */ false, + /* isConstructor */ false, + false, + "ObjectMethod" + ); + this.checkGetterSetterParams(prop); + return prop; + } + + if (isAsync || isGenerator || this.match(tt.parenL)) { + if (isPattern) this.unexpected(); + prop.kind = "method"; + prop.method = true; + return this.parseMethod( + prop, + isGenerator, + isAsync, + /* isConstructor */ false, + false, + "ObjectMethod" + ); + } + } + + // if `isPattern` is true, parse https://tc39.es/ecma262/#prod-BindingProperty + // else https://tc39.es/ecma262/#prod-PropertyDefinition + parseObjectProperty( + prop: N.ObjectProperty, + startPos: ?number, + startLoc: ?Position, + isPattern: boolean, + refExpressionErrors: ?ExpressionErrors + ): ?N.ObjectProperty { + prop.shorthand = false; + + if (this.eat(tt.colon)) { + prop.value = isPattern + ? this.parseMaybeDefault(this.state.start, this.state.startLoc) + : this.parseMaybeAssignAllowIn(refExpressionErrors); + + return this.finishNode(prop, "ObjectProperty"); + } + + if (!prop.computed && prop.key.type === "Identifier") { + // PropertyDefinition: + // IdentifierReference + // CoveredInitializedName + // Note: `{ eval } = {}` will be checked in `checkLVal` later. + this.checkReservedWord(prop.key.name, prop.key.start, true, false); + + if (isPattern) { + prop.value = this.parseMaybeDefault( + startPos, + startLoc, + cloneIdentifier(prop.key) + ); + } else if (this.match(tt.eq) && refExpressionErrors) { + if (refExpressionErrors.shorthandAssign === -1) { + refExpressionErrors.shorthandAssign = this.state.start; + } + prop.value = this.parseMaybeDefault( + startPos, + startLoc, + cloneIdentifier(prop.key) + ); + } else { + prop.value = cloneIdentifier(prop.key); + } + prop.shorthand = true; + + return this.finishNode(prop, "ObjectProperty"); + } + } + + parseObjPropValue( + prop: any, + startPos: ?number, + startLoc: ?Position, + isGenerator: boolean, + isAsync: boolean, + isPattern: boolean, + isAccessor: boolean, + refExpressionErrors?: ?ExpressionErrors + ): void { + const node = + this.parseObjectMethod( + prop, + isGenerator, + isAsync, + isPattern, + isAccessor + ) || + this.parseObjectProperty( + prop, + startPos, + startLoc, + isPattern, + refExpressionErrors + ); + + if (!node) this.unexpected(); + + // $FlowFixMe + return node; + } + + parsePropertyName( + prop: N.ObjectOrClassMember | N.ClassMember | N.TsNamedTypeElementBase, + isPrivateNameAllowed: boolean + ): N.Expression | N.Identifier { + if (this.eat(tt.bracketL)) { + (prop: $FlowSubtype).computed = true; + prop.key = this.parseMaybeAssignAllowIn(); + this.expect(tt.bracketR); + } else { + const oldInPropertyName = this.state.inPropertyName; + this.state.inPropertyName = true; + // We check if it's valid for it to be a private name when we push it. + const type = this.state.type; + (prop: $FlowFixMe).key = + type === tt.num || + type === tt.string || + type === tt.bigint || + type === tt.decimal + ? this.parseExprAtom() + : this.parseMaybePrivateName(isPrivateNameAllowed); + + if (type !== tt.privateName) { + // ClassPrivateProperty is never computed, so we don't assign in that case. + prop.computed = false; + } + + this.state.inPropertyName = oldInPropertyName; + } + + return prop.key; + } + + // Initialize empty function node. + + initFunction(node: N.BodilessFunctionOrMethodBase, isAsync: ?boolean): void { + node.id = null; + node.generator = false; + node.async = !!isAsync; + } + + // Parse object or class method. + + parseMethod( + node: T, + isGenerator: boolean, + isAsync: boolean, + isConstructor: boolean, + allowDirectSuper: boolean, + type: string, + inClassScope: boolean = false + ): T { + this.initFunction(node, isAsync); + node.generator = !!isGenerator; + const allowModifiers = isConstructor; // For TypeScript parameter properties + this.scope.enter( + SCOPE_FUNCTION | + SCOPE_SUPER | + (inClassScope ? SCOPE_CLASS : 0) | + (allowDirectSuper ? SCOPE_DIRECT_SUPER : 0) + ); + this.prodParam.enter(functionFlags(isAsync, node.generator)); + this.parseFunctionParams((node: any), allowModifiers); + this.parseFunctionBodyAndFinish(node, type, true); + this.prodParam.exit(); + this.scope.exit(); + + return node; + } + + // parse an array literal or tuple literal + // https://tc39.es/ecma262/#prod-ArrayLiteral + // https://tc39.es/proposal-record-tuple/#prod-TupleLiteral + parseArrayLike( + close: TokenType, + canBePattern: boolean, + isTuple: boolean, + refExpressionErrors: ?ExpressionErrors + ): N.ArrayExpression | N.TupleExpression { + if (isTuple) { + this.expectPlugin("recordAndTuple"); + } + const oldInFSharpPipelineDirectBody = this.state.inFSharpPipelineDirectBody; + this.state.inFSharpPipelineDirectBody = false; + const node = this.startNode(); + this.next(); + node.elements = this.parseExprList( + close, + /* allowEmpty */ !isTuple, + refExpressionErrors, + node + ); + this.state.inFSharpPipelineDirectBody = oldInFSharpPipelineDirectBody; + return this.finishNode( + node, + isTuple ? "TupleExpression" : "ArrayExpression" + ); + } + + // Parse arrow function expression. + // If the parameters are provided, they will be converted to an + // assignable list. + parseArrowExpression( + node: N.ArrowFunctionExpression, + params: ?(N.Expression[]), + isAsync: boolean, + trailingCommaPos: ?number + ): N.ArrowFunctionExpression { + this.scope.enter(SCOPE_FUNCTION | SCOPE_ARROW); + let flags = functionFlags(isAsync, false); + // ConciseBody and AsyncConciseBody inherit [In] + if (!this.match(tt.bracketL) && this.prodParam.hasIn) { + flags |= PARAM_IN; + } + this.prodParam.enter(flags); + this.initFunction(node, isAsync); + const oldMaybeInArrowParameters = this.state.maybeInArrowParameters; + + if (params) { + this.state.maybeInArrowParameters = true; + this.setArrowFunctionParameters(node, params, trailingCommaPos); + } + this.state.maybeInArrowParameters = false; + this.parseFunctionBody(node, true); + + this.prodParam.exit(); + this.scope.exit(); + this.state.maybeInArrowParameters = oldMaybeInArrowParameters; + + return this.finishNode(node, "ArrowFunctionExpression"); + } + + setArrowFunctionParameters( + node: N.ArrowFunctionExpression, + params: N.Expression[], + trailingCommaPos: ?number + ): void { + node.params = this.toAssignableList(params, trailingCommaPos, false); + } + + parseFunctionBodyAndFinish( + node: N.BodilessFunctionOrMethodBase, + type: string, + isMethod?: boolean = false + ): void { + // $FlowIgnore (node is not bodiless if we get here) + this.parseFunctionBody(node, false, isMethod); + this.finishNode(node, type); + } + + // Parse function body and check parameters. + parseFunctionBody( + node: N.Function, + allowExpression: ?boolean, + isMethod?: boolean = false + ): void { + const isExpression = allowExpression && !this.match(tt.braceL); + this.expressionScope.enter(newExpressionScope()); + + if (isExpression) { + // https://tc39.es/ecma262/#prod-ExpressionBody + node.body = this.parseMaybeAssign(); + this.checkParams(node, false, allowExpression, false); + } else { + const oldStrict = this.state.strict; + // Start a new scope with regard to labels + // flag (restore them to their old value afterwards). + const oldLabels = this.state.labels; + this.state.labels = []; + + // FunctionBody[Yield, Await]: + // StatementList[?Yield, ?Await, +Return] opt + this.prodParam.enter(this.prodParam.currentFlags() | PARAM_RETURN); + node.body = this.parseBlock( + true, + false, + // Strict mode function checks after we parse the statements in the function body. + (hasStrictModeDirective: boolean) => { + const nonSimple = !this.isSimpleParamList(node.params); + + if (hasStrictModeDirective && nonSimple) { + // This logic is here to align the error location with the ESTree plugin. + const errorPos = + // $FlowIgnore + (node.kind === "method" || node.kind === "constructor") && + // $FlowIgnore + !!node.key + ? node.key.end + : node.start; + this.raise(errorPos, Errors.IllegalLanguageModeDirective); + } + + const strictModeChanged = !oldStrict && this.state.strict; + + // Add the params to varDeclaredNames to ensure that an error is thrown + // if a let/const declaration in the function clashes with one of the params. + this.checkParams( + node, + !this.state.strict && !allowExpression && !isMethod && !nonSimple, + allowExpression, + strictModeChanged + ); + + // Ensure the function name isn't a forbidden identifier in strict mode, e.g. 'eval' + if (this.state.strict && node.id) { + this.checkLVal( + node.id, + "function name", + BIND_OUTSIDE, + undefined, + undefined, + strictModeChanged + ); + } + } + ); + this.prodParam.exit(); + this.expressionScope.exit(); + this.state.labels = oldLabels; + } + } + + isSimpleParamList( + params: $ReadOnlyArray + ): boolean { + for (let i = 0, len = params.length; i < len; i++) { + if (params[i].type !== "Identifier") return false; + } + return true; + } + + checkParams( + node: N.Function, + allowDuplicates: boolean, + // eslint-disable-next-line no-unused-vars + isArrowFunction: ?boolean, + strictModeChanged?: boolean = true + ): void { + const checkClashes = new Set(); + for (const param of node.params) { + this.checkLVal( + param, + "function parameter list", + BIND_VAR, + allowDuplicates ? null : checkClashes, + undefined, + strictModeChanged + ); + } + } + + // Parses a comma-separated list of expressions, and returns them as + // an array. `close` is the token type that ends the list, and + // `allowEmpty` can be turned on to allow subsequent commas with + // nothing in between them to be parsed as `null` (which is needed + // for array literals). + + parseExprList( + close: TokenType, + allowEmpty?: boolean, + refExpressionErrors?: ?ExpressionErrors, + nodeForExtra?: ?N.Node + ): $ReadOnlyArray { + const elts = []; + let first = true; + + while (!this.eat(close)) { + if (first) { + first = false; + } else { + this.expect(tt.comma); + if (this.match(close)) { + if (nodeForExtra) { + this.addExtra( + nodeForExtra, + "trailingComma", + this.state.lastTokStart + ); + } + this.next(); + break; + } + } + + elts.push(this.parseExprListItem(allowEmpty, refExpressionErrors)); + } + return elts; + } + + parseExprListItem( + allowEmpty: ?boolean, + refExpressionErrors?: ?ExpressionErrors, + allowPlaceholder: ?boolean + ): ?N.Expression { + let elt; + if (this.match(tt.comma)) { + if (!allowEmpty) { + this.raise(this.state.pos, Errors.UnexpectedToken, ","); + } + elt = null; + } else if (this.match(tt.ellipsis)) { + const spreadNodeStartPos = this.state.start; + const spreadNodeStartLoc = this.state.startLoc; + + elt = this.parseParenItem( + this.parseSpread(refExpressionErrors), + spreadNodeStartPos, + spreadNodeStartLoc + ); + } else if (this.match(tt.question)) { + this.expectPlugin("partialApplication"); + if (!allowPlaceholder) { + this.raise(this.state.start, Errors.UnexpectedArgumentPlaceholder); + } + const node = this.startNode(); + this.next(); + elt = this.finishNode(node, "ArgumentPlaceholder"); + } else { + elt = this.parseMaybeAssignAllowIn( + refExpressionErrors, + this.parseParenItem + ); + } + return elt; + } + + // Parse the next token as an identifier. If `liberal` is true (used + // when parsing properties), it will also convert keywords into + // identifiers. + // This shouldn't be used to parse the keywords of meta properties, since they + // are not identifiers and cannot contain escape sequences. + + parseIdentifier(liberal?: boolean): N.Identifier { + const node = this.startNode(); + const name = this.parseIdentifierName(node.start, liberal); + + return this.createIdentifier(node, name); + } + + createIdentifier(node: N.Identifier, name: string): N.Identifier { + node.name = name; + node.loc.identifierName = name; + + return this.finishNode(node, "Identifier"); + } + + parseIdentifierName(pos: number, liberal?: boolean): string { + let name: string; + + const { start, type } = this.state; + + if (tokenIsKeywordOrIdentifier(type)) { + name = this.state.value; + } else { + throw this.unexpected(); + } + + if (liberal) { + // If the current token is not used as a keyword, set its type to "tt.name". + // This will prevent this.next() from throwing about unexpected escapes. + this.state.type = tt.name; + } else { + this.checkReservedWord(name, start, tokenIsKeyword(type), false); + } + + this.next(); + + return name; + } + + checkReservedWord( + word: string, + startLoc: number, + checkKeywords: boolean, + isBinding: boolean + ): void { + // Every JavaScript reserved word is 10 characters or less. + if (word.length > 10) { + return; + } + // Most identifiers are not reservedWord-like, they don't need special + // treatments afterward, which very likely ends up throwing errors + if (!canBeReservedWord(word)) { + return; + } + + if (word === "yield") { + if (this.prodParam.hasYield) { + this.raise(startLoc, Errors.YieldBindingIdentifier); + return; + } + } else if (word === "await") { + if (this.prodParam.hasAwait) { + this.raise(startLoc, Errors.AwaitBindingIdentifier); + return; + } else if (this.scope.inStaticBlock) { + this.raise(startLoc, Errors.AwaitBindingIdentifierInStaticBlock); + return; + } else { + this.expressionScope.recordAsyncArrowParametersError( + startLoc, + Errors.AwaitBindingIdentifier + ); + } + } else if (word === "arguments") { + if (this.scope.inClassAndNotInNonArrowFunction) { + this.raise(startLoc, Errors.ArgumentsInClass); + return; + } + } + + if (checkKeywords && isKeyword(word)) { + this.raise(startLoc, Errors.UnexpectedKeyword, word); + return; + } + + const reservedTest = !this.state.strict + ? isReservedWord + : isBinding + ? isStrictBindReservedWord + : isStrictReservedWord; + + if (reservedTest(word, this.inModule)) { + this.raise(startLoc, Errors.UnexpectedReservedWord, word); + } + } + + isAwaitAllowed(): boolean { + if (this.prodParam.hasAwait) return true; + if (this.options.allowAwaitOutsideFunction && !this.scope.inFunction) { + return true; + } + return false; + } + + // Parses await expression inside async function. + + parseAwait(startPos: number, startLoc: Position): N.AwaitExpression { + const node = this.startNodeAt(startPos, startLoc); + + this.expressionScope.recordParameterInitializerError( + node.start, + Errors.AwaitExpressionFormalParameter + ); + + if (this.eat(tt.star)) { + this.raise(node.start, Errors.ObsoleteAwaitStar); + } + + if (!this.scope.inFunction && !this.options.allowAwaitOutsideFunction) { + if (this.isAmbiguousAwait()) { + this.ambiguousScriptDifferentAst = true; + } else { + this.sawUnambiguousESM = true; + } + } + + if (!this.state.soloAwait) { + node.argument = this.parseMaybeUnary(null, true); + } + + return this.finishNode(node, "AwaitExpression"); + } + + isAmbiguousAwait(): boolean { + return ( + this.hasPrecedingLineBreak() || + // All the following expressions are ambiguous: + // await + 0, await - 0, await ( 0 ), await [ 0 ], await / 0 /u, await `` + this.match(tt.plusMin) || + this.match(tt.parenL) || + this.match(tt.bracketL) || + this.match(tt.backQuote) || + // Sometimes the tokenizer generates tt.slash for regexps, and this is + // handler by parseExprAtom + this.match(tt.regexp) || + this.match(tt.slash) || + // This code could be parsed both as a modulo operator or as an intrinsic: + // await %x(0) + (this.hasPlugin("v8intrinsic") && this.match(tt.modulo)) + ); + } + + // Parses yield expression inside generator. + + parseYield(): N.YieldExpression { + const node = this.startNode(); + + this.expressionScope.recordParameterInitializerError( + node.start, + Errors.YieldInParameter + ); + + this.next(); + let delegating = false; + let argument = null; + if (!this.hasPrecedingLineBreak()) { + delegating = this.eat(tt.star); + switch (this.state.type) { + case tt.semi: + case tt.eof: + case tt.braceR: + case tt.parenR: + case tt.bracketR: + case tt.braceBarR: + case tt.colon: + case tt.comma: + // The above is the complete set of tokens that can + // follow an AssignmentExpression, and none of them + // can start an AssignmentExpression + if (!delegating) break; + /* fallthrough */ + default: + argument = this.parseMaybeAssign(); + } + } + node.delegate = delegating; + node.argument = argument; + return this.finishNode(node, "YieldExpression"); + } + + // Validates a pipeline (for any of the pipeline Babylon plugins) at the point + // of the infix operator `|>`. + + checkPipelineAtInfixOperator(left: N.Expression, leftStartPos: number) { + if (this.getPluginOption("pipelineOperator", "proposal") === "smart") { + if (left.type === "SequenceExpression") { + // Ensure that the pipeline head is not a comma-delimited + // sequence expression. + this.raise(leftStartPos, Errors.PipelineHeadSequenceExpression); + } + } + } + + // This helper method is to be called immediately + // after a Hack-style pipe body is parsed. + // The `startPos` is the starting position of the pipe body. + + checkHackPipeBodyEarlyErrors(startPos: number): void { + if (!this.topicReferenceWasUsedInCurrentContext()) { + // A Hack pipe body must use the topic reference at least once. + this.raise(startPos, Errors.PipeTopicUnused); + } + } + + parseSmartPipelineBodyInStyle( + childExpr: N.Expression, + startPos: number, + startLoc: Position + ): N.PipelineBody { + const bodyNode = this.startNodeAt(startPos, startLoc); + if (this.isSimpleReference(childExpr)) { + bodyNode.callee = childExpr; + return this.finishNode(bodyNode, "PipelineBareFunction"); + } else { + this.checkSmartPipeTopicBodyEarlyErrors(startPos); + bodyNode.expression = childExpr; + return this.finishNode(bodyNode, "PipelineTopicExpression"); + } + } + + isSimpleReference(expression: N.Expression): boolean { + switch (expression.type) { + case "MemberExpression": + return ( + !expression.computed && this.isSimpleReference(expression.object) + ); + case "Identifier": + return true; + default: + return false; + } + } + + // This helper method is to be called immediately + // after a topic-style smart-mix pipe body is parsed. + // The `startPos` is the starting position of the pipe body. + + checkSmartPipeTopicBodyEarlyErrors(startPos: number): void { + // If the following token is invalidly `=>`, then throw a human-friendly error + // instead of something like 'Unexpected token, expected ";"'. + // For example, `x => x |> y => #` (assuming `#` is the topic reference) + // groups into `x => (x |> y) => #`, + // and `(x |> y) => #` is an invalid arrow function. + // This is because smart-mix `|>` has tighter precedence than `=>`. + if (this.match(tt.arrow)) { + throw this.raise(this.state.start, Errors.PipelineBodyNoArrow); + } + + // A topic-style smart-mix pipe body must use the topic reference at least once. + else if (!this.topicReferenceWasUsedInCurrentContext()) { + this.raise(startPos, Errors.PipelineTopicUnused); + } + } + + // Enable topic references from outer contexts within Hack-style pipe bodies. + // The function modifies the parser's topic-context state to enable or disable + // the use of topic references. + // The function then calls a callback, then resets the parser + // to the old topic-context state that it had before the function was called. + + withTopicBindingContext(callback: () => T): T { + const outerContextTopicState = this.state.topicContext; + this.state.topicContext = { + // Enable the use of the primary topic reference. + maxNumOfResolvableTopics: 1, + // Hide the use of any topic references from outer contexts. + maxTopicIndex: null, + }; + + try { + return callback(); + } finally { + this.state.topicContext = outerContextTopicState; + } + } + + // This helper method is used only with the deprecated smart-mix pipe proposal. + // Disables topic references from outer contexts within syntax constructs + // such as the bodies of iteration statements. + // The function modifies the parser's topic-context state to enable or disable + // the use of topic references with the smartPipelines plugin. They then run a + // callback, then they reset the parser to the old topic-context state that it + // had before the function was called. + + withSmartMixTopicForbiddingContext(callback: () => T): T { + const proposal = this.getPluginOption("pipelineOperator", "proposal"); + if (proposal === "smart") { + // Reset the parser’s topic context only if the smart-mix pipe proposal is active. + const outerContextTopicState = this.state.topicContext; + this.state.topicContext = { + // Disable the use of the primary topic reference. + maxNumOfResolvableTopics: 0, + // Hide the use of any topic references from outer contexts. + maxTopicIndex: null, + }; + + try { + return callback(); + } finally { + this.state.topicContext = outerContextTopicState; + } + } else { + // If the pipe proposal is "minimal", "fsharp", or "hack", + // or if no pipe proposal is active, + // then the callback result is returned + // without touching any extra parser state. + return callback(); + } + } + + withSoloAwaitPermittingContext(callback: () => T): T { + const outerContextSoloAwaitState = this.state.soloAwait; + this.state.soloAwait = true; + + try { + return callback(); + } finally { + this.state.soloAwait = outerContextSoloAwaitState; + } + } + + allowInAnd(callback: () => T): T { + const flags = this.prodParam.currentFlags(); + const prodParamToSet = PARAM_IN & ~flags; + if (prodParamToSet) { + this.prodParam.enter(flags | PARAM_IN); + try { + return callback(); + } finally { + this.prodParam.exit(); + } + } + return callback(); + } + + disallowInAnd(callback: () => T): T { + const flags = this.prodParam.currentFlags(); + const prodParamToClear = PARAM_IN & flags; + if (prodParamToClear) { + this.prodParam.enter(flags & ~PARAM_IN); + try { + return callback(); + } finally { + this.prodParam.exit(); + } + } + return callback(); + } + + // Register the use of a topic reference within the current + // topic-binding context. + registerTopicReference(): void { + this.state.topicContext.maxTopicIndex = 0; + } + + topicReferenceIsAllowedInCurrentContext(): boolean { + return this.state.topicContext.maxNumOfResolvableTopics >= 1; + } + + topicReferenceWasUsedInCurrentContext(): boolean { + return ( + this.state.topicContext.maxTopicIndex != null && + this.state.topicContext.maxTopicIndex >= 0 + ); + } + + parseFSharpPipelineBody(prec: number): N.Expression { + const startPos = this.state.start; + const startLoc = this.state.startLoc; + + this.state.potentialArrowAt = this.state.start; + const oldInFSharpPipelineDirectBody = this.state.inFSharpPipelineDirectBody; + this.state.inFSharpPipelineDirectBody = true; + + const ret = this.parseExprOp( + this.parseMaybeUnaryOrPrivate(), + startPos, + startLoc, + prec + ); + + this.state.inFSharpPipelineDirectBody = oldInFSharpPipelineDirectBody; + + return ret; + } + + // https://github.com/tc39/proposal-js-module-blocks + parseModuleExpression(): N.ModuleExpression { + this.expectPlugin("moduleBlocks"); + const node = this.startNode(); + this.next(); // eat "module" + this.eat(tt.braceL); + + const revertScopes = this.initializeScopes(/** inModule */ true); + this.enterInitialScopes(); + + const program = this.startNode(); + try { + node.body = this.parseProgram(program, tt.braceR, "module"); + } finally { + revertScopes(); + } + this.eat(tt.braceR); + return this.finishNode(node, "ModuleExpression"); + } +} diff --git a/benchmark/babel-traverse/enter-visitor-context-change/bench.mjs b/benchmark/babel-traverse/enter-visitor-context-change/bench.mjs new file mode 100644 index 000000000000..374aade7c7a2 --- /dev/null +++ b/benchmark/babel-traverse/enter-visitor-context-change/bench.mjs @@ -0,0 +1,37 @@ +import Benchmark from "benchmark"; +import baseline from "@babel-baseline/traverse"; +import current from "@babel/traverse"; +import parser from "@babel-baseline/parser"; +import { report } from "../../util.mjs"; + +const suite = new Benchmark.Suite(); +function createInput(length) { + return parser.parse(";".repeat(length)); +} +function benchCases(name, implementation, options) { + for (const length of [256, 512, 1024, 2048]) { + const input = createInput(length); + suite.add(`${name} ${length} empty statements`, () => { + implementation(input, options); + }); + } +} + +benchCases("baseline", baseline.default, { + enter() {}, +}); +benchCases("baseline mutating context", baseline.default, { + enter(path) { + path.context = undefined; + }, +}); +benchCases("current", current.default, { + enter() {}, +}); +benchCases("current mutating context", current.default, { + enter(path) { + path.context = undefined; + }, +}); + +suite.on("cycle", report).run(); diff --git a/benchmark/package.json b/benchmark/package.json index b990a397f06b..52d46e582aad 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -3,12 +3,18 @@ "private": true, "type": "module", "devDependencies": { + "@babel-baseline/core": "npm:@babel/core@7.15.8", "@babel-baseline/generator": "npm:@babel/generator@7.14.5", "@babel-baseline/helper-validator-identifier": "npm:@babel/helper-validator-identifier@7.10.4", "@babel-baseline/parser": "npm:@babel/parser@7.14.8", + "@babel-baseline/traverse": "npm:@babel/traverse@7.15.4", + "@babel/core": "workspace:*", "@babel/generator": "workspace:*", "@babel/helper-validator-identifier": "workspace:*", "@babel/parser": "workspace:*", + "@babel/preset-env": "workspace:*", + "@babel/preset-flow": "workspace:*", + "@babel/traverse": "workspace:*", "benchmark": "^2.1.4" }, "version": "7.15.0" diff --git a/packages/babel-plugin-proposal-async-generator-functions/test/fixtures/regression/13801/input.js b/packages/babel-plugin-proposal-async-generator-functions/test/fixtures/regression/13801/input.js new file mode 100644 index 000000000000..4b27e8a45091 --- /dev/null +++ b/packages/babel-plugin-proposal-async-generator-functions/test/fixtures/regression/13801/input.js @@ -0,0 +1,11 @@ +async function main() { + async () => { + // IIFE: required for babel to crash + for await (const string of async_iterable) { + // for await: required for babel to crash + console.log(string); + } + }; + + const [one] = [1]; // array destructuring: required for babel to crash +} diff --git a/packages/babel-plugin-proposal-async-generator-functions/test/fixtures/regression/13801/options.json b/packages/babel-plugin-proposal-async-generator-functions/test/fixtures/regression/13801/options.json new file mode 100644 index 000000000000..c6c52d46575c --- /dev/null +++ b/packages/babel-plugin-proposal-async-generator-functions/test/fixtures/regression/13801/options.json @@ -0,0 +1,8 @@ +{ + "plugins": [ + "transform-destructuring", + "transform-regenerator", + "proposal-async-generator-functions" + ], + "externalHelpers": false +} diff --git a/packages/babel-plugin-proposal-async-generator-functions/test/fixtures/regression/13801/output.js b/packages/babel-plugin-proposal-async-generator-functions/test/fixtures/regression/13801/output.js new file mode 100644 index 000000000000..e4528efb9cb4 --- /dev/null +++ b/packages/babel-plugin-proposal-async-generator-functions/test/fixtures/regression/13801/output.js @@ -0,0 +1,91 @@ +function _asyncIterator(iterable) { var method; if (typeof Symbol !== "undefined") { if (Symbol.asyncIterator) method = iterable[Symbol.asyncIterator]; if (method == null && Symbol.iterator) method = iterable[Symbol.iterator]; } if (method == null) method = iterable["@@asyncIterator"]; if (method == null) method = iterable["@@iterator"]; if (method == null) throw new TypeError("Object is not async iterable"); return method.call(iterable); } + +function main() { + var one; + return regeneratorRuntime.async(function main$(_context2) { + while (1) switch (_context2.prev = _context2.next) { + case 0: + () => { + var _iteratorAbruptCompletion, _didIteratorError, _iteratorError, _iterator, _step, string; + + return regeneratorRuntime.async(function _callee$(_context) { + while (1) switch (_context.prev = _context.next) { + case 0: + // IIFE: required for babel to crash + _iteratorAbruptCompletion = false; + _didIteratorError = false; + _context.prev = 2; + _iterator = _asyncIterator(async_iterable); + + case 4: + _context.next = 6; + return regeneratorRuntime.awrap(_iterator.next()); + + case 6: + if (!(_iteratorAbruptCompletion = !(_step = _context.sent).done)) { + _context.next = 12; + break; + } + + string = _step.value; + // for await: required for babel to crash + console.log(string); + + case 9: + _iteratorAbruptCompletion = false; + _context.next = 4; + break; + + case 12: + _context.next = 18; + break; + + case 14: + _context.prev = 14; + _context.t0 = _context["catch"](2); + _didIteratorError = true; + _iteratorError = _context.t0; + + case 18: + _context.prev = 18; + _context.prev = 19; + + if (!(_iteratorAbruptCompletion && _iterator.return != null)) { + _context.next = 23; + break; + } + + _context.next = 23; + return regeneratorRuntime.awrap(_iterator.return()); + + case 23: + _context.prev = 23; + + if (!_didIteratorError) { + _context.next = 26; + break; + } + + throw _iteratorError; + + case 26: + return _context.finish(23); + + case 27: + return _context.finish(18); + + case 28: + case "end": + return _context.stop(); + } + }, null, null, [[2, 14, 18, 28], [19,, 23, 27]], Promise); + }; + + one = 1; // array destructuring: required for babel to crash + + case 2: + case "end": + return _context2.stop(); + } + }, null, null, null, Promise); +} diff --git a/packages/babel-traverse/src/path/context.ts b/packages/babel-traverse/src/path/context.ts index 0a8291bbae4c..1b9083185d2d 100644 --- a/packages/babel-traverse/src/path/context.ts +++ b/packages/babel-traverse/src/path/context.ts @@ -61,6 +61,14 @@ export function isDenylisted(this: NodePath): boolean { // TODO: Remove in Babel 8 export { isDenylisted as isBlacklisted }; +function restoreContext(path: NodePath, context: TraversalContext) { + if (path.context !== context) { + path.context = context; + path.state = context.state; + path.opts = context.opts; + } +} + export function visit(this: NodePath): boolean { if (!this.node) { return false; @@ -74,15 +82,17 @@ export function visit(this: NodePath): boolean { return false; } - // Note: We need to check "this.shouldSkip" twice because - // the visitor can set it to true. Usually .shouldSkip is false + const currentContext = this.context; + // Note: We need to check "this.shouldSkip" first because + // another visitor can set it to true. Usually .shouldSkip is false // before calling the enter visitor, but it can be true in case of // a requeued node (e.g. by .replaceWith()) that is then marked // with .skip(). - if (this.shouldSkip || this.call("enter") || this.shouldSkip) { + if (this.shouldSkip || this.call("enter")) { this.debug("Skip..."); return this.shouldStop; } + restoreContext(this, currentContext); this.debug("Recursing into..."); traverse.node( @@ -94,6 +104,8 @@ export function visit(this: NodePath): boolean { this.skipKeys, ); + restoreContext(this, currentContext); + this.call("exit"); return this.shouldStop; diff --git a/packages/babel-traverse/test/traverse.js b/packages/babel-traverse/test/traverse.js index 34c5270d509e..10afa2d72a05 100644 --- a/packages/babel-traverse/test/traverse.js +++ b/packages/babel-traverse/test/traverse.js @@ -216,4 +216,63 @@ describe("traverse", function () { expect(visited).toBe(true); }); }); + describe("path.visit()", () => { + it("should preserve traversal context after enter hook is executed", () => { + const ast = parse("{;}"); + // The test initiates a sub-traverse from program. When the `enter` hook of BlockStatement + // is called, the unshiftContainer will change the traversal context of the BlockStatement + // to the one of Program which has an EmptyStatement visitor. If the traversal context + // is not restored after the `enter` hook is executed, the `EmptyStatement` visitor will + // be run twice: one in the sub-traverse and the other in the top level traverse. + let emptyStatementVisitedCounter = 0; + traverse(ast, { + noScope: true, + Program(path) { + path.traverse({ + noScope: true, + BlockStatement: { + enter(path) { + path.parentPath.unshiftContainer("body", [t.numericLiteral(0)]); + }, + }, + }); + }, + EmptyStatement() { + ++emptyStatementVisitedCounter; + }, + }); + expect(emptyStatementVisitedCounter).toBe(1); + }); + it("should preserve traversal context after visitor is executed", () => { + const ast = parse("{;}"); + // The test initiates a sub-traverse from program. During the BlockStatement is traversed, + // the EmptyStatement visitor will be called and the unshiftContainer will change the + // traversal context of the BlockStatement to that of Program which has an EmptyStatement + // visitor. If the traversal context is not restored after `enter` hook is executed, + // the `BlockStatement:exit` visitor will be run twice: one in the sub-traverse and the other + // in the top level traverse. + let blockStatementVisitedCounter = 0; + traverse(ast, { + noScope: true, + Program(path) { + path.traverse({ + noScope: true, + EmptyStatement: { + enter(path) { + path.parentPath.parentPath.unshiftContainer("body", [ + t.numericLiteral(0), + ]); + }, + }, + }); + }, + BlockStatement: { + exit() { + ++blockStatementVisitedCounter; + }, + }, + }); + expect(blockStatementVisitedCounter).toBe(1); + }); + }); }); diff --git a/yarn.lock b/yarn.lock index 25f18dac9db3..de8d778b1efb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,6 +5,29 @@ __metadata: version: 4 cacheKey: 8 +"@babel-baseline/core@npm:@babel/core@7.15.8, @babel/core@npm:7.15.8, @babel/core@npm:^7.1.0, @babel/core@npm:^7.15.0, @babel/core@npm:^7.7.2, @babel/core@npm:^7.7.5": + version: 7.15.8 + resolution: "@babel/core@npm:7.15.8" + dependencies: + "@babel/code-frame": ^7.15.8 + "@babel/generator": ^7.15.8 + "@babel/helper-compilation-targets": ^7.15.4 + "@babel/helper-module-transforms": ^7.15.8 + "@babel/helpers": ^7.15.4 + "@babel/parser": ^7.15.8 + "@babel/template": ^7.15.4 + "@babel/traverse": ^7.15.4 + "@babel/types": ^7.15.6 + convert-source-map: ^1.7.0 + debug: ^4.1.0 + gensync: ^1.0.0-beta.2 + json5: ^2.1.2 + semver: ^6.3.0 + source-map: ^0.5.0 + checksum: 61e5050580a2808344f23161c971e917fe711a546e3afa4d022be4ec5325f8bdf559cc9afd962e39ecc3643d9cdcbbac7abc7b8dd05330020475317564c8d290 + languageName: node + linkType: hard + "@babel-baseline/generator@npm:@babel/generator@7.14.5": version: 7.14.5 resolution: "@babel/generator@npm:7.14.5" @@ -32,6 +55,23 @@ __metadata: languageName: node linkType: hard +"@babel-baseline/traverse@npm:@babel/traverse@7.15.4, @babel/traverse@npm:^7.0.0, @babel/traverse@npm:^7.1.0, @babel/traverse@npm:^7.12.9, @babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.14.5, @babel/traverse@npm:^7.15.4, @babel/traverse@npm:^7.7.2": + version: 7.15.4 + resolution: "@babel/traverse@npm:7.15.4" + dependencies: + "@babel/code-frame": ^7.14.5 + "@babel/generator": ^7.15.4 + "@babel/helper-function-name": ^7.15.4 + "@babel/helper-hoist-variables": ^7.15.4 + "@babel/helper-split-export-declaration": ^7.15.4 + "@babel/parser": ^7.15.4 + "@babel/types": ^7.15.4 + debug: ^4.1.0 + globals: ^11.1.0 + checksum: 831506a92c8ed76dc60504de37663bf5a553d7b1b009a94defc082cddb6c380c5487a1aa9438bcd7b9891a2a72758a63e4f878154aa70699d09b388b1445d774 + languageName: node + linkType: hard + "@babel-internal/runtime-integration-rollup@workspace:test/runtime-integration/rollup": version: 0.0.0-use.local resolution: "@babel-internal/runtime-integration-rollup@workspace:test/runtime-integration/rollup" @@ -85,12 +125,18 @@ __metadata: version: 0.0.0-use.local resolution: "@babel/benchmark@workspace:benchmark" dependencies: + "@babel-baseline/core": "npm:@babel/core@7.15.8" "@babel-baseline/generator": "npm:@babel/generator@7.14.5" "@babel-baseline/helper-validator-identifier": "npm:@babel/helper-validator-identifier@7.10.4" "@babel-baseline/parser": "npm:@babel/parser@7.14.8" + "@babel-baseline/traverse": "npm:@babel/traverse@7.15.4" + "@babel/core": "workspace:*" "@babel/generator": "workspace:*" "@babel/helper-validator-identifier": "workspace:*" "@babel/parser": "workspace:*" + "@babel/preset-env": "workspace:*" + "@babel/preset-flow": "workspace:*" + "@babel/traverse": "workspace:*" benchmark: ^2.1.4 languageName: unknown linkType: soft @@ -160,12 +206,12 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/code-frame@npm:7.14.5" +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.14.5, @babel/code-frame@npm:^7.15.8": + version: 7.15.8 + resolution: "@babel/code-frame@npm:7.15.8" dependencies: "@babel/highlight": ^7.14.5 - checksum: 0adbe4f8d91586f764f524e57631f582ab988b2ef504391a5d89db29bfaaf7c67c237798ed4a249b6a2d7135852cf94d3d07ce6b9739dd1df1f271d5ed069565 + checksum: d75950f0e0925b33ab5e870079134509c13bcdbf96c8bf4d0dea91606775bc044258c762104ab20882fda3b07cbff24176ed77dfb57af5a901bde33ddfe690bb languageName: node linkType: hard @@ -221,29 +267,6 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:7.15.0, @babel/core@npm:^7.1.0, @babel/core@npm:^7.15.0, @babel/core@npm:^7.7.2, @babel/core@npm:^7.7.5": - version: 7.15.0 - resolution: "@babel/core@npm:7.15.0" - dependencies: - "@babel/code-frame": ^7.14.5 - "@babel/generator": ^7.15.0 - "@babel/helper-compilation-targets": ^7.15.0 - "@babel/helper-module-transforms": ^7.15.0 - "@babel/helpers": ^7.14.8 - "@babel/parser": ^7.15.0 - "@babel/template": ^7.14.5 - "@babel/traverse": ^7.15.0 - "@babel/types": ^7.15.0 - convert-source-map: ^1.7.0 - debug: ^4.1.0 - gensync: ^1.0.0-beta.2 - json5: ^2.1.2 - semver: ^6.3.0 - source-map: ^0.5.0 - checksum: 6f7ac97d2d2eebe62a431ce55b37753aa443b762da0524640caa2f7d4417750f8e21f3eb30d62f25e479f93dac505c868d24011b124cfa6905abebb23b44715c - languageName: node - linkType: hard - "@babel/core@workspace:*, @babel/core@workspace:^7.14.5, @babel/core@workspace:^7.15.5, @babel/core@workspace:packages/babel-core": version: 0.0.0-use.local resolution: "@babel/core@workspace:packages/babel-core" @@ -363,14 +386,14 @@ __metadata: languageName: unknown linkType: soft -"@babel/generator@npm:^7.12.5, @babel/generator@npm:^7.15.0, @babel/generator@npm:^7.7.2": - version: 7.15.0 - resolution: "@babel/generator@npm:7.15.0" +"@babel/generator@npm:^7.12.5, @babel/generator@npm:^7.15.4, @babel/generator@npm:^7.15.8, @babel/generator@npm:^7.7.2": + version: 7.15.8 + resolution: "@babel/generator@npm:7.15.8" dependencies: - "@babel/types": ^7.15.0 + "@babel/types": ^7.15.6 jsesc: ^2.5.1 source-map: ^0.5.0 - checksum: ef227c4c39ab810616b1d76cf9fa7b452b3a36ae1f26d52c2a7c68edcba29d6dd3cd3e88c58f6e3969a58dadee7b73016d3cabbd6f0040ff832f686db4679628 + checksum: 3afc4d50280352125b6f1bca01fd1e4b272e1cf26248879fb38b74f8c67d7f9304c650e182623f9e7855d8154c6f05f66df81817a71de66e7dfe6670785eb344 languageName: node linkType: hard @@ -434,9 +457,9 @@ __metadata: languageName: unknown linkType: soft -"@babel/helper-compilation-targets@npm:^7.13.0, @babel/helper-compilation-targets@npm:^7.14.5, @babel/helper-compilation-targets@npm:^7.15.0": - version: 7.15.0 - resolution: "@babel/helper-compilation-targets@npm:7.15.0" +"@babel/helper-compilation-targets@npm:^7.13.0, @babel/helper-compilation-targets@npm:^7.14.5, @babel/helper-compilation-targets@npm:^7.15.0, @babel/helper-compilation-targets@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/helper-compilation-targets@npm:7.15.4" dependencies: "@babel/compat-data": ^7.15.0 "@babel/helper-validator-option": ^7.14.5 @@ -444,7 +467,7 @@ __metadata: semver: ^6.3.0 peerDependencies: "@babel/core": ^7.0.0 - checksum: 82a1f5d8041d39454fe5d7d109e32e90f5c6c13f0e87c7ac94332ac79a1fb62ab135b2f8ceba07ba307bb0db792c1f64796aec68bb258a13aa69a56ee65e2427 + checksum: a2b9767d5658da90bd79170b4b0d2987930fb6708d48428619f9f4664c47e3f9409801b76c7603446404b453c67e54682cc86840cb1c29aa06c956533ebaf5ba languageName: node linkType: hard @@ -578,14 +601,14 @@ __metadata: languageName: unknown linkType: soft -"@babel/helper-function-name@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/helper-function-name@npm:7.14.5" +"@babel/helper-function-name@npm:^7.14.5, @babel/helper-function-name@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/helper-function-name@npm:7.15.4" dependencies: - "@babel/helper-get-function-arity": ^7.14.5 - "@babel/template": ^7.14.5 - "@babel/types": ^7.14.5 - checksum: fd8ffa82f7622b6e9a6294fb3b98b42e743ab2a8e3c329367667a960b5b98b48bc5ebf8be7308981f1985b9f3c69e1a3b4a91c8944ae97c31803240da92fb3c8 + "@babel/helper-get-function-arity": ^7.15.4 + "@babel/template": ^7.15.4 + "@babel/types": ^7.15.4 + checksum: 0500e8e40753fdc25252b30609b12df8ebb997a4e5b4c2145774855c026a4338c0510fc7b819035d5f9d76cf3bd63417c0b7b58f0836a10996300f2f925c4e0f languageName: node linkType: hard @@ -599,12 +622,12 @@ __metadata: languageName: unknown linkType: soft -"@babel/helper-get-function-arity@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/helper-get-function-arity@npm:7.14.5" +"@babel/helper-get-function-arity@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/helper-get-function-arity@npm:7.15.4" dependencies: - "@babel/types": ^7.14.5 - checksum: a60779918b677a35e177bb4f46babfd54e9790587b6a4f076092a9eff2a940cbeacdeb10c94331b26abfe838769554d72293d16df897246cfccd1444e5e27cb7 + "@babel/types": ^7.15.4 + checksum: 1a3dba8700ec69b5b120401769897a1a0ca2edcf6b546659d49946dcc8b0755c4c58dd8f15739f5cf851d4ca1db76f56759897c6f5b9f76f2fef989dc4f8fd54 languageName: node linkType: hard @@ -616,12 +639,12 @@ __metadata: languageName: unknown linkType: soft -"@babel/helper-hoist-variables@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/helper-hoist-variables@npm:7.14.5" +"@babel/helper-hoist-variables@npm:^7.14.5, @babel/helper-hoist-variables@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/helper-hoist-variables@npm:7.15.4" dependencies: - "@babel/types": ^7.14.5 - checksum: 35af58eebffca10988de7003e044ce2d27212aea72ac6d2c4604137da7f1e193cc694d8d60805d0d0beaf3d990f6f2dcc2622c52e3d3148e37017a29cacf2e56 + "@babel/types": ^7.15.4 + checksum: 1a9ae0a27112b5f4e4ab91da2a1b40a8f91d8ce195e965d900ec3f13b583a1ab36834fb3edc2812523fa1d586ce21c3e6d8ce437d168e23a5d8e7e2e46b50f6f languageName: node linkType: hard @@ -634,12 +657,12 @@ __metadata: languageName: unknown linkType: soft -"@babel/helper-member-expression-to-functions@npm:^7.15.0": - version: 7.15.0 - resolution: "@babel/helper-member-expression-to-functions@npm:7.15.0" +"@babel/helper-member-expression-to-functions@npm:^7.15.0, @babel/helper-member-expression-to-functions@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/helper-member-expression-to-functions@npm:7.15.4" dependencies: - "@babel/types": ^7.15.0 - checksum: 63b4824839990fbf3fe38b5c8a7b002a73bb2161e72b7146b1dc256671bcf36f34587a927e597a556dd496b49089cf13ea77877482aef1f35f628899042127ae + "@babel/types": ^7.15.4 + checksum: 30cf27e2afbaf1d58d189c5f36951a6af7d2bfccdfdb7d57e91749620d9c3c37d78324a1725079d3ab4a0e5c4e5f3d5f19a275d5dd269baa2aa8852835b05d6d languageName: node linkType: hard @@ -652,12 +675,12 @@ __metadata: languageName: unknown linkType: soft -"@babel/helper-module-imports@npm:^7.10.4, @babel/helper-module-imports@npm:^7.12.13, @babel/helper-module-imports@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/helper-module-imports@npm:7.14.5" +"@babel/helper-module-imports@npm:^7.10.4, @babel/helper-module-imports@npm:^7.12.13, @babel/helper-module-imports@npm:^7.14.5, @babel/helper-module-imports@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/helper-module-imports@npm:7.15.4" dependencies: - "@babel/types": ^7.14.5 - checksum: b98279908698a50a22634e683924cb25eb93edf1bf28ac65691dfa82d7a1a4dae4e6b12b8ef9f9a50171ca484620bce544f270873c53505d8a45364c5b665c0c + "@babel/types": ^7.15.4 + checksum: 519681cb9c27fcacd85ef13534020db3a2bac1d53a4d988fd9f3cf1ec223854311d4193c961cc2031c4d1df3b1a35a849b38237302752ae3d29eb00e5b9a904a languageName: node linkType: hard @@ -695,28 +718,28 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.12.1, @babel/helper-module-transforms@npm:^7.14.5, @babel/helper-module-transforms@npm:^7.15.0": - version: 7.15.0 - resolution: "@babel/helper-module-transforms@npm:7.15.0" +"@babel/helper-module-transforms@npm:^7.12.1, @babel/helper-module-transforms@npm:^7.14.5, @babel/helper-module-transforms@npm:^7.15.0, @babel/helper-module-transforms@npm:^7.15.8": + version: 7.15.8 + resolution: "@babel/helper-module-transforms@npm:7.15.8" dependencies: - "@babel/helper-module-imports": ^7.14.5 - "@babel/helper-replace-supers": ^7.15.0 - "@babel/helper-simple-access": ^7.14.8 - "@babel/helper-split-export-declaration": ^7.14.5 - "@babel/helper-validator-identifier": ^7.14.9 - "@babel/template": ^7.14.5 - "@babel/traverse": ^7.15.0 - "@babel/types": ^7.15.0 - checksum: 65eca31a9571d43c454cad13b26e17a0909e1fb439a939d2f17268f016ec85cec2fe7a9abcadea863d1b80b448f89647ac9be0abd76265c0e274205794031f33 + "@babel/helper-module-imports": ^7.15.4 + "@babel/helper-replace-supers": ^7.15.4 + "@babel/helper-simple-access": ^7.15.4 + "@babel/helper-split-export-declaration": ^7.15.4 + "@babel/helper-validator-identifier": ^7.15.7 + "@babel/template": ^7.15.4 + "@babel/traverse": ^7.15.4 + "@babel/types": ^7.15.6 + checksum: 67aea0ba226e066ef04ba642325cf39b1c517945b7e7d5596755f4eef9b81865522553b75deec77b30edd3d5069c866b71c30f4f8aa8d93077eabc0e0c603da0 languageName: node linkType: hard -"@babel/helper-optimise-call-expression@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/helper-optimise-call-expression@npm:7.14.5" +"@babel/helper-optimise-call-expression@npm:^7.14.5, @babel/helper-optimise-call-expression@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/helper-optimise-call-expression@npm:7.15.4" dependencies: - "@babel/types": ^7.14.5 - checksum: c7af558c63eb5449bf2249f1236d892ed54a400cb6c721756cde573b996c12c64dee6b57fa18ad1a0025d152e6f689444f7ea32997a1d56e1af66c3eda18843d + "@babel/types": ^7.15.4 + checksum: 7c929d1a3dbed7ee776dd8a4502b92433bb14ce6217372581db117de294edcf7b8678b1f703b8309c769bb46f2e4f005cdb3958dec508a486b2b03a9a919b542 languageName: node linkType: hard @@ -773,15 +796,15 @@ __metadata: languageName: unknown linkType: soft -"@babel/helper-replace-supers@npm:^7.14.5, @babel/helper-replace-supers@npm:^7.15.0": - version: 7.15.0 - resolution: "@babel/helper-replace-supers@npm:7.15.0" +"@babel/helper-replace-supers@npm:^7.14.5, @babel/helper-replace-supers@npm:^7.15.0, @babel/helper-replace-supers@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/helper-replace-supers@npm:7.15.4" dependencies: - "@babel/helper-member-expression-to-functions": ^7.15.0 - "@babel/helper-optimise-call-expression": ^7.14.5 - "@babel/traverse": ^7.15.0 - "@babel/types": ^7.15.0 - checksum: e1fce39b88ac32058a6fad15f0840cc40a63af7d60ef1d3bca0fcda3e4d88422d164a165c3b1efbcbda3b80ac68165fa79005fe27fc5569d2b9582a8cc002db3 + "@babel/helper-member-expression-to-functions": ^7.15.4 + "@babel/helper-optimise-call-expression": ^7.15.4 + "@babel/traverse": ^7.15.4 + "@babel/types": ^7.15.4 + checksum: b08a23914a5f7f964aefa4518255006d3a58e4c0cf972527c1fe3c79ebff4d6d50c9f1d370b8d62e0085766a654910e39ba196fab522d794142d2219eea8430d languageName: node linkType: hard @@ -796,12 +819,12 @@ __metadata: languageName: unknown linkType: soft -"@babel/helper-simple-access@npm:^7.14.8": - version: 7.14.8 - resolution: "@babel/helper-simple-access@npm:7.14.8" +"@babel/helper-simple-access@npm:^7.14.8, @babel/helper-simple-access@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/helper-simple-access@npm:7.15.4" dependencies: - "@babel/types": ^7.14.8 - checksum: c1dae88c956154c854bb1679d19b9158ff1c8241329a4a70026ec16c594b9637e73647e5a1a0f9b7c47b2309201f633c259fb41d06a800496283debce6a67fab + "@babel/types": ^7.15.4 + checksum: 8c3462264d6755c1e190a709fa90667c1691cb61cdca2d3f9119dd93adfd9fbcb292bcc48dbd7e065b8c27d9371f2793799a92aec124a3260288ed112e00c839 languageName: node linkType: hard @@ -832,12 +855,12 @@ __metadata: languageName: unknown linkType: soft -"@babel/helper-split-export-declaration@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/helper-split-export-declaration@npm:7.14.5" +"@babel/helper-split-export-declaration@npm:^7.14.5, @babel/helper-split-export-declaration@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/helper-split-export-declaration@npm:7.15.4" dependencies: - "@babel/types": ^7.14.5 - checksum: 93437025a33747bfd37d6d5a9cdac8f4b6b3e5c0c53c0e24c5444575e731ea64fd5471a51a039fd74ff3378f916ea2d69d9f10274d253ed6f832952be2fd65f0 + "@babel/types": ^7.15.4 + checksum: 6baf45996e1323fdfc30666e9c0b3219d74c54dc71e9130acfa4d9d4c53faa95618ac383a1c82a156555908323384a416b4a29e88b337de98fdb476212134f99 languageName: node linkType: hard @@ -864,10 +887,10 @@ __metadata: languageName: unknown linkType: soft -"@babel/helper-validator-identifier@npm:^7.14.5, @babel/helper-validator-identifier@npm:^7.14.9": - version: 7.14.9 - resolution: "@babel/helper-validator-identifier@npm:7.14.9" - checksum: 58552531a7674363e74672434c312ddaf1545b8a43308e1a7f38db58bf79c796c095a6dab6a6105eb0d783b97441f6cbb525bb887f29a35f232fcdbd8cb240dc +"@babel/helper-validator-identifier@npm:^7.14.5, @babel/helper-validator-identifier@npm:^7.14.9, @babel/helper-validator-identifier@npm:^7.15.7": + version: 7.15.7 + resolution: "@babel/helper-validator-identifier@npm:7.15.7" + checksum: f041c28c531d1add5cc345b25d5df3c29c62bce3205b4d4a93dcd164ccf630350acba252d374fad8f5d8ea526995a215829f27183ba7ce7ce141843bf23068a6 languageName: node linkType: hard @@ -916,14 +939,14 @@ __metadata: languageName: unknown linkType: soft -"@babel/helpers@npm:^7.12.5, @babel/helpers@npm:^7.14.8": - version: 7.14.8 - resolution: "@babel/helpers@npm:7.14.8" +"@babel/helpers@npm:^7.12.5, @babel/helpers@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/helpers@npm:7.15.4" dependencies: - "@babel/template": ^7.14.5 - "@babel/traverse": ^7.14.8 - "@babel/types": ^7.14.8 - checksum: 2f1358c19fc1ee744c183f81b499b73977da7d3d3f7a881d457b235754394a503e4717353f29364bd5feb7fa406b1edd1aab92b5ab0765dba945fb559eeb1c65 + "@babel/template": ^7.15.4 + "@babel/traverse": ^7.15.4 + "@babel/types": ^7.15.4 + checksum: e60738110086c183d0ce369ad56949d5dceeb7d73d8fdb892f36d5b8525192e6b97f4563eb77334f47ac27ac43a21f3c4cd53bff342c2a0d5f4008a2b0169c89 languageName: node linkType: hard @@ -984,12 +1007,12 @@ __metadata: languageName: unknown linkType: soft -"@babel/parser@npm:^7.0.0, @babel/parser@npm:^7.12.7, @babel/parser@npm:^7.14.5, @babel/parser@npm:^7.15.0, @babel/parser@npm:^7.7.2": - version: 7.15.0 - resolution: "@babel/parser@npm:7.15.0" +"@babel/parser@npm:^7.0.0, @babel/parser@npm:^7.12.7, @babel/parser@npm:^7.15.4, @babel/parser@npm:^7.15.8, @babel/parser@npm:^7.7.2": + version: 7.15.8 + resolution: "@babel/parser@npm:7.15.8" bin: parser: ./bin/babel-parser.js - checksum: f9739478b91d2c151246b5d61399d22ba2139897e979d7bfe62d9cafce6337f007ebf662933c950ad4f18858d1d25a44a9ad22157ce1870dbe6b356f0cdebe8f + checksum: a26c91967655f3961bc0c2565f7b9ac870ee3db86c9a0f00b96a7fb65210687be023431c79b3ed2a13b9c945e6afa09c36542ee508741e7ce3039a5b0f18c4b2 languageName: node linkType: hard @@ -3515,14 +3538,14 @@ __metadata: languageName: unknown linkType: soft -"@babel/template@npm:^7.12.7, @babel/template@npm:^7.14.5, @babel/template@npm:^7.3.3": - version: 7.14.5 - resolution: "@babel/template@npm:7.14.5" +"@babel/template@npm:^7.12.7, @babel/template@npm:^7.14.5, @babel/template@npm:^7.15.4, @babel/template@npm:^7.3.3": + version: 7.15.4 + resolution: "@babel/template@npm:7.15.4" dependencies: "@babel/code-frame": ^7.14.5 - "@babel/parser": ^7.14.5 - "@babel/types": ^7.14.5 - checksum: 4939199c5b1ca8940e14c87f30f4fab5f35c909bef88447131075349027546927b4e3e08e50db5c2db2024f2c6585a4fe571c739c835ac980f7a4ada2dd8a623 + "@babel/parser": ^7.15.4 + "@babel/types": ^7.15.4 + checksum: 58ca51fdd40bbaaddf2e46513dd05d5823f214cb2877b3f353abf5541a033a1b6570c29c2c80e60f2b55966326e40bebbf53666b261646ccf410b3d984af42ce languageName: node linkType: hard @@ -3546,23 +3569,6 @@ __metadata: languageName: unknown linkType: soft -"@babel/traverse@npm:^7.0.0, @babel/traverse@npm:^7.1.0, @babel/traverse@npm:^7.12.9, @babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.14.5, @babel/traverse@npm:^7.14.8, @babel/traverse@npm:^7.15.0, @babel/traverse@npm:^7.7.2": - version: 7.15.0 - resolution: "@babel/traverse@npm:7.15.0" - dependencies: - "@babel/code-frame": ^7.14.5 - "@babel/generator": ^7.15.0 - "@babel/helper-function-name": ^7.14.5 - "@babel/helper-hoist-variables": ^7.14.5 - "@babel/helper-split-export-declaration": ^7.14.5 - "@babel/parser": ^7.15.0 - "@babel/types": ^7.15.0 - debug: ^4.1.0 - globals: ^11.1.0 - checksum: e13056690a2a4a4dd699e241b89d4f7cf701ceef2f4ee0efc32a8cc4e07e1bbd397423868ecfec8aa98a769486f7d08778420d48f981b4f5dbb1b2f211daf656 - languageName: node - linkType: hard - "@babel/traverse@workspace:*, @babel/traverse@workspace:^7.15.4, @babel/traverse@workspace:packages/babel-traverse": version: 0.0.0-use.local resolution: "@babel/traverse@workspace:packages/babel-traverse" @@ -3580,13 +3586,13 @@ __metadata: languageName: unknown linkType: soft -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.12.7, @babel/types@npm:^7.14.5, @babel/types@npm:^7.14.8, @babel/types@npm:^7.15.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4": - version: 7.15.0 - resolution: "@babel/types@npm:7.15.0" +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.12.7, @babel/types@npm:^7.14.5, @babel/types@npm:^7.15.0, @babel/types@npm:^7.15.4, @babel/types@npm:^7.15.6, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4": + version: 7.15.6 + resolution: "@babel/types@npm:7.15.6" dependencies: "@babel/helper-validator-identifier": ^7.14.9 to-fast-properties: ^2.0.0 - checksum: 6d6bcdfce94b5446520a24087c6dede453e28425af092965b304d4028e9bca79712fd691cdad031e3570c7667bf3206e5f642bcccbfccb33d42ca4a8203587f9 + checksum: 37f497dde10d238b5eb184efab83b415a86611e3d73dc0434de0cfb851b20ee606a3b7e1525e5b2d522fac1248d0345fea0468006f246262511b80cd3ed2419f languageName: node linkType: hard