[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:
parent
7109150ad0
commit
192c55839e
1
BUILD.gn
1
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",
|
||||
|
@ -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) \
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
131
src/builtins/promise-reaction-job.tq
Normal file
131
src/builtins/promise-reaction-job.tq
Normal 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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user