| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 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-constructor.h" | |
| 7 #include "src/builtins/builtins-utils.h" | |
| 8 #include "src/builtins/builtins.h" | |
| 9 #include "src/code-factory.h" | |
| 10 #include "src/code-stub-assembler.h" | |
| 11 #include "src/objects-inl.h" | |
| 12 | |
| 13 namespace v8 { | |
| 14 namespace internal { | |
| 15 | |
| 16 Node* PromiseBuiltinsAssembler::AllocateJSPromise(Node* context) { | |
| 17 Node* const native_context = LoadNativeContext(context); | |
| 18 Node* const promise_fun = | |
| 19 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | |
| 20 Node* const initial_map = | |
| 21 LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset); | |
| 22 Node* const instance = AllocateJSObjectFromMap(initial_map); | |
| 23 return instance; | |
| 24 } | |
| 25 | |
| 26 void PromiseBuiltinsAssembler::PromiseInit(Node* promise) { | |
| 27 StoreObjectField(promise, JSPromise::kStatusOffset, | |
| 28 SmiConstant(v8::Promise::kPending)); | |
| 29 StoreObjectField(promise, JSPromise::kFlagsOffset, SmiConstant(0)); | |
| 30 } | |
| 31 | |
| 32 Node* PromiseBuiltinsAssembler::AllocateAndInitJSPromise(Node* context) { | |
| 33 return AllocateAndInitJSPromise(context, UndefinedConstant()); | |
| 34 } | |
| 35 | |
| 36 Node* PromiseBuiltinsAssembler::AllocateAndInitJSPromise(Node* context, | |
| 37 Node* parent) { | |
| 38 Node* const instance = AllocateJSPromise(context); | |
| 39 PromiseInit(instance); | |
| 40 | |
| 41 Label out(this); | |
| 42 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &out); | |
| 43 CallRuntime(Runtime::kPromiseHookInit, context, instance, parent); | |
| 44 Goto(&out); | |
| 45 | |
| 46 Bind(&out); | |
| 47 return instance; | |
| 48 } | |
| 49 | |
| 50 Node* PromiseBuiltinsAssembler::AllocateAndSetJSPromise(Node* context, | |
| 51 Node* status, | |
| 52 Node* result) { | |
| 53 CSA_ASSERT(this, TaggedIsSmi(status)); | |
| 54 | |
| 55 Node* const instance = AllocateJSPromise(context); | |
| 56 | |
| 57 StoreObjectFieldNoWriteBarrier(instance, JSPromise::kStatusOffset, status); | |
| 58 StoreObjectFieldNoWriteBarrier(instance, JSPromise::kResultOffset, result); | |
| 59 StoreObjectFieldNoWriteBarrier(instance, JSPromise::kFlagsOffset, | |
| 60 SmiConstant(0)); | |
| 61 | |
| 62 Label out(this); | |
| 63 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &out); | |
| 64 CallRuntime(Runtime::kPromiseHookInit, context, instance, | |
| 65 UndefinedConstant()); | |
| 66 Goto(&out); | |
| 67 | |
| 68 Bind(&out); | |
| 69 return instance; | |
| 70 } | |
| 71 | |
| 72 std::pair<Node*, Node*> | |
| 73 PromiseBuiltinsAssembler::CreatePromiseResolvingFunctions( | |
| 74 Node* promise, Node* debug_event, Node* native_context) { | |
| 75 Node* const promise_context = CreatePromiseResolvingFunctionsContext( | |
| 76 promise, debug_event, native_context); | |
| 77 Node* const map = LoadContextElement( | |
| 78 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); | |
| 79 Node* const resolve_info = | |
| 80 LoadContextElement(native_context, Context::PROMISE_RESOLVE_SHARED_FUN); | |
| 81 Node* const resolve = | |
| 82 AllocateFunctionWithMapAndContext(map, resolve_info, promise_context); | |
| 83 Node* const reject_info = | |
| 84 LoadContextElement(native_context, Context::PROMISE_REJECT_SHARED_FUN); | |
| 85 Node* const reject = | |
| 86 AllocateFunctionWithMapAndContext(map, reject_info, promise_context); | |
| 87 return std::make_pair(resolve, reject); | |
| 88 } | |
| 89 | |
| 90 Node* PromiseBuiltinsAssembler::NewPromiseCapability(Node* context, | |
| 91 Node* constructor, | |
| 92 Node* debug_event) { | |
| 93 if (debug_event == nullptr) { | |
| 94 debug_event = TrueConstant(); | |
| 95 } | |
| 96 | |
| 97 Node* native_context = LoadNativeContext(context); | |
| 98 | |
| 99 Node* map = LoadRoot(Heap::kJSPromiseCapabilityMapRootIndex); | |
| 100 Node* capability = AllocateJSObjectFromMap(map); | |
| 101 | |
| 102 StoreObjectFieldNoWriteBarrier( | |
| 103 capability, JSPromiseCapability::kPromiseOffset, UndefinedConstant()); | |
| 104 StoreObjectFieldNoWriteBarrier( | |
| 105 capability, JSPromiseCapability::kResolveOffset, UndefinedConstant()); | |
| 106 StoreObjectFieldNoWriteBarrier(capability, JSPromiseCapability::kRejectOffset, | |
| 107 UndefinedConstant()); | |
| 108 | |
| 109 Variable var_result(this, MachineRepresentation::kTagged); | |
| 110 var_result.Bind(capability); | |
| 111 | |
| 112 Label if_builtin_promise(this), if_custom_promise(this, Label::kDeferred), | |
| 113 out(this); | |
| 114 Branch(WordEqual(constructor, | |
| 115 LoadContextElement(native_context, | |
| 116 Context::PROMISE_FUNCTION_INDEX)), | |
| 117 &if_builtin_promise, &if_custom_promise); | |
| 118 | |
| 119 Bind(&if_builtin_promise); | |
| 120 { | |
| 121 Node* promise = AllocateJSPromise(context); | |
| 122 PromiseInit(promise); | |
| 123 StoreObjectFieldNoWriteBarrier( | |
| 124 capability, JSPromiseCapability::kPromiseOffset, promise); | |
| 125 | |
| 126 Node* resolve = nullptr; | |
| 127 Node* reject = nullptr; | |
| 128 | |
| 129 std::tie(resolve, reject) = | |
| 130 CreatePromiseResolvingFunctions(promise, debug_event, native_context); | |
| 131 StoreObjectField(capability, JSPromiseCapability::kResolveOffset, resolve); | |
| 132 StoreObjectField(capability, JSPromiseCapability::kRejectOffset, reject); | |
| 133 | |
| 134 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &out); | |
| 135 CallRuntime(Runtime::kPromiseHookInit, context, promise, | |
| 136 UndefinedConstant()); | |
| 137 Goto(&out); | |
| 138 } | |
| 139 | |
| 140 Bind(&if_custom_promise); | |
| 141 { | |
| 142 Label if_notcallable(this, Label::kDeferred); | |
| 143 Node* executor_context = | |
| 144 CreatePromiseGetCapabilitiesExecutorContext(capability, native_context); | |
| 145 Node* executor_info = LoadContextElement( | |
| 146 native_context, Context::PROMISE_GET_CAPABILITIES_EXECUTOR_SHARED_FUN); | |
| 147 Node* function_map = LoadContextElement( | |
| 148 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); | |
| 149 Node* executor = AllocateFunctionWithMapAndContext( | |
| 150 function_map, executor_info, executor_context); | |
| 151 | |
| 152 Node* promise = ConstructJS(CodeFactory::Construct(isolate()), context, | |
| 153 constructor, executor); | |
| 154 | |
| 155 Node* resolve = | |
| 156 LoadObjectField(capability, JSPromiseCapability::kResolveOffset); | |
| 157 GotoIf(TaggedIsSmi(resolve), &if_notcallable); | |
| 158 GotoIfNot(IsCallableMap(LoadMap(resolve)), &if_notcallable); | |
| 159 | |
| 160 Node* reject = | |
| 161 LoadObjectField(capability, JSPromiseCapability::kRejectOffset); | |
| 162 GotoIf(TaggedIsSmi(reject), &if_notcallable); | |
| 163 GotoIfNot(IsCallableMap(LoadMap(reject)), &if_notcallable); | |
| 164 | |
| 165 StoreObjectField(capability, JSPromiseCapability::kPromiseOffset, promise); | |
| 166 | |
| 167 Goto(&out); | |
| 168 | |
| 169 Bind(&if_notcallable); | |
| 170 Node* message = SmiConstant(MessageTemplate::kPromiseNonCallable); | |
| 171 StoreObjectField(capability, JSPromiseCapability::kPromiseOffset, | |
| 172 UndefinedConstant()); | |
| 173 StoreObjectField(capability, JSPromiseCapability::kResolveOffset, | |
| 174 UndefinedConstant()); | |
| 175 StoreObjectField(capability, JSPromiseCapability::kRejectOffset, | |
| 176 UndefinedConstant()); | |
| 177 CallRuntime(Runtime::kThrowTypeError, context, message); | |
| 178 Unreachable(); | |
| 179 } | |
| 180 | |
| 181 Bind(&out); | |
| 182 return var_result.value(); | |
| 183 } | |
| 184 | |
| 185 Node* PromiseBuiltinsAssembler::CreatePromiseContext(Node* native_context, | |
| 186 int slots) { | |
| 187 DCHECK_GE(slots, Context::MIN_CONTEXT_SLOTS); | |
| 188 | |
| 189 Node* const context = Allocate(FixedArray::SizeFor(slots)); | |
| 190 StoreMapNoWriteBarrier(context, Heap::kFunctionContextMapRootIndex); | |
| 191 StoreObjectFieldNoWriteBarrier(context, FixedArray::kLengthOffset, | |
| 192 SmiConstant(slots)); | |
| 193 | |
| 194 Node* const empty_fn = | |
| 195 LoadContextElement(native_context, Context::CLOSURE_INDEX); | |
| 196 StoreContextElementNoWriteBarrier(context, Context::CLOSURE_INDEX, empty_fn); | |
| 197 StoreContextElementNoWriteBarrier(context, Context::PREVIOUS_INDEX, | |
| 198 UndefinedConstant()); | |
| 199 StoreContextElementNoWriteBarrier(context, Context::EXTENSION_INDEX, | |
| 200 TheHoleConstant()); | |
| 201 StoreContextElementNoWriteBarrier(context, Context::NATIVE_CONTEXT_INDEX, | |
| 202 native_context); | |
| 203 return context; | |
| 204 } | |
| 205 | |
| 206 Node* PromiseBuiltinsAssembler::CreatePromiseResolvingFunctionsContext( | |
| 207 Node* promise, Node* debug_event, Node* native_context) { | |
| 208 Node* const context = | |
| 209 CreatePromiseContext(native_context, kPromiseContextLength); | |
| 210 StoreContextElementNoWriteBarrier(context, kAlreadyVisitedSlot, | |
| 211 SmiConstant(0)); | |
| 212 StoreContextElementNoWriteBarrier(context, kPromiseSlot, promise); | |
| 213 StoreContextElementNoWriteBarrier(context, kDebugEventSlot, debug_event); | |
| 214 return context; | |
| 215 } | |
| 216 | |
| 217 Node* PromiseBuiltinsAssembler::CreatePromiseGetCapabilitiesExecutorContext( | |
| 218 Node* promise_capability, Node* native_context) { | |
| 219 int kContextLength = kCapabilitiesContextLength; | |
| 220 Node* context = CreatePromiseContext(native_context, kContextLength); | |
| 221 StoreContextElementNoWriteBarrier(context, kCapabilitySlot, | |
| 222 promise_capability); | |
| 223 return context; | |
| 224 } | |
| 225 | |
| 226 Node* PromiseBuiltinsAssembler::ThrowIfNotJSReceiver( | |
| 227 Node* context, Node* value, MessageTemplate::Template msg_template, | |
| 228 const char* method_name) { | |
| 229 Label out(this), throw_exception(this, Label::kDeferred); | |
| 230 Variable var_value_map(this, MachineRepresentation::kTagged); | |
| 231 | |
| 232 GotoIf(TaggedIsSmi(value), &throw_exception); | |
| 233 | |
| 234 // Load the instance type of the {value}. | |
| 235 var_value_map.Bind(LoadMap(value)); | |
| 236 Node* const value_instance_type = LoadMapInstanceType(var_value_map.value()); | |
| 237 | |
| 238 Branch(IsJSReceiverInstanceType(value_instance_type), &out, &throw_exception); | |
| 239 | |
| 240 // The {value} is not a compatible receiver for this method. | |
| 241 Bind(&throw_exception); | |
| 242 { | |
| 243 Node* const method = | |
| 244 method_name == nullptr | |
| 245 ? UndefinedConstant() | |
| 246 : HeapConstant( | |
| 247 isolate()->factory()->NewStringFromAsciiChecked(method_name)); | |
| 248 Node* const message_id = SmiConstant(msg_template); | |
| 249 CallRuntime(Runtime::kThrowTypeError, context, message_id, method); | |
| 250 Unreachable(); | |
| 251 } | |
| 252 | |
| 253 Bind(&out); | |
| 254 return var_value_map.value(); | |
| 255 } | |
| 256 | |
| 257 Node* PromiseBuiltinsAssembler::PromiseHasHandler(Node* promise) { | |
| 258 Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset); | |
| 259 return IsSetWord(SmiUntag(flags), 1 << JSPromise::kHasHandlerBit); | |
| 260 } | |
| 261 | |
| 262 void PromiseBuiltinsAssembler::PromiseSetHasHandler(Node* promise) { | |
| 263 Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset); | |
| 264 Node* const new_flags = | |
| 265 SmiOr(flags, SmiConstant(1 << JSPromise::kHasHandlerBit)); | |
| 266 StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset, new_flags); | |
| 267 } | |
| 268 | |
| 269 void PromiseBuiltinsAssembler::PromiseSetHandledHint(Node* promise) { | |
| 270 Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset); | |
| 271 Node* const new_flags = | |
| 272 SmiOr(flags, SmiConstant(1 << JSPromise::kHandledHintBit)); | |
| 273 StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset, new_flags); | |
| 274 } | |
| 275 | |
| 276 Node* PromiseBuiltinsAssembler::SpeciesConstructor(Node* context, Node* object, | |
| 277 Node* default_constructor) { | |
| 278 Isolate* isolate = this->isolate(); | |
| 279 Variable var_result(this, MachineRepresentation::kTagged); | |
| 280 var_result.Bind(default_constructor); | |
| 281 | |
| 282 // 2. Let C be ? Get(O, "constructor"). | |
| 283 Node* const constructor = | |
| 284 GetProperty(context, object, isolate->factory()->constructor_string()); | |
| 285 | |
| 286 // 3. If C is undefined, return defaultConstructor. | |
| 287 Label out(this); | |
| 288 GotoIf(IsUndefined(constructor), &out); | |
| 289 | |
| 290 // 4. If Type(C) is not Object, throw a TypeError exception. | |
| 291 ThrowIfNotJSReceiver(context, constructor, | |
| 292 MessageTemplate::kConstructorNotReceiver); | |
| 293 | |
| 294 // 5. Let S be ? Get(C, @@species). | |
| 295 Node* const species = | |
| 296 GetProperty(context, constructor, isolate->factory()->species_symbol()); | |
| 297 | |
| 298 // 6. If S is either undefined or null, return defaultConstructor. | |
| 299 GotoIf(IsUndefined(species), &out); | |
| 300 GotoIf(WordEqual(species, NullConstant()), &out); | |
| 301 | |
| 302 // 7. If IsConstructor(S) is true, return S. | |
| 303 Label throw_error(this); | |
| 304 Node* species_bitfield = LoadMapBitField(LoadMap(species)); | |
| 305 GotoIfNot(Word32Equal(Word32And(species_bitfield, | |
| 306 Int32Constant((1 << Map::kIsConstructor))), | |
| 307 Int32Constant(1 << Map::kIsConstructor)), | |
| 308 &throw_error); | |
| 309 var_result.Bind(species); | |
| 310 Goto(&out); | |
| 311 | |
| 312 // 8. Throw a TypeError exception. | |
| 313 Bind(&throw_error); | |
| 314 { | |
| 315 Node* const message_id = | |
| 316 SmiConstant(MessageTemplate::kSpeciesNotConstructor); | |
| 317 CallRuntime(Runtime::kThrowTypeError, context, message_id); | |
| 318 Unreachable(); | |
| 319 } | |
| 320 | |
| 321 Bind(&out); | |
| 322 return var_result.value(); | |
| 323 } | |
| 324 | |
| 325 void PromiseBuiltinsAssembler::AppendPromiseCallback(int offset, Node* promise, | |
| 326 Node* value) { | |
| 327 Node* elements = LoadObjectField(promise, offset); | |
| 328 Node* length = LoadFixedArrayBaseLength(elements); | |
| 329 CodeStubAssembler::ParameterMode mode = OptimalParameterMode(); | |
| 330 length = TaggedToParameter(length, mode); | |
| 331 | |
| 332 Node* delta = IntPtrOrSmiConstant(1, mode); | |
| 333 Node* new_capacity = IntPtrOrSmiAdd(length, delta, mode); | |
| 334 | |
| 335 const ElementsKind kind = FAST_ELEMENTS; | |
| 336 const WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER; | |
| 337 const CodeStubAssembler::AllocationFlags flags = | |
| 338 CodeStubAssembler::kAllowLargeObjectAllocation; | |
| 339 int additional_offset = 0; | |
| 340 | |
| 341 Node* new_elements = AllocateFixedArray(kind, new_capacity, mode, flags); | |
| 342 | |
| 343 CopyFixedArrayElements(kind, elements, new_elements, length, barrier_mode, | |
| 344 mode); | |
| 345 StoreFixedArrayElement(new_elements, length, value, barrier_mode, | |
| 346 additional_offset, mode); | |
| 347 | |
| 348 StoreObjectField(promise, offset, new_elements); | |
| 349 } | |
| 350 | |
| 351 Node* PromiseBuiltinsAssembler::InternalPromiseThen(Node* context, | |
| 352 Node* promise, | |
| 353 Node* on_resolve, | |
| 354 Node* on_reject) { | |
| 355 Isolate* isolate = this->isolate(); | |
| 356 | |
| 357 // 2. If IsPromise(promise) is false, throw a TypeError exception. | |
| 358 ThrowIfNotInstanceType(context, promise, JS_PROMISE_TYPE, | |
| 359 "Promise.prototype.then"); | |
| 360 | |
| 361 Node* const native_context = LoadNativeContext(context); | |
| 362 Node* const promise_fun = | |
| 363 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | |
| 364 | |
| 365 // 3. Let C be ? SpeciesConstructor(promise, %Promise%). | |
| 366 Node* constructor = SpeciesConstructor(context, promise, promise_fun); | |
| 367 | |
| 368 // 4. Let resultCapability be ? NewPromiseCapability(C). | |
| 369 Callable call_callable = CodeFactory::Call(isolate); | |
| 370 Label fast_promise_capability(this), promise_capability(this), | |
| 371 perform_promise_then(this); | |
| 372 Variable var_deferred_promise(this, MachineRepresentation::kTagged), | |
| 373 var_deferred_on_resolve(this, MachineRepresentation::kTagged), | |
| 374 var_deferred_on_reject(this, MachineRepresentation::kTagged); | |
| 375 | |
| 376 Branch(WordEqual(promise_fun, constructor), &fast_promise_capability, | |
| 377 &promise_capability); | |
| 378 | |
| 379 Bind(&fast_promise_capability); | |
| 380 { | |
| 381 Node* const deferred_promise = AllocateAndInitJSPromise(context, promise); | |
| 382 var_deferred_promise.Bind(deferred_promise); | |
| 383 var_deferred_on_resolve.Bind(UndefinedConstant()); | |
| 384 var_deferred_on_reject.Bind(UndefinedConstant()); | |
| 385 Goto(&perform_promise_then); | |
| 386 } | |
| 387 | |
| 388 Bind(&promise_capability); | |
| 389 { | |
| 390 Node* const capability = NewPromiseCapability(context, constructor); | |
| 391 var_deferred_promise.Bind( | |
| 392 LoadObjectField(capability, JSPromiseCapability::kPromiseOffset)); | |
| 393 var_deferred_on_resolve.Bind( | |
| 394 LoadObjectField(capability, JSPromiseCapability::kResolveOffset)); | |
| 395 var_deferred_on_reject.Bind( | |
| 396 LoadObjectField(capability, JSPromiseCapability::kRejectOffset)); | |
| 397 Goto(&perform_promise_then); | |
| 398 } | |
| 399 | |
| 400 // 5. Return PerformPromiseThen(promise, onFulfilled, onRejected, | |
| 401 // resultCapability). | |
| 402 Bind(&perform_promise_then); | |
| 403 Node* const result = InternalPerformPromiseThen( | |
| 404 context, promise, on_resolve, on_reject, var_deferred_promise.value(), | |
| 405 var_deferred_on_resolve.value(), var_deferred_on_reject.value()); | |
| 406 return result; | |
| 407 } | |
| 408 | |
| 409 Node* PromiseBuiltinsAssembler::InternalPerformPromiseThen( | |
| 410 Node* context, Node* promise, Node* on_resolve, Node* on_reject, | |
| 411 Node* deferred_promise, Node* deferred_on_resolve, | |
| 412 Node* deferred_on_reject) { | |
| 413 | |
| 414 Variable var_on_resolve(this, MachineRepresentation::kTagged), | |
| 415 var_on_reject(this, MachineRepresentation::kTagged); | |
| 416 | |
| 417 var_on_resolve.Bind(on_resolve); | |
| 418 var_on_reject.Bind(on_reject); | |
| 419 | |
| 420 Label out(this), if_onresolvenotcallable(this), onrejectcheck(this), | |
| 421 append_callbacks(this); | |
| 422 GotoIf(TaggedIsSmi(on_resolve), &if_onresolvenotcallable); | |
| 423 | |
| 424 Isolate* isolate = this->isolate(); | |
| 425 Node* const on_resolve_map = LoadMap(on_resolve); | |
| 426 Branch(IsCallableMap(on_resolve_map), &onrejectcheck, | |
| 427 &if_onresolvenotcallable); | |
| 428 | |
| 429 Bind(&if_onresolvenotcallable); | |
| 430 { | |
| 431 Node* const default_resolve_handler_symbol = HeapConstant( | |
| 432 isolate->factory()->promise_default_resolve_handler_symbol()); | |
| 433 var_on_resolve.Bind(default_resolve_handler_symbol); | |
| 434 Goto(&onrejectcheck); | |
| 435 } | |
| 436 | |
| 437 Bind(&onrejectcheck); | |
| 438 { | |
| 439 Label if_onrejectnotcallable(this); | |
| 440 GotoIf(TaggedIsSmi(on_reject), &if_onrejectnotcallable); | |
| 441 | |
| 442 Node* const on_reject_map = LoadMap(on_reject); | |
| 443 Branch(IsCallableMap(on_reject_map), &append_callbacks, | |
| 444 &if_onrejectnotcallable); | |
| 445 | |
| 446 Bind(&if_onrejectnotcallable); | |
| 447 { | |
| 448 Node* const default_reject_handler_symbol = HeapConstant( | |
| 449 isolate->factory()->promise_default_reject_handler_symbol()); | |
| 450 var_on_reject.Bind(default_reject_handler_symbol); | |
| 451 Goto(&append_callbacks); | |
| 452 } | |
| 453 } | |
| 454 | |
| 455 Bind(&append_callbacks); | |
| 456 { | |
| 457 Label fulfilled_check(this); | |
| 458 Node* const status = LoadObjectField(promise, JSPromise::kStatusOffset); | |
| 459 GotoIfNot(SmiEqual(status, SmiConstant(v8::Promise::kPending)), | |
| 460 &fulfilled_check); | |
| 461 | |
| 462 Node* const existing_deferred_promise = | |
| 463 LoadObjectField(promise, JSPromise::kDeferredPromiseOffset); | |
| 464 | |
| 465 Label if_noexistingcallbacks(this), if_existingcallbacks(this); | |
| 466 Branch(IsUndefined(existing_deferred_promise), &if_noexistingcallbacks, | |
| 467 &if_existingcallbacks); | |
| 468 | |
| 469 Bind(&if_noexistingcallbacks); | |
| 470 { | |
| 471 // Store callbacks directly in the slots. | |
| 472 StoreObjectField(promise, JSPromise::kDeferredPromiseOffset, | |
| 473 deferred_promise); | |
| 474 StoreObjectField(promise, JSPromise::kDeferredOnResolveOffset, | |
| 475 deferred_on_resolve); | |
| 476 StoreObjectField(promise, JSPromise::kDeferredOnRejectOffset, | |
| 477 deferred_on_reject); | |
| 478 StoreObjectField(promise, JSPromise::kFulfillReactionsOffset, | |
| 479 var_on_resolve.value()); | |
| 480 StoreObjectField(promise, JSPromise::kRejectReactionsOffset, | |
| 481 var_on_reject.value()); | |
| 482 Goto(&out); | |
| 483 } | |
| 484 | |
| 485 Bind(&if_existingcallbacks); | |
| 486 { | |
| 487 Label if_singlecallback(this), if_multiplecallbacks(this); | |
| 488 BranchIfJSObject(existing_deferred_promise, &if_singlecallback, | |
| 489 &if_multiplecallbacks); | |
| 490 | |
| 491 Bind(&if_singlecallback); | |
| 492 { | |
| 493 // Create new FixedArrays to store callbacks, and migrate | |
| 494 // existing callbacks. | |
| 495 Node* const deferred_promise_arr = | |
| 496 AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2)); | |
| 497 StoreFixedArrayElement(deferred_promise_arr, 0, | |
| 498 existing_deferred_promise); | |
| 499 StoreFixedArrayElement(deferred_promise_arr, 1, deferred_promise); | |
| 500 | |
| 501 Node* const deferred_on_resolve_arr = | |
| 502 AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2)); | |
| 503 StoreFixedArrayElement( | |
| 504 deferred_on_resolve_arr, 0, | |
| 505 LoadObjectField(promise, JSPromise::kDeferredOnResolveOffset)); | |
| 506 StoreFixedArrayElement(deferred_on_resolve_arr, 1, deferred_on_resolve); | |
| 507 | |
| 508 Node* const deferred_on_reject_arr = | |
| 509 AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2)); | |
| 510 StoreFixedArrayElement( | |
| 511 deferred_on_reject_arr, 0, | |
| 512 LoadObjectField(promise, JSPromise::kDeferredOnRejectOffset)); | |
| 513 StoreFixedArrayElement(deferred_on_reject_arr, 1, deferred_on_reject); | |
| 514 | |
| 515 Node* const fulfill_reactions = | |
| 516 AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2)); | |
| 517 StoreFixedArrayElement( | |
| 518 fulfill_reactions, 0, | |
| 519 LoadObjectField(promise, JSPromise::kFulfillReactionsOffset)); | |
| 520 StoreFixedArrayElement(fulfill_reactions, 1, var_on_resolve.value()); | |
| 521 | |
| 522 Node* const reject_reactions = | |
| 523 AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2)); | |
| 524 StoreFixedArrayElement( | |
| 525 reject_reactions, 0, | |
| 526 LoadObjectField(promise, JSPromise::kRejectReactionsOffset)); | |
| 527 StoreFixedArrayElement(reject_reactions, 1, var_on_reject.value()); | |
| 528 | |
| 529 // Store new FixedArrays in promise. | |
| 530 StoreObjectField(promise, JSPromise::kDeferredPromiseOffset, | |
| 531 deferred_promise_arr); | |
| 532 StoreObjectField(promise, JSPromise::kDeferredOnResolveOffset, | |
| 533 deferred_on_resolve_arr); | |
| 534 StoreObjectField(promise, JSPromise::kDeferredOnRejectOffset, | |
| 535 deferred_on_reject_arr); | |
| 536 StoreObjectField(promise, JSPromise::kFulfillReactionsOffset, | |
| 537 fulfill_reactions); | |
| 538 StoreObjectField(promise, JSPromise::kRejectReactionsOffset, | |
| 539 reject_reactions); | |
| 540 Goto(&out); | |
| 541 } | |
| 542 | |
| 543 Bind(&if_multiplecallbacks); | |
| 544 { | |
| 545 AppendPromiseCallback(JSPromise::kDeferredPromiseOffset, promise, | |
| 546 deferred_promise); | |
| 547 AppendPromiseCallback(JSPromise::kDeferredOnResolveOffset, promise, | |
| 548 deferred_on_resolve); | |
| 549 AppendPromiseCallback(JSPromise::kDeferredOnRejectOffset, promise, | |
| 550 deferred_on_reject); | |
| 551 AppendPromiseCallback(JSPromise::kFulfillReactionsOffset, promise, | |
| 552 var_on_resolve.value()); | |
| 553 AppendPromiseCallback(JSPromise::kRejectReactionsOffset, promise, | |
| 554 var_on_reject.value()); | |
| 555 Goto(&out); | |
| 556 } | |
| 557 } | |
| 558 | |
| 559 Bind(&fulfilled_check); | |
| 560 { | |
| 561 Label reject(this); | |
| 562 Node* const result = LoadObjectField(promise, JSPromise::kResultOffset); | |
| 563 GotoIfNot(WordEqual(status, SmiConstant(v8::Promise::kFulfilled)), | |
| 564 &reject); | |
| 565 | |
| 566 Node* info = AllocatePromiseReactionJobInfo( | |
| 567 result, var_on_resolve.value(), deferred_promise, deferred_on_resolve, | |
| 568 deferred_on_reject, context); | |
| 569 // TODO(gsathya): Move this to TF | |
| 570 CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, info); | |
| 571 Goto(&out); | |
| 572 | |
| 573 Bind(&reject); | |
| 574 { | |
| 575 Node* const has_handler = PromiseHasHandler(promise); | |
| 576 Label enqueue(this); | |
| 577 | |
| 578 // TODO(gsathya): Fold these runtime calls and move to TF. | |
| 579 GotoIf(has_handler, &enqueue); | |
| 580 CallRuntime(Runtime::kPromiseRevokeReject, context, promise); | |
| 581 Goto(&enqueue); | |
| 582 | |
| 583 Bind(&enqueue); | |
| 584 { | |
| 585 Node* info = AllocatePromiseReactionJobInfo( | |
| 586 result, var_on_reject.value(), deferred_promise, | |
| 587 deferred_on_resolve, deferred_on_reject, context); | |
| 588 // TODO(gsathya): Move this to TF | |
| 589 CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, info); | |
| 590 Goto(&out); | |
| 591 } | |
| 592 } | |
| 593 } | |
| 594 } | |
| 595 | |
| 596 Bind(&out); | |
| 597 PromiseSetHasHandler(promise); | |
| 598 return deferred_promise; | |
| 599 } | |
| 600 | |
| 601 // Promise fast path implementations rely on unmodified JSPromise instances. | |
| 602 // We use a fairly coarse granularity for this and simply check whether both | |
| 603 // the promise itself is unmodified (i.e. its map has not changed) and its | |
| 604 // prototype is unmodified. | |
| 605 // TODO(gsathya): Refactor this out to prevent code dupe with builtins-regexp | |
| 606 void PromiseBuiltinsAssembler::BranchIfFastPath(Node* context, Node* promise, | |
| 607 Label* if_isunmodified, | |
| 608 Label* if_ismodified) { | |
| 609 Node* const native_context = LoadNativeContext(context); | |
| 610 Node* const promise_fun = | |
| 611 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | |
| 612 BranchIfFastPath(native_context, promise_fun, promise, if_isunmodified, | |
| 613 if_ismodified); | |
| 614 } | |
| 615 | |
| 616 void PromiseBuiltinsAssembler::BranchIfFastPath(Node* native_context, | |
| 617 Node* promise_fun, | |
| 618 Node* promise, | |
| 619 Label* if_isunmodified, | |
| 620 Label* if_ismodified) { | |
| 621 CSA_ASSERT(this, IsNativeContext(native_context)); | |
| 622 CSA_ASSERT(this, | |
| 623 WordEqual(promise_fun, | |
| 624 LoadContextElement(native_context, | |
| 625 Context::PROMISE_FUNCTION_INDEX))); | |
| 626 | |
| 627 Node* const map = LoadMap(promise); | |
| 628 Node* const initial_map = | |
| 629 LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset); | |
| 630 Node* const has_initialmap = WordEqual(map, initial_map); | |
| 631 | |
| 632 GotoIfNot(has_initialmap, if_ismodified); | |
| 633 | |
| 634 Node* const initial_proto_initial_map = | |
| 635 LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_MAP_INDEX); | |
| 636 Node* const proto_map = LoadMap(LoadMapPrototype(map)); | |
| 637 Node* const proto_has_initialmap = | |
| 638 WordEqual(proto_map, initial_proto_initial_map); | |
| 639 | |
| 640 Branch(proto_has_initialmap, if_isunmodified, if_ismodified); | |
| 641 } | |
| 642 | |
| 643 Node* PromiseBuiltinsAssembler::AllocatePromiseResolveThenableJobInfo( | |
| 644 Node* thenable, Node* then, Node* resolve, Node* reject, Node* context) { | |
| 645 Node* const info = Allocate(PromiseResolveThenableJobInfo::kSize); | |
| 646 StoreMapNoWriteBarrier(info, | |
| 647 Heap::kPromiseResolveThenableJobInfoMapRootIndex); | |
| 648 StoreObjectFieldNoWriteBarrier( | |
| 649 info, PromiseResolveThenableJobInfo::kThenableOffset, thenable); | |
| 650 StoreObjectFieldNoWriteBarrier( | |
| 651 info, PromiseResolveThenableJobInfo::kThenOffset, then); | |
| 652 StoreObjectFieldNoWriteBarrier( | |
| 653 info, PromiseResolveThenableJobInfo::kResolveOffset, resolve); | |
| 654 StoreObjectFieldNoWriteBarrier( | |
| 655 info, PromiseResolveThenableJobInfo::kRejectOffset, reject); | |
| 656 StoreObjectFieldNoWriteBarrier( | |
| 657 info, PromiseResolveThenableJobInfo::kContextOffset, context); | |
| 658 return info; | |
| 659 } | |
| 660 | |
| 661 void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context, | |
| 662 Node* promise, | |
| 663 Node* result) { | |
| 664 Isolate* isolate = this->isolate(); | |
| 665 | |
| 666 Variable var_reason(this, MachineRepresentation::kTagged), | |
| 667 var_then(this, MachineRepresentation::kTagged); | |
| 668 | |
| 669 Label do_enqueue(this), fulfill(this), if_cycle(this, Label::kDeferred), | |
| 670 if_rejectpromise(this, Label::kDeferred), out(this); | |
| 671 | |
| 672 Label cycle_check(this); | |
| 673 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &cycle_check); | |
| 674 CallRuntime(Runtime::kPromiseHookResolve, context, promise); | |
| 675 Goto(&cycle_check); | |
| 676 | |
| 677 Bind(&cycle_check); | |
| 678 // 6. If SameValue(resolution, promise) is true, then | |
| 679 GotoIf(SameValue(promise, result), &if_cycle); | |
| 680 | |
| 681 // 7. If Type(resolution) is not Object, then | |
| 682 GotoIf(TaggedIsSmi(result), &fulfill); | |
| 683 GotoIfNot(IsJSReceiver(result), &fulfill); | |
| 684 | |
| 685 Label if_nativepromise(this), if_notnativepromise(this, Label::kDeferred); | |
| 686 Node* const native_context = LoadNativeContext(context); | |
| 687 Node* const promise_fun = | |
| 688 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | |
| 689 BranchIfFastPath(native_context, promise_fun, result, &if_nativepromise, | |
| 690 &if_notnativepromise); | |
| 691 | |
| 692 // Resolution is a native promise and if it's already resolved or | |
| 693 // rejected, shortcircuit the resolution procedure by directly | |
| 694 // reusing the value from the promise. | |
| 695 Bind(&if_nativepromise); | |
| 696 { | |
| 697 Node* const thenable_status = | |
| 698 LoadObjectField(result, JSPromise::kStatusOffset); | |
| 699 Node* const thenable_value = | |
| 700 LoadObjectField(result, JSPromise::kResultOffset); | |
| 701 | |
| 702 Label if_isnotpending(this); | |
| 703 GotoIfNot(SmiEqual(SmiConstant(v8::Promise::kPending), thenable_status), | |
| 704 &if_isnotpending); | |
| 705 | |
| 706 // TODO(gsathya): Use a marker here instead of the actual then | |
| 707 // callback, and check for the marker in PromiseResolveThenableJob | |
| 708 // and perform PromiseThen. | |
| 709 Node* const then = | |
| 710 LoadContextElement(native_context, Context::PROMISE_THEN_INDEX); | |
| 711 var_then.Bind(then); | |
| 712 Goto(&do_enqueue); | |
| 713 | |
| 714 Bind(&if_isnotpending); | |
| 715 { | |
| 716 Label if_fulfilled(this), if_rejected(this); | |
| 717 Branch(SmiEqual(SmiConstant(v8::Promise::kFulfilled), thenable_status), | |
| 718 &if_fulfilled, &if_rejected); | |
| 719 | |
| 720 Bind(&if_fulfilled); | |
| 721 { | |
| 722 PromiseFulfill(context, promise, thenable_value, | |
| 723 v8::Promise::kFulfilled); | |
| 724 PromiseSetHasHandler(promise); | |
| 725 Goto(&out); | |
| 726 } | |
| 727 | |
| 728 Bind(&if_rejected); | |
| 729 { | |
| 730 Label reject(this); | |
| 731 Node* const has_handler = PromiseHasHandler(result); | |
| 732 | |
| 733 // Promise has already been rejected, but had no handler. | |
| 734 // Revoke previously triggered reject event. | |
| 735 GotoIf(has_handler, &reject); | |
| 736 CallRuntime(Runtime::kPromiseRevokeReject, context, result); | |
| 737 Goto(&reject); | |
| 738 | |
| 739 Bind(&reject); | |
| 740 // Don't cause a debug event as this case is forwarding a rejection. | |
| 741 InternalPromiseReject(context, promise, thenable_value, false); | |
| 742 PromiseSetHasHandler(result); | |
| 743 Goto(&out); | |
| 744 } | |
| 745 } | |
| 746 } | |
| 747 | |
| 748 Bind(&if_notnativepromise); | |
| 749 { | |
| 750 // 8. Let then be Get(resolution, "then"). | |
| 751 Node* const then = | |
| 752 GetProperty(context, result, isolate->factory()->then_string()); | |
| 753 | |
| 754 // 9. If then is an abrupt completion, then | |
| 755 GotoIfException(then, &if_rejectpromise, &var_reason); | |
| 756 | |
| 757 // 11. If IsCallable(thenAction) is false, then | |
| 758 GotoIf(TaggedIsSmi(then), &fulfill); | |
| 759 Node* const then_map = LoadMap(then); | |
| 760 GotoIfNot(IsCallableMap(then_map), &fulfill); | |
| 761 var_then.Bind(then); | |
| 762 Goto(&do_enqueue); | |
| 763 } | |
| 764 | |
| 765 Bind(&do_enqueue); | |
| 766 { | |
| 767 // TODO(gsathya): Add fast path for native promises with unmodified | |
| 768 // PromiseThen (which don't need these resolving functions, but | |
| 769 // instead can just call resolve/reject directly). | |
| 770 Node* resolve = nullptr; | |
| 771 Node* reject = nullptr; | |
| 772 std::tie(resolve, reject) = CreatePromiseResolvingFunctions( | |
| 773 promise, FalseConstant(), native_context); | |
| 774 | |
| 775 Node* const info = AllocatePromiseResolveThenableJobInfo( | |
| 776 result, var_then.value(), resolve, reject, context); | |
| 777 | |
| 778 Label enqueue(this); | |
| 779 GotoIfNot(IsDebugActive(), &enqueue); | |
| 780 | |
| 781 GotoIf(TaggedIsSmi(result), &enqueue); | |
| 782 GotoIfNot(HasInstanceType(result, JS_PROMISE_TYPE), &enqueue); | |
| 783 | |
| 784 // Mark the dependency of the new promise on the resolution | |
| 785 Node* const key = | |
| 786 HeapConstant(isolate->factory()->promise_handled_by_symbol()); | |
| 787 CallRuntime(Runtime::kSetProperty, context, result, key, promise, | |
| 788 SmiConstant(STRICT)); | |
| 789 Goto(&enqueue); | |
| 790 | |
| 791 // 12. Perform EnqueueJob("PromiseJobs", | |
| 792 // PromiseResolveThenableJob, « promise, resolution, thenAction»). | |
| 793 Bind(&enqueue); | |
| 794 // TODO(gsathya): Move this to TF | |
| 795 CallRuntime(Runtime::kEnqueuePromiseResolveThenableJob, context, info); | |
| 796 Goto(&out); | |
| 797 } | |
| 798 | |
| 799 // 7.b Return FulfillPromise(promise, resolution). | |
| 800 Bind(&fulfill); | |
| 801 { | |
| 802 PromiseFulfill(context, promise, result, v8::Promise::kFulfilled); | |
| 803 Goto(&out); | |
| 804 } | |
| 805 | |
| 806 Bind(&if_cycle); | |
| 807 { | |
| 808 // 6.a Let selfResolutionError be a newly created TypeError object. | |
| 809 Node* const message_id = SmiConstant(MessageTemplate::kPromiseCyclic); | |
| 810 Node* const error = | |
| 811 CallRuntime(Runtime::kNewTypeError, context, message_id, result); | |
| 812 var_reason.Bind(error); | |
| 813 | |
| 814 // 6.b Return RejectPromise(promise, selfResolutionError). | |
| 815 Goto(&if_rejectpromise); | |
| 816 } | |
| 817 | |
| 818 // 9.a Return RejectPromise(promise, then.[[Value]]). | |
| 819 Bind(&if_rejectpromise); | |
| 820 { | |
| 821 // Don't cause a debug event as this case is forwarding a rejection. | |
| 822 InternalPromiseReject(context, promise, var_reason.value(), false); | |
| 823 Goto(&out); | |
| 824 } | |
| 825 | |
| 826 Bind(&out); | |
| 827 } | |
| 828 | |
| 829 void PromiseBuiltinsAssembler::PromiseFulfill( | |
| 830 Node* context, Node* promise, Node* result, | |
| 831 v8::Promise::PromiseState status) { | |
| 832 Label do_promisereset(this), debug_async_event_enqueue_recurring(this); | |
| 833 | |
| 834 Node* const status_smi = SmiConstant(static_cast<int>(status)); | |
| 835 Node* const deferred_promise = | |
| 836 LoadObjectField(promise, JSPromise::kDeferredPromiseOffset); | |
| 837 | |
| 838 GotoIf(IsUndefined(deferred_promise), &debug_async_event_enqueue_recurring); | |
| 839 | |
| 840 Node* const tasks = | |
| 841 status == v8::Promise::kFulfilled | |
| 842 ? LoadObjectField(promise, JSPromise::kFulfillReactionsOffset) | |
| 843 : LoadObjectField(promise, JSPromise::kRejectReactionsOffset); | |
| 844 | |
| 845 Node* const deferred_on_resolve = | |
| 846 LoadObjectField(promise, JSPromise::kDeferredOnResolveOffset); | |
| 847 Node* const deferred_on_reject = | |
| 848 LoadObjectField(promise, JSPromise::kDeferredOnRejectOffset); | |
| 849 | |
| 850 Node* const info = AllocatePromiseReactionJobInfo( | |
| 851 result, tasks, deferred_promise, deferred_on_resolve, deferred_on_reject, | |
| 852 context); | |
| 853 | |
| 854 CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, info); | |
| 855 Goto(&debug_async_event_enqueue_recurring); | |
| 856 | |
| 857 Bind(&debug_async_event_enqueue_recurring); | |
| 858 { | |
| 859 GotoIfNot(IsDebugActive(), &do_promisereset); | |
| 860 CallRuntime(Runtime::kDebugAsyncEventEnqueueRecurring, context, promise, | |
| 861 status_smi); | |
| 862 Goto(&do_promisereset); | |
| 863 } | |
| 864 | |
| 865 Bind(&do_promisereset); | |
| 866 { | |
| 867 StoreObjectField(promise, JSPromise::kStatusOffset, status_smi); | |
| 868 StoreObjectField(promise, JSPromise::kResultOffset, result); | |
| 869 StoreObjectFieldRoot(promise, JSPromise::kDeferredPromiseOffset, | |
| 870 Heap::kUndefinedValueRootIndex); | |
| 871 StoreObjectFieldRoot(promise, JSPromise::kDeferredOnResolveOffset, | |
| 872 Heap::kUndefinedValueRootIndex); | |
| 873 StoreObjectFieldRoot(promise, JSPromise::kDeferredOnRejectOffset, | |
| 874 Heap::kUndefinedValueRootIndex); | |
| 875 StoreObjectFieldRoot(promise, JSPromise::kFulfillReactionsOffset, | |
| 876 Heap::kUndefinedValueRootIndex); | |
| 877 StoreObjectFieldRoot(promise, JSPromise::kRejectReactionsOffset, | |
| 878 Heap::kUndefinedValueRootIndex); | |
| 879 } | |
| 880 } | |
| 881 | |
| 882 void PromiseBuiltinsAssembler::BranchIfAccessCheckFailed( | |
| 883 Node* context, Node* native_context, Node* promise_constructor, | |
| 884 Node* executor, Label* if_noaccess) { | |
| 885 Variable var_executor(this, MachineRepresentation::kTagged); | |
| 886 var_executor.Bind(executor); | |
| 887 Label has_access(this), call_runtime(this, Label::kDeferred); | |
| 888 | |
| 889 // If executor is a bound function, load the bound function until we've | |
| 890 // reached an actual function. | |
| 891 Label found_function(this), loop_over_bound_function(this, &var_executor); | |
| 892 Goto(&loop_over_bound_function); | |
| 893 Bind(&loop_over_bound_function); | |
| 894 { | |
| 895 Node* executor_type = LoadInstanceType(var_executor.value()); | |
| 896 GotoIf(InstanceTypeEqual(executor_type, JS_FUNCTION_TYPE), &found_function); | |
| 897 GotoIfNot(InstanceTypeEqual(executor_type, JS_BOUND_FUNCTION_TYPE), | |
| 898 &call_runtime); | |
| 899 var_executor.Bind(LoadObjectField( | |
| 900 var_executor.value(), JSBoundFunction::kBoundTargetFunctionOffset)); | |
| 901 Goto(&loop_over_bound_function); | |
| 902 } | |
| 903 | |
| 904 // Load the context from the function and compare it to the Promise | |
| 905 // constructor's context. If they match, everything is fine, otherwise, bail | |
| 906 // out to the runtime. | |
| 907 Bind(&found_function); | |
| 908 { | |
| 909 Node* function_context = | |
| 910 LoadObjectField(var_executor.value(), JSFunction::kContextOffset); | |
| 911 Node* native_function_context = LoadNativeContext(function_context); | |
| 912 Branch(WordEqual(native_context, native_function_context), &has_access, | |
| 913 &call_runtime); | |
| 914 } | |
| 915 | |
| 916 Bind(&call_runtime); | |
| 917 { | |
| 918 Branch(WordEqual(CallRuntime(Runtime::kAllowDynamicFunction, context, | |
| 919 promise_constructor), | |
| 920 BooleanConstant(true)), | |
| 921 &has_access, if_noaccess); | |
| 922 } | |
| 923 | |
| 924 Bind(&has_access); | |
| 925 } | |
| 926 | |
| 927 void PromiseBuiltinsAssembler::InternalPromiseReject(Node* context, | |
| 928 Node* promise, Node* value, | |
| 929 Node* debug_event) { | |
| 930 Label out(this); | |
| 931 GotoIfNot(IsDebugActive(), &out); | |
| 932 GotoIfNot(WordEqual(TrueConstant(), debug_event), &out); | |
| 933 CallRuntime(Runtime::kDebugPromiseReject, context, promise, value); | |
| 934 Goto(&out); | |
| 935 | |
| 936 Bind(&out); | |
| 937 InternalPromiseReject(context, promise, value, false); | |
| 938 } | |
| 939 | |
| 940 // This duplicates a lot of logic from PromiseRejectEvent in | |
| 941 // runtime-promise.cc | |
| 942 void PromiseBuiltinsAssembler::InternalPromiseReject(Node* context, | |
| 943 Node* promise, Node* value, | |
| 944 bool debug_event) { | |
| 945 Label fulfill(this), report_unhandledpromise(this), run_promise_hook(this); | |
| 946 | |
| 947 if (debug_event) { | |
| 948 GotoIfNot(IsDebugActive(), &run_promise_hook); | |
| 949 CallRuntime(Runtime::kDebugPromiseReject, context, promise, value); | |
| 950 Goto(&run_promise_hook); | |
| 951 } else { | |
| 952 Goto(&run_promise_hook); | |
| 953 } | |
| 954 | |
| 955 Bind(&run_promise_hook); | |
| 956 { | |
| 957 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &report_unhandledpromise); | |
| 958 CallRuntime(Runtime::kPromiseHookResolve, context, promise); | |
| 959 Goto(&report_unhandledpromise); | |
| 960 } | |
| 961 | |
| 962 Bind(&report_unhandledpromise); | |
| 963 { | |
| 964 GotoIf(PromiseHasHandler(promise), &fulfill); | |
| 965 CallRuntime(Runtime::kReportPromiseReject, context, promise, value); | |
| 966 Goto(&fulfill); | |
| 967 } | |
| 968 | |
| 969 Bind(&fulfill); | |
| 970 PromiseFulfill(context, promise, value, v8::Promise::kRejected); | |
| 971 } | |
| 972 | |
| 973 // ES#sec-promise-reject-functions | |
| 974 // Promise Reject Functions | |
| 975 TF_BUILTIN(PromiseRejectClosure, PromiseBuiltinsAssembler) { | |
| 976 Node* const value = Parameter(1); | |
| 977 Node* const context = Parameter(4); | |
| 978 | |
| 979 Label out(this); | |
| 980 | |
| 981 // 3. Let alreadyResolved be F.[[AlreadyResolved]]. | |
| 982 int has_already_visited_slot = kAlreadyVisitedSlot; | |
| 983 | |
| 984 Node* const has_already_visited = | |
| 985 LoadContextElement(context, has_already_visited_slot); | |
| 986 | |
| 987 // 4. If alreadyResolved.[[Value]] is true, return undefined. | |
| 988 GotoIf(SmiEqual(has_already_visited, SmiConstant(1)), &out); | |
| 989 | |
| 990 // 5.Set alreadyResolved.[[Value]] to true. | |
| 991 StoreContextElementNoWriteBarrier(context, has_already_visited_slot, | |
| 992 SmiConstant(1)); | |
| 993 | |
| 994 // 2. Let promise be F.[[Promise]]. | |
| 995 Node* const promise = | |
| 996 LoadContextElement(context, IntPtrConstant(kPromiseSlot)); | |
| 997 Node* const debug_event = | |
| 998 LoadContextElement(context, IntPtrConstant(kDebugEventSlot)); | |
| 999 | |
| 1000 InternalPromiseReject(context, promise, value, debug_event); | |
| 1001 Return(UndefinedConstant()); | |
| 1002 | |
| 1003 Bind(&out); | |
| 1004 Return(UndefinedConstant()); | |
| 1005 } | |
| 1006 | |
| 1007 TF_BUILTIN(PromiseConstructor, PromiseBuiltinsAssembler) { | |
| 1008 Node* const executor = Parameter(1); | |
| 1009 Node* const new_target = Parameter(2); | |
| 1010 Node* const context = Parameter(4); | |
| 1011 Isolate* isolate = this->isolate(); | |
| 1012 | |
| 1013 Label if_targetisundefined(this, Label::kDeferred); | |
| 1014 | |
| 1015 GotoIf(IsUndefined(new_target), &if_targetisundefined); | |
| 1016 | |
| 1017 Label if_notcallable(this, Label::kDeferred); | |
| 1018 | |
| 1019 GotoIf(TaggedIsSmi(executor), &if_notcallable); | |
| 1020 | |
| 1021 Node* const executor_map = LoadMap(executor); | |
| 1022 GotoIfNot(IsCallableMap(executor_map), &if_notcallable); | |
| 1023 | |
| 1024 Node* const native_context = LoadNativeContext(context); | |
| 1025 Node* const promise_fun = | |
| 1026 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | |
| 1027 Node* const is_debug_active = IsDebugActive(); | |
| 1028 Label if_targetisnotmodified(this), | |
| 1029 if_targetismodified(this, Label::kDeferred), run_executor(this), | |
| 1030 debug_push(this), if_noaccess(this, Label::kDeferred); | |
| 1031 | |
| 1032 BranchIfAccessCheckFailed(context, native_context, promise_fun, executor, | |
| 1033 &if_noaccess); | |
| 1034 | |
| 1035 Branch(WordEqual(promise_fun, new_target), &if_targetisnotmodified, | |
| 1036 &if_targetismodified); | |
| 1037 | |
| 1038 Variable var_result(this, MachineRepresentation::kTagged), | |
| 1039 var_reject_call(this, MachineRepresentation::kTagged), | |
| 1040 var_reason(this, MachineRepresentation::kTagged); | |
| 1041 | |
| 1042 Bind(&if_targetisnotmodified); | |
| 1043 { | |
| 1044 Node* const instance = AllocateAndInitJSPromise(context); | |
| 1045 var_result.Bind(instance); | |
| 1046 Goto(&debug_push); | |
| 1047 } | |
| 1048 | |
| 1049 Bind(&if_targetismodified); | |
| 1050 { | |
| 1051 ConstructorBuiltinsAssembler constructor_assembler(this->state()); | |
| 1052 Node* const instance = constructor_assembler.EmitFastNewObject( | |
| 1053 context, promise_fun, new_target); | |
| 1054 PromiseInit(instance); | |
| 1055 var_result.Bind(instance); | |
| 1056 | |
| 1057 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &debug_push); | |
| 1058 CallRuntime(Runtime::kPromiseHookInit, context, instance, | |
| 1059 UndefinedConstant()); | |
| 1060 Goto(&debug_push); | |
| 1061 } | |
| 1062 | |
| 1063 Bind(&debug_push); | |
| 1064 { | |
| 1065 GotoIfNot(is_debug_active, &run_executor); | |
| 1066 CallRuntime(Runtime::kDebugPushPromise, context, var_result.value()); | |
| 1067 Goto(&run_executor); | |
| 1068 } | |
| 1069 | |
| 1070 Bind(&run_executor); | |
| 1071 { | |
| 1072 Label out(this), if_rejectpromise(this), debug_pop(this, Label::kDeferred); | |
| 1073 | |
| 1074 Node *resolve, *reject; | |
| 1075 std::tie(resolve, reject) = CreatePromiseResolvingFunctions( | |
| 1076 var_result.value(), TrueConstant(), native_context); | |
| 1077 Callable call_callable = CodeFactory::Call(isolate); | |
| 1078 | |
| 1079 Node* const maybe_exception = CallJS(call_callable, context, executor, | |
| 1080 UndefinedConstant(), resolve, reject); | |
| 1081 | |
| 1082 GotoIfException(maybe_exception, &if_rejectpromise, &var_reason); | |
| 1083 Branch(is_debug_active, &debug_pop, &out); | |
| 1084 | |
| 1085 Bind(&if_rejectpromise); | |
| 1086 { | |
| 1087 Callable call_callable = CodeFactory::Call(isolate); | |
| 1088 CallJS(call_callable, context, reject, UndefinedConstant(), | |
| 1089 var_reason.value()); | |
| 1090 Branch(is_debug_active, &debug_pop, &out); | |
| 1091 } | |
| 1092 | |
| 1093 Bind(&debug_pop); | |
| 1094 { | |
| 1095 CallRuntime(Runtime::kDebugPopPromise, context); | |
| 1096 Goto(&out); | |
| 1097 } | |
| 1098 Bind(&out); | |
| 1099 Return(var_result.value()); | |
| 1100 } | |
| 1101 | |
| 1102 // 1. If NewTarget is undefined, throw a TypeError exception. | |
| 1103 Bind(&if_targetisundefined); | |
| 1104 { | |
| 1105 Node* const message_id = SmiConstant(MessageTemplate::kNotAPromise); | |
| 1106 CallRuntime(Runtime::kThrowTypeError, context, message_id, new_target); | |
| 1107 Unreachable(); | |
| 1108 } | |
| 1109 | |
| 1110 // 2. If IsCallable(executor) is false, throw a TypeError exception. | |
| 1111 Bind(&if_notcallable); | |
| 1112 { | |
| 1113 Node* const message_id = | |
| 1114 SmiConstant(MessageTemplate::kResolverNotAFunction); | |
| 1115 CallRuntime(Runtime::kThrowTypeError, context, message_id, executor); | |
| 1116 Unreachable(); | |
| 1117 } | |
| 1118 | |
| 1119 // Silently fail if the stack looks fishy. | |
| 1120 Bind(&if_noaccess); | |
| 1121 { | |
| 1122 Node* const counter_id = | |
| 1123 SmiConstant(v8::Isolate::kPromiseConstructorReturnedUndefined); | |
| 1124 CallRuntime(Runtime::kIncrementUseCounter, context, counter_id); | |
| 1125 Return(UndefinedConstant()); | |
| 1126 } | |
| 1127 } | |
| 1128 | |
| 1129 TF_BUILTIN(PromiseInternalConstructor, PromiseBuiltinsAssembler) { | |
| 1130 Node* const parent = Parameter(1); | |
| 1131 Node* const context = Parameter(4); | |
| 1132 Return(AllocateAndInitJSPromise(context, parent)); | |
| 1133 } | |
| 1134 | |
| 1135 TF_BUILTIN(IsPromise, PromiseBuiltinsAssembler) { | |
| 1136 Node* const maybe_promise = Parameter(1); | |
| 1137 Label if_notpromise(this, Label::kDeferred); | |
| 1138 | |
| 1139 GotoIf(TaggedIsSmi(maybe_promise), &if_notpromise); | |
| 1140 | |
| 1141 Node* const result = | |
| 1142 SelectBooleanConstant(HasInstanceType(maybe_promise, JS_PROMISE_TYPE)); | |
| 1143 Return(result); | |
| 1144 | |
| 1145 Bind(&if_notpromise); | |
| 1146 Return(FalseConstant()); | |
| 1147 } | |
| 1148 | |
| 1149 // ES#sec-promise.prototype.then | |
| 1150 // Promise.prototype.catch ( onFulfilled, onRejected ) | |
| 1151 TF_BUILTIN(PromiseThen, PromiseBuiltinsAssembler) { | |
| 1152 // 1. Let promise be the this value. | |
| 1153 Node* const promise = Parameter(0); | |
| 1154 Node* const on_resolve = Parameter(1); | |
| 1155 Node* const on_reject = Parameter(2); | |
| 1156 Node* const context = Parameter(5); | |
| 1157 | |
| 1158 Node* const result = | |
| 1159 InternalPromiseThen(context, promise, on_resolve, on_reject); | |
| 1160 Return(result); | |
| 1161 } | |
| 1162 | |
| 1163 // ES#sec-promise-resolve-functions | |
| 1164 // Promise Resolve Functions | |
| 1165 TF_BUILTIN(PromiseResolveClosure, PromiseBuiltinsAssembler) { | |
| 1166 Node* const value = Parameter(1); | |
| 1167 Node* const context = Parameter(4); | |
| 1168 | |
| 1169 Label out(this); | |
| 1170 | |
| 1171 // 3. Let alreadyResolved be F.[[AlreadyResolved]]. | |
| 1172 int has_already_visited_slot = kAlreadyVisitedSlot; | |
| 1173 | |
| 1174 Node* const has_already_visited = | |
| 1175 LoadContextElement(context, has_already_visited_slot); | |
| 1176 | |
| 1177 // 4. If alreadyResolved.[[Value]] is true, return undefined. | |
| 1178 GotoIf(SmiEqual(has_already_visited, SmiConstant(1)), &out); | |
| 1179 | |
| 1180 // 5.Set alreadyResolved.[[Value]] to true. | |
| 1181 StoreContextElementNoWriteBarrier(context, has_already_visited_slot, | |
| 1182 SmiConstant(1)); | |
| 1183 | |
| 1184 // 2. Let promise be F.[[Promise]]. | |
| 1185 Node* const promise = | |
| 1186 LoadContextElement(context, IntPtrConstant(kPromiseSlot)); | |
| 1187 | |
| 1188 InternalResolvePromise(context, promise, value); | |
| 1189 Return(UndefinedConstant()); | |
| 1190 | |
| 1191 Bind(&out); | |
| 1192 Return(UndefinedConstant()); | |
| 1193 } | |
| 1194 | |
| 1195 TF_BUILTIN(ResolvePromise, PromiseBuiltinsAssembler) { | |
| 1196 Node* const promise = Parameter(1); | |
| 1197 Node* const result = Parameter(2); | |
| 1198 Node* const context = Parameter(5); | |
| 1199 | |
| 1200 InternalResolvePromise(context, promise, result); | |
| 1201 Return(UndefinedConstant()); | |
| 1202 } | |
| 1203 | |
| 1204 TF_BUILTIN(PromiseHandleReject, PromiseBuiltinsAssembler) { | |
| 1205 typedef PromiseHandleRejectDescriptor Descriptor; | |
| 1206 | |
| 1207 Node* const promise = Parameter(Descriptor::kPromise); | |
| 1208 Node* const on_reject = Parameter(Descriptor::kOnReject); | |
| 1209 Node* const exception = Parameter(Descriptor::kException); | |
| 1210 Node* const context = Parameter(Descriptor::kContext); | |
| 1211 | |
| 1212 Callable call_callable = CodeFactory::Call(isolate()); | |
| 1213 Variable var_unused(this, MachineRepresentation::kTagged); | |
| 1214 | |
| 1215 Label if_internalhandler(this), if_customhandler(this, Label::kDeferred); | |
| 1216 Branch(IsUndefined(on_reject), &if_internalhandler, &if_customhandler); | |
| 1217 | |
| 1218 Bind(&if_internalhandler); | |
| 1219 { | |
| 1220 InternalPromiseReject(context, promise, exception, false); | |
| 1221 Return(UndefinedConstant()); | |
| 1222 } | |
| 1223 | |
| 1224 Bind(&if_customhandler); | |
| 1225 { | |
| 1226 CallJS(call_callable, context, on_reject, UndefinedConstant(), exception); | |
| 1227 Return(UndefinedConstant()); | |
| 1228 } | |
| 1229 } | |
| 1230 | |
| 1231 TF_BUILTIN(PromiseHandle, PromiseBuiltinsAssembler) { | |
| 1232 Node* const value = Parameter(1); | |
| 1233 Node* const handler = Parameter(2); | |
| 1234 Node* const deferred_promise = Parameter(3); | |
| 1235 Node* const deferred_on_resolve = Parameter(4); | |
| 1236 Node* const deferred_on_reject = Parameter(5); | |
| 1237 Node* const context = Parameter(8); | |
| 1238 Isolate* isolate = this->isolate(); | |
| 1239 | |
| 1240 Variable var_reason(this, MachineRepresentation::kTagged); | |
| 1241 | |
| 1242 Node* const is_debug_active = IsDebugActive(); | |
| 1243 Label run_handler(this), if_rejectpromise(this), promisehook_before(this), | |
| 1244 promisehook_after(this), debug_pop(this); | |
| 1245 | |
| 1246 GotoIfNot(is_debug_active, &promisehook_before); | |
| 1247 CallRuntime(Runtime::kDebugPushPromise, context, deferred_promise); | |
| 1248 Goto(&promisehook_before); | |
| 1249 | |
| 1250 Bind(&promisehook_before); | |
| 1251 { | |
| 1252 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &run_handler); | |
| 1253 CallRuntime(Runtime::kPromiseHookBefore, context, deferred_promise); | |
| 1254 Goto(&run_handler); | |
| 1255 } | |
| 1256 | |
| 1257 Bind(&run_handler); | |
| 1258 { | |
| 1259 Label if_defaulthandler(this), if_callablehandler(this), | |
| 1260 if_internalhandler(this), if_customhandler(this, Label::kDeferred); | |
| 1261 Variable var_result(this, MachineRepresentation::kTagged); | |
| 1262 | |
| 1263 Branch(IsSymbol(handler), &if_defaulthandler, &if_callablehandler); | |
| 1264 | |
| 1265 Bind(&if_defaulthandler); | |
| 1266 { | |
| 1267 Label if_resolve(this), if_reject(this); | |
| 1268 Node* const default_resolve_handler_symbol = HeapConstant( | |
| 1269 isolate->factory()->promise_default_resolve_handler_symbol()); | |
| 1270 Branch(WordEqual(default_resolve_handler_symbol, handler), &if_resolve, | |
| 1271 &if_reject); | |
| 1272 | |
| 1273 Bind(&if_resolve); | |
| 1274 { | |
| 1275 var_result.Bind(value); | |
| 1276 Branch(IsUndefined(deferred_on_resolve), &if_internalhandler, | |
| 1277 &if_customhandler); | |
| 1278 } | |
| 1279 | |
| 1280 Bind(&if_reject); | |
| 1281 { | |
| 1282 var_reason.Bind(value); | |
| 1283 Goto(&if_rejectpromise); | |
| 1284 } | |
| 1285 } | |
| 1286 | |
| 1287 Bind(&if_callablehandler); | |
| 1288 { | |
| 1289 Callable call_callable = CodeFactory::Call(isolate); | |
| 1290 Node* const result = | |
| 1291 CallJS(call_callable, context, handler, UndefinedConstant(), value); | |
| 1292 var_result.Bind(result); | |
| 1293 GotoIfException(result, &if_rejectpromise, &var_reason); | |
| 1294 Branch(IsUndefined(deferred_on_resolve), &if_internalhandler, | |
| 1295 &if_customhandler); | |
| 1296 } | |
| 1297 | |
| 1298 Bind(&if_internalhandler); | |
| 1299 InternalResolvePromise(context, deferred_promise, var_result.value()); | |
| 1300 Goto(&promisehook_after); | |
| 1301 | |
| 1302 Bind(&if_customhandler); | |
| 1303 { | |
| 1304 Callable call_callable = CodeFactory::Call(isolate); | |
| 1305 Node* const maybe_exception = | |
| 1306 CallJS(call_callable, context, deferred_on_resolve, | |
| 1307 UndefinedConstant(), var_result.value()); | |
| 1308 GotoIfException(maybe_exception, &if_rejectpromise, &var_reason); | |
| 1309 Goto(&promisehook_after); | |
| 1310 } | |
| 1311 } | |
| 1312 | |
| 1313 Bind(&if_rejectpromise); | |
| 1314 { | |
| 1315 Callable promise_handle_reject = CodeFactory::PromiseHandleReject(isolate); | |
| 1316 CallStub(promise_handle_reject, context, deferred_promise, | |
| 1317 deferred_on_reject, var_reason.value()); | |
| 1318 Goto(&promisehook_after); | |
| 1319 } | |
| 1320 | |
| 1321 Bind(&promisehook_after); | |
| 1322 { | |
| 1323 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &debug_pop); | |
| 1324 CallRuntime(Runtime::kPromiseHookAfter, context, deferred_promise); | |
| 1325 Goto(&debug_pop); | |
| 1326 } | |
| 1327 | |
| 1328 Bind(&debug_pop); | |
| 1329 { | |
| 1330 Label out(this); | |
| 1331 | |
| 1332 GotoIfNot(is_debug_active, &out); | |
| 1333 CallRuntime(Runtime::kDebugPopPromise, context); | |
| 1334 Goto(&out); | |
| 1335 | |
| 1336 Bind(&out); | |
| 1337 Return(UndefinedConstant()); | |
| 1338 } | |
| 1339 } | |
| 1340 | |
| 1341 // ES#sec-promise.prototype.catch | |
| 1342 // Promise.prototype.catch ( onRejected ) | |
| 1343 TF_BUILTIN(PromiseCatch, PromiseBuiltinsAssembler) { | |
| 1344 // 1. Let promise be the this value. | |
| 1345 Node* const promise = Parameter(0); | |
| 1346 Node* const on_resolve = UndefinedConstant(); | |
| 1347 Node* const on_reject = Parameter(1); | |
| 1348 Node* const context = Parameter(4); | |
| 1349 | |
| 1350 Label if_internalthen(this), if_customthen(this, Label::kDeferred); | |
| 1351 GotoIf(TaggedIsSmi(promise), &if_customthen); | |
| 1352 BranchIfFastPath(context, promise, &if_internalthen, &if_customthen); | |
| 1353 | |
| 1354 Bind(&if_internalthen); | |
| 1355 { | |
| 1356 Node* const result = | |
| 1357 InternalPromiseThen(context, promise, on_resolve, on_reject); | |
| 1358 Return(result); | |
| 1359 } | |
| 1360 | |
| 1361 Bind(&if_customthen); | |
| 1362 { | |
| 1363 Node* const then = | |
| 1364 GetProperty(context, promise, isolate()->factory()->then_string()); | |
| 1365 Callable call_callable = CodeFactory::Call(isolate()); | |
| 1366 Node* const result = | |
| 1367 CallJS(call_callable, context, then, promise, on_resolve, on_reject); | |
| 1368 Return(result); | |
| 1369 } | |
| 1370 } | |
| 1371 | |
| 1372 TF_BUILTIN(PromiseResolve, PromiseBuiltinsAssembler) { | |
| 1373 // 1. Let C be the this value. | |
| 1374 Node* receiver = Parameter(0); | |
| 1375 Node* value = Parameter(1); | |
| 1376 Node* context = Parameter(4); | |
| 1377 Isolate* isolate = this->isolate(); | |
| 1378 | |
| 1379 // 2. If Type(C) is not Object, throw a TypeError exception. | |
| 1380 ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject, | |
| 1381 "PromiseResolve"); | |
| 1382 | |
| 1383 Node* const native_context = LoadNativeContext(context); | |
| 1384 Node* const promise_fun = | |
| 1385 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | |
| 1386 | |
| 1387 Label if_valueisnativepromise(this), if_valueisnotnativepromise(this), | |
| 1388 if_valueisnotpromise(this); | |
| 1389 | |
| 1390 // 3.If IsPromise(x) is true, then | |
| 1391 GotoIf(TaggedIsSmi(value), &if_valueisnotpromise); | |
| 1392 | |
| 1393 // This shortcircuits the constructor lookups. | |
| 1394 GotoIfNot(HasInstanceType(value, JS_PROMISE_TYPE), &if_valueisnotpromise); | |
| 1395 | |
| 1396 // This adds a fast path as non-subclassed native promises don't have | |
| 1397 // an observable constructor lookup. | |
| 1398 BranchIfFastPath(native_context, promise_fun, value, &if_valueisnativepromise, | |
| 1399 &if_valueisnotnativepromise); | |
| 1400 | |
| 1401 Bind(&if_valueisnativepromise); | |
| 1402 { | |
| 1403 GotoIfNot(WordEqual(promise_fun, receiver), &if_valueisnotnativepromise); | |
| 1404 Return(value); | |
| 1405 } | |
| 1406 | |
| 1407 // At this point, value or/and receiver are not native promises, but | |
| 1408 // they could be of the same subclass. | |
| 1409 Bind(&if_valueisnotnativepromise); | |
| 1410 { | |
| 1411 // 3.a Let xConstructor be ? Get(x, "constructor"). | |
| 1412 // The constructor lookup is observable. | |
| 1413 Node* const constructor = | |
| 1414 GetProperty(context, value, isolate->factory()->constructor_string()); | |
| 1415 | |
| 1416 // 3.b If SameValue(xConstructor, C) is true, return x. | |
| 1417 GotoIfNot(SameValue(constructor, receiver), &if_valueisnotpromise); | |
| 1418 | |
| 1419 Return(value); | |
| 1420 } | |
| 1421 | |
| 1422 Bind(&if_valueisnotpromise); | |
| 1423 { | |
| 1424 Label if_nativepromise(this), if_notnativepromise(this); | |
| 1425 Branch(WordEqual(promise_fun, receiver), &if_nativepromise, | |
| 1426 &if_notnativepromise); | |
| 1427 | |
| 1428 // This adds a fast path for native promises that don't need to | |
| 1429 // create NewPromiseCapability. | |
| 1430 Bind(&if_nativepromise); | |
| 1431 { | |
| 1432 Node* const result = AllocateAndInitJSPromise(context); | |
| 1433 InternalResolvePromise(context, result, value); | |
| 1434 Return(result); | |
| 1435 } | |
| 1436 | |
| 1437 Bind(&if_notnativepromise); | |
| 1438 { | |
| 1439 // 4. Let promiseCapability be ? NewPromiseCapability(C). | |
| 1440 Node* const capability = NewPromiseCapability(context, receiver); | |
| 1441 | |
| 1442 // 5. Perform ? Call(promiseCapability.[[Resolve]], undefined, « x »). | |
| 1443 Callable call_callable = CodeFactory::Call(isolate); | |
| 1444 Node* const resolve = | |
| 1445 LoadObjectField(capability, JSPromiseCapability::kResolveOffset); | |
| 1446 CallJS(call_callable, context, resolve, UndefinedConstant(), value); | |
| 1447 | |
| 1448 // 6. Return promiseCapability.[[Promise]]. | |
| 1449 Node* const result = | |
| 1450 LoadObjectField(capability, JSPromiseCapability::kPromiseOffset); | |
| 1451 Return(result); | |
| 1452 } | |
| 1453 } | |
| 1454 } | |
| 1455 | |
| 1456 TF_BUILTIN(PromiseGetCapabilitiesExecutor, PromiseBuiltinsAssembler) { | |
| 1457 Node* const resolve = Parameter(1); | |
| 1458 Node* const reject = Parameter(2); | |
| 1459 Node* const context = Parameter(5); | |
| 1460 | |
| 1461 Node* const capability = LoadContextElement(context, kCapabilitySlot); | |
| 1462 | |
| 1463 Label if_alreadyinvoked(this, Label::kDeferred); | |
| 1464 GotoIf(WordNotEqual( | |
| 1465 LoadObjectField(capability, JSPromiseCapability::kResolveOffset), | |
| 1466 UndefinedConstant()), | |
| 1467 &if_alreadyinvoked); | |
| 1468 GotoIf(WordNotEqual( | |
| 1469 LoadObjectField(capability, JSPromiseCapability::kRejectOffset), | |
| 1470 UndefinedConstant()), | |
| 1471 &if_alreadyinvoked); | |
| 1472 | |
| 1473 StoreObjectField(capability, JSPromiseCapability::kResolveOffset, resolve); | |
| 1474 StoreObjectField(capability, JSPromiseCapability::kRejectOffset, reject); | |
| 1475 | |
| 1476 Return(UndefinedConstant()); | |
| 1477 | |
| 1478 Bind(&if_alreadyinvoked); | |
| 1479 Node* message = SmiConstant(MessageTemplate::kPromiseExecutorAlreadyInvoked); | |
| 1480 CallRuntime(Runtime::kThrowTypeError, context, message); | |
| 1481 Unreachable(); | |
| 1482 } | |
| 1483 | |
| 1484 TF_BUILTIN(NewPromiseCapability, PromiseBuiltinsAssembler) { | |
| 1485 Node* constructor = Parameter(1); | |
| 1486 Node* debug_event = Parameter(2); | |
| 1487 Node* context = Parameter(5); | |
| 1488 | |
| 1489 CSA_ASSERT_JS_ARGC_EQ(this, 2); | |
| 1490 | |
| 1491 Return(NewPromiseCapability(context, constructor, debug_event)); | |
| 1492 } | |
| 1493 | |
| 1494 TF_BUILTIN(PromiseReject, PromiseBuiltinsAssembler) { | |
| 1495 // 1. Let C be the this value. | |
| 1496 Node* const receiver = Parameter(0); | |
| 1497 Node* const reason = Parameter(1); | |
| 1498 Node* const context = Parameter(4); | |
| 1499 | |
| 1500 // 2. If Type(C) is not Object, throw a TypeError exception. | |
| 1501 ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject, | |
| 1502 "PromiseReject"); | |
| 1503 | |
| 1504 Label if_nativepromise(this), if_custompromise(this, Label::kDeferred); | |
| 1505 Node* const native_context = LoadNativeContext(context); | |
| 1506 Node* const promise_fun = | |
| 1507 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | |
| 1508 Branch(WordEqual(promise_fun, receiver), &if_nativepromise, | |
| 1509 &if_custompromise); | |
| 1510 | |
| 1511 Bind(&if_nativepromise); | |
| 1512 { | |
| 1513 Node* const promise = AllocateAndSetJSPromise( | |
| 1514 context, SmiConstant(v8::Promise::kRejected), reason); | |
| 1515 CallRuntime(Runtime::kPromiseRejectEventFromStack, context, promise, | |
| 1516 reason); | |
| 1517 Return(promise); | |
| 1518 } | |
| 1519 | |
| 1520 Bind(&if_custompromise); | |
| 1521 { | |
| 1522 // 3. Let promiseCapability be ? NewPromiseCapability(C). | |
| 1523 Node* const capability = NewPromiseCapability(context, receiver); | |
| 1524 | |
| 1525 // 4. Perform ? Call(promiseCapability.[[Reject]], undefined, « r »). | |
| 1526 Node* const reject = | |
| 1527 LoadObjectField(capability, JSPromiseCapability::kRejectOffset); | |
| 1528 Callable call_callable = CodeFactory::Call(isolate()); | |
| 1529 CallJS(call_callable, context, reject, UndefinedConstant(), reason); | |
| 1530 | |
| 1531 // 5. Return promiseCapability.[[Promise]]. | |
| 1532 Node* const promise = | |
| 1533 LoadObjectField(capability, JSPromiseCapability::kPromiseOffset); | |
| 1534 Return(promise); | |
| 1535 } | |
| 1536 } | |
| 1537 | |
| 1538 TF_BUILTIN(InternalPromiseReject, PromiseBuiltinsAssembler) { | |
| 1539 Node* const promise = Parameter(1); | |
| 1540 Node* const reason = Parameter(2); | |
| 1541 Node* const debug_event = Parameter(3); | |
| 1542 Node* const context = Parameter(6); | |
| 1543 | |
| 1544 InternalPromiseReject(context, promise, reason, debug_event); | |
| 1545 Return(UndefinedConstant()); | |
| 1546 } | |
| 1547 | |
| 1548 Node* PromiseBuiltinsAssembler::CreatePromiseFinallyContext( | |
| 1549 Node* on_finally, Node* native_context) { | |
| 1550 Node* const context = | |
| 1551 CreatePromiseContext(native_context, kOnFinallyContextLength); | |
| 1552 StoreContextElementNoWriteBarrier(context, kOnFinallySlot, on_finally); | |
| 1553 return context; | |
| 1554 } | |
| 1555 | |
| 1556 std::pair<Node*, Node*> PromiseBuiltinsAssembler::CreatePromiseFinallyFunctions( | |
| 1557 Node* on_finally, Node* native_context) { | |
| 1558 Node* const promise_context = | |
| 1559 CreatePromiseFinallyContext(on_finally, native_context); | |
| 1560 Node* const map = LoadContextElement( | |
| 1561 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); | |
| 1562 Node* const then_finally_info = LoadContextElement( | |
| 1563 native_context, Context::PROMISE_THEN_FINALLY_SHARED_FUN); | |
| 1564 Node* const then_finally = AllocateFunctionWithMapAndContext( | |
| 1565 map, then_finally_info, promise_context); | |
| 1566 Node* const catch_finally_info = LoadContextElement( | |
| 1567 native_context, Context::PROMISE_CATCH_FINALLY_SHARED_FUN); | |
| 1568 Node* const catch_finally = AllocateFunctionWithMapAndContext( | |
| 1569 map, catch_finally_info, promise_context); | |
| 1570 return std::make_pair(then_finally, catch_finally); | |
| 1571 } | |
| 1572 | |
| 1573 TF_BUILTIN(PromiseValueThunkFinally, PromiseBuiltinsAssembler) { | |
| 1574 Node* const context = Parameter(3); | |
| 1575 | |
| 1576 Node* const value = LoadContextElement(context, kOnFinallySlot); | |
| 1577 Return(value); | |
| 1578 } | |
| 1579 | |
| 1580 Node* PromiseBuiltinsAssembler::CreateValueThunkFunctionContext( | |
| 1581 Node* value, Node* native_context) { | |
| 1582 Node* const context = | |
| 1583 CreatePromiseContext(native_context, kOnFinallyContextLength); | |
| 1584 StoreContextElementNoWriteBarrier(context, kOnFinallySlot, value); | |
| 1585 return context; | |
| 1586 } | |
| 1587 | |
| 1588 Node* PromiseBuiltinsAssembler::CreateValueThunkFunction(Node* value, | |
| 1589 Node* native_context) { | |
| 1590 Node* const value_thunk_context = | |
| 1591 CreateValueThunkFunctionContext(value, native_context); | |
| 1592 Node* const map = LoadContextElement( | |
| 1593 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); | |
| 1594 Node* const value_thunk_info = LoadContextElement( | |
| 1595 native_context, Context::PROMISE_VALUE_THUNK_FINALLY_SHARED_FUN); | |
| 1596 Node* const value_thunk = AllocateFunctionWithMapAndContext( | |
| 1597 map, value_thunk_info, value_thunk_context); | |
| 1598 return value_thunk; | |
| 1599 } | |
| 1600 | |
| 1601 TF_BUILTIN(PromiseThenFinally, PromiseBuiltinsAssembler) { | |
| 1602 CSA_ASSERT_JS_ARGC_EQ(this, 1); | |
| 1603 | |
| 1604 Node* const value = Parameter(1); | |
| 1605 Node* const context = Parameter(4); | |
| 1606 | |
| 1607 Node* const on_finally = LoadContextElement(context, kOnFinallySlot); | |
| 1608 | |
| 1609 // 2.a Let result be ? Call(onFinally, undefined). | |
| 1610 Callable call_callable = CodeFactory::Call(isolate()); | |
| 1611 Node* result = | |
| 1612 CallJS(call_callable, context, on_finally, UndefinedConstant()); | |
| 1613 | |
| 1614 // 2.b Let promise be ! PromiseResolve( %Promise%, result). | |
| 1615 Node* const promise = AllocateAndInitJSPromise(context); | |
| 1616 InternalResolvePromise(context, promise, result); | |
| 1617 | |
| 1618 // 2.c Let valueThunk be equivalent to a function that returns value. | |
| 1619 Node* native_context = LoadNativeContext(context); | |
| 1620 Node* const value_thunk = CreateValueThunkFunction(value, native_context); | |
| 1621 | |
| 1622 // 2.d Let promiseCapability be ! NewPromiseCapability( %Promise%). | |
| 1623 Node* const promise_capability = AllocateAndInitJSPromise(context, promise); | |
| 1624 | |
| 1625 // 2.e Return PerformPromiseThen(promise, valueThunk, undefined, | |
| 1626 // promiseCapability). | |
| 1627 InternalPerformPromiseThen(context, promise, value_thunk, UndefinedConstant(), | |
| 1628 promise_capability, UndefinedConstant(), | |
| 1629 UndefinedConstant()); | |
| 1630 Return(promise_capability); | |
| 1631 } | |
| 1632 | |
| 1633 TF_BUILTIN(PromiseThrowerFinally, PromiseBuiltinsAssembler) { | |
| 1634 Node* const context = Parameter(3); | |
| 1635 | |
| 1636 Node* const reason = LoadContextElement(context, kOnFinallySlot); | |
| 1637 CallRuntime(Runtime::kThrow, context, reason); | |
| 1638 Unreachable(); | |
| 1639 } | |
| 1640 | |
| 1641 Node* PromiseBuiltinsAssembler::CreateThrowerFunctionContext( | |
| 1642 Node* reason, Node* native_context) { | |
| 1643 Node* const context = | |
| 1644 CreatePromiseContext(native_context, kOnFinallyContextLength); | |
| 1645 StoreContextElementNoWriteBarrier(context, kOnFinallySlot, reason); | |
| 1646 return context; | |
| 1647 } | |
| 1648 | |
| 1649 Node* PromiseBuiltinsAssembler::CreateThrowerFunction(Node* reason, | |
| 1650 Node* native_context) { | |
| 1651 Node* const thrower_context = | |
| 1652 CreateThrowerFunctionContext(reason, native_context); | |
| 1653 Node* const map = LoadContextElement( | |
| 1654 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); | |
| 1655 Node* const thrower_info = LoadContextElement( | |
| 1656 native_context, Context::PROMISE_THROWER_FINALLY_SHARED_FUN); | |
| 1657 Node* const thrower = | |
| 1658 AllocateFunctionWithMapAndContext(map, thrower_info, thrower_context); | |
| 1659 return thrower; | |
| 1660 } | |
| 1661 | |
| 1662 TF_BUILTIN(PromiseCatchFinally, PromiseBuiltinsAssembler) { | |
| 1663 CSA_ASSERT_JS_ARGC_EQ(this, 1); | |
| 1664 | |
| 1665 Node* const reason = Parameter(1); | |
| 1666 Node* const context = Parameter(4); | |
| 1667 | |
| 1668 Node* const on_finally = LoadContextElement(context, kOnFinallySlot); | |
| 1669 | |
| 1670 // 2.a Let result be ? Call(onFinally, undefined). | |
| 1671 Callable call_callable = CodeFactory::Call(isolate()); | |
| 1672 Node* result = | |
| 1673 CallJS(call_callable, context, on_finally, UndefinedConstant()); | |
| 1674 | |
| 1675 // 2.b Let promise be ! PromiseResolve( %Promise%, result). | |
| 1676 Node* const promise = AllocateAndInitJSPromise(context); | |
| 1677 InternalResolvePromise(context, promise, result); | |
| 1678 | |
| 1679 // 2.c Let thrower be equivalent to a function that throws reason. | |
| 1680 Node* native_context = LoadNativeContext(context); | |
| 1681 Node* const thrower = CreateThrowerFunction(reason, native_context); | |
| 1682 | |
| 1683 // 2.d Let promiseCapability be ! NewPromiseCapability( %Promise%). | |
| 1684 Node* const promise_capability = AllocateAndInitJSPromise(context, promise); | |
| 1685 | |
| 1686 // 2.e Return PerformPromiseThen(promise, thrower, undefined, | |
| 1687 // promiseCapability). | |
| 1688 InternalPerformPromiseThen(context, promise, thrower, UndefinedConstant(), | |
| 1689 promise_capability, UndefinedConstant(), | |
| 1690 UndefinedConstant()); | |
| 1691 Return(promise_capability); | |
| 1692 } | |
| 1693 | |
| 1694 TF_BUILTIN(PromiseFinally, PromiseBuiltinsAssembler) { | |
| 1695 CSA_ASSERT_JS_ARGC_EQ(this, 1); | |
| 1696 | |
| 1697 // 1. Let promise be the this value. | |
| 1698 Node* const promise = Parameter(0); | |
| 1699 Node* const on_finally = Parameter(1); | |
| 1700 Node* const context = Parameter(4); | |
| 1701 | |
| 1702 // 2. If IsPromise(promise) is false, throw a TypeError exception. | |
| 1703 ThrowIfNotInstanceType(context, promise, JS_PROMISE_TYPE, | |
| 1704 "Promise.prototype.finally"); | |
| 1705 | |
| 1706 Variable var_then_finally(this, MachineRepresentation::kTagged), | |
| 1707 var_catch_finally(this, MachineRepresentation::kTagged); | |
| 1708 | |
| 1709 Label if_notcallable(this, Label::kDeferred), perform_finally(this); | |
| 1710 | |
| 1711 // 3. Let thenFinally be ! CreateThenFinally(onFinally). | |
| 1712 // 4. Let catchFinally be ! CreateCatchFinally(onFinally). | |
| 1713 GotoIf(TaggedIsSmi(on_finally), &if_notcallable); | |
| 1714 Node* const on_finally_map = LoadMap(on_finally); | |
| 1715 GotoIfNot(IsCallableMap(on_finally_map), &if_notcallable); | |
| 1716 | |
| 1717 Node* const native_context = LoadNativeContext(context); | |
| 1718 Node* then_finally = nullptr; | |
| 1719 Node* catch_finally = nullptr; | |
| 1720 std::tie(then_finally, catch_finally) = | |
| 1721 CreatePromiseFinallyFunctions(on_finally, native_context); | |
| 1722 var_then_finally.Bind(then_finally); | |
| 1723 var_catch_finally.Bind(catch_finally); | |
| 1724 Goto(&perform_finally); | |
| 1725 | |
| 1726 Bind(&if_notcallable); | |
| 1727 { | |
| 1728 var_then_finally.Bind(on_finally); | |
| 1729 var_catch_finally.Bind(on_finally); | |
| 1730 Goto(&perform_finally); | |
| 1731 } | |
| 1732 | |
| 1733 // 5. Return PerformPromiseThen(promise, valueThunk, undefined, | |
| 1734 // promiseCapability). | |
| 1735 Bind(&perform_finally); | |
| 1736 Label if_nativepromise(this), if_custompromise(this, Label::kDeferred); | |
| 1737 BranchIfFastPath(context, promise, &if_nativepromise, &if_custompromise); | |
| 1738 | |
| 1739 Bind(&if_nativepromise); | |
| 1740 { | |
| 1741 Node* deferred_promise = AllocateAndInitJSPromise(context, promise); | |
| 1742 InternalPerformPromiseThen(context, promise, var_then_finally.value(), | |
| 1743 var_catch_finally.value(), deferred_promise, | |
| 1744 UndefinedConstant(), UndefinedConstant()); | |
| 1745 Return(deferred_promise); | |
| 1746 } | |
| 1747 | |
| 1748 Bind(&if_custompromise); | |
| 1749 { | |
| 1750 Node* const then = | |
| 1751 GetProperty(context, promise, isolate()->factory()->then_string()); | |
| 1752 Callable call_callable = CodeFactory::Call(isolate()); | |
| 1753 // 5. Return ? Invoke(promise, "then", « thenFinally, catchFinally »). | |
| 1754 Node* const result = | |
| 1755 CallJS(call_callable, context, then, promise, var_then_finally.value(), | |
| 1756 var_catch_finally.value()); | |
| 1757 Return(result); | |
| 1758 } | |
| 1759 } | |
| 1760 | |
| 1761 } // namespace internal | |
| 1762 } // namespace v8 | |
| OLD | NEW |