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-promise.h" | 5 #include "src/builtins/builtins-promise.h" |
6 #include "src/builtins/builtins-constructor.h" | 6 #include "src/builtins/builtins-constructor.h" |
7 #include "src/builtins/builtins-utils.h" | 7 #include "src/builtins/builtins-utils.h" |
8 #include "src/builtins/builtins.h" | 8 #include "src/builtins/builtins.h" |
9 #include "src/code-factory.h" | 9 #include "src/code-factory.h" |
10 #include "src/code-stub-assembler.h" | 10 #include "src/code-stub-assembler.h" |
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
225 Node* promise_capability, Node* native_context) { | 225 Node* promise_capability, Node* native_context) { |
226 int kContextLength = GetPromiseCapabilityExecutor::kContextLength; | 226 int kContextLength = GetPromiseCapabilityExecutor::kContextLength; |
227 Node* context = CreatePromiseContext(native_context, kContextLength); | 227 Node* context = CreatePromiseContext(native_context, kContextLength); |
228 StoreContextElementNoWriteBarrier( | 228 StoreContextElementNoWriteBarrier( |
229 context, GetPromiseCapabilityExecutor::kCapabilitySlot, | 229 context, GetPromiseCapabilityExecutor::kCapabilitySlot, |
230 promise_capability); | 230 promise_capability); |
231 return context; | 231 return context; |
232 } | 232 } |
233 | 233 |
234 Node* PromiseBuiltinsAssembler::ThrowIfNotJSReceiver( | 234 Node* PromiseBuiltinsAssembler::ThrowIfNotJSReceiver( |
235 Node* context, Node* value, MessageTemplate::Template msg_template) { | 235 Node* context, Node* value, MessageTemplate::Template msg_template, |
| 236 const char* method_name) { |
236 Label out(this), throw_exception(this, Label::kDeferred); | 237 Label out(this), throw_exception(this, Label::kDeferred); |
237 Variable var_value_map(this, MachineRepresentation::kTagged); | 238 Variable var_value_map(this, MachineRepresentation::kTagged); |
238 | 239 |
239 GotoIf(TaggedIsSmi(value), &throw_exception); | 240 GotoIf(TaggedIsSmi(value), &throw_exception); |
240 | 241 |
241 // Load the instance type of the {value}. | 242 // Load the instance type of the {value}. |
242 var_value_map.Bind(LoadMap(value)); | 243 var_value_map.Bind(LoadMap(value)); |
243 Node* const value_instance_type = LoadMapInstanceType(var_value_map.value()); | 244 Node* const value_instance_type = LoadMapInstanceType(var_value_map.value()); |
244 | 245 |
245 Branch(IsJSReceiverInstanceType(value_instance_type), &out, &throw_exception); | 246 Branch(IsJSReceiverInstanceType(value_instance_type), &out, &throw_exception); |
246 | 247 |
247 // The {value} is not a compatible receiver for this method. | 248 // The {value} is not a compatible receiver for this method. |
248 Bind(&throw_exception); | 249 Bind(&throw_exception); |
249 { | 250 { |
| 251 Node* const method = |
| 252 method_name == nullptr |
| 253 ? UndefinedConstant() |
| 254 : HeapConstant( |
| 255 isolate()->factory()->NewStringFromAsciiChecked(method_name)); |
250 Node* const message_id = SmiConstant(msg_template); | 256 Node* const message_id = SmiConstant(msg_template); |
251 CallRuntime(Runtime::kThrowTypeError, context, message_id); | 257 CallRuntime(Runtime::kThrowTypeError, context, message_id, method); |
252 var_value_map.Bind(UndefinedConstant()); | 258 var_value_map.Bind(UndefinedConstant()); |
253 Goto(&out); // Never reached. | 259 Goto(&out); // Never reached. |
254 } | 260 } |
255 | 261 |
256 Bind(&out); | 262 Bind(&out); |
257 return var_value_map.value(); | 263 return var_value_map.value(); |
258 } | 264 } |
259 | 265 |
260 Node* PromiseBuiltinsAssembler::PromiseHasHandler(Node* promise) { | 266 Node* PromiseBuiltinsAssembler::PromiseHasHandler(Node* promise) { |
261 Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset); | 267 Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset); |
(...skipping 338 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
600 } | 606 } |
601 | 607 |
602 // Promise fast path implementations rely on unmodified JSPromise instances. | 608 // Promise fast path implementations rely on unmodified JSPromise instances. |
603 // We use a fairly coarse granularity for this and simply check whether both | 609 // We use a fairly coarse granularity for this and simply check whether both |
604 // the promise itself is unmodified (i.e. its map has not changed) and its | 610 // the promise itself is unmodified (i.e. its map has not changed) and its |
605 // prototype is unmodified. | 611 // prototype is unmodified. |
606 // TODO(gsathya): Refactor this out to prevent code dupe with builtins-regexp | 612 // TODO(gsathya): Refactor this out to prevent code dupe with builtins-regexp |
607 void PromiseBuiltinsAssembler::BranchIfFastPath(Node* context, Node* promise, | 613 void PromiseBuiltinsAssembler::BranchIfFastPath(Node* context, Node* promise, |
608 Label* if_isunmodified, | 614 Label* if_isunmodified, |
609 Label* if_ismodified) { | 615 Label* if_ismodified) { |
610 // TODO(gsathya): Assert if promise is receiver | |
611 Node* const map = LoadMap(promise); | |
612 Node* const native_context = LoadNativeContext(context); | 616 Node* const native_context = LoadNativeContext(context); |
613 Node* const promise_fun = | 617 Node* const promise_fun = |
614 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | 618 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); |
| 619 BranchIfFastPath(native_context, promise_fun, promise, if_isunmodified, |
| 620 if_ismodified); |
| 621 } |
| 622 |
| 623 void PromiseBuiltinsAssembler::BranchIfFastPath(Node* native_context, |
| 624 Node* promise_fun, |
| 625 Node* promise, |
| 626 Label* if_isunmodified, |
| 627 Label* if_ismodified) { |
| 628 CSA_ASSERT(this, IsNativeContext(native_context)); |
| 629 CSA_ASSERT(this, |
| 630 WordEqual(promise_fun, |
| 631 LoadContextElement(native_context, |
| 632 Context::PROMISE_FUNCTION_INDEX))); |
| 633 |
| 634 Node* const map = LoadMap(promise); |
615 Node* const initial_map = | 635 Node* const initial_map = |
616 LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset); | 636 LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset); |
617 Node* const has_initialmap = WordEqual(map, initial_map); | 637 Node* const has_initialmap = WordEqual(map, initial_map); |
618 | 638 |
619 GotoUnless(has_initialmap, if_ismodified); | 639 GotoUnless(has_initialmap, if_ismodified); |
620 | 640 |
621 Node* const initial_proto_initial_map = | 641 Node* const initial_proto_initial_map = |
622 LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_MAP_INDEX); | 642 LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_MAP_INDEX); |
623 Node* const proto_map = LoadMap(LoadMapPrototype(map)); | 643 Node* const proto_map = LoadMap(LoadMapPrototype(map)); |
624 Node* const proto_has_initialmap = | 644 Node* const proto_has_initialmap = |
(...skipping 20 matching lines...) Expand all Loading... |
645 | 665 |
646 Bind(&cycle_check); | 666 Bind(&cycle_check); |
647 // 6. If SameValue(resolution, promise) is true, then | 667 // 6. If SameValue(resolution, promise) is true, then |
648 GotoIf(SameValue(promise, result, context), &if_cycle); | 668 GotoIf(SameValue(promise, result, context), &if_cycle); |
649 | 669 |
650 // 7. If Type(resolution) is not Object, then | 670 // 7. If Type(resolution) is not Object, then |
651 GotoIf(TaggedIsSmi(result), &fulfill); | 671 GotoIf(TaggedIsSmi(result), &fulfill); |
652 GotoUnless(IsJSReceiver(result), &fulfill); | 672 GotoUnless(IsJSReceiver(result), &fulfill); |
653 | 673 |
654 Label if_nativepromise(this), if_notnativepromise(this, Label::kDeferred); | 674 Label if_nativepromise(this), if_notnativepromise(this, Label::kDeferred); |
655 BranchIfFastPath(context, result, &if_nativepromise, &if_notnativepromise); | 675 Node* const native_context = LoadNativeContext(context); |
| 676 Node* const promise_fun = |
| 677 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); |
| 678 BranchIfFastPath(native_context, promise_fun, result, &if_nativepromise, |
| 679 &if_notnativepromise); |
656 | 680 |
657 // Resolution is a native promise and if it's already resolved or | 681 // Resolution is a native promise and if it's already resolved or |
658 // rejected, shortcircuit the resolution procedure by directly | 682 // rejected, shortcircuit the resolution procedure by directly |
659 // reusing the value from the promise. | 683 // reusing the value from the promise. |
660 Bind(&if_nativepromise); | 684 Bind(&if_nativepromise); |
661 { | 685 { |
662 Node* const thenable_status = | 686 Node* const thenable_status = |
663 LoadObjectField(result, JSPromise::kStatusOffset); | 687 LoadObjectField(result, JSPromise::kStatusOffset); |
664 Node* const thenable_value = | 688 Node* const thenable_value = |
665 LoadObjectField(result, JSPromise::kResultOffset); | 689 LoadObjectField(result, JSPromise::kResultOffset); |
666 | 690 |
667 Label if_isnotpending(this); | 691 Label if_isnotpending(this); |
668 GotoUnless(SmiEqual(SmiConstant(v8::Promise::kPending), thenable_status), | 692 GotoUnless(SmiEqual(SmiConstant(v8::Promise::kPending), thenable_status), |
669 &if_isnotpending); | 693 &if_isnotpending); |
670 | 694 |
671 // TODO(gsathya): Use a marker here instead of the actual then | 695 // TODO(gsathya): Use a marker here instead of the actual then |
672 // callback, and check for the marker in PromiseResolveThenableJob | 696 // callback, and check for the marker in PromiseResolveThenableJob |
673 // and perform PromiseThen. | 697 // and perform PromiseThen. |
674 Node* const native_context = LoadNativeContext(context); | |
675 Node* const then = | 698 Node* const then = |
676 LoadContextElement(native_context, Context::PROMISE_THEN_INDEX); | 699 LoadContextElement(native_context, Context::PROMISE_THEN_INDEX); |
677 var_then.Bind(then); | 700 var_then.Bind(then); |
678 Goto(&do_enqueue); | 701 Goto(&do_enqueue); |
679 | 702 |
680 Bind(&if_isnotpending); | 703 Bind(&if_isnotpending); |
681 { | 704 { |
682 Label if_fulfilled(this), if_rejected(this); | 705 Label if_fulfilled(this), if_rejected(this); |
683 Branch(SmiEqual(SmiConstant(v8::Promise::kFulfilled), thenable_status), | 706 Branch(SmiEqual(SmiConstant(v8::Promise::kFulfilled), thenable_status), |
684 &if_fulfilled, &if_rejected); | 707 &if_fulfilled, &if_rejected); |
(...skipping 515 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1200 Callable getproperty_callable = CodeFactory::GetProperty(isolate); | 1223 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
1201 Node* const then = | 1224 Node* const then = |
1202 CallStub(getproperty_callable, context, promise, then_str); | 1225 CallStub(getproperty_callable, context, promise, then_str); |
1203 Callable call_callable = CodeFactory::Call(isolate); | 1226 Callable call_callable = CodeFactory::Call(isolate); |
1204 Node* const result = | 1227 Node* const result = |
1205 CallJS(call_callable, context, then, promise, on_resolve, on_reject); | 1228 CallJS(call_callable, context, then, promise, on_resolve, on_reject); |
1206 Return(result); | 1229 Return(result); |
1207 } | 1230 } |
1208 } | 1231 } |
1209 | 1232 |
| 1233 TF_BUILTIN(PromiseResolve, PromiseBuiltinsAssembler) { |
| 1234 // 1. Let C be the this value. |
| 1235 Node* receiver = Parameter(0); |
| 1236 Node* value = Parameter(1); |
| 1237 Node* context = Parameter(4); |
| 1238 Isolate* isolate = this->isolate(); |
| 1239 |
| 1240 // 2. If Type(C) is not Object, throw a TypeError exception. |
| 1241 ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject, |
| 1242 "PromiseResolve"); |
| 1243 |
| 1244 Label if_valueisnativepromise(this), if_valueisnotnativepromise(this), |
| 1245 if_valueisnotpromise(this); |
| 1246 |
| 1247 // 3.If IsPromise(x) is true, then |
| 1248 GotoIf(TaggedIsSmi(value), &if_valueisnotpromise); |
| 1249 |
| 1250 // This shortcircuits the constructor lookups. |
| 1251 GotoUnless(HasInstanceType(value, JS_PROMISE_TYPE), &if_valueisnotpromise); |
| 1252 |
| 1253 // This adds a fast path as non-subclassed native promises don't have |
| 1254 // an observable constructor lookup. |
| 1255 Node* const native_context = LoadNativeContext(context); |
| 1256 Node* const promise_fun = |
| 1257 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); |
| 1258 BranchIfFastPath(native_context, promise_fun, value, &if_valueisnativepromise, |
| 1259 &if_valueisnotnativepromise); |
| 1260 |
| 1261 Bind(&if_valueisnativepromise); |
| 1262 { |
| 1263 GotoUnless(WordEqual(promise_fun, receiver), &if_valueisnotnativepromise); |
| 1264 Return(value); |
| 1265 } |
| 1266 |
| 1267 // At this point, value or/and receiver are not native promises, but |
| 1268 // they could be of the same subclass. |
| 1269 Bind(&if_valueisnotnativepromise); |
| 1270 { |
| 1271 // 3.a Let xConstructor be ? Get(x, "constructor"). |
| 1272 // The constructor lookup is observable. |
| 1273 Node* const constructor_str = |
| 1274 HeapConstant(isolate->factory()->constructor_string()); |
| 1275 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
| 1276 Node* const constructor = |
| 1277 CallStub(getproperty_callable, context, value, constructor_str); |
| 1278 |
| 1279 // 3.b If SameValue(xConstructor, C) is true, return x. |
| 1280 GotoUnless(SameValue(constructor, receiver, context), |
| 1281 &if_valueisnotpromise); |
| 1282 |
| 1283 Return(value); |
| 1284 } |
| 1285 |
| 1286 Bind(&if_valueisnotpromise); |
| 1287 { |
| 1288 Label if_nativepromise(this), if_notnativepromise(this); |
| 1289 BranchIfFastPath(context, receiver, &if_nativepromise, |
| 1290 &if_notnativepromise); |
| 1291 |
| 1292 // This adds a fast path for native promises that don't need to |
| 1293 // create NewPromiseCapability. |
| 1294 Bind(&if_nativepromise); |
| 1295 { |
| 1296 Label do_resolve(this); |
| 1297 |
| 1298 Node* const result = AllocateAndInitJSPromise(context); |
| 1299 InternalResolvePromise(context, result, value); |
| 1300 Return(result); |
| 1301 } |
| 1302 |
| 1303 Bind(&if_notnativepromise); |
| 1304 { |
| 1305 // 4. Let promiseCapability be ? NewPromiseCapability(C). |
| 1306 Node* const capability = NewPromiseCapability(context, receiver); |
| 1307 |
| 1308 // 5. Perform ? Call(promiseCapability.[[Resolve]], undefined, « x »). |
| 1309 Callable call_callable = CodeFactory::Call(isolate); |
| 1310 Node* const resolve = |
| 1311 LoadObjectField(capability, JSPromiseCapability::kResolveOffset); |
| 1312 CallJS(call_callable, context, resolve, UndefinedConstant(), value); |
| 1313 |
| 1314 // 6. Return promiseCapability.[[Promise]]. |
| 1315 Node* const result = |
| 1316 LoadObjectField(capability, JSPromiseCapability::kPromiseOffset); |
| 1317 Return(result); |
| 1318 } |
| 1319 } |
| 1320 } |
| 1321 |
1210 TF_BUILTIN(PromiseGetCapabilitiesExecutor, PromiseBuiltinsAssembler) { | 1322 TF_BUILTIN(PromiseGetCapabilitiesExecutor, PromiseBuiltinsAssembler) { |
1211 Node* const resolve = Parameter(1); | 1323 Node* const resolve = Parameter(1); |
1212 Node* const reject = Parameter(2); | 1324 Node* const reject = Parameter(2); |
1213 Node* const context = Parameter(5); | 1325 Node* const context = Parameter(5); |
1214 | 1326 |
1215 Node* const capability = LoadContextElement( | 1327 Node* const capability = LoadContextElement( |
1216 context, GetPromiseCapabilityExecutor::kCapabilitySlot); | 1328 context, GetPromiseCapabilityExecutor::kCapabilitySlot); |
1217 | 1329 |
1218 Label if_alreadyinvoked(this, Label::kDeferred); | 1330 Label if_alreadyinvoked(this, Label::kDeferred); |
1219 GotoIf(WordNotEqual( | 1331 GotoIf(WordNotEqual( |
(...skipping 20 matching lines...) Expand all Loading... |
1240 Node* debug_event = Parameter(2); | 1352 Node* debug_event = Parameter(2); |
1241 Node* context = Parameter(5); | 1353 Node* context = Parameter(5); |
1242 | 1354 |
1243 CSA_ASSERT_JS_ARGC_EQ(this, 2); | 1355 CSA_ASSERT_JS_ARGC_EQ(this, 2); |
1244 | 1356 |
1245 Return(NewPromiseCapability(context, constructor, debug_event)); | 1357 Return(NewPromiseCapability(context, constructor, debug_event)); |
1246 } | 1358 } |
1247 | 1359 |
1248 } // namespace internal | 1360 } // namespace internal |
1249 } // namespace v8 | 1361 } // namespace v8 |
OLD | NEW |