[5] [JSC] Defer GC across direct eval cache key construction
Severity: High | Component: JSC interpreter | 32f1bfb
Rated High because the diff inserts a DeferGC across an interval where DirectEvalCodeCache::CacheLookupKey holds a raw StringImpl* extracted from a rope fiber; without it, GC during parser/profiler allocations can sweep the unreferenced fiber, leaving the cache key (and subsequent insertion) dereferencing freed memory.
JSC::eval() wraps the cache-miss compilation path in DeferGC. The key continues to be constructed as programStr.data.impl(); the deferral keeps the rope fiber alive across all GC-safepoint operations between key construction and cache insertion.
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp
Raw StringImpl* lifetime escape: the eval cache key references a rope fiber whose only root is the on-stack rope, across GC-safepoint parser allocations.
Patch Details
DeferGC brackets the cache-miss branch from key construction through cache insertion. No reference is taken on the StringImpl directly; the deferral suppresses sweeping while the unreferenced fiber remains in play.
Background
DirectEvalCodeCache memoizes compiled DirectEvalExecutables per (StringImpl*, BytecodeIndex) to make repeated eval(sameString) at the same call site cheap. JSString::value() returns a StringViewWithUnderlyingString; for ropes, this resolves the rope and the resulting view aliases the contents of one of the rope's fiber JSStrings rather than holding an independent RefPtr<StringImpl>.
Analysis
Pre-fix, cacheKey.impl was the raw StringImpl* taken from programStr.data.impl(). After resolution, the rope's internal layout can mutate (rope flattening, substring sharing), and the fiber whose StringImpl lives in the key may become unreachable from the on-stack rope. The cache-miss branch then performs source-profiler hook, LiteralParser allocations for the sloppy-JSON eval fast path, collectClosureVariablesUnderTDZ, full parser allocations, and finally cache insertion — every step a potential GC safepoint.