From 192c55839e9f1541fef90946c212beda2c50c8e6 Mon Sep 17 00:00:00 2001 From: Joshua Litt Date: Thu, 12 Dec 2019 10:49:37 -0800 Subject: [PATCH] [promises] Port PromiseReactionJob to torque. Bug: v8:9838 Change-Id: I770133cdf719efeee8de9415bda0586d0f5ac8d9 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1954329 Reviewed-by: Maya Lekova Commit-Queue: Joshua Litt Cr-Commit-Position: refs/heads/master@{#65446} --- BUILD.gn | 1 + src/builtins/builtins-definitions.h | 3 - src/builtins/builtins-promise-gen.cc | 139 --------------------------- src/builtins/builtins-promise-gen.h | 4 - src/builtins/promise-reaction-job.tq | 131 +++++++++++++++++++++++++ 5 files changed, 132 insertions(+), 146 deletions(-) create mode 100644 src/builtins/promise-reaction-job.tq diff --git a/BUILD.gn b/BUILD.gn index 8b420300af..7f02d76b72 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -973,6 +973,7 @@ torque_files = [ "src/builtins/object.tq", "src/builtins/promise-abstract-operations.tq", "src/builtins/promise-constructor.tq", + "src/builtins/promise-reaction-job.tq", "src/builtins/promise-resolve.tq", "src/builtins/promise-then.tq", "src/builtins/promise-jobs.tq", diff --git a/src/builtins/builtins-definitions.h b/src/builtins/builtins-definitions.h index 17539c01ab..2464eb0cb9 100644 --- a/src/builtins/builtins-definitions.h +++ b/src/builtins/builtins-definitions.h @@ -720,9 +720,6 @@ namespace internal { CPP(IsPromise) \ /* ES #sec-promise.prototype.catch */ \ TFJ(PromisePrototypeCatch, 1, kReceiver, kOnRejected) \ - /* ES #sec-promisereactionjob */ \ - TFS(PromiseRejectReactionJob, kReason, kHandler, kPromiseOrCapability) \ - TFS(PromiseFulfillReactionJob, kValue, kHandler, kPromiseOrCapability) \ /* ES #sec-promise.reject */ \ TFJ(PromiseReject, 1, kReceiver, kReason) \ TFJ(PromisePrototypeFinally, 1, kReceiver, kOnFinally) \ diff --git a/src/builtins/builtins-promise-gen.cc b/src/builtins/builtins-promise-gen.cc index 97c050f9c8..2eb53637d8 100644 --- a/src/builtins/builtins-promise-gen.cc +++ b/src/builtins/builtins-promise-gen.cc @@ -540,145 +540,6 @@ TF_BUILTIN(PromisePrototypeCatch, PromiseBuiltinsAssembler) { Return(InvokeThen(native_context, receiver, on_fulfilled, on_rejected)); } -// ES #sec-promisereactionjob -void PromiseBuiltinsAssembler::PromiseReactionJob(Node* context, Node* argument, - Node* handler, - Node* promise_or_capability, - PromiseReaction::Type type) { - CSA_ASSERT(this, TaggedIsNotSmi(handler)); - CSA_ASSERT(this, Word32Or(IsUndefined(handler), IsCallable(handler))); - CSA_ASSERT(this, TaggedIsNotSmi(promise_or_capability)); - CSA_ASSERT(this, - Word32Or(Word32Or(IsJSPromise(promise_or_capability), - IsPromiseCapability(promise_or_capability)), - IsUndefined(promise_or_capability))); - - VARIABLE(var_handler_result, MachineRepresentation::kTagged, argument); - Label if_handler_callable(this), if_fulfill(this), if_reject(this), - if_internal(this); - Branch(IsUndefined(handler), - type == PromiseReaction::kFulfill ? &if_fulfill : &if_reject, - &if_handler_callable); - - BIND(&if_handler_callable); - { - Node* const result = CallJS( - CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined), - context, handler, UndefinedConstant(), argument); - GotoIfException(result, &if_reject, &var_handler_result); - var_handler_result.Bind(result); - 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); - { - Label if_promise(this), if_promise_capability(this, Label::kDeferred); - Node* const value = var_handler_result.value(); - Branch(IsPromiseCapability(promise_or_capability), &if_promise_capability, - &if_promise); - - BIND(&if_promise); - { - // For fast native promises we can skip the indirection - // via the promiseCapability.[[Resolve]] function and - // run the resolve logic directly from here. - TailCallBuiltin(Builtins::kResolvePromise, context, promise_or_capability, - value); - } - - BIND(&if_promise_capability); - { - // In the general case we need to call the (user provided) - // promiseCapability.[[Resolve]] function. - const TNode resolve = LoadObjectField( - promise_or_capability, PromiseCapability::kResolveOffset); - const TNode result = CallJS( - CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined), - context, resolve, UndefinedConstant(), value); - GotoIfException(result, &if_reject, &var_handler_result); - Return(result); - } - } - - BIND(&if_reject); - if (type == PromiseReaction::kReject) { - Label if_promise(this), if_promise_capability(this, Label::kDeferred); - Node* const reason = var_handler_result.value(); - Branch(IsPromiseCapability(promise_or_capability), &if_promise_capability, - &if_promise); - - BIND(&if_promise); - { - // For fast native promises we can skip the indirection - // via the promiseCapability.[[Reject]] function and - // run the resolve logic directly from here. - TailCallBuiltin(Builtins::kRejectPromise, context, promise_or_capability, - reason, FalseConstant()); - } - - BIND(&if_promise_capability); - { - // In the general case we need to call the (user provided) - // promiseCapability.[[Reject]] function. - Label if_exception(this, Label::kDeferred); - VARIABLE(var_exception, MachineRepresentation::kTagged, - TheHoleConstant()); - const TNode reject = LoadObjectField( - promise_or_capability, PromiseCapability::kRejectOffset); - const TNode result = CallJS( - CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined), - context, reject, UndefinedConstant(), reason); - GotoIfException(result, &if_exception, &var_exception); - Return(result); - - // Swallow the exception here. - BIND(&if_exception); - TailCallRuntime(Runtime::kReportMessage, context, var_exception.value()); - } - } else { - // We have to call out to the dedicated PromiseRejectReactionJob builtin - // here, instead of just doing the work inline, as otherwise the catch - // predictions in the debugger will be wrong, which just walks the stack - // and checks for certain builtins. - TailCallBuiltin(Builtins::kPromiseRejectReactionJob, context, - var_handler_result.value(), UndefinedConstant(), - promise_or_capability); - } -} - -// ES #sec-promisereactionjob -TF_BUILTIN(PromiseFulfillReactionJob, PromiseBuiltinsAssembler) { - Node* const context = Parameter(Descriptor::kContext); - Node* const value = Parameter(Descriptor::kValue); - Node* const handler = Parameter(Descriptor::kHandler); - Node* const promise_or_capability = - Parameter(Descriptor::kPromiseOrCapability); - - PromiseReactionJob(context, value, handler, promise_or_capability, - PromiseReaction::kFulfill); -} - -// ES #sec-promisereactionjob -TF_BUILTIN(PromiseRejectReactionJob, PromiseBuiltinsAssembler) { - Node* const context = Parameter(Descriptor::kContext); - Node* const reason = Parameter(Descriptor::kReason); - Node* const handler = Parameter(Descriptor::kHandler); - Node* const promise_or_capability = - Parameter(Descriptor::kPromiseOrCapability); - - PromiseReactionJob(context, reason, handler, promise_or_capability, - PromiseReaction::kReject); -} - // ES6 #sec-getcapabilitiesexecutor-functions TF_BUILTIN(PromiseGetCapabilitiesExecutor, PromiseBuiltinsAssembler) { Node* const resolve = Parameter(Descriptor::kResolve); diff --git a/src/builtins/builtins-promise-gen.h b/src/builtins/builtins-promise-gen.h index 445ce9532f..2ff3862411 100644 --- a/src/builtins/builtins-promise-gen.h +++ b/src/builtins/builtins-promise-gen.h @@ -141,10 +141,6 @@ class V8_EXPORT_PRIVATE PromiseBuiltinsAssembler : public CodeStubAssembler { TNode PromiseStatus(Node* promise); - void PromiseReactionJob(Node* context, Node* argument, Node* handler, - Node* promise_or_capability, - PromiseReaction::Type type); - TNode IsPromiseStatus(TNode actual, v8::Promise::PromiseState expected); TNode AllocateJSPromise(TNode context); diff --git a/src/builtins/promise-reaction-job.tq b/src/builtins/promise-reaction-job.tq new file mode 100644 index 0000000000..1db33f4a7a --- /dev/null +++ b/src/builtins/promise-reaction-job.tq @@ -0,0 +1,131 @@ +// Copyright 2019 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include 'src/builtins/builtins-promise-gen.h' + +namespace runtime { + extern transitioning runtime + ReportMessage(implicit context: Context)(JSAny): JSAny; +} + +namespace promise { + + transitioning + macro RejectPromiseReactionJob( + context: Context, + promiseOrCapability: JSPromise|PromiseCapability|Undefined, reason: JSAny, + reactionType: constexpr PromiseReactionType): JSAny { + if constexpr (reactionType == kPromiseReactionReject) { + if (IsJSPromise(promiseOrCapability)) { + // For fast native promises we can skip the indirection via the + // promiseCapability.[[Reject]] function and run the resolve logic + // directly from here. + return RejectPromise( + UnsafeCast(promiseOrCapability), reason, False); + } else + deferred { + assert(IsPromiseCapability(promiseOrCapability)); + // In the general case we need to call the (user provided) + // promiseCapability.[[Reject]] function. + try { + const promiseCapability = + UnsafeCast(promiseOrCapability); + const reject = UnsafeCast(promiseCapability.reject); + return Call(context, reject, Undefined, reason); + } catch (e) { + // Swallow the exception here. + return runtime::ReportMessage(e); + } + } + } else { + StaticAssert(reactionType == kPromiseReactionFulfill); + // We have to call out to the dedicated PromiseRejectReactionJob + // builtin here, instead of just doing the work inline, as otherwise + // the catch predictions in the debugger will be wrong, which just + // walks the stack and checks for certain builtins. + return PromiseRejectReactionJob(reason, Undefined, promiseOrCapability); + } + } + + transitioning + macro FuflfillPromiseReactionJob( + context: Context, + promiseOrCapability: JSPromise|PromiseCapability|Undefined, result: JSAny, + reactionType: constexpr PromiseReactionType): JSAny { + if (IsJSPromise(promiseOrCapability)) { + // For fast native promises we can skip the indirection via the + // promiseCapability.[[Resolve]] function and run the resolve logic + // directly from here. + return ResolvePromise( + context, UnsafeCast(promiseOrCapability), result); + } else + deferred { + assert(IsPromiseCapability(promiseOrCapability)); + // In the general case we need to call the (user provided) + // promiseCapability.[[Resolve]] function. + const promiseCapability = + UnsafeCast(promiseOrCapability); + const resolve = UnsafeCast(promiseCapability.resolve); + try { + return Call(context, resolve, Undefined, result); + } catch (e) { + return RejectPromiseReactionJob( + context, promiseOrCapability, e, reactionType); + } + } + } + + // https://tc39.es/ecma262/#sec-promisereactionjob + transitioning + macro PromiseReactionJob( + context: Context, argument: JSAny, handler: Callable|Undefined, + promiseOrCapability: JSPromise|PromiseCapability|Undefined, + reactionType: constexpr PromiseReactionType): JSAny { + if (handler == Undefined) { + if constexpr (reactionType == kPromiseReactionFulfill) { + return FuflfillPromiseReactionJob( + context, promiseOrCapability, argument, reactionType); + } else { + StaticAssert(reactionType == kPromiseReactionReject); + return RejectPromiseReactionJob( + context, promiseOrCapability, argument, reactionType); + } + } else { + try { + const result = + Call(context, UnsafeCast(handler), Undefined, argument); + if (promiseOrCapability == Undefined) { + // 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 Undefined; + } else { + return FuflfillPromiseReactionJob( + context, promiseOrCapability, result, reactionType); + } + } catch (e) { + return RejectPromiseReactionJob( + context, promiseOrCapability, e, reactionType); + } + } + } + + transitioning builtin + PromiseFulfillReactionJob(implicit context: Context)( + value: JSAny, handler: Callable|Undefined, + promiseOrCapability: JSPromise|PromiseCapability|Undefined): JSAny { + return PromiseReactionJob( + context, value, handler, promiseOrCapability, kPromiseReactionFulfill); + } + + transitioning builtin + PromiseRejectReactionJob(implicit context: Context)( + reason: JSAny, handler: Callable|Undefined, + promiseOrCapability: JSPromise|PromiseCapability|Undefined): JSAny { + return PromiseReactionJob( + context, reason, handler, promiseOrCapability, kPromiseReactionReject); + } +}