[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:
Joshua Litt 2019-11-25 12:43:28 -08:00 committed by Commit Bot
parent 3114000ac1
commit 95e9ac05de
5 changed files with 116 additions and 122 deletions

View File

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

View File

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

View File

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

View File

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

View 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;
}
}