Changeset 244312 in webkit


Ignore:
Timestamp:
Apr 15, 2019 5:02:32 PM (5 years ago)
Author:
Devin Rousso
Message:

Web Inspector: fake value descriptors for promises add a catch handler, preventing "rejectionhandled" events from being fired
https://bugs.webkit.org/show_bug.cgi?id=196484
<rdar://problem/49114725>

Reviewed by Joseph Pecoraro.

Source/JavaScriptCore:

Only add a catch handler when the promise is reachable via a native getter and is known to
have rejected. A non-rejected promise doesn't need a catch handler, and any promise that
isn't reachable via a getter won't actually be reached, as InjectedScript doesn't call any
functions, instead only getting the function object itself.

  • inspector/InjectedScriptSource.js:

(InjectedScript.prototype._propertyDescriptors.createFakeValueDescriptor):

  • inspector/JSInjectedScriptHost.h:
  • inspector/JSInjectedScriptHost.cpp:

(Inspector::JSInjectedScriptHost::isPromiseRejectedWithNativeGetterTypeError): Added.

  • inspector/JSInjectedScriptHostPrototype.cpp:

(Inspector::JSInjectedScriptHostPrototype::finishCreation):
(Inspector::jsInjectedScriptHostPrototypeFunctionIsPromiseRejectedWithNativeGetterTypeError): Added.

  • runtime/ErrorInstance.h:

(JSC::ErrorInstance::setNativeGetterTypeError): Added.
(JSC::ErrorInstance::isNativeGetterTypeError const): Added.

  • runtime/Error.h:

(JSC::throwVMGetterTypeError): Added.

  • runtime/Error.cpp:

(JSC::createGetterTypeError): Added.
(JSC::throwGetterTypeError): Added.
(JSC::throwDOMAttributeGetterTypeError):

Source/WebCore:

Test: inspector/runtime/promise-native-getter.html

Mark errors created from getters as being isNativeGetterTypeError.

  • bindings/js/JSDOMExceptionHandling.cpp:

(WebCore::throwGetterTypeError):
(WebCore::rejectPromiseWithGetterTypeError):
(WebCore::rejectPromiseWithThisTypeError):

  • bindings/js/JSDOMGlobalObject.cpp:

(WebCore::makeGetterTypeErrorForBuiltins):

  • bindings/js/JSDOMPromiseDeferred.h:
  • bindings/js/JSDOMPromiseDeferred.cpp:

(WebCore::createRejectedPromiseWithTypeError):

  • Modules/streams/WritableStream.js:

(getter.closed):
(getter.ready):

LayoutTests:

  • inspector/runtime/promise-native-getter.html: Added.
  • inspector/runtime/promise-native-getter-expected.txt: Added.
