[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/object.tq",
|
||||||
"src/builtins/promise-abstract-operations.tq",
|
"src/builtins/promise-abstract-operations.tq",
|
||||||
"src/builtins/promise-constructor.tq",
|
"src/builtins/promise-constructor.tq",
|
||||||
|
"src/builtins/promise-reaction-job.tq",
|
||||||
"src/builtins/promise-resolve.tq",
|
"src/builtins/promise-resolve.tq",
|
||||||
"src/builtins/promise-then.tq",
|
"src/builtins/promise-then.tq",
|
||||||
"src/builtins/promise-jobs.tq",
|
"src/builtins/promise-jobs.tq",
|
||||||
|
@ -720,9 +720,6 @@ namespace internal {
|
|||||||
CPP(IsPromise) \
|
CPP(IsPromise) \
|
||||||
/* ES #sec-promise.prototype.catch */ \
|
/* ES #sec-promise.prototype.catch */ \
|
||||||
TFJ(PromisePrototypeCatch, 1, kReceiver, kOnRejected) \
|
TFJ(PromisePrototypeCatch, 1, kReceiver, kOnRejected) \
|
||||||
/* ES #sec-promisereactionjob */ \
|
|
||||||
TFS(PromiseRejectReactionJob, kReason, kHandler, kPromiseOrCapability) \
|
|
||||||
TFS(PromiseFulfillReactionJob, kValue, kHandler, kPromiseOrCapability) \
|
|
||||||
/* ES #sec-promise.reject */ \
|
/* ES #sec-promise.reject */ \
|
||||||
TFJ(PromiseReject, 1, kReceiver, kReason) \
|
TFJ(PromiseReject, 1, kReceiver, kReason) \
|
||||||
TFJ(PromisePrototypeFinally, 1, kReceiver, kOnFinally) \
|
TFJ(PromisePrototypeFinally, 1, kReceiver, kOnFinally) \
|
||||||
|
@ -540,145 +540,6 @@ TF_BUILTIN(PromisePrototypeCatch, PromiseBuiltinsAssembler) {
|
|||||||
Return(InvokeThen(native_context, receiver, on_fulfilled, on_rejected));
|
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
|
// ES6 #sec-getcapabilitiesexecutor-functions
|
||||||
TF_BUILTIN(PromiseGetCapabilitiesExecutor, PromiseBuiltinsAssembler) {
|
TF_BUILTIN(PromiseGetCapabilitiesExecutor, PromiseBuiltinsAssembler) {
|
||||||
Node* const resolve = Parameter(Descriptor::kResolve);
|
Node* const resolve = Parameter(Descriptor::kResolve);
|
||||||
|
@ -141,10 +141,6 @@ class V8_EXPORT_PRIVATE PromiseBuiltinsAssembler : public CodeStubAssembler {
|
|||||||
|
|
||||||
TNode<Word32T> PromiseStatus(Node* promise);
|
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,
|
TNode<BoolT> IsPromiseStatus(TNode<Word32T> actual,
|
||||||
v8::Promise::PromiseState expected);
|
v8::Promise::PromiseState expected);
|
||||||
TNode<JSPromise> AllocateJSPromise(TNode<Context> context);
|
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