Context
Found during round-20 review of the #2088 implementation plan (docs/plans/issue-2088.md, PR #2612) — the escape analysis's own accepted Category F limitation around eval/new Function (allReferencesTracked, subtreeContainsReassignmentOf, subtreeContainsThisKeyword's own doc comments; the Risks table's "Scope growth during implementation" row).
The gap (accepted, not a soundness bug)
eval('T.alpha()')/new Function('return T.alpha()') can invoke a tracked binding's property through a reference that exists only inside a string literal — invisible to every AST-level walk in this design by construction, since no static analysis can see through an opaque runtime string. This is already disclosed, repeatedly, as Category F (the same standing platform-level limitation this design accepts for with's resolution target and for every write/read scan in src/extractors/javascript.ts).
The option not taken
The extractor already emits a distinguishable <dynamic:eval>/new Function call shape (dynamic-call resolution, ADR-002). A FILE-LEVEL fail-safe is possible: if a file contains ANY such call at all, treat every escape-analysis walk over that file as unproven (escaping) rather than trusting the walk's own coverage — this would make the design's own "non-vacuous coverage" claim (round 8's standing rule) literally, rather than only structurally-modulo-eval, true.
This was not built in round 20: it is new design scope (a file-wide pre-check threaded into every WU-2b entry point, in both engines), not a fix to an existing predicate, and costs recall broadly (an entire file loses T1 correlation the moment it contains any eval/new Function call, anywhere, whether or not it's anywhere near the table in question) for a limitation Category F already accepts as unattemptable.
Suggested fix shape
A per-file boolean (fileContainsDynamicEval, threaded alongside exportedNames/definitionNames into computeObjectLiteralSiteEscapes), set once via a lightweight scan for <dynamic:eval>/new Function-tagged calls already collected in that file's calls array — no new AST walk needed, just a fold over already-extracted data. Needs its own WU-10 fixture pair (a file with an eval call unrelated to the table, proving the widened escape still triggers, and a guard proving an eval-free file is unaffected) and a Rust mirror.
Context
Found during round-20 review of the #2088 implementation plan (
docs/plans/issue-2088.md, PR #2612) — the escape analysis's own accepted Category F limitation aroundeval/new Function(allReferencesTracked,subtreeContainsReassignmentOf,subtreeContainsThisKeyword's own doc comments; the Risks table's "Scope growth during implementation" row).The gap (accepted, not a soundness bug)
eval('T.alpha()')/new Function('return T.alpha()')can invoke a tracked binding's property through a reference that exists only inside a string literal — invisible to every AST-level walk in this design by construction, since no static analysis can see through an opaque runtime string. This is already disclosed, repeatedly, as Category F (the same standing platform-level limitation this design accepts forwith's resolution target and for every write/read scan insrc/extractors/javascript.ts).The option not taken
The extractor already emits a distinguishable
<dynamic:eval>/new Functioncall shape (dynamic-call resolution, ADR-002). A FILE-LEVEL fail-safe is possible: if a file contains ANY such call at all, treat every escape-analysis walk over that file as unproven (escaping) rather than trusting the walk's own coverage — this would make the design's own "non-vacuous coverage" claim (round 8's standing rule) literally, rather than only structurally-modulo-eval, true.This was not built in round 20: it is new design scope (a file-wide pre-check threaded into every WU-2b entry point, in both engines), not a fix to an existing predicate, and costs recall broadly (an entire file loses T1 correlation the moment it contains any
eval/new Functioncall, anywhere, whether or not it's anywhere near the table in question) for a limitation Category F already accepts as unattemptable.Suggested fix shape
A per-file boolean (
fileContainsDynamicEval, threaded alongsideexportedNames/definitionNamesintocomputeObjectLiteralSiteEscapes), set once via a lightweight scan for<dynamic:eval>/new Function-tagged calls already collected in that file'scallsarray — no new AST walk needed, just a fold over already-extracted data. Needs its own WU-10 fixture pair (a file with anevalcall unrelated to the table, proving the widened escape still triggers, and a guard proving aneval-free file is unaffected) and a Rust mirror.