mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-05 08:13:06 +08:00
Leave factories that declare a class out of per-site clones
A class's constructor and methods are analyzed in receiver contexts rooted at each member. Inside a per-site clone of the function that declares the class, they resolved that function's bindings in the default context: their writes through captured variables were lost, and reads of the written objects folded to stale literals. A function with a class declaration or expression anywhere inside is no longer cloned per site. The static-tables case adds a factory whose class method writes the factory's object at initialization. It prints Node's `1,1,1` (it printed `1,1,0`), and the count of constant reads stays at 8. zod3's report is unchanged: 180 constant objects and 360 foldable reads.
This commit is contained in:
@@ -1441,6 +1441,10 @@ class Analyzer {
|
||||
// clone is its own context root, so it would resolve them in the
|
||||
// default context instead, and their writes would be lost.
|
||||
if (info.root !== null && info.root !== info.node) return null;
|
||||
// A class's constructor and methods run in receiver contexts of their
|
||||
// own, rooted at each member: inside a clone they too would resolve the
|
||||
// function's bindings in the default context.
|
||||
if (this.declaresClass(info.node)) return null;
|
||||
const sc = this.argsFrom.get(args);
|
||||
if (sc === undefined || sc.inLoop || this.forceLoop || this.repeatingSites.has(site))
|
||||
return null;
|
||||
@@ -1468,6 +1472,19 @@ class Analyzer {
|
||||
return clone;
|
||||
}
|
||||
|
||||
private readonly classDeclarers = new Map<Node, boolean>();
|
||||
/** Whether a class declaration or expression is anywhere inside `node`. */
|
||||
private declaresClass(node: Node): boolean {
|
||||
let r = this.classDeclarers.get(node);
|
||||
if (r === undefined) {
|
||||
const visit = (n: Node): true | undefined =>
|
||||
ts.isClassLikeDeclaration(n) ? true : ts.forEachChild(n, visit);
|
||||
r = ts.forEachChild(node, visit) === true;
|
||||
this.classDeclarers.set(node, r);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private readonly factories = new Map<Node, boolean>();
|
||||
isFactory(node: Node): boolean {
|
||||
let r = this.factories.get(node);
|
||||
|
||||
+14
-1
@@ -87,8 +87,21 @@ var setBound = function () {
|
||||
this.v = 1;
|
||||
}.bind(lib.bound);
|
||||
setBound();
|
||||
// A factory that declares a class whose methods write the factory's
|
||||
// bindings.
|
||||
function makeTally() {
|
||||
var tally = { n: 0 };
|
||||
class Tally {
|
||||
bump() {
|
||||
tally.n = 1;
|
||||
}
|
||||
}
|
||||
new Tally().bump();
|
||||
return tally;
|
||||
}
|
||||
lib.tallied = makeTally();
|
||||
lib.inits = function () {
|
||||
return lib.made.v + "," + lib.bound.v;
|
||||
return lib.made.v + "," + lib.bound.v + "," + lib.tallied.n;
|
||||
};
|
||||
|
||||
// A constructor function with prototype methods.
|
||||
|
||||
Reference in New Issue
Block a user