[promises] Port PromiseConstructor to Torque.
Bug: v8:9838 Change-Id: Iceeb7e274c0cc9fd7066fa538818aebf23ce2678 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1925566 Commit-Queue: Joshua Litt <joshualitt@chromium.org> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Cr-Commit-Position: refs/heads/master@{#65176}
This commit is contained in:
parent
3114000ac1
commit
95e9ac05de
1
BUILD.gn
1
BUILD.gn
@ -972,6 +972,7 @@ torque_files = [
|
|||||||
"src/builtins/object-fromentries.tq",
|
"src/builtins/object-fromentries.tq",
|
||||||
"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/proxy-constructor.tq",
|
"src/builtins/proxy-constructor.tq",
|
||||||
"src/builtins/proxy-delete-property.tq",
|
"src/builtins/proxy-delete-property.tq",
|
||||||
"src/builtins/proxy-get-property.tq",
|
"src/builtins/proxy-get-property.tq",
|
||||||
|
@ -717,8 +717,6 @@ namespace internal {
|
|||||||
TFJ(PromiseGetCapabilitiesExecutor, 2, kReceiver, kResolve, kReject) \
|
TFJ(PromiseGetCapabilitiesExecutor, 2, kReceiver, kResolve, kReject) \
|
||||||
TFJ(PromiseConstructorLazyDeoptContinuation, 4, kReceiver, kPromise, \
|
TFJ(PromiseConstructorLazyDeoptContinuation, 4, kReceiver, kPromise, \
|
||||||
kReject, kException, kResult) \
|
kReject, kException, kResult) \
|
||||||
/* ES6 #sec-promise-executor */ \
|
|
||||||
TFJ(PromiseConstructor, 1, kReceiver, kExecutor) \
|
|
||||||
CPP(IsPromise) \
|
CPP(IsPromise) \
|
||||||
/* ES #sec-promise.prototype.then */ \
|
/* ES #sec-promise.prototype.then */ \
|
||||||
TFJ(PromisePrototypeThen, 2, kReceiver, kOnFulfilled, kOnRejected) \
|
TFJ(PromisePrototypeThen, 2, kReceiver, kOnFulfilled, kOnRejected) \
|
||||||
|
@ -441,7 +441,8 @@ void PromiseBuiltinsAssembler::BranchIfPromiseThenLookupChainIntact(
|
|||||||
|
|
||||||
void PromiseBuiltinsAssembler::BranchIfAccessCheckFailed(
|
void PromiseBuiltinsAssembler::BranchIfAccessCheckFailed(
|
||||||
SloppyTNode<Context> context, SloppyTNode<Context> native_context,
|
SloppyTNode<Context> context, SloppyTNode<Context> native_context,
|
||||||
Node* promise_constructor, Node* executor, Label* if_noaccess) {
|
TNode<Object> promise_constructor, TNode<Object> executor,
|
||||||
|
Label* if_noaccess) {
|
||||||
VARIABLE(var_executor, MachineRepresentation::kTagged);
|
VARIABLE(var_executor, MachineRepresentation::kTagged);
|
||||||
var_executor.Bind(executor);
|
var_executor.Bind(executor);
|
||||||
Label has_access(this), call_runtime(this, Label::kDeferred);
|
Label has_access(this), call_runtime(this, Label::kDeferred);
|
||||||
@ -528,119 +529,6 @@ TF_BUILTIN(PromiseConstructorLazyDeoptContinuation, PromiseBuiltinsAssembler) {
|
|||||||
Return(promise);
|
Return(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ES6 #sec-promise-executor
|
|
||||||
TF_BUILTIN(PromiseConstructor, PromiseBuiltinsAssembler) {
|
|
||||||
TNode<Object> executor = CAST(Parameter(Descriptor::kExecutor));
|
|
||||||
TNode<Object> new_target = CAST(Parameter(Descriptor::kJSNewTarget));
|
|
||||||
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
|
|
||||||
Isolate* isolate = this->isolate();
|
|
||||||
|
|
||||||
Label if_targetisundefined(this, Label::kDeferred);
|
|
||||||
|
|
||||||
GotoIf(IsUndefined(new_target), &if_targetisundefined);
|
|
||||||
|
|
||||||
Label if_notcallable(this, Label::kDeferred);
|
|
||||||
|
|
||||||
GotoIf(TaggedIsSmi(executor), &if_notcallable);
|
|
||||||
|
|
||||||
const TNode<Map> executor_map = LoadMap(CAST(executor));
|
|
||||||
GotoIfNot(IsCallableMap(executor_map), &if_notcallable);
|
|
||||||
|
|
||||||
const TNode<NativeContext> native_context = LoadNativeContext(context);
|
|
||||||
const TNode<JSFunction> promise_fun =
|
|
||||||
CAST(LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX));
|
|
||||||
Node* const is_debug_active = IsDebugActive();
|
|
||||||
Label if_targetisnotmodified(this),
|
|
||||||
if_targetismodified(this, Label::kDeferred), run_executor(this),
|
|
||||||
debug_push(this), if_noaccess(this, Label::kDeferred);
|
|
||||||
|
|
||||||
BranchIfAccessCheckFailed(context, native_context, promise_fun, executor,
|
|
||||||
&if_noaccess);
|
|
||||||
|
|
||||||
Branch(TaggedEqual(promise_fun, new_target), &if_targetisnotmodified,
|
|
||||||
&if_targetismodified);
|
|
||||||
|
|
||||||
VARIABLE(var_result, MachineRepresentation::kTagged);
|
|
||||||
VARIABLE(var_reject_call, MachineRepresentation::kTagged);
|
|
||||||
VARIABLE(var_reason, MachineRepresentation::kTagged);
|
|
||||||
|
|
||||||
BIND(&if_targetisnotmodified);
|
|
||||||
{
|
|
||||||
const TNode<JSPromise> instance = AllocateAndInitJSPromise(context);
|
|
||||||
var_result.Bind(instance);
|
|
||||||
Goto(&debug_push);
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&if_targetismodified);
|
|
||||||
{
|
|
||||||
ConstructorBuiltinsAssembler constructor_assembler(this->state());
|
|
||||||
TNode<JSObject> instance = constructor_assembler.EmitFastNewObject(
|
|
||||||
context, promise_fun, CAST(new_target));
|
|
||||||
PromiseInit(instance);
|
|
||||||
var_result.Bind(instance);
|
|
||||||
|
|
||||||
GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &debug_push);
|
|
||||||
CallRuntime(Runtime::kPromiseHookInit, context, instance,
|
|
||||||
UndefinedConstant());
|
|
||||||
Goto(&debug_push);
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&debug_push);
|
|
||||||
{
|
|
||||||
GotoIfNot(is_debug_active, &run_executor);
|
|
||||||
CallRuntime(Runtime::kDebugPushPromise, context, var_result.value());
|
|
||||||
Goto(&run_executor);
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&run_executor);
|
|
||||||
{
|
|
||||||
Label out(this), if_rejectpromise(this), debug_pop(this, Label::kDeferred);
|
|
||||||
|
|
||||||
PromiseResolvingFunctions funcs = CreatePromiseResolvingFunctions(
|
|
||||||
context, CAST(var_result.value()), TrueConstant(), native_context);
|
|
||||||
Node *resolve = funcs.resolve, *reject = funcs.reject;
|
|
||||||
|
|
||||||
Node* const maybe_exception = CallJS(
|
|
||||||
CodeFactory::Call(isolate, ConvertReceiverMode::kNullOrUndefined),
|
|
||||||
context, executor, UndefinedConstant(), resolve, reject);
|
|
||||||
|
|
||||||
GotoIfException(maybe_exception, &if_rejectpromise, &var_reason);
|
|
||||||
Branch(is_debug_active, &debug_pop, &out);
|
|
||||||
|
|
||||||
BIND(&if_rejectpromise);
|
|
||||||
{
|
|
||||||
CallJS(CodeFactory::Call(isolate, ConvertReceiverMode::kNullOrUndefined),
|
|
||||||
context, reject, UndefinedConstant(), var_reason.value());
|
|
||||||
Branch(is_debug_active, &debug_pop, &out);
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&debug_pop);
|
|
||||||
{
|
|
||||||
CallRuntime(Runtime::kDebugPopPromise, context);
|
|
||||||
Goto(&out);
|
|
||||||
}
|
|
||||||
BIND(&out);
|
|
||||||
Return(var_result.value());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. If NewTarget is undefined, throw a TypeError exception.
|
|
||||||
BIND(&if_targetisundefined);
|
|
||||||
ThrowTypeError(context, MessageTemplate::kNotAPromise, new_target);
|
|
||||||
|
|
||||||
// 2. If IsCallable(executor) is false, throw a TypeError exception.
|
|
||||||
BIND(&if_notcallable);
|
|
||||||
ThrowTypeError(context, MessageTemplate::kResolverNotAFunction, executor);
|
|
||||||
|
|
||||||
// Silently fail if the stack looks fishy.
|
|
||||||
BIND(&if_noaccess);
|
|
||||||
{
|
|
||||||
const TNode<Smi> counter_id =
|
|
||||||
SmiConstant(v8::Isolate::kPromiseConstructorReturnedUndefined);
|
|
||||||
CallRuntime(Runtime::kIncrementUseCounter, context, counter_id);
|
|
||||||
Return(UndefinedConstant());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ES#sec-promise.prototype.then
|
// ES#sec-promise.prototype.then
|
||||||
// Promise.prototype.then ( onFulfilled, onRejected )
|
// Promise.prototype.then ( onFulfilled, onRejected )
|
||||||
TF_BUILTIN(PromisePrototypeThen, PromiseBuiltinsAssembler) {
|
TF_BUILTIN(PromisePrototypeThen, PromiseBuiltinsAssembler) {
|
||||||
|
@ -66,9 +66,13 @@ class V8_EXPORT_PRIVATE PromiseBuiltinsAssembler : public CodeStubAssembler {
|
|||||||
TNode<JSPromise> promise, TNode<Object> debug_event,
|
TNode<JSPromise> promise, TNode<Object> debug_event,
|
||||||
TNode<NativeContext> native_context);
|
TNode<NativeContext> native_context);
|
||||||
|
|
||||||
protected:
|
void BranchIfAccessCheckFailed(SloppyTNode<Context> context,
|
||||||
|
SloppyTNode<Context> native_context,
|
||||||
|
TNode<Object> promise_constructor,
|
||||||
|
TNode<Object> executor, Label* if_noaccess);
|
||||||
void PromiseInit(Node* promise);
|
void PromiseInit(Node* promise);
|
||||||
|
|
||||||
|
protected:
|
||||||
void PromiseSetHasHandler(Node* promise);
|
void PromiseSetHasHandler(Node* promise);
|
||||||
void PromiseSetHandledHint(Node* promise);
|
void PromiseSetHandledHint(Node* promise);
|
||||||
|
|
||||||
@ -106,11 +110,6 @@ class V8_EXPORT_PRIVATE PromiseBuiltinsAssembler : public CodeStubAssembler {
|
|||||||
template <typename... TArgs>
|
template <typename... TArgs>
|
||||||
Node* InvokeThen(Node* native_context, Node* receiver, TArgs... args);
|
Node* InvokeThen(Node* native_context, Node* receiver, TArgs... args);
|
||||||
|
|
||||||
void BranchIfAccessCheckFailed(SloppyTNode<Context> context,
|
|
||||||
SloppyTNode<Context> native_context,
|
|
||||||
Node* promise_constructor, Node* executor,
|
|
||||||
Label* if_noaccess);
|
|
||||||
|
|
||||||
std::pair<Node*, Node*> CreatePromiseFinallyFunctions(Node* on_finally,
|
std::pair<Node*, Node*> CreatePromiseFinallyFunctions(Node* on_finally,
|
||||||
Node* constructor,
|
Node* constructor,
|
||||||
Node* native_context);
|
Node* native_context);
|
||||||
|
108
src/builtins/promise-constructor.tq
Normal file
108
src/builtins/promise-constructor.tq
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
// 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-constructor-gen.h'
|
||||||
|
#include 'src/builtins/builtins-promise-gen.h'
|
||||||
|
|
||||||
|
namespace runtime {
|
||||||
|
extern transitioning runtime
|
||||||
|
DebugPushPromise(implicit context: Context)(JSAny): JSAny;
|
||||||
|
|
||||||
|
extern transitioning runtime
|
||||||
|
DebugPopPromise(implicit context: Context)(): JSAny;
|
||||||
|
|
||||||
|
extern transitioning runtime
|
||||||
|
PromiseHookInit(implicit context: Context)(Object, Object): JSAny;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://tc39.es/ecma262/#sec-promise-constructor
|
||||||
|
namespace promise {
|
||||||
|
|
||||||
|
extern runtime IncrementUseCounter(Context, Smi): void;
|
||||||
|
type UseCounterFeature extends int31
|
||||||
|
constexpr 'v8::Isolate::UseCounterFeature';
|
||||||
|
const kNotAPromise: constexpr MessageTemplate
|
||||||
|
generates 'MessageTemplate::kNotAPromise';
|
||||||
|
const kResolverNotAFunction: constexpr MessageTemplate
|
||||||
|
generates 'MessageTemplate::kResolverNotAFunction';
|
||||||
|
const kPromiseConstructorReturnedUndefined: constexpr UseCounterFeature
|
||||||
|
generates 'v8::Isolate::kPromiseConstructorReturnedUndefined';
|
||||||
|
|
||||||
|
extern macro
|
||||||
|
PromiseBuiltinsAssembler::BranchIfAccessCheckFailed(
|
||||||
|
Context, Context, Object, Object): void labels NoAccess;
|
||||||
|
|
||||||
|
extern macro
|
||||||
|
IsDebugActive(): bool;
|
||||||
|
|
||||||
|
transitioning macro
|
||||||
|
HasAccessCheckFailed(
|
||||||
|
context: Context, nativeContext: Context, promiseFun: Object,
|
||||||
|
executor: Object): bool {
|
||||||
|
BranchIfAccessCheckFailed(context, nativeContext, promiseFun, executor)
|
||||||
|
otherwise return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern macro ConstructorBuiltinsAssembler::EmitFastNewObject(
|
||||||
|
Context, JSFunction, JSReceiver): JSObject;
|
||||||
|
|
||||||
|
extern macro PromiseBuiltinsAssembler::PromiseInit(Object): void;
|
||||||
|
|
||||||
|
extern macro
|
||||||
|
PromiseBuiltinsAssembler::IsPromiseHookEnabledOrHasAsyncEventDelegate(): bool;
|
||||||
|
|
||||||
|
// https://tc39.es/ecma262/#sec-promise-executor
|
||||||
|
transitioning javascript builtin
|
||||||
|
PromiseConstructor(
|
||||||
|
js-implicit context: NativeContext, receiver: JSAny,
|
||||||
|
newTarget: JSAny)(executor: JSAny): JSAny {
|
||||||
|
// 1. If NewTarget is undefined, throw a TypeError exception.
|
||||||
|
if (newTarget == Undefined) {
|
||||||
|
ThrowTypeError(kNotAPromise, newTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. If IsCallable(executor) is false, throw a TypeError exception.
|
||||||
|
if (!TaggedIsCallable(executor)) {
|
||||||
|
ThrowTypeError(kResolverNotAFunction, executor);
|
||||||
|
}
|
||||||
|
|
||||||
|
const promiseFun = UnsafeCast<JSFunction>(context[PROMISE_FUNCTION_INDEX]);
|
||||||
|
|
||||||
|
// Silently fail if the stack looks fishy.
|
||||||
|
if (HasAccessCheckFailed(context, context, promiseFun, executor)) {
|
||||||
|
IncrementUseCounter(
|
||||||
|
context, SmiConstant(kPromiseConstructorReturnedUndefined));
|
||||||
|
return Undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
let result: JSPromise;
|
||||||
|
if (promiseFun == newTarget) {
|
||||||
|
result = AllocateAndInitJSPromise(context);
|
||||||
|
} else {
|
||||||
|
const resultObject = EmitFastNewObject(
|
||||||
|
context, promiseFun, UnsafeCast<JSReceiver>(newTarget));
|
||||||
|
PromiseInit(resultObject);
|
||||||
|
result = UnsafeCast<JSPromise>(resultObject);
|
||||||
|
if (IsPromiseHookEnabledOrHasAsyncEventDelegate()) {
|
||||||
|
runtime::PromiseHookInit(result, Undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isDebugActive = IsDebugActive();
|
||||||
|
if (isDebugActive) runtime::DebugPushPromise(result);
|
||||||
|
|
||||||
|
const funcs = CreatePromiseResolvingFunctions(result, True, context);
|
||||||
|
const resolve = funcs.resolve;
|
||||||
|
const reject = funcs.reject;
|
||||||
|
try {
|
||||||
|
Call(context, UnsafeCast<Callable>(executor), Undefined, resolve, reject);
|
||||||
|
} catch (e) {
|
||||||
|
Call(context, reject, Undefined, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDebugActive) runtime::DebugPopPromise();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user