Branch: refs/heads/main
Home: https://github.com/WebKit/WebKit
Commit: cdf77b6570ffbe789a2dab116ee1097319ff3145
https://github.com/WebKit/WebKit/commit/cdf77b6570ffbe789a2dab116ee1097319ff3145
Author: Sosuke Suzuki <[email protected]>
Date: 2026-05-14 (Thu, 14 May 2026)
Changed paths:
A JSTests/microbenchmarks/await-object-literal.js
A JSTests/microbenchmarks/promise-resolve-object-literal.js
A JSTests/microbenchmarks/promise-then-chain-object-literal.js
A JSTests/stress/promise-resolve-non-thenable-structure-cache-cross-realm.js
A JSTests/stress/promise-resolve-non-thenable-structure-cache-dictionary.js
A JSTests/stress/promise-resolve-non-thenable-structure-cache.js
M Source/JavaScriptCore/runtime/JSGlobalObject.cpp
M Source/JavaScriptCore/runtime/JSPromise.cpp
M Source/JavaScriptCore/runtime/Structure.h
Log Message:
-----------
[JSC] Cache `isDefinitelyNonThenable` result on Structure
https://bugs.webkit.org/show_bug.cgi?id=314815
Reviewed by Yusuke Suzuki.
isDefinitelyNonThenable() walks the resolution object's prototype chain on every
promise resolution -- once per `await`, once per `.then()` handler return -- to
prove the object cannot have a `then` property. For the dominant pattern of
resolving plain object literals (`{ a, b, c }`), the chain is just
[self, Object.prototype], yet we still pay 4 bit checks plus a load per
prototype hop on every call. V8 reaches the same conclusion in O(1) by carrying
a "may have interesting properties" bit on each Map.
This patch caches the result on the Structure in a 2-bit lazily-computed state.
A `true` cache (NonThenable) is sound only while the realm's
promiseThenWatchpointSet is intact, so we extend that watchpoint set to also
watch `then` absence on Object.prototype: any addition of `then` to
Object.prototype fires it before a stale `true` could be read. Caching `true`
is therefore restricted to chains the watchpoint fully covers -- exactly
[self, Object.prototype] or [self] (null proto). Deeper chains
([self, Foo.prototype, ...]) cannot be guarded against a later
`Foo.prototype.then = ...`, so they are marked Uncacheable: subsequent calls
go straight to the walk and skip the cache-establishment check, leaving the
existing fast path unchanged. A `false` cache (MaybeThenable) is always safe
because going stale only forgoes the optimization, never miscompiles.
Dictionary structures are mutated in place when properties are added or removed,
so any cached state could become stale (caching NonThenable, then adding `then`
later does not produce a new Structure). We give up caching entirely for them;
the per-call walk remains correct because `hasSpecialProperties` is updated in
place even for dictionaries.
Baseline Patched
promise-resolve-object-literal 9.9748+-0.1166 ^
9.2475+-0.2010 ^ definitely 1.0787x faster
promise-then-chain-object-literal 20.6694+-0.1839 ^
20.0792+-0.1701 ^ definitely 1.0294x faster
await-object-literal 17.3544+-0.1630 ^
16.3586+-0.0531 ^ definitely 1.0609x faster
Tests: JSTests/microbenchmarks/await-object-literal.js
JSTests/microbenchmarks/promise-resolve-object-literal.js
JSTests/microbenchmarks/promise-then-chain-object-literal.js
JSTests/stress/promise-resolve-non-thenable-structure-cache-cross-realm.js
JSTests/stress/promise-resolve-non-thenable-structure-cache-dictionary.js
JSTests/stress/promise-resolve-non-thenable-structure-cache.js
* JSTests/microbenchmarks/await-object-literal.js: Added.
(async run):
* JSTests/microbenchmarks/promise-resolve-object-literal.js: Added.
* JSTests/microbenchmarks/promise-then-chain-object-literal.js: Added.
(i.p.p.then.x):
* JSTests/stress/promise-resolve-non-thenable-structure-cache-cross-realm.js:
Added.
(shouldBe):
(asyncTest.async resolveForeignObject):
(asyncTest.async bothRealmsWarm):
(asyncTest.async poisonOtherRealmObjectProto.other.Function):
(asyncTest.async poisonOtherRealmObjectProto):
* JSTests/stress/promise-resolve-non-thenable-structure-cache-dictionary.js:
Added.
(shouldBe):
(asyncTest):
(asyncTest.async cacheableDictionaryGainsThen):
(asyncTest.async uncacheableDictionaryGainsThen):
(asyncTest.async cacheableDictionaryPromiseResolve):
(asyncTest.async dictionaryNullProtoGainsThen):
* JSTests/stress/promise-resolve-non-thenable-structure-cache.js: Added.
(shouldBe):
(asyncTest.async plainObjectLiteral):
(asyncTest.async promiseResolveObjectLiteral):
(asyncTest.async ownThenDataProperty):
(asyncTest.async ownThenGetter.const.v.await.get then):
(asyncTest.async ownThenGetter):
(asyncTest.async nullProto):
(asyncTest.async frozenSealed):
(asyncTest.async classInstanceDeepChain.A):
(asyncTest.async classInstanceDeepChain.B):
(asyncTest.async classInstanceDeepChain.C):
(asyncTest.async classInstanceDeepChain):
(asyncTest.async classProtoGainsThen.Foo):
(asyncTest.async classProtoGainsThen.Foo.prototype.then):
(asyncTest.async classProtoGainsThen):
(asyncTest.async megamorphicShapes.makeShape):
(asyncTest.async megamorphicShapes):
(asyncTest.async structureTransition.o.then):
(asyncTest.async structureTransition):
(asyncTest.async forAwaitOf.async gen):
(asyncTest.async forAwaitOf):
(asyncTest.async asyncGenObjectLiterals.async g):
(asyncTest.async asyncGenObjectLiterals):
(asyncTest.async promiseAllMixed):
(asyncTest.async sharedStructure.make):
(asyncTest.async sharedStructure):
(asyncTest.async symbolKeysHarmless):
(asyncTest.async thenAccessorOnProto.Wrapper):
(asyncTest.async thenMidChain.A):
(asyncTest.async thenMidChain.B):
(asyncTest.async thenMidChain.C):
(asyncTest.async thenMidChain.A.prototype.then):
(asyncTest.async thenMidChain):
(asyncTest.async setPrototypeOf.MakeProto):
(asyncTest.async setPrototypeOf):
(asyncTest.async reflectConstruct.Base):
(asyncTest.async reflectConstruct.Derived):
(asyncTest.async reflectConstruct):
(asyncTest.async builtins):
(asyncTest.async objectProtoNonThenAdditionHarmless):
(asyncTest.async objectProtoThenPoisonLast.Object.prototype.then):
(asyncTest.async objectProtoThenPoisonLast.try.Z):
(asyncTest.async objectProtoThenPoisonLast):
* Source/JavaScriptCore/runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
* Source/JavaScriptCore/runtime/JSPromise.cpp:
(JSC::isDefinitelyNonThenable):
* Source/JavaScriptCore/runtime/Structure.h:
Canonical link: https://commits.webkit.org/313294@main
To unsubscribe from these emails, change your notification settings at
https://github.com/WebKit/WebKit/settings/notifications