WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
RESOLVED FIXED
312466
Promise.prototype.finally does not seem to observably assert whether IsConstructor(C) is true
https://bugs.webkit.org/show_bug.cgi?id=312466
Summary
Promise.prototype.finally does not seem to observably assert whether IsConstr...
Francisco Tolmasky
Reported
2026-04-16 05:39:22 PDT
The following line does not throw any errors in Safari (and just returns undefined), but *does* throw an error in Chrome and Firefox: `Promise.prototype.finally.call({ constructor: { [Symbol.species]: () => 10 }, then() { } })` If you run that line in Chrome you'll see that you get: ``` VM619:1 Uncaught TypeError: object.constructor[Symbol.species] is not a constructor at Object.finally (<anonymous>) at <anonymous>:1:27 ``` In Firefox you get: ``` Uncaught TypeError: @@species property of object's constructor is not a constructor <anonymous> debugger eval code:1 ``` The relevant part of the spec is found at step 4 of the promise.prototype.finally definition (
https://tc39.es/ecma262/#sec-promise.prototype.finally
), copied here: "4. Assert: IsConstructor(C) is true." I believe that Safari's choice to not throw *is* technically allowed by the spec since the spec says that "Asserts" do not necessarily need to have any semantic effect, and thus both strategies seem acceptable. However I think it would be preferable for Safari to match Chrome and Firefox's behavior, both for interoperability reasons, but also because throwing when the assertion is false is actually quite useful. In particular, this is currently employed in the wild as a means to implement an "IsConstructor" function that is side-effect-free. That is to say, you can call IsConstructor(F) and be assured that F will never itself be called as a result. Unfortunately, techniques like using Reflect.construct(F, []) and checking for an error are *not* side-effect free in this way. They WILL call F F in the case where F *is* a constructor.
Attachments
Add attachment
proposed patch, testcase, etc.
Francisco Tolmasky
Comment 1
2026-04-16 05:53:06 PDT
Another quick comment: it also arguably makes implementing a rock solid IsConstructor possible at all. Without it, you need to do the difficult task of filtering non-Reflect.construct not a constructor errors. Just as a quick example, Reflect.construct(Element, []) throws with "illegal constructor" just to show you how quickly you run into this sort of thing. Element is however detected perfectly fine with the Promise.prototype.finally method in the browsers that throw.
Tetsuharu Ohzeki [UTC+9]
Comment 2
2026-04-16 09:36:59 PDT
I seem this case is covered by
https://github.com/tc39/test262/blob/1775ee48c9d71fbd1ec6a1a66ac23efcbd627f8f/test/built-ins/Promise/prototype/finally/species-constructor-throws.js
and it was imported in
https://commits.webkit.org/308596@main
, but the above test just checks what the thrown is just a TypeError, but not checking the detail of it. Its TypeError is caused by that mimic |this| lacks |then|.
Tetsuharu Ohzeki [UTC+9]
Comment 3
2026-04-16 09:39:44 PDT
Pull request:
https://github.com/WebKit/WebKit/pull/62901
EWS
Comment 4
2026-04-21 16:05:47 PDT
Committed
311725@main
(e704581e3ddb): <
https://commits.webkit.org/311725@main
> Reviewed commits have been landed. Closing PR #62901 and removing active labels.
Radar WebKit Bug Importer
Comment 5
2026-04-21 16:06:14 PDT
<
rdar://problem/175290627
>
Note
You need to
log in
before you can comment on or make changes to this bug.
Top of Page
Format For Printing
XML
Clone This Bug