| OLD | NEW |
| 1 // Copyright 2016 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/builtins/builtins-utils.h" | 5 #include "src/builtins/builtins-utils.h" |
| 6 #include "src/builtins/builtins.h" | 6 #include "src/builtins/builtins.h" |
| 7 | 7 |
| 8 #include "src/code-factory.h" | 8 #include "src/code-factory.h" |
| 9 #include "src/promise-utils.h" | 9 #include "src/promise-utils.h" |
| 10 | 10 |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 74 &resolve, &reject); | 74 &resolve, &reject); |
| 75 | 75 |
| 76 Handle<FixedArray> result = isolate->factory()->NewFixedArray(2); | 76 Handle<FixedArray> result = isolate->factory()->NewFixedArray(2); |
| 77 result->set(0, *resolve); | 77 result->set(0, *resolve); |
| 78 result->set(1, *reject); | 78 result->set(1, *reject); |
| 79 | 79 |
| 80 return *isolate->factory()->NewJSArrayWithElements(result, FAST_ELEMENTS, 2, | 80 return *isolate->factory()->NewJSArrayWithElements(result, FAST_ELEMENTS, 2, |
| 81 NOT_TENURED); | 81 NOT_TENURED); |
| 82 } | 82 } |
| 83 | 83 |
| 84 void PromiseInit(CodeStubAssembler* a, compiler::Node* promise, | |
| 85 compiler::Node* status, compiler::Node* result) { | |
| 86 CSA_ASSERT(a, a->TaggedIsSmi(status)); | |
| 87 a->StoreObjectField(promise, JSPromise::kStatusOffset, status); | |
| 88 a->StoreObjectField(promise, JSPromise::kResultOffset, result); | |
| 89 } | |
| 90 | |
| 91 void Builtins::Generate_PromiseConstructor( | 84 void Builtins::Generate_PromiseConstructor( |
| 92 compiler::CodeAssemblerState* state) { | 85 compiler::CodeAssemblerState* state) { |
| 93 CodeStubAssembler a(state); | 86 CodeStubAssembler a(state); |
| 94 typedef CodeStubAssembler::Variable Variable; | 87 typedef CodeStubAssembler::Variable Variable; |
| 95 typedef CodeStubAssembler::Label Label; | 88 typedef CodeStubAssembler::Label Label; |
| 96 typedef compiler::Node Node; | 89 typedef compiler::Node Node; |
| 97 | 90 |
| 98 Node* const executor = a.Parameter(1); | 91 Node* const executor = a.Parameter(1); |
| 99 Node* const new_target = a.Parameter(2); | 92 Node* const new_target = a.Parameter(2); |
| 100 Node* const context = a.Parameter(4); | 93 Node* const context = a.Parameter(4); |
| 101 Isolate* isolate = a.isolate(); | 94 Isolate* isolate = a.isolate(); |
| 102 | 95 |
| 103 Label if_targetisundefined(&a, Label::kDeferred); | 96 Label if_targetisundefined(&a, Label::kDeferred); |
| 104 | 97 |
| 105 a.GotoIf(a.IsUndefined(new_target), &if_targetisundefined); | 98 a.GotoIf(a.IsUndefined(new_target), &if_targetisundefined); |
| 106 | 99 |
| 107 Label if_notcallable(&a, Label::kDeferred); | 100 Label if_notcallable(&a, Label::kDeferred); |
| 108 | 101 |
| 109 a.GotoIf(a.TaggedIsSmi(executor), &if_notcallable); | 102 a.GotoIf(a.TaggedIsSmi(executor), &if_notcallable); |
| 110 | 103 |
| 111 Node* const executor_map = a.LoadMap(executor); | 104 Node* const executor_map = a.LoadMap(executor); |
| 112 a.GotoUnless(a.IsCallableMap(executor_map), &if_notcallable); | 105 a.GotoUnless(a.IsCallableMap(executor_map), &if_notcallable); |
| 113 | 106 |
| 114 Node* const native_context = a.LoadNativeContext(context); | 107 Node* const native_context = a.LoadNativeContext(context); |
| 115 Node* const promise_fun = | 108 Node* const promise_fun = |
| 116 a.LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | 109 a.LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); |
| 117 Node* const is_debug_active = a.IsDebugActive(); | 110 Node* const is_debug_active = a.IsDebugActive(); |
| 111 |
| 118 Label if_targetisnotmodified(&a), if_targetismodified(&a, Label::kDeferred), | 112 Label if_targetisnotmodified(&a), if_targetismodified(&a, Label::kDeferred), |
| 119 run_executor(&a), debug_push(&a, Label::kDeferred), init(&a); | 113 run_executor(&a), debug_push(&a, Label::kDeferred); |
| 120 | |
| 121 a.Branch(a.WordEqual(promise_fun, new_target), &if_targetisnotmodified, | 114 a.Branch(a.WordEqual(promise_fun, new_target), &if_targetisnotmodified, |
| 122 &if_targetismodified); | 115 &if_targetismodified); |
| 123 | 116 |
| 124 Variable var_result(&a, MachineRepresentation::kTagged), | 117 Variable var_result(&a, MachineRepresentation::kTagged), |
| 125 var_reject_call(&a, MachineRepresentation::kTagged), | 118 var_reject_call(&a, MachineRepresentation::kTagged), |
| 126 var_reason(&a, MachineRepresentation::kTagged); | 119 var_reason(&a, MachineRepresentation::kTagged); |
| 127 | 120 |
| 128 a.Bind(&if_targetisnotmodified); | 121 a.Bind(&if_targetisnotmodified); |
| 129 { | 122 { |
| 130 Node* const initial_map = a.LoadObjectField( | 123 Node* const initial_map = a.LoadObjectField( |
| 131 promise_fun, JSFunction::kPrototypeOrInitialMapOffset); | 124 promise_fun, JSFunction::kPrototypeOrInitialMapOffset); |
| 132 | 125 |
| 133 Node* const instance = a.AllocateJSObjectFromMap(initial_map); | 126 Node* const instance = a.AllocateJSObjectFromMap(initial_map); |
| 134 var_result.Bind(instance); | 127 var_result.Bind(instance); |
| 135 a.Goto(&init); | 128 a.Branch(is_debug_active, &debug_push, &run_executor); |
| 136 } | 129 } |
| 137 | 130 |
| 138 a.Bind(&if_targetismodified); | 131 a.Bind(&if_targetismodified); |
| 139 { | 132 { |
| 140 Callable fast_new_object_stub = CodeFactory::FastNewObject(isolate); | 133 Callable fast_new_object_stub = CodeFactory::FastNewObject(isolate); |
| 141 Node* const instance = | 134 Node* const instance = |
| 142 a.CallStub(fast_new_object_stub, context, promise_fun, new_target); | 135 a.CallStub(fast_new_object_stub, context, promise_fun, new_target); |
| 143 | 136 |
| 144 var_result.Bind(instance); | 137 var_result.Bind(instance); |
| 145 a.Goto(&init); | |
| 146 } | |
| 147 | |
| 148 a.Bind(&init); | |
| 149 { | |
| 150 PromiseInit(&a, var_result.value(), a.SmiConstant(kPromisePending), | |
| 151 a.UndefinedConstant()); | |
| 152 a.Branch(is_debug_active, &debug_push, &run_executor); | 138 a.Branch(is_debug_active, &debug_push, &run_executor); |
| 153 } | 139 } |
| 154 | 140 |
| 155 a.Bind(&debug_push); | 141 a.Bind(&debug_push); |
| 156 { | 142 { |
| 157 a.CallRuntime(Runtime::kDebugPushPromise, context, var_result.value()); | 143 a.CallRuntime(Runtime::kDebugPushPromise, context, var_result.value()); |
| 158 a.Goto(&run_executor); | 144 a.Goto(&run_executor); |
| 159 } | 145 } |
| 160 | 146 |
| 161 a.Bind(&run_executor); | 147 a.Bind(&run_executor); |
| 162 { | 148 { |
| 163 Label out(&a), if_rejectpromise(&a), debug_pop(&a, Label::kDeferred); | 149 Label out(&a), if_rejectpromise(&a), debug_pop(&a, Label::kDeferred); |
| 164 | 150 |
| 165 // TODO(gsathya): Move this to TF. | 151 Node* const key = a.LoadRoot(Heap::kpromise_state_symbolRootIndex); |
| 152 Node* const value = a.SmiConstant(kPromisePending); |
| 153 Node* const language_mode = a.SmiConstant(STRICT); |
| 154 // TODO(ishell): Use SetProperty stub once available. |
| 155 a.CallRuntime(Runtime::kSetProperty, context, var_result.value(), key, |
| 156 value, language_mode); |
| 166 Node* const resolving_functions = a.CallRuntime( | 157 Node* const resolving_functions = a.CallRuntime( |
| 167 Runtime::kCreateResolvingFunctions, context, var_result.value()); | 158 Runtime::kCreateResolvingFunctions, context, var_result.value()); |
| 168 Node* const resolve = | 159 Node* const resolve = |
| 169 a.LoadFixedArrayElement(resolving_functions, a.IntPtrConstant(0)); | 160 a.LoadFixedArrayElement(resolving_functions, a.IntPtrConstant(0)); |
| 170 Node* const reject = | 161 Node* const reject = |
| 171 a.LoadFixedArrayElement(resolving_functions, a.IntPtrConstant(1)); | 162 a.LoadFixedArrayElement(resolving_functions, a.IntPtrConstant(1)); |
| 172 Callable call_callable = CodeFactory::Call(isolate); | 163 Callable call_callable = CodeFactory::Call(isolate); |
| 173 | 164 |
| 174 Node* const maybe_exception = | 165 Node* const maybe_exception = |
| 175 a.CallJS(call_callable, context, executor, a.UndefinedConstant(), | 166 a.CallJS(call_callable, context, executor, a.UndefinedConstant(), |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 218 typedef compiler::Node Node; | 209 typedef compiler::Node Node; |
| 219 CodeStubAssembler a(state); | 210 CodeStubAssembler a(state); |
| 220 | 211 |
| 221 Node* const context = a.Parameter(3); | 212 Node* const context = a.Parameter(3); |
| 222 Node* const native_context = a.LoadNativeContext(context); | 213 Node* const native_context = a.LoadNativeContext(context); |
| 223 Node* const promise_fun = | 214 Node* const promise_fun = |
| 224 a.LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | 215 a.LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); |
| 225 Node* const initial_map = | 216 Node* const initial_map = |
| 226 a.LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset); | 217 a.LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset); |
| 227 Node* const instance = a.AllocateJSObjectFromMap(initial_map); | 218 Node* const instance = a.AllocateJSObjectFromMap(initial_map); |
| 228 | |
| 229 PromiseInit(&a, instance, a.SmiConstant(kPromisePending), | |
| 230 a.UndefinedConstant()); | |
| 231 a.Return(instance); | 219 a.Return(instance); |
| 232 } | 220 } |
| 233 | 221 |
| 234 void Builtins::Generate_PromiseCreateAndSet( | |
| 235 compiler::CodeAssemblerState* state) { | |
| 236 typedef compiler::Node Node; | |
| 237 CodeStubAssembler a(state); | |
| 238 | |
| 239 Node* const status = a.Parameter(1); | |
| 240 Node* const result = a.Parameter(2); | |
| 241 Node* const context = a.Parameter(5); | |
| 242 Node* const native_context = a.LoadNativeContext(context); | |
| 243 | |
| 244 Node* const promise_fun = | |
| 245 a.LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | |
| 246 Node* const initial_map = | |
| 247 a.LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset); | |
| 248 Node* const instance = a.AllocateJSObjectFromMap(initial_map); | |
| 249 | |
| 250 PromiseInit(&a, instance, status, result); | |
| 251 a.Return(instance); | |
| 252 } | |
| 253 | |
| 254 namespace { | |
| 255 | |
| 256 compiler::Node* ThrowIfNotJSReceiver(CodeStubAssembler* a, Isolate* isolate, | |
| 257 compiler::Node* context, | |
| 258 compiler::Node* value, | |
| 259 MessageTemplate::Template msg_template) { | |
| 260 typedef compiler::Node Node; | |
| 261 typedef CodeStubAssembler::Label Label; | |
| 262 typedef CodeStubAssembler::Variable Variable; | |
| 263 | |
| 264 Label out(a), throw_exception(a, Label::kDeferred); | |
| 265 Variable var_value_map(a, MachineRepresentation::kTagged); | |
| 266 | |
| 267 a->GotoIf(a->TaggedIsSmi(value), &throw_exception); | |
| 268 | |
| 269 // Load the instance type of the {value}. | |
| 270 var_value_map.Bind(a->LoadMap(value)); | |
| 271 Node* const value_instance_type = | |
| 272 a->LoadMapInstanceType(var_value_map.value()); | |
| 273 | |
| 274 a->Branch(a->IsJSReceiverInstanceType(value_instance_type), &out, | |
| 275 &throw_exception); | |
| 276 | |
| 277 // The {value} is not a compatible receiver for this method. | |
| 278 a->Bind(&throw_exception); | |
| 279 { | |
| 280 Node* const message_id = a->SmiConstant(msg_template); | |
| 281 a->CallRuntime(Runtime::kThrowTypeError, context, message_id); | |
| 282 var_value_map.Bind(a->UndefinedConstant()); | |
| 283 a->Goto(&out); // Never reached. | |
| 284 } | |
| 285 | |
| 286 a->Bind(&out); | |
| 287 return var_value_map.value(); | |
| 288 } | |
| 289 } // namespace | |
| 290 | |
| 291 void Builtins::Generate_IsPromise(compiler::CodeAssemblerState* state) { | 222 void Builtins::Generate_IsPromise(compiler::CodeAssemblerState* state) { |
| 292 CodeStubAssembler a(state); | 223 CodeStubAssembler a(state); |
| 293 typedef compiler::Node Node; | 224 typedef compiler::Node Node; |
| 294 typedef CodeStubAssembler::Label Label; | 225 typedef CodeStubAssembler::Label Label; |
| 295 | 226 |
| 296 Node* const maybe_promise = a.Parameter(1); | 227 Node* const maybe_promise = a.Parameter(1); |
| 297 Label if_notpromise(&a, Label::kDeferred); | 228 Label if_ispromise(&a), if_isnotpromise(&a, Label::kDeferred); |
| 229 a.GotoIf(a.TaggedIsSmi(maybe_promise), &if_isnotpromise); |
| 298 | 230 |
| 299 a.GotoIf(a.TaggedIsSmi(maybe_promise), &if_notpromise); | 231 a.Branch(a.HasInstanceType(maybe_promise, JS_PROMISE_TYPE), &if_ispromise, |
| 232 &if_isnotpromise); |
| 300 | 233 |
| 301 Node* const result = a.SelectBooleanConstant( | 234 a.Bind(&if_ispromise); |
| 302 a.HasInstanceType(maybe_promise, JS_PROMISE_TYPE)); | 235 a.Return(a.BooleanConstant(true)); |
| 303 a.Return(result); | |
| 304 | 236 |
| 305 a.Bind(&if_notpromise); | 237 a.Bind(&if_isnotpromise); |
| 306 a.Return(a.FalseConstant()); | 238 a.Return(a.BooleanConstant(false)); |
| 307 } | |
| 308 | |
| 309 namespace { | |
| 310 compiler::Node* SpeciesConstructor(CodeStubAssembler* a, Isolate* isolate, | |
| 311 compiler::Node* context, | |
| 312 compiler::Node* object, | |
| 313 compiler::Node* default_constructor) { | |
| 314 typedef compiler::Node Node; | |
| 315 typedef CodeStubAssembler::Label Label; | |
| 316 typedef CodeStubAssembler::Variable Variable; | |
| 317 | |
| 318 Variable var_result(a, MachineRepresentation::kTagged); | |
| 319 var_result.Bind(default_constructor); | |
| 320 | |
| 321 // 2. Let C be ? Get(O, "constructor"). | |
| 322 Node* const constructor_str = | |
| 323 a->HeapConstant(isolate->factory()->constructor_string()); | |
| 324 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); | |
| 325 Node* const constructor = | |
| 326 a->CallStub(getproperty_callable, context, object, constructor_str); | |
| 327 | |
| 328 // 3. If C is undefined, return defaultConstructor. | |
| 329 Label out(a); | |
| 330 a->GotoIf(a->IsUndefined(constructor), &out); | |
| 331 | |
| 332 // 4. If Type(C) is not Object, throw a TypeError exception. | |
| 333 ThrowIfNotJSReceiver(a, a->isolate(), context, constructor, | |
| 334 MessageTemplate::kConstructorNotReceiver); | |
| 335 | |
| 336 // 5. Let S be ? Get(C, @@species). | |
| 337 Node* const species_symbol = | |
| 338 a->HeapConstant(isolate->factory()->species_symbol()); | |
| 339 Node* const species = | |
| 340 a->CallStub(getproperty_callable, context, constructor, species_symbol); | |
| 341 | |
| 342 // 6. If S is either undefined or null, return defaultConstructor. | |
| 343 a->GotoIf(a->IsUndefined(species), &out); | |
| 344 a->GotoIf(a->WordEqual(species, a->NullConstant()), &out); | |
| 345 | |
| 346 // 7. If IsConstructor(S) is true, return S. | |
| 347 Label throw_error(a); | |
| 348 Node* species_bitfield = a->LoadMapBitField(a->LoadMap(species)); | |
| 349 a->GotoUnless( | |
| 350 a->Word32Equal(a->Word32And(species_bitfield, | |
| 351 a->Int32Constant((1 << Map::kIsConstructor))), | |
| 352 a->Int32Constant(1 << Map::kIsConstructor)), | |
| 353 &throw_error); | |
| 354 var_result.Bind(species); | |
| 355 a->Goto(&out); | |
| 356 | |
| 357 // 8. Throw a TypeError exception. | |
| 358 a->Bind(&throw_error); | |
| 359 { | |
| 360 Node* const message_id = | |
| 361 a->SmiConstant(MessageTemplate::kSpeciesNotConstructor); | |
| 362 a->CallRuntime(Runtime::kThrowTypeError, context, message_id); | |
| 363 a->Goto(&out); | |
| 364 } | |
| 365 | |
| 366 a->Bind(&out); | |
| 367 return var_result.value(); | |
| 368 } | |
| 369 | |
| 370 void AppendPromiseCallback(CodeStubAssembler* a, int offset, | |
| 371 compiler::Node* promise, compiler::Node* value) { | |
| 372 typedef compiler::Node Node; | |
| 373 | |
| 374 Node* elements = a->LoadObjectField(promise, offset); | |
| 375 Node* length = a->LoadFixedArrayBaseLength(elements); | |
| 376 CodeStubAssembler::ParameterMode mode = a->OptimalParameterMode(); | |
| 377 length = a->UntagParameter(length, mode); | |
| 378 | |
| 379 Node* delta = a->IntPtrOrSmiConstant(1, mode); | |
| 380 Node* new_capacity = a->IntPtrAdd(length, delta); | |
| 381 ElementsKind kind = FAST_ELEMENTS; | |
| 382 | |
| 383 Node* new_elements = a->AllocateFixedArray(kind, new_capacity, mode); | |
| 384 | |
| 385 a->CopyFixedArrayElements(kind, elements, new_elements, length, | |
| 386 UPDATE_WRITE_BARRIER, mode); | |
| 387 a->StoreFixedArrayElement(new_elements, length, value, UPDATE_WRITE_BARRIER, | |
| 388 0, mode); | |
| 389 | |
| 390 a->StoreObjectField(promise, offset, new_elements); | |
| 391 } | |
| 392 | |
| 393 void InternalPerformPromiseThen(CodeStubAssembler* a, compiler::Node* context, | |
| 394 compiler::Node* promise, | |
| 395 compiler::Node* on_resolve, | |
| 396 compiler::Node* on_reject, | |
| 397 compiler::Node* deferred) { | |
| 398 typedef CodeStubAssembler::Variable Variable; | |
| 399 typedef CodeStubAssembler::Label Label; | |
| 400 typedef compiler::Node Node; | |
| 401 | |
| 402 Isolate* isolate = a->isolate(); | |
| 403 Node* const native_context = a->LoadNativeContext(context); | |
| 404 | |
| 405 Variable var_on_resolve(a, MachineRepresentation::kTagged), | |
| 406 var_on_reject(a, MachineRepresentation::kTagged); | |
| 407 | |
| 408 var_on_resolve.Bind(on_resolve); | |
| 409 var_on_reject.Bind(on_reject); | |
| 410 | |
| 411 Label out(a), if_onresolvenotcallable(a), onrejectcheck(a), | |
| 412 append_callbacks(a); | |
| 413 a->GotoIf(a->TaggedIsSmi(on_resolve), &if_onresolvenotcallable); | |
| 414 | |
| 415 Node* const on_resolve_map = a->LoadMap(on_resolve); | |
| 416 a->Branch(a->IsCallableMap(on_resolve_map), &onrejectcheck, | |
| 417 &if_onresolvenotcallable); | |
| 418 | |
| 419 a->Bind(&if_onresolvenotcallable); | |
| 420 { | |
| 421 var_on_resolve.Bind(a->LoadContextElement( | |
| 422 native_context, Context::PROMISE_ID_RESOLVE_HANDLER_INDEX)); | |
| 423 a->Goto(&onrejectcheck); | |
| 424 } | |
| 425 | |
| 426 a->Bind(&onrejectcheck); | |
| 427 { | |
| 428 Label if_onrejectnotcallable(a); | |
| 429 a->GotoIf(a->TaggedIsSmi(on_reject), &if_onrejectnotcallable); | |
| 430 | |
| 431 Node* const on_reject_map = a->LoadMap(on_reject); | |
| 432 a->Branch(a->IsCallableMap(on_reject_map), &append_callbacks, | |
| 433 &if_onrejectnotcallable); | |
| 434 | |
| 435 a->Bind(&if_onrejectnotcallable); | |
| 436 { | |
| 437 var_on_reject.Bind(a->LoadContextElement( | |
| 438 native_context, Context::PROMISE_ID_REJECT_HANDLER_INDEX)); | |
| 439 a->Goto(&append_callbacks); | |
| 440 } | |
| 441 } | |
| 442 | |
| 443 a->Bind(&append_callbacks); | |
| 444 { | |
| 445 Label fulfilled_check(a); | |
| 446 Node* const status = a->LoadObjectField(promise, JSPromise::kStatusOffset); | |
| 447 a->GotoUnless(a->SmiEqual(status, a->SmiConstant(kPromisePending)), | |
| 448 &fulfilled_check); | |
| 449 | |
| 450 Node* const existing_deferred = | |
| 451 a->LoadObjectField(promise, JSPromise::kDeferredOffset); | |
| 452 | |
| 453 Label if_noexistingcallbacks(a), if_existingcallbacks(a); | |
| 454 a->Branch(a->IsUndefined(existing_deferred), &if_noexistingcallbacks, | |
| 455 &if_existingcallbacks); | |
| 456 | |
| 457 a->Bind(&if_noexistingcallbacks); | |
| 458 { | |
| 459 // Store callbacks directly in the slots. | |
| 460 a->StoreObjectField(promise, JSPromise::kDeferredOffset, deferred); | |
| 461 a->StoreObjectField(promise, JSPromise::kFulfillReactionsOffset, | |
| 462 var_on_resolve.value()); | |
| 463 a->StoreObjectField(promise, JSPromise::kRejectReactionsOffset, | |
| 464 var_on_reject.value()); | |
| 465 a->Goto(&out); | |
| 466 } | |
| 467 | |
| 468 a->Bind(&if_existingcallbacks); | |
| 469 { | |
| 470 Label if_singlecallback(a), if_multiplecallbacks(a); | |
| 471 a->BranchIfJSObject(existing_deferred, &if_singlecallback, | |
| 472 &if_multiplecallbacks); | |
| 473 | |
| 474 a->Bind(&if_singlecallback); | |
| 475 { | |
| 476 // Create new FixedArrays to store callbacks. | |
| 477 Node* const deferreds = | |
| 478 a->AllocateFixedArray(FAST_ELEMENTS, a->Int32Constant(2)); | |
| 479 Node* const fulfill_reactions = | |
| 480 a->AllocateFixedArray(FAST_ELEMENTS, a->Int32Constant(2)); | |
| 481 Node* const reject_reactions = | |
| 482 a->AllocateFixedArray(FAST_ELEMENTS, a->Int32Constant(2)); | |
| 483 | |
| 484 // Store existing callbacks in FixedArrays. | |
| 485 a->StoreFixedArrayElement(deferreds, 0, existing_deferred); | |
| 486 a->StoreFixedArrayElement( | |
| 487 fulfill_reactions, 0, | |
| 488 a->LoadObjectField(promise, JSPromise::kFulfillReactionsOffset)); | |
| 489 a->StoreFixedArrayElement( | |
| 490 reject_reactions, 0, | |
| 491 a->LoadObjectField(promise, JSPromise::kRejectReactionsOffset)); | |
| 492 | |
| 493 // Store new callbacks in FixedArrays. | |
| 494 a->StoreFixedArrayElement(deferreds, 1, deferred); | |
| 495 a->StoreFixedArrayElement(fulfill_reactions, 1, var_on_resolve.value()); | |
| 496 a->StoreFixedArrayElement(reject_reactions, 1, var_on_reject.value()); | |
| 497 | |
| 498 // Store new FixedArrays in promise. | |
| 499 a->StoreObjectField(promise, JSPromise::kDeferredOffset, deferreds); | |
| 500 a->StoreObjectField(promise, JSPromise::kFulfillReactionsOffset, | |
| 501 fulfill_reactions); | |
| 502 a->StoreObjectField(promise, JSPromise::kRejectReactionsOffset, | |
| 503 reject_reactions); | |
| 504 a->Goto(&out); | |
| 505 } | |
| 506 | |
| 507 a->Bind(&if_multiplecallbacks); | |
| 508 { | |
| 509 AppendPromiseCallback(a, JSPromise::kDeferredOffset, promise, deferred); | |
| 510 AppendPromiseCallback(a, JSPromise::kFulfillReactionsOffset, promise, | |
| 511 var_on_resolve.value()); | |
| 512 AppendPromiseCallback(a, JSPromise::kRejectReactionsOffset, promise, | |
| 513 var_on_reject.value()); | |
| 514 a->Goto(&out); | |
| 515 } | |
| 516 } | |
| 517 | |
| 518 a->Bind(&fulfilled_check); | |
| 519 { | |
| 520 Label reject(a); | |
| 521 Node* const result = | |
| 522 a->LoadObjectField(promise, JSPromise::kResultOffset); | |
| 523 a->GotoUnless(a->WordEqual(status, a->SmiConstant(kPromiseFulfilled)), | |
| 524 &reject); | |
| 525 | |
| 526 // TODO(gsathya): Move this to TF. | |
| 527 a->CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, result, | |
| 528 var_on_resolve.value(), deferred, | |
| 529 a->SmiConstant(kPromiseFulfilled)); | |
| 530 a->Goto(&out); | |
| 531 | |
| 532 a->Bind(&reject); | |
| 533 { | |
| 534 Callable getproperty_callable = CodeFactory::GetProperty(isolate); | |
| 535 Node* const key = | |
| 536 a->HeapConstant(isolate->factory()->promise_has_handler_symbol()); | |
| 537 Node* const has_handler = | |
| 538 a->CallStub(getproperty_callable, context, promise, key); | |
| 539 | |
| 540 Label enqueue(a); | |
| 541 | |
| 542 // TODO(gsathya): Fold these runtime calls and move to TF. | |
| 543 a->GotoIf(a->WordEqual(has_handler, a->TrueConstant()), &enqueue); | |
| 544 a->CallRuntime(Runtime::kPromiseRevokeReject, context, promise); | |
| 545 a->Goto(&enqueue); | |
| 546 | |
| 547 a->Bind(&enqueue); | |
| 548 { | |
| 549 a->CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, result, | |
| 550 var_on_reject.value(), deferred, | |
| 551 a->SmiConstant(kPromiseRejected)); | |
| 552 | |
| 553 a->Goto(&out); | |
| 554 } | |
| 555 } | |
| 556 } | |
| 557 } | |
| 558 | |
| 559 a->Bind(&out); | |
| 560 } | |
| 561 | |
| 562 } // namespace | |
| 563 | |
| 564 void Builtins::Generate_PerformPromiseThen( | |
| 565 compiler::CodeAssemblerState* state) { | |
| 566 CodeStubAssembler a(state); | |
| 567 typedef compiler::Node Node; | |
| 568 | |
| 569 Node* const promise = a.Parameter(1); | |
| 570 Node* const on_resolve = a.Parameter(2); | |
| 571 Node* const on_reject = a.Parameter(3); | |
| 572 Node* const deferred = a.Parameter(4); | |
| 573 Node* const context = a.Parameter(7); | |
| 574 | |
| 575 InternalPerformPromiseThen(&a, context, promise, on_resolve, on_reject, | |
| 576 deferred); | |
| 577 | |
| 578 // TODO(gsathya): This is unused, but value is returned according to spec. | |
| 579 a.Return(promise); | |
| 580 } | |
| 581 | |
| 582 void Builtins::Generate_PromiseThen(compiler::CodeAssemblerState* state) { | |
| 583 CodeStubAssembler a(state); | |
| 584 typedef compiler::Node Node; | |
| 585 typedef CodeStubAssembler::Label Label; | |
| 586 typedef CodeStubAssembler::Variable Variable; | |
| 587 | |
| 588 // 1. Let promise be the this value. | |
| 589 Node* const promise = a.Parameter(0); | |
| 590 Node* const on_resolve = a.Parameter(1); | |
| 591 Node* const on_reject = a.Parameter(2); | |
| 592 Node* const context = a.Parameter(5); | |
| 593 Isolate* isolate = a.isolate(); | |
| 594 | |
| 595 // 2. If IsPromise(promise) is false, throw a TypeError exception. | |
| 596 a.ThrowIfNotInstanceType(context, promise, JS_PROMISE_TYPE, | |
| 597 "Promise.prototype.then"); | |
| 598 | |
| 599 Node* const native_context = a.LoadNativeContext(context); | |
| 600 Node* const promise_fun = | |
| 601 a.LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | |
| 602 | |
| 603 // 3. Let C be ? SpeciesConstructor(promise, %Promise%). | |
| 604 Node* constructor = | |
| 605 SpeciesConstructor(&a, isolate, context, promise, promise_fun); | |
| 606 | |
| 607 // 4. Let resultCapability be ? NewPromiseCapability(C). | |
| 608 Callable call_callable = CodeFactory::Call(isolate); | |
| 609 Label fast_promise_capability(&a), promise_capability(&a), | |
| 610 perform_promise_then(&a); | |
| 611 Variable var_deferred(&a, MachineRepresentation::kTagged); | |
| 612 | |
| 613 a.Branch(a.WordEqual(promise_fun, constructor), &fast_promise_capability, | |
| 614 &promise_capability); | |
| 615 | |
| 616 // TODO(gsathya): Remove deferred object and move | |
| 617 // NewPromiseCapbability functions to TF. | |
| 618 a.Bind(&fast_promise_capability); | |
| 619 { | |
| 620 // TODO(gsathya): Move this to TF. | |
| 621 Node* const promise_internal_capability = a.LoadContextElement( | |
| 622 native_context, Context::INTERNAL_PROMISE_CAPABILITY_INDEX); | |
| 623 Node* const capability = | |
| 624 a.CallJS(call_callable, context, promise_internal_capability, | |
| 625 a.UndefinedConstant()); | |
| 626 var_deferred.Bind(capability); | |
| 627 a.Goto(&perform_promise_then); | |
| 628 } | |
| 629 | |
| 630 a.Bind(&promise_capability); | |
| 631 { | |
| 632 // TODO(gsathya): Move this to TF. | |
| 633 Node* const new_promise_capability = a.LoadContextElement( | |
| 634 native_context, Context::NEW_PROMISE_CAPABILITY_INDEX); | |
| 635 Node* const capability = | |
| 636 a.CallJS(call_callable, context, new_promise_capability, | |
| 637 a.UndefinedConstant(), constructor); | |
| 638 var_deferred.Bind(capability); | |
| 639 a.Goto(&perform_promise_then); | |
| 640 } | |
| 641 | |
| 642 // 5. Return PerformPromiseThen(promise, onFulfilled, onRejected, | |
| 643 // resultCapability). | |
| 644 a.Bind(&perform_promise_then); | |
| 645 InternalPerformPromiseThen(&a, context, promise, on_resolve, on_reject, | |
| 646 var_deferred.value()); | |
| 647 | |
| 648 // TODO(gsathya): Protect with debug check. | |
| 649 a.CallRuntime( | |
| 650 Runtime::kSetProperty, context, promise, | |
| 651 a.HeapConstant(isolate->factory()->promise_has_handler_symbol()), | |
| 652 a.TrueConstant(), a.SmiConstant(STRICT)); | |
| 653 | |
| 654 // TODO(gsathya): This call will be removed once we don't have to | |
| 655 // deal with deferred objects. | |
| 656 Callable getproperty_callable = CodeFactory::GetProperty(isolate); | |
| 657 Node* const key = | |
| 658 a.HeapConstant(isolate->factory()->NewStringFromAsciiChecked("promise")); | |
| 659 Node* const result = | |
| 660 a.CallStub(getproperty_callable, context, var_deferred.value(), key); | |
| 661 | |
| 662 a.Return(result); | |
| 663 } | 239 } |
| 664 | 240 |
| 665 } // namespace internal | 241 } // namespace internal |
| 666 } // namespace v8 | 242 } // namespace v8 |
| OLD | NEW |