reaches
What definitions' code can reach outside the codebase (the clock, randomness, the network, files, the
environment, other processes), through everything they use: by hash. What a definition could reach, not what a run
of it did: a function that reads the clock somewhere counts, called or not. Each one's own effects are worked out
once and kept with it, and so is what each component reaches (kept with its first member). src has what isn't
saved yet (a scratch file's definitions and watch expressions).
import type { CodebaseCore } from "#codebase/tcmts/cli/codebase"; import { ownEffectsKey, reached } from "#codebase/tcmts/cli/effects"; import { ownEffects } from "#codebase/tcmts/core/effects"; import type { Effect } from "#codebase/tcmts/core/effects"; import { definesFunction, writtenOutRefs } from "#codebase/tcmts/core/ir"; import type { DefRecord } from "#codebase/tcmts/core/types"; import type { DefSource } from "#codebase/tcmts/loader"; export function reaches( cb: CodebaseCore, hashes: Iterable<string>, src: DefSource = cb.source(), ): Map<string, Effect[]> { const key = ownEffectsKey(); // (what's followed changes with `writtenOutRefs`: what's kept under the last one's key is worked out again) const through = `reaches ${key.slice("effects ".length)} ${String((writtenOutRefs as unknown as Record<symbol, string>)[Symbol.for("tcm.hash")] ?? "?").slice(0, 8)}`; const learned: [string, string, Effect[]][] = []; const own = (m: DefRecord): Effect[] => { const kept = cb.store.getMetadata(m.hash, key); if (kept !== undefined) return JSON.parse(kept) as Effect[]; const e = ownEffects(m.ir); learned.push([m.hash, key, e]); return e; }; const of = (comp: string): Effect[] => { const known = reached.get(comp); if (known) return known; const members = src.members(comp); const kept = members[0] && cb.store.getMetadata(members[0].hash, through); if (kept) { const r = JSON.parse(kept) as Effect[]; reached.set(comp, r); return r; } // (components make no cycles: what one uses is in others) reached.set(comp, []); const found = new Set<Effect>(); for (const m of members) { for (const e of own(m)) found.add(e); // a function whose source it only writes out (`${f}`) runs wherever that goes, not here const written = writtenOutRefs(m.ir); for (const d of m.deps) { if (d.pos !== "value" || !d.ref.startsWith("#")) continue; const dep = src.get(d.ref.slice(1)); if (dep && written.has(dep.hash) && dep.kind === "term" && definesFunction(dep.ir)) continue; if (dep && dep.componentHash !== comp) for (const e of of(dep.componentHash)) found.add(e); } } const r = [...found].sort(); reached.set(comp, r); if (members[0]) learned.push([members[0].hash, through, r]); return r; }; const out = new Map<string, Effect[]>(); for (const h of hashes) { const rec = src.get(h); out.set(h, rec ? of(rec.componentHash) : []); } if (learned.length) cb.store.transaction(() => { for (const [h, k, e] of learned) cb.store.setMetadata(h, k, JSON.stringify(e)); }); return out; }
uses
- tcmts.cli.codebase.CodebaseCore
- tcmts.cli.effects.ownEffectsKey
- tcmts.cli.effects.reached
- tcmts.core.effects.Effect
- tcmts.core.effects.ownEffects
- tcmts.core.ir.definesFunction
- tcmts.core.ir.writtenOutRefs
- tcmts.core.types.DefRecord
- tcmts.loader.DefSource