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:
63
packages/babel-parser/src/util/production-parameter.js
Normal file
63
packages/babel-parser/src/util/production-parameter.js
Normal 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);
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
// @flow
|
||||
import {
|
||||
SCOPE_ARROW,
|
||||
SCOPE_ASYNC,
|
||||
SCOPE_DIRECT_SUPER,
|
||||
SCOPE_FUNCTION,
|
||||
SCOPE_GENERATOR,
|
||||
SCOPE_SIMPLE_CATCH,
|
||||
SCOPE_SUPER,
|
||||
SCOPE_PROGRAM,
|
||||
@@ -53,25 +51,6 @@ export default class ScopeHandler<IScope: Scope = Scope> {
|
||||
get inFunction() {
|
||||
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() {
|
||||
return (this.currentThisScope().flags & SCOPE_SUPER) > 0;
|
||||
}
|
||||
|
||||
@@ -2,17 +2,15 @@
|
||||
|
||||
// Each scope gets a bitset that may contain these flags
|
||||
// prettier-ignore
|
||||
export const SCOPE_OTHER = 0b0000000000,
|
||||
SCOPE_PROGRAM = 0b0000000001,
|
||||
SCOPE_FUNCTION = 0b0000000010,
|
||||
SCOPE_ASYNC = 0b0000000100,
|
||||
SCOPE_GENERATOR = 0b0000001000,
|
||||
SCOPE_ARROW = 0b0000010000,
|
||||
SCOPE_SIMPLE_CATCH = 0b0000100000,
|
||||
SCOPE_SUPER = 0b0001000000,
|
||||
SCOPE_DIRECT_SUPER = 0b0010000000,
|
||||
SCOPE_CLASS = 0b0100000000,
|
||||
SCOPE_TS_MODULE = 0b1000000000,
|
||||
export const SCOPE_OTHER = 0b00000000,
|
||||
SCOPE_PROGRAM = 0b00000001,
|
||||
SCOPE_FUNCTION = 0b00000010,
|
||||
SCOPE_ARROW = 0b00000100,
|
||||
SCOPE_SIMPLE_CATCH = 0b00001000,
|
||||
SCOPE_SUPER = 0b00010000,
|
||||
SCOPE_DIRECT_SUPER = 0b00100000,
|
||||
SCOPE_CLASS = 0b01000000,
|
||||
SCOPE_TS_MODULE = 0b10000000,
|
||||
SCOPE_VAR = SCOPE_PROGRAM | SCOPE_FUNCTION | SCOPE_TS_MODULE;
|
||||
|
||||
export type ScopeFlags =
|
||||
@@ -20,22 +18,12 @@ export type ScopeFlags =
|
||||
| typeof SCOPE_PROGRAM
|
||||
| typeof SCOPE_FUNCTION
|
||||
| typeof SCOPE_VAR
|
||||
| typeof SCOPE_ASYNC
|
||||
| typeof SCOPE_GENERATOR
|
||||
| typeof SCOPE_ARROW
|
||||
| typeof SCOPE_SIMPLE_CATCH
|
||||
| typeof SCOPE_SUPER
|
||||
| typeof SCOPE_DIRECT_SUPER
|
||||
| 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).
|
||||
// prettier-ignore
|
||||
export const BIND_KIND_VALUE = 0b00000_0000_01,
|
||||
|
||||
Reference in New Issue
Block a user