blob: 32028b819dabdfeae6bc230d84ef0d8358394008 [file] [log] [blame]
// 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.h'
#include 'src/builtins/builtins-promise-gen.h'
namespace promise {
// TODO(joshualitt): The below ContextSlots are only available on synthetic
// contexts created by the promise pipeline for use in the promise pipeline.
// However, with Torque we should type the context and its slots to prevent
// accidentially using these slots on contexts which don't support them.
const kPromiseBuiltinsValueSlot: constexpr ContextSlot
generates 'PromiseBuiltins::kValueSlot';
const kPromiseBuiltinsOnFinallySlot: constexpr ContextSlot
generates 'PromiseBuiltins::kOnFinallySlot';
const kPromiseBuiltinsConstructorSlot: constexpr ContextSlot
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)():
JSAny {
return UnsafeCast<JSAny>(context[kPromiseBuiltinsValueSlot]);
}
transitioning javascript builtin
PromiseThrowerFinally(js-implicit context: Context, receiver: JSAny)():
never {
const reason = UnsafeCast<JSAny>(context[kPromiseBuiltinsValueSlot]);
Throw(reason);
}
macro CreateThrowerFunction(implicit context: Context)(
nativeContext: NativeContext, reason: JSAny): JSFunction {
const throwerContext = AllocateSyntheticFunctionContext(
nativeContext, kPromiseBuiltinsPromiseValueThunkOrReasonContextLength);
throwerContext[kPromiseBuiltinsValueSlot] = reason;
const map = UnsafeCast<Map>(
nativeContext
[NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]);
const throwerInfo = UnsafeCast<SharedFunctionInfo>(
nativeContext[NativeContextSlot::PROMISE_THROWER_FINALLY_SHARED_FUN]);
return AllocateFunctionWithMapAndContext(map, throwerInfo, throwerContext);
}
transitioning javascript builtin
PromiseCatchFinally(js-implicit context: Context, receiver: JSAny)(
reason: 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 thrower be equivalent to a function that throws reason.
const nativeContext = LoadNativeContext(context);
const thrower = CreateThrowerFunction(nativeContext, reason);
// 8. Return ? Invoke(promise, "then", « thrower »).
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));
}
}