blob: bdeebb086bbb6797b07b32262aa0bee03d7ddd6c [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 {
type PromiseValueThunkOrReasonContext extends FunctionContext;
extern enum PromiseValueThunkOrReasonContextSlot extends intptr
constexpr 'PromiseBuiltins::PromiseValueThunkOrReasonContextSlot' {
kValueSlot: Slot<PromiseValueThunkOrReasonContext, JSAny>,
kPromiseValueThunkOrReasonContextLength
}
type PromiseFinallyContext extends FunctionContext;
extern enum PromiseFinallyContextSlot extends intptr
constexpr 'PromiseBuiltins::PromiseFinallyContextSlot' {
kOnFinallySlot: Slot<PromiseFinallyContext, Callable>,
kConstructorSlot: Slot<PromiseFinallyContext, Constructor>,
kPromiseFinallyContextLength
}
transitioning javascript builtin PromiseValueThunkFinally(
js-implicit context: Context, receiver: JSAny)(): JSAny {
const context = %RawDownCast<PromiseValueThunkOrReasonContext>(context);
return *ContextSlot(
context, PromiseValueThunkOrReasonContextSlot::kValueSlot);
}
transitioning javascript builtin PromiseThrowerFinally(
js-implicit context: Context, receiver: JSAny)(): never {
const context = %RawDownCast<PromiseValueThunkOrReasonContext>(context);
const reason =
*ContextSlot(context, PromiseValueThunkOrReasonContextSlot::kValueSlot);
Throw(reason);
}
const kPromiseThrowerFinallySharedFun: constexpr intptr
generates 'RootIndex::kPromiseThrowerFinallySharedFun';
macro CreateThrowerFunction(
implicit context: Context)(nativeContext: NativeContext,
reason: JSAny): JSFunction {
const throwerContext = %RawDownCast<PromiseValueThunkOrReasonContext>(
AllocateSyntheticFunctionContext(
nativeContext,
PromiseValueThunkOrReasonContextSlot::
kPromiseValueThunkOrReasonContextLength));
InitContextSlot(
throwerContext, PromiseValueThunkOrReasonContextSlot::kValueSlot, reason);
return AllocateRootFunctionWithContext(
kPromiseThrowerFinallySharedFun, throwerContext, nativeContext);
}
transitioning javascript builtin PromiseCatchFinally(
js-implicit context: Context, receiver: JSAny)(reason: JSAny): JSAny {
const context = %RawDownCast<PromiseFinallyContext>(context);
// 1. Let onFinally be F.[[OnFinally]].
// 2. Assert: IsCallable(onFinally) is true.
const onFinally: Callable =
*ContextSlot(context, PromiseFinallyContextSlot::kOnFinallySlot);
// 3. Let result be ? Call(onFinally).
const result = Call(context, onFinally, Undefined);
// 4. Let C be F.[[Constructor]].
const constructor: Constructor =
*ContextSlot(context, PromiseFinallyContextSlot::kConstructorSlot);
// 5. Assert: IsConstructor(C) is true.
dcheck(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));
}
const kPromiseValueThunkFinallySharedFun: constexpr intptr
generates 'RootIndex::kPromiseValueThunkFinallySharedFun';
macro CreateValueThunkFunction(
implicit context: Context)(nativeContext: NativeContext,
value: JSAny): JSFunction {
const valueThunkContext = %RawDownCast<PromiseValueThunkOrReasonContext>(
AllocateSyntheticFunctionContext(
nativeContext,
PromiseValueThunkOrReasonContextSlot::
kPromiseValueThunkOrReasonContextLength));
InitContextSlot(
valueThunkContext, PromiseValueThunkOrReasonContextSlot::kValueSlot,
value);
return AllocateRootFunctionWithContext(
kPromiseValueThunkFinallySharedFun, valueThunkContext, nativeContext);
}
transitioning javascript builtin PromiseThenFinally(
js-implicit context: Context, receiver: JSAny)(value: JSAny): JSAny {
const context = %RawDownCast<PromiseFinallyContext>(context);
// 1. Let onFinally be F.[[OnFinally]].
// 2. Assert: IsCallable(onFinally) is true.
const onFinally =
*ContextSlot(context, PromiseFinallyContextSlot::kOnFinallySlot);
// 3. Let result be ? Call(onFinally).
const result = Call(context, onFinally, Undefined);
// 4. Let C be F.[[Constructor]].
const constructor =
*ContextSlot(context, PromiseFinallyContextSlot::kConstructorSlot);
// 5. Assert: IsConstructor(C) is true.
dcheck(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;
}
const kPromiseThenFinallySharedFun:
constexpr intptr generates 'RootIndex::kPromiseThenFinallySharedFun';
const kPromiseCatchFinallySharedFun: constexpr intptr
generates 'RootIndex::kPromiseCatchFinallySharedFun';
macro CreatePromiseFinallyFunctions(
implicit context: Context)(nativeContext: NativeContext,
onFinally: Callable, constructor: Constructor): PromiseFinallyFunctions {
const promiseContext =
%RawDownCast<PromiseFinallyContext>(AllocateSyntheticFunctionContext(
nativeContext,
PromiseFinallyContextSlot::kPromiseFinallyContextLength));
InitContextSlot(
promiseContext, PromiseFinallyContextSlot::kOnFinallySlot, onFinally);
InitContextSlot(
promiseContext, PromiseFinallyContextSlot::kConstructorSlot, constructor);
const thenFinally = AllocateRootFunctionWithContext(
kPromiseThenFinallySharedFun, promiseContext, nativeContext);
const catchFinally = AllocateRootFunctionWithContext(
kPromiseCatchFinallySharedFun, promiseContext, nativeContext);
return PromiseFinallyFunctions{
then_finally: thenFinally,
catch_finally: catchFinally
};
}
// https://tc39.es/ecma262/#sec-promise.prototype.finally
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%).
// This builtin is attached to JSFunction created by the bootstrapper so
// `context` is the native context.
check(Is<NativeContext>(context));
const nativeContext = UnsafeCast<NativeContext>(context);
const promiseFun = *NativeContextSlot(ContextSlot::PROMISE_FUNCTION_INDEX);
let constructor: Constructor = UnsafeCast<Constructor>(promiseFun);
const receiverMap = jsReceiver.map;
if (!IsJSPromiseMap(receiverMap) ||
!IsPromiseSpeciesLookupChainIntact(nativeContext, receiverMap))
deferred {
constructor =
UnsafeCast<Constructor>(SpeciesConstructor(jsReceiver, promiseFun));
}
// 4. Assert: IsConstructor(C) is true.
dcheck(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;
typeswitch (onFinally) {
case (onFinally: Callable): {
const pair =
CreatePromiseFinallyFunctions(nativeContext, onFinally, constructor);
thenFinally = pair.then_finally;
catchFinally = pair.catch_finally;
}
case (JSAny): deferred {
thenFinally = onFinally;
catchFinally = onFinally;
}
}
// 7. Return ? Invoke(promise, "then", « thenFinally, catchFinally »).
return UnsafeCast<JSAny>(
InvokeThen(nativeContext, receiver, thenFinally, catchFinally));
}
extern macro PromiseCatchFinallySharedFunConstant(): SharedFunctionInfo;
extern macro PromiseThenFinallySharedFunConstant(): SharedFunctionInfo;
extern macro PromiseThrowerFinallySharedFunConstant(): SharedFunctionInfo;
extern macro PromiseValueThunkFinallySharedFunConstant(): SharedFunctionInfo;
}