Support multiple static blocks (#12738)

This commit is contained in:
Huáng Jùnliàng 2021-03-12 17:29:55 -05:00 committed by GitHub
parent 05fa18e652
commit 1a05b81387
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 231 additions and 52 deletions

View File

@ -49,7 +49,6 @@ export const ErrorMessages = Object.freeze({
"`%0` has already been exported. Exported identifiers must be unique.", "`%0` has already been exported. Exported identifiers must be unique.",
DuplicateProto: "Redefinition of __proto__ property", DuplicateProto: "Redefinition of __proto__ property",
DuplicateRegExpFlags: "Duplicate regular expression flag", DuplicateRegExpFlags: "Duplicate regular expression flag",
DuplicateStaticBlock: "Duplicate static block in the same class",
ElementAfterRest: "Rest element must be last element", ElementAfterRest: "Rest element must be last element",
EscapedCharNotAnIdentifier: "Invalid Unicode escape", EscapedCharNotAnIdentifier: "Invalid Unicode escape",
ExportBindingIsString: ExportBindingIsString:

View File

@ -1204,7 +1204,6 @@ export default class StatementParser extends ExpressionParser {
const state: N.ParseClassMemberState = { const state: N.ParseClassMemberState = {
constructorAllowsSuper, constructorAllowsSuper,
hadConstructor: false, hadConstructor: false,
hadStaticBlock: false,
}; };
let decorators: N.Decorator[] = []; let decorators: N.Decorator[] = [];
const classBody: N.ClassBody = this.startNode(); const classBody: N.ClassBody = this.startNode();
@ -1313,11 +1312,7 @@ export default class StatementParser extends ExpressionParser {
return; return;
} }
if (this.eat(tt.braceL)) { if (this.eat(tt.braceL)) {
this.parseClassStaticBlock( this.parseClassStaticBlock(classBody, ((member: any): N.StaticBlock));
classBody,
((member: any): N.StaticBlock),
state,
);
return; return;
} }
} }
@ -1521,7 +1516,6 @@ export default class StatementParser extends ExpressionParser {
parseClassStaticBlock( parseClassStaticBlock(
classBody: N.ClassBody, classBody: N.ClassBody,
member: N.StaticBlock & { decorators?: Array<N.Decorator> }, member: N.StaticBlock & { decorators?: Array<N.Decorator> },
state: N.ParseClassMemberState,
) { ) {
this.expectPlugin("classStaticBlock", member.start); this.expectPlugin("classStaticBlock", member.start);
// Start a new lexical scope // Start a new lexical scope
@ -1538,13 +1532,9 @@ export default class StatementParser extends ExpressionParser {
this.scope.exit(); this.scope.exit();
this.state.labels = oldLabels; this.state.labels = oldLabels;
classBody.body.push(this.finishNode<N.StaticBlock>(member, "StaticBlock")); classBody.body.push(this.finishNode<N.StaticBlock>(member, "StaticBlock"));
if (state.hadStaticBlock) {
this.raise(member.start, Errors.DuplicateStaticBlock);
}
if (member.decorators?.length) { if (member.decorators?.length) {
this.raise(member.start, Errors.DecoratorStaticBlock); this.raise(member.start, Errors.DecoratorStaticBlock);
} }
state.hadStaticBlock = true;
} }
pushClassProperty(classBody: N.ClassBody, prop: N.ClassProperty) { pushClassProperty(classBody: N.ClassBody, prop: N.ClassProperty) {

View File

@ -1528,6 +1528,5 @@ export type ParseSubscriptState = {
export type ParseClassMemberState = {| export type ParseClassMemberState = {|
hadConstructor: boolean, hadConstructor: boolean,
hadStaticBlock: boolean,
constructorAllowsSuper: boolean, constructorAllowsSuper: boolean,
|}; |};

View File

@ -1,9 +1,6 @@
{ {
"type": "File", "type": "File",
"start":0,"end":53,"loc":{"start":{"line":1,"column":0},"end":{"line":5,"column":1}}, "start":0,"end":53,"loc":{"start":{"line":1,"column":0},"end":{"line":5,"column":1}},
"errors": [
"SyntaxError: Duplicate static block in the same class (4:2)"
],
"program": { "program": {
"type": "Program", "type": "Program",
"start":0,"end":53,"loc":{"start":{"line":1,"column":0},"end":{"line":5,"column":1}}, "start":0,"end":53,"loc":{"start":{"line":1,"column":0},"end":{"line":5,"column":1}},

View File

@ -30,30 +30,28 @@ export default declare(({ types: t, template, assertVersion }) => {
const { scope } = path; const { scope } = path;
const classBody = path.get("body"); const classBody = path.get("body");
const privateNames = new Set(); const privateNames = new Set();
let staticBlockPath; const body = classBody.get("body");
for (const path of classBody.get("body")) { for (const path of body) {
if (path.isPrivate()) { if (path.isPrivate()) {
privateNames.add(path.get("key.id").node.name); privateNames.add(path.get("key.id").node.name);
} else if (path.isStaticBlock()) {
staticBlockPath = path;
} }
} }
if (!staticBlockPath) { for (const path of body) {
return; if (!path.isStaticBlock()) continue;
} const staticBlockPrivateId = generateUid(scope, privateNames);
privateNames.add(staticBlockPrivateId);
const staticBlockRef = t.privateName( const staticBlockRef = t.privateName(
t.identifier(generateUid(scope, privateNames)), t.identifier(staticBlockPrivateId),
); );
classBody.pushContainer( path.replaceWith(
"body",
t.classPrivateProperty( t.classPrivateProperty(
staticBlockRef, staticBlockRef,
template.expression.ast`(() => { ${staticBlockPath.node.body} })()`, template.expression.ast`(() => { ${path.node.body} })()`,
[], [],
/* static */ true, /* static */ true,
), ),
); );
staticBlockPath.remove(); }
}, },
}, },
}; };

View File

@ -1,7 +1,7 @@
class Foo { class Foo {
static bar = 42;
static { static {
this.foo = Foo.bar; this.foo = Foo.bar;
} }
static bar = 42;
} }
expect(Foo.foo).toBe(42); expect(Foo.foo).toBe(42);

View File

@ -0,0 +1,7 @@
class Foo {
static bar = 42;
static {
this.foo = Foo.bar;
}
}
expect(Foo.foo).toBe(42);

View File

@ -0,0 +1,8 @@
class Foo {
static bar = 42;
static #_ = (() => {
this.foo = Foo.bar;
})();
}
expect(Foo.foo).toBe(42);

View File

@ -1,7 +1,7 @@
class Foo { class Foo {
static bar = 42;
static { static {
this.foo = this.bar; this.foo = this.bar;
} }
static bar = 42;
} }
expect(Foo.foo).toBe(42); expect(Foo.foo).toBe(42);

View File

@ -1,7 +1,7 @@
class Foo { class Foo {
static bar = 42;
static { static {
this.foo = this.bar; this.foo = this.bar;
} }
static bar = 42;
} }
expect(Foo.foo).toBe(42); expect(Foo.foo).toBe(42);

View File

@ -1,8 +1,14 @@
class Foo { class Foo {
static #bar = 21; static #bar = 21;
static { static {
this.foo = this.#bar + this.qux; this.foo = this.#bar;
this.qux1 = this.qux;
} }
static qux = 21; static qux = 21;
static {
this.qux2 = this.qux;
}
} }
expect(Foo.foo).toBe(42); expect(Foo.foo).toBe(21);
expect(Foo.qux1).toBe(undefined);
expect(Foo.qux2).toBe(21);

View File

@ -0,0 +1,11 @@
class Foo {
static #bar = 21;
static {
this.foo = this.#bar;
this.qux1 = this.qux;
}
static qux = 21;
static {
this.qux2 = this.qux;
}
}

View File

@ -0,0 +1,11 @@
class Foo {
static #bar = 21;
static #_ = (() => {
this.foo = this.#bar;
this.qux1 = this.qux;
})();
static qux = 21;
static #_2 = (() => {
this.qux2 = this.qux;
})();
}

