Skip to content

WU-2b: optional file-level eval/new Function fail-safe for the escape analysis's non-vacuous-coverage claim #2642

Description

@carlos-alm

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions