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 |