JSC: PerformPromiseThenOneHandler for single-handler .then()
PerformPromiseThen is the generic four-child DFG IR node (promise, fulfill handler, reject handler, result capability) and is conservative because either handler can be callable or null/undefined at runtime. The Abstract Interpreter propagates type predictions using SpeculatedType bit sets — SpecFunction for callables, SpecOther for null/undefined. When DFGConstantFoldingPhase proves one handler slot is SpecOther, it converts the node to PerformPromiseThenOneHandler, encoding handler kind in a flag and emitting direct flag-and-slot writes instead of allocating a reaction cell.
Significance
This puts JIT-emitted code directly into promise handler dispatch — a security-sensitive path where a misclassified handler can silently drop or misroute a callback.
Audit directions
-
Classification boundary in
classifyPerformPromiseThen. The conversion gates on an AI type-proof that one handler isSpecOther. If the AI over-widens or misclassifies a polymorphic value, the one-handler node fires with a handler slot that is actually callable, silently dropping or misrouting the handler. -
Inline flag-write correctness. The fast path writes promise internal flags and stores the handler value directly. If the flag encoding or slot offset diverges from what the slow path or GC expects, it corrupts promise state in ways that surface as type confusion across await chains.
-
Store-barrier insertion.
DFGStoreBarrierInsertionPhase.cppmust correctly insert write barriers for the new node type. Missing barriers let the GC miss roots and collect live handler functions — a UAF in managed-heap terms. -
Slow-branch fallback. The JIT emits the fast path gated on the promise being pending; already-settled promises fall to
operationPerformPromiseThenOneHandler. If the C++ operation assumes preconditions the JIT does not actually guarantee, there is a window for incorrect behavior on the slow path.