Skip to content

Commit

Permalink
fix accessors when class-field is unsupported
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed May 4, 2024
1 parent 1f28305 commit 85fd597
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 17 deletions.
35 changes: 18 additions & 17 deletions internal/bundler_tests/snapshots/snapshots_lower.txt
Expand Up @@ -436,7 +436,7 @@ class StaticPrivate {
}

---------- /out/ts-assign/ts-assign.js ----------
var _a, _b;
var _a, _b, _a2, __a;
class Foo {
#one = 1;
get one() {
Expand Down Expand Up @@ -483,30 +483,30 @@ class Foo {
}
class Normal {
constructor() {
this.#a = b;
__privateAdd(this, _a2, b);
this.c = d;
}
#a;
get a() {
return this.#a;
return __privateGet(this, _a2);
}
set a(_) {
this.#a = _;
__privateSet(this, _a2, _);
}
}
_a2 = new WeakMap();
class Private {
constructor() {
this.#_a = b;
__privateAdd(this, __a, b);
this.c = d;
}
#_a;
get #a() {
return this.#_a;
return __privateGet(this, __a);
}
set #a(_) {
this.#_a = _;
__privateSet(this, __a, _);
}
}
__a = new WeakMap();
class StaticNormal {
static #a = b;
static get a() {
Expand Down Expand Up @@ -571,6 +571,7 @@ class StaticPrivate {
}

---------- /out/ts-assign/ts-assign.js ----------
var _a, __a;
class Foo {
accessor one = 1;
accessor #two = 2;
Expand All @@ -581,30 +582,30 @@ class Foo {
}
class Normal {
constructor() {
this.#a = b;
__privateAdd(this, _a, b);
this.c = d;
}
#a;
get a() {
return this.#a;
return __privateGet(this, _a);
}
set a(_) {
this.#a = _;
__privateSet(this, _a, _);
}
}
_a = new WeakMap();
class Private {
constructor() {
this.#_a = b;
__privateAdd(this, __a, b);
this.c = d;
}
#_a;
get #a() {
return this.#_a;
return __privateGet(this, __a);
}
set #a(_) {
this.#_a = _;
__privateSet(this, __a, _);
}
}
__a = new WeakMap();
class StaticNormal {
static accessor a = b;
static {
Expand Down
7 changes: 7 additions & 0 deletions internal/js_parser/js_parser_lower_class.go
Expand Up @@ -1462,6 +1462,13 @@ func (ctx *lowerClassContext) rewriteAutoAccessorToGetSet(
// Replace this accessor with other properties
loc := keyExprNoSideEffects.Loc
storagePrivate := &js_ast.EPrivateIdentifier{Ref: storageRef}
if mustLowerField {
// Forward the accessor's lowering status on to the storage field. If we
// don't do this, then we risk having the underlying private symbol
// behaving differently than if it were authored manually (e.g. being
// placed outside of the class body, which is a syntax error).
p.symbols[storageRef.InnerIndex].Flags |= ast.PrivateSymbolMustBeLowered
}
storageNeedsToBeLowered := p.privateSymbolNeedsToBeLowered(storagePrivate)
storageProp := js_ast.Property{
Loc: prop.Loc,
Expand Down
101 changes: 101 additions & 0 deletions internal/js_parser/js_parser_lower_test.go
Expand Up @@ -794,4 +794,105 @@ func TestLowerAutoAccessors(t *testing.T) {
"class Foo {\n static #x = null;\n static get x() {\n return this.#x;\n }\n static set x(_) {\n this.#x = _;\n }\n}\n")
expectPrintedWithUnsupportedFeatures(t, compat.Decorators, "class Foo { static accessor [x] = null }",
"var _a;\nclass Foo {\n static #a = null;\n static get [_a = x]() {\n return this.#a;\n }\n static set [_a](_) {\n this.#a = _;\n }\n}\n")

// Test various combinations of flags
expectPrintedWithUnsupportedFeatures(t, compat.Decorators|compat.ClassPrivateField, "class Foo { accessor x = null }",
`var _x;
class Foo {
constructor() {
__privateAdd(this, _x, null);
}
get x() {
return __privateGet(this, _x);
}
set x(_) {
__privateSet(this, _x, _);
}
}
_x = new WeakMap();
`)
expectPrintedWithUnsupportedFeatures(t, compat.Decorators|compat.ClassPrivateStaticField, "class Foo { static accessor x = null }",
`var _x;
class Foo {
static get x() {
return __privateGet(this, _x);
}
static set x(_) {
__privateSet(this, _x, _);
}
}
_x = new WeakMap();
__privateAdd(Foo, _x, null);
`)
expectPrintedWithUnsupportedFeatures(t, compat.Decorators|compat.ClassField|compat.ClassPrivateField, "class Foo { accessor x = null }",
`var _x;
class Foo {
constructor() {
__privateAdd(this, _x, null);
}
get x() {
return __privateGet(this, _x);
}
set x(_) {
__privateSet(this, _x, _);
}
}
_x = new WeakMap();
`)
expectPrintedWithUnsupportedFeatures(t, compat.Decorators|compat.ClassStaticField|compat.ClassPrivateStaticField, "class Foo { static accessor x = null }",
`var _x;
class Foo {
static get x() {
return __privateGet(this, _x);
}
static set x(_) {
__privateSet(this, _x, _);
}
}
_x = new WeakMap();
__privateAdd(Foo, _x, null);
`)
expectPrintedWithUnsupportedFeatures(t, compat.Decorators|compat.ClassField|compat.ClassPrivateField, "class Foo { accessor x = 1; static accessor y = 2 }",
`var _x, _y;
class Foo {
constructor() {
__privateAdd(this, _x, 1);
}
get x() {
return __privateGet(this, _x);
}
set x(_) {
__privateSet(this, _x, _);
}
static get y() {
return __privateGet(this, _y);
}
static set y(_) {
__privateSet(this, _y, _);
}
}
_x = new WeakMap();
_y = new WeakMap();
__privateAdd(Foo, _y, 2);
`)
expectPrintedWithUnsupportedFeatures(t, compat.Decorators|compat.ClassStaticField|compat.ClassPrivateStaticField, "class Foo { accessor x = 1; static accessor y = 2 }",
`var _y;
class Foo {
#x = 1;
get x() {
return this.#x;
}
set x(_) {
this.#x = _;
}
static get y() {
return __privateGet(this, _y);
}
static set y(_) {
__privateSet(this, _y, _);
}
}
_y = new WeakMap();
__privateAdd(Foo, _y, 2);
`)
}
20 changes: 20 additions & 0 deletions scripts/end-to-end-tests.js
Expand Up @@ -5732,6 +5732,26 @@ for (let flags of [['--target=es2022'], ['--target=es6'], ['--bundle', '--target
}`,
}),

// Check various combinations of flags
test(['in.ts', '--outfile=node.js', '--supported:class-field=false'].concat(flags), {
'in.ts': `
class Foo {
accessor foo = 1
static accessor bar = 2
}
if (new Foo().foo !== 1 || Foo.bar !== 2) throw 'fail'
`,
}),
test(['in.ts', '--outfile=node.js', '--supported:class-static-field=false'].concat(flags), {
'in.ts': `
class Foo {
accessor foo = 1
static accessor bar = 2
}
if (new Foo().foo !== 1 || Foo.bar !== 2) throw 'fail'
`,
}),

// Make sure class body side effects aren't reordered
test(['in.ts', '--outfile=node.js', '--supported:class-field=false'].concat(flags), {
'in.ts': `
Expand Down

0 comments on commit 85fd597

Please sign in to comment.