Parse class static block (#12079)

Co-authored-by: Brian Ng <bng412@gmail.com>
This commit is contained in:
Huáng Jùnliàng
2020-10-06 12:42:53 -04:00
committed by Nicolò Ribaudo
parent 6830c90ac9
commit 3ccca88178
66 changed files with 1823 additions and 46 deletions

View File

@@ -4,8 +4,8 @@
// The Errors key follows https://cs.chromium.org/chromium/src/v8/src/common/message-template.h unless it does not exist
export const ErrorMessages = Object.freeze({
AccessorIsGenerator: "A %0ter cannot be a generator",
ArgumentsDisallowedInInitializer:
"'arguments' is not allowed in class field initializer",
ArgumentsInClass:
"'arguments' is only allowed in functions and class methods",
AsyncFunctionInSingleStatementContext:
"Async functions can only be declared at the top level or inside a block",
AwaitBindingIdentifier:
@@ -32,6 +32,7 @@ export const ErrorMessages = Object.freeze({
DecoratorExportClass:
"Using the export keyword between a decorator and a class is not allowed. Please use `export @dec class` instead.",
DecoratorSemicolon: "Decorators must not be followed by a semicolon",
DecoratorStaticBlock: "Decorators can't be used with a static block",
DeletePrivateField: "Deleting a private field is not allowed",
DestructureNamedImport:
"ES2015 named imports do not destructure. Use another statement for destructuring after the import.",
@@ -41,6 +42,7 @@ export const ErrorMessages = Object.freeze({
"`%0` has already been exported. Exported identifiers must be unique.",
DuplicateProto: "Redefinition of __proto__ property",
DuplicateRegExpFlags: "Duplicate regular expression flag",
DuplicateStaticBlock: "Duplicate static block in the same class",
ElementAfterRest: "Rest element must be last element",
EscapedCharNotAnIdentifier: "Invalid Unicode escape",
ExportBindingIsString:

View File

@@ -2390,7 +2390,7 @@ export default class ExpressionParser extends LValParser {
!this.scope.inNonArrowFunction &&
word === "arguments"
) {
this.raise(startLoc, Errors.ArgumentsDisallowedInInitializer);
this.raise(startLoc, Errors.ArgumentsInClass);
return;
}
if (checkKeywords && isKeyword(word)) {

View File

@@ -797,7 +797,7 @@ export default class StatementParser extends ExpressionParser {
}
// Parse a semicolon-enclosed block of statements, handling `"use
// strict"` declarations when `allowStrict` is true (used for
// strict"` declarations when `allowDirectives` is true (used for
// function bodies).
parseBlock(
@@ -1207,7 +1207,11 @@ export default class StatementParser extends ExpressionParser {
): N.ClassBody {
this.classScope.enter();
const state = { hadConstructor: false };
const state: N.ParseClassMemberState = {
constructorAllowsSuper,
hadConstructor: false,
hadStaticBlock: false,
};
let decorators: N.Decorator[] = [];
const classBody: N.ClassBody = this.startNode();
classBody.body = [];
@@ -1239,7 +1243,7 @@ export default class StatementParser extends ExpressionParser {
decorators = [];
}
this.parseClassMember(classBody, member, state, constructorAllowsSuper);
this.parseClassMember(classBody, member, state);
if (
member.kind === "constructor" &&
@@ -1305,31 +1309,33 @@ export default class StatementParser extends ExpressionParser {
parseClassMember(
classBody: N.ClassBody,
member: N.ClassMember,
state: { hadConstructor: boolean },
constructorAllowsSuper: boolean,
state: N.ParseClassMemberState,
): void {
const isStatic = this.isContextual("static");
if (isStatic && this.parseClassMemberFromModifier(classBody, member)) {
// a class element named 'static'
return;
if (isStatic) {
if (this.parseClassMemberFromModifier(classBody, member)) {
// a class element named 'static'
return;
}
if (this.eat(tt.braceL)) {
this.parseClassStaticBlock(
classBody,
((member: any): N.StaticBlock),
state,
);
return;
}
}
this.parseClassMemberWithIsStatic(
classBody,
member,
state,
isStatic,
constructorAllowsSuper,
);
this.parseClassMemberWithIsStatic(classBody, member, state, isStatic);
}
parseClassMemberWithIsStatic(
classBody: N.ClassBody,
member: N.ClassMember,
state: { hadConstructor: boolean },
state: N.ParseClassMemberState,
isStatic: boolean,
constructorAllowsSuper: boolean,
) {
const publicMethod: $FlowSubtype<N.ClassMethod> = member;
const privateMethod: $FlowSubtype<N.ClassPrivateMethod> = member;
@@ -1396,7 +1402,7 @@ export default class StatementParser extends ExpressionParser {
this.raise(key.start, Errors.DuplicateConstructor);
}
state.hadConstructor = true;
allowsDirectSuper = constructorAllowsSuper;
allowsDirectSuper = state.constructorAllowsSuper;
}
this.pushClassMethod(
@@ -1515,6 +1521,35 @@ export default class StatementParser extends ExpressionParser {
return key;
}
parseClassStaticBlock(
classBody: N.ClassBody,
member: N.StaticBlock & { decorators?: Array<N.Decorator> },
state: N.ParseClassMemberState,
) {
this.expectPlugin("classStaticBlock", member.start);
// Start a new lexical scope
this.scope.enter(SCOPE_CLASS | SCOPE_SUPER);
// Start a new scope with regard to loop labels
const oldLabels = this.state.labels;
this.state.labels = [];
// ClassStaticBlockStatementList:
// StatementList[~Yield, ~Await, ~Return] opt
this.prodParam.enter(PARAM);
const body = (member.body = []);
this.parseBlockOrModuleBlockBody(body, undefined, false, tt.braceR);
this.prodParam.exit();
this.scope.exit();
this.state.labels = oldLabels;
classBody.body.push(this.finishNode<N.StaticBlock>(member, "StaticBlock"));
if (state.hadStaticBlock) {
this.raise(member.start, Errors.DuplicateStaticBlock);
}
if (member.decorators?.length) {
this.raise(member.start, Errors.DecoratorStaticBlock);
}
state.hadStaticBlock = true;
}
pushClassProperty(classBody: N.ClassBody, prop: N.ClassProperty) {
if (
!prop.computed &&

View File

@@ -2096,8 +2096,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
parseClassMember(
classBody: N.ClassBody,
member: any,
state: { hadConstructor: boolean },
constructorAllowsSuper: boolean,
state: N.ParseClassMemberState,
): void {
const pos = this.state.start;
if (this.isContextual("declare")) {
@@ -2109,7 +2108,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
member.declare = true;
}
super.parseClassMember(classBody, member, state, constructorAllowsSuper);
super.parseClassMember(classBody, member, state);
if (member.declare) {
if (

View File

@@ -2102,8 +2102,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
parseClassMember(
classBody: N.ClassBody,
member: any,
state: { hadConstructor: boolean },
constructorAllowsSuper: boolean,
state: N.ParseClassMemberState,
): void {
this.tsParseModifiers(member, ["declare"]);
const accessibility = this.parseAccessModifier();
@@ -2111,12 +2110,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.tsParseModifiers(member, ["declare"]);
const callParseClassMember = () => {
super.parseClassMember(
classBody,
member,
state,
constructorAllowsSuper,
);
super.parseClassMember(classBody, member, state);
};
if (member.declare) {
this.tsInDeclareContext(callParseClassMember);
@@ -2128,9 +2122,8 @@ export default (superClass: Class<Parser>): Class<Parser> =>
parseClassMemberWithIsStatic(
classBody: N.ClassBody,
member: N.ClassMember | N.TsIndexSignature,
state: { hadConstructor: boolean },
state: N.ParseClassMemberState,
isStatic: boolean,
constructorAllowsSuper: boolean,
): void {
this.tsParseModifiers(member, ["abstract", "readonly", "declare"]);
@@ -2160,13 +2153,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
/*:: invariant(member.type !== "TSIndexSignature") */
super.parseClassMemberWithIsStatic(
classBody,
member,
state,
isStatic,
constructorAllowsSuper,
);
super.parseClassMemberWithIsStatic(classBody, member, state, isStatic);
}
parsePostMemberNameModifiers(

View File

@@ -705,7 +705,7 @@ export type ClassBase = HasDecorators & {
export type ClassBody = NodeBase & {
type: "ClassBody",
body: Array<ClassMember | TsIndexSignature>, // TODO: $ReadOnlyArray
body: Array<ClassMember | StaticBlock | TsIndexSignature>, // TODO: $ReadOnlyArray
};
// | Placeholder<"ClassBody">;
@@ -719,6 +719,11 @@ export type ClassMemberBase = NodeBase &
optional?: ?true,
};
export type StaticBlock = NodeBase & {
type: "StaticBlock",
body: Array<Statement>,
};
export type ClassMember =
| ClassMethod
| ClassPrivateMethod
@@ -1489,3 +1494,9 @@ export type ParseSubscriptState = {
maybeAsyncArrow: boolean,
stop: boolean,
};
export type ParseClassMemberState = {|
hadConstructor: boolean,
hadStaticBlock: boolean,
constructorAllowsSuper: boolean,
|};