Chromium Code Reviews| 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 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 183 Node* promise_capability, Node* native_context) { | 183 Node* promise_capability, Node* native_context) { |
| 184 int kContextLength = GetPromiseCapabilityExecutor::kContextLength; | 184 int kContextLength = GetPromiseCapabilityExecutor::kContextLength; |
| 185 Node* context = CreatePromiseContext(native_context, kContextLength); | 185 Node* context = CreatePromiseContext(native_context, kContextLength); |
| 186 StoreContextElementNoWriteBarrier( | 186 StoreContextElementNoWriteBarrier( |
| 187 context, GetPromiseCapabilityExecutor::kCapabilitySlot, | 187 context, GetPromiseCapabilityExecutor::kCapabilitySlot, |
| 188 promise_capability); | 188 promise_capability); |
| 189 return context; | 189 return context; |
| 190 } | 190 } |
| 191 | 191 |
| 192 Node* PromiseBuiltinsAssembler::ThrowIfNotJSReceiver( | 192 Node* PromiseBuiltinsAssembler::ThrowIfNotJSReceiver( |
| 193 Node* context, Node* value, MessageTemplate::Template msg_template) { | 193 Node* context, Node* value, MessageTemplate::Template msg_template, |
| 194 const char* method_name) { | |
| 194 Label out(this), throw_exception(this, Label::kDeferred); | 195 Label out(this), throw_exception(this, Label::kDeferred); |
| 195 Variable var_value_map(this, MachineRepresentation::kTagged); | 196 Variable var_value_map(this, MachineRepresentation::kTagged); |
| 196 | 197 |
| 197 GotoIf(TaggedIsSmi(value), &throw_exception); | 198 GotoIf(TaggedIsSmi(value), &throw_exception); |
| 198 | 199 |
| 199 // Load the instance type of the {value}. | 200 // Load the instance type of the {value}. |
| 200 var_value_map.Bind(LoadMap(value)); | 201 var_value_map.Bind(LoadMap(value)); |
| 201 Node* const value_instance_type = LoadMapInstanceType(var_value_map.value()); | 202 Node* const value_instance_type = LoadMapInstanceType(var_value_map.value()); |
| 202 | 203 |
| 203 Branch(IsJSReceiverInstanceType(value_instance_type), &out, &throw_exception); | 204 Branch(IsJSReceiverInstanceType(value_instance_type), &out, &throw_exception); |
| 204 | 205 |
| 205 // The {value} is not a compatible receiver for this method. | 206 // The {value} is not a compatible receiver for this method. |
| 206 Bind(&throw_exception); | 207 Bind(&throw_exception); |
| 207 { | 208 { |
| 209 Isolate* isolate = this->isolate(); | |
|
adamk
2017/01/04 19:19:21
I don't think you need this, can just use "isolate
gsathya
2017/01/04 19:37:17
Done.
| |
| 210 Node* method; | |
|
adamk
2017/01/04 19:19:21
Node* const for consistency.
gsathya
2017/01/04 19:37:17
Done.
| |
| 211 method = method_name == NULL | |
|
adamk
2017/01/04 19:19:21
s/NULL/nullptr/
And you can merge this with the d
gsathya
2017/01/04 19:37:17
Done.
| |
| 212 ? UndefinedConstant() | |
| 213 : HeapConstant(isolate->factory()->NewStringFromAsciiChecked( | |
| 214 method_name)); | |
| 215 | |
| 208 Node* const message_id = SmiConstant(msg_template); | 216 Node* const message_id = SmiConstant(msg_template); |
| 209 CallRuntime(Runtime::kThrowTypeError, context, message_id); | 217 CallRuntime(Runtime::kThrowTypeError, context, message_id, method); |
| 210 var_value_map.Bind(UndefinedConstant()); | 218 var_value_map.Bind(UndefinedConstant()); |
| 211 Goto(&out); // Never reached. | 219 Goto(&out); // Never reached. |
| 212 } | 220 } |
| 213 | 221 |
| 214 Bind(&out); | 222 Bind(&out); |
| 215 return var_value_map.value(); | 223 return var_value_map.value(); |
| 216 } | 224 } |
| 217 | 225 |
| 218 Node* PromiseBuiltinsAssembler::PromiseHasHandler(Node* promise) { | 226 Node* PromiseBuiltinsAssembler::PromiseHasHandler(Node* promise) { |
| 219 Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset); | 227 Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset); |
| (...skipping 338 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 558 } | 566 } |
| 559 | 567 |
| 560 // Promise fast path implementations rely on unmodified JSPromise instances. | 568 // Promise fast path implementations rely on unmodified JSPromise instances. |
| 561 // We use a fairly coarse granularity for this and simply check whether both | 569 // We use a fairly coarse granularity for this and simply check whether both |
| 562 // the promise itself is unmodified (i.e. its map has not changed) and its | 570 // the promise itself is unmodified (i.e. its map has not changed) and its |
| 563 // prototype is unmodified. | 571 // prototype is unmodified. |
| 564 // TODO(gsathya): Refactor this out to prevent code dupe with builtins-regexp | 572 // TODO(gsathya): Refactor this out to prevent code dupe with builtins-regexp |
| 565 void PromiseBuiltinsAssembler::BranchIfFastPath(Node* context, Node* promise, | 573 void PromiseBuiltinsAssembler::BranchIfFastPath(Node* context, Node* promise, |
| 566 Label* if_isunmodified, | 574 Label* if_isunmodified, |
| 567 Label* if_ismodified) { | 575 Label* if_ismodified) { |
| 568 // TODO(gsathya): Assert if promise is receiver | |
| 569 Node* const map = LoadMap(promise); | |
| 570 Node* const native_context = LoadNativeContext(context); | 576 Node* const native_context = LoadNativeContext(context); |
| 571 Node* const promise_fun = | 577 Node* const promise_fun = |
| 572 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | 578 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); |
| 579 BranchIfFastPath(native_context, promise_fun, promise, if_isunmodified, | |
| 580 if_ismodified); | |
| 581 } | |
| 582 | |
| 583 void PromiseBuiltinsAssembler::BranchIfFastPath(Node* native_context, | |
| 584 Node* promise_fun, | |
| 585 Node* promise, | |
| 586 Label* if_isunmodified, | |
| 587 Label* if_ismodified) { | |
| 588 CSA_ASSERT(this, IsNativeContext(native_context)); | |
| 589 CSA_ASSERT(this, | |
| 590 WordEqual(promise_fun, | |
| 591 LoadContextElement(native_context, | |
| 592 Context::PROMISE_FUNCTION_INDEX))); | |
| 593 | |
| 594 Node* const map = LoadMap(promise); | |
| 573 Node* const initial_map = | 595 Node* const initial_map = |
| 574 LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset); | 596 LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset); |
| 575 Node* const has_initialmap = WordEqual(map, initial_map); | 597 Node* const has_initialmap = WordEqual(map, initial_map); |
| 576 | 598 |
| 577 GotoUnless(has_initialmap, if_ismodified); | 599 GotoUnless(has_initialmap, if_ismodified); |
| 578 | 600 |
| 579 Node* const initial_proto_initial_map = | 601 Node* const initial_proto_initial_map = |
| 580 LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_MAP_INDEX); | 602 LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_MAP_INDEX); |
| 581 Node* const proto_map = LoadMap(LoadMapPrototype(map)); | 603 Node* const proto_map = LoadMap(LoadMapPrototype(map)); |
| 582 Node* const proto_has_initialmap = | 604 Node* const proto_has_initialmap = |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 603 | 625 |
| 604 Bind(&cycle_check); | 626 Bind(&cycle_check); |
| 605 // 6. If SameValue(resolution, promise) is true, then | 627 // 6. If SameValue(resolution, promise) is true, then |
| 606 GotoIf(SameValue(promise, result, context), &if_cycle); | 628 GotoIf(SameValue(promise, result, context), &if_cycle); |
| 607 | 629 |
| 608 // 7. If Type(resolution) is not Object, then | 630 // 7. If Type(resolution) is not Object, then |
| 609 GotoIf(TaggedIsSmi(result), &fulfill); | 631 GotoIf(TaggedIsSmi(result), &fulfill); |
| 610 GotoUnless(IsJSReceiver(result), &fulfill); | 632 GotoUnless(IsJSReceiver(result), &fulfill); |
| 611 | 633 |
| 612 Label if_nativepromise(this), if_notnativepromise(this, Label::kDeferred); | 634 Label if_nativepromise(this), if_notnativepromise(this, Label::kDeferred); |
| 613 BranchIfFastPath(context, result, &if_nativepromise, &if_notnativepromise); | 635 Node* const native_context = LoadNativeContext(context); |
| 636 Node* const promise_fun = | |
| 637 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | |
| 638 BranchIfFastPath(native_context, promise_fun, result, &if_nativepromise, | |
| 639 &if_notnativepromise); | |
| 614 | 640 |
| 615 // Resolution is a native promise and if it's already resolved or | 641 // Resolution is a native promise and if it's already resolved or |
| 616 // rejected, shortcircuit the resolution procedure by directly | 642 // rejected, shortcircuit the resolution procedure by directly |
| 617 // reusing the value from the promise. | 643 // reusing the value from the promise. |
| 618 Bind(&if_nativepromise); | 644 Bind(&if_nativepromise); |
| 619 { | 645 { |
| 620 Node* const thenable_status = | 646 Node* const thenable_status = |
| 621 LoadObjectField(result, JSPromise::kStatusOffset); | 647 LoadObjectField(result, JSPromise::kStatusOffset); |
| 622 Node* const thenable_value = | 648 Node* const thenable_value = |
| 623 LoadObjectField(result, JSPromise::kResultOffset); | 649 LoadObjectField(result, JSPromise::kResultOffset); |
| 624 | 650 |
| 625 Label if_isnotpending(this); | 651 Label if_isnotpending(this); |
| 626 GotoUnless(SmiEqual(SmiConstant(v8::Promise::kPending), thenable_status), | 652 GotoUnless(SmiEqual(SmiConstant(v8::Promise::kPending), thenable_status), |
| 627 &if_isnotpending); | 653 &if_isnotpending); |
| 628 | 654 |
| 629 // TODO(gsathya): Use a marker here instead of the actual then | 655 // TODO(gsathya): Use a marker here instead of the actual then |
| 630 // callback, and check for the marker in PromiseResolveThenableJob | 656 // callback, and check for the marker in PromiseResolveThenableJob |
| 631 // and perform PromiseThen. | 657 // and perform PromiseThen. |
| 632 Node* const native_context = LoadNativeContext(context); | |
| 633 Node* const then = | 658 Node* const then = |
| 634 LoadContextElement(native_context, Context::PROMISE_THEN_INDEX); | 659 LoadContextElement(native_context, Context::PROMISE_THEN_INDEX); |
| 635 var_then.Bind(then); | 660 var_then.Bind(then); |
| 636 Goto(&do_enqueue); | 661 Goto(&do_enqueue); |
| 637 | 662 |
| 638 Bind(&if_isnotpending); | 663 Bind(&if_isnotpending); |
| 639 { | 664 { |
| 640 Label if_fulfilled(this), if_rejected(this); | 665 Label if_fulfilled(this), if_rejected(this); |
| 641 Branch(SmiEqual(SmiConstant(v8::Promise::kFulfilled), thenable_status), | 666 Branch(SmiEqual(SmiConstant(v8::Promise::kFulfilled), thenable_status), |
| 642 &if_fulfilled, &if_rejected); | 667 &if_fulfilled, &if_rejected); |
| (...skipping 485 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1128 Callable getproperty_callable = CodeFactory::GetProperty(isolate); | 1153 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
| 1129 Node* const then = | 1154 Node* const then = |
| 1130 CallStub(getproperty_callable, context, promise, then_str); | 1155 CallStub(getproperty_callable, context, promise, then_str); |
| 1131 Callable call_callable = CodeFactory::Call(isolate); | 1156 Callable call_callable = CodeFactory::Call(isolate); |
| 1132 Node* const result = | 1157 Node* const result = |
| 1133 CallJS(call_callable, context, then, promise, on_resolve, on_reject); | 1158 CallJS(call_callable, context, then, promise, on_resolve, on_reject); |
| 1134 Return(result); | 1159 Return(result); |
| 1135 } | 1160 } |
| 1136 } | 1161 } |
| 1137 | 1162 |
| 1163 TF_BUILTIN(PromiseResolve, PromiseBuiltinsAssembler) { | |
| 1164 // 1. Let C be the this value. | |
| 1165 Node* receiver = Parameter(0); | |
| 1166 Node* value = Parameter(1); | |
| 1167 Node* context = Parameter(4); | |
| 1168 Isolate* isolate = this->isolate(); | |
| 1169 | |
| 1170 // 2. If Type(C) is not Object, throw a TypeError exception. | |
| 1171 ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject, | |
| 1172 "PromiseResolve"); | |
| 1173 | |
| 1174 Label if_valueisnativepromise(this), if_valueisnotnativepromise(this), | |
| 1175 if_valueisnotpromise(this); | |
| 1176 | |
| 1177 // 3.If IsPromise(x) is true, then | |
| 1178 GotoIf(TaggedIsSmi(value), &if_valueisnotpromise); | |
| 1179 | |
| 1180 // This shortcircuits the constructor lookups. | |
| 1181 GotoUnless(HasInstanceType(value, JS_PROMISE_TYPE), &if_valueisnotpromise); | |
| 1182 | |
| 1183 // This adds a fast path as non-subclassed native promises don't have | |
| 1184 // an observable constructor lookup. | |
| 1185 Node* const native_context = LoadNativeContext(context); | |
| 1186 Node* const promise_fun = | |
| 1187 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | |
| 1188 BranchIfFastPath(native_context, promise_fun, value, &if_valueisnativepromise, | |
| 1189 &if_valueisnotnativepromise); | |
| 1190 | |
| 1191 Bind(&if_valueisnativepromise); | |
| 1192 { | |
| 1193 GotoUnless(WordEqual(promise_fun, receiver), &if_valueisnotnativepromise); | |
| 1194 Return(value); | |
| 1195 } | |
| 1196 | |
| 1197 // At this point, value or/and receiver are not native promises, but | |
| 1198 // they could be of the same subclass. | |
| 1199 Bind(&if_valueisnotnativepromise); | |
| 1200 { | |
| 1201 // 3.a Let xConstructor be ? Get(x, "constructor"). | |
| 1202 // The constructor lookup is observable. | |
| 1203 Node* const constructor_str = | |
| 1204 HeapConstant(isolate->factory()->constructor_string()); | |
| 1205 Callable getproperty_callable = CodeFactory::GetProperty(isolate); | |
| 1206 Node* const constructor = | |
| 1207 CallStub(getproperty_callable, context, value, constructor_str); | |
| 1208 | |
| 1209 // 3.b If SameValue(xConstructor, C) is true, return x. | |
| 1210 GotoUnless(SameValue(constructor, receiver, context), | |
| 1211 &if_valueisnotpromise); | |
| 1212 | |
| 1213 Return(value); | |
| 1214 } | |
| 1215 | |
| 1216 Bind(&if_valueisnotpromise); | |
| 1217 { | |
| 1218 Label if_nativepromise(this), if_notnativepromise(this); | |
| 1219 BranchIfFastPath(context, receiver, &if_nativepromise, | |
| 1220 &if_notnativepromise); | |
| 1221 | |
| 1222 // This adds a fast path for native promises that don't need to | |
| 1223 // create NewPromiseCapability. | |
| 1224 Bind(&if_nativepromise); | |
| 1225 { | |
| 1226 Label do_resolve(this); | |
| 1227 | |
| 1228 Node* const result = AllocateJSPromise(context); | |
| 1229 PromiseInit(result); | |
| 1230 | |
| 1231 GotoUnless(IsPromiseHookEnabled(), &do_resolve); | |
| 1232 CallRuntime(Runtime::kPromiseHookInit, context, result, | |
| 1233 UndefinedConstant()); | |
| 1234 Goto(&do_resolve); | |
| 1235 | |
| 1236 Bind(&do_resolve); | |
| 1237 InternalResolvePromise(context, result, value); | |
| 1238 Return(result); | |
| 1239 } | |
| 1240 | |
| 1241 Bind(&if_notnativepromise); | |
| 1242 { | |
| 1243 // 4. Let promiseCapability be ? NewPromiseCapability(C). | |
| 1244 Node* const capability = NewPromiseCapability(context, receiver); | |
| 1245 | |
| 1246 // 5. Perform ? Call(promiseCapability.[[Resolve]], undefined, « x »). | |
| 1247 Callable call_callable = CodeFactory::Call(isolate); | |
| 1248 Node* const resolve = | |
| 1249 LoadObjectField(capability, JSPromiseCapability::kResolveOffset); | |
| 1250 CallJS(call_callable, context, resolve, UndefinedConstant(), value); | |
| 1251 | |
| 1252 // 6. Return promiseCapability.[[Promise]]. | |
| 1253 Node* const result = | |
| 1254 LoadObjectField(capability, JSPromiseCapability::kPromiseOffset); | |
| 1255 Return(result); | |
| 1256 } | |
| 1257 } | |
| 1258 } | |
| 1259 | |
| 1138 TF_BUILTIN(PromiseGetCapabilitiesExecutor, PromiseBuiltinsAssembler) { | 1260 TF_BUILTIN(PromiseGetCapabilitiesExecutor, PromiseBuiltinsAssembler) { |
| 1139 Node* const resolve = Parameter(1); | 1261 Node* const resolve = Parameter(1); |
| 1140 Node* const reject = Parameter(2); | 1262 Node* const reject = Parameter(2); |
| 1141 Node* const context = Parameter(5); | 1263 Node* const context = Parameter(5); |
| 1142 | 1264 |
| 1143 Node* const capability = LoadContextElement( | 1265 Node* const capability = LoadContextElement( |
| 1144 context, GetPromiseCapabilityExecutor::kCapabilitySlot); | 1266 context, GetPromiseCapabilityExecutor::kCapabilitySlot); |
| 1145 | 1267 |
| 1146 Label if_alreadyinvoked(this, Label::kDeferred); | 1268 Label if_alreadyinvoked(this, Label::kDeferred); |
| 1147 GotoIf(WordNotEqual( | 1269 GotoIf(WordNotEqual( |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 1168 Node* debug_event = Parameter(2); | 1290 Node* debug_event = Parameter(2); |
| 1169 Node* context = Parameter(5); | 1291 Node* context = Parameter(5); |
| 1170 | 1292 |
| 1171 CSA_ASSERT_JS_ARGC_EQ(this, 2); | 1293 CSA_ASSERT_JS_ARGC_EQ(this, 2); |
| 1172 | 1294 |
| 1173 Return(NewPromiseCapability(context, constructor, debug_event)); | 1295 Return(NewPromiseCapability(context, constructor, debug_event)); |
| 1174 } | 1296 } |
| 1175 | 1297 |
| 1176 } // namespace internal | 1298 } // namespace internal |
| 1177 } // namespace v8 | 1299 } // namespace v8 |
| OLD | NEW |