Commit e24b95dc authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[esnext] Implement "Editorial: refactor PerformPromiseThen to allow no capability".

This implements the editorial change in https://github.com/tc39/ecma262/pull/1146
which removes the need to allocate the throwaway promise in await (the throwaway
promise was not exposed to user JavaScript anyways, but this spec change allows
us to rely on this behavior). Now we still need the throwaway promise for proper
before and after events with both DevTools (which might change) and PromiseHooks.
So if either DevTools or PromiseHooks is on, we call into %AwaitPromisesInit,
which then allocates the throwaway promise and does all the other debugger/hooks
related setup.

This gives around 7% improvement on the doxbee-async-es2017-native and around 1-2%
on the parallel-async-es2017-native benchmarks.

Bug: v8:7253, v8:8285
Ref: tc39/ecma262#1146
Tbr: ulan@chromium.org
Change-Id: I972ba0538ec8c00808e95b183603025c7e55a6d3
Cq-Include-Trybots: luci.chromium.try:linux_chromium_headless_rel;luci.chromium.try:linux_chromium_rel_ng;master.tryserver.blink:linux_trusty_blink_rel
Reviewed-on: https://chromium-review.googlesource.com/c/1270798
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarMaya Lekova <mslekova@chromium.org>
Reviewed-by: 's avatarSathya Gunasekaran <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56506}
parent 5ac88053
...@@ -32,10 +32,8 @@ Node* AsyncBuiltinsAssembler::AwaitOld(Node* context, Node* generator, ...@@ -32,10 +32,8 @@ Node* AsyncBuiltinsAssembler::AwaitOld(Node* context, Node* generator,
static const int kWrappedPromiseOffset = static const int kWrappedPromiseOffset =
FixedArray::SizeFor(Context::MIN_CONTEXT_SLOTS); FixedArray::SizeFor(Context::MIN_CONTEXT_SLOTS);
static const int kThrowawayPromiseOffset =
kWrappedPromiseOffset + JSPromise::kSizeWithEmbedderFields;
static const int kResolveClosureOffset = static const int kResolveClosureOffset =
kThrowawayPromiseOffset + JSPromise::kSizeWithEmbedderFields; kWrappedPromiseOffset + JSPromise::kSizeWithEmbedderFields;
static const int kRejectClosureOffset = static const int kRejectClosureOffset =
kResolveClosureOffset + JSFunction::kSizeWithoutPrototype; kResolveClosureOffset + JSFunction::kSizeWithoutPrototype;
static const int kTotalSize = static const int kTotalSize =
...@@ -81,80 +79,38 @@ Node* AsyncBuiltinsAssembler::AwaitOld(Node* context, Node* generator, ...@@ -81,80 +79,38 @@ Node* AsyncBuiltinsAssembler::AwaitOld(Node* context, Node* generator,
PromiseInit(wrapped_value); PromiseInit(wrapped_value);
} }
Node* const throwaway = InnerAllocate(base, kThrowawayPromiseOffset); // Initialize resolve handler
{
// Initialize throwawayPromise
StoreMapNoWriteBarrier(throwaway, promise_map);
InitializeJSObjectFromMap(
throwaway, promise_map,
IntPtrConstant(JSPromise::kSizeWithEmbedderFields));
PromiseInit(throwaway);
}
Node* const on_resolve = InnerAllocate(base, kResolveClosureOffset); Node* const on_resolve = InnerAllocate(base, kResolveClosureOffset);
{ InitializeNativeClosure(closure_context, native_context, on_resolve,
// Initialize resolve handler on_resolve_context_index);
InitializeNativeClosure(closure_context, native_context, on_resolve,
on_resolve_context_index);
}
// Initialize reject handler
Node* const on_reject = InnerAllocate(base, kRejectClosureOffset); Node* const on_reject = InnerAllocate(base, kRejectClosureOffset);
{ InitializeNativeClosure(closure_context, native_context, on_reject,
// Initialize reject handler on_reject_context_index);
InitializeNativeClosure(closure_context, native_context, on_reject,
on_reject_context_index); VARIABLE(var_throwaway, MachineRepresentation::kTaggedPointer,
} UndefinedConstant());
{ // Deal with PromiseHooks and debug support in the runtime. This
// Add PromiseHooks if needed // also allocates the throwaway promise, which is only needed in
Label next(this); // case of PromiseHooks or debugging.
GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &next); Label if_debugging(this, Label::kDeferred), do_resolve_promise(this);
CallRuntime(Runtime::kAwaitPromisesInit, context, wrapped_value, GotoIf(IsDebugActive(), &if_debugging);
outer_promise, throwaway); Branch(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &if_debugging,
Goto(&next); &do_resolve_promise);
BIND(&next); BIND(&if_debugging);
} var_throwaway.Bind(CallRuntime(Runtime::kAwaitPromisesInit, context, value,
wrapped_value, outer_promise, on_reject,
is_predicted_as_caught));
Goto(&do_resolve_promise);
BIND(&do_resolve_promise);
// Perform ! Call(promiseCapability.[[Resolve]], undefined, « promise »). // Perform ! Call(promiseCapability.[[Resolve]], undefined, « promise »).
CallBuiltin(Builtins::kResolvePromise, context, wrapped_value, value); CallBuiltin(Builtins::kResolvePromise, context, wrapped_value, value);
// The Promise will be thrown away and not handled, but it shouldn't trigger
// unhandled reject events as its work is done
PromiseSetHasHandler(throwaway);
Label do_perform_promise_then(this);
GotoIfNot(IsDebugActive(), &do_perform_promise_then);
{
Label common(this);
GotoIf(TaggedIsSmi(value), &common);
GotoIfNot(HasInstanceType(value, JS_PROMISE_TYPE), &common);
{
// Mark the reject handler callback to be a forwarding edge, rather
// than a meaningful catch handler
Node* const key =
HeapConstant(factory()->promise_forwarding_handler_symbol());
SetPropertyStrict(CAST(context), CAST(on_reject), CAST(key),
TrueConstant());
GotoIf(IsFalse(is_predicted_as_caught), &common);
PromiseSetHandledHint(value);
}
Goto(&common);
BIND(&common);
// Mark the dependency to outer Promise in case the throwaway Promise is
// found on the Promise stack
CSA_SLOW_ASSERT(this, HasInstanceType(outer_promise, JS_PROMISE_TYPE));
Node* const key = HeapConstant(factory()->promise_handled_by_symbol());
SetPropertyStrict(CAST(context), CAST(throwaway), CAST(key),
CAST(outer_promise));
}
Goto(&do_perform_promise_then);
BIND(&do_perform_promise_then);
return CallBuiltin(Builtins::kPerformPromiseThen, context, wrapped_value, return CallBuiltin(Builtins::kPerformPromiseThen, context, wrapped_value,
on_resolve, on_reject, throwaway); on_resolve, on_reject, var_throwaway.value());
} }
Node* AsyncBuiltinsAssembler::AwaitOptimized( Node* AsyncBuiltinsAssembler::AwaitOptimized(
...@@ -167,10 +123,8 @@ Node* AsyncBuiltinsAssembler::AwaitOptimized( ...@@ -167,10 +123,8 @@ Node* AsyncBuiltinsAssembler::AwaitOptimized(
CSA_ASSERT(this, IsFunctionWithPrototypeSlotMap(LoadMap(promise_fun))); CSA_ASSERT(this, IsFunctionWithPrototypeSlotMap(LoadMap(promise_fun)));
CSA_ASSERT(this, IsConstructor(promise_fun)); CSA_ASSERT(this, IsConstructor(promise_fun));
static const int kThrowawayPromiseOffset =
FixedArray::SizeFor(Context::MIN_CONTEXT_SLOTS);
static const int kResolveClosureOffset = static const int kResolveClosureOffset =
kThrowawayPromiseOffset + JSPromise::kSizeWithEmbedderFields; FixedArray::SizeFor(Context::MIN_CONTEXT_SLOTS);
static const int kRejectClosureOffset = static const int kRejectClosureOffset =
kResolveClosureOffset + JSFunction::kSizeWithoutPrototype; kResolveClosureOffset + JSFunction::kSizeWithoutPrototype;
static const int kTotalSize = static const int kTotalSize =
...@@ -199,84 +153,35 @@ Node* AsyncBuiltinsAssembler::AwaitOptimized( ...@@ -199,84 +153,35 @@ Node* AsyncBuiltinsAssembler::AwaitOptimized(
closure_context, Context::NATIVE_CONTEXT_INDEX, native_context); closure_context, Context::NATIVE_CONTEXT_INDEX, native_context);
} }
Node* const promise_map = // Initialize resolve handler
LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
// Assert that the JSPromise map has an instance size is
// JSPromise::kSizeWithEmbedderFields.
CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(promise_map),
IntPtrConstant(JSPromise::kSizeWithEmbedderFields /
kPointerSize)));
Node* const throwaway = InnerAllocate(base, kThrowawayPromiseOffset);
{
// Initialize throwawayPromise
StoreMapNoWriteBarrier(throwaway, promise_map);
InitializeJSObjectFromMap(
throwaway, promise_map,
IntPtrConstant(JSPromise::kSizeWithEmbedderFields));
PromiseInit(throwaway);
}
Node* const on_resolve = InnerAllocate(base, kResolveClosureOffset); Node* const on_resolve = InnerAllocate(base, kResolveClosureOffset);
{ InitializeNativeClosure(closure_context, native_context, on_resolve,
// Initialize resolve handler on_resolve_context_index);
InitializeNativeClosure(closure_context, native_context, on_resolve,
on_resolve_context_index);
}
// Initialize reject handler
Node* const on_reject = InnerAllocate(base, kRejectClosureOffset); Node* const on_reject = InnerAllocate(base, kRejectClosureOffset);
{ InitializeNativeClosure(closure_context, native_context, on_reject,
// Initialize reject handler on_reject_context_index);
InitializeNativeClosure(closure_context, native_context, on_reject,
on_reject_context_index); VARIABLE(var_throwaway, MachineRepresentation::kTaggedPointer,
} UndefinedConstant());
{ // Deal with PromiseHooks and debug support in the runtime. This
// Add PromiseHooks if needed // also allocates the throwaway promise, which is only needed in
Label next(this); // case of PromiseHooks or debugging.
GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &next); Label if_debugging(this, Label::kDeferred), do_perform_promise_then(this);
CallRuntime(Runtime::kAwaitPromisesInit, context, promise, outer_promise, GotoIf(IsDebugActive(), &if_debugging);
throwaway); Branch(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &if_debugging,
Goto(&next); &do_perform_promise_then);
BIND(&next); BIND(&if_debugging);
} var_throwaway.Bind(CallRuntime(Runtime::kAwaitPromisesInit, context, value,
promise, outer_promise, on_reject,
// The Promise will be thrown away and not handled, but it shouldn't trigger is_predicted_as_caught));
// unhandled reject events as its work is done
PromiseSetHasHandler(throwaway);
Label do_perform_promise_then(this);
GotoIfNot(IsDebugActive(), &do_perform_promise_then);
{
Label common(this);
GotoIf(TaggedIsSmi(value), &common);
GotoIfNot(HasInstanceType(value, JS_PROMISE_TYPE), &common);
{
// Mark the reject handler callback to be a forwarding edge, rather
// than a meaningful catch handler
Node* const key =
HeapConstant(factory()->promise_forwarding_handler_symbol());
SetPropertyStrict(CAST(context), CAST(on_reject), CAST(key),
TrueConstant());
GotoIf(IsFalse(is_predicted_as_caught), &common);
PromiseSetHandledHint(value);
}
Goto(&common);
BIND(&common);
// Mark the dependency to outer Promise in case the throwaway Promise is
// found on the Promise stack
CSA_SLOW_ASSERT(this, HasInstanceType(outer_promise, JS_PROMISE_TYPE));
Node* const key = HeapConstant(factory()->promise_handled_by_symbol());
SetPropertyStrict(CAST(context), CAST(throwaway), CAST(key),
CAST(outer_promise));
}
Goto(&do_perform_promise_then); Goto(&do_perform_promise_then);
BIND(&do_perform_promise_then); BIND(&do_perform_promise_then);
return CallBuiltin(Builtins::kPerformPromiseThen, native_context, promise, return CallBuiltin(Builtins::kPerformPromiseThen, native_context, promise,
on_resolve, on_reject, throwaway); on_resolve, on_reject, var_throwaway.value());
} }
Node* AsyncBuiltinsAssembler::Await(Node* context, Node* generator, Node* value, Node* AsyncBuiltinsAssembler::Await(Node* context, Node* generator, Node* value,
......
...@@ -823,13 +823,15 @@ void InternalBuiltinsAssembler::RunPromiseHook( ...@@ -823,13 +823,15 @@ void InternalBuiltinsAssembler::RunPromiseHook(
BIND(&hook); BIND(&hook);
{ {
// Get to the underlying JSPromise instance. // Get to the underlying JSPromise instance.
Node* const promise = Select<HeapObject>( TNode<HeapObject> promise = Select<HeapObject>(
IsJSPromise(promise_or_capability), IsPromiseCapability(promise_or_capability),
[=] { return promise_or_capability; },
[=] { [=] {
return CAST(LoadObjectField(promise_or_capability, return CAST(LoadObjectField(promise_or_capability,
PromiseCapability::kPromiseOffset)); PromiseCapability::kPromiseOffset));
}); },
[=] { return promise_or_capability; });
GotoIf(IsUndefined(promise), &done_hook);
CallRuntime(id, context, promise); CallRuntime(id, context, promise);
Goto(&done_hook); Goto(&done_hook);
} }
......
...@@ -318,8 +318,11 @@ void PromiseBuiltinsAssembler::PerformPromiseThen( ...@@ -318,8 +318,11 @@ void PromiseBuiltinsAssembler::PerformPromiseThen(
Word32Or(IsCallable(on_fulfilled), IsUndefined(on_fulfilled))); Word32Or(IsCallable(on_fulfilled), IsUndefined(on_fulfilled)));
CSA_ASSERT(this, Word32Or(IsCallable(on_rejected), IsUndefined(on_rejected))); CSA_ASSERT(this, Word32Or(IsCallable(on_rejected), IsUndefined(on_rejected)));
CSA_ASSERT(this, TaggedIsNotSmi(result_promise_or_capability)); CSA_ASSERT(this, TaggedIsNotSmi(result_promise_or_capability));
CSA_ASSERT(this, Word32Or(IsJSPromise(result_promise_or_capability), CSA_ASSERT(
IsPromiseCapability(result_promise_or_capability))); this,
Word32Or(Word32Or(IsJSPromise(result_promise_or_capability),
IsPromiseCapability(result_promise_or_capability)),
IsUndefined(result_promise_or_capability)));
Label if_pending(this), if_notpending(this), done(this); Label if_pending(this), if_notpending(this), done(this);
Node* const status = PromiseStatus(promise); Node* const status = PromiseStatus(promise);
...@@ -390,7 +393,8 @@ TF_BUILTIN(PerformPromiseThen, PromiseBuiltinsAssembler) { ...@@ -390,7 +393,8 @@ TF_BUILTIN(PerformPromiseThen, PromiseBuiltinsAssembler) {
Node* const result_promise = Parameter(Descriptor::kResultPromise); Node* const result_promise = Parameter(Descriptor::kResultPromise);
CSA_ASSERT(this, TaggedIsNotSmi(result_promise)); CSA_ASSERT(this, TaggedIsNotSmi(result_promise));
CSA_ASSERT(this, IsJSPromise(result_promise)); CSA_ASSERT(
this, Word32Or(IsJSPromise(result_promise), IsUndefined(result_promise)));
PerformPromiseThen(context, promise, on_fulfilled, on_rejected, PerformPromiseThen(context, promise, on_fulfilled, on_rejected,
result_promise); result_promise);
...@@ -1154,11 +1158,14 @@ void PromiseBuiltinsAssembler::PromiseReactionJob(Node* context, Node* argument, ...@@ -1154,11 +1158,14 @@ void PromiseBuiltinsAssembler::PromiseReactionJob(Node* context, Node* argument,
CSA_ASSERT(this, TaggedIsNotSmi(handler)); CSA_ASSERT(this, TaggedIsNotSmi(handler));
CSA_ASSERT(this, Word32Or(IsUndefined(handler), IsCallable(handler))); CSA_ASSERT(this, Word32Or(IsUndefined(handler), IsCallable(handler)));
CSA_ASSERT(this, TaggedIsNotSmi(promise_or_capability)); CSA_ASSERT(this, TaggedIsNotSmi(promise_or_capability));
CSA_ASSERT(this, Word32Or(IsJSPromise(promise_or_capability), CSA_ASSERT(this,
IsPromiseCapability(promise_or_capability))); Word32Or(Word32Or(IsJSPromise(promise_or_capability),
IsPromiseCapability(promise_or_capability)),
IsUndefined(promise_or_capability)));
VARIABLE(var_handler_result, MachineRepresentation::kTagged, argument); VARIABLE(var_handler_result, MachineRepresentation::kTagged, argument);
Label if_handler_callable(this), if_fulfill(this), if_reject(this); Label if_handler_callable(this), if_fulfill(this), if_reject(this),
if_internal(this);
Branch(IsUndefined(handler), Branch(IsUndefined(handler),
type == PromiseReaction::kFulfill ? &if_fulfill : &if_reject, type == PromiseReaction::kFulfill ? &if_fulfill : &if_reject,
&if_handler_callable); &if_handler_callable);
...@@ -1170,7 +1177,16 @@ void PromiseBuiltinsAssembler::PromiseReactionJob(Node* context, Node* argument, ...@@ -1170,7 +1177,16 @@ void PromiseBuiltinsAssembler::PromiseReactionJob(Node* context, Node* argument,
context, handler, UndefinedConstant(), argument); context, handler, UndefinedConstant(), argument);
GotoIfException(result, &if_reject, &var_handler_result); GotoIfException(result, &if_reject, &var_handler_result);
var_handler_result.Bind(result); var_handler_result.Bind(result);
Goto(&if_fulfill); Branch(IsUndefined(promise_or_capability), &if_internal, &if_fulfill);
}
BIND(&if_internal);
{
// There's no [[Capability]] for this promise reaction job, which
// means that this is a specification-internal operation (aka await)
// where the result does not matter (see the specification change in
// https://github.com/tc39/ecma262/pull/1146 for details).
Return(UndefinedConstant());
} }
BIND(&if_fulfill); BIND(&if_fulfill);
......
...@@ -911,6 +911,8 @@ class V8_EXPORT_PRIVATE Factory { ...@@ -911,6 +911,8 @@ class V8_EXPORT_PRIVATE Factory {
// Converts the given ToPrimitive hint to it's string representation. // Converts the given ToPrimitive hint to it's string representation.
Handle<String> ToPrimitiveHintString(ToPrimitiveHint hint); Handle<String> ToPrimitiveHintString(ToPrimitiveHint hint);
Handle<JSPromise> NewJSPromiseWithoutHook(
PretenureFlag pretenure = NOT_TENURED);
Handle<JSPromise> NewJSPromise(PretenureFlag pretenure = NOT_TENURED); Handle<JSPromise> NewJSPromise(PretenureFlag pretenure = NOT_TENURED);
Handle<CallHandlerInfo> NewCallHandlerInfo(bool has_no_side_effect = false); Handle<CallHandlerInfo> NewCallHandlerInfo(bool has_no_side_effect = false);
...@@ -978,9 +980,6 @@ class V8_EXPORT_PRIVATE Factory { ...@@ -978,9 +980,6 @@ class V8_EXPORT_PRIVATE Factory {
Handle<JSArray> NewJSArray(ElementsKind elements_kind, Handle<JSArray> NewJSArray(ElementsKind elements_kind,
PretenureFlag pretenure = NOT_TENURED); PretenureFlag pretenure = NOT_TENURED);
Handle<JSPromise> NewJSPromiseWithoutHook(
PretenureFlag pretenure = NOT_TENURED);
Handle<SharedFunctionInfo> NewSharedFunctionInfo( Handle<SharedFunctionInfo> NewSharedFunctionInfo(
MaybeHandle<String> name, MaybeHandle<HeapObject> maybe_function_data, MaybeHandle<String> name, MaybeHandle<HeapObject> maybe_function_data,
int maybe_builtin_index, FunctionKind kind = kNormalFunction); int maybe_builtin_index, FunctionKind kind = kNormalFunction);
......
...@@ -682,11 +682,14 @@ void CaptureAsyncStackTrace(Isolate* isolate, Handle<JSPromise> promise, ...@@ -682,11 +682,14 @@ void CaptureAsyncStackTrace(Isolate* isolate, Handle<JSPromise> promise,
reaction->promise_or_capability(), isolate); reaction->promise_or_capability(), isolate);
if (promise_or_capability->IsJSPromise()) { if (promise_or_capability->IsJSPromise()) {
promise = Handle<JSPromise>::cast(promise_or_capability); promise = Handle<JSPromise>::cast(promise_or_capability);
} else { } else if (promise_or_capability->IsPromiseCapability()) {
Handle<PromiseCapability> capability = Handle<PromiseCapability> capability =
Handle<PromiseCapability>::cast(promise_or_capability); Handle<PromiseCapability>::cast(promise_or_capability);
if (!capability->promise()->IsJSPromise()) return; if (!capability->promise()->IsJSPromise()) return;
promise = handle(JSPromise::cast(capability->promise()), isolate); promise = handle(JSPromise::cast(capability->promise()), isolate);
} else {
// Otherwise the {promise_or_capability} must be undefined here.
CHECK(promise_or_capability->IsUndefined(isolate));
} }
} }
} }
...@@ -2273,21 +2276,23 @@ bool InternalPromiseHasUserDefinedRejectHandler(Isolate* isolate, ...@@ -2273,21 +2276,23 @@ bool InternalPromiseHasUserDefinedRejectHandler(Isolate* isolate,
Handle<PromiseReaction> reaction = Handle<PromiseReaction>::cast(current); Handle<PromiseReaction> reaction = Handle<PromiseReaction>::cast(current);
Handle<HeapObject> promise_or_capability( Handle<HeapObject> promise_or_capability(
reaction->promise_or_capability(), isolate); reaction->promise_or_capability(), isolate);
Handle<JSPromise> promise = Handle<JSPromise>::cast( if (!promise_or_capability->IsUndefined(isolate)) {
promise_or_capability->IsJSPromise() Handle<JSPromise> promise = Handle<JSPromise>::cast(
? promise_or_capability promise_or_capability->IsJSPromise()
: handle(Handle<PromiseCapability>::cast(promise_or_capability) ? promise_or_capability
->promise(), : handle(Handle<PromiseCapability>::cast(promise_or_capability)
isolate)); ->promise(),
if (reaction->reject_handler()->IsUndefined(isolate)) { isolate));
if (InternalPromiseHasUserDefinedRejectHandler(isolate, promise)) { if (reaction->reject_handler()->IsUndefined(isolate)) {
return true; if (InternalPromiseHasUserDefinedRejectHandler(isolate, promise)) {
} return true;
} else { }
Handle<JSReceiver> current_handler( } else {
JSReceiver::cast(reaction->reject_handler()), isolate); Handle<JSReceiver> current_handler(
if (PromiseHandlerCheck(isolate, current_handler, promise)) { JSReceiver::cast(reaction->reject_handler()), isolate);
return true; if (PromiseHandlerCheck(isolate, current_handler, promise)) {
return true;
}
} }
} }
current = handle(reaction->next(), isolate); current = handle(reaction->next(), isolate);
......
...@@ -1337,7 +1337,8 @@ void PromiseReactionJobTask::PromiseReactionJobTaskVerify(Isolate* isolate) { ...@@ -1337,7 +1337,8 @@ void PromiseReactionJobTask::PromiseReactionJobTaskVerify(Isolate* isolate) {
CHECK(handler()->IsUndefined(isolate) || handler()->IsCallable()); CHECK(handler()->IsUndefined(isolate) || handler()->IsCallable());
VerifyHeapPointer(isolate, promise_or_capability()); VerifyHeapPointer(isolate, promise_or_capability());
CHECK(promise_or_capability()->IsJSPromise() || CHECK(promise_or_capability()->IsJSPromise() ||
promise_or_capability()->IsPromiseCapability()); promise_or_capability()->IsPromiseCapability() ||
promise_or_capability()->IsUndefined(isolate));
} }
void MicrotaskQueue::MicrotaskQueueVerify(Isolate* isolate) { void MicrotaskQueue::MicrotaskQueueVerify(Isolate* isolate) {
...@@ -1396,7 +1397,8 @@ void PromiseReaction::PromiseReactionVerify(Isolate* isolate) { ...@@ -1396,7 +1397,8 @@ void PromiseReaction::PromiseReactionVerify(Isolate* isolate) {
fulfill_handler()->IsCallable()); fulfill_handler()->IsCallable());
VerifyHeapPointer(isolate, promise_or_capability()); VerifyHeapPointer(isolate, promise_or_capability());
CHECK(promise_or_capability()->IsJSPromise() || CHECK(promise_or_capability()->IsJSPromise() ||
promise_or_capability()->IsPromiseCapability()); promise_or_capability()->IsPromiseCapability() ||
promise_or_capability()->IsUndefined(isolate));
} }
void JSPromise::JSPromiseVerify(Isolate* isolate) { void JSPromise::JSPromiseVerify(Isolate* isolate) {
......
...@@ -29,7 +29,8 @@ class PromiseReactionJobTask : public Microtask { ...@@ -29,7 +29,8 @@ class PromiseReactionJobTask : public Microtask {
DECL_ACCESSORS(argument, Object) DECL_ACCESSORS(argument, Object)
DECL_ACCESSORS(context, Context) DECL_ACCESSORS(context, Context)
DECL_ACCESSORS(handler, HeapObject) DECL_ACCESSORS(handler, HeapObject)
// [promise_or_capability]: Either a JSPromise or a PromiseCapability. // [promise_or_capability]: Either a JSPromise (in case of native promises),
// a PromiseCapability (general case), or undefined (in case of await).
DECL_ACCESSORS(promise_or_capability, HeapObject) DECL_ACCESSORS(promise_or_capability, HeapObject)
static const int kArgumentOffset = Microtask::kHeaderSize; static const int kArgumentOffset = Microtask::kHeaderSize;
...@@ -124,10 +125,8 @@ class PromiseCapability : public Struct { ...@@ -124,10 +125,8 @@ class PromiseCapability : public Struct {
// //
// The PromiseReaction::promise_or_capability field can either hold a JSPromise // The PromiseReaction::promise_or_capability field can either hold a JSPromise
// instance (in the fast case of a native promise) or a PromiseCapability in // instance (in the fast case of a native promise) or a PromiseCapability in
// case of a Promise subclass. // case of a Promise subclass. In case of await it can also be undefined if
// // PromiseHooks are disabled (see https://github.com/tc39/ecma262/pull/1146).
// We need to keep the context in the PromiseReaction so that we can run
// the default handlers (in case they are undefined) in the proper context.
// //
// The PromiseReaction objects form a singly-linked list, terminated by // The PromiseReaction objects form a singly-linked list, terminated by
// Smi 0. On the JSPromise instance they are linked in reverse order, // Smi 0. On the JSPromise instance they are linked in reverse order,
...@@ -140,6 +139,8 @@ class PromiseReaction : public Struct { ...@@ -140,6 +139,8 @@ class PromiseReaction : public Struct {
DECL_ACCESSORS(next, Object) DECL_ACCESSORS(next, Object)
DECL_ACCESSORS(reject_handler, HeapObject) DECL_ACCESSORS(reject_handler, HeapObject)
DECL_ACCESSORS(fulfill_handler, HeapObject) DECL_ACCESSORS(fulfill_handler, HeapObject)
// [promise_or_capability]: Either a JSPromise (in case of native promises),
// a PromiseCapability (general case), or undefined (in case of await).
DECL_ACCESSORS(promise_or_capability, HeapObject) DECL_ACCESSORS(promise_or_capability, HeapObject)
static const int kNextOffset = Struct::kHeaderSize; static const int kNextOffset = Struct::kHeaderSize;
......
...@@ -132,20 +132,50 @@ RUNTIME_FUNCTION(Runtime_PromiseHookInit) { ...@@ -132,20 +132,50 @@ RUNTIME_FUNCTION(Runtime_PromiseHookInit) {
} }
RUNTIME_FUNCTION(Runtime_AwaitPromisesInit) { RUNTIME_FUNCTION(Runtime_AwaitPromisesInit) {
DCHECK_EQ(3, args.length()); DCHECK_EQ(5, args.length());
HandleScope scope(isolate); HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(JSPromise, wrapped_value, 0); CONVERT_ARG_HANDLE_CHECKED(Object, value, 0);
CONVERT_ARG_HANDLE_CHECKED(JSPromise, outer_promise, 1); CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 1);
CONVERT_ARG_HANDLE_CHECKED(JSPromise, throwaway, 2); CONVERT_ARG_HANDLE_CHECKED(JSPromise, outer_promise, 2);
isolate->RunPromiseHook(PromiseHookType::kInit, wrapped_value, outer_promise); CONVERT_ARG_HANDLE_CHECKED(JSFunction, reject_handler, 3);
isolate->RunPromiseHook(PromiseHookType::kInit, throwaway, wrapped_value); CONVERT_BOOLEAN_ARG_CHECKED(is_predicted_as_caught, 4);
// Allocate the throwaway promise and fire the appropriate PromiseHooks.
Handle<JSPromise> throwaway = isolate->factory()->NewJSPromiseWithoutHook();
isolate->RunPromiseHook(PromiseHookType::kInit, promise, outer_promise);
isolate->RunPromiseHook(PromiseHookType::kInit, throwaway, promise);
// On inspector side we capture async stack trace and store it by // On inspector side we capture async stack trace and store it by
// outer_promise->async_task_id when async function is suspended first time. // outer_promise->async_task_id when async function is suspended first time.
// To use captured stack trace later throwaway promise should have the same // To use captured stack trace later throwaway promise should have the same
// async_task_id as outer_promise since we generate WillHandle and DidHandle // async_task_id as outer_promise since we generate WillHandle and DidHandle
// events using throwaway promise. // events using throwaway promise.
throwaway->set_async_task_id(outer_promise->async_task_id()); throwaway->set_async_task_id(outer_promise->async_task_id());
return ReadOnlyRoots(isolate).undefined_value();
// The Promise will be thrown away and not handled, but it
// shouldn't trigger unhandled reject events as its work is done
throwaway->set_has_handler(true);
// Enable proper debug support for promises.
if (isolate->debug()->is_active()) {
if (value->IsJSPromise()) {
Object::SetProperty(
isolate, reject_handler,
isolate->factory()->promise_forwarding_handler_symbol(),
isolate->factory()->true_value(), LanguageMode::kStrict)
.Check();
Handle<JSPromise>::cast(value)->set_handled_hint(is_predicted_as_caught);
}
// Mark the dependency to {outer_promise} in case the {throwaway}
// Promise is found on the Promise stack
Object::SetProperty(isolate, throwaway,
isolate->factory()->promise_handled_by_symbol(),
outer_promise, LanguageMode::kStrict)
.Check();
}
return *throwaway;
} }
RUNTIME_FUNCTION(Runtime_PromiseHookBefore) { RUNTIME_FUNCTION(Runtime_PromiseHookBefore) {
......
...@@ -348,7 +348,7 @@ namespace internal { ...@@ -348,7 +348,7 @@ namespace internal {
F(PromiseHookAfter, 1, 1) \ F(PromiseHookAfter, 1, 1) \
F(PromiseHookBefore, 1, 1) \ F(PromiseHookBefore, 1, 1) \
F(PromiseHookInit, 2, 1) \ F(PromiseHookInit, 2, 1) \
F(AwaitPromisesInit, 3, 1) \ F(AwaitPromisesInit, 5, 1) \
F(PromiseMarkAsHandled, 1, 1) \ F(PromiseMarkAsHandled, 1, 1) \
F(PromiseRejectEventFromStack, 2, 1) \ F(PromiseRejectEventFromStack, 2, 1) \
F(PromiseResult, 1, 1) \ F(PromiseResult, 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