Refactor await/yield production parameter tracking (#10956)

* test: add test fixtures

* refactor: track AWAIT and YIELD in separate handler

* fix flow errors

* add flow type annotation to production-parameter

* address review comments

* refactor: track [Return] parameter
This commit is contained in:
Huáng Jùnliàng 2020-02-09 23:31:29 +09:00 committed by GitHub
parent 8ab27c8ffe
commit 38529699d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 650 additions and 73 deletions

View File

@ -5,6 +5,7 @@ import type State from "../tokenizer/state";
import type { PluginsMap } from "./index"; import type { PluginsMap } from "./index";
import type ScopeHandler from "../util/scope"; import type ScopeHandler from "../util/scope";
import type ClassScopeHandler from "../util/class-scope"; import type ClassScopeHandler from "../util/class-scope";
import type ProductionParameterHandler from "../util/production-parameter";
export default class BaseParser { export default class BaseParser {
// Properties set by constructor in index.js // Properties set by constructor in index.js
@ -12,6 +13,7 @@ export default class BaseParser {
inModule: boolean; inModule: boolean;
scope: ScopeHandler<*>; scope: ScopeHandler<*>;
classScope: ClassScopeHandler; classScope: ClassScopeHandler;
prodParam: ProductionParameterHandler;
plugins: PluginsMap; plugins: PluginsMap;
filename: ?string; filename: ?string;
sawUnambiguousESM: boolean = false; sawUnambiguousESM: boolean = false;

View File

@ -33,15 +33,20 @@ import * as charCodes from "charcodes";
import { import {
BIND_OUTSIDE, BIND_OUTSIDE,
BIND_VAR, BIND_VAR,
functionFlags,
SCOPE_ARROW, SCOPE_ARROW,
SCOPE_CLASS, SCOPE_CLASS,
SCOPE_DIRECT_SUPER, SCOPE_DIRECT_SUPER,
SCOPE_FUNCTION,
SCOPE_SUPER, SCOPE_SUPER,
SCOPE_PROGRAM, SCOPE_PROGRAM,
SCOPE_ASYNC,
} from "../util/scopeflags"; } from "../util/scopeflags";
import { ExpressionErrors } from "./util"; import { ExpressionErrors } from "./util";
import {
PARAM_AWAIT,
PARAM_RETURN,
PARAM,
functionFlags,
} from "../util/production-parameter";
export default class ExpressionParser extends LValParser { export default class ExpressionParser extends LValParser {
// Forward-declaration: defined in statement.js // Forward-declaration: defined in statement.js
@ -108,11 +113,12 @@ export default class ExpressionParser extends LValParser {
// Convenience method to parse an Expression only // Convenience method to parse an Expression only
getExpression(): N.Expression { getExpression(): N.Expression {
let scopeFlags = SCOPE_PROGRAM; let paramFlags = PARAM;
if (this.hasPlugin("topLevelAwait") && this.inModule) { if (this.hasPlugin("topLevelAwait") && this.inModule) {
scopeFlags |= SCOPE_ASYNC; paramFlags |= PARAM_AWAIT;
} }
this.scope.enter(scopeFlags); this.scope.enter(SCOPE_PROGRAM);
this.prodParam.enter(paramFlags);
this.nextToken(); this.nextToken();
const expr = this.parseExpression(); const expr = this.parseExpression();
if (!this.match(tt.eof)) { if (!this.match(tt.eof)) {
@ -131,9 +137,15 @@ export default class ExpressionParser extends LValParser {
// and, *if* the syntactic construct they handle is present, wrap // and, *if* the syntactic construct they handle is present, wrap
// the AST node that the inner parser gave them in another node. // the AST node that the inner parser gave them in another node.
// Parse a full expression. The optional arguments are used to // Parse a full expression.
// forbid the `in` operator (in for loops initialization expressions) // - `noIn`
// and provide reference for storing '=' operator inside shorthand // is used to forbid the `in` operator (in for loops initialization expressions)
// When `noIn` is true, the production parameter [In] is not present.
// Whenever [?In] appears in the right-hand sides of a production, we pass
// `noIn` to the subroutine calls.
// - `refExpressionErrors `
// provides reference for storing '=' operator inside shorthand
// property assignment in contexts where both object expression // property assignment in contexts where both object expression
// and object pattern might appear (so it's possible to raise // and object pattern might appear (so it's possible to raise
// delayed syntax error at correct position). // delayed syntax error at correct position).
@ -169,7 +181,7 @@ export default class ExpressionParser extends LValParser {
const startPos = this.state.start; const startPos = this.state.start;
const startLoc = this.state.startLoc; const startLoc = this.state.startLoc;
if (this.isContextual("yield")) { if (this.isContextual("yield")) {
if (this.scope.inGenerator) { if (this.prodParam.hasYield) {
let left = this.parseYield(noIn); let left = this.parseYield(noIn);
if (afterLeftParse) { if (afterLeftParse) {
left = afterLeftParse.call(this, left, startPos, startLoc); left = afterLeftParse.call(this, left, startPos, startLoc);
@ -367,7 +379,7 @@ export default class ExpressionParser extends LValParser {
if ( if (
this.match(tt.name) && this.match(tt.name) &&
this.state.value === "await" && this.state.value === "await" &&
this.scope.inAsync this.prodParam.hasAwait
) { ) {
throw this.raise( throw this.raise(
this.state.start, this.state.start,
@ -1161,7 +1173,7 @@ export default class ExpressionParser extends LValParser {
this.next(); this.next();
meta = this.createIdentifier(meta, "function"); meta = this.createIdentifier(meta, "function");
if (this.scope.inGenerator && this.eat(tt.dot)) { if (this.prodParam.hasYield && this.eat(tt.dot)) {
return this.parseMetaProperty(node, meta, "sent"); return this.parseMetaProperty(node, meta, "sent");
} }
return this.parseFunction(node); return this.parseFunction(node);
@ -1833,13 +1845,15 @@ export default class ExpressionParser extends LValParser {
node.generator = !!isGenerator; node.generator = !!isGenerator;
const allowModifiers = isConstructor; // For TypeScript parameter properties const allowModifiers = isConstructor; // For TypeScript parameter properties
this.scope.enter( this.scope.enter(
functionFlags(isAsync, node.generator) | SCOPE_FUNCTION |
SCOPE_SUPER | SCOPE_SUPER |
(inClassScope ? SCOPE_CLASS : 0) | (inClassScope ? SCOPE_CLASS : 0) |
(allowDirectSuper ? SCOPE_DIRECT_SUPER : 0), (allowDirectSuper ? SCOPE_DIRECT_SUPER : 0),
); );
this.prodParam.enter(functionFlags(isAsync, node.generator));
this.parseFunctionParams((node: any), allowModifiers); this.parseFunctionParams((node: any), allowModifiers);
this.parseFunctionBodyAndFinish(node, type, true); this.parseFunctionBodyAndFinish(node, type, true);
this.prodParam.exit();
this.scope.exit(); this.scope.exit();
this.state.yieldPos = oldYieldPos; this.state.yieldPos = oldYieldPos;
@ -1857,7 +1871,8 @@ export default class ExpressionParser extends LValParser {
isAsync: boolean, isAsync: boolean,
trailingCommaPos: ?number, trailingCommaPos: ?number,
): N.ArrowFunctionExpression { ): N.ArrowFunctionExpression {
this.scope.enter(functionFlags(isAsync, false) | SCOPE_ARROW); this.scope.enter(SCOPE_FUNCTION | SCOPE_ARROW);
this.prodParam.enter(functionFlags(isAsync, false));
this.initFunction(node, isAsync); this.initFunction(node, isAsync);
const oldMaybeInArrowParameters = this.state.maybeInArrowParameters; const oldMaybeInArrowParameters = this.state.maybeInArrowParameters;
const oldYieldPos = this.state.yieldPos; const oldYieldPos = this.state.yieldPos;
@ -1872,6 +1887,7 @@ export default class ExpressionParser extends LValParser {
this.state.awaitPos = -1; this.state.awaitPos = -1;
this.parseFunctionBody(node, true); this.parseFunctionBody(node, true);
this.prodParam.exit();
this.scope.exit(); this.scope.exit();
this.state.maybeInArrowParameters = oldMaybeInArrowParameters; this.state.maybeInArrowParameters = oldMaybeInArrowParameters;
this.state.yieldPos = oldYieldPos; this.state.yieldPos = oldYieldPos;
@ -1949,7 +1965,11 @@ export default class ExpressionParser extends LValParser {
allowExpression, allowExpression,
!oldStrict && useStrict, !oldStrict && useStrict,
); );
// FunctionBody[Yield, Await]:
// StatementList[?Yield, ?Await, +Return] opt
this.prodParam.enter(this.prodParam.currentFlags() | PARAM_RETURN);
node.body = this.parseBlock(true, false); node.body = this.parseBlock(true, false);
this.prodParam.exit();
this.state.labels = oldLabels; this.state.labels = oldLabels;
} }
@ -2140,7 +2160,7 @@ export default class ExpressionParser extends LValParser {
checkKeywords: boolean, checkKeywords: boolean,
isBinding: boolean, isBinding: boolean,
): void { ): void {
if (this.scope.inGenerator && word === "yield") { if (this.prodParam.hasYield && word === "yield") {
this.raise( this.raise(
startLoc, startLoc,
"Can not use 'yield' as identifier inside a generator", "Can not use 'yield' as identifier inside a generator",
@ -2149,7 +2169,7 @@ export default class ExpressionParser extends LValParser {
} }
if (word === "await") { if (word === "await") {
if (this.scope.inAsync) { if (this.prodParam.hasAwait) {
this.raise( this.raise(
startLoc, startLoc,
"Can not use 'await' as identifier inside an async function", "Can not use 'await' as identifier inside an async function",
@ -2187,7 +2207,7 @@ export default class ExpressionParser extends LValParser {
: isStrictReservedWord; : isStrictReservedWord;
if (reservedTest(word, this.inModule)) { if (reservedTest(word, this.inModule)) {
if (!this.scope.inAsync && word === "await") { if (!this.prodParam.hasAwait && word === "await") {
this.raise( this.raise(
startLoc, startLoc,
"Can not use keyword 'await' outside an async function", "Can not use keyword 'await' outside an async function",
@ -2199,10 +2219,10 @@ export default class ExpressionParser extends LValParser {
} }
isAwaitAllowed(): boolean { isAwaitAllowed(): boolean {
if (this.scope.inFunction) return this.scope.inAsync; if (this.scope.inFunction) return this.prodParam.hasAwait;
if (this.options.allowAwaitOutsideFunction) return true; if (this.options.allowAwaitOutsideFunction) return true;
if (this.hasPlugin("topLevelAwait")) { if (this.hasPlugin("topLevelAwait")) {
return this.inModule && this.scope.inAsync; return this.inModule && this.prodParam.hasAwait;
} }
return false; return false;
} }

View File

@ -5,9 +5,13 @@ import type { File /*::, JSXOpeningElement */ } from "../types";
import type { PluginList } from "../plugin-utils"; import type { PluginList } from "../plugin-utils";
import { getOptions } from "../options"; import { getOptions } from "../options";
import StatementParser from "./statement"; import StatementParser from "./statement";
import { SCOPE_ASYNC, SCOPE_PROGRAM } from "../util/scopeflags"; import { SCOPE_PROGRAM } from "../util/scopeflags";
import ScopeHandler from "../util/scope"; import ScopeHandler from "../util/scope";
import ClassScopeHandler from "../util/class-scope"; import ClassScopeHandler from "../util/class-scope";
import ProductionParameterHandler, {
PARAM_AWAIT,
PARAM,
} from "../util/production-parameter";
export type PluginsMap = Map<string, { [string]: any }>; export type PluginsMap = Map<string, { [string]: any }>;
@ -28,6 +32,7 @@ export default class Parser extends StatementParser {
this.options = options; this.options = options;
this.inModule = this.options.sourceType === "module"; this.inModule = this.options.sourceType === "module";
this.scope = new ScopeHandler(this.raise.bind(this), this.inModule); this.scope = new ScopeHandler(this.raise.bind(this), this.inModule);
this.prodParam = new ProductionParameterHandler();
this.classScope = new ClassScopeHandler(this.raise.bind(this)); this.classScope = new ClassScopeHandler(this.raise.bind(this));
this.plugins = pluginsMap(this.options.plugins); this.plugins = pluginsMap(this.options.plugins);
this.filename = options.sourceFilename; this.filename = options.sourceFilename;
@ -39,11 +44,12 @@ export default class Parser extends StatementParser {
} }
parse(): File { parse(): File {
let scopeFlags = SCOPE_PROGRAM; let paramFlags = PARAM;
if (this.hasPlugin("topLevelAwait") && this.inModule) { if (this.hasPlugin("topLevelAwait") && this.inModule) {
scopeFlags |= SCOPE_ASYNC; paramFlags |= PARAM_AWAIT;
} }
this.scope.enter(scopeFlags); this.scope.enter(SCOPE_PROGRAM);
this.prodParam.enter(paramFlags);
const file = this.startNode(); const file = this.startNode();
const program = this.startNode(); const program = this.startNode();
this.nextToken(); this.nextToken();

View File

@ -15,8 +15,8 @@ import {
BIND_LEXICAL, BIND_LEXICAL,
BIND_VAR, BIND_VAR,
BIND_FUNCTION, BIND_FUNCTION,
functionFlags,
SCOPE_CLASS, SCOPE_CLASS,
SCOPE_FUNCTION,
SCOPE_OTHER, SCOPE_OTHER,
SCOPE_SIMPLE_CATCH, SCOPE_SIMPLE_CATCH,
SCOPE_SUPER, SCOPE_SUPER,
@ -28,6 +28,7 @@ import {
type BindingTypes, type BindingTypes,
} from "../util/scopeflags"; } from "../util/scopeflags";
import { ExpressionErrors } from "./util"; import { ExpressionErrors } from "./util";
import { PARAM, functionFlags } from "../util/production-parameter";
const loopLabel = { kind: "loop" }, const loopLabel = { kind: "loop" },
switchLabel = { kind: "switch" }; switchLabel = { kind: "switch" };
@ -574,7 +575,7 @@ export default class StatementParser extends ExpressionParser {
} }
parseReturnStatement(node: N.ReturnStatement): N.ReturnStatement { parseReturnStatement(node: N.ReturnStatement): N.ReturnStatement {
if (!this.scope.inFunction && !this.options.allowReturnOutsideFunction) { if (!this.prodParam.hasReturn && !this.options.allowReturnOutsideFunction) {
this.raise(this.state.start, "'return' outside of function"); this.raise(this.state.start, "'return' outside of function");
} }
@ -1059,7 +1060,8 @@ export default class StatementParser extends ExpressionParser {
this.state.maybeInArrowParameters = false; this.state.maybeInArrowParameters = false;
this.state.yieldPos = -1; this.state.yieldPos = -1;
this.state.awaitPos = -1; this.state.awaitPos = -1;
this.scope.enter(functionFlags(node.async, node.generator)); this.scope.enter(SCOPE_FUNCTION);
this.prodParam.enter(functionFlags(isAsync, node.generator));
if (!isStatement) { if (!isStatement) {
node.id = this.parseFunctionId(); node.id = this.parseFunctionId();
@ -1078,6 +1080,7 @@ export default class StatementParser extends ExpressionParser {
); );
}); });
this.prodParam.exit();
this.scope.exit(); this.scope.exit();
if (isStatement && !isHangingStatement) { if (isStatement && !isHangingStatement) {
@ -1599,9 +1602,12 @@ export default class StatementParser extends ExpressionParser {
node: N.ClassPrivateProperty, node: N.ClassPrivateProperty,
): N.ClassPrivateProperty { ): N.ClassPrivateProperty {
this.scope.enter(SCOPE_CLASS | SCOPE_SUPER); this.scope.enter(SCOPE_CLASS | SCOPE_SUPER);
// [In] production parameter is tracked in parseMaybeAssign
this.prodParam.enter(PARAM);
node.value = this.eat(tt.eq) ? this.parseMaybeAssign() : null; node.value = this.eat(tt.eq) ? this.parseMaybeAssign() : null;
this.semicolon(); this.semicolon();
this.prodParam.exit();
this.scope.exit(); this.scope.exit();
@ -1614,6 +1620,8 @@ export default class StatementParser extends ExpressionParser {
} }
this.scope.enter(SCOPE_CLASS | SCOPE_SUPER); this.scope.enter(SCOPE_CLASS | SCOPE_SUPER);
// [In] production parameter is tracked in parseMaybeAssign
this.prodParam.enter(PARAM);
if (this.match(tt.eq)) { if (this.match(tt.eq)) {
this.expectPlugin("classProperties"); this.expectPlugin("classProperties");
@ -1624,6 +1632,7 @@ export default class StatementParser extends ExpressionParser {
} }
this.semicolon(); this.semicolon();
this.prodParam.exit();
this.scope.exit(); this.scope.exit();
return this.finishNode(node, "ClassProperty"); return this.finishNode(node, "ClassProperty");

View File

@ -12,13 +12,13 @@ import { types as tc } from "../tokenizer/context";
import * as charCodes from "charcodes"; import * as charCodes from "charcodes";
import { isIteratorStart } from "../util/identifier"; import { isIteratorStart } from "../util/identifier";
import { import {
functionFlags,
type BindingTypes, type BindingTypes,
BIND_NONE, BIND_NONE,
BIND_LEXICAL, BIND_LEXICAL,
BIND_VAR, BIND_VAR,
BIND_FUNCTION, BIND_FUNCTION,
SCOPE_ARROW, SCOPE_ARROW,
SCOPE_FUNCTION,
SCOPE_OTHER, SCOPE_OTHER,
} from "../util/scopeflags"; } from "../util/scopeflags";
import type { ExpressionErrors } from "../parser/util"; import type { ExpressionErrors } from "../parser/util";
@ -1889,7 +1889,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
node.extra?.trailingComma, node.extra?.trailingComma,
); );
// Enter scope, as checkParams defines bindings // Enter scope, as checkParams defines bindings
this.scope.enter(functionFlags(false, false) | SCOPE_ARROW); this.scope.enter(SCOPE_FUNCTION | SCOPE_ARROW);
// Use super's method to force the parameters to be checked // Use super's method to force the parameters to be checked
super.checkParams(node, false, true); super.checkParams(node, false, true);
this.scope.exit(); this.scope.exit();

View File

@ -26,6 +26,7 @@ import {
import TypeScriptScopeHandler from "./scope"; import TypeScriptScopeHandler from "./scope";
import * as charCodes from "charcodes"; import * as charCodes from "charcodes";
import type { ExpressionErrors } from "../../parser/util"; import type { ExpressionErrors } from "../../parser/util";
import { PARAM } from "../../util/production-parameter";
type TsModifier = type TsModifier =
| "readonly" | "readonly"
@ -1265,7 +1266,9 @@ export default (superClass: Class<Parser>): Class<Parser> =>
node.body = inner; node.body = inner;
} else { } else {
this.scope.enter(SCOPE_TS_MODULE); this.scope.enter(SCOPE_TS_MODULE);
this.prodParam.enter(PARAM);
node.body = this.tsParseModuleBlock(); node.body = this.tsParseModuleBlock();
this.prodParam.exit();
this.scope.exit(); this.scope.exit();
} }
return this.finishNode(node, "TSModuleDeclaration"); return this.finishNode(node, "TSModuleDeclaration");
@ -1284,7 +1287,9 @@ export default (superClass: Class<Parser>): Class<Parser> =>
} }
if (this.match(tt.braceL)) { if (this.match(tt.braceL)) {
this.scope.enter(SCOPE_TS_MODULE); this.scope.enter(SCOPE_TS_MODULE);
this.prodParam.enter(PARAM);
node.body = this.tsParseModuleBlock(); node.body = this.tsParseModuleBlock();
this.prodParam.exit();
this.scope.exit(); this.scope.exit();
} else { } else {
this.semicolon(); this.semicolon();
@ -1439,11 +1444,13 @@ export default (superClass: Class<Parser>): Class<Parser> =>
// Would like to use tsParseAmbientExternalModuleDeclaration here, but already ran past "global". // Would like to use tsParseAmbientExternalModuleDeclaration here, but already ran past "global".
if (this.match(tt.braceL)) { if (this.match(tt.braceL)) {
this.scope.enter(SCOPE_TS_MODULE); this.scope.enter(SCOPE_TS_MODULE);
this.prodParam.enter(PARAM);
const mod: N.TsModuleDeclaration = node; const mod: N.TsModuleDeclaration = node;
mod.global = true; mod.global = true;
mod.id = expr; mod.id = expr;
mod.body = this.tsParseModuleBlock(); mod.body = this.tsParseModuleBlock();
this.scope.exit(); this.scope.exit();
this.prodParam.exit();
return this.finishNode(mod, "TSModuleDeclaration"); return this.finishNode(mod, "TSModuleDeclaration");
} }
break; break;

View File

@ -60,7 +60,7 @@ tt.name.updateContext = function(prevType) {
if (prevType !== tt.dot) { if (prevType !== tt.dot) {
if ( if (
(this.state.value === "of" && !this.state.exprAllowed) || (this.state.value === "of" && !this.state.exprAllowed) ||
(this.state.value === "yield" && this.scope.inGenerator) (this.state.value === "yield" && this.prodParam.hasYield)
) { ) {
allowed = true; allowed = true;
} }

View File

@ -0,0 +1,63 @@
// @flow
export const PARAM = 0b000, // Initial Parameter flags
PARAM_YIELD = 0b001, // track [Yield] production parameter
PARAM_AWAIT = 0b010, // track [Await] production parameter
PARAM_RETURN = 0b100; // track [Return] production parameter
// ProductionParameterHandler is a stack fashioned production parameter tracker
// https://tc39.es/ecma262/#sec-grammar-notation
// The tracked parameters are defined above. Note that the [In] parameter is
// tracked in `noIn` argument of `parseExpression`.
//
// Whenever [+Await]/[+Yield] appears in the right-hand sides of a production,
// we must enter a new tracking stack. For example when parsing
//
// AsyncFunctionDeclaration [Yield, Await]:
// async [no LineTerminator here] function BindingIdentifier[?Yield, ?Await]
// ( FormalParameters[~Yield, +Await] ) { AsyncFunctionBody }
//
// we must follow such process:
//
// 1. parse async keyword
// 2. parse function keyword
// 3. parse bindingIdentifier <= inherit current parameters: [?Await]
// 4. enter new stack with (PARAM_AWAIT)
// 5. parse formal parameters <= must have [Await] parameter [+Await]
// 6. parse function body
// 7. exit current stack
export type ParamKind = typeof PARAM | typeof PARAM_AWAIT | typeof PARAM_YIELD;
export default class ProductionParameterHandler {
stacks: Array<ParamKind> = [];
enter(flags: ParamKind) {
this.stacks.push(flags);
}
exit() {
this.stacks.pop();
}
currentFlags(): ParamKind {
return this.stacks[this.stacks.length - 1];
}
get hasAwait(): boolean {
return (this.currentFlags() & PARAM_AWAIT) > 0;
}
get hasYield(): boolean {
return (this.currentFlags() & PARAM_YIELD) > 0;
}
get hasReturn(): boolean {
return (this.currentFlags() & PARAM_RETURN) > 0;
}
}
export function functionFlags(
isAsync: boolean,
isGenerator: boolean,
): ParamKind {
return (isAsync ? PARAM_AWAIT : 0) | (isGenerator ? PARAM_YIELD : 0);
}

View File

@ -1,10 +1,8 @@
// @flow // @flow
import { import {
SCOPE_ARROW, SCOPE_ARROW,
SCOPE_ASYNC,
SCOPE_DIRECT_SUPER, SCOPE_DIRECT_SUPER,
SCOPE_FUNCTION, SCOPE_FUNCTION,
SCOPE_GENERATOR,
SCOPE_SIMPLE_CATCH, SCOPE_SIMPLE_CATCH,
SCOPE_SUPER, SCOPE_SUPER,
SCOPE_PROGRAM, SCOPE_PROGRAM,
@ -53,25 +51,6 @@ export default class ScopeHandler<IScope: Scope = Scope> {
get inFunction() { get inFunction() {
return (this.currentVarScope().flags & SCOPE_FUNCTION) > 0; return (this.currentVarScope().flags & SCOPE_FUNCTION) > 0;
} }
get inGenerator() {
return (this.currentVarScope().flags & SCOPE_GENERATOR) > 0;
}
// the following loop always exit because SCOPE_PROGRAM is SCOPE_VAR
// $FlowIgnore
get inAsync() {
for (let i = this.scopeStack.length - 1; ; i--) {
const scope = this.scopeStack[i];
const isVarScope = scope.flags & SCOPE_VAR;
const isClassScope = scope.flags & SCOPE_CLASS;
if (isClassScope && !isVarScope) {
// If it meets a class scope before a var scope, it means it is a class property initializer
// which does not have an [Await] parameter in its grammar
return false;
} else if (isVarScope) {
return (scope.flags & SCOPE_ASYNC) > 0;
}
}
}
get allowSuper() { get allowSuper() {
return (this.currentThisScope().flags & SCOPE_SUPER) > 0; return (this.currentThisScope().flags & SCOPE_SUPER) > 0;
} }

View File

@ -2,17 +2,15 @@
// Each scope gets a bitset that may contain these flags // Each scope gets a bitset that may contain these flags
// prettier-ignore // prettier-ignore
export const SCOPE_OTHER = 0b0000000000, export const SCOPE_OTHER = 0b00000000,
SCOPE_PROGRAM = 0b0000000001, SCOPE_PROGRAM = 0b00000001,
SCOPE_FUNCTION = 0b0000000010, SCOPE_FUNCTION = 0b00000010,
SCOPE_ASYNC = 0b0000000100, SCOPE_ARROW = 0b00000100,
SCOPE_GENERATOR = 0b0000001000, SCOPE_SIMPLE_CATCH = 0b00001000,
SCOPE_ARROW = 0b0000010000, SCOPE_SUPER = 0b00010000,
SCOPE_SIMPLE_CATCH = 0b0000100000, SCOPE_DIRECT_SUPER = 0b00100000,
SCOPE_SUPER = 0b0001000000, SCOPE_CLASS = 0b01000000,
SCOPE_DIRECT_SUPER = 0b0010000000, SCOPE_TS_MODULE = 0b10000000,
SCOPE_CLASS = 0b0100000000,
SCOPE_TS_MODULE = 0b1000000000,
SCOPE_VAR = SCOPE_PROGRAM | SCOPE_FUNCTION | SCOPE_TS_MODULE; SCOPE_VAR = SCOPE_PROGRAM | SCOPE_FUNCTION | SCOPE_TS_MODULE;
export type ScopeFlags = export type ScopeFlags =
@ -20,22 +18,12 @@ export type ScopeFlags =
| typeof SCOPE_PROGRAM | typeof SCOPE_PROGRAM
| typeof SCOPE_FUNCTION | typeof SCOPE_FUNCTION
| typeof SCOPE_VAR | typeof SCOPE_VAR
| typeof SCOPE_ASYNC
| typeof SCOPE_GENERATOR
| typeof SCOPE_ARROW | typeof SCOPE_ARROW
| typeof SCOPE_SIMPLE_CATCH | typeof SCOPE_SIMPLE_CATCH
| typeof SCOPE_SUPER | typeof SCOPE_SUPER
| typeof SCOPE_DIRECT_SUPER | typeof SCOPE_DIRECT_SUPER
| typeof SCOPE_CLASS; | typeof SCOPE_CLASS;
export function functionFlags(isAsync: boolean, isGenerator: boolean) {
return (
SCOPE_FUNCTION |
(isAsync ? SCOPE_ASYNC : 0) |
(isGenerator ? SCOPE_GENERATOR : 0)
);
}
// These flags are meant to be _only_ used inside the Scope class (or subclasses). // These flags are meant to be _only_ used inside the Scope class (or subclasses).
// prettier-ignore // prettier-ignore
export const BIND_KIND_VALUE = 0b00000_0000_01, export const BIND_KIND_VALUE = 0b00000_0000_01,

View File

@ -0,0 +1,6 @@
function* foo() {
class C {
// here yield is an identifier reference
p = yield + 42;
}
}

View File

@ -0,0 +1,3 @@
{
"plugins": ["classProperties"]
}

View File

@ -0,0 +1,264 @@
{
"type": "File",
"start": 0,
"end": 100,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 6,
"column": 1
}
},
"errors": [
"SyntaxError: Unexpected reserved word 'yield' (4:8)"
],
"program": {
"type": "Program",
"start": 0,
"end": 100,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 6,
"column": 1
}
},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "FunctionDeclaration",
"start": 0,
"end": 100,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 6,
"column": 1
}
},
"id": {
"type": "Identifier",
"start": 10,
"end": 13,
"loc": {
"start": {
"line": 1,
"column": 10
},
"end": {
"line": 1,
"column": 13
},
"identifierName": "foo"
},
"name": "foo"
},
"generator": true,
"async": false,
"params": [],
"body": {
"type": "BlockStatement",
"start": 16,
"end": 100,
"loc": {
"start": {
"line": 1,
"column": 16
},
"end": {
"line": 6,
"column": 1
}
},
"body": [
{
"type": "ClassDeclaration",
"start": 20,
"end": 98,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 5,
"column": 3
}
},
"id": {
"type": "Identifier",
"start": 26,
"end": 27,
"loc": {
"start": {
"line": 2,
"column": 8
},
"end": {
"line": 2,
"column": 9
},
"identifierName": "C"
},
"name": "C"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start": 28,
"end": 98,
"loc": {
"start": {
"line": 2,
"column": 10
},
"end": {
"line": 5,
"column": 3
}
},
"body": [
{
"type": "ClassProperty",
"start": 79,
"end": 94,
"loc": {
"start": {
"line": 4,
"column": 4
},
"end": {
"line": 4,
"column": 19
}
},
"static": false,
"key": {
"type": "Identifier",
"start": 79,
"end": 80,
"loc": {
"start": {
"line": 4,
"column": 4
},
"end": {
"line": 4,
"column": 5
},
"identifierName": "p"
},
"name": "p"
},
"computed": false,
"value": {
"type": "BinaryExpression",
"start": 83,
"end": 93,
"loc": {
"start": {
"line": 4,
"column": 8
},
"end": {
"line": 4,
"column": 18
}
},
"left": {
"type": "Identifier",
"start": 83,
"end": 88,
"loc": {
"start": {
"line": 4,
"column": 8
},
"end": {
"line": 4,
"column": 13
},
"identifierName": "yield"
},
"name": "yield"
},
"operator": "+",
"right": {
"type": "NumericLiteral",
"start": 91,
"end": 93,
"loc": {
"start": {
"line": 4,
"column": 16
},
"end": {
"line": 4,
"column": 18
}
},
"extra": {
"rawValue": 42,
"raw": "42"
},
"value": 42
}
},
"leadingComments": [
{
"type": "CommentLine",
"value": " here yield is an identifier reference",
"start": 34,
"end": 74,
"loc": {
"start": {
"line": 3,
"column": 4
},
"end": {
"line": 3,
"column": 44
}
}
}
]
}
]
}
}
],
"directives": []
}
}
],
"directives": []
},
"comments": [
{
"type": "CommentLine",
"value": " here yield is an identifier reference",
"start": 34,
"end": 74,
"loc": {
"start": {
"line": 3,
"column": 4
},
"end": {
"line": 3,
"column": 44
}
}
}
]
}

View File

@ -0,0 +1,3 @@
class C {
p = function* () { yield + 42 };
}

View File

@ -0,0 +1,3 @@
{
"plugins": ["classProperties"]
}

View File

@ -0,0 +1,224 @@
{
"type": "File",
"start": 0,
"end": 46,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 3,
"column": 1
}
},
"program": {
"type": "Program",
"start": 0,
"end": 46,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 3,
"column": 1
}
},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ClassDeclaration",
"start": 0,
"end": 46,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 3,
"column": 1
}
},
"id": {
"type": "Identifier",
"start": 6,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 6
},
"end": {
"line": 1,
"column": 7
},
"identifierName": "C"
},
"name": "C"
},
"superClass": null,
"body": {
"type": "ClassBody",
"start": 8,
"end": 46,
"loc": {
"start": {
"line": 1,
"column": 8
},
"end": {
"line": 3,
"column": 1
}
},
"body": [
{
"type": "ClassProperty",
"start": 12,
"end": 44,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 34
}
},
"static": false,
"key": {
"type": "Identifier",
"start": 12,
"end": 13,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 3
},
"identifierName": "p"
},
"name": "p"
},
"computed": false,
"value": {
"type": "FunctionExpression",
"start": 16,
"end": 43,
"loc": {
"start": {
"line": 2,
"column": 6
},
"end": {
"line": 2,
"column": 33
}
},
"id": null,
"generator": true,
"async": false,
"params": [],
"body": {
"type": "BlockStatement",
"start": 29,
"end": 43,
"loc": {
"start": {
"line": 2,
"column": 19
},
"end": {
"line": 2,
"column": 33
}
},
"body": [
{
"type": "ExpressionStatement",
"start": 31,
"end": 41,
"loc": {
"start": {
"line": 2,
"column": 21
},
"end": {
"line": 2,
"column": 31
}
},
"expression": {
"type": "YieldExpression",
"start": 31,
"end": 41,
"loc": {
"start": {
"line": 2,
"column": 21
},
"end": {
"line": 2,
"column": 31
}
},
"delegate": false,
"argument": {
"type": "UnaryExpression",
"start": 37,
"end": 41,
"loc": {
"start": {
"line": 2,
"column": 27
},
"end": {
"line": 2,
"column": 31
}
},
"operator": "+",
"prefix": true,
"argument": {
"type": "NumericLiteral",
"start": 39,
"end": 41,
"loc": {
"start": {
"line": 2,
"column": 29
},
"end": {
"line": 2,
"column": 31
}
},
"extra": {
"rawValue": 42,
"raw": "42"
},
"value": 42
}
}
}
}
],
"directives": []
}
}
}
]
}
}
],
"directives": []
}
}