1// Copyright 2019 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include 'src/builtins/builtins-promise.h' 6#include 'src/builtins/builtins-promise-gen.h' 7 8namespace promise { 9 10type PromiseValueThunkOrReasonContext extends FunctionContext; 11extern enum PromiseValueThunkOrReasonContextSlot extends intptr 12constexpr 'PromiseBuiltins::PromiseValueThunkOrReasonContextSlot' { 13 kValueSlot: Slot<PromiseValueThunkOrReasonContext, JSAny>, 14 kPromiseValueThunkOrReasonContextLength 15} 16 17type PromiseFinallyContext extends FunctionContext; 18extern enum PromiseFinallyContextSlot extends intptr 19constexpr 'PromiseBuiltins::PromiseFinallyContextSlot' { 20 kOnFinallySlot: Slot<PromiseFinallyContext, Callable>, 21 kConstructorSlot: Slot<PromiseFinallyContext, Constructor>, 22 kPromiseFinallyContextLength 23} 24 25transitioning javascript builtin 26PromiseValueThunkFinally( 27 js-implicit context: Context, receiver: JSAny)(): JSAny { 28 const context = %RawDownCast<PromiseValueThunkOrReasonContext>(context); 29 return *ContextSlot( 30 context, PromiseValueThunkOrReasonContextSlot::kValueSlot); 31} 32 33transitioning javascript builtin 34PromiseThrowerFinally(js-implicit context: Context, receiver: JSAny)(): never { 35 const context = %RawDownCast<PromiseValueThunkOrReasonContext>(context); 36 const reason = 37 *ContextSlot(context, PromiseValueThunkOrReasonContextSlot::kValueSlot); 38 Throw(reason); 39} 40 41macro CreateThrowerFunction(implicit context: Context)( 42 nativeContext: NativeContext, reason: JSAny): JSFunction { 43 const throwerContext = %RawDownCast<PromiseValueThunkOrReasonContext>( 44 AllocateSyntheticFunctionContext( 45 nativeContext, 46 PromiseValueThunkOrReasonContextSlot:: 47 kPromiseValueThunkOrReasonContextLength)); 48 InitContextSlot( 49 throwerContext, PromiseValueThunkOrReasonContextSlot::kValueSlot, reason); 50 const map = *ContextSlot( 51 nativeContext, ContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); 52 const throwerInfo = PromiseThrowerFinallySharedFunConstant(); 53 return AllocateFunctionWithMapAndContext(map, throwerInfo, throwerContext); 54} 55 56transitioning javascript builtin 57PromiseCatchFinally( 58 js-implicit context: Context, receiver: JSAny)(reason: JSAny): JSAny { 59 const context = %RawDownCast<PromiseFinallyContext>(context); 60 // 1. Let onFinally be F.[[OnFinally]]. 61 // 2. Assert: IsCallable(onFinally) is true. 62 const onFinally: Callable = 63 *ContextSlot(context, PromiseFinallyContextSlot::kOnFinallySlot); 64 65 // 3. Let result be ? Call(onFinally). 66 const result = Call(context, onFinally, Undefined); 67 68 // 4. Let C be F.[[Constructor]]. 69 const constructor: Constructor = 70 *ContextSlot(context, PromiseFinallyContextSlot::kConstructorSlot); 71 72 // 5. Assert: IsConstructor(C) is true. 73 dcheck(IsConstructor(constructor)); 74 75 // 6. Let promise be ? PromiseResolve(C, result). 76 const promise = PromiseResolve(constructor, result); 77 78 // 7. Let thrower be equivalent to a function that throws reason. 79 const nativeContext = LoadNativeContext(context); 80 const thrower = CreateThrowerFunction(nativeContext, reason); 81 82 // 8. Return ? Invoke(promise, "then", « thrower »). 83 return UnsafeCast<JSAny>(InvokeThen(nativeContext, promise, thrower)); 84} 85 86macro CreateValueThunkFunction(implicit context: Context)( 87 nativeContext: NativeContext, value: JSAny): JSFunction { 88 const valueThunkContext = %RawDownCast<PromiseValueThunkOrReasonContext>( 89 AllocateSyntheticFunctionContext( 90 nativeContext, 91 PromiseValueThunkOrReasonContextSlot:: 92 kPromiseValueThunkOrReasonContextLength)); 93 InitContextSlot( 94 valueThunkContext, PromiseValueThunkOrReasonContextSlot::kValueSlot, 95 value); 96 const map = *ContextSlot( 97 nativeContext, ContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); 98 const valueThunkInfo = PromiseValueThunkFinallySharedFunConstant(); 99 return AllocateFunctionWithMapAndContext( 100 map, valueThunkInfo, valueThunkContext); 101} 102 103transitioning javascript builtin 104PromiseThenFinally( 105 js-implicit context: Context, receiver: JSAny)(value: JSAny): JSAny { 106 const context = %RawDownCast<PromiseFinallyContext>(context); 107 // 1. Let onFinally be F.[[OnFinally]]. 108 // 2. Assert: IsCallable(onFinally) is true. 109 const onFinally = 110 *ContextSlot(context, PromiseFinallyContextSlot::kOnFinallySlot); 111 112 // 3. Let result be ? Call(onFinally). 113 const result = Call(context, onFinally, Undefined); 114 115 // 4. Let C be F.[[Constructor]]. 116 const constructor = 117 *ContextSlot(context, PromiseFinallyContextSlot::kConstructorSlot); 118 119 // 5. Assert: IsConstructor(C) is true. 120 dcheck(IsConstructor(constructor)); 121 122 // 6. Let promise be ? PromiseResolve(C, result). 123 const promise = PromiseResolve(constructor, result); 124 125 // 7. Let valueThunk be equivalent to a function that returns value. 126 const nativeContext = LoadNativeContext(context); 127 const valueThunk = CreateValueThunkFunction(nativeContext, value); 128 129 // 8. Return ? Invoke(promise, "then", « valueThunk »). 130 return UnsafeCast<JSAny>(InvokeThen(nativeContext, promise, valueThunk)); 131} 132 133struct PromiseFinallyFunctions { 134 then_finally: JSFunction; 135 catch_finally: JSFunction; 136} 137 138macro CreatePromiseFinallyFunctions(implicit context: Context)( 139 nativeContext: NativeContext, onFinally: Callable, 140 constructor: Constructor): PromiseFinallyFunctions { 141 const promiseContext = 142 %RawDownCast<PromiseFinallyContext>(AllocateSyntheticFunctionContext( 143 nativeContext, 144 PromiseFinallyContextSlot::kPromiseFinallyContextLength)); 145 InitContextSlot( 146 promiseContext, PromiseFinallyContextSlot::kOnFinallySlot, onFinally); 147 InitContextSlot( 148 promiseContext, PromiseFinallyContextSlot::kConstructorSlot, constructor); 149 const map = *ContextSlot( 150 nativeContext, ContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); 151 const thenFinallyInfo = PromiseThenFinallySharedFunConstant(); 152 const thenFinally = 153 AllocateFunctionWithMapAndContext(map, thenFinallyInfo, promiseContext); 154 const catchFinallyInfo = PromiseCatchFinallySharedFunConstant(); 155 const catchFinally = 156 AllocateFunctionWithMapAndContext(map, catchFinallyInfo, promiseContext); 157 return PromiseFinallyFunctions{ 158 then_finally: thenFinally, 159 catch_finally: catchFinally 160 }; 161} 162 163// https://tc39.es/ecma262/#sec-promise.prototype.finally 164transitioning javascript builtin 165PromisePrototypeFinally( 166 js-implicit context: Context, receiver: JSAny)(onFinally: JSAny): JSAny { 167 // 1. Let promise be the this value. 168 // 2. If Type(promise) is not Object, throw a TypeError exception. 169 const jsReceiver = Cast<JSReceiver>(receiver) otherwise ThrowTypeError( 170 MessageTemplate::kCalledOnNonObject, 'Promise.prototype.finally'); 171 172 // 3. Let C be ? SpeciesConstructor(promise, %Promise%). 173 // This builtin is attached to JSFunction created by the bootstrapper so 174 // `context` is the native context. 175 check(Is<NativeContext>(context)); 176 const nativeContext = UnsafeCast<NativeContext>(context); 177 const promiseFun = *NativeContextSlot(ContextSlot::PROMISE_FUNCTION_INDEX); 178 179 let constructor: Constructor = UnsafeCast<Constructor>(promiseFun); 180 const receiverMap = jsReceiver.map; 181 if (!IsJSPromiseMap(receiverMap) || 182 !IsPromiseSpeciesLookupChainIntact(nativeContext, receiverMap)) 183 deferred { 184 constructor = 185 UnsafeCast<Constructor>(SpeciesConstructor(jsReceiver, promiseFun)); 186 } 187 188 // 4. Assert: IsConstructor(C) is true. 189 dcheck(IsConstructor(constructor)); 190 191 // 5. If IsCallable(onFinally) is not true, 192 // a. Let thenFinally be onFinally. 193 // b. Let catchFinally be onFinally. 194 // 6. Else, 195 // a. Let thenFinally be a new built-in function object as defined 196 // in ThenFinally Function. 197 // b. Let catchFinally be a new built-in function object as 198 // defined in CatchFinally Function. 199 // c. Set thenFinally and catchFinally's [[Constructor]] internal 200 // slots to C. 201 // d. Set thenFinally and catchFinally's [[OnFinally]] internal 202 // slots to onFinally. 203 let thenFinally: JSAny; 204 let catchFinally: JSAny; 205 typeswitch (onFinally) { 206 case (onFinally: Callable): { 207 const pair = 208 CreatePromiseFinallyFunctions(nativeContext, onFinally, constructor); 209 thenFinally = pair.then_finally; 210 catchFinally = pair.catch_finally; 211 } 212 case (JSAny): deferred { 213 thenFinally = onFinally; 214 catchFinally = onFinally; 215 } 216 } 217 218 // 7. Return ? Invoke(promise, "then", « thenFinally, catchFinally »). 219 return UnsafeCast<JSAny>( 220 InvokeThen(nativeContext, receiver, thenFinally, catchFinally)); 221} 222 223extern macro PromiseCatchFinallySharedFunConstant(): SharedFunctionInfo; 224extern macro PromiseThenFinallySharedFunConstant(): SharedFunctionInfo; 225extern macro PromiseThrowerFinallySharedFunConstant(): SharedFunctionInfo; 226extern macro PromiseValueThunkFinallySharedFunConstant(): SharedFunctionInfo; 227} 228