Keep static-object rewrites when propertyIsEnumerable asks another class's prototype

The analysis reads F.prototype as ⊤ beside the class prototype objects it
tracks, so every class looked queried by any G.prototype.propertyIsEnumerable
call. The query reaches a class's prototype only when G may be that class's
function.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Malte Ubl
2026-10-25 14:49:24 -07:00
co-authored by Claude Opus 5.5
parent 8a87cf015c
commit 14e4c7d1ef
@@ -608,7 +608,16 @@ export function planStaticObjectRewrites(
* as the object `propertyIsEnumerable` is asked about. */
const reflectsOn = (atoms: ReadonlySet<Atom>, ctorPath: string): boolean =>
enumerabilityEscapes ||
enumerabilityQueries.some((recv) => mayBeValue(recv, atoms, ctorPath)) ||
enumerabilityQueries.some((recv) => {
// `F.prototype` is one of `atoms` (a class's prototype) only when F
// may be that class's function, which `atoms` holds too: the analysis
// reads a prototype as ⊤ beside the class prototype objects it tracks.
let e: ts.Expression = recv;
while (ts.isParenthesizedExpression(e)) e = e.expression;
if (ts.isPropertyAccessExpression(e) && e.name.text === "prototype")
return mayBeValue(e.expression, atoms, ctorPath);
return mayBeValue(recv, atoms, ctorPath);
}) ||
reflective.some((call) => call.arguments.nodes.some((arg) => mayBe(arg, atoms, ctorPath)));
/** Whether a call that freezes, seals or redefines an object may reach one of `atoms`. */
const reshapes = (atoms: ReadonlySet<Atom>, ctorPath: string): boolean =>