Commit ed70c77f authored by Camillo Bruni's avatar Camillo Bruni Committed by Commit Bot

[isolate] Partially avoid recursion in rejection handler check

Isolate::PromiseHasUserDefinedRejectionHandler no longer descends
recursively the outer_promise chain but uses an std::stack to avoid
stack overflows with very long promise chains.

Change-Id: Icdf86a34d89b734adc7139357b2ba6b37a7882ad
Bug: chromium:1096139
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2316298Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69090}
parent 62cb792c
...@@ -1827,7 +1827,12 @@ void Debug::OnException(Handle<Object> exception, Handle<Object> promise, ...@@ -1827,7 +1827,12 @@ void Debug::OnException(Handle<Object> exception, Handle<Object> promise,
Just(ShouldThrow::kThrowOnError)) Just(ShouldThrow::kThrowOnError))
.Assert(); .Assert();
// Check whether the promise reject is considered an uncaught exception. // Check whether the promise reject is considered an uncaught exception.
uncaught = !isolate_->PromiseHasUserDefinedRejectHandler(jspromise); if (jspromise->IsJSPromise()) {
uncaught = !isolate_->PromiseHasUserDefinedRejectHandler(
Handle<JSPromise>::cast(jspromise));
} else {
uncaught = true;
}
} }
if (!debug_delegate_) return; if (!debug_delegate_) return;
......
...@@ -2450,12 +2450,8 @@ void Isolate::PopPromise() { ...@@ -2450,12 +2450,8 @@ void Isolate::PopPromise() {
} }
namespace { namespace {
bool InternalPromiseHasUserDefinedRejectHandler(Isolate* isolate, bool PromiseIsRejectHandler(Isolate* isolate, Handle<JSReceiver> handler) {
Handle<JSPromise> promise); // Recurse to the forwarding Promise (e.g. return false) due to
bool PromiseHandlerCheck(Isolate* isolate, Handle<JSReceiver> handler,
Handle<JSReceiver> deferred_promise) {
// Recurse to the forwarding Promise, if any. This may be due to
// - await reaction forwarding to the throwaway Promise, which has // - await reaction forwarding to the throwaway Promise, which has
// a dependency edge to the outer Promise. // a dependency edge to the outer Promise.
// - PromiseIdResolveHandler forwarding to the output of .then // - PromiseIdResolveHandler forwarding to the output of .then
...@@ -2464,75 +2460,61 @@ bool PromiseHandlerCheck(Isolate* isolate, Handle<JSReceiver> handler, ...@@ -2464,75 +2460,61 @@ bool PromiseHandlerCheck(Isolate* isolate, Handle<JSReceiver> handler,
// Otherwise, this is a real reject handler for the Promise. // Otherwise, this is a real reject handler for the Promise.
Handle<Symbol> key = isolate->factory()->promise_forwarding_handler_symbol(); Handle<Symbol> key = isolate->factory()->promise_forwarding_handler_symbol();
Handle<Object> forwarding_handler = JSReceiver::GetDataProperty(handler, key); Handle<Object> forwarding_handler = JSReceiver::GetDataProperty(handler, key);
if (forwarding_handler->IsUndefined(isolate)) { return forwarding_handler->IsUndefined(isolate);
return true;
}
if (!deferred_promise->IsJSPromise()) {
return true;
}
return InternalPromiseHasUserDefinedRejectHandler(
isolate, Handle<JSPromise>::cast(deferred_promise));
} }
bool InternalPromiseHasUserDefinedRejectHandler(Isolate* isolate, bool PromiseHasUserDefinedRejectHandlerInternal(Isolate* isolate,
Handle<JSPromise> promise) { Handle<JSPromise> promise) {
// If this promise was marked as being handled by a catch block Handle<Object> current(promise->reactions(), isolate);
// in an async function, then it has a user-defined reject handler. while (!current->IsSmi()) {
if (promise->handled_hint()) return true; Handle<PromiseReaction> reaction = Handle<PromiseReaction>::cast(current);
Handle<HeapObject> promise_or_capability(reaction->promise_or_capability(),
// If this Promise is subsumed by another Promise (a Promise resolved isolate);
// with another Promise, or an intermediate, hidden, throwaway Promise if (!promise_or_capability->IsUndefined(isolate)) {
// within async/await), then recurse on the outer Promise. if (!promise_or_capability->IsJSPromise()) {
// In this case, the dependency is one possible way that the Promise promise_or_capability = handle(
// could be resolved, so it does not subsume the other following cases. Handle<PromiseCapability>::cast(promise_or_capability)->promise(),
Handle<Symbol> key = isolate->factory()->promise_handled_by_symbol(); isolate);
Handle<Object> outer_promise_obj = JSObject::GetDataProperty(promise, key); }
if (outer_promise_obj->IsJSPromise() && Handle<JSPromise> promise =
InternalPromiseHasUserDefinedRejectHandler( Handle<JSPromise>::cast(promise_or_capability);
isolate, Handle<JSPromise>::cast(outer_promise_obj))) { if (!reaction->reject_handler().IsUndefined(isolate)) {
return true; Handle<JSReceiver> reject_handler(
} JSReceiver::cast(reaction->reject_handler()), isolate);
if (PromiseIsRejectHandler(isolate, reject_handler)) return true;
if (promise->status() == Promise::kPending) {
for (Handle<Object> current(promise->reactions(), isolate);
!current->IsSmi();) {
Handle<PromiseReaction> reaction = Handle<PromiseReaction>::cast(current);
Handle<HeapObject> promise_or_capability(
reaction->promise_or_capability(), isolate);
if (!promise_or_capability->IsUndefined(isolate)) {
Handle<JSPromise> promise = Handle<JSPromise>::cast(
promise_or_capability->IsJSPromise()
? promise_or_capability
: handle(Handle<PromiseCapability>::cast(promise_or_capability)
->promise(),
isolate));
if (reaction->reject_handler().IsUndefined(isolate)) {
if (InternalPromiseHasUserDefinedRejectHandler(isolate, promise)) {
return true;
}
} else {
Handle<JSReceiver> current_handler(
JSReceiver::cast(reaction->reject_handler()), isolate);
if (PromiseHandlerCheck(isolate, current_handler, promise)) {
return true;
}
}
} }
current = handle(reaction->next(), isolate); if (isolate->PromiseHasUserDefinedRejectHandler(promise)) return true;
} }
current = handle(reaction->next(), isolate);
} }
return false; return false;
} }
} // namespace } // namespace
bool Isolate::PromiseHasUserDefinedRejectHandler(Handle<Object> promise) { bool Isolate::PromiseHasUserDefinedRejectHandler(Handle<JSPromise> promise) {
if (!promise->IsJSPromise()) return false; Handle<Symbol> key = factory()->promise_handled_by_symbol();
return InternalPromiseHasUserDefinedRejectHandler( std::stack<Handle<JSPromise>> promises;
this, Handle<JSPromise>::cast(promise)); // First descend into the outermost promise and collect the stack of
// Promises for reverse processing.
while (true) {
// 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 (promise->status() == Promise::kPending) {
promises.push(promise);
}
Handle<Object> outer_promise_obj = JSObject::GetDataProperty(promise, key);
if (!outer_promise_obj->IsJSPromise()) break;
promise = Handle<JSPromise>::cast(outer_promise_obj);
}
while (!promises.empty()) {
promise = promises.top();
if (PromiseHasUserDefinedRejectHandlerInternal(this, promise)) return true;
promises.pop();
}
return false;
} }
Handle<Object> Isolate::GetPromiseOnStackOnThrow() { Handle<Object> Isolate::GetPromiseOnStackOnThrow() {
...@@ -2591,8 +2573,11 @@ Handle<Object> Isolate::GetPromiseOnStackOnThrow() { ...@@ -2591,8 +2573,11 @@ Handle<Object> Isolate::GetPromiseOnStackOnThrow() {
// assume that async function calls are awaited. // assume that async function calls are awaited.
if (!promise_on_stack) return retval; if (!promise_on_stack) return retval;
retval = promise_on_stack->promise(); retval = promise_on_stack->promise();
if (PromiseHasUserDefinedRejectHandler(retval)) { if (retval->IsJSPromise()) {
return retval; if (PromiseHasUserDefinedRejectHandler(
Handle<JSPromise>::cast(retval))) {
return retval;
}
} }
promise_on_stack = promise_on_stack->prev(); promise_on_stack = promise_on_stack->prev();
continue; continue;
......
...@@ -729,7 +729,7 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory { ...@@ -729,7 +729,7 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
Handle<Object> GetPromiseOnStackOnThrow(); Handle<Object> GetPromiseOnStackOnThrow();
// Heuristically guess whether a Promise is handled by user catch handler // Heuristically guess whether a Promise is handled by user catch handler
bool PromiseHasUserDefinedRejectHandler(Handle<Object> promise); bool PromiseHasUserDefinedRejectHandler(Handle<JSPromise> promise);
class ExceptionScope { class ExceptionScope {
public: public:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment