Parse class static block (#12079)
Co-authored-by: Brian Ng <bng412@gmail.com>
This commit is contained in:
committed by
Nicolò Ribaudo
parent
6830c90ac9
commit
3ccca88178
@@ -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:
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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 &&
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|};
|
||||
|
||||
Reference in New Issue
Block a user