Location:
trunk
Files:
2 added
15 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r244304 r244312  
     12019-04-15  Devin Rousso  <drousso@apple.com>
     2
     3        Web Inspector: fake value descriptors for promises add a catch handler, preventing "rejectionhandled" events from being fired
     4        https://bugs.webkit.org/show_bug.cgi?id=196484
     5        <rdar://problem/49114725>
     6
     7        Reviewed by Joseph Pecoraro.
     8
     9        * inspector/runtime/promise-native-getter.html: Added.
     10        * inspector/runtime/promise-native-getter-expected.txt: Added.
     11
    1122019-04-15  Shawn Roberts  <sroberts@apple.com>
    213
  • trunk/Source/JavaScriptCore/ChangeLog

    r244309 r244312  
     12019-04-15  Devin Rousso  <drousso@apple.com>
     2
     3        Web Inspector: fake value descriptors for promises add a catch handler, preventing "rejectionhandled" events from being fired
     4        https://bugs.webkit.org/show_bug.cgi?id=196484
     5        <rdar://problem/49114725>
     6
     7        Reviewed by Joseph Pecoraro.
     8
     9        Only add a catch handler when the promise is reachable via a native getter and is known to
     10        have rejected. A non-rejected promise doesn't need a catch handler, and any promise that
     11        isn't reachable via a getter won't actually be reached, as `InjectedScript` doesn't call any
     12        functions, instead only getting the function object itself.
     13
     14        * inspector/InjectedScriptSource.js:
     15        (InjectedScript.prototype._propertyDescriptors.createFakeValueDescriptor):
     16
     17        * inspector/JSInjectedScriptHost.h:
     18        * inspector/JSInjectedScriptHost.cpp:
     19        (Inspector::JSInjectedScriptHost::isPromiseRejectedWithNativeGetterTypeError): Added.
     20        * inspector/JSInjectedScriptHostPrototype.cpp:
     21        (Inspector::JSInjectedScriptHostPrototype::finishCreation):
     22        (Inspector::jsInjectedScriptHostPrototypeFunctionIsPromiseRejectedWithNativeGetterTypeError): Added.
     23
     24        * runtime/ErrorInstance.h:
     25        (JSC::ErrorInstance::setNativeGetterTypeError): Added.
     26        (JSC::ErrorInstance::isNativeGetterTypeError const): Added.
     27
     28        * runtime/Error.h:
     29        (JSC::throwVMGetterTypeError): Added.
     30        * runtime/Error.cpp:
     31        (JSC::createGetterTypeError): Added.
     32        (JSC::throwGetterTypeError): Added.
     33        (JSC::throwDOMAttributeGetterTypeError):
     34
    1352019-04-15  Robin Morisset  <rmorisset@apple.com>
    236
  • trunk/Source/JavaScriptCore/inspector/InjectedScriptSource.js

    r243161 r244312  
    654654                    fakeDescriptor.symbol = symbol;
    655655                // Silence any possible unhandledrejection exceptions created from accessing a native accessor with a wrong this object.
    656                 if (fakeDescriptor.value instanceof Promise)
     656                if (fakeDescriptor.value instanceof Promise && InjectedScriptHost.isPromiseRejectedWithNativeGetterTypeError(fakeDescriptor.value))
    657657                    fakeDescriptor.value.catch(function(){});
    658658                return fakeDescriptor;
  • trunk/Source/JavaScriptCore/inspector/JSInjectedScriptHost.cpp

    r239583 r244312  
    143143}
    144144
     145JSValue JSInjectedScriptHost::isPromiseRejectedWithNativeGetterTypeError(ExecState* exec)
     146{
     147    VM& vm = exec->vm();
     148    auto scope = DECLARE_THROW_SCOPE(vm);
     149
     150    auto* promise = jsDynamicCast<JSPromise*>(vm, exec->argument(0));
     151    if (!promise || promise->status(vm) != JSPromise::Status::Rejected)
     152        return throwTypeError(exec, scope, "InjectedScriptHost.isPromiseRejectedWithNativeGetterTypeError first argument must be a rejected Promise."_s);
     153
     154    bool result = false;
     155    if (auto* errorInstance = jsDynamicCast<ErrorInstance*>(vm, promise->result(vm)))
     156        result = errorInstance->isNativeGetterTypeError();
     157    return jsBoolean(result);
     158}
     159
    145160JSValue JSInjectedScriptHost::subtype(ExecState* exec)
    146161{
  • trunk/Source/JavaScriptCore/inspector/JSInjectedScriptHost.h

    r239583 r244312  
    6363    JSC::JSValue internalConstructorName(JSC::ExecState*);
    6464    JSC::JSValue isHTMLAllCollection(JSC::ExecState*);
     65    JSC::JSValue isPromiseRejectedWithNativeGetterTypeError(JSC::ExecState*);
    6566    JSC::JSValue subtype(JSC::ExecState*);
    6667    JSC::JSValue functionDetails(JSC::ExecState*);
  • trunk/Source/JavaScriptCore/inspector/JSInjectedScriptHostPrototype.cpp

    r242650 r244312  
    4444static EncodedJSValue JSC_HOST_CALL jsInjectedScriptHostPrototypeFunctionInternalConstructorName(ExecState*);
    4545static EncodedJSValue JSC_HOST_CALL jsInjectedScriptHostPrototypeFunctionIsHTMLAllCollection(ExecState*);
     46static EncodedJSValue JSC_HOST_CALL jsInjectedScriptHostPrototypeFunctionIsPromiseRejectedWithNativeGetterTypeError(ExecState*);
    4647static EncodedJSValue JSC_HOST_CALL jsInjectedScriptHostPrototypeFunctionProxyTargetValue(ExecState*);
    4748static EncodedJSValue JSC_HOST_CALL jsInjectedScriptHostPrototypeFunctionWeakMapSize(ExecState*);
     
    6869    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("internalConstructorName", jsInjectedScriptHostPrototypeFunctionInternalConstructorName, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
    6970    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("isHTMLAllCollection", jsInjectedScriptHostPrototypeFunctionIsHTMLAllCollection, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
     71    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("isPromiseRejectedWithNativeGetterTypeError", jsInjectedScriptHostPrototypeFunctionIsPromiseRejectedWithNativeGetterTypeError, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
    7072    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("proxyTargetValue", jsInjectedScriptHostPrototypeFunctionProxyTargetValue, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
    7173    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("weakMapSize", jsInjectedScriptHostPrototypeFunctionWeakMapSize, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
     
    119121}
    120122
     123EncodedJSValue JSC_HOST_CALL jsInjectedScriptHostPrototypeFunctionIsPromiseRejectedWithNativeGetterTypeError(ExecState* exec)
     124{
     125    VM& vm = exec->vm();
     126    auto scope = DECLARE_THROW_SCOPE(vm);
     127
     128    JSValue thisValue = exec->thisValue();
     129    JSInjectedScriptHost* castedThis = jsDynamicCast<JSInjectedScriptHost*>(vm, thisValue);
     130    if (!castedThis)
     131        return throwVMTypeError(exec, scope);
     132
     133    return JSValue::encode(castedThis->isPromiseRejectedWithNativeGetterTypeError(exec));
     134}
     135
    121136EncodedJSValue JSC_HOST_CALL jsInjectedScriptHostPrototypeFunctionProxyTargetValue(ExecState* exec)
    122137{
  • trunk/Source/JavaScriptCore/runtime/Error.cpp

    r242596 r244312  
    124124}
    125125
     126JSObject* createGetterTypeError(ExecState* exec, const String& message)
     127{
     128    ASSERT(!message.isEmpty());
     129    JSGlobalObject* globalObject = exec->lexicalGlobalObject();
     130    auto* error = ErrorInstance::create(exec, globalObject->vm(), globalObject->errorStructure(ErrorType::TypeError), message);
     131    error->setNativeGetterTypeError();
     132    return error;
     133}
     134
    126135class FindFirstCallerFrameWithCodeblockFunctor {
    127136public:
     
    291300}
    292301
     302Exception* throwGetterTypeError(ExecState* exec, ThrowScope& scope, const String& message)
     303{
     304    return throwException(exec, scope, createGetterTypeError(exec, message));
     305}
     306
    293307JSValue throwDOMAttributeGetterTypeError(ExecState* exec, ThrowScope& scope, const ClassInfo* classInfo, PropertyName propertyName)
    294308{
    295     return throwTypeError(exec, scope, makeString("The ", classInfo->className, '.', String(propertyName.uid()), " getter can only be used on instances of ", classInfo->className));
     309    return throwGetterTypeError(exec, scope, makeString("The ", classInfo->className, '.', String(propertyName.uid()), " getter can only be used on instances of ", classInfo->className));
    296310}
    297311
  • trunk/Source/JavaScriptCore/runtime/Error.h

    r242596 r244312  
    6868JS_EXPORT_PRIVATE JSObject* createError(ExecState*, ErrorType, const String&);
    6969
     70JSObject* createGetterTypeError(ExecState*, const String&);
     71
    7072std::unique_ptr<Vector<StackFrame>> getStackTrace(ExecState*, VM&, JSObject*, bool useCurrentFrame);
    7173void getBytecodeOffset(ExecState*, VM&, Vector<StackFrame>*, CallFrame*&, unsigned& bytecodeOffset);
     
    8587JS_EXPORT_PRIVATE Exception* throwSyntaxError(ExecState*, ThrowScope&, const String& errorMessage);
    8688inline Exception* throwRangeError(ExecState* state, ThrowScope& scope, const String& errorMessage) { return throwException(state, scope, createRangeError(state, errorMessage)); }
     89
     90JS_EXPORT_PRIVATE Exception* throwGetterTypeError(ExecState*, ThrowScope&, const String& errorMessage);
    8791JS_EXPORT_PRIVATE JSValue throwDOMAttributeGetterTypeError(ExecState*, ThrowScope&, const ClassInfo*, PropertyName);
    8892
     
    9599inline EncodedJSValue throwVMTypeError(ExecState* exec, ThrowScope& scope, const String& errorMessage) { return JSValue::encode(throwTypeError(exec, scope, errorMessage)); }
    96100inline EncodedJSValue throwVMRangeError(ExecState* state, ThrowScope& scope, const String& errorMessage) { return JSValue::encode(throwRangeError(state, scope, errorMessage)); }
     101inline EncodedJSValue throwVMGetterTypeError(ExecState* exec, ThrowScope& scope, const String& errorMessage) { return JSValue::encode(throwGetterTypeError(exec, scope, errorMessage)); }
    97102inline EncodedJSValue throwVMDOMAttributeGetterTypeError(ExecState* state, ThrowScope& scope, const ClassInfo* classInfo, PropertyName propertyName) { return JSValue::encode(throwDOMAttributeGetterTypeError(state, scope, classInfo, propertyName)); }
    98103
  • trunk/Source/JavaScriptCore/runtime/ErrorInstance.h

    r240965 r244312  
    6666    bool isOutOfMemoryError() const { return m_outOfMemoryError; }
    6767
     68    void setNativeGetterTypeError() { m_nativeGetterTypeError = true; }
     69    bool isNativeGetterTypeError() const { return m_nativeGetterTypeError; }
     70
    6871    JS_EXPORT_PRIVATE String sanitizedToString(ExecState*);
    6972   
     
    106109    bool m_outOfMemoryError { false };
    107110    bool m_errorInfoMaterialized { false };
     111    bool m_nativeGetterTypeError { false };
    108112};
    109113
  • trunk/Source/WebCore/ChangeLog

    r244307 r244312  
     12019-04-15  Devin Rousso  <drousso@apple.com>
     2
     3        Web Inspector: fake value descriptors for promises add a catch handler, preventing "rejectionhandled" events from being fired
     4        https://bugs.webkit.org/show_bug.cgi?id=196484
     5        <rdar://problem/49114725>
     6
     7        Reviewed by Joseph Pecoraro.
     8
     9        Test: inspector/runtime/promise-native-getter.html
     10
     11        Mark errors created from getters as being `isNativeGetterTypeError`.
     12
     13        * bindings/js/JSDOMExceptionHandling.cpp:
     14        (WebCore::throwGetterTypeError):
     15        (WebCore::rejectPromiseWithGetterTypeError):
     16        (WebCore::rejectPromiseWithThisTypeError):
     17
     18        * bindings/js/JSDOMGlobalObject.cpp:
     19        (WebCore::makeGetterTypeErrorForBuiltins):
     20
     21        * bindings/js/JSDOMPromiseDeferred.h:
     22        * bindings/js/JSDOMPromiseDeferred.cpp:
     23        (WebCore::createRejectedPromiseWithTypeError):
     24
     25        * Modules/streams/WritableStream.js:
     26        (getter.closed):
     27        (getter.ready):
     28
    1292019-04-15  Jer Noble  <jer.noble@apple.com>
    230
  • trunk/Source/WebCore/Modules/streams/WritableStream.js

    r242365 r244312  
    155155
    156156    if (!@isWritableStream(this))
    157         return @Promise.@reject(@makeTypeError("The WritableStream.closed getter can only be used on instances of WritableStream"));
     157        return @Promise.@reject(@makeGetterTypeError("WritableStream", "closed"));
    158158
    159159    return @getByIdDirectPrivate(this, "closedPromiseCapability").@promise;
     
    166166
    167167    if (!@isWritableStream(this))
    168         return @Promise.@reject(@makeTypeError("The WritableStream.ready getter can only be used on instances of WritableStream"));
     168        return @Promise.@reject(@makeGetterTypeError("WritableStream", "ready"));
    169169
    170170    return @getByIdDirectPrivate(this, "readyPromiseCapability").@promise;
  • trunk/Source/WebCore/bindings/js/JSDOMExceptionHandling.cpp

    r233122 r244312  
    271271JSC::EncodedJSValue throwGetterTypeError(JSC::ExecState& state, JSC::ThrowScope& scope, const char* interfaceName, const char* attributeName)
    272272{
    273     return throwVMTypeError(&state, scope, makeGetterTypeErrorMessage(interfaceName, attributeName));
     273    return throwVMGetterTypeError(&state, scope, makeGetterTypeErrorMessage(interfaceName, attributeName));
    274274}
    275275
    276276JSC::EncodedJSValue rejectPromiseWithGetterTypeError(JSC::ExecState& state, const char* interfaceName, const char* attributeName)
    277277{
    278     return createRejectedPromiseWithTypeError(state, makeGetterTypeErrorMessage(interfaceName, attributeName));
     278    return createRejectedPromiseWithTypeError(state, makeGetterTypeErrorMessage(interfaceName, attributeName), RejectedPromiseWithTypeErrorCause::NativeGetter);
    279279}
    280280
     
    303303JSC::EncodedJSValue rejectPromiseWithThisTypeError(JSC::ExecState& state, const char* interfaceName, const char* methodName)
    304304{
    305     return createRejectedPromiseWithTypeError(state, makeThisTypeErrorMessage(interfaceName, methodName));
     305    return createRejectedPromiseWithTypeError(state, makeThisTypeErrorMessage(interfaceName, methodName), RejectedPromiseWithTypeErrorCause::InvalidThis);
    306306}
    307307
  • trunk/Source/WebCore/bindings/js/JSDOMGlobalObject.cpp

    r242636 r244312  
    101101    auto attributeName = execState->uncheckedArgument(1).getString(execState);
    102102    scope.assertNoException();
    103     return JSValue::encode(createTypeError(execState, makeGetterTypeErrorMessage(interfaceName.utf8().data(), attributeName.utf8().data())));
     103
     104    auto error = static_cast<ErrorInstance*>(createTypeError(execState, makeGetterTypeErrorMessage(interfaceName.utf8().data(), attributeName.utf8().data())));
     105    error->setNativeGetterTypeError();
     106    return JSValue::encode(error);
    104107}
    105108
  • trunk/Source/WebCore/bindings/js/JSDOMPromiseDeferred.cpp

    r239256 r244312  
    198198}
    199199
    200 JSC::EncodedJSValue createRejectedPromiseWithTypeError(JSC::ExecState& state, const String& errorMessage)
     200JSC::EncodedJSValue createRejectedPromiseWithTypeError(JSC::ExecState& state, const String& errorMessage, RejectedPromiseWithTypeErrorCause cause)
    201201{
    202202    ASSERT(state.lexicalGlobalObject());
     
    205205    auto promiseConstructor = globalObject.promiseConstructor();
    206206    auto rejectFunction = promiseConstructor->get(&state, state.vm().propertyNames->builtinNames().rejectPrivateName());
    207     auto rejectionValue = createTypeError(&state, errorMessage);
     207    auto* rejectionValue = static_cast<ErrorInstance*>(createTypeError(&state, errorMessage));
     208    if (cause == RejectedPromiseWithTypeErrorCause::NativeGetter)
     209        rejectionValue->setNativeGetterTypeError();
    208210
    209211    CallData callData;
  • trunk/Source/WebCore/bindings/js/JSDOMPromiseDeferred.h

    r239256 r244312  
    258258void fulfillPromiseWithArrayBuffer(Ref<DeferredPromise>&&, const void*, size_t);
    259259WEBCORE_EXPORT void rejectPromiseWithExceptionIfAny(JSC::ExecState&, JSDOMGlobalObject&, JSC::JSPromiseDeferred&);
    260 JSC::EncodedJSValue createRejectedPromiseWithTypeError(JSC::ExecState&, const String&);
     260
     261enum class RejectedPromiseWithTypeErrorCause { NativeGetter, InvalidThis };
     262JSC::EncodedJSValue createRejectedPromiseWithTypeError(JSC::ExecState&, const String&, RejectedPromiseWithTypeErrorCause);
    261263
    262264using PromiseFunction = void(JSC::ExecState&, Ref<DeferredPromise>&&);
Note: See TracChangeset for help on using the changeset viewer.