DFG/FTL: Object.defineProperty descriptor field extraction
JSTests/microbenchmarks/object-define-property-put-by-id-direct.js
JSC's optimizing JIT has two upper tiers: DFG (mid-tier, dataflow graph) and FTL (top-tier, B3 backend). Object.defineProperty was historically opaque to the JIT because the descriptor is a general object. This commit exploits two watchpoints — the sane-chain watchpoint on Object.prototype and a descriptor-field watchpoint — to prove the descriptor has a known structure at compile time. DFG then inserts GetByOffset nodes that read each descriptor field directly into a new ObjectDefinePropertyFromFields IR node. When all attributes are default and the base object has no existing property for the key, the call collapses further to PutByIdDirect, hitting the inline-cache fast path.
ObjectDefineProperty(obj, key, desc)
|
v
ObjectDefinePropertyFromFields(obj, key, e, c, v, w, get, set)
|
+--[e=true,c=true,w=true; no existing prop; non-indexed key]
| +--> PutByIdDirect(obj, key, value) ~12x faster
|
+--[data desc, any attr differs] --> DefineDataProperty
|
+--[accessor desc] --> DefineAccessorProperty
Significance
Hot Object.defineProperty with a default data descriptor now compiles to a single PutByIdDirect, yielding up to 11.9x speedup on the microbenchmark. The optimization touches watchpoint logic, structure assumptions, property storage semantics, and FTL allocation sinking — every layer where prior DFG bugs have produced exploitable JIT primitives.
Audit directions
-
Watchpoint invalidation gaps. The optimization compiles under sane-chain and descriptor-field watchpoints on
Object.prototype. Subtle prototype mutations (non-enumerable property, Symbol property, Proxy interposition) that fail to fire the watchpoint would leave the JIT reading from stale structure offsets viaGetByOffseton the descriptor. Cross-realm descriptors are an explicit edge case inobject-define-property-fields-refinement.js; verify the watchpoint scope covers them. -
tryFoldDefineDataPropertyToPutByIdDirectprecondition completeness. Lowering toPutByIdDirectrequires no existing property, non-indexed key, and all attributes true. These are checked structurally at compile time viaPropertyStatus. Confirm runtime paths — Proxy-wrapped targets, objects with indexed numeric string keys that pass the non-indexed check, prototype chain lookups — cannot violate these invariants after the IC stub is generated. -
'empty' vs. present-but-undefined field semantics. Each descriptor field is
GetByOffset(desc, offset) OR empty. If structure analysis incorrectly concludes a field is absent when it is present-but-undefined,definePropertyreceives wrong attribute bits — potentially creating a non-writable or non-configurable property when the caller expected otherwise. The accessor/data field overlap (get/set vs. value/writable offsets) is a specific area to verify. -
FTL allocation-sinking interaction. If the sunk descriptor allocation escapes through an untracked path — exception during
defineProperty, OSR exit, bailout that materializes the object — the escape-analysis failure can produce UAF or wrong-typed materialization. Check whetherObjectDefinePropertyFromFieldsis correctly marked as an escape point on slow-path transitions.