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 runtime { 9 extern transitioning runtime 10 AllowDynamicFunction(implicit context: Context)(JSAny): JSAny; 11} 12 13// Unsafe functions that should be used very carefully. 14namespace promise_internal { 15 extern macro PromiseBuiltinsAssembler::ZeroOutEmbedderOffsets(JSPromise): 16 void; 17 18 extern macro PromiseBuiltinsAssembler::AllocateJSPromise(Context): HeapObject; 19} 20 21namespace promise { 22 extern macro IsFunctionWithPrototypeSlotMap(Map): bool; 23 24 @export 25 macro PromiseHasHandler(promise: JSPromise): bool { 26 return promise.HasHandler(); 27 } 28 29 @export 30 macro PromiseInit(promise: JSPromise): void { 31 assert(PromiseState::kPending == 0); 32 promise.reactions_or_result = kZero; 33 promise.flags = 0; 34 promise_internal::ZeroOutEmbedderOffsets(promise); 35 } 36 37 macro InnerNewJSPromise(implicit context: Context)(): JSPromise { 38 const nativeContext = LoadNativeContext(context); 39 const promiseFun = UnsafeCast<JSFunction>( 40 nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX]); 41 assert(IsFunctionWithPrototypeSlotMap(promiseFun.map)); 42 const promiseMap = UnsafeCast<Map>(promiseFun.prototype_or_initial_map); 43 const promiseHeapObject = promise_internal::AllocateJSPromise(context); 44 * UnsafeConstCast(& promiseHeapObject.map) = promiseMap; 45 const promise = UnsafeCast<JSPromise>(promiseHeapObject); 46 promise.properties_or_hash = kEmptyFixedArray; 47 promise.elements = kEmptyFixedArray; 48 promise.reactions_or_result = kZero; 49 promise.flags = 0; 50 return promise; 51 } 52 53 macro NewPromiseFulfillReactionJobTask(implicit context: Context)( 54 handlerContext: Context, argument: Object, handler: Callable|Undefined, 55 promiseOrCapability: JSPromise|PromiseCapability| 56 Undefined): PromiseFulfillReactionJobTask { 57 const nativeContext = LoadNativeContext(handlerContext); 58 return new PromiseFulfillReactionJobTask{ 59 map: PromiseFulfillReactionJobTaskMapConstant(), 60 argument, 61 context: handlerContext, 62 handler, 63 promise_or_capability: promiseOrCapability, 64 continuation_preserved_embedder_data: nativeContext 65 [NativeContextSlot::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX] 66 }; 67 } 68 69 macro NewPromiseRejectReactionJobTask(implicit context: Context)( 70 handlerContext: Context, argument: Object, handler: Callable|Undefined, 71 promiseOrCapability: JSPromise|PromiseCapability| 72 Undefined): PromiseRejectReactionJobTask { 73 const nativeContext = LoadNativeContext(handlerContext); 74 return new PromiseRejectReactionJobTask{ 75 map: PromiseRejectReactionJobTaskMapConstant(), 76 argument, 77 context: handlerContext, 78 handler, 79 promise_or_capability: promiseOrCapability, 80 continuation_preserved_embedder_data: nativeContext 81 [NativeContextSlot::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX] 82 }; 83 } 84 85 // These allocate and initialize a promise with pending state and 86 // undefined fields. 87 // 88 // This uses the given parent as the parent promise for the promise 89 // init hook. 90 @export 91 transitioning macro NewJSPromise(implicit context: Context)(parent: Object): 92 JSPromise { 93 const instance = InnerNewJSPromise(); 94 PromiseInit(instance); 95 if (IsPromiseHookEnabledOrHasAsyncEventDelegate()) { 96 runtime::PromiseHookInit(instance, parent); 97 } 98 return instance; 99 } 100 101 // This uses undefined as the parent promise for the promise init 102 // hook. 103 @export 104 transitioning macro NewJSPromise(implicit context: Context)(): JSPromise { 105 return NewJSPromise(Undefined); 106 } 107 108 // This allocates and initializes a promise with the given state and 109 // fields. 110 @export 111 transitioning macro NewJSPromise(implicit context: Context)( 112 status: constexpr PromiseState, result: JSAny): JSPromise { 113 assert(status != PromiseState::kPending); 114 assert(kJSPromiseStatusShift == 0); 115 116 const instance = InnerNewJSPromise(); 117 instance.reactions_or_result = result; 118 instance.SetStatus(status); 119 promise_internal::ZeroOutEmbedderOffsets(instance); 120 121 if (IsPromiseHookEnabledOrHasAsyncEventDelegate()) { 122 runtime::PromiseHookInit(instance, Undefined); 123 } 124 return instance; 125 } 126 127 macro NewPromiseReaction(implicit context: Context)( 128 handlerContext: Context, next: Zero|PromiseReaction, 129 promiseOrCapability: JSPromise|PromiseCapability|Undefined, 130 fulfillHandler: Callable|Undefined, 131 rejectHandler: Callable|Undefined): PromiseReaction { 132 const nativeContext = LoadNativeContext(handlerContext); 133 return new PromiseReaction{ 134 map: PromiseReactionMapConstant(), 135 next: next, 136 reject_handler: rejectHandler, 137 fulfill_handler: fulfillHandler, 138 promise_or_capability: promiseOrCapability, 139 continuation_preserved_embedder_data: nativeContext 140 [NativeContextSlot::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX] 141 }; 142 } 143 144 extern macro PromiseResolveThenableJobTaskMapConstant(): Map; 145 146 macro NewPromiseResolveThenableJobTask(implicit context: Context)( 147 promiseToResolve: JSPromise, then: JSReceiver, thenable: JSReceiver, 148 thenContext: Context): PromiseResolveThenableJobTask { 149 return new PromiseResolveThenableJobTask{ 150 map: PromiseResolveThenableJobTaskMapConstant(), 151 context: thenContext, 152 promise_to_resolve: promiseToResolve, 153 then: then, 154 thenable: thenable 155 }; 156 } 157 158 struct InvokeThenOneArgFunctor { 159 transitioning 160 macro Call( 161 nativeContext: NativeContext, then: JSAny, receiver: JSAny, arg1: JSAny, 162 _arg2: JSAny): JSAny { 163 return Call(nativeContext, then, receiver, arg1); 164 } 165 } 166 167 struct InvokeThenTwoArgFunctor { 168 transitioning 169 macro Call( 170 nativeContext: NativeContext, then: JSAny, receiver: JSAny, arg1: JSAny, 171 arg2: JSAny): JSAny { 172 return Call(nativeContext, then, receiver, arg1, arg2); 173 } 174 } 175 176 transitioning 177 macro InvokeThen<F: type>(implicit context: Context)( 178 nativeContext: NativeContext, receiver: JSAny, arg1: JSAny, arg2: JSAny, 179 callFunctor: F): JSAny { 180 // We can skip the "then" lookup on {receiver} if it's [[Prototype]] 181 // is the (initial) Promise.prototype and the Promise#then protector 182 // is intact, as that guards the lookup path for the "then" property 183 // on JSPromise instances which have the (initial) %PromisePrototype%. 184 if (!Is<Smi>(receiver) && 185 IsPromiseThenLookupChainIntact( 186 nativeContext, UnsafeCast<HeapObject>(receiver).map)) { 187 const then = UnsafeCast<JSAny>( 188 nativeContext[NativeContextSlot::PROMISE_THEN_INDEX]); 189 return callFunctor.Call(nativeContext, then, receiver, arg1, arg2); 190 } else 191 deferred { 192 const then = UnsafeCast<JSAny>(GetProperty(receiver, kThenString)); 193 return callFunctor.Call(nativeContext, then, receiver, arg1, arg2); 194 } 195 } 196 197 transitioning 198 macro InvokeThen(implicit context: Context)( 199 nativeContext: NativeContext, receiver: JSAny, arg: JSAny): JSAny { 200 return InvokeThen( 201 nativeContext, receiver, arg, Undefined, InvokeThenOneArgFunctor{}); 202 } 203 204 transitioning 205 macro InvokeThen(implicit context: Context)( 206 nativeContext: NativeContext, receiver: JSAny, arg1: JSAny, 207 arg2: JSAny): JSAny { 208 return InvokeThen( 209 nativeContext, receiver, arg1, arg2, InvokeThenTwoArgFunctor{}); 210 } 211 212 transitioning 213 macro BranchIfAccessCheckFailed(implicit context: Context)( 214 nativeContext: NativeContext, promiseConstructor: JSAny, 215 executor: JSAny): void labels IfNoAccess { 216 try { 217 // If executor is a bound function, load the bound function until we've 218 // reached an actual function. 219 let foundExecutor = executor; 220 while (true) { 221 typeswitch (foundExecutor) { 222 case (f: JSFunction): { 223 // Load the context from the function and compare it to the Promise 224 // constructor's context. If they match, everything is fine, 225 // otherwise, bail out to the runtime. 226 const functionContext = f.context; 227 const nativeFunctionContext = LoadNativeContext(functionContext); 228 if (TaggedEqual(nativeContext, nativeFunctionContext)) { 229 goto HasAccess; 230 } else { 231 goto CallRuntime; 232 } 233 } 234 case (b: JSBoundFunction): { 235 foundExecutor = b.bound_target_function; 236 } 237 case (Object): { 238 goto CallRuntime; 239 } 240 } 241 } 242 } 243 label CallRuntime deferred { 244 const result = runtime::AllowDynamicFunction(promiseConstructor); 245 if (result != True) { 246 goto IfNoAccess; 247 } 248 } 249 label HasAccess {} 250 } 251} 252