View File

@ -0,0 +1,10 @@
class Base {
constructor() {
this.Foo = class {
static {
this.foo = new.target;
}
}
}
}
expect((new Base).Foo.foo).toBe(undefined);

View File

@ -0,0 +1,10 @@
class Base {
constructor() {
this.Foo = class {
static {
this.foo = new.target;
}
}
}
}
expect((new Base).Foo.foo).toBe(undefined);

View File

@ -0,0 +1,12 @@
class Base {
constructor() {
this.Foo = class {
static #_ = (() => {
this.foo = new.target;
})();
};
}
}
expect(new Base().Foo.foo).toBe(undefined);

View File

@ -0,0 +1,10 @@
class Base {
constructor() {
this.Foo = class {
static {
this.foo = new.target;
}
}
}
}
expect((new Base).Foo.foo).toBe(undefined);

View File

@ -0,0 +1,10 @@
class Base {
constructor() {
this.Foo = class {
static {
this.foo = new.target;
}
}
}
}
expect((new Base).Foo.foo).toBe(undefined);

View File

@ -1,7 +1,7 @@
class Foo { class Foo {
static bar = 42;
static { static {
this.foo = Foo.bar; this.foo = Foo.bar;
} }
static bar = 42;
} }
expect(Foo.foo).toBe(42); expect(Foo.foo).toBe(42);

View File

@ -1,7 +1,7 @@
class Foo { class Foo {
static bar = 42;
static { static {
this.foo = Foo.bar; this.foo = Foo.bar;
} }
static bar = 42;
} }
expect(Foo.foo).toBe(42); expect(Foo.foo).toBe(42);

