candor-ts 0.23.0 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/scan.mjs +37 -14
package/package.json
CHANGED
package/scan.mjs
CHANGED
|
@@ -3043,25 +3043,48 @@ const scanEnvWrites = (node) => {
|
|
|
3043
3043
|
for (const sf of sources) scanEnvWrites(sf);
|
|
3044
3044
|
|
|
3045
3045
|
// ---- pass 3: the least fixpoint (SEMANTICS §5a), effects + the literal surfaces -------------------
|
|
3046
|
+
// WORKLIST least-fixpoint. The old `while (changed) { for [,rec] of fns }` swept every function on every
|
|
3047
|
+
// pass, so its pass count equalled the longest back-to-front call chain — O(V²) on deep whole-project
|
|
3048
|
+
// graphs. Instead, when a function's set grows, re-enqueue only its callers (the functions whose union
|
|
3049
|
+
// reads it) via a callee→callers reverse index. Same monotone set-union (confluent) least fixpoint →
|
|
3050
|
+
// order-independent → identical result. The `edges` are fixed, so the reverse index is shared by every
|
|
3051
|
+
// sweep below (effects + each literal surface).
|
|
3052
|
+
const callersOf = new Map();
|
|
3053
|
+
for (const [name, rec] of fns)
|
|
3054
|
+
for (const callee of rec.edges) {
|
|
3055
|
+
let cs = callersOf.get(callee);
|
|
3056
|
+
if (!cs) callersOf.set(callee, (cs = []));
|
|
3057
|
+
cs.push(name);
|
|
3058
|
+
}
|
|
3046
3059
|
const inferred = new Map([...fns.keys()].map((k) => [k, new Set(fns.get(k).direct)]));
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
for (
|
|
3060
|
+
{
|
|
3061
|
+
const queue = [...fns.keys()];
|
|
3062
|
+
const queued = new Set(queue);
|
|
3063
|
+
for (let head = 0; head < queue.length; head++) {
|
|
3064
|
+
const name = queue[head];
|
|
3065
|
+
queued.delete(name);
|
|
3051
3066
|
const mine = inferred.get(name);
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3067
|
+
const before = mine.size;
|
|
3068
|
+
for (const callee of fns.get(name).edges)
|
|
3069
|
+
for (const e of inferred.get(callee) ?? []) mine.add(e);
|
|
3070
|
+
if (mine.size !== before)
|
|
3071
|
+
for (const c of callersOf.get(name) ?? [])
|
|
3072
|
+
if (!queued.has(c)) { queued.add(c); queue.push(c); }
|
|
3055
3073
|
}
|
|
3056
3074
|
}
|
|
3057
3075
|
for (const m of ["hosts", "tables", "cmds", "paths", "blind", "incomplete"]) {
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3076
|
+
const queue = [...fns.keys()];
|
|
3077
|
+
const queued = new Set(queue);
|
|
3078
|
+
for (let head = 0; head < queue.length; head++) {
|
|
3079
|
+
const name = queue[head];
|
|
3080
|
+
queued.delete(name);
|
|
3081
|
+
const rec = fns.get(name);
|
|
3082
|
+
const before = rec[m].size;
|
|
3083
|
+
for (const callee of rec.edges)
|
|
3084
|
+
for (const v of fns.get(callee)?.[m] ?? []) rec[m].add(v);
|
|
3085
|
+
if (rec[m].size !== before)
|
|
3086
|
+
for (const c of callersOf.get(name) ?? [])
|
|
3087
|
+
if (!queued.has(c)) { queued.add(c); queue.push(c); }
|
|
3065
3088
|
}
|
|
3066
3089
|
}
|
|
3067
3090
|
|