mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-08 08:44:37 +08:00
- A named function-local class expression whose only reference to its own
name is a shorthand property (`static wrap() { return { Packed }; }`)
now gets its name binding. The self-reference check read the shorthand
name's property symbol, so the class lowered without the binding and the
property held a placeholder function instead of the class.
- A function-local class's prototype takes its name from the class
object's interned name instead of a registered copy. The registry kept
every such prototype alive until the process exited, so evaluating a
local class repeatedly kept each evaluation's prototype and the method
state it captures: a program that evaluates a local class 200,000 times
and prints each instance peaked at 121 MB, and now at 2 MB.
Reusing a freed object clears its name.
54 lines
1.7 KiB
TypeScript
54 lines
1.7 KiB
TypeScript
// Function-local class expressions that refer to themselves by their own name.
|
|
function counter(start: number) {
|
|
const C = class Counter {
|
|
value: number;
|
|
constructor(value: number) { this.value = value; }
|
|
next(): Counter { return new Counter(this.value + 1); }
|
|
static first(): Counter { return new Counter(start); }
|
|
static isSelf(value: unknown): boolean { return value === Counter; }
|
|
};
|
|
return C;
|
|
}
|
|
const Counter1 = counter(10);
|
|
console.log(Counter1.first().next().next().value);
|
|
console.log(Counter1.isSelf(Counter1), Counter1.isSelf(counter(1)), Counter1.name);
|
|
|
|
function box<T>(seed: T) {
|
|
return class Box {
|
|
value: T;
|
|
constructor(value: T) { this.value = value; }
|
|
again(): Box { return new Box(this.value); }
|
|
static of(): Box { return new Box(seed); }
|
|
};
|
|
}
|
|
const StringBox = box("seed");
|
|
console.log(new StringBox("x").again().value, StringBox.of().value);
|
|
const NumberBox = box(7);
|
|
console.log(NumberBox.of().again().value + 1);
|
|
|
|
function nested(n: number) {
|
|
const Outer = class Tree {
|
|
children: Tree[] = [];
|
|
static build(depth: number): Tree {
|
|
const tree = new Tree();
|
|
if (depth > 0) tree.children.push(Tree.build(depth - 1), Tree.build(depth - 1));
|
|
return tree;
|
|
}
|
|
size(): number { return 1 + this.children.reduce((total, child) => total + child.size(), 0); }
|
|
};
|
|
return Outer.build(n).size();
|
|
}
|
|
console.log(nested(3));
|
|
|
|
// The only reference to the name is a shorthand property.
|
|
function registry(tag: string) {
|
|
return class Entry {
|
|
readonly tag = tag;
|
|
static table(): Record<string, unknown> {
|
|
return { Entry };
|
|
}
|
|
};
|
|
}
|
|
const Entry1 = registry("e");
|
|
console.log(Entry1.table().Entry === Entry1, new Entry1().tag);
|