Changeset 249058 in webkit
- Timestamp:
- Aug 23, 2019 11:51:20 AM (5 years ago)
- Location:
- trunk/Source
- Files:
-
- 11 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/API/JSContextRef.cpp
r248533 r249058 29 29 30 30 #include "APICast.h" 31 #include "APIUtils.h" 31 32 #include "CallFrame.h" 32 33 #include "InitializeThreading.h" … … 38 39 #include "SourceProvider.h" 39 40 #include "StackVisitor.h" 41 #include "StrongInlines.h" 40 42 #include "Watchdog.h" 41 43 #include <wtf/text/StringBuilder.h> … … 253 255 } 254 256 257 void JSGlobalContextSetUnhandledRejectionCallback(JSGlobalContextRef ctx, JSObjectRef function, JSValueRef* exception) 258 { 259 if (!ctx) { 260 ASSERT_NOT_REACHED(); 261 return; 262 } 263 264 ExecState* exec = toJS(ctx); 265 VM& vm = exec->vm(); 266 JSLockHolder locker(vm); 267 268 JSObject* object = toJS(function); 269 if (!object->isFunction(vm)) { 270 *exception = toRef(createTypeError(exec)); 271 return; 272 } 273 274 vm.vmEntryGlobalObject(exec)->setUnhandledRejectionCallback(vm, object); 275 } 255 276 256 277 class BacktraceFunctor { -
trunk/Source/JavaScriptCore/API/JSContextRefPrivate.h
r243376 r249058 129 129 JS_EXPORT void JSGlobalContextSetIncludesNativeCallStackWhenReportingExceptions(JSGlobalContextRef ctx, bool includesNativeCallStack) JSC_API_AVAILABLE(macos(10.10), ios(8.0)); 130 130 131 /*! 132 @function 133 @abstract Sets the unhandled promise rejection callback for a context. 134 @discussion Similar to window.addEventListener('unhandledrejection'), but for contexts not associated with a web view. 135 @param ctx The JSGlobalContext to set the callback on. 136 @param function The callback function to set, which receives the promise and rejection reason as arguments. 137 @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. 138 */ 139 JS_EXPORT void JSGlobalContextSetUnhandledRejectionCallback(JSGlobalContextRef ctx, JSObjectRef function, JSValueRef* exception) JSC_API_AVAILABLE(macos(JSC_MAC_TBA), ios(JSC_IOS_TBA)); 140 131 141 #ifdef __cplusplus 132 142 } -
trunk/Source/JavaScriptCore/API/tests/testapi.cpp
r244996 r249058 30 30 #include "JSObject.h" 31 31 32 #include <JavaScriptCore/JSContextRefPrivate.h> 32 33 #include <JavaScriptCore/JSObjectRefPrivate.h> 33 34 #include <JavaScriptCore/JavaScript.h> … … 138 139 void promiseResolveTrue(); 139 140 void promiseRejectTrue(); 141 void promiseUnhandledRejection(); 142 void promiseUnhandledRejectionFromUnhandledRejectionCallback(); 143 void promiseEarlyHandledRejections(); 140 144 141 145 int failed() const { return m_failed; } … … 455 459 auto trueValue = JSValueMakeBoolean(context, true); 456 460 JSObjectCallAsFunction(context, resolve, resolve, 1, &trueValue, &exception); 457 check(!exception, "No exception should be thrown resolv epromise");461 check(!exception, "No exception should be thrown resolving promise"); 458 462 check(passedTrueCalled, "then response function should have been called."); 459 463 } … … 480 484 APIString catchString("catch"); 481 485 JSValueRef catchFunction = JSObjectGetProperty(context, promise, catchString, &exception); 482 check(!exception && catchFunction && JSValueIsObject(context, catchFunction), "Promise should have a thenobject property");486 check(!exception && catchFunction && JSValueIsObject(context, catchFunction), "Promise should have a catch object property"); 483 487 484 488 JSValueRef passedTrueFunction = JSObjectMakeFunctionWithCallback(context, trueString, passedTrue); … … 488 492 auto trueValue = JSValueMakeBoolean(context, true); 489 493 JSObjectCallAsFunction(context, reject, reject, 1, &trueValue, &exception); 490 check(!exception, "No exception should be thrown resolve promise"); 491 check(passedTrueCalled, "then response function should have been called."); 494 check(!exception, "No exception should be thrown rejecting promise"); 495 check(passedTrueCalled, "catch response function should have been called."); 496 } 497 498 void TestAPI::promiseUnhandledRejection() 499 { 500 JSObjectRef reject; 501 JSValueRef exception = nullptr; 502 static auto promise = JSObjectMakeDeferredPromise(context, nullptr, &reject, &exception); 503 check(!exception, "creating a (reject-only) deferred promise should not throw"); 504 static auto reason = JSValueMakeString(context, APIString("reason")); 505 506 static TestAPI* tester = this; 507 static bool callbackCalled = false; 508 auto callback = [](JSContextRef ctx, JSObjectRef, JSObjectRef, size_t argumentCount, const JSValueRef arguments[], JSValueRef*) -> JSValueRef { 509 tester->check(argumentCount && JSValueIsStrictEqual(ctx, arguments[0], promise), "callback should receive rejected promise as first argument"); 510 tester->check(argumentCount > 1 && JSValueIsStrictEqual(ctx, arguments[1], reason), "callback should receive rejection reason as second argument"); 511 tester->check(argumentCount == 2, "callback should not receive a third argument"); 512 callbackCalled = true; 513 return JSValueMakeUndefined(ctx); 514 }; 515 auto callbackFunction = JSObjectMakeFunctionWithCallback(context, APIString("callback"), callback); 516 517 JSGlobalContextSetUnhandledRejectionCallback(context, callbackFunction, &exception); 518 check(!exception, "setting unhandled rejection callback should not throw"); 519 520 JSObjectCallAsFunction(context, reject, reject, 1, &reason, &exception); 521 check(!exception && callbackCalled, "unhandled rejection callback should be called upon unhandled rejection"); 522 } 523 524 void TestAPI::promiseUnhandledRejectionFromUnhandledRejectionCallback() 525 { 526 static JSObjectRef reject; 527 static JSValueRef exception = nullptr; 528 JSObjectMakeDeferredPromise(context, nullptr, &reject, &exception); 529 check(!exception, "creating a (reject-only) deferred promise should not throw"); 530 531 static auto callbackCallCount = 0; 532 auto callback = [](JSContextRef ctx, JSObjectRef, JSObjectRef, size_t, const JSValueRef[], JSValueRef*) -> JSValueRef { 533 if (!callbackCallCount) 534 JSObjectCallAsFunction(ctx, reject, reject, 0, nullptr, &exception); 535 callbackCallCount++; 536 return JSValueMakeUndefined(ctx); 537 }; 538 auto callbackFunction = JSObjectMakeFunctionWithCallback(context, APIString("callback"), callback); 539 540 JSGlobalContextSetUnhandledRejectionCallback(context, callbackFunction, &exception); 541 check(!exception, "setting unhandled rejection callback should not throw"); 542 543 callFunction("(function () { Promise.reject(); })"); 544 check(!exception && callbackCallCount == 2, "unhandled rejection from unhandled rejection callback should also trigger the callback"); 545 } 546 547 void TestAPI::promiseEarlyHandledRejections() 548 { 549 JSValueRef exception = nullptr; 550 551 static bool callbackCalled = false; 552 auto callback = [](JSContextRef ctx, JSObjectRef, JSObjectRef, size_t, const JSValueRef[], JSValueRef*) -> JSValueRef { 553 callbackCalled = true; 554 return JSValueMakeUndefined(ctx); 555 }; 556 auto callbackFunction = JSObjectMakeFunctionWithCallback(context, APIString("callback"), callback); 557 558 JSGlobalContextSetUnhandledRejectionCallback(context, callbackFunction, &exception); 559 check(!exception, "setting unhandled rejection callback should not throw"); 560 561 callFunction("(function () { const p = Promise.reject(); p.catch(() => {}); })"); 562 check(!callbackCalled, "unhandled rejection callback should not be called for synchronous early-handled rejection"); 563 564 callFunction("(function () { const p = Promise.reject(); Promise.resolve().then(() => { p.catch(() => {}); }); })"); 565 check(!callbackCalled, "unhandled rejection callback should not be called for asynchronous early-handled rejection"); 492 566 } 493 567 … … 522 596 RUN(promiseResolveTrue()); 523 597 RUN(promiseRejectTrue()); 598 RUN(promiseUnhandledRejection()); 599 RUN(promiseUnhandledRejectionFromUnhandledRejectionCallback()); 600 RUN(promiseEarlyHandledRejections()); 524 601 525 602 if (tasks.isEmpty()) { -
trunk/Source/JavaScriptCore/ChangeLog
r249052 r249058 1 2019-08-23 Ross Kirsling <ross.kirsling@sony.com> 2 3 JSC should have public API for unhandled promise rejections 4 https://bugs.webkit.org/show_bug.cgi?id=197172 5 6 Reviewed by Keith Miller. 7 8 This patch makes it possible to register a unhandled promise rejection callback via the JSC API. 9 Since there is no event loop in such an environment, this callback fires off of the microtask queue. 10 The callback receives the promise and rejection reason as arguments and its return value is ignored. 11 12 * API/JSContextRef.cpp: 13 (JSGlobalContextSetUnhandledRejectionCallback): Added. 14 * API/JSContextRefPrivate.h: 15 Add new C++ API call. 16 17 * API/tests/testapi.cpp: 18 (TestAPI::promiseResolveTrue): Clean up test output. 19 (TestAPI::promiseRejectTrue): Clean up test output. 20 (TestAPI::promiseUnhandledRejection): Added. 21 (TestAPI::promiseUnhandledRejectionFromUnhandledRejectionCallback): Added. 22 (TestAPI::promiseEarlyHandledRejections): Added. 23 (testCAPIViaCpp): 24 Add new C++ API test. 25 26 * jsc.cpp: 27 (GlobalObject::finishCreation): 28 (functionSetUnhandledRejectionCallback): Added. 29 Add corresponding global to JSC shell. 30 31 * runtime/JSGlobalObject.h: 32 (JSC::JSGlobalObject::setUnhandledRejectionCallback): Added. 33 (JSC::JSGlobalObject::unhandledRejectionCallback const): Added. 34 Keep a strong reference to the callback. 35 36 * runtime/JSGlobalObjectFunctions.cpp: 37 (JSC::globalFuncHostPromiseRejectionTracker): 38 Add default behavior. 39 40 * runtime/VM.cpp: 41 (JSC::VM::callPromiseRejectionCallback): Added. 42 (JSC::VM::didExhaustMicrotaskQueue): Added. 43 (JSC::VM::promiseRejected): Added. 44 (JSC::VM::drainMicrotasks): 45 When microtask queue is exhausted, deal with any pending unhandled rejections 46 (in a manner based on RejectedPromiseTracker's reportUnhandledRejections), 47 then make sure this didn't cause any new microtasks to be added to the queue. 48 49 * runtime/VM.h: 50 Store unhandled rejections. 51 (This collection will always be empty in the presence of WebCore.) 52 1 53 2019-08-22 Mark Lam <mark.lam@apple.com> 2 54 -
trunk/Source/JavaScriptCore/jsc.cpp
r248929 r249058 385 385 static EncodedJSValue JSC_HOST_CALL functionTotalCompileTime(ExecState*); 386 386 387 static EncodedJSValue JSC_HOST_CALL functionSetUnhandledRejectionCallback(ExecState*); 388 387 389 struct Script { 388 390 enum class StrictMode { … … 639 641 addFunction(vm, "mallocInALoop", functionMallocInALoop, 0); 640 642 addFunction(vm, "totalCompileTime", functionTotalCompileTime, 0); 643 644 addFunction(vm, "setUnhandledRejectionCallback", functionSetUnhandledRejectionCallback, 1); 641 645 } 642 646 … … 2385 2389 #endif // ENABLE(WEBASSEMBLY) 2386 2390 2391 EncodedJSValue JSC_HOST_CALL functionSetUnhandledRejectionCallback(ExecState* exec) 2392 { 2393 VM& vm = exec->vm(); 2394 JSObject* object = exec->argument(0).getObject(); 2395 auto scope = DECLARE_THROW_SCOPE(vm); 2396 2397 if (!object || !object->isFunction(vm)) 2398 return throwVMTypeError(exec, scope); 2399 2400 exec->lexicalGlobalObject()->setUnhandledRejectionCallback(vm, object); 2401 return JSValue::encode(jsUndefined()); 2402 } 2403 2387 2404 // Use SEH for Release builds only to get rid of the crash report dialog 2388 2405 // (luckily the same tests fail in Release and Debug builds so far). Need to -
trunk/Source/JavaScriptCore/runtime/JSGlobalObject.h
r248906 r249058 426 426 String m_name; 427 427 428 Strong<JSObject> m_unhandledRejectionCallback; 429 428 430 Debugger* m_debugger; 429 431 … … 808 810 const String& name() const { return m_name; } 809 811 812 void setUnhandledRejectionCallback(VM& vm, JSObject* function) { m_unhandledRejectionCallback.set(vm, function); } 813 JSObject* unhandledRejectionCallback() const { return m_unhandledRejectionCallback.get(); } 814 810 815 JSObject* arrayBufferConstructor() const { return m_arrayBufferStructure.constructor(this); } 811 816 -
trunk/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp
r245645 r249058 767 767 auto scope = DECLARE_THROW_SCOPE(vm); 768 768 769 if (!globalObject->globalObjectMethodTable()->promiseRejectionTracker) 769 JSPromise* promise = jsCast<JSPromise*>(exec->argument(0)); 770 771 // InternalPromises should not be exposed to user scripts. 772 if (jsDynamicCast<JSInternalPromise*>(vm, promise)) 770 773 return JSValue::encode(jsUndefined()); 771 774 772 JSPromise* promise = jsCast<JSPromise*>(exec->argument(0));773 775 JSValue operationValue = exec->argument(1); 774 776 … … 778 780 scope.assertNoException(); 779 781 780 globalObject->globalObjectMethodTable()->promiseRejectionTracker(globalObject, exec, promise, operation); 782 if (globalObject->globalObjectMethodTable()->promiseRejectionTracker) 783 globalObject->globalObjectMethodTable()->promiseRejectionTracker(globalObject, exec, promise, operation); 784 else { 785 switch (operation) { 786 case JSPromiseRejectionOperation::Reject: 787 vm.promiseRejected(promise); 788 break; 789 case JSPromiseRejectionOperation::Handle: 790 // do nothing 791 break; 792 } 793 } 781 794 RETURN_IF_EXCEPTION(scope, { }); 782 795 -
trunk/Source/JavaScriptCore/runtime/VM.cpp
r248846 r249058 1091 1091 } 1092 1092 1093 void VM::callPromiseRejectionCallback(Strong<JSPromise>& promise) 1094 { 1095 JSObject* callback = promise->globalObject()->unhandledRejectionCallback(); 1096 if (!callback) 1097 return; 1098 1099 auto scope = DECLARE_CATCH_SCOPE(*this); 1100 1101 CallData callData; 1102 CallType callType = getCallData(*this, callback, callData); 1103 ASSERT(callType != CallType::None); 1104 1105 MarkedArgumentBuffer args; 1106 args.append(promise.get()); 1107 args.append(promise->result(*this)); 1108 call(promise->globalObject()->globalExec(), callback, callType, callData, jsNull(), args); 1109 scope.clearException(); 1110 } 1111 1112 void VM::didExhaustMicrotaskQueue() 1113 { 1114 auto unhandledRejections = WTFMove(m_aboutToBeNotifiedRejectedPromises); 1115 for (auto& promise : unhandledRejections) { 1116 if (promise->isHandled(*this)) 1117 continue; 1118 1119 callPromiseRejectionCallback(promise); 1120 } 1121 } 1122 1123 void VM::promiseRejected(JSPromise* promise) 1124 { 1125 m_aboutToBeNotifiedRejectedPromises.constructAndAppend(*this, promise); 1126 } 1127 1093 1128 void VM::drainMicrotasks() 1094 1129 { 1095 while (!m_microtaskQueue.isEmpty()) { 1096 m_microtaskQueue.takeFirst()->run(); 1097 if (m_onEachMicrotaskTick) 1098 m_onEachMicrotaskTick(*this); 1099 } 1130 do { 1131 while (!m_microtaskQueue.isEmpty()) { 1132 m_microtaskQueue.takeFirst()->run(); 1133 if (m_onEachMicrotaskTick) 1134 m_onEachMicrotaskTick(*this); 1135 } 1136 didExhaustMicrotaskQueue(); 1137 } while (!m_microtaskQueue.isEmpty()); 1100 1138 finalizeSynchronousJSExecution(); 1101 1139 } -
trunk/Source/JavaScriptCore/runtime/VM.h
r248546 r249058 124 124 class JSGlobalObject; 125 125 class JSObject; 126 class JSPromise; 126 127 class JSPropertyNameEnumerator; 127 128 class JSRunLoopTimer; … … 923 924 void notifyNeedWatchdogCheck() { m_traps.fireTrap(VMTraps::NeedWatchdogCheck); } 924 925 926 void promiseRejected(JSPromise*); 927 925 928 #if ENABLE(EXCEPTION_SCOPE_VERIFICATION) 926 929 StackTrace* nativeStackTraceOfLastThrow() const { return m_nativeStackTraceOfLastThrow.get(); } … … 1008 1011 static void primitiveGigacageDisabledCallback(void*); 1009 1012 void primitiveGigacageDisabled(); 1013 1014 void callPromiseRejectionCallback(Strong<JSPromise>&); 1015 void didExhaustMicrotaskQueue(); 1010 1016 1011 1017 #if ENABLE(GC_VALIDATION) … … 1064 1070 std::unique_ptr<BytecodeIntrinsicRegistry> m_bytecodeIntrinsicRegistry; 1065 1071 1072 // FIXME: We should remove handled promises from this list at GC flip. <https://webkit.org/b/201005> 1073 Vector<Strong<JSPromise>> m_aboutToBeNotifiedRejectedPromises; 1074 1066 1075 WTF::Function<void(VM&)> m_onEachMicrotaskTick; 1067 1076 uintptr_t m_currentWeakRefVersion { 0 }; -
trunk/Source/WebCore/ChangeLog
r249056 r249058 1 2019-08-23 Ross Kirsling <ross.kirsling@sony.com> 2 3 JSC should have public API for unhandled promise rejections 4 https://bugs.webkit.org/show_bug.cgi?id=197172 5 6 Reviewed by Keith Miller. 7 8 * bindings/js/JSDOMGlobalObject.cpp: 9 (WebCore::JSDOMGlobalObject::promiseRejectionTracker): 10 Move JSInternalPromise early-out to JSC side. 11 1 12 2019-08-23 Kate Cheney <katherine_cheney@apple.com> 2 13 -
trunk/Source/WebCore/bindings/js/JSDOMGlobalObject.cpp
r244312 r249058 217 217 // https://html.spec.whatwg.org/multipage/webappapis.html#the-hostpromiserejectiontracker-implementation 218 218 219 VM& vm = exec->vm();220 219 auto& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(jsGlobalObject); 221 220 auto* context = globalObject.scriptExecutionContext(); 222 221 if (!context) 223 return;224 225 // InternalPromises should not be exposed to user scripts.226 if (JSC::jsDynamicCast<JSC::JSInternalPromise*>(vm, promise))227 222 return; 228 223
Note: See TracChangeset
for help on using the changeset viewer.