mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-19 22:04:48 +00:00
fix(transformer): avoid synthetic private fields for static blocks (#27300)
## Summary Fixes #27244. Lowering each static block to a synthetic private static field can introduce a field after earlier static initialization has made the class non-extensible. Under the proposal to make non-extensibility apply to private fields, this adds an observable TypeError. This change folds blocks into subsequent static field initializers and defers trailing blocks through a closure initialized safely inside the class. Any synthetic carrier field is initialized before the deferred blocks execute. ## Changes - Preserve static initialization order and class-expression results, including recursive initialization. - Keep class declarations in their temporal dead zone until initialization completes, while preserving the separate inner class binding and export behavior. - Preserve inferred function and class names, including computed, private, and symbol keys. Capture computed names for each class evaluation so later evaluations cannot overwrite them. - Fix the CommonJS and ESM `setFunctionName` helpers to distinguish `Symbol("")` from `Symbol()`, producing `"[]"` and `""` respectively. - Introduce wrapper scopes only when the transformation needs temporary bindings. - Add regression fixtures and update expected outputs and conformance reports. --------- Co-authored-by: Dunqing <dengqing0821@gmail.com>
This commit is contained in:
1 parent
676cc60a96
commit
aec14bc7d2
69 files changed
+2151
-109
No files matched your search
@@ -197,6 +197,8 @@ pub enum Helper {
|
||||
UsingCtx,
|
||||
/// Runtime helper `taggedTemplateLiteral`.
|
||||
TaggedTemplateLiteral,
|
||||
/// Runtime helper `setFunctionName`.
|
||||
SetFunctionName,
|
||||
}
|
||||
|
||||
impl Helper {
|
||||
@@ -232,6 +234,7 @@ impl Helper {
|
||||
Self::DecorateMetadata => "decorateMetadata",
|
||||
Self::UsingCtx => "usingCtx",
|
||||
Self::TaggedTemplateLiteral => "taggedTemplateLiteral",
|
||||
Self::SetFunctionName => "setFunctionName",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -17,7 +17,7 @@ pub struct ES2022<'a> {
|
||||
options: ES2022Options,
|
||||
|
||||
// Plugins
|
||||
class_static_block: Option<ClassStaticBlock>,
|
||||
class_static_block: Option<ClassStaticBlock<'a>>,
|
||||
class_properties: Option<ClassProperties<'a>>,
|
||||
}
|
||||
|
||||
@@ -43,6 +43,12 @@ impl ES2022<'_> {
|
||||
}
|
||||
|
||||
impl<'a> Traverse<'a, TransformState<'a>> for ES2022<'a> {
|
||||
fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if let Some(class_static_block) = &mut self.class_static_block {
|
||||
class_static_block.enter_statement(stmt, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline] // Because this is a no-op in release mode
|
||||
fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if let Some(class_properties) = &mut self.class_properties {
|
||||
@@ -54,12 +60,16 @@ impl<'a> Traverse<'a, TransformState<'a>> for ES2022<'a> {
|
||||
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if let Some(class_properties) = &mut self.class_properties {
|
||||
class_properties.enter_expression(expr, ctx);
|
||||
} else if let Some(class_static_block) = &mut self.class_static_block {
|
||||
class_static_block.enter_expression(expr, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if let Some(class_properties) = &mut self.class_properties {
|
||||
class_properties.exit_expression(expr, ctx);
|
||||
} else if let Some(class_static_block) = &mut self.class_static_block {
|
||||
class_static_block.exit_expression(expr, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +89,8 @@ impl<'a> Traverse<'a, TransformState<'a>> for ES2022<'a> {
|
||||
fn exit_class(&mut self, class: &mut Class<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if let Some(class_properties) = &mut self.class_properties {
|
||||
class_properties.exit_class(class, ctx);
|
||||
} else if let Some(class_static_block) = &mut self.class_static_block {
|
||||
class_static_block.exit_class(class, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,6 +111,8 @@ impl<'a> Traverse<'a, TransformState<'a>> for ES2022<'a> {
|
||||
) {
|
||||
if let Some(class_properties) = &mut self.class_properties {
|
||||
class_properties.enter_property_definition(prop, ctx);
|
||||
} else if let Some(class_static_block) = &mut self.class_static_block {
|
||||
class_static_block.enter_property_definition(prop, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -633,6 +633,7 @@ impl<'a> Traverse<'a, TransformState<'a>> for TransformerImpl<'a> {
|
||||
}
|
||||
self.x2_es2018.enter_statement(stmt, ctx);
|
||||
self.x2_es2026.enter_statement(stmt, ctx);
|
||||
self.x2_es2022.enter_statement(stmt, ctx);
|
||||
}
|
||||
|
||||
fn enter_declaration(&mut self, decl: &mut Declaration<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import _typeof from "./typeof.js";
|
||||
function setFunctionName(e, t, n) {
|
||||
"symbol" == _typeof(t) && (t = (t = t.description) ? "[" + t + "]" : "");
|
||||
"symbol" == _typeof(t) && (t = void 0 === (t = t.description) ? "" : "[" + t + "]");
|
||||
try {
|
||||
Object.defineProperty(e, "name", {
|
||||
configurable: !0,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
var _typeof = require("./typeof.js")["default"];
|
||||
function setFunctionName(e, t, n) {
|
||||
"symbol" == _typeof(t) && (t = (t = t.description) ? "[" + t + "]" : "");
|
||||
"symbol" == _typeof(t) && (t = void 0 === (t = t.description) ? "" : "[" + t + "]");
|
||||
try {
|
||||
Object.defineProperty(e, "name", {
|
||||
configurable: !0,
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
var _staticBlock;
|
||||
let Foo = (class Foo {
|
||||
static bar = ((_value) => (_staticBlock = () => (this.foo = Foo.bar, this), _value))(42);
|
||||
}, _staticBlock());
|
||||
expect(Foo.foo).toBe(42);
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
var _staticBlock;
|
||||
let Foo = (class Foo {
|
||||
static bar = ((_value) => (_staticBlock = () => (this.foo = this.bar, this), _value))(42);
|
||||
}, _staticBlock());
|
||||
expect(Foo.foo).toBe(42);
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
var _staticBlock, _staticBlock2;
|
||||
let Foo = ({ "Foo": class {
|
||||
static #_ = _staticBlock = () => ((() => {
|
||||
expect(this.name).toBe("Foo");
|
||||
if (false) use(this);
|
||||
})(), this);
|
||||
} }["Foo"], _staticBlock());
|
||||
let obj = { ["someName"]: ({ "someName": class {
|
||||
static x = ((_value) => (_staticBlock2 = () => ((() => {
|
||||
if (false) use(this);
|
||||
expect(this.name).toBe("someName");
|
||||
})(), this), _value))(expect(this.name).toBe("someName"));
|
||||
} }["someName"], _staticBlock2()) };
|
||||
expect(Foo.name).toBe("Foo");
|
||||
expect(obj.someName.name).toBe("someName");
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
var _staticBlock, _staticBlock2, _staticBlock3;
|
||||
let Foo = (class Foo extends (class extends (class Base {
|
||||
static #_ = _staticBlock = () => (this.qux = 21, this);
|
||||
}, _staticBlock()) {
|
||||
static #_ = _staticBlock2 = () => (this.bar = 21, this);
|
||||
}, _staticBlock2()) {
|
||||
static #_ = _staticBlock3 = () => (this.foo = this.bar + this.qux, this);
|
||||
}, _staticBlock3());
|
||||
expect(Foo.foo).toBe(42);
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
var _staticBlock;
|
||||
let Foo = (class Foo {
|
||||
static #bar = 21;
|
||||
static qux = ((_value) => (_staticBlock = () => (this.qux2 = this.qux, this), _value))(((() => {
|
||||
this.foo = this.#bar;
|
||||
this.qux1 = this.qux;
|
||||
})(), (() => {
|
||||
if (foo) bar;
|
||||
})(), 21));
|
||||
}, _staticBlock());
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
var _staticBlock;
|
||||
let Foo = (class Foo {
|
||||
static #_() {}
|
||||
static #_2 = _staticBlock = () => (this.foo = this.#_, this);
|
||||
}, _staticBlock());
|
||||
expect(Foo.foo).toBe(42);
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
var _staticBlock;
|
||||
let C = (class C {
|
||||
static #_ = _staticBlock = () => ((() => {})(), this.foo = 42, (() => {
|
||||
this.bar = 42;
|
||||
this.bar = 42;
|
||||
})(), this);
|
||||
}, _staticBlock());
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
var _staticBlock;
|
||||
let res = [];
|
||||
let foo = 1;
|
||||
let A = (class A {
|
||||
static x = ((_value) => (_staticBlock = () => ((() => {
|
||||
var foo = 4;
|
||||
})(), res.push(foo), this), _value))(((() => {
|
||||
var foo = 3;
|
||||
})(), res.push(foo), void 0));
|
||||
}, _staticBlock());
|
||||
expect(res).toEqual([1, 1]);
|
||||
@@ -1,6 +1,6 @@
|
||||
commit: 4f4ef5d4
|
||||
|
||||
Passed: 731/1166
|
||||
Passed: 741/1166
|
||||
|
||||
# All Passed:
|
||||
* babel-plugin-transform-logical-assignment-operators
|
||||
@@ -369,37 +369,7 @@ x Output mismatch
|
||||
x Output mismatch
|
||||
|
||||
|
||||
# babel-plugin-transform-class-static-block (11/24)
|
||||
* class-static-block/before-static-fields/input.js
|
||||
x Output mismatch
|
||||
|
||||
* class-static-block/class-binding/input.js
|
||||
x Output mismatch
|
||||
|
||||
* class-static-block/class-declaration/input.js
|
||||
x Output mismatch
|
||||
|
||||
* class-static-block/class-inferred-name/input.js
|
||||
x Output mismatch
|
||||
|
||||
* class-static-block/in-class-heritage/input.js
|
||||
x Output mismatch
|
||||
|
||||
* class-static-block/multiple-static-initializers/input.js
|
||||
x Output mismatch
|
||||
|
||||
* class-static-block/name-conflict/input.js
|
||||
x Output mismatch
|
||||
|
||||
* class-static-block/new-target/input.js
|
||||
x Output mismatch
|
||||
|
||||
* class-static-block/preserve-comments/input.js
|
||||
x Output mismatch
|
||||
|
||||
* class-static-block/var-scope/input.js
|
||||
x Output mismatch
|
||||
|
||||
# babel-plugin-transform-class-static-block (21/24)
|
||||
* integration/in-class-heritage/input.js
|
||||
x Output mismatch
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
commit: 4f4ef5d4
|
||||
|
||||
Passed: 282/410
|
||||
Passed: 298/426
|
||||
|
||||
# All Passed:
|
||||
* babel-plugin-transform-class-static-block
|
||||
|
||||
@@ -2,7 +2,7 @@ commit: 4f4ef5d4
|
||||
|
||||
node: v26.5.0
|
||||
|
||||
Passed: 23 of 25 (92.00%)
|
||||
Passed: 34 of 36 (94.44%)
|
||||
|
||||
Failures:
|
||||
|
||||
|
||||
+4
-8
@@ -1,9 +1,5 @@
|
||||
var _staticBlock;
|
||||
let a, b, d, e;
|
||||
|
||||
class C {
|
||||
static #_ = a = this;
|
||||
static #_2 = [b, c] = this;
|
||||
static #_3 = d ??= this;
|
||||
static #_4 = e.f = this;
|
||||
static #_5 = [g.h, i] = this;
|
||||
}
|
||||
let C = (class C {
|
||||
static #_ = _staticBlock = () => (a = this, [b, c] = this, d ??= this, e.f = this, [g.h, i] = this, this);
|
||||
}, _staticBlock());
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
class C {
|
||||
static #_ = C;
|
||||
static #_2 = x;
|
||||
}
|
||||
var _staticBlock;
|
||||
let C = (class C {
|
||||
static #_ = _staticBlock = () => (C, x, this);
|
||||
}, _staticBlock());
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
class C {
|
||||
static #_ = (() => {})();
|
||||
static #_2 = (() => {})();
|
||||
}
|
||||
var _staticBlock;
|
||||
let C = (class C {
|
||||
static #_ = _staticBlock = () => ((() => {})(), (() => {})(), this);
|
||||
}, _staticBlock());
|
||||
+5
-6
@@ -1,11 +1,10 @@
|
||||
var _staticBlock;
|
||||
let x, y;
|
||||
|
||||
class C {
|
||||
static #_ = x = (() => this)();
|
||||
static #_2 = (() => {
|
||||
let C = (class C {
|
||||
static #_ = _staticBlock = () => (x = (() => this)(), (() => {
|
||||
if (true) {
|
||||
y = this;
|
||||
z = this;
|
||||
}
|
||||
})();
|
||||
}
|
||||
})(), this);
|
||||
}, _staticBlock());
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
const classes = [];
|
||||
for (const key of ['a', 'b']) {
|
||||
classes.push(class { [key] = class { static { this.seen = this.name; } }; });
|
||||
}
|
||||
expect(new classes[0]().a.seen).toBe("a");
|
||||
expect(new classes[1]().b.seen).toBe("b");
|
||||
let nextKey = "first";
|
||||
class Factory {
|
||||
Outer = class { [nextKey] = class { static { this.seen = this.name; } }; };
|
||||
}
|
||||
const First = new Factory().Outer;
|
||||
nextKey = "second";
|
||||
const Second = new Factory().Outer;
|
||||
expect(new First().first.seen).toBe("first");
|
||||
expect(new Second().second.seen).toBe("second");
|
||||
function parameter(Outer = class { [nextKey] = class { static { this.seen = this.name; } }; }) {
|
||||
return Outer;
|
||||
}
|
||||
nextKey = "third";
|
||||
const Third = parameter();
|
||||
nextKey = "fourth";
|
||||
const Fourth = parameter();
|
||||
expect(new Third().third.seen).toBe("third");
|
||||
expect(new Fourth().fourth.seen).toBe("fourth");
|
||||
const repeated = [];
|
||||
while (repeated.length < 2 && repeated.push(class {
|
||||
[String(repeated.length)] = class { static { this.seen = this.name; } };
|
||||
})) {}
|
||||
expect(new repeated[0]()["0"].seen).toBe("0");
|
||||
expect(new repeated[1]()["1"].seen).toBe("1");
|
||||
function* suspended() {
|
||||
const result = [];
|
||||
for (const key of ["a", "b"]) {
|
||||
result.push(class { [yield key] = class { static { this.seen = this.name; } }; });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
const generator = suspended();
|
||||
expect(generator.next().value).toBe("a");
|
||||
expect(generator.next("a").value).toBe("b");
|
||||
const suspendedClasses = generator.next("b").value;
|
||||
expect(new suspendedClasses[0]().a.seen).toBe("a");
|
||||
expect(new suspendedClasses[1]().b.seen).toBe("b");
|
||||
|
||||
function* suspendedHeader() {
|
||||
const result = [];
|
||||
while (result.length < 2 && result.push(class {
|
||||
[yield result.length] = class { static { this.seen = this.name; } };
|
||||
static { Object.freeze(this); }
|
||||
})) {}
|
||||
return result;
|
||||
}
|
||||
const headerGenerator = suspendedHeader();
|
||||
expect(headerGenerator.next().value).toBe(0);
|
||||
expect(headerGenerator.next("a").value).toBe(1);
|
||||
const headerClasses = headerGenerator.next("b").value;
|
||||
expect(new headerClasses[0]().a.seen).toBe("a");
|
||||
expect(new headerClasses[1]().b.seen).toBe("b");
|
||||
expect(headerClasses[0].name).toBe("");
|
||||
expect(Object.isFrozen(headerClasses[0])).toBe(true);
|
||||
function* suspendedName() {
|
||||
const Named = class {
|
||||
[yield "key"] = class { static {} };
|
||||
static seen = this.name;
|
||||
};
|
||||
const Method = class {
|
||||
[yield "key"] = class { static {} };
|
||||
static ["na" + "me"]() {}
|
||||
static seen = this.name;
|
||||
};
|
||||
const Getter = class {
|
||||
[yield "key"] = class { static {} };
|
||||
static get ["na" + "me"]() { return "getter"; }
|
||||
static seen = this.name;
|
||||
};
|
||||
class Declaration {
|
||||
[yield "key"] = class { static {} };
|
||||
}
|
||||
const Original = Declaration;
|
||||
Declaration = null;
|
||||
return [Named, Method, Getter, Original];
|
||||
}
|
||||
const namesGenerator = suspendedName();
|
||||
namesGenerator.next();
|
||||
namesGenerator.next("a");
|
||||
namesGenerator.next("a");
|
||||
namesGenerator.next("a");
|
||||
const [Named, Method, Getter, Original] = namesGenerator.next("a").value;
|
||||
expect(Named.seen).toBe("Named");
|
||||
expect(Method.seen).toBe(Method.name);
|
||||
expect(typeof Method.seen).toBe("function");
|
||||
expect(Getter.seen).toBe("getter");
|
||||
expect(new Original().a.name).toBe("a");
|
||||
|
||||
function* symbolNamedClass() {
|
||||
const key = Symbol("");
|
||||
return { [key]: class {
|
||||
[yield "field"] = class { static {} };
|
||||
} }[key];
|
||||
}
|
||||
const symbolGenerator = symbolNamedClass();
|
||||
expect(symbolGenerator.next().value).toBe("field");
|
||||
const SymbolNamed = symbolGenerator.next("field").value;
|
||||
expect(SymbolNamed.name).toBe("[]");
|
||||
expect(new SymbolNamed().field.name).toBe("field");
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
const classes = [];
|
||||
for (const key of ['a', 'b']) {
|
||||
classes.push(class { [key] = class { static { this.seen = this.name; } }; });
|
||||
}
|
||||
console.log(new classes[0]().a.seen, new classes[1]().b.seen);
|
||||
|
||||
function* suspended() {
|
||||
const classes = [];
|
||||
while (classes.length < 2 && classes.push(class {
|
||||
[yield classes.length] = class { static { this.seen = this.name; } };
|
||||
static { Object.freeze(this); }
|
||||
})) {}
|
||||
return classes;
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
const classes = [];
|
||||
for (const key of ["a", "b"]) {
|
||||
classes.push((() => {
|
||||
let _key;
|
||||
return class {
|
||||
[_key = babelHelpers.toPropertyKey(key)] = (() => {
|
||||
var _staticBlock;
|
||||
return ((_name, _object) => _object[_name])(_key, { [_key]: class {
|
||||
static #_ = _staticBlock = () => (this.seen = this.name, this);
|
||||
} }), _staticBlock();
|
||||
})();
|
||||
};
|
||||
})());
|
||||
}
|
||||
console.log(new classes[0]().a.seen, new classes[1]().b.seen);
|
||||
function* suspended() {
|
||||
var _staticBlock2;
|
||||
let _key2;
|
||||
const classes = [];
|
||||
while (classes.length < 2 && classes.push((class _Class {
|
||||
static #_ = ((_value) => (_staticBlock2 = () => (Object.freeze(this), this), _value))((babelHelpers.setFunctionName(this, ""), _key2));
|
||||
[_key2 = babelHelpers.toPropertyKey(yield classes.length)] = (() => {
|
||||
var _staticBlock3;
|
||||
return { [_Class.#_]: class {
|
||||
static #_ = _staticBlock3 = () => (this.seen = this.name, this);
|
||||
} }[_Class.#_], _staticBlock3();
|
||||
})();
|
||||
}, _staticBlock2()))) {}
|
||||
return classes;
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export default class {
|
||||
static { expect(this.name).toBe("default"); }
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
var _staticBlock;
|
||||
export default ({ "default": class {
|
||||
static #_ = _staticBlock = () => (expect(this.name).toBe("default"), this);
|
||||
} }["default"], _staticBlock());
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
const read = () => A;
|
||||
export default class A {
|
||||
static { expect(() => read()).toThrow(ReferenceError); expect(A).toBe(this); }
|
||||
}
|
||||
expect(read()).toBe(A);
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
var _staticBlock;
|
||||
const read = () => A;
|
||||
let A = (class A {
|
||||
static #_ = _staticBlock = () => ((() => {
|
||||
expect(() => read()).toThrow(ReferenceError);
|
||||
expect(A).toBe(this);
|
||||
})(), this);
|
||||
}, _staticBlock());
|
||||
export { A as default };
|
||||
expect(read()).toBe(A);
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
const read = () => A;
|
||||
export class A {
|
||||
static { expect(() => read()).toThrow(ReferenceError); expect(A).toBe(this); }
|
||||
}
|
||||
expect(read()).toBe(A);
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
var _staticBlock;
|
||||
const read = () => A;
|
||||
export let A = (class A {
|
||||
static #_ = _staticBlock = () => ((() => {
|
||||
expect(() => read()).toThrow(ReferenceError);
|
||||
expect(A).toBe(this);
|
||||
})(), this);
|
||||
}, _staticBlock());
|
||||
expect(read()).toBe(A);
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
const read = () => A;
|
||||
let observed;
|
||||
class A {
|
||||
static {
|
||||
expect(A).toBe(this);
|
||||
try { read(); observed = "initialized"; } catch (error) { observed = error.name; }
|
||||
}
|
||||
}
|
||||
expect(observed).toBe("ReferenceError");
|
||||
expect(read()).toBe(A);
|
||||
const Original = A;
|
||||
A = 42;
|
||||
expect(A).toBe(42);
|
||||
expect(typeof Original).toBe("function");
|
||||
let Saved;
|
||||
let readThrowing;
|
||||
try {
|
||||
readThrowing = () => Throwing;
|
||||
class Throwing {
|
||||
static { Saved = this; throw new Error("stop"); }
|
||||
}
|
||||
} catch {}
|
||||
expect(typeof Saved).toBe("function");
|
||||
expect(readThrowing).toThrow(ReferenceError);
|
||||
class Self {
|
||||
static getSelf() { return Self; }
|
||||
static { expect(() => { Self = null; }).toThrow(TypeError); }
|
||||
}
|
||||
const OriginalSelf = Self;
|
||||
Self = null;
|
||||
expect(OriginalSelf.getSelf()).toBe(OriginalSelf);
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
const read = () => A;
|
||||
class A {
|
||||
static { try { read(); console.log('initialized'); } catch (e) { console.log(e.name); } }
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
var _staticBlock;
|
||||
const read = () => A;
|
||||
let A = (class A {
|
||||
static #_ = _staticBlock = () => ((() => {
|
||||
try {
|
||||
read();
|
||||
console.log("initialized");
|
||||
} catch (e) {
|
||||
console.log(e.name);
|
||||
}
|
||||
})(), this);
|
||||
}, _staticBlock());
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
let index = 0;
|
||||
let recursive;
|
||||
function objectPattern({ value = {
|
||||
[index++]: class {
|
||||
static { if (index < 2) recursive = objectPattern({}); }
|
||||
static [0] = () => {};
|
||||
}
|
||||
} } = {}) { return value; }
|
||||
const result = objectPattern({});
|
||||
expect(index).toBe(2);
|
||||
expect(result[0].name).toBe("0");
|
||||
expect(recursive[1].name).toBe("1");
|
||||
expect(result[0][0].name).toBe("0");
|
||||
|
||||
let arrayIndex = 0;
|
||||
let arrayRecursive;
|
||||
function arrayPattern([value = {
|
||||
[arrayIndex++]: class {
|
||||
static { if (arrayIndex < 2) arrayRecursive = arrayPattern([]); }
|
||||
static [0] = () => {};
|
||||
}
|
||||
}] = []) { return value; }
|
||||
const arrayResult = arrayPattern([]);
|
||||
expect(arrayIndex).toBe(2);
|
||||
expect(arrayResult[0].name).toBe("0");
|
||||
expect(arrayRecursive[1].name).toBe("1");
|
||||
expect(arrayResult[0][0].name).toBe("0");
|
||||
|
||||
let nestedIndex = 0;
|
||||
let nestedRecursive;
|
||||
function nestedPattern({ outer: { value = {
|
||||
[nestedIndex++]: class {
|
||||
static { if (nestedIndex < 2) nestedRecursive = nestedPattern({ outer: {} }); }
|
||||
static [0] = () => {};
|
||||
}
|
||||
} } } = { outer: {} }) { return value; }
|
||||
const nestedResult = nestedPattern({ outer: {} });
|
||||
expect(nestedIndex).toBe(2);
|
||||
expect(nestedResult[0].name).toBe("0");
|
||||
expect(nestedRecursive[1].name).toBe("1");
|
||||
expect(nestedResult[0][0].name).toBe("0");
|
||||
|
||||
let restIndex = 0;
|
||||
let restRecursive;
|
||||
function restPattern(...[{ value = {
|
||||
[restIndex++]: class {
|
||||
static { if (restIndex < 2) restRecursive = restPattern({}); }
|
||||
static [0] = () => {};
|
||||
}
|
||||
} }]) { return value; }
|
||||
const restResult = restPattern({});
|
||||
expect(restIndex).toBe(2);
|
||||
expect(restResult[0].name).toBe("0");
|
||||
expect(restRecursive[1].name).toBe("1");
|
||||
expect(restResult[0][0].name).toBe("0");
|
||||
|
||||
let keyIndex = 0;
|
||||
let keyRecursive;
|
||||
function computedPattern({ [Object.values({
|
||||
[keyIndex++]: class {
|
||||
static { if (keyIndex < 2) keyRecursive = computedPattern({ 1: "inner" }); }
|
||||
static [0] = () => {};
|
||||
}
|
||||
})[0].name]: value }) { return value; }
|
||||
const keyResult = computedPattern({ 0: "outer" });
|
||||
expect(keyIndex).toBe(2);
|
||||
expect(keyResult).toBe("outer");
|
||||
expect(keyRecursive).toBe("inner");
|
||||
|
||||
function parenthesized(C = (((class {
|
||||
static value = 0;
|
||||
static { this.seen = this.name; }
|
||||
})))) { return C; }
|
||||
const Parenthesized = parenthesized();
|
||||
expect([Parenthesized.name, Parenthesized.seen]).toEqual(["C", "C"]);
|
||||
|
||||
function nestedClassDefault(Outer = ((class {
|
||||
static Inner = class {
|
||||
static value = 0;
|
||||
static { this.seen = this.name; }
|
||||
};
|
||||
}))) { return Outer; }
|
||||
const NestedOuter = nestedClassDefault();
|
||||
expect(NestedOuter.name).toBe("Outer");
|
||||
expect([NestedOuter.Inner.name, NestedOuter.Inner.seen]).toEqual(["Inner", "Inner"]);
|
||||
|
||||
function noTemporaryDefault(C = (class {
|
||||
static {}
|
||||
static value = 1;
|
||||
})) { return C; }
|
||||
function bodyDefault(callback = function inner(C = (class {
|
||||
static {}
|
||||
static value = 2;
|
||||
})) { return C; }) { return callback(); }
|
||||
expect([noTemporaryDefault().name, noTemporaryDefault().value]).toEqual(["C", 1]);
|
||||
expect([bodyDefault().name, bodyDefault().value]).toEqual(["C", 2]);
|
||||
|
||||
let SwitchClass;
|
||||
switch (0) {
|
||||
case ({ [0]: class { static {} } }[0], 0):
|
||||
SwitchClass = { [0]: class { static {} } }[0];
|
||||
break;
|
||||
}
|
||||
expect(SwitchClass.name).toBe("0");
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
let index = 0;
|
||||
let recursive;
|
||||
function objectPattern({ value = {
|
||||
[index++]: class {
|
||||
static { if (index < 2) recursive = objectPattern({}); }
|
||||
static [0] = () => {};
|
||||
}
|
||||
} } = {}) { return value; }
|
||||
const result = objectPattern({});
|
||||
|
||||
let arrayIndex = 0;
|
||||
let arrayRecursive;
|
||||
function arrayPattern([value = {
|
||||
[arrayIndex++]: class {
|
||||
static { if (arrayIndex < 2) arrayRecursive = arrayPattern([]); }
|
||||
static [0] = () => {};
|
||||
}
|
||||
}] = []) { return value; }
|
||||
const arrayResult = arrayPattern([]);
|
||||
|
||||
let nestedIndex = 0;
|
||||
let nestedRecursive;
|
||||
function nestedPattern({ outer: { value = {
|
||||
[nestedIndex++]: class {
|
||||
static { if (nestedIndex < 2) nestedRecursive = nestedPattern({ outer: {} }); }
|
||||
static [0] = () => {};
|
||||
}
|
||||
} } } = { outer: {} }) { return value; }
|
||||
const nestedResult = nestedPattern({ outer: {} });
|
||||
|
||||
let restIndex = 0;
|
||||
let restRecursive;
|
||||
function restPattern(...[{ value = {
|
||||
[restIndex++]: class {
|
||||
static { if (restIndex < 2) restRecursive = restPattern({}); }
|
||||
static [0] = () => {};
|
||||
}
|
||||
} }]) { return value; }
|
||||
const restResult = restPattern({});
|
||||
|
||||
let keyIndex = 0;
|
||||
let keyRecursive;
|
||||
function computedPattern({ [Object.values({
|
||||
[keyIndex++]: class {
|
||||
static { if (keyIndex < 2) keyRecursive = computedPattern({ 1: "inner" }); }
|
||||
static [0] = () => {};
|
||||
}
|
||||
})[0].name]: value }) { return value; }
|
||||
const keyResult = computedPattern({ 0: "outer" });
|
||||
|
||||
function parenthesized(C = (((class {
|
||||
static value = 0;
|
||||
static { this.seen = this.name; }
|
||||
})))) { return C; }
|
||||
const Parenthesized = parenthesized();
|
||||
|
||||
function nestedClassDefault(Outer = ((class {
|
||||
static Inner = class {
|
||||
static value = 0;
|
||||
static { this.seen = this.name; }
|
||||
};
|
||||
}))) { return Outer; }
|
||||
const NestedOuter = nestedClassDefault();
|
||||
|
||||
function noTemporaryDefault(C = (class {
|
||||
static {}
|
||||
static value = 1;
|
||||
})) { return C; }
|
||||
function bodyDefault(callback = function inner(C = (class {
|
||||
static {}
|
||||
static value = 2;
|
||||
})) { return C; }) { return callback(); }
|
||||
|
||||
let SwitchClass;
|
||||
switch (0) {
|
||||
case ({ [0]: class { static {} } }[0], 0):
|
||||
SwitchClass = { [0]: class { static {} } }[0];
|
||||
break;
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"plugins": [],
|
||||
"presets": [["env", {"targets": {"node": "16"}}]]
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
var _staticBlock3;
|
||||
let _key, _key3, _key5, _key7, _key9, _key11;
|
||||
let index = 0;
|
||||
let recursive;
|
||||
function objectPattern({ value = { [_key = babelHelpers.toPropertyKey(index++)]: (() => {
|
||||
let _key2;
|
||||
return ((_name, _object) => _object[_name])(_key, { [_key]: class {
|
||||
static [_key2 = babelHelpers.toPropertyKey(0)] = ((() => {
|
||||
if (index < 2) recursive = objectPattern({});
|
||||
})(), babelHelpers.setFunctionName(() => {}, _key2));
|
||||
} });
|
||||
})() } } = {}) {
|
||||
return value;
|
||||
}
|
||||
const result = objectPattern({});
|
||||
let arrayIndex = 0;
|
||||
let arrayRecursive;
|
||||
function arrayPattern([value = { [_key3 = babelHelpers.toPropertyKey(arrayIndex++)]: (() => {
|
||||
let _key4;
|
||||
return ((_name2, _object2) => _object2[_name2])(_key3, { [_key3]: class {
|
||||
static [_key4 = babelHelpers.toPropertyKey(0)] = ((() => {
|
||||
if (arrayIndex < 2) arrayRecursive = arrayPattern([]);
|
||||
})(), babelHelpers.setFunctionName(() => {}, _key4));
|
||||
} });
|
||||
})() }] = []) {
|
||||
return value;
|
||||
}
|
||||
const arrayResult = arrayPattern([]);
|
||||
let nestedIndex = 0;
|
||||
let nestedRecursive;
|
||||
function nestedPattern({ outer: { value = { [_key5 = babelHelpers.toPropertyKey(nestedIndex++)]: (() => {
|
||||
let _key6;
|
||||
return ((_name3, _object3) => _object3[_name3])(_key5, { [_key5]: class {
|
||||
static [_key6 = babelHelpers.toPropertyKey(0)] = ((() => {
|
||||
if (nestedIndex < 2) nestedRecursive = nestedPattern({ outer: {} });
|
||||
})(), babelHelpers.setFunctionName(() => {}, _key6));
|
||||
} });
|
||||
})() } } } = { outer: {} }) {
|
||||
return value;
|
||||
}
|
||||
const nestedResult = nestedPattern({ outer: {} });
|
||||
let restIndex = 0;
|
||||
let restRecursive;
|
||||
function restPattern(...[{ value = { [_key7 = babelHelpers.toPropertyKey(restIndex++)]: (() => {
|
||||
let _key8;
|
||||
return ((_name4, _object4) => _object4[_name4])(_key7, { [_key7]: class {
|
||||
static [_key8 = babelHelpers.toPropertyKey(0)] = ((() => {
|
||||
if (restIndex < 2) restRecursive = restPattern({});
|
||||
})(), babelHelpers.setFunctionName(() => {}, _key8));
|
||||
} });
|
||||
})() } }]) {
|
||||
return value;
|
||||
}
|
||||
const restResult = restPattern({});
|
||||
let keyIndex = 0;
|
||||
let keyRecursive;
|
||||
function computedPattern({ [Object.values({ [_key9 = babelHelpers.toPropertyKey(keyIndex++)]: (() => {
|
||||
let _key10;
|
||||
return ((_name5, _object5) => _object5[_name5])(_key9, { [_key9]: class {
|
||||
static [_key10 = babelHelpers.toPropertyKey(0)] = ((() => {
|
||||
if (keyIndex < 2) keyRecursive = computedPattern({ 1: "inner" });
|
||||
})(), babelHelpers.setFunctionName(() => {}, _key10));
|
||||
} });
|
||||
})() })[0].name]: value }) {
|
||||
return value;
|
||||
}
|
||||
const keyResult = computedPattern({ 0: "outer" });
|
||||
function parenthesized(C = (() => {
|
||||
var _staticBlock;
|
||||
return { "C": class {
|
||||
static value = ((_value) => (_staticBlock = () => (this.seen = this.name, this), _value))(0);
|
||||
} }["C"], _staticBlock();
|
||||
})()) {
|
||||
return C;
|
||||
}
|
||||
const Parenthesized = parenthesized();
|
||||
function nestedClassDefault(Outer = (() => {
|
||||
var _staticBlock2;
|
||||
return { "Outer": class {
|
||||
static Inner = ({ "Inner": class {
|
||||
static value = ((_value2) => (_staticBlock2 = () => (this.seen = this.name, this), _value2))(0);
|
||||
} }["Inner"], _staticBlock2());
|
||||
} }["Outer"];
|
||||
})()) {
|
||||
return Outer;
|
||||
}
|
||||
const NestedOuter = nestedClassDefault();
|
||||
function noTemporaryDefault(C = class {
|
||||
static value = ((() => {})(), 1);
|
||||
}) {
|
||||
return C;
|
||||
}
|
||||
function bodyDefault(callback = function inner(C = class {
|
||||
static value = ((() => {})(), 2);
|
||||
}) {
|
||||
return C;
|
||||
}) {
|
||||
return callback();
|
||||
}
|
||||
let SwitchClass;
|
||||
switch (0) {
|
||||
case { [_key11 = babelHelpers.toPropertyKey(0)]: (((_name6, _object6) => _object6[_name6])(_key11, { [_key11]: class {
|
||||
static #_ = _staticBlock3 = () => ((() => {})(), this);
|
||||
} }), _staticBlock3()) }[0], 0:
|
||||
var _staticBlock4;
|
||||
let _key12;
|
||||
SwitchClass = { [_key12 = babelHelpers.toPropertyKey(0)]: (((_name7, _object7) => _object7[_name7])(_key12, { [_key12]: class {
|
||||
static #_ = _staticBlock4 = () => ((() => {})(), this);
|
||||
} }), _staticBlock4()) }[0];
|
||||
break;
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
const calls = [];
|
||||
class Before {
|
||||
static { calls.push("before"); }
|
||||
declare static omitted: unknown;
|
||||
}
|
||||
class After {
|
||||
declare static omitted: unknown;
|
||||
static { calls.push("after"); }
|
||||
}
|
||||
class Mixed {
|
||||
static { calls.push("first"); }
|
||||
declare static omitted: unknown;
|
||||
static present = (calls.push("field"), 1);
|
||||
declare static trailing: unknown;
|
||||
static { calls.push("last"); }
|
||||
}
|
||||
expect(calls).toEqual(["before", "after", "first", "field", "last"]);
|
||||
expect(Object.hasOwn(Before, "omitted")).toBe(false);
|
||||
expect(Object.hasOwn(After, "omitted")).toBe(false);
|
||||
expect(Object.hasOwn(Mixed, "omitted")).toBe(false);
|
||||
expect(Object.hasOwn(Mixed, "trailing")).toBe(false);
|
||||
expect(Mixed.present).toBe(1);
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
const calls = [];
|
||||
class Before {
|
||||
static { calls.push("before"); }
|
||||
declare static omitted: unknown;
|
||||
}
|
||||
class After {
|
||||
declare static omitted: unknown;
|
||||
static { calls.push("after"); }
|
||||
}
|
||||
class Mixed {
|
||||
static { calls.push("first"); }
|
||||
declare static omitted: unknown;
|
||||
static present = (calls.push("field"), 1);
|
||||
declare static trailing: unknown;
|
||||
static { calls.push("last"); }
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"plugins": ["transform-typescript"],
|
||||
"presets": [["env", {"targets": {"node": "16"}}]]
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
var _staticBlock, _staticBlock2, _staticBlock3;
|
||||
const calls = [];
|
||||
let Before = (class Before {
|
||||
static #_ = _staticBlock = () => (calls.push("before"), this);
|
||||
}, _staticBlock());
|
||||
let After = (class After {
|
||||
static #_ = _staticBlock2 = () => (calls.push("after"), this);
|
||||
}, _staticBlock2());
|
||||
let Mixed = (class Mixed {
|
||||
static present = ((_value) => (_staticBlock3 = () => (calls.push("last"), this), _value))((calls.push("first"), calls.push("field"), 1));
|
||||
}, _staticBlock3());
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
const key = Symbol("");
|
||||
class A {
|
||||
Computed = class { static [key] = function () {}; static {} static x = 4; };
|
||||
Child = class { static {} static x = 1; };
|
||||
["computed"] = class { static {} static x = 2; };
|
||||
}
|
||||
function create(Child = class { static {} static x = 3; }) {
|
||||
return Child;
|
||||
}
|
||||
const a = new A();
|
||||
expect([a.Child.name, a.Child.x]).toEqual(["Child", 1]);
|
||||
expect([a.computed.name, a.computed.x]).toEqual(["computed", 2]);
|
||||
const child = create();
|
||||
expect([child.name, child.x]).toEqual(["Child", 3]);
|
||||
expect([a.Computed.name, a.Computed[key].name, a.Computed.x]).toEqual(["Computed", "[]", 4]);
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
const key = Symbol("");
|
||||
class A {
|
||||
Computed = class { static [key] = function () {}; static {} static x = 4; };
|
||||
Child = class { static {} static x = 1; };
|
||||
["computed"] = class { static {} static x = 2; };
|
||||
}
|
||||
function create(Child = class { static {} static x = 3; }) {
|
||||
return Child;
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
const key = Symbol("");
|
||||
class A {
|
||||
Computed = class {
|
||||
static [key] = function () {};
|
||||
static x = ((() => {})(), 4);
|
||||
};
|
||||
Child = class {
|
||||
static x = ((() => {})(), 1);
|
||||
};
|
||||
["computed"] = class {
|
||||
static x = ((() => {})(), 2);
|
||||
};
|
||||
}
|
||||
function create(Child = class {
|
||||
static x = ((() => {})(), 3);
|
||||
}) {
|
||||
return Child;
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
const events = [];
|
||||
class A {
|
||||
static x = 42;
|
||||
static #value = 7;
|
||||
static { events.push(this.x, this.#value); Object.preventExtensions(this); }
|
||||
static { events.push(this.x, this.#value); }
|
||||
}
|
||||
expect(A.x).toBe(42);
|
||||
expect(events).toEqual([42, 7, 42, 7]);
|
||||
expect(Object.isExtensible(A)).toBe(false);
|
||||
const BlockOnly = class {
|
||||
static { Object.preventExtensions(this); }
|
||||
static { events.push("done"); }
|
||||
};
|
||||
expect(Object.isExtensible(BlockOnly)).toBe(false);
|
||||
expect(events[4]).toBe("done");
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
class A {
|
||||
static x = Object.preventExtensions(this);
|
||||
static {
|
||||
this.y = 1;
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
var _staticBlock;
|
||||
let A = (class A {
|
||||
static x = ((_value) => (_staticBlock = () => (this.y = 1, this), _value))(Object.preventExtensions(this));
|
||||
}, _staticBlock());
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
function preserveEval(
|
||||
{ value = (eval("var exposed = 7"), class { static {} }) } = {},
|
||||
other = exposed,
|
||||
) {
|
||||
return other;
|
||||
}
|
||||
const result = preserveEval({});
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"sourceType": "script",
|
||||
"presets": [["env", {"targets": {"node": "16"}}]]
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
var _staticBlock;
|
||||
function preserveEval({ value = (eval("var exposed = 7"), class {
|
||||
static #_ = _staticBlock = () => ((() => {})(), this);
|
||||
}, _staticBlock()) } = {}, other = exposed) {
|
||||
return other;
|
||||
}
|
||||
const result = preserveEval({});
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
class A {
|
||||
static {}
|
||||
static C = class { static seen = this.name; };
|
||||
static {}
|
||||
static Custom = class { static name = "custom"; };
|
||||
static {}
|
||||
static Frozen = class { static { Object.freeze(this); } };
|
||||
static {}
|
||||
static #Private = class { static seen = this.name; };
|
||||
static {}
|
||||
static getPrivate() { return this.#Private; }
|
||||
static Method = class { static name() {} static seen = this.name; };
|
||||
static {}
|
||||
static Getter = class { static get name() { return "getter"; } static seen = this.name; };
|
||||
static {}
|
||||
}
|
||||
expect([A.C.name, A.C.seen]).toEqual(["C", "C"]);
|
||||
expect(A.Custom.name).toBe("custom");
|
||||
expect(A.Frozen.name).toBe("Frozen");
|
||||
expect(Object.isFrozen(A.Frozen)).toBe(true);
|
||||
expect([A.getPrivate().name, A.getPrivate().seen]).toEqual(["#Private", "#Private"]);
|
||||
expect(A.Method.seen).toBe(A.Method.name);
|
||||
expect(typeof A.Method.name).toBe("function");
|
||||
expect([A.Getter.name, A.Getter.seen]).toEqual(["getter", "getter"]);
|
||||
const MethodName = class { static name() {} static { this.seen = this.name; } };
|
||||
expect(MethodName.seen).toBe(MethodName.name);
|
||||
expect(typeof MethodName.name).toBe("function");
|
||||
const methodKey = "name";
|
||||
const ComputedMethodName = class { static [methodKey]() {} static { this.seen = this.name; } };
|
||||
expect(ComputedMethodName.seen).toBe(ComputedMethodName.name);
|
||||
expect(typeof ComputedMethodName.name).toBe("function");
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
class A {
|
||||
static {}
|
||||
static C = class { static seen = this.name; };
|
||||
static {}
|
||||
static Custom = class { static name = "custom"; };
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
class A {
|
||||
static C = ((() => {})(), { "C": class {
|
||||
static seen = this.name;
|
||||
} }["C"]);
|
||||
static Custom = ((() => {})(), { "Custom": class {
|
||||
static name = "custom";
|
||||
} }["Custom"]);
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
const A = class { static { this.seen = this.name; } };
|
||||
expect([A.name, A.seen]).toEqual(["A", "A"]);
|
||||
let assigned;
|
||||
assigned = class { static { this.seen = this.name; } };
|
||||
expect([assigned.name, assigned.seen]).toEqual(["assigned", "assigned"]);
|
||||
let logical;
|
||||
logical ||= class { static { this.seen = this.name; } };
|
||||
expect([logical.name, logical.seen]).toEqual(["logical", "logical"]);
|
||||
function defaults(Param = class { static { this.seen = this.name; } }) {
|
||||
return [Param.name, Param.seen];
|
||||
}
|
||||
expect(defaults()).toEqual(["Param", "Param"]);
|
||||
const { value: Destructured = class { static { this.seen = this.name; } } } = {};
|
||||
expect([Destructured.name, Destructured.seen]).toEqual(["Destructured", "Destructured"]);
|
||||
let target;
|
||||
({ target = class { static { this.seen = this.name; } } } = {});
|
||||
expect([target.name, target.seen]).toEqual(["target", "target"]);
|
||||
let other;
|
||||
[other = class { static { this.seen = this.name; } }] = [];
|
||||
expect([other.name, other.seen]).toEqual(["other", "other"]);
|
||||
const named = class Explicit { static { this.seen = this.name; } };
|
||||
expect([named.name, named.seen]).toEqual(["Explicit", "Explicit"]);
|
||||
const anonymous = (0, class { static { this.seen = this.name; } });
|
||||
expect([anonymous.name, anonymous.seen]).toEqual(["", ""]);
|
||||
const object = {
|
||||
entry: class { static { this.seen = this.name; } },
|
||||
[Symbol.for("key")]: class { static { this.seen = this.name; } },
|
||||
["__proto__"]: class { static { this.seen = this.name; } },
|
||||
};
|
||||
expect([object.entry.name, object.entry.seen]).toEqual(["entry", "entry"]);
|
||||
expect([object[Symbol.for("key")].name, object[Symbol.for("key")].seen]).toEqual(["[key]", "[key]"]);
|
||||
expect(object.__proto__.seen).toBe("__proto__");
|
||||
const proto = { __proto__: class { static { this.seen = this.name; } } };
|
||||
expect(Object.getPrototypeOf(proto).seen).toBe("");
|
||||
class Fields {
|
||||
Child = class { static { this.seen = this.name; } };
|
||||
static Child = class { static { this.seen = this.name; } };
|
||||
}
|
||||
expect(new Fields().Child.seen).toBe("Child");
|
||||
expect(Fields.Child.seen).toBe("Child");
|
||||
const receiver = {};
|
||||
receiver.member = class { static { this.seen = this.name; } };
|
||||
expect([receiver.member.name, receiver.member.seen]).toEqual(["", ""]);
|
||||
[receiver.defaulted = class { static { this.seen = this.name; } }] = [];
|
||||
expect([receiver.defaulted.name, receiver.defaulted.seen]).toEqual(["", ""]);
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
const A = class { static { this.seen = this.name; } };
|
||||
const object = { [Symbol.for("key")]: class { static { this.seen = this.name; } } };
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
var _staticBlock, _staticBlock2;
|
||||
let _key;
|
||||
const A = ({ "A": class {
|
||||
static #_ = _staticBlock = () => (this.seen = this.name, this);
|
||||
} }["A"], _staticBlock());
|
||||
const object = { [_key = babelHelpers.toPropertyKey(Symbol.for("key"))]: (((_name, _object) => _object[_name])(_key, { [_key]: class {
|
||||
static #_ = _staticBlock2 = () => (this.seen = this.name, this);
|
||||
} }), _staticBlock2()) };
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
let conversions = 0;
|
||||
let evaluations = 0;
|
||||
const key = { [Symbol.toPrimitive]() { conversions++; return "method"; } };
|
||||
function getKey() { evaluations++; return key; }
|
||||
let fieldName = "before";
|
||||
const symbol = Symbol("symbol");
|
||||
const unnamed = Symbol();
|
||||
class A {
|
||||
static { fieldName = "after"; }
|
||||
static [fieldName] = function () {};
|
||||
static {}
|
||||
static [getKey()] = () => {};
|
||||
static {}
|
||||
static [symbol] = function () {};
|
||||
static {}
|
||||
static [unnamed] = () => {};
|
||||
static {}
|
||||
static #privateFunction = function () {};
|
||||
static {}
|
||||
static #privateArrow = () => {};
|
||||
static {}
|
||||
static 123 = function () {};
|
||||
static {}
|
||||
static getNames() { return [this.#privateFunction.name, this.#privateArrow.name]; }
|
||||
}
|
||||
expect(A.before.name).toBe("before");
|
||||
expect(A.method.name).toBe("method");
|
||||
expect([evaluations, conversions]).toEqual([1, 1]);
|
||||
expect(A[symbol].name).toBe("[symbol]");
|
||||
expect(A[unnamed].name).toBe("");
|
||||
expect(A.getNames()).toEqual(["#privateFunction", "#privateArrow"]);
|
||||
expect(A[123].name).toBe("123");
|
||||
let objectConversions = 0;
|
||||
const objectKey = { toString() { objectConversions++; return "ObjectClass"; } };
|
||||
const object = { [objectKey]: class { static { this.seen = this.name; } } };
|
||||
expect(object.ObjectClass.seen).toBe("ObjectClass");
|
||||
expect(objectConversions).toBe(1);
|
||||
|
||||
for (const [key, name] of [[Symbol(), ""], [Symbol(""), "[]"], [Symbol("symbol"), "[symbol]"]]) {
|
||||
class FunctionField {
|
||||
static {}
|
||||
static [key] = function () {};
|
||||
}
|
||||
class ArrowField {
|
||||
static [key] = () => {};
|
||||
static {}
|
||||
}
|
||||
expect(FunctionField[key].name).toBe(name);
|
||||
expect(ArrowField[key].name).toBe(name);
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
const key = Symbol("method");
|
||||
class A {
|
||||
static {}
|
||||
static [key] = function () {};
|
||||
static {}
|
||||
static #f = () => {};
|
||||
static getName() { return this.#f.name; }
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
let _key;
|
||||
const key = Symbol("method");
|
||||
class A {
|
||||
static [_key = babelHelpers.toPropertyKey(key)] = ((() => {})(), babelHelpers.setFunctionName(function() {}, _key));
|
||||
static #f = ((() => {})(), babelHelpers.setFunctionName(() => {}, "#f"));
|
||||
static getName() {
|
||||
return this.#f.name;
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
class A {
|
||||
static {
|
||||
}
|
||||
static f = function () {};
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
class A {
|
||||
static f = ((() => {})(), babelHelpers.setFunctionName(function () {}, "f"));
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
let depth = 0;
|
||||
const seen = [];
|
||||
class Outer {
|
||||
Child = class {
|
||||
static id = depth++;
|
||||
static child = depth < 2 ? new Outer().Child : null;
|
||||
static { seen.push(this.id); }
|
||||
};
|
||||
}
|
||||
const result = new Outer();
|
||||
expect(seen).toEqual([1, 0]);
|
||||
expect(result.Child.id).toBe(0);
|
||||
expect(result.Child.child.id).toBe(1);
|
||||
let calls = 0;
|
||||
const initialized = [];
|
||||
function make(C = class {
|
||||
static id = calls++;
|
||||
static child = calls < 2 ? make() : null;
|
||||
static { initialized.push(this.id); }
|
||||
}) { return C; }
|
||||
const C = make();
|
||||
expect(initialized).toEqual([1, 0]);
|
||||
expect(C.id).toBe(0);
|
||||
expect(C.child.id).toBe(1);
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
let depth = 0;
|
||||
const seen = [];
|
||||
class Outer {
|
||||
Child = class {
|
||||
static id = depth++;
|
||||
static child = depth < 2 ? new Outer().Child : null;
|
||||
static { seen.push(this.id); }
|
||||
};
|
||||
}
|
||||
const result = new Outer();
|
||||
console.log(JSON.stringify(seen), result.Child.id);
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
let depth = 0;
|
||||
const seen = [];
|
||||
class Outer {
|
||||
Child = (() => {
|
||||
var _staticBlock;
|
||||
return { "Child": class {
|
||||
static id = depth++;
|
||||
static child = ((_value) => (_staticBlock = () => (seen.push(this.id), this), _value))(depth < 2 ? new Outer().Child : null);
|
||||
} }["Child"], _staticBlock();
|
||||
})();
|
||||
}
|
||||
const result = new Outer();
|
||||
console.log(JSON.stringify(seen), result.Child.id);
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
class Names {
|
||||
static {}
|
||||
static asFunction = (function () {} as any);
|
||||
static {}
|
||||
static assertedArrow = (<any>(() => {}));
|
||||
static {}
|
||||
static satisfiesFunction = (function () {} satisfies Function);
|
||||
static {}
|
||||
static nonNullClass = class { static seen = this.name; }!;
|
||||
static {}
|
||||
static asClass = (class { static seen = this.name; } as any);
|
||||
static {}
|
||||
static satisfiesClass = (class { static seen = this.name; } satisfies { new(): unknown; seen: string });
|
||||
static {}
|
||||
static namedFunction = (function Explicit() {} as any);
|
||||
static {}
|
||||
static nestedClass = (((class { static seen = this.name; }) satisfies { new(): unknown; seen: string }) as any);
|
||||
static {}
|
||||
}
|
||||
expect(Names.asFunction.name).toBe("asFunction");
|
||||
expect(Names.assertedArrow.name).toBe("assertedArrow");
|
||||
expect(Names.satisfiesFunction.name).toBe("satisfiesFunction");
|
||||
expect([Names.nonNullClass.name, Names.nonNullClass.seen]).toEqual(["nonNullClass", "nonNullClass"]);
|
||||
expect([Names.asClass.name, Names.asClass.seen]).toEqual(["asClass", "asClass"]);
|
||||
expect([Names.satisfiesClass.name, Names.satisfiesClass.seen]).toEqual(["satisfiesClass", "satisfiesClass"]);
|
||||
expect(Names.namedFunction.name).toBe("Explicit");
|
||||
expect([Names.nestedClass.name, Names.nestedClass.seen]).toEqual(["nestedClass", "nestedClass"]);
|
||||
|
||||
function defaultClass(C = (class {
|
||||
static value = 0;
|
||||
static { this.seen = this.name; }
|
||||
} as any)) { return C; }
|
||||
const Default = defaultClass();
|
||||
expect([Default.name, Default.seen]).toEqual(["C", "C"]);
|
||||
const objectClasses = {
|
||||
ObjectClass: (class {
|
||||
static value = 0;
|
||||
static { this.seen = this.name; }
|
||||
} as any),
|
||||
};
|
||||
expect([objectClasses.ObjectClass.name, objectClasses.ObjectClass.seen]).toEqual(["ObjectClass", "ObjectClass"]);
|
||||
const instanceKey = "instance";
|
||||
class Holder {
|
||||
[instanceKey] = (class {
|
||||
static value = 0;
|
||||
static { this.seen = this.name; }
|
||||
} as any);
|
||||
}
|
||||
const Instance = new Holder().instance;
|
||||
expect([Instance.name, Instance.seen]).toEqual(["instance", "instance"]);
|
||||
function* suspendedFields() {
|
||||
const Holder = (class {
|
||||
[yield "key"] = (class {
|
||||
static value = 0;
|
||||
static { this.seen = this.name; }
|
||||
} as any);
|
||||
} as any);
|
||||
return Holder;
|
||||
}
|
||||
const generator = suspendedFields();
|
||||
expect(generator.next().value).toBe("key");
|
||||
const Suspended = generator.next("field").value;
|
||||
expect(Suspended.name).toBe("Holder");
|
||||
const Field = new Suspended().field;
|
||||
expect([Field.name, Field.seen]).toEqual(["field", "field"]);
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
class Names {
|
||||
static {}
|
||||
static asFunction = (function () {} as any);
|
||||
static {}
|
||||
static assertedArrow = (<any>(() => {}));
|
||||
static {}
|
||||
static satisfiesFunction = (function () {} satisfies Function);
|
||||
static {}
|
||||
static nonNullClass = class { static seen = this.name; }!;
|
||||
static {}
|
||||
static asClass = (class { static seen = this.name; } as any);
|
||||
static {}
|
||||
static satisfiesClass = (class { static seen = this.name; } satisfies { new(): unknown; seen: string });
|
||||
static {}
|
||||
static namedFunction = (function Explicit() {} as any);
|
||||
static {}
|
||||
static nestedClass = (((class { static seen = this.name; }) satisfies { new(): unknown; seen: string }) as any);
|
||||
static {}
|
||||
}
|
||||
|
||||
function defaultClass(C = (class {
|
||||
static value = 0;
|
||||
static { this.seen = this.name; }
|
||||
} as any)) { return C; }
|
||||
const Default = defaultClass();
|
||||
const objectClasses = {
|
||||
ObjectClass: (class {
|
||||
static value = 0;
|
||||
static { this.seen = this.name; }
|
||||
} as any),
|
||||
};
|
||||
const instanceKey = "instance";
|
||||
class Holder {
|
||||
[instanceKey] = (class {
|
||||
static value = 0;
|
||||
static { this.seen = this.name; }
|
||||
} as any);
|
||||
}
|
||||
function* suspendedFields() {
|
||||
const Holder = (class {
|
||||
[yield "key"] = (class {
|
||||
static value = 0;
|
||||
static { this.seen = this.name; }
|
||||
} as any);
|
||||
} as any);
|
||||
return Holder;
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"plugins": ["transform-typescript"],
|
||||
"presets": [["env", {"targets": {"node": "16"}}]]
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
var _staticBlock, _staticBlock3;
|
||||
let _key;
|
||||
let Names = (class Names {
|
||||
static asFunction = ((() => {})(), babelHelpers.setFunctionName(function() {}, "asFunction"));
|
||||
static assertedArrow = ((() => {})(), babelHelpers.setFunctionName((() => {}), "assertedArrow"));
|
||||
static satisfiesFunction = ((() => {})(), babelHelpers.setFunctionName(function() {}, "satisfiesFunction"));
|
||||
static nonNullClass = ((() => {})(), { "nonNullClass": class {
|
||||
static seen = this.name;
|
||||
} }["nonNullClass"]);
|
||||
static asClass = ((() => {})(), { "asClass": class {
|
||||
static seen = this.name;
|
||||
} }["asClass"]);
|
||||
static satisfiesClass = ((() => {})(), { "satisfiesClass": class {
|
||||
static seen = this.name;
|
||||
} }["satisfiesClass"]);
|
||||
static namedFunction = ((() => {})(), function Explicit() {});
|
||||
static nestedClass = ((_value) => (_staticBlock = () => ((() => {})(), this), _value))(((() => {})(), { "nestedClass": class {
|
||||
static seen = this.name;
|
||||
} }["nestedClass"]));
|
||||
}, _staticBlock());
|
||||
function defaultClass(C = (() => {
|
||||
var _staticBlock2;
|
||||
return { "C": class {
|
||||
static value = ((_value2) => (_staticBlock2 = () => (this.seen = this.name, this), _value2))(0);
|
||||
} }["C"], _staticBlock2();
|
||||
})()) {
|
||||
return C;
|
||||
}
|
||||
const Default = defaultClass();
|
||||
const objectClasses = { ObjectClass: ({ "ObjectClass": class {
|
||||
static value = ((_value3) => (_staticBlock3 = () => (this.seen = this.name, this), _value3))(0);
|
||||
} }["ObjectClass"], _staticBlock3()) };
|
||||
const instanceKey = "instance";
|
||||
class Holder {
|
||||
[_key = babelHelpers.toPropertyKey(instanceKey)] = (() => {
|
||||
var _staticBlock4;
|
||||
return ((_name, _object) => _object[_name])(_key, { [_key]: class {
|
||||
static value = ((_value4) => (_staticBlock4 = () => (this.seen = this.name, this), _value4))(0);
|
||||
} }), _staticBlock4();
|
||||
})();
|
||||
}
|
||||
function* suspendedFields() {
|
||||
let _key2;
|
||||
const Holder = class _Class {
|
||||
static #_ = (babelHelpers.setFunctionName(this, "Holder"), _key2);
|
||||
[_key2 = babelHelpers.toPropertyKey(yield "key")] = (() => {
|
||||
var _staticBlock5;
|
||||
return { [_Class.#_]: class {
|
||||
static value = ((_value5) => (_staticBlock5 = () => (this.seen = this.name, this), _value5))(0);
|
||||
} }[_Class.#_], _staticBlock5();
|
||||
})();
|
||||
};
|
||||
return Holder;
|
||||
}
|
||||
+5
-7
@@ -1,6 +1,6 @@
|
||||
function foo() { }
|
||||
|
||||
export class C {
|
||||
var _staticBlock;
|
||||
function foo() {}
|
||||
export let C = (class C {
|
||||
#_ = 1;
|
||||
static #_2 = 2;
|
||||
#_3() {}
|
||||
@@ -8,9 +8,7 @@ export class C {
|
||||
accessor #_5 = 5;
|
||||
static accessor #_6 = 6;
|
||||
_7 = 7;
|
||||
static _8 = 8;
|
||||
static _8 = ((_value) => (_staticBlock = () => (foo(), this), _value))(8);
|
||||
_9() {}
|
||||
static _10() {}
|
||||
|
||||
static #_7 = foo();
|
||||
}
|
||||
}, _staticBlock());
|
||||
Reference in new issue
Block a user