View File

@ -1,7 +1,7 @@
class Foo { class Foo {
static bar = 42;
static { static {
this.foo = this.bar; this.foo = this.bar;
} }
static bar = 42;
} }
expect(Foo.foo).toBe(42); expect(Foo.foo).toBe(42);

View File

@ -1,7 +1,7 @@
class Foo { class Foo {
static bar = 42;
static { static {
this.foo = this.bar; this.foo = this.bar;
} }
static bar = 42;
} }
expect(Foo.foo).toBe(42); expect(Foo.foo).toBe(42);

View File

@ -1,8 +1,14 @@
class Foo { class Foo {
static #bar = 21; static #bar = 21;
static { static {
this.foo = this.#bar + this.qux; this.foo = this.#bar;
this.qux1 = this.qux;
} }
static qux = 21; static qux = 21;
static {
this.qux2 = this.qux;
}
} }
expect(Foo.foo).toBe(42); expect(Foo.foo).toBe(21);
expect(Foo.qux1).toBe(undefined);
expect(Foo.qux2).toBe(21);

View File

@ -0,0 +1,11 @@
class Foo {
static #bar = 21;
static {
this.foo = this.#bar;
this.qux1 = this.qux;
}
static qux = 21;
static {
this.qux2 = this.qux;
}
}

View File

@ -0,0 +1,26 @@
var _bar = babelHelpers.classPrivateFieldLooseKey("bar");
var _ = babelHelpers.classPrivateFieldLooseKey("_");
var _2 = babelHelpers.classPrivateFieldLooseKey("_2");
class Foo {}
Object.defineProperty(Foo, _bar, {
writable: true,
value: 21
});
Object.defineProperty(Foo, _, {
writable: true,
value: (() => {
Foo.foo = babelHelpers.classPrivateFieldLooseBase(Foo, _bar)[_bar];
Foo.qux1 = Foo.qux;
})()
});
Foo.qux = 21;
Object.defineProperty(Foo, _2, {
writable: true,
value: (() => {
Foo.qux2 = Foo.qux;
})()
});

View File

@ -0,0 +1,10 @@
class Base {
constructor() {
this.Foo = class {
static {
this.foo = new.target;
}
}
}
}
expect((new Base).Foo.foo).toBe(undefined);

View File

@ -0,0 +1,11 @@
class Base {
constructor() {
this.Foo = class {
static {
// fixme: new.target should be undefined after transformed
this.foo = new.target;
}
}
}
}
expect((new Base).Foo.foo).toBe(undefined);

View File

@ -1,7 +1,7 @@
class Foo { class Foo {
static bar = 42;
static { static {
this.foo = Foo.bar; this.foo = Foo.bar;
} }
static bar = 42;
} }
expect(Foo.foo).toBe(42); expect(Foo.foo).toBe(42);

View File

@ -1,7 +1,7 @@
class Foo { class Foo {
static bar = 42;
static { static {
this.foo = Foo.bar; this.foo = Foo.bar;
} }
static bar = 42;
} }
expect(Foo.foo).toBe(42); expect(Foo.foo).toBe(42);

View File

@ -1,7 +1,7 @@
class Foo { class Foo {
static bar = 42;
static { static {
this.foo = this.bar; this.foo = this.bar;
} }
static bar = 42;
} }
expect(Foo.foo).toBe(42); expect(Foo.foo).toBe(42);

View File

@ -1,7 +1,7 @@
class Foo { class Foo {
static bar = 42;
static { static {
this.foo = this.bar; this.foo = this.bar;
} }
static bar = 42;
} }
expect(Foo.foo).toBe(42); expect(Foo.foo).toBe(42);

View File

@ -1,8 +1,14 @@
class Foo { class Foo {
static #bar = 21; static #bar = 21;
static { static {
this.foo = this.#bar + this.qux; this.foo = this.#bar;
this.qux1 = this.qux;
} }
static qux = 21; static qux = 21;
static {
this.qux2 = this.qux;
}
} }
expect(Foo.foo).toBe(42); expect(Foo.foo).toBe(21);
expect(Foo.qux1).toBe(undefined);
expect(Foo.qux2).toBe(21);

View File

@ -0,0 +1,11 @@
class Foo {
static #bar = 21;
static {
this.foo = this.#bar;
this.qux1 = this.qux;
}
static qux = 21;
static {
this.qux2 = this.qux;
}
}

View File

@ -0,0 +1,20 @@
class Foo {}
var _bar = {
writable: true,
value: 21
};
var _ = {
writable: true,
value: (() => {
Foo.foo = babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _bar);
Foo.qux1 = Foo.qux;
})()
};
babelHelpers.defineProperty(Foo, "qux", 21);
var _2 = {
writable: true,
value: (() => {
Foo.qux2 = Foo.qux;
})()
};