Index: src/isolate.cc |
diff --git a/src/isolate.cc b/src/isolate.cc |
index 46805d26df30d63f2376925973ad25441c0f8c7b..9cbabef01d9859a8be2e0d3935aab0f21140ec7e 100644 |
--- a/src/isolate.cc |
+++ b/src/isolate.cc |
@@ -1808,23 +1808,91 @@ void Isolate::PopPromise() { |
global_handles()->Destroy(global_promise.location()); |
} |
-bool Isolate::PromiseHasUserDefinedRejectHandler(Handle<Object> promise) { |
- Handle<JSFunction> fun = promise_has_user_defined_reject_handler(); |
- Handle<Object> has_reject_handler; |
- // If we are, e.g., overflowing the stack, don't try to call out to JS |
- if (!AllowJavascriptExecution::IsAllowed(this)) return false; |
- // Call the registered function to check for a handler |
- if (Execution::TryCall(this, fun, promise, 0, NULL) |
- .ToHandle(&has_reject_handler)) { |
- return has_reject_handler->IsTrue(this); |
- } |
- // If an exception is thrown in the course of execution of this built-in |
- // function, it indicates either a bug, or a synthetic uncatchable |
- // exception in the shutdown path. In either case, it's OK to predict either |
- // way in DevTools. |
+namespace { |
+bool InternalPromiseHasUserDefinedRejectHandler(Isolate* isolate, |
+ Handle<JSPromise> promise); |
+ |
+bool PromiseHandlerCheck(Isolate* isolate, Handle<JSReceiver> handler, |
+ Handle<JSObject> deferred) { |
+ // Recurse to the forwarding Promise, if any. This may be due to |
+ // - await reaction forwarding to the throwaway Promise, which has |
+ // a dependency edge to the outer Promise. |
+ // - PromiseIdResolveHandler forwarding to the output of .then |
+ // - Promise.all/Promise.race forwarding to a throwaway Promise, which |
+ // has a dependency edge to the generated outer Promise. |
+ Handle<Symbol> key = isolate->factory()->promise_forwarding_handler_symbol(); |
+ Handle<Object> forwarding_handler = JSReceiver::GetDataProperty(handler, key); |
+ if (forwarding_handler->IsUndefined(isolate)) { |
+ return true; |
+ } |
+ |
+ // TODO(gsathya): Remove this once we get rid of deferred objects. |
+ Handle<String> promise_str = isolate->factory()->promise_string(); |
+ Handle<Object> deferred_promise_obj = |
+ JSObject::GetDataProperty(deferred, promise_str); |
+ if (!deferred_promise_obj->IsJSPromise()) { |
+ return true; |
+ } |
+ |
+ return InternalPromiseHasUserDefinedRejectHandler( |
+ isolate, Handle<JSPromise>::cast(deferred_promise_obj)); |
+ |
+ // Otherwise, this is a real reject handler for the Promise |
+ return true; |
+} |
+ |
+bool InternalPromiseHasUserDefinedRejectHandler(Isolate* isolate, |
+ Handle<JSPromise> promise) { |
+ // If this promise was marked as being handled by a catch block |
+ // in an async function, then it has a user-defined reject handler. |
+ if (promise->handled_hint()) return true; |
+ |
+ // If this Promise is subsumed by another Promise (a Promise resolved |
+ // with another Promise, or an intermediate, hidden, throwaway Promise |
+ // within async/await), then recurse on the outer Promise. |
+ // In this case, the dependency is one possible way that the Promise |
+ // could be resolved, so it does not subsume the other following cases. |
+ Handle<Symbol> key = isolate->factory()->promise_handled_by_symbol(); |
+ Handle<Object> outer_promise_obj = JSObject::GetDataProperty(promise, key); |
+ if (outer_promise_obj->IsJSPromise() && |
+ InternalPromiseHasUserDefinedRejectHandler( |
+ isolate, Handle<JSPromise>::cast(outer_promise_obj))) { |
+ return true; |
+ } |
+ |
+ Handle<Object> queue(promise->reject_reactions(), isolate); |
+ Handle<Object> deferred(promise->deferred(), isolate); |
+ |
+ if (queue->IsUndefined(isolate)) { |
+ return false; |
+ } |
+ |
+ if (queue->IsCallable()) { |
+ return PromiseHandlerCheck(isolate, Handle<JSReceiver>::cast(queue), |
+ Handle<JSObject>::cast(deferred)); |
+ } |
+ |
+ Handle<FixedArray> queue_arr = Handle<FixedArray>::cast(queue); |
+ Handle<FixedArray> deferred_arr = Handle<FixedArray>::cast(deferred); |
+ for (int i = 0; i < deferred_arr->length(); i++) { |
+ Handle<JSReceiver> queue_item(JSReceiver::cast(queue_arr->get(i))); |
+ Handle<JSObject> deferred_item(JSObject::cast(deferred_arr->get(i))); |
+ if (PromiseHandlerCheck(isolate, queue_item, deferred_item)) { |
+ return true; |
+ } |
+ } |
+ |
return false; |
} |
+} // namespace |
+ |
+bool Isolate::PromiseHasUserDefinedRejectHandler(Handle<Object> promise) { |
+ if (!promise->IsJSPromise()) return false; |
+ return InternalPromiseHasUserDefinedRejectHandler( |
+ this, Handle<JSPromise>::cast(promise)); |
+} |
+ |
Handle<Object> Isolate::GetPromiseOnStackOnThrow() { |
Handle<Object> undefined = factory()->undefined_value(); |
ThreadLocalTop* tltop = thread_local_top(); |
@@ -1842,7 +1910,7 @@ Handle<Object> Isolate::GetPromiseOnStackOnThrow() { |
continue; |
case HandlerTable::CAUGHT: |
case HandlerTable::DESUGARING: |
- if (retval->IsJSObject()) { |
+ if (retval->IsJSPromise()) { |
// Caught the result of an inner async/await invocation. |
// Mark the inner promise as caught in the "synchronous case" so |
// that Debug::OnException will see. In the synchronous case, |
@@ -1850,10 +1918,7 @@ Handle<Object> Isolate::GetPromiseOnStackOnThrow() { |
// await, the function which has this exception event has not yet |
// returned, so the generated Promise has not yet been marked |
// by AsyncFunctionAwaitCaught with promiseHandledHintSymbol. |
- Handle<Symbol> key = factory()->promise_handled_hint_symbol(); |
- JSObject::SetProperty(Handle<JSObject>::cast(retval), key, |
- factory()->true_value(), STRICT) |
- .Assert(); |
+ Handle<JSPromise>::cast(retval)->set_handled_hint(true); |
} |
return retval; |
case HandlerTable::PROMISE: |