Changeset 258664 in webkit


Ignore:
Timestamp:
Mar 18, 2020 3:27:22 PM (4 years ago)
Author:
ysuzuki@apple.com
Message:

Add a way to mark a rejected promise as handled
https://bugs.webkit.org/show_bug.cgi?id=209241

Reviewed by Michael Saboff.

JSTests:

  • stress/reject-as-handled.js: Added.

(shouldBe):
(set new):

Source/JavaScriptCore:

Some of WebCore promise implementations (WebAnimation etc.) want to reject promise
as handled state to suppress UnhandledPromiseRejection tracking. For example, a
lot of WebCore implementations expose Promise DOM attributes which will be rejected
at some conditions. But we do not want to force users setting a handler for each such an
attribute.

This patch adds JSPromise::rejectAsHandled C++ function. This simply sets isHandledFlag
before executing JSPromise::reject if we are not calling a reject function yet.

  • runtime/JSPromise.cpp:

(JSC::JSPromise::rejectAsHandled):

  • runtime/JSPromise.h:
  • tools/JSDollarVM.cpp:

(JSC::functionRejectPromiseAsHandled):
(JSC::JSDollarVM::finishCreation):

Source/WebCore:

This adds an interface using JSPromise::rejectAsHandled to DOMPromise classes.

  • bindings/js/DOMPromiseProxy.h:

(WebCore::DOMPromiseProxy<IDLType>::reject):
(WebCore::DOMPromiseProxy<IDLVoid>::reject):
(WebCore::DOMPromiseProxyWithResolveCallback<IDLType>::reject):

  • bindings/js/JSDOMPromiseDeferred.cpp:

(WebCore::DeferredPromise::callFunction):
(WebCore::DeferredPromise::reject):

  • bindings/js/JSDOMPromiseDeferred.h:

(WebCore::DeferredPromise::reject):
(WebCore::DeferredPromise::rejectWithCallback):
(WebCore::DOMPromiseDeferredBase::reject):
(WebCore::DOMPromiseDeferredBase::rejectType):

Location:
trunk
Files:
1 added
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r258531 r258664  
     12020-03-18  Yusuke Suzuki  <ysuzuki@apple.com>
     2
     3        Add a way to mark a rejected promise as handled
     4        https://bugs.webkit.org/show_bug.cgi?id=209241
     5
     6        Reviewed by Michael Saboff.
     7
     8        * stress/reject-as-handled.js: Added.
     9        (shouldBe):
     10        (set new):
     11
    1122020-03-16  Keith Miller  <keith_miller@apple.com>
    213
  • trunk/Source/JavaScriptCore/ChangeLog

    r258603 r258664  
     12020-03-18  Yusuke Suzuki  <ysuzuki@apple.com>
     2
     3        Add a way to mark a rejected promise as handled
     4        https://bugs.webkit.org/show_bug.cgi?id=209241
     5
     6        Reviewed by Michael Saboff.
     7
     8        Some of WebCore promise implementations (WebAnimation etc.) want to reject promise
     9        as handled state to suppress UnhandledPromiseRejection tracking. For example, a
     10        lot of WebCore implementations expose Promise DOM attributes which will be rejected
     11        at some conditions. But we do not want to force users setting a handler for each such an
     12        attribute.
     13
     14        This patch adds `JSPromise::rejectAsHandled` C++ function. This simply sets isHandledFlag
     15        before executing `JSPromise::reject` if we are not calling a reject function yet.
     16
     17        * runtime/JSPromise.cpp:
     18        (JSC::JSPromise::rejectAsHandled):
     19        * runtime/JSPromise.h:
     20        * tools/JSDollarVM.cpp:
     21        (JSC::functionRejectPromiseAsHandled):
     22        (JSC::JSDollarVM::finishCreation):
     23
    1242020-03-17  Yusuke Suzuki  <ysuzuki@apple.com>
    225
  • trunk/Source/JavaScriptCore/runtime/JSPromise.cpp

    r251691 r258664  
    183183}
    184184
     185void JSPromise::rejectAsHandled(JSGlobalObject* lexicalGlobalObject, JSValue value)
     186{
     187    // Setting isHandledFlag before calling reject since this removes round-trip between JSC and PromiseRejectionTracker, and it does not show an user-observable behavior.
     188    VM& vm = lexicalGlobalObject->vm();
     189    uint32_t flags = this->flags();
     190    if (!(flags & isFirstResolvingFunctionCalledFlag))
     191        internalField(static_cast<unsigned>(Field::Flags)).set(vm, this, jsNumber(flags | isHandledFlag));
     192    reject(lexicalGlobalObject, value);
     193}
     194
    185195void JSPromise::reject(JSGlobalObject* lexicalGlobalObject, Exception* reason)
    186196{
     
    188198}
    189199
     200void JSPromise::rejectAsHandled(JSGlobalObject* lexicalGlobalObject, Exception* reason)
     201{
     202    rejectAsHandled(lexicalGlobalObject, reason->value());
     203}
     204
    190205} // namespace JSC
  • trunk/Source/JavaScriptCore/runtime/JSPromise.h

    r253007 r258664  
    6969    JS_EXPORT_PRIVATE void resolve(JSGlobalObject*, JSValue);
    7070    JS_EXPORT_PRIVATE void reject(JSGlobalObject*, JSValue);
     71    JS_EXPORT_PRIVATE void rejectAsHandled(JSGlobalObject*, JSValue);
    7172    JS_EXPORT_PRIVATE void reject(JSGlobalObject*, Exception*);
     73    JS_EXPORT_PRIVATE void rejectAsHandled(JSGlobalObject*, Exception*);
    7274
    7375    struct DeferredData {
  • trunk/Source/JavaScriptCore/tools/JSDollarVM.cpp

    r258059 r258664  
    29302930}
    29312931
     2932static EncodedJSValue JSC_HOST_CALL functionRejectPromiseAsHandled(JSGlobalObject* globalObject, CallFrame* callFrame)
     2933{
     2934    JSPromise* promise = jsCast<JSPromise*>(callFrame->uncheckedArgument(0));
     2935    JSValue reason = callFrame->uncheckedArgument(1);
     2936    promise->rejectAsHandled(globalObject, reason);
     2937    return JSValue::encode(jsUndefined());
     2938}
     2939
    29322940void JSDollarVM::finishCreation(VM& vm)
    29332941{
     
    30643072
    30653073    addFunction(vm, "hasOwnLengthProperty", functionHasOwnLengthProperty, 1);
     3074    addFunction(vm, "rejectPromiseAsHandled", functionRejectPromiseAsHandled, 1);
    30663075
    30673076    m_objectDoingSideEffectPutWithoutCorrectSlotStatusStructure.set(vm, this, ObjectDoingSideEffectPutWithoutCorrectSlotStatus::createStructure(vm, globalObject, jsNull()));
  • trunk/Source/WebCore/ChangeLog

    r258663 r258664  
     12020-03-18  Yusuke Suzuki  <ysuzuki@apple.com>
     2
     3        Add a way to mark a rejected promise as handled
     4        https://bugs.webkit.org/show_bug.cgi?id=209241
     5
     6        Reviewed by Michael Saboff.
     7
     8        This adds an interface using JSPromise::rejectAsHandled to DOMPromise classes.
     9
     10        * bindings/js/DOMPromiseProxy.h:
     11        (WebCore::DOMPromiseProxy<IDLType>::reject):
     12        (WebCore::DOMPromiseProxy<IDLVoid>::reject):
     13        (WebCore::DOMPromiseProxyWithResolveCallback<IDLType>::reject):
     14        * bindings/js/JSDOMPromiseDeferred.cpp:
     15        (WebCore::DeferredPromise::callFunction):
     16        (WebCore::DeferredPromise::reject):
     17        * bindings/js/JSDOMPromiseDeferred.h:
     18        (WebCore::DeferredPromise::reject):
     19        (WebCore::DeferredPromise::rejectWithCallback):
     20        (WebCore::DOMPromiseDeferredBase::reject):
     21        (WebCore::DOMPromiseDeferredBase::rejectType):
     22
    1232020-03-18  youenn fablet  <youenn@apple.com>
    224
  • trunk/Source/WebCore/bindings/js/DOMPromiseProxy.h

    r251425 r258664  
    5252    void resolve(typename IDLType::ParameterType);
    5353    void resolveWithNewlyCreated(typename IDLType::ParameterType);
    54     void reject(Exception);
     54    void reject(Exception, RejectAsHandled = RejectAsHandled::No);
    5555   
    5656private:
     
    7373
    7474    void resolve();
    75     void reject(Exception);
     75    void reject(Exception, RejectAsHandled = RejectAsHandled::No);
    7676
    7777private:
     
    103103    void resolve(typename IDLType::ParameterType);
    104104    void resolveWithNewlyCreated(typename IDLType::ParameterType);
    105     void reject(Exception);
     105    void reject(Exception, RejectAsHandled = RejectAsHandled::No);
    106106   
    107107private:
     
    174174
    175175template<typename IDLType>
    176 inline void DOMPromiseProxy<IDLType>::reject(Exception exception)
     176inline void DOMPromiseProxy<IDLType>::reject(Exception exception, RejectAsHandled rejectAsHandled)
    177177{
    178178    ASSERT(!m_valueOrException);
     
    180180    m_valueOrException = ExceptionOr<Value> { WTFMove(exception) };
    181181    for (auto& deferredPromise : m_deferredPromises)
    182         deferredPromise->reject(m_valueOrException->exception());
     182        deferredPromise->reject(m_valueOrException->exception(), rejectAsHandled);
    183183}
    184184
     
    230230}
    231231
    232 inline void DOMPromiseProxy<IDLVoid>::reject(Exception exception)
     232inline void DOMPromiseProxy<IDLVoid>::reject(Exception exception, RejectAsHandled rejectAsHandled)
    233233{
    234234    ASSERT(!m_valueOrException);
    235235    m_valueOrException = ExceptionOr<void> { WTFMove(exception) };
    236236    for (auto& deferredPromise : m_deferredPromises)
    237         deferredPromise->reject(m_valueOrException->exception());
     237        deferredPromise->reject(m_valueOrException->exception(), rejectAsHandled);
    238238}
    239239
     
    313313
    314314template<typename IDLType>
    315 inline void DOMPromiseProxyWithResolveCallback<IDLType>::reject(Exception exception)
     315inline void DOMPromiseProxyWithResolveCallback<IDLType>::reject(Exception exception, RejectAsHandled rejectAsHandled)
    316316{
    317317    ASSERT(!m_valueOrException);
     
    319319    m_valueOrException = ExceptionOr<void> { WTFMove(exception) };
    320320    for (auto& deferredPromise : m_deferredPromises)
    321         deferredPromise->reject(m_valueOrException->exception());
    322 }
    323 
    324 }
     321        deferredPromise->reject(m_valueOrException->exception(), rejectAsHandled);
     322}
     323
     324}
  • trunk/Source/WebCore/bindings/js/JSDOMPromiseDeferred.cpp

    r252607 r258664  
    7272        deferred()->reject(&lexicalGlobalObject, resolution);
    7373        break;
     74    case ResolveMode::RejectAsHandled:
     75        deferred()->rejectAsHandled(&lexicalGlobalObject, resolution);
     76        break;
    7477    }
    7578
     
    9396}
    9497
    95 void DeferredPromise::reject()
    96 {
    97     if (shouldIgnoreRequestToFulfill())
    98         return;
    99 
    100     ASSERT(deferred());
    101     ASSERT(m_globalObject);
    102     auto& lexicalGlobalObject = *m_globalObject;
    103     JSC::JSLockHolder locker(&lexicalGlobalObject);
    104     reject(lexicalGlobalObject, JSC::jsUndefined());
    105 }
    106 
    107 void DeferredPromise::reject(std::nullptr_t)
    108 {
    109     if (shouldIgnoreRequestToFulfill())
    110         return;
    111 
    112     ASSERT(deferred());
    113     ASSERT(m_globalObject);
    114     auto& lexicalGlobalObject = *m_globalObject;
    115     JSC::JSLockHolder locker(&lexicalGlobalObject);
    116     reject(lexicalGlobalObject, JSC::jsNull());
    117 }
    118 
    119 void DeferredPromise::reject(Exception exception)
     98void DeferredPromise::reject(RejectAsHandled rejectAsHandled)
     99{
     100    if (shouldIgnoreRequestToFulfill())
     101        return;
     102
     103    ASSERT(deferred());
     104    ASSERT(m_globalObject);
     105    auto& lexicalGlobalObject = *m_globalObject;
     106    JSC::JSLockHolder locker(&lexicalGlobalObject);
     107    reject(lexicalGlobalObject, JSC::jsUndefined(), rejectAsHandled);
     108}
     109
     110void DeferredPromise::reject(std::nullptr_t, RejectAsHandled rejectAsHandled)
     111{
     112    if (shouldIgnoreRequestToFulfill())
     113        return;
     114
     115    ASSERT(deferred());
     116    ASSERT(m_globalObject);
     117    auto& lexicalGlobalObject = *m_globalObject;
     118    JSC::JSLockHolder locker(&lexicalGlobalObject);
     119    reject(lexicalGlobalObject, JSC::jsNull(), rejectAsHandled);
     120}
     121
     122void DeferredPromise::reject(Exception exception, RejectAsHandled rejectAsHandled)
    120123{
    121124    if (shouldIgnoreRequestToFulfill())
     
    135138        scope.clearException();
    136139
    137         reject<IDLAny>(error);
     140        reject<IDLAny>(error, rejectAsHandled);
    138141        return;
    139142    }
     
    146149    }
    147150
    148     reject(lexicalGlobalObject, error);
    149 }
    150 
    151 void DeferredPromise::reject(ExceptionCode ec, const String& message)
     151    reject(lexicalGlobalObject, error, rejectAsHandled);
     152}
     153
     154void DeferredPromise::reject(ExceptionCode ec, const String& message, RejectAsHandled rejectAsHandled)
    152155{
    153156    if (shouldIgnoreRequestToFulfill())
     
    167170        scope.clearException();
    168171
    169         reject<IDLAny>(error);
     172        reject<IDLAny>(error, rejectAsHandled);
    170173        return;
    171174    }
     
    179182
    180183
    181     reject(lexicalGlobalObject, error);
    182 }
    183 
    184 void DeferredPromise::reject(const JSC::PrivateName& privateName)
     184    reject(lexicalGlobalObject, error, rejectAsHandled);
     185}
     186
     187void DeferredPromise::reject(const JSC::PrivateName& privateName, RejectAsHandled rejectAsHandled)
    185188{
    186189    if (shouldIgnoreRequestToFulfill())
     
    191194    JSC::JSGlobalObject* lexicalGlobalObject = m_globalObject.get();
    192195    JSC::JSLockHolder locker(lexicalGlobalObject);
    193     reject(*lexicalGlobalObject, JSC::Symbol::create(lexicalGlobalObject->vm(), privateName.uid()));
     196    reject(*lexicalGlobalObject, JSC::Symbol::create(lexicalGlobalObject->vm(), privateName.uid()), rejectAsHandled);
    194197}
    195198
  • trunk/Source/WebCore/bindings/js/JSDOMPromiseDeferred.h

    r252646 r258664  
    3737
    3838class JSDOMWindow;
     39enum class RejectAsHandled : uint8_t { No, Yes };
    3940
    4041class DeferredPromise : public DOMGuarded<JSC::JSPromise> {
     
    110111
    111112    template<class IDLType>
    112     void reject(typename IDLType::ParameterType value)
    113     {
    114         if (shouldIgnoreRequestToFulfill())
    115             return;
    116 
    117         ASSERT(deferred());
    118         ASSERT(globalObject());
    119         JSC::JSGlobalObject* lexicalGlobalObject = globalObject();
    120         JSC::JSLockHolder locker(lexicalGlobalObject);
    121         reject(*lexicalGlobalObject, toJS<IDLType>(*lexicalGlobalObject, *globalObject(), std::forward<typename IDLType::ParameterType>(value)));
    122     }
    123 
    124     void reject();
    125     void reject(std::nullptr_t);
    126     void reject(Exception);
    127     WEBCORE_EXPORT void reject(ExceptionCode, const String& = { });
    128     void reject(const JSC::PrivateName&);
     113    void reject(typename IDLType::ParameterType value, RejectAsHandled rejectAsHandled = RejectAsHandled::No)
     114    {
     115        if (shouldIgnoreRequestToFulfill())
     116            return;
     117
     118        ASSERT(deferred());
     119        ASSERT(globalObject());
     120        JSC::JSGlobalObject* lexicalGlobalObject = globalObject();
     121        JSC::JSLockHolder locker(lexicalGlobalObject);
     122        reject(*lexicalGlobalObject, toJS<IDLType>(*lexicalGlobalObject, *globalObject(), std::forward<typename IDLType::ParameterType>(value)), rejectAsHandled);
     123    }
     124
     125    void reject(RejectAsHandled = RejectAsHandled::No);
     126    void reject(std::nullptr_t, RejectAsHandled = RejectAsHandled::No);
     127    void reject(Exception, RejectAsHandled = RejectAsHandled::No);
     128    WEBCORE_EXPORT void reject(ExceptionCode, const String& = { }, RejectAsHandled = RejectAsHandled::No);
     129    void reject(const JSC::PrivateName&, RejectAsHandled = RejectAsHandled::No);
    129130
    130131    template<typename Callback>
     
    142143
    143144    template<typename Callback>
    144     void rejectWithCallback(Callback callback)
    145     {
    146         if (shouldIgnoreRequestToFulfill())
    147             return;
    148 
    149         ASSERT(deferred());
    150         ASSERT(globalObject());
    151         JSC::JSGlobalObject* lexicalGlobalObject = globalObject();
    152         JSC::JSLockHolder locker(lexicalGlobalObject);
    153         reject(*lexicalGlobalObject, callback(*globalObject()));
     145    void rejectWithCallback(Callback callback, RejectAsHandled rejectAsHandled = RejectAsHandled::No)
     146    {
     147        if (shouldIgnoreRequestToFulfill())
     148            return;
     149
     150        ASSERT(deferred());
     151        ASSERT(globalObject());
     152        JSC::JSGlobalObject* lexicalGlobalObject = globalObject();
     153        JSC::JSLockHolder locker(lexicalGlobalObject);
     154        reject(*lexicalGlobalObject, callback(*globalObject()), rejectAsHandled);
    154155    }
    155156
     
    169170    JSC::JSPromise* deferred() const { return guarded(); }
    170171
    171     enum class ResolveMode { Resolve, Reject };
     172    enum class ResolveMode { Resolve, Reject, RejectAsHandled };
    172173    WEBCORE_EXPORT void callFunction(JSC::JSGlobalObject&, ResolveMode, JSC::JSValue resolution);
    173174
    174175    void resolve(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue resolution) { callFunction(lexicalGlobalObject, ResolveMode::Resolve, resolution); }
    175     void reject(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue resolution) { callFunction(lexicalGlobalObject, ResolveMode::Reject, resolution); }
     176    void reject(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue resolution, RejectAsHandled rejectAsHandled)
     177    {
     178        callFunction(lexicalGlobalObject, rejectAsHandled == RejectAsHandled::Yes ? ResolveMode::RejectAsHandled : ResolveMode::Reject, resolution);
     179    }
    176180
    177181    Mode m_mode;
     
    208212    }
    209213
    210     void reject()
    211     {
    212         m_promise->reject();
     214    void reject(RejectAsHandled rejectAsHandled = RejectAsHandled::No)
     215    {
     216        m_promise->reject(rejectAsHandled);
    213217    }
    214218
     
    220224
    221225    template<typename IDLType>
    222     void rejectType(typename IDLType::ParameterType value)
    223     {
    224         m_promise->reject<IDLType>(std::forward<typename IDLType::ParameterType>(value));
     226    void rejectType(typename IDLType::ParameterType value, RejectAsHandled rejectAsHandled = RejectAsHandled::No)
     227    {
     228        m_promise->reject<IDLType>(std::forward<typename IDLType::ParameterType>(value), rejectAsHandled);
    225229    }
    226230
Note: See TracChangeset for help on using the changeset viewer.