[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 <mslekova@chromium.org>
Commit-Queue: Joshua Litt <joshualitt@chromium.org>
Cr-Commit-Position: refs/heads/master@{#65446}
This commit is contained in:
Joshua Litt 2019-12-12 10:49:37 -08:00 committed by Commit Bot
parent 7109150ad0
commit 192c55839e
5 changed files with 132 additions and 146 deletions

View File

@ -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",

View File

@ -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) \

View File

@ -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<Object> resolve = LoadObjectField(
promise_or_capability, PromiseCapability::kResolveOffset);
const TNode<Object> 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<Object> reject = LoadObjectField(
promise_or_capability, PromiseCapability::kRejectOffset);
const TNode<Object> 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);

View File

@ -141,10 +141,6 @@ class V8_EXPORT_PRIVATE PromiseBuiltinsAssembler : public CodeStubAssembler {
TNode<Word32T> PromiseStatus(Node* promise);
void PromiseReactionJob(Node* context, Node* argument, Node* handler,
Node* promise_or_capability,
PromiseReaction::Type type);
TNode<BoolT> IsPromiseStatus(TNode<Word32T> actual,
v8::Promise::PromiseState expected);
TNode<JSPromise> AllocateJSPromise(TNode<Context> context);

View File

@ -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<JSPromise>(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<PromiseCapability>(promiseOrCapability);
const reject = UnsafeCast<Callable>(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<JSPromise>(promiseOrCapability), result);
} else
deferred {
assert(IsPromiseCapability(promiseOrCapability));
// In the general case we need to call the (user provided)
// promiseCapability.[[Resolve]] function.
const promiseCapability =
UnsafeCast<PromiseCapability>(promiseOrCapability);
const resolve = UnsafeCast<Callable>(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<Callable>(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);
}
}