Commit 0f5c69c5 authored by gsathya's avatar gsathya Committed by Commit bot

[promises] Move PromiseHasUserDefinedRejectHandler to c++

BUG=v8:5343

Review-Url: https://codereview.chromium.org/2604483002
Cr-Commit-Position: refs/heads/master@{#41947}
parent 1344e3a9
......@@ -105,8 +105,6 @@ enum ContextLookupFlags {
V(PROMISE_CATCH_INDEX, JSFunction, promise_catch) \
V(PROMISE_CREATE_INDEX, JSFunction, promise_create) \
V(PROMISE_FUNCTION_INDEX, JSFunction, promise_function) \
V(PROMISE_HAS_USER_DEFINED_REJECT_HANDLER_INDEX, JSFunction, \
promise_has_user_defined_reject_handler) \
V(PROMISE_DEBUG_GET_INFO_INDEX, JSFunction, promise_debug_get_info) \
V(PROMISE_REJECT_INDEX, JSFunction, promise_reject) \
V(PROMISE_INTERNAL_REJECT_INDEX, JSFunction, promise_internal_reject) \
......
......@@ -218,7 +218,6 @@
V(promise_debug_marker_symbol) \
V(promise_forwarding_handler_symbol) \
V(promise_handled_by_symbol) \
V(promise_handled_hint_symbol) \
V(sealed_symbol) \
V(stack_trace_symbol) \
V(strict_function_transition_symbol) \
......
......@@ -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:
......
......@@ -32,8 +32,6 @@ var promiseHandledBySymbol =
utils.ImportNow("promise_handled_by_symbol");
var promiseForwardingHandlerSymbol =
utils.ImportNow("promise_forwarding_handler_symbol");
var promiseHandledHintSymbol =
utils.ImportNow("promise_handled_hint_symbol");
// -------------------------------------------------------------------
......@@ -114,7 +112,7 @@ function AsyncFunctionAwaitUncaught(generator, awaited, outerPromise) {
// prediction indicates that there is a locally surrounding catch block
function AsyncFunctionAwaitCaught(generator, awaited, outerPromise) {
if (DEBUG_IS_ACTIVE && %is_promise(awaited)) {
SET_PRIVATE(awaited, promiseHandledHintSymbol, true);
%PromiseMarkHandledHint(awaited);
}
AsyncFunctionAwait(generator, awaited, outerPromise);
}
......
......@@ -18,8 +18,6 @@ var promiseHandledBySymbol =
utils.ImportNow("promise_handled_by_symbol");
var promiseForwardingHandlerSymbol =
utils.ImportNow("promise_forwarding_handler_symbol");
var promiseHandledHintSymbol =
utils.ImportNow("promise_handled_hint_symbol");
var ObjectHasOwnProperty; // Used by HAS_PRIVATE.
var GlobalPromise = global.Promise;
......@@ -263,68 +261,6 @@ function PromiseRace(iterable) {
return deferred.promise;
}
// Utility for debugger
function PromiseHasUserDefinedRejectHandlerCheck(handler, 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.
if (GET_PRIVATE(handler, promiseForwardingHandlerSymbol)) {
return PromiseHasUserDefinedRejectHandlerRecursive(deferred.promise);
}
// Otherwise, this is a real reject handler for the Promise
return true;
}
function PromiseHasUserDefinedRejectHandlerRecursive(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 (GET_PRIVATE(promise, promiseHandledHintSymbol)) 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.
var outerPromise = GET_PRIVATE(promise, promiseHandledBySymbol);
if (outerPromise &&
PromiseHasUserDefinedRejectHandlerRecursive(outerPromise)) {
return true;
}
if (!%is_promise(promise)) return false;
var queue = %PromiseRejectReactions(promise);
var deferred = %PromiseDeferred(promise);
if (IS_UNDEFINED(queue)) return false;
if (!IS_ARRAY(queue)) {
return PromiseHasUserDefinedRejectHandlerCheck(queue, deferred);
}
for (var i = 0; i < queue.length; i++) {
if (PromiseHasUserDefinedRejectHandlerCheck(queue[i], deferred[i])) {
return true;
}
}
return false;
}
// Return whether the promise will be handled by a user-defined reject
// handler somewhere down the promise chain. For this, we do a depth-first
// search for a reject handler that's not the default PromiseIdRejectHandler.
// This function also traverses dependencies of one Promise on another,
// set up through async/await and Promises resolved with Promises.
function PromiseHasUserDefinedRejectHandler() {
return PromiseHasUserDefinedRejectHandlerRecursive(this);
};
function MarkPromiseAsHandled(promise) {
%PromiseMarkAsHandled(promise);
}
......@@ -341,7 +277,6 @@ utils.InstallFunctions(GlobalPromise, DONT_ENUM, [
%InstallToContext([
"promise_create", PromiseCreate,
"promise_has_user_defined_reject_handler", PromiseHasUserDefinedRejectHandler,
"promise_reject", DoRejectPromise,
// TODO(gsathya): Remove this once we update the promise builtin.
"promise_internal_reject", RejectPromise,
......
......@@ -7096,6 +7096,7 @@ ACCESSORS(JSPromise, fulfill_reactions, Object, kFulfillReactionsOffset)
ACCESSORS(JSPromise, reject_reactions, Object, kRejectReactionsOffset)
SMI_ACCESSORS(JSPromise, flags, kFlagsOffset)
BOOL_ACCESSORS(JSPromise, flags, has_handler, kHasHandlerBit)
BOOL_ACCESSORS(JSPromise, flags, handled_hint, kHandledHintBit)
ACCESSORS(JSRegExp, data, Object, kDataOffset)
ACCESSORS(JSRegExp, flags, Object, kFlagsOffset)
......
......@@ -8655,6 +8655,10 @@ class JSPromise : public JSObject {
// [has_handler]: Whether this promise has a reject handler or not.
DECL_BOOLEAN_ACCESSORS(has_handler)
// [handled_hint]: Whether this promise will be handled by a catch
// block in an async function.
DECL_BOOLEAN_ACCESSORS(handled_hint)
static const char* Status(int status);
DECLARE_CAST(JSPromise)
......@@ -8675,6 +8679,7 @@ class JSPromise : public JSObject {
// Flags layout.
static const int kHasHandlerBit = 0;
static const int kHandledHintBit = 1;
};
// Regular expressions
......
......@@ -279,7 +279,7 @@ RUNTIME_FUNCTION(Runtime_PromiseRejectReactions) {
}
RUNTIME_FUNCTION(Runtime_PromiseMarkAsHandled) {
HandleScope scope(isolate);
SealHandleScope shs(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
......@@ -287,6 +287,15 @@ RUNTIME_FUNCTION(Runtime_PromiseMarkAsHandled) {
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_PromiseMarkHandledHint) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
promise->set_handled_hint(true);
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_PromiseHookInit) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
......
......@@ -313,6 +313,7 @@ namespace internal {
F(PromiseHookBefore, 1, 1) \
F(PromiseHookAfter, 1, 1) \
F(PromiseMarkAsHandled, 1, 1) \
F(PromiseMarkHandledHint, 1, 1) \
F(PromiseRejectEventFromStack, 2, 1) \
F(PromiseRejectReactions, 1, 1) \
F(PromiseRevokeReject, 1, 1) \
......
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