Files
scriptc/tests/corpus/6917-local-class-expression-self-reference.ts
T
Chris Tate 14694925c7 Bind a class expression's name used only in a shorthand property, and stop keeping local class prototypes alive
- 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.
2026-10-22 08:32:24 -05:00

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);