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-gen.h' 6 7namespace runtime { 8extern transitioning runtime 9ResolvePromise(implicit context: Context)(JSPromise, JSAny): JSAny; 10} 11 12namespace promise { 13extern macro ConstructorStringConstant(): String; 14const kConstructorString: String = ConstructorStringConstant(); 15 16// https://tc39.es/ecma262/#sec-promise.resolve 17transitioning javascript builtin 18PromiseResolveTrampoline( 19 js-implicit context: NativeContext, receiver: JSAny)(value: JSAny): JSAny { 20 // 1. Let C be the this value. 21 // 2. If Type(C) is not Object, throw a TypeError exception. 22 const receiver = Cast<JSReceiver>(receiver) otherwise 23 ThrowTypeError(MessageTemplate::kCalledOnNonObject, 'PromiseResolve'); 24 25 // 3. Return ? PromiseResolve(C, x). 26 return PromiseResolve(receiver, value); 27} 28 29transitioning builtin 30PromiseResolve(implicit context: Context)( 31 constructor: JSReceiver, value: JSAny): JSAny { 32 const nativeContext = LoadNativeContext(context); 33 const promiseFun = *NativeContextSlot( 34 nativeContext, ContextSlot::PROMISE_FUNCTION_INDEX); 35 try { 36 // Check if {value} is a JSPromise. 37 const value = Cast<JSPromise>(value) otherwise NeedToAllocate; 38 39 // We can skip the "constructor" lookup on {value} if it's [[Prototype]] 40 // is the (initial) Promise.prototype and the @@species protector is 41 // intact, as that guards the lookup path for "constructor" on 42 // JSPromise instances which have the (initial) Promise.prototype. 43 const promisePrototype = 44 *NativeContextSlot( 45 nativeContext, ContextSlot::PROMISE_PROTOTYPE_INDEX); 46 // Check that Torque load elimination works. 47 static_assert(nativeContext == LoadNativeContext(context)); 48 if (value.map.prototype != promisePrototype) { 49 goto SlowConstructor; 50 } 51 52 if (IsPromiseSpeciesProtectorCellInvalid()) goto SlowConstructor; 53 54 // If the {constructor} is the Promise function, we just immediately 55 // return the {value} here and don't bother wrapping it into a 56 // native Promise. 57 if (promiseFun != constructor) goto SlowConstructor; 58 return value; 59 } label SlowConstructor deferred { 60 // At this point, value or/and constructor are not native promises, but 61 // they could be of the same subclass. 62 const valueConstructor = GetProperty(value, kConstructorString); 63 if (valueConstructor != constructor) goto NeedToAllocate; 64 return value; 65 } label NeedToAllocate { 66 if (promiseFun == constructor) { 67 // This adds a fast path for native promises that don't need to 68 // create NewPromiseCapability. 69 const result = NewJSPromise(); 70 ResolvePromise(context, result, value); 71 return result; 72 } else 73 deferred { 74 const capability = NewPromiseCapability(constructor, True); 75 const resolve = UnsafeCast<Callable>(capability.resolve); 76 Call(context, resolve, Undefined, value); 77 return capability.promise; 78 } 79 } 80} 81 82extern macro IsJSReceiverMap(Map): bool; 83 84extern macro IsPromiseThenProtectorCellInvalid(): bool; 85 86extern macro ThenStringConstant(): String; 87 88const kThenString: String = ThenStringConstant(); 89 90// https://tc39.es/ecma262/#sec-promise-resolve-functions 91transitioning builtin 92ResolvePromise(implicit context: Context)( 93 promise: JSPromise, resolution: JSAny): JSAny { 94 // 7. If SameValue(resolution, promise) is true, then 95 // If promise hook is enabled or the debugger is active, let 96 // the runtime handle this operation, which greatly reduces 97 // the complexity here and also avoids a couple of back and 98 // forth between JavaScript and C++ land. 99 // We also let the runtime handle it if promise == resolution. 100 // We can use pointer comparison here, since the {promise} is guaranteed 101 // to be a JSPromise inside this function and thus is reference comparable. 102 if (IsIsolatePromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate() || 103 TaggedEqual(promise, resolution)) 104 deferred { 105 return runtime::ResolvePromise(promise, resolution); 106 } 107 108 let then: Object = Undefined; 109 try { 110 // 8. If Type(resolution) is not Object, then 111 // 8.a Return FulfillPromise(promise, resolution). 112 if (TaggedIsSmi(resolution)) { 113 return FulfillPromise(promise, resolution); 114 } 115 116 const heapResolution = UnsafeCast<HeapObject>(resolution); 117 const resolutionMap = heapResolution.map; 118 if (!IsJSReceiverMap(resolutionMap)) { 119 return FulfillPromise(promise, resolution); 120 } 121 122 // We can skip the "then" lookup on {resolution} if its [[Prototype]] 123 // is the (initial) Promise.prototype and the Promise#then protector 124 // is intact, as that guards the lookup path for the "then" property 125 // on JSPromise instances which have the (initial) %PromisePrototype%. 126 if (IsForceSlowPath()) { 127 goto Slow; 128 } 129 130 if (IsPromiseThenProtectorCellInvalid()) { 131 goto Slow; 132 } 133 134 const nativeContext = LoadNativeContext(context); 135 if (!IsJSPromiseMap(resolutionMap)) { 136 // We can skip the lookup of "then" if the {resolution} is a (newly 137 // created) IterResultObject, as the Promise#then() protector also 138 // ensures that the intrinsic %ObjectPrototype% doesn't contain any 139 // "then" property. This helps to avoid negative lookups on iterator 140 // results from async generators. 141 dcheck(IsJSReceiverMap(resolutionMap)); 142 dcheck(!IsPromiseThenProtectorCellInvalid()); 143 if (resolutionMap == 144 *NativeContextSlot( 145 nativeContext, ContextSlot::ITERATOR_RESULT_MAP_INDEX)) { 146 return FulfillPromise(promise, resolution); 147 } else { 148 goto Slow; 149 } 150 } 151 152 const promisePrototype = 153 *NativeContextSlot( 154 nativeContext, ContextSlot::PROMISE_PROTOTYPE_INDEX); 155 if (resolutionMap.prototype == promisePrototype) { 156 // The {resolution} is a native Promise in this case. 157 then = *NativeContextSlot(nativeContext, ContextSlot::PROMISE_THEN_INDEX); 158 // Check that Torque load elimination works. 159 static_assert(nativeContext == LoadNativeContext(context)); 160 goto Enqueue; 161 } 162 goto Slow; 163 } label Slow deferred { 164 // 9. Let then be Get(resolution, "then"). 165 // 10. If then is an abrupt completion, then 166 try { 167 then = GetProperty(resolution, kThenString); 168 } catch (e, _message) { 169 // a. Return RejectPromise(promise, then.[[Value]]). 170 return RejectPromise(promise, e, False); 171 } 172 173 // 11. Let thenAction be then.[[Value]]. 174 // 12. If IsCallable(thenAction) is false, then 175 if (!Is<Callable>(then)) { 176 // a. Return FulfillPromise(promise, resolution). 177 return FulfillPromise(promise, resolution); 178 } 179 goto Enqueue; 180 } label Enqueue { 181 // 13. Let job be NewPromiseResolveThenableJob(promise, resolution, 182 // thenAction). 183 const task = NewPromiseResolveThenableJobTask( 184 promise, UnsafeCast<JSReceiver>(resolution), 185 UnsafeCast<Callable>(then)); 186 187 // 14. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]). 188 // 15. Return undefined. 189 return EnqueueMicrotask(task.context, task); 190 } 191} 192} 193