| 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 |