Commit a0a7288b authored by Joshua Litt's avatar Joshua Litt Committed by Commit Bot

[promises] Port remaining Promise*Finally to Torque.

Bug: v8:9838
Change-Id: Ibf0c0b0f55a3728810de026c0132ff89e2c9861f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1961943Reviewed-by: 's avatarMaya Lekova <mslekova@chromium.org>
Commit-Queue: Joshua Litt <joshualitt@chromium.org>
Cr-Commit-Position: refs/heads/master@{#65523}
parent 7edaa330
......@@ -714,8 +714,6 @@ namespace internal {
TFJ(PromiseConstructorLazyDeoptContinuation, 4, kReceiver, kPromise, \
kReject, kException, kResult) \
CPP(IsPromise) \
TFJ(PromisePrototypeFinally, 1, kReceiver, kOnFinally) \
TFJ(PromiseThenFinally, 1, kReceiver, kValue) \
/* ES #sec-promise.all */ \
TFJ(PromiseAll, 1, kReceiver, kIterable) \
TFJ(PromiseAllResolveElementClosure, 1, kReceiver, kValue) \
......
......@@ -396,157 +396,6 @@ TF_BUILTIN(PromiseConstructorLazyDeoptContinuation, PromiseBuiltinsAssembler) {
Return(promise);
}
std::pair<Node*, Node*> PromiseBuiltinsAssembler::CreatePromiseFinallyFunctions(
Node* on_finally, Node* constructor, Node* native_context) {
const TNode<Context> promise_context = AllocateSyntheticFunctionContext(
CAST(native_context), PromiseBuiltins::kPromiseFinallyContextLength);
StoreContextElementNoWriteBarrier(
promise_context, PromiseBuiltins::kOnFinallySlot, on_finally);
StoreContextElementNoWriteBarrier(
promise_context, PromiseBuiltins::kConstructorSlot, constructor);
const TNode<Map> map = CAST(LoadContextElement(
native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX));
const TNode<SharedFunctionInfo> then_finally_info = CAST(LoadContextElement(
native_context, Context::PROMISE_THEN_FINALLY_SHARED_FUN));
const TNode<JSFunction> then_finally = AllocateFunctionWithMapAndContext(
map, then_finally_info, promise_context);
const TNode<SharedFunctionInfo> catch_finally_info = CAST(LoadContextElement(
native_context, Context::PROMISE_CATCH_FINALLY_SHARED_FUN));
const TNode<JSFunction> catch_finally = AllocateFunctionWithMapAndContext(
map, catch_finally_info, promise_context);
return std::make_pair(then_finally, catch_finally);
}
Node* PromiseBuiltinsAssembler::CreateValueThunkFunction(Node* value,
Node* native_context) {
const TNode<Context> value_thunk_context = AllocateSyntheticFunctionContext(
CAST(native_context),
PromiseBuiltins::kPromiseValueThunkOrReasonContextLength);
StoreContextElementNoWriteBarrier(value_thunk_context,
PromiseBuiltins::kValueSlot, value);
const TNode<Map> map = CAST(LoadContextElement(
native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX));
const TNode<SharedFunctionInfo> value_thunk_info = CAST(LoadContextElement(
native_context, Context::PROMISE_VALUE_THUNK_FINALLY_SHARED_FUN));
const TNode<JSFunction> value_thunk = AllocateFunctionWithMapAndContext(
map, value_thunk_info, value_thunk_context);
return value_thunk;
}
TF_BUILTIN(PromiseThenFinally, PromiseBuiltinsAssembler) {
CSA_ASSERT_JS_ARGC_EQ(this, 1);
Node* const value = Parameter(Descriptor::kValue);
Node* const context = Parameter(Descriptor::kContext);
// 1. Let onFinally be F.[[OnFinally]].
const TNode<HeapObject> on_finally =
CAST(LoadContextElement(context, PromiseBuiltins::kOnFinallySlot));
// 2. Assert: IsCallable(onFinally) is true.
CSA_ASSERT(this, IsCallable(on_finally));
// 3. Let result be ? Call(onFinally).
Node* const result = CallJS(
CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
context, on_finally, UndefinedConstant());
// 4. Let C be F.[[Constructor]].
const TNode<JSFunction> constructor =
CAST(LoadContextElement(context, PromiseBuiltins::kConstructorSlot));
// 5. Assert: IsConstructor(C) is true.
CSA_ASSERT(this, IsConstructor(constructor));
// 6. Let promise be ? PromiseResolve(C, result).
const TNode<Object> promise =
CallBuiltin(Builtins::kPromiseResolve, context, constructor, result);
// 7. Let valueThunk be equivalent to a function that returns value.
const TNode<NativeContext> native_context = LoadNativeContext(context);
Node* const value_thunk = CreateValueThunkFunction(value, native_context);
// 8. Return ? Invoke(promise, "then", « valueThunk »).
Return(InvokeThen(native_context, promise, value_thunk));
}
TF_BUILTIN(PromisePrototypeFinally, PromiseBuiltinsAssembler) {
CSA_ASSERT_JS_ARGC_EQ(this, 1);
// 1. Let promise be the this value.
Node* const receiver = Parameter(Descriptor::kReceiver);
Node* const on_finally = Parameter(Descriptor::kOnFinally);
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
// 2. If Type(promise) is not Object, throw a TypeError exception.
ThrowIfNotJSReceiver(context, CAST(receiver),
MessageTemplate::kCalledOnNonObject,
"Promise.prototype.finally");
// 3. Let C be ? SpeciesConstructor(promise, %Promise%).
const TNode<NativeContext> native_context = LoadNativeContext(context);
const TNode<Object> promise_fun =
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
VARIABLE(var_constructor, MachineRepresentation::kTagged, promise_fun);
Label slow_constructor(this, Label::kDeferred), done_constructor(this);
const TNode<Map> receiver_map = LoadMap(receiver);
GotoIfNot(IsJSPromiseMap(receiver_map), &slow_constructor);
BranchIfPromiseSpeciesLookupChainIntact(native_context, receiver_map,
&done_constructor, &slow_constructor);
BIND(&slow_constructor);
{
const TNode<JSReceiver> constructor =
SpeciesConstructor(context, receiver, CAST(promise_fun));
var_constructor.Bind(constructor);
Goto(&done_constructor);
}
BIND(&done_constructor);
Node* const constructor = var_constructor.value();
// 4. Assert: IsConstructor(C) is true.
CSA_ASSERT(this, IsConstructor(constructor));
VARIABLE(var_then_finally, MachineRepresentation::kTagged);
VARIABLE(var_catch_finally, MachineRepresentation::kTagged);
Label if_notcallable(this, Label::kDeferred), perform_finally(this);
GotoIf(TaggedIsSmi(on_finally), &if_notcallable);
GotoIfNot(IsCallable(on_finally), &if_notcallable);
// 6. Else,
// a. Let thenFinally be a new built-in function object as defined
// in ThenFinally Function.
// b. Let catchFinally be a new built-in function object as
// defined in CatchFinally Function.
// c. Set thenFinally and catchFinally's [[Constructor]] internal
// slots to C.
// d. Set thenFinally and catchFinally's [[OnFinally]] internal
// slots to onFinally.
Node* then_finally = nullptr;
Node* catch_finally = nullptr;
std::tie(then_finally, catch_finally) =
CreatePromiseFinallyFunctions(on_finally, constructor, native_context);
var_then_finally.Bind(then_finally);
var_catch_finally.Bind(catch_finally);
Goto(&perform_finally);
// 5. If IsCallable(onFinally) is not true,
// a. Let thenFinally be onFinally.
// b. Let catchFinally be onFinally.
BIND(&if_notcallable);
{
var_then_finally.Bind(on_finally);
var_catch_finally.Bind(on_finally);
Goto(&perform_finally);
}
// 7. Return ? Invoke(promise, "then", « thenFinally, catchFinally »).
BIND(&perform_finally);
Return(InvokeThen(native_context, CAST(receiver), var_then_finally.value(),
var_catch_finally.value()));
}
TNode<Object> PromiseBuiltinsAssembler::PerformPromiseAll(
Node* context, Node* constructor, Node* capability,
const IteratorRecord& iterator,
......
......@@ -145,11 +145,6 @@ class V8_EXPORT_PRIVATE PromiseBuiltinsAssembler : public CodeStubAssembler {
Node* CallResolve(Node* native_context, Node* constructor, Node* resolve,
Node* value, Label* if_exception, Variable* var_exception);
std::pair<Node*, Node*> CreatePromiseFinallyFunctions(Node* on_finally,
Node* constructor,
Node* native_context);
Node* CreateValueThunkFunction(Node* value, Node* native_context);
using PromiseAllResolvingElementFunction =
std::function<TNode<Object>(TNode<Context> context, TNode<Smi> index,
TNode<NativeContext> native_context,
......
......@@ -19,6 +19,8 @@ namespace promise {
generates 'PromiseBuiltins::kConstructorSlot';
const kPromiseBuiltinsPromiseValueThunkOrReasonContextLength: constexpr int31
generates 'PromiseBuiltins::kPromiseValueThunkOrReasonContextLength';
const kPromiseBuiltinsPromiseFinallyContextLength: constexpr int31
generates 'PromiseBuiltins::kPromiseFinallyContextLength';
transitioning javascript builtin
PromiseValueThunkFinally(js-implicit context: Context, receiver: JSAny)():
......@@ -80,4 +82,131 @@ namespace promise {
return UnsafeCast<JSAny>(InvokeThen(nativeContext, promise, thrower));
}
macro CreateValueThunkFunction(implicit context: Context)(
nativeContext: NativeContext, value: JSAny): JSFunction {
const valueThunkContext = AllocateSyntheticFunctionContext(
nativeContext, kPromiseBuiltinsPromiseValueThunkOrReasonContextLength);
valueThunkContext[kPromiseBuiltinsValueSlot] = value;
const map = UnsafeCast<Map>(
nativeContext
[NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]);
const valueThunkInfo = UnsafeCast<SharedFunctionInfo>(
nativeContext
[NativeContextSlot::PROMISE_VALUE_THUNK_FINALLY_SHARED_FUN]);
return AllocateFunctionWithMapAndContext(
map, valueThunkInfo, valueThunkContext);
}
transitioning javascript builtin
PromiseThenFinally(js-implicit context: Context, receiver: JSAny)(
value: JSAny): JSAny {
// 1. Let onFinally be F.[[OnFinally]].
// 2. Assert: IsCallable(onFinally) is true.
const onFinally =
UnsafeCast<Callable>(context[kPromiseBuiltinsOnFinallySlot]);
// 3. Let result be ? Call(onFinally).
const result = Call(context, onFinally, Undefined);
// 4. Let C be F.[[Constructor]].
const constructor =
UnsafeCast<JSFunction>(context[kPromiseBuiltinsConstructorSlot]);
// 5. Assert: IsConstructor(C) is true.
assert(IsConstructor(constructor));
// 6. Let promise be ? PromiseResolve(C, result).
const promise = PromiseResolve(constructor, result);
// 7. Let valueThunk be equivalent to a function that returns value.
const nativeContext = LoadNativeContext(context);
const valueThunk = CreateValueThunkFunction(nativeContext, value);
// 8. Return ? Invoke(promise, "then", « valueThunk »).
return UnsafeCast<JSAny>(InvokeThen(nativeContext, promise, valueThunk));
}
struct PromiseFinallyFunctions {
then_finally: JSFunction;
catch_finally: JSFunction;
}
macro CreatePromiseFinallyFunctions(implicit context: Context)(
nativeContext: NativeContext, onFinally: Callable,
constructor: JSReceiver): PromiseFinallyFunctions {
const promiseContext = AllocateSyntheticFunctionContext(
nativeContext, kPromiseBuiltinsPromiseFinallyContextLength);
promiseContext[kPromiseBuiltinsOnFinallySlot] = onFinally;
promiseContext[kPromiseBuiltinsConstructorSlot] = constructor;
const map = UnsafeCast<Map>(
nativeContext
[NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]);
const thenFinallyInfo = UnsafeCast<SharedFunctionInfo>(
nativeContext[NativeContextSlot::PROMISE_THEN_FINALLY_SHARED_FUN]);
const thenFinally =
AllocateFunctionWithMapAndContext(map, thenFinallyInfo, promiseContext);
const catchFinallyInfo = UnsafeCast<SharedFunctionInfo>(
nativeContext[NativeContextSlot::PROMISE_CATCH_FINALLY_SHARED_FUN]);
const catchFinally = AllocateFunctionWithMapAndContext(
map, catchFinallyInfo, promiseContext);
return PromiseFinallyFunctions{
then_finally: thenFinally,
catch_finally: catchFinally
};
}
transitioning javascript builtin
PromisePrototypeFinally(js-implicit context: Context, receiver: JSAny)(
onFinally: JSAny): JSAny {
// 1. Let promise be the this value.
// 2. If Type(promise) is not Object, throw a TypeError exception.
const jsReceiver = Cast<JSReceiver>(receiver) otherwise ThrowTypeError(
MessageTemplate::kCalledOnNonObject, 'Promise.prototype.finally');
// 3. Let C be ? SpeciesConstructor(promise, %Promise%).
const nativeContext = LoadNativeContext(context);
const promiseFun = UnsafeCast<Callable>(
nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX]);
let constructor: JSReceiver = promiseFun;
const receiverMap = jsReceiver.map;
if (!IsJSPromiseMap(receiverMap) ||
!IsPromiseSpeciesLookupChainIntact(nativeContext, receiverMap))
deferred {
constructor = SpeciesConstructor(jsReceiver, promiseFun);
}
// 4. Assert: IsConstructor(C) is true.
assert(IsConstructor(constructor));
// 5. If IsCallable(onFinally) is not true,
// a. Let thenFinally be onFinally.
// b. Let catchFinally be onFinally.
// 6. Else,
// a. Let thenFinally be a new built-in function object as defined
// in ThenFinally Function.
// b. Let catchFinally be a new built-in function object as
// defined in CatchFinally Function.
// c. Set thenFinally and catchFinally's [[Constructor]] internal
// slots to C.
// d. Set thenFinally and catchFinally's [[OnFinally]] internal
// slots to onFinally.
let thenFinally: JSAny;
let catchFinally: JSAny;
if (!TaggedIsSmi(onFinally) &&
IsCallable(UnsafeCast<HeapObject>(onFinally))) {
const pair = CreatePromiseFinallyFunctions(
nativeContext, UnsafeCast<Callable>(onFinally), constructor);
thenFinally = pair.then_finally;
catchFinally = pair.catch_finally;
} else
deferred {
thenFinally = onFinally;
catchFinally = onFinally;
}
// 7. Return ? Invoke(promise, "then", « thenFinally, catchFinally »).
return UnsafeCast<JSAny>(
InvokeThen(nativeContext, receiver, thenFinally, catchFinally));
}
}
......@@ -49,9 +49,12 @@ extern enum NativeContextSlot extends intptr constexpr 'Context::Field' {
STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX,
PROMISE_CAPABILITY_DEFAULT_RESOLVE_SHARED_FUN_INDEX,
PROMISE_CAPABILITY_DEFAULT_REJECT_SHARED_FUN_INDEX,
PROMISE_CATCH_FINALLY_SHARED_FUN,
PROMISE_GET_CAPABILITIES_EXECUTOR_SHARED_FUN,
PROMISE_PROTOTYPE_INDEX,
PROMISE_THROWER_FINALLY_SHARED_FUN,
PROMISE_THEN_FINALLY_SHARED_FUN,
PROMISE_VALUE_THUNK_FINALLY_SHARED_FUN,
...
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment