| 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 #include "src/code-factory.h" | 7 #include "src/code-factory.h" |
| 8 #include "src/code-stub-assembler.h" | 8 #include "src/code-stub-assembler.h" |
| 9 #include "src/promise-utils.h" | 9 #include "src/promise-utils.h" |
| 10 | 10 |
| 11 namespace v8 { | 11 namespace v8 { |
| 12 namespace internal { | 12 namespace internal { |
| 13 | 13 |
| 14 // ES#sec-promise-reject-functions | 14 typedef compiler::Node Node; |
| 15 // Promise Reject Functions | 15 typedef CodeStubAssembler::ParameterMode ParameterMode; |
| 16 BUILTIN(PromiseRejectClosure) { | 16 typedef compiler::CodeAssemblerState CodeAssemblerState; |
| 17 HandleScope scope(isolate); | |
| 18 | 17 |
| 19 Handle<Context> context(isolate->context(), isolate); | 18 class PromiseBuiltinsAssembler : public CodeStubAssembler { |
| 19 public: |
| 20 explicit PromiseBuiltinsAssembler(CodeAssemblerState* state) |
| 21 : CodeStubAssembler(state) {} |
| 20 | 22 |
| 21 if (PromiseUtils::HasAlreadyVisited(context)) { | 23 protected: |
| 22 return isolate->heap()->undefined_value(); | 24 Node* ThrowIfNotJSReceiver(Node* context, Node* value, |
| 25 MessageTemplate::Template msg_template); |
| 26 |
| 27 Node* SpeciesConstructor(Node* context, Node* object, |
| 28 Node* default_constructor); |
| 29 |
| 30 Node* PromiseHasHandler(Node* promise); |
| 31 |
| 32 void PromiseSetHasHandler(Node* promise); |
| 33 |
| 34 void AppendPromiseCallback(int offset, compiler::Node* promise, |
| 35 compiler::Node* value); |
| 36 |
| 37 Node* InternalPerformPromiseThen(Node* context, Node* promise, |
| 38 Node* on_resolve, Node* on_reject, |
| 39 Node* deferred); |
| 40 |
| 41 void InternalResolvePromise(Node* context, Node* promise, Node* result, |
| 42 Label* out); |
| 43 |
| 44 void BranchIfFastPath(Node* context, Node* promise, Label* if_isunmodified, |
| 45 Label* if_ismodified); |
| 46 }; |
| 47 |
| 48 Node* PromiseBuiltinsAssembler::ThrowIfNotJSReceiver( |
| 49 Node* context, Node* value, MessageTemplate::Template msg_template) { |
| 50 Label out(this), throw_exception(this, Label::kDeferred); |
| 51 Variable var_value_map(this, MachineRepresentation::kTagged); |
| 52 |
| 53 GotoIf(TaggedIsSmi(value), &throw_exception); |
| 54 |
| 55 // Load the instance type of the {value}. |
| 56 var_value_map.Bind(LoadMap(value)); |
| 57 Node* const value_instance_type = LoadMapInstanceType(var_value_map.value()); |
| 58 |
| 59 Branch(IsJSReceiverInstanceType(value_instance_type), &out, &throw_exception); |
| 60 |
| 61 // The {value} is not a compatible receiver for this method. |
| 62 Bind(&throw_exception); |
| 63 { |
| 64 Node* const message_id = SmiConstant(msg_template); |
| 65 CallRuntime(Runtime::kThrowTypeError, context, message_id); |
| 66 var_value_map.Bind(UndefinedConstant()); |
| 67 Goto(&out); // Never reached. |
| 23 } | 68 } |
| 24 | 69 |
| 25 PromiseUtils::SetAlreadyVisited(context); | 70 Bind(&out); |
| 26 Handle<Object> value = args.atOrUndefined(isolate, 1); | |
| 27 Handle<JSObject> promise = handle(PromiseUtils::GetPromise(context), isolate); | |
| 28 Handle<Object> debug_event = | |
| 29 handle(PromiseUtils::GetDebugEvent(context), isolate); | |
| 30 MaybeHandle<Object> maybe_result; | |
| 31 Handle<Object> argv[] = {promise, value, debug_event}; | |
| 32 RETURN_FAILURE_ON_EXCEPTION( | |
| 33 isolate, Execution::Call(isolate, isolate->promise_internal_reject(), | |
| 34 isolate->factory()->undefined_value(), | |
| 35 arraysize(argv), argv)); | |
| 36 return isolate->heap()->undefined_value(); | |
| 37 } | |
| 38 | |
| 39 // ES#sec-createresolvingfunctions | |
| 40 // CreateResolvingFunctions ( promise ) | |
| 41 BUILTIN(CreateResolvingFunctions) { | |
| 42 HandleScope scope(isolate); | |
| 43 DCHECK_EQ(3, args.length()); | |
| 44 | |
| 45 Handle<JSObject> promise = args.at<JSObject>(1); | |
| 46 Handle<Object> debug_event = args.at(2); | |
| 47 Handle<JSFunction> resolve, reject; | |
| 48 | |
| 49 PromiseUtils::CreateResolvingFunctions(isolate, promise, debug_event, | |
| 50 &resolve, &reject); | |
| 51 | |
| 52 Handle<FixedArray> result = isolate->factory()->NewFixedArray(2); | |
| 53 result->set(0, *resolve); | |
| 54 result->set(1, *reject); | |
| 55 | |
| 56 return *isolate->factory()->NewJSArrayWithElements(result, FAST_ELEMENTS, 2, | |
| 57 NOT_TENURED); | |
| 58 } | |
| 59 | |
| 60 void Builtins::Generate_PromiseConstructor( | |
| 61 compiler::CodeAssemblerState* state) { | |
| 62 CodeStubAssembler a(state); | |
| 63 typedef CodeStubAssembler::Variable Variable; | |
| 64 typedef CodeStubAssembler::Label Label; | |
| 65 typedef compiler::Node Node; | |
| 66 | |
| 67 Node* const executor = a.Parameter(1); | |
| 68 Node* const new_target = a.Parameter(2); | |
| 69 Node* const context = a.Parameter(4); | |
| 70 Isolate* isolate = a.isolate(); | |
| 71 | |
| 72 Label if_targetisundefined(&a, Label::kDeferred); | |
| 73 | |
| 74 a.GotoIf(a.IsUndefined(new_target), &if_targetisundefined); | |
| 75 | |
| 76 Label if_notcallable(&a, Label::kDeferred); | |
| 77 | |
| 78 a.GotoIf(a.TaggedIsSmi(executor), &if_notcallable); | |
| 79 | |
| 80 Node* const executor_map = a.LoadMap(executor); | |
| 81 a.GotoUnless(a.IsCallableMap(executor_map), &if_notcallable); | |
| 82 | |
| 83 Node* const native_context = a.LoadNativeContext(context); | |
| 84 Node* const promise_fun = | |
| 85 a.LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | |
| 86 Node* const is_debug_active = a.IsDebugActive(); | |
| 87 Label if_targetisnotmodified(&a), if_targetismodified(&a, Label::kDeferred), | |
| 88 run_executor(&a), debug_push(&a, Label::kDeferred), init(&a); | |
| 89 | |
| 90 a.Branch(a.WordEqual(promise_fun, new_target), &if_targetisnotmodified, | |
| 91 &if_targetismodified); | |
| 92 | |
| 93 Variable var_result(&a, MachineRepresentation::kTagged), | |
| 94 var_reject_call(&a, MachineRepresentation::kTagged), | |
| 95 var_reason(&a, MachineRepresentation::kTagged); | |
| 96 | |
| 97 a.Bind(&if_targetisnotmodified); | |
| 98 { | |
| 99 Node* const instance = a.AllocateJSPromise(context); | |
| 100 var_result.Bind(instance); | |
| 101 a.Goto(&init); | |
| 102 } | |
| 103 | |
| 104 a.Bind(&if_targetismodified); | |
| 105 { | |
| 106 Callable fast_new_object_stub = CodeFactory::FastNewObject(isolate); | |
| 107 Node* const instance = | |
| 108 a.CallStub(fast_new_object_stub, context, promise_fun, new_target); | |
| 109 | |
| 110 var_result.Bind(instance); | |
| 111 a.Goto(&init); | |
| 112 } | |
| 113 | |
| 114 a.Bind(&init); | |
| 115 { | |
| 116 a.PromiseInit(var_result.value()); | |
| 117 a.Branch(is_debug_active, &debug_push, &run_executor); | |
| 118 } | |
| 119 | |
| 120 a.Bind(&debug_push); | |
| 121 { | |
| 122 a.CallRuntime(Runtime::kDebugPushPromise, context, var_result.value()); | |
| 123 a.Goto(&run_executor); | |
| 124 } | |
| 125 | |
| 126 a.Bind(&run_executor); | |
| 127 { | |
| 128 Label out(&a), if_rejectpromise(&a), debug_pop(&a, Label::kDeferred); | |
| 129 | |
| 130 // TODO(gsathya): Move this to TF. | |
| 131 Node* const resolving_functions = a.CallRuntime( | |
| 132 Runtime::kCreateResolvingFunctions, context, var_result.value()); | |
| 133 Node* const resolve = | |
| 134 a.LoadFixedArrayElement(resolving_functions, a.IntPtrConstant(0)); | |
| 135 Node* const reject = | |
| 136 a.LoadFixedArrayElement(resolving_functions, a.IntPtrConstant(1)); | |
| 137 Callable call_callable = CodeFactory::Call(isolate); | |
| 138 | |
| 139 Node* const maybe_exception = | |
| 140 a.CallJS(call_callable, context, executor, a.UndefinedConstant(), | |
| 141 resolve, reject); | |
| 142 | |
| 143 a.GotoIfException(maybe_exception, &if_rejectpromise, &var_reason); | |
| 144 a.Branch(is_debug_active, &debug_pop, &out); | |
| 145 | |
| 146 a.Bind(&if_rejectpromise); | |
| 147 { | |
| 148 Callable call_callable = CodeFactory::Call(isolate); | |
| 149 a.CallJS(call_callable, context, reject, a.UndefinedConstant(), | |
| 150 var_reason.value()); | |
| 151 a.Branch(is_debug_active, &debug_pop, &out); | |
| 152 } | |
| 153 | |
| 154 a.Bind(&debug_pop); | |
| 155 { | |
| 156 a.CallRuntime(Runtime::kDebugPopPromise, context); | |
| 157 a.Goto(&out); | |
| 158 } | |
| 159 a.Bind(&out); | |
| 160 a.Return(var_result.value()); | |
| 161 } | |
| 162 | |
| 163 // 1. If NewTarget is undefined, throw a TypeError exception. | |
| 164 a.Bind(&if_targetisundefined); | |
| 165 { | |
| 166 Node* const message_id = a.SmiConstant(MessageTemplate::kNotAPromise); | |
| 167 a.CallRuntime(Runtime::kThrowTypeError, context, message_id, new_target); | |
| 168 a.Return(a.UndefinedConstant()); // Never reached. | |
| 169 } | |
| 170 | |
| 171 // 2. If IsCallable(executor) is false, throw a TypeError exception. | |
| 172 a.Bind(&if_notcallable); | |
| 173 { | |
| 174 Node* const message_id = | |
| 175 a.SmiConstant(MessageTemplate::kResolverNotAFunction); | |
| 176 a.CallRuntime(Runtime::kThrowTypeError, context, message_id, executor); | |
| 177 a.Return(a.UndefinedConstant()); // Never reached. | |
| 178 } | |
| 179 } | |
| 180 | |
| 181 void Builtins::Generate_PromiseInternalConstructor( | |
| 182 compiler::CodeAssemblerState* state) { | |
| 183 typedef compiler::Node Node; | |
| 184 CodeStubAssembler a(state); | |
| 185 | |
| 186 Node* const context = a.Parameter(3); | |
| 187 Node* const instance = a.AllocateJSPromise(context); | |
| 188 a.PromiseInit(instance); | |
| 189 a.Return(instance); | |
| 190 } | |
| 191 | |
| 192 void Builtins::Generate_PromiseCreateAndSet( | |
| 193 compiler::CodeAssemblerState* state) { | |
| 194 typedef compiler::Node Node; | |
| 195 CodeStubAssembler a(state); | |
| 196 | |
| 197 Node* const status = a.Parameter(1); | |
| 198 Node* const result = a.Parameter(2); | |
| 199 Node* const context = a.Parameter(5); | |
| 200 | |
| 201 Node* const instance = a.AllocateJSPromise(context); | |
| 202 a.PromiseSet(instance, status, result); | |
| 203 a.Return(instance); | |
| 204 } | |
| 205 | |
| 206 namespace { | |
| 207 | |
| 208 compiler::Node* ThrowIfNotJSReceiver(CodeStubAssembler* a, Isolate* isolate, | |
| 209 compiler::Node* context, | |
| 210 compiler::Node* value, | |
| 211 MessageTemplate::Template msg_template) { | |
| 212 typedef compiler::Node Node; | |
| 213 typedef CodeStubAssembler::Label Label; | |
| 214 typedef CodeStubAssembler::Variable Variable; | |
| 215 | |
| 216 Label out(a), throw_exception(a, Label::kDeferred); | |
| 217 Variable var_value_map(a, MachineRepresentation::kTagged); | |
| 218 | |
| 219 a->GotoIf(a->TaggedIsSmi(value), &throw_exception); | |
| 220 | |
| 221 // Load the instance type of the {value}. | |
| 222 var_value_map.Bind(a->LoadMap(value)); | |
| 223 Node* const value_instance_type = | |
| 224 a->LoadMapInstanceType(var_value_map.value()); | |
| 225 | |
| 226 a->Branch(a->IsJSReceiverInstanceType(value_instance_type), &out, | |
| 227 &throw_exception); | |
| 228 | |
| 229 // The {value} is not a compatible receiver for this method. | |
| 230 a->Bind(&throw_exception); | |
| 231 { | |
| 232 Node* const message_id = a->SmiConstant(msg_template); | |
| 233 a->CallRuntime(Runtime::kThrowTypeError, context, message_id); | |
| 234 var_value_map.Bind(a->UndefinedConstant()); | |
| 235 a->Goto(&out); // Never reached. | |
| 236 } | |
| 237 | |
| 238 a->Bind(&out); | |
| 239 return var_value_map.value(); | 71 return var_value_map.value(); |
| 240 } | 72 } |
| 241 | 73 |
| 242 } // namespace | 74 Node* PromiseBuiltinsAssembler::PromiseHasHandler(Node* promise) { |
| 243 | 75 Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset); |
| 244 void Builtins::Generate_IsPromise(compiler::CodeAssemblerState* state) { | 76 return IsSetWord(SmiUntag(flags), 1 << JSPromise::kHasHandlerBit); |
| 245 CodeStubAssembler a(state); | |
| 246 typedef compiler::Node Node; | |
| 247 typedef CodeStubAssembler::Label Label; | |
| 248 | |
| 249 Node* const maybe_promise = a.Parameter(1); | |
| 250 Label if_notpromise(&a, Label::kDeferred); | |
| 251 | |
| 252 a.GotoIf(a.TaggedIsSmi(maybe_promise), &if_notpromise); | |
| 253 | |
| 254 Node* const result = a.SelectBooleanConstant( | |
| 255 a.HasInstanceType(maybe_promise, JS_PROMISE_TYPE)); | |
| 256 a.Return(result); | |
| 257 | |
| 258 a.Bind(&if_notpromise); | |
| 259 a.Return(a.FalseConstant()); | |
| 260 } | 77 } |
| 261 | 78 |
| 262 namespace { | 79 void PromiseBuiltinsAssembler::PromiseSetHasHandler(Node* promise) { |
| 263 | 80 Node* const flags = |
| 264 compiler::Node* PromiseHasHandler(CodeStubAssembler* a, | 81 SmiUntag(LoadObjectField(promise, JSPromise::kFlagsOffset)); |
| 265 compiler::Node* promise) { | 82 Node* const new_flags = |
| 266 typedef compiler::Node Node; | 83 WordOr(flags, IntPtrConstant(1 << JSPromise::kHasHandlerBit)); |
| 267 | 84 StoreObjectField(promise, JSPromise::kFlagsOffset, SmiTag(new_flags)); |
| 268 Node* const flags = a->LoadObjectField(promise, JSPromise::kFlagsOffset); | |
| 269 return a->IsSetWord(a->SmiUntag(flags), 1 << JSPromise::kHasHandlerBit); | |
| 270 } | 85 } |
| 271 | 86 |
| 272 void PromiseSetHasHandler(CodeStubAssembler* a, compiler::Node* promise) { | 87 Node* PromiseBuiltinsAssembler::SpeciesConstructor(Node* context, Node* object, |
| 273 typedef compiler::Node Node; | 88 Node* default_constructor) { |
| 274 | 89 Isolate* isolate = this->isolate(); |
| 275 Node* const flags = | 90 Variable var_result(this, MachineRepresentation::kTagged); |
| 276 a->SmiUntag(a->LoadObjectField(promise, JSPromise::kFlagsOffset)); | |
| 277 Node* const new_flags = | |
| 278 a->WordOr(flags, a->IntPtrConstant(1 << JSPromise::kHasHandlerBit)); | |
| 279 a->StoreObjectField(promise, JSPromise::kFlagsOffset, a->SmiTag(new_flags)); | |
| 280 } | |
| 281 | |
| 282 compiler::Node* SpeciesConstructor(CodeStubAssembler* a, Isolate* isolate, | |
| 283 compiler::Node* context, | |
| 284 compiler::Node* object, | |
| 285 compiler::Node* default_constructor) { | |
| 286 typedef compiler::Node Node; | |
| 287 typedef CodeStubAssembler::Label Label; | |
| 288 typedef CodeStubAssembler::Variable Variable; | |
| 289 | |
| 290 Variable var_result(a, MachineRepresentation::kTagged); | |
| 291 var_result.Bind(default_constructor); | 91 var_result.Bind(default_constructor); |
| 292 | 92 |
| 293 // 2. Let C be ? Get(O, "constructor"). | 93 // 2. Let C be ? Get(O, "constructor"). |
| 294 Node* const constructor_str = | 94 Node* const constructor_str = |
| 295 a->HeapConstant(isolate->factory()->constructor_string()); | 95 HeapConstant(isolate->factory()->constructor_string()); |
| 296 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); | 96 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
| 297 Node* const constructor = | 97 Node* const constructor = |
| 298 a->CallStub(getproperty_callable, context, object, constructor_str); | 98 CallStub(getproperty_callable, context, object, constructor_str); |
| 299 | 99 |
| 300 // 3. If C is undefined, return defaultConstructor. | 100 // 3. If C is undefined, return defaultConstructor. |
| 301 Label out(a); | 101 Label out(this); |
| 302 a->GotoIf(a->IsUndefined(constructor), &out); | 102 GotoIf(IsUndefined(constructor), &out); |
| 303 | 103 |
| 304 // 4. If Type(C) is not Object, throw a TypeError exception. | 104 // 4. If Type(C) is not Object, throw a TypeError exception. |
| 305 ThrowIfNotJSReceiver(a, a->isolate(), context, constructor, | 105 ThrowIfNotJSReceiver(context, constructor, |
| 306 MessageTemplate::kConstructorNotReceiver); | 106 MessageTemplate::kConstructorNotReceiver); |
| 307 | 107 |
| 308 // 5. Let S be ? Get(C, @@species). | 108 // 5. Let S be ? Get(C, @@species). |
| 309 Node* const species_symbol = | 109 Node* const species_symbol = |
| 310 a->HeapConstant(isolate->factory()->species_symbol()); | 110 HeapConstant(isolate->factory()->species_symbol()); |
| 311 Node* const species = | 111 Node* const species = |
| 312 a->CallStub(getproperty_callable, context, constructor, species_symbol); | 112 CallStub(getproperty_callable, context, constructor, species_symbol); |
| 313 | 113 |
| 314 // 6. If S is either undefined or null, return defaultConstructor. | 114 // 6. If S is either undefined or null, return defaultConstructor. |
| 315 a->GotoIf(a->IsUndefined(species), &out); | 115 GotoIf(IsUndefined(species), &out); |
| 316 a->GotoIf(a->WordEqual(species, a->NullConstant()), &out); | 116 GotoIf(WordEqual(species, NullConstant()), &out); |
| 317 | 117 |
| 318 // 7. If IsConstructor(S) is true, return S. | 118 // 7. If IsConstructor(S) is true, return S. |
| 319 Label throw_error(a); | 119 Label throw_error(this); |
| 320 Node* species_bitfield = a->LoadMapBitField(a->LoadMap(species)); | 120 Node* species_bitfield = LoadMapBitField(LoadMap(species)); |
| 321 a->GotoUnless( | 121 GotoUnless(Word32Equal(Word32And(species_bitfield, |
| 322 a->Word32Equal(a->Word32And(species_bitfield, | 122 Int32Constant((1 << Map::kIsConstructor))), |
| 323 a->Int32Constant((1 << Map::kIsConstructor))), | 123 Int32Constant(1 << Map::kIsConstructor)), |
| 324 a->Int32Constant(1 << Map::kIsConstructor)), | 124 &throw_error); |
| 325 &throw_error); | |
| 326 var_result.Bind(species); | 125 var_result.Bind(species); |
| 327 a->Goto(&out); | 126 Goto(&out); |
| 328 | 127 |
| 329 // 8. Throw a TypeError exception. | 128 // 8. Throw a TypeError exception. |
| 330 a->Bind(&throw_error); | 129 Bind(&throw_error); |
| 331 { | 130 { |
| 332 Node* const message_id = | 131 Node* const message_id = |
| 333 a->SmiConstant(MessageTemplate::kSpeciesNotConstructor); | 132 SmiConstant(MessageTemplate::kSpeciesNotConstructor); |
| 334 a->CallRuntime(Runtime::kThrowTypeError, context, message_id); | 133 CallRuntime(Runtime::kThrowTypeError, context, message_id); |
| 335 a->Goto(&out); | 134 Goto(&out); |
| 336 } | 135 } |
| 337 | 136 |
| 338 a->Bind(&out); | 137 Bind(&out); |
| 339 return var_result.value(); | 138 return var_result.value(); |
| 340 } | 139 } |
| 341 | 140 |
| 342 void AppendPromiseCallback(CodeStubAssembler* a, int offset, | 141 void PromiseBuiltinsAssembler::AppendPromiseCallback(int offset, Node* promise, |
| 343 compiler::Node* promise, compiler::Node* value) { | 142 Node* value) { |
| 344 typedef compiler::Node Node; | 143 Node* elements = LoadObjectField(promise, offset); |
| 144 Node* length = LoadFixedArrayBaseLength(elements); |
| 145 CodeStubAssembler::ParameterMode mode = OptimalParameterMode(); |
| 146 length = TaggedToParameter(length, mode); |
| 345 | 147 |
| 346 Node* elements = a->LoadObjectField(promise, offset); | 148 Node* delta = IntPtrOrSmiConstant(1, mode); |
| 347 Node* length = a->LoadFixedArrayBaseLength(elements); | 149 Node* new_capacity = IntPtrOrSmiAdd(length, delta, mode); |
| 348 CodeStubAssembler::ParameterMode mode = a->OptimalParameterMode(); | |
| 349 length = a->TaggedToParameter(length, mode); | |
| 350 | |
| 351 Node* delta = a->IntPtrOrSmiConstant(1, mode); | |
| 352 Node* new_capacity = a->IntPtrOrSmiAdd(length, delta, mode); | |
| 353 | 150 |
| 354 const ElementsKind kind = FAST_ELEMENTS; | 151 const ElementsKind kind = FAST_ELEMENTS; |
| 355 const WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER; | 152 const WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER; |
| 356 const CodeStubAssembler::AllocationFlags flags = | 153 const CodeStubAssembler::AllocationFlags flags = |
| 357 CodeStubAssembler::kAllowLargeObjectAllocation; | 154 CodeStubAssembler::kAllowLargeObjectAllocation; |
| 358 int additional_offset = 0; | 155 int additional_offset = 0; |
| 359 | 156 |
| 360 Node* new_elements = a->AllocateFixedArray(kind, new_capacity, mode, flags); | 157 Node* new_elements = AllocateFixedArray(kind, new_capacity, mode, flags); |
| 361 | 158 |
| 362 a->CopyFixedArrayElements(kind, elements, new_elements, length, barrier_mode, | 159 CopyFixedArrayElements(kind, elements, new_elements, length, barrier_mode, |
| 363 mode); | 160 mode); |
| 364 a->StoreFixedArrayElement(new_elements, length, value, barrier_mode, | 161 StoreFixedArrayElement(new_elements, length, value, barrier_mode, |
| 365 additional_offset, mode); | 162 additional_offset, mode); |
| 366 | 163 |
| 367 a->StoreObjectField(promise, offset, new_elements); | 164 StoreObjectField(promise, offset, new_elements); |
| 368 } | 165 } |
| 369 | 166 |
| 370 compiler::Node* InternalPerformPromiseThen(CodeStubAssembler* a, | 167 Node* PromiseBuiltinsAssembler::InternalPerformPromiseThen(Node* context, |
| 371 compiler::Node* context, | 168 Node* promise, |
| 372 compiler::Node* promise, | 169 Node* on_resolve, |
| 373 compiler::Node* on_resolve, | 170 Node* on_reject, |
| 374 compiler::Node* on_reject, | 171 Node* deferred) { |
| 375 compiler::Node* deferred) { | 172 Node* const native_context = LoadNativeContext(context); |
| 376 typedef CodeStubAssembler::Variable Variable; | 173 |
| 377 typedef CodeStubAssembler::Label Label; | 174 Variable var_on_resolve(this, MachineRepresentation::kTagged), |
| 378 typedef compiler::Node Node; | 175 var_on_reject(this, MachineRepresentation::kTagged); |
| 379 Isolate* isolate = a->isolate(); | |
| 380 Node* const native_context = a->LoadNativeContext(context); | |
| 381 | |
| 382 Variable var_on_resolve(a, MachineRepresentation::kTagged), | |
| 383 var_on_reject(a, MachineRepresentation::kTagged); | |
| 384 | 176 |
| 385 var_on_resolve.Bind(on_resolve); | 177 var_on_resolve.Bind(on_resolve); |
| 386 var_on_reject.Bind(on_reject); | 178 var_on_reject.Bind(on_reject); |
| 387 | 179 |
| 388 Label out(a), if_onresolvenotcallable(a), onrejectcheck(a), | 180 Label out(this), if_onresolvenotcallable(this), onrejectcheck(this), |
| 389 append_callbacks(a); | 181 append_callbacks(this); |
| 390 a->GotoIf(a->TaggedIsSmi(on_resolve), &if_onresolvenotcallable); | 182 GotoIf(TaggedIsSmi(on_resolve), &if_onresolvenotcallable); |
| 391 | 183 |
| 392 Node* const on_resolve_map = a->LoadMap(on_resolve); | 184 Node* const on_resolve_map = LoadMap(on_resolve); |
| 393 a->Branch(a->IsCallableMap(on_resolve_map), &onrejectcheck, | 185 Branch(IsCallableMap(on_resolve_map), &onrejectcheck, |
| 394 &if_onresolvenotcallable); | 186 &if_onresolvenotcallable); |
| 395 | 187 |
| 396 a->Bind(&if_onresolvenotcallable); | 188 Bind(&if_onresolvenotcallable); |
| 397 { | 189 { |
| 398 var_on_resolve.Bind(a->LoadContextElement( | 190 var_on_resolve.Bind(LoadContextElement( |
| 399 native_context, Context::PROMISE_ID_RESOLVE_HANDLER_INDEX)); | 191 native_context, Context::PROMISE_ID_RESOLVE_HANDLER_INDEX)); |
| 400 a->Goto(&onrejectcheck); | 192 Goto(&onrejectcheck); |
| 401 } | 193 } |
| 402 | 194 |
| 403 a->Bind(&onrejectcheck); | 195 Bind(&onrejectcheck); |
| 404 { | 196 { |
| 405 Label if_onrejectnotcallable(a); | 197 Label if_onrejectnotcallable(this); |
| 406 a->GotoIf(a->TaggedIsSmi(on_reject), &if_onrejectnotcallable); | 198 GotoIf(TaggedIsSmi(on_reject), &if_onrejectnotcallable); |
| 407 | 199 |
| 408 Node* const on_reject_map = a->LoadMap(on_reject); | 200 Node* const on_reject_map = LoadMap(on_reject); |
| 409 a->Branch(a->IsCallableMap(on_reject_map), &append_callbacks, | 201 Branch(IsCallableMap(on_reject_map), &append_callbacks, |
| 410 &if_onrejectnotcallable); | 202 &if_onrejectnotcallable); |
| 411 | 203 |
| 412 a->Bind(&if_onrejectnotcallable); | 204 Bind(&if_onrejectnotcallable); |
| 413 { | 205 { |
| 414 var_on_reject.Bind(a->LoadContextElement( | 206 var_on_reject.Bind(LoadContextElement( |
| 415 native_context, Context::PROMISE_ID_REJECT_HANDLER_INDEX)); | 207 native_context, Context::PROMISE_ID_REJECT_HANDLER_INDEX)); |
| 416 a->Goto(&append_callbacks); | 208 Goto(&append_callbacks); |
| 417 } | 209 } |
| 418 } | 210 } |
| 419 | 211 |
| 420 a->Bind(&append_callbacks); | 212 Bind(&append_callbacks); |
| 421 { | 213 { |
| 422 Label fulfilled_check(a); | 214 Label fulfilled_check(this); |
| 423 Node* const status = a->LoadObjectField(promise, JSPromise::kStatusOffset); | 215 Node* const status = LoadObjectField(promise, JSPromise::kStatusOffset); |
| 424 a->GotoUnless(a->SmiEqual(status, a->SmiConstant(kPromisePending)), | 216 GotoUnless(SmiEqual(status, SmiConstant(kPromisePending)), |
| 425 &fulfilled_check); | 217 &fulfilled_check); |
| 426 | 218 |
| 427 Node* const existing_deferred = | 219 Node* const existing_deferred = |
| 428 a->LoadObjectField(promise, JSPromise::kDeferredOffset); | 220 LoadObjectField(promise, JSPromise::kDeferredOffset); |
| 429 | 221 |
| 430 Label if_noexistingcallbacks(a), if_existingcallbacks(a); | 222 Label if_noexistingcallbacks(this), if_existingcallbacks(this); |
| 431 a->Branch(a->IsUndefined(existing_deferred), &if_noexistingcallbacks, | 223 Branch(IsUndefined(existing_deferred), &if_noexistingcallbacks, |
| 432 &if_existingcallbacks); | 224 &if_existingcallbacks); |
| 433 | 225 |
| 434 a->Bind(&if_noexistingcallbacks); | 226 Bind(&if_noexistingcallbacks); |
| 435 { | 227 { |
| 436 // Store callbacks directly in the slots. | 228 // Store callbacks directly in the slots. |
| 437 a->StoreObjectField(promise, JSPromise::kDeferredOffset, deferred); | 229 StoreObjectField(promise, JSPromise::kDeferredOffset, deferred); |
| 438 a->StoreObjectField(promise, JSPromise::kFulfillReactionsOffset, | 230 StoreObjectField(promise, JSPromise::kFulfillReactionsOffset, |
| 439 var_on_resolve.value()); | 231 var_on_resolve.value()); |
| 440 a->StoreObjectField(promise, JSPromise::kRejectReactionsOffset, | 232 StoreObjectField(promise, JSPromise::kRejectReactionsOffset, |
| 441 var_on_reject.value()); | 233 var_on_reject.value()); |
| 442 a->Goto(&out); | 234 Goto(&out); |
| 443 } | 235 } |
| 444 | 236 |
| 445 a->Bind(&if_existingcallbacks); | 237 Bind(&if_existingcallbacks); |
| 446 { | 238 { |
| 447 Label if_singlecallback(a), if_multiplecallbacks(a); | 239 Label if_singlecallback(this), if_multiplecallbacks(this); |
| 448 a->BranchIfJSObject(existing_deferred, &if_singlecallback, | 240 BranchIfJSObject(existing_deferred, &if_singlecallback, |
| 449 &if_multiplecallbacks); | 241 &if_multiplecallbacks); |
| 450 | 242 |
| 451 a->Bind(&if_singlecallback); | 243 Bind(&if_singlecallback); |
| 452 { | 244 { |
| 453 // Create new FixedArrays to store callbacks, and migrate | 245 // Create new FixedArrays to store callbacks, and migrate |
| 454 // existing callbacks. | 246 // existing callbacks. |
| 455 Node* const deferreds = | 247 Node* const deferreds = |
| 456 a->AllocateFixedArray(FAST_ELEMENTS, a->IntPtrConstant(2)); | 248 AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2)); |
| 457 a->StoreFixedArrayElement(deferreds, 0, existing_deferred); | 249 StoreFixedArrayElement(deferreds, 0, existing_deferred); |
| 458 a->StoreFixedArrayElement(deferreds, 1, deferred); | 250 StoreFixedArrayElement(deferreds, 1, deferred); |
| 459 | 251 |
| 460 Node* const fulfill_reactions = | 252 Node* const fulfill_reactions = |
| 461 a->AllocateFixedArray(FAST_ELEMENTS, a->IntPtrConstant(2)); | 253 AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2)); |
| 462 a->StoreFixedArrayElement( | 254 StoreFixedArrayElement( |
| 463 fulfill_reactions, 0, | 255 fulfill_reactions, 0, |
| 464 a->LoadObjectField(promise, JSPromise::kFulfillReactionsOffset)); | 256 LoadObjectField(promise, JSPromise::kFulfillReactionsOffset)); |
| 465 a->StoreFixedArrayElement(fulfill_reactions, 1, var_on_resolve.value()); | 257 StoreFixedArrayElement(fulfill_reactions, 1, var_on_resolve.value()); |
| 466 | 258 |
| 467 Node* const reject_reactions = | 259 Node* const reject_reactions = |
| 468 a->AllocateFixedArray(FAST_ELEMENTS, a->IntPtrConstant(2)); | 260 AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2)); |
| 469 a->StoreFixedArrayElement( | 261 StoreFixedArrayElement( |
| 470 reject_reactions, 0, | 262 reject_reactions, 0, |
| 471 a->LoadObjectField(promise, JSPromise::kRejectReactionsOffset)); | 263 LoadObjectField(promise, JSPromise::kRejectReactionsOffset)); |
| 472 a->StoreFixedArrayElement(reject_reactions, 1, var_on_reject.value()); | 264 StoreFixedArrayElement(reject_reactions, 1, var_on_reject.value()); |
| 473 | 265 |
| 474 // Store new FixedArrays in promise. | 266 // Store new FixedArrays in promise. |
| 475 a->StoreObjectField(promise, JSPromise::kDeferredOffset, deferreds); | 267 StoreObjectField(promise, JSPromise::kDeferredOffset, deferreds); |
| 476 a->StoreObjectField(promise, JSPromise::kFulfillReactionsOffset, | 268 StoreObjectField(promise, JSPromise::kFulfillReactionsOffset, |
| 477 fulfill_reactions); | 269 fulfill_reactions); |
| 478 a->StoreObjectField(promise, JSPromise::kRejectReactionsOffset, | 270 StoreObjectField(promise, JSPromise::kRejectReactionsOffset, |
| 479 reject_reactions); | 271 reject_reactions); |
| 480 a->Goto(&out); | 272 Goto(&out); |
| 481 } | 273 } |
| 482 | 274 |
| 483 a->Bind(&if_multiplecallbacks); | 275 Bind(&if_multiplecallbacks); |
| 484 { | 276 { |
| 485 AppendPromiseCallback(a, JSPromise::kDeferredOffset, promise, deferred); | 277 AppendPromiseCallback(JSPromise::kDeferredOffset, promise, deferred); |
| 486 AppendPromiseCallback(a, JSPromise::kFulfillReactionsOffset, promise, | 278 AppendPromiseCallback(JSPromise::kFulfillReactionsOffset, promise, |
| 487 var_on_resolve.value()); | 279 var_on_resolve.value()); |
| 488 AppendPromiseCallback(a, JSPromise::kRejectReactionsOffset, promise, | 280 AppendPromiseCallback(JSPromise::kRejectReactionsOffset, promise, |
| 489 var_on_reject.value()); | 281 var_on_reject.value()); |
| 490 a->Goto(&out); | 282 Goto(&out); |
| 491 } | 283 } |
| 492 } | 284 } |
| 493 | 285 |
| 494 a->Bind(&fulfilled_check); | 286 Bind(&fulfilled_check); |
| 495 { | 287 { |
| 496 Label reject(a); | 288 Label reject(this); |
| 497 Node* const result = | 289 Node* const result = LoadObjectField(promise, JSPromise::kResultOffset); |
| 498 a->LoadObjectField(promise, JSPromise::kResultOffset); | 290 GotoUnless(WordEqual(status, SmiConstant(kPromiseFulfilled)), &reject); |
| 499 a->GotoUnless(a->WordEqual(status, a->SmiConstant(kPromiseFulfilled)), | |
| 500 &reject); | |
| 501 | 291 |
| 502 // TODO(gsathya): Move this to TF. | 292 // TODO(gsathya): Move this to TF. |
| 503 a->CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, promise, | 293 CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, promise, result, |
| 504 result, var_on_resolve.value(), deferred, | 294 var_on_resolve.value(), deferred, |
| 505 a->SmiConstant(kPromiseFulfilled)); | 295 SmiConstant(kPromiseFulfilled)); |
| 506 a->Goto(&out); | 296 Goto(&out); |
| 507 | 297 |
| 508 a->Bind(&reject); | 298 Bind(&reject); |
| 509 { | 299 { |
| 510 Node* const has_handler = PromiseHasHandler(a, promise); | 300 Node* const has_handler = PromiseHasHandler(promise); |
| 511 Label enqueue(a); | 301 Label enqueue(this); |
| 512 | 302 |
| 513 // TODO(gsathya): Fold these runtime calls and move to TF. | 303 // TODO(gsathya): Fold these runtime calls and move to TF. |
| 514 a->GotoIf(has_handler, &enqueue); | 304 GotoIf(has_handler, &enqueue); |
| 515 a->CallRuntime(Runtime::kPromiseRevokeReject, context, promise); | 305 CallRuntime(Runtime::kPromiseRevokeReject, context, promise); |
| 516 a->Goto(&enqueue); | 306 Goto(&enqueue); |
| 517 | 307 |
| 518 a->Bind(&enqueue); | 308 Bind(&enqueue); |
| 519 { | 309 { |
| 520 a->CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, promise, | 310 CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, promise, |
| 521 result, var_on_reject.value(), deferred, | 311 result, var_on_reject.value(), deferred, |
| 522 a->SmiConstant(kPromiseRejected)); | 312 SmiConstant(kPromiseRejected)); |
| 523 | 313 |
| 524 a->Goto(&out); | 314 Goto(&out); |
| 525 } | 315 } |
| 526 } | 316 } |
| 527 } | 317 } |
| 528 } | 318 } |
| 529 | 319 |
| 530 a->Bind(&out); | 320 Bind(&out); |
| 531 PromiseSetHasHandler(a, promise); | 321 PromiseSetHasHandler(promise); |
| 532 | 322 |
| 533 // TODO(gsathya): This call will be removed once we don't have to | 323 // TODO(gsathya): This call will be removed once we don't have to |
| 534 // deal with deferred objects. | 324 // deal with deferred objects. |
| 325 Isolate* isolate = this->isolate(); |
| 535 Callable getproperty_callable = CodeFactory::GetProperty(isolate); | 326 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
| 536 Node* const key = | 327 Node* const key = |
| 537 a->HeapConstant(isolate->factory()->NewStringFromAsciiChecked("promise")); | 328 HeapConstant(isolate->factory()->NewStringFromAsciiChecked("promise")); |
| 538 Node* const result = | 329 Node* const result = CallStub(getproperty_callable, context, deferred, key); |
| 539 a->CallStub(getproperty_callable, context, deferred, key); | |
| 540 | 330 |
| 541 return result; | 331 return result; |
| 542 } | 332 } |
| 543 | 333 |
| 544 } // namespace | |
| 545 | |
| 546 void Builtins::Generate_PerformPromiseThen( | |
| 547 compiler::CodeAssemblerState* state) { | |
| 548 CodeStubAssembler a(state); | |
| 549 typedef compiler::Node Node; | |
| 550 | |
| 551 Node* const promise = a.Parameter(1); | |
| 552 Node* const on_resolve = a.Parameter(2); | |
| 553 Node* const on_reject = a.Parameter(3); | |
| 554 Node* const deferred = a.Parameter(4); | |
| 555 Node* const context = a.Parameter(7); | |
| 556 | |
| 557 Node* const result = InternalPerformPromiseThen( | |
| 558 &a, context, promise, on_resolve, on_reject, deferred); | |
| 559 | |
| 560 // TODO(gsathya): This is unused, but value is returned according to spec. | |
| 561 a.Return(result); | |
| 562 } | |
| 563 | |
| 564 void Builtins::Generate_PromiseThen(compiler::CodeAssemblerState* state) { | |
| 565 CodeStubAssembler a(state); | |
| 566 typedef compiler::Node Node; | |
| 567 typedef CodeStubAssembler::Label Label; | |
| 568 typedef CodeStubAssembler::Variable Variable; | |
| 569 | |
| 570 // 1. Let promise be the this value. | |
| 571 Node* const promise = a.Parameter(0); | |
| 572 Node* const on_resolve = a.Parameter(1); | |
| 573 Node* const on_reject = a.Parameter(2); | |
| 574 Node* const context = a.Parameter(5); | |
| 575 Isolate* isolate = a.isolate(); | |
| 576 | |
| 577 // 2. If IsPromise(promise) is false, throw a TypeError exception. | |
| 578 a.ThrowIfNotInstanceType(context, promise, JS_PROMISE_TYPE, | |
| 579 "Promise.prototype.then"); | |
| 580 | |
| 581 Node* const native_context = a.LoadNativeContext(context); | |
| 582 Node* const promise_fun = | |
| 583 a.LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | |
| 584 | |
| 585 // 3. Let C be ? SpeciesConstructor(promise, %Promise%). | |
| 586 Node* constructor = | |
| 587 SpeciesConstructor(&a, isolate, context, promise, promise_fun); | |
| 588 | |
| 589 // 4. Let resultCapability be ? NewPromiseCapability(C). | |
| 590 Callable call_callable = CodeFactory::Call(isolate); | |
| 591 Label fast_promise_capability(&a), promise_capability(&a), | |
| 592 perform_promise_then(&a); | |
| 593 Variable var_deferred(&a, MachineRepresentation::kTagged); | |
| 594 | |
| 595 a.Branch(a.WordEqual(promise_fun, constructor), &fast_promise_capability, | |
| 596 &promise_capability); | |
| 597 | |
| 598 // TODO(gsathya): Remove deferred object and move | |
| 599 // NewPromiseCapbability functions to TF. | |
| 600 a.Bind(&fast_promise_capability); | |
| 601 { | |
| 602 // TODO(gsathya): Move this to TF. | |
| 603 Node* const promise_internal_capability = a.LoadContextElement( | |
| 604 native_context, Context::INTERNAL_PROMISE_CAPABILITY_INDEX); | |
| 605 Node* const capability = | |
| 606 a.CallJS(call_callable, context, promise_internal_capability, | |
| 607 a.UndefinedConstant()); | |
| 608 var_deferred.Bind(capability); | |
| 609 a.Goto(&perform_promise_then); | |
| 610 } | |
| 611 | |
| 612 a.Bind(&promise_capability); | |
| 613 { | |
| 614 // TODO(gsathya): Move this to TF. | |
| 615 Node* const new_promise_capability = a.LoadContextElement( | |
| 616 native_context, Context::NEW_PROMISE_CAPABILITY_INDEX); | |
| 617 Node* const capability = | |
| 618 a.CallJS(call_callable, context, new_promise_capability, | |
| 619 a.UndefinedConstant(), constructor); | |
| 620 var_deferred.Bind(capability); | |
| 621 a.Goto(&perform_promise_then); | |
| 622 } | |
| 623 | |
| 624 // 5. Return PerformPromiseThen(promise, onFulfilled, onRejected, | |
| 625 // resultCapability). | |
| 626 a.Bind(&perform_promise_then); | |
| 627 Node* const result = InternalPerformPromiseThen( | |
| 628 &a, context, promise, on_resolve, on_reject, var_deferred.value()); | |
| 629 a.Return(result); | |
| 630 } | |
| 631 | |
| 632 namespace { | |
| 633 | |
| 634 // Promise fast path implementations rely on unmodified JSPromise instances. | 334 // Promise fast path implementations rely on unmodified JSPromise instances. |
| 635 // We use a fairly coarse granularity for this and simply check whether both | 335 // We use a fairly coarse granularity for this and simply check whether both |
| 636 // the promise itself is unmodified (i.e. its map has not changed) and its | 336 // the promise itself is unmodified (i.e. its map has not changed) and its |
| 637 // prototype is unmodified. | 337 // prototype is unmodified. |
| 638 // TODO(gsathya): Refactor this out to prevent code dupe with builtins-regexp | 338 // TODO(gsathya): Refactor this out to prevent code dupe with builtins-regexp |
| 639 void BranchIfFastPath(CodeStubAssembler* a, compiler::Node* context, | 339 void PromiseBuiltinsAssembler::BranchIfFastPath(Node* context, Node* promise, |
| 640 compiler::Node* promise, | 340 Label* if_isunmodified, |
| 641 CodeStubAssembler::Label* if_isunmodified, | 341 Label* if_ismodified) { |
| 642 CodeStubAssembler::Label* if_ismodified) { | |
| 643 typedef compiler::Node Node; | |
| 644 | |
| 645 // TODO(gsathya): Assert if promise is receiver | 342 // TODO(gsathya): Assert if promise is receiver |
| 646 Node* const map = a->LoadMap(promise); | 343 Node* const map = LoadMap(promise); |
| 647 Node* const native_context = a->LoadNativeContext(context); | 344 Node* const native_context = LoadNativeContext(context); |
| 648 Node* const promise_fun = | 345 Node* const promise_fun = |
| 649 a->LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); | 346 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); |
| 650 Node* const initial_map = | 347 Node* const initial_map = |
| 651 a->LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset); | 348 LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset); |
| 652 Node* const has_initialmap = a->WordEqual(map, initial_map); | 349 Node* const has_initialmap = WordEqual(map, initial_map); |
| 653 | 350 |
| 654 a->GotoUnless(has_initialmap, if_ismodified); | 351 GotoUnless(has_initialmap, if_ismodified); |
| 655 | 352 |
| 656 Node* const initial_proto_initial_map = a->LoadContextElement( | 353 Node* const initial_proto_initial_map = |
| 657 native_context, Context::PROMISE_PROTOTYPE_MAP_INDEX); | 354 LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_MAP_INDEX); |
| 658 Node* const proto_map = a->LoadMap(a->LoadMapPrototype(map)); | 355 Node* const proto_map = LoadMap(LoadMapPrototype(map)); |
| 659 Node* const proto_has_initialmap = | 356 Node* const proto_has_initialmap = |
| 660 a->WordEqual(proto_map, initial_proto_initial_map); | 357 WordEqual(proto_map, initial_proto_initial_map); |
| 661 | 358 |
| 662 a->Branch(proto_has_initialmap, if_isunmodified, if_ismodified); | 359 Branch(proto_has_initialmap, if_isunmodified, if_ismodified); |
| 663 } | 360 } |
| 664 | 361 |
| 665 void InternalResolvePromise(CodeStubAssembler* a, compiler::Node* context, | 362 void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context, |
| 666 compiler::Node* promise, compiler::Node* result, | 363 Node* promise, |
| 667 CodeStubAssembler::Label* out) { | 364 Node* result, |
| 668 typedef CodeStubAssembler::Variable Variable; | 365 Label* out) { |
| 669 typedef CodeStubAssembler::Label Label; | 366 Isolate* isolate = this->isolate(); |
| 670 typedef compiler::Node Node; | 367 |
| 671 | 368 Variable var_reason(this, MachineRepresentation::kTagged), |
| 672 Isolate* isolate = a->isolate(); | 369 var_then(this, MachineRepresentation::kTagged); |
| 673 | 370 |
| 674 Variable var_reason(a, MachineRepresentation::kTagged), | 371 Label do_enqueue(this), fulfill(this), if_cycle(this, Label::kDeferred), |
| 675 var_then(a, MachineRepresentation::kTagged); | 372 if_rejectpromise(this, Label::kDeferred); |
| 676 | |
| 677 Label do_enqueue(a), fulfill(a), if_cycle(a, Label::kDeferred), | |
| 678 if_rejectpromise(a, Label::kDeferred); | |
| 679 | 373 |
| 680 // 6. If SameValue(resolution, promise) is true, then | 374 // 6. If SameValue(resolution, promise) is true, then |
| 681 a->GotoIf(a->SameValue(promise, result, context), &if_cycle); | 375 GotoIf(SameValue(promise, result, context), &if_cycle); |
| 682 | 376 |
| 683 // 7. If Type(resolution) is not Object, then | 377 // 7. If Type(resolution) is not Object, then |
| 684 a->GotoIf(a->TaggedIsSmi(result), &fulfill); | 378 GotoIf(TaggedIsSmi(result), &fulfill); |
| 685 a->GotoUnless(a->IsJSReceiver(result), &fulfill); | 379 GotoUnless(IsJSReceiver(result), &fulfill); |
| 686 | 380 |
| 687 Label if_nativepromise(a), if_notnativepromise(a, Label::kDeferred); | 381 Label if_nativepromise(this), if_notnativepromise(this, Label::kDeferred); |
| 688 BranchIfFastPath(a, context, result, &if_nativepromise, &if_notnativepromise); | 382 BranchIfFastPath(context, result, &if_nativepromise, &if_notnativepromise); |
| 689 | 383 |
| 690 // Resolution is a native promise and if it's already resolved or | 384 // Resolution is a native promise and if it's already resolved or |
| 691 // rejected, shortcircuit the resolution procedure by directly | 385 // rejected, shortcircuit the resolution procedure by directly |
| 692 // reusing the value from the promise. | 386 // reusing the value from the promise. |
| 693 a->Bind(&if_nativepromise); | 387 Bind(&if_nativepromise); |
| 694 { | 388 { |
| 695 Node* const thenable_status = | 389 Node* const thenable_status = |
| 696 a->LoadObjectField(result, JSPromise::kStatusOffset); | 390 LoadObjectField(result, JSPromise::kStatusOffset); |
| 697 Node* const thenable_value = | 391 Node* const thenable_value = |
| 698 a->LoadObjectField(result, JSPromise::kResultOffset); | 392 LoadObjectField(result, JSPromise::kResultOffset); |
| 699 | 393 |
| 700 Label if_isnotpending(a); | 394 Label if_isnotpending(this); |
| 701 a->GotoUnless(a->SmiEqual(a->SmiConstant(kPromisePending), thenable_status), | 395 GotoUnless(SmiEqual(SmiConstant(kPromisePending), thenable_status), |
| 702 &if_isnotpending); | 396 &if_isnotpending); |
| 703 | 397 |
| 704 // TODO(gsathya): Use a marker here instead of the actual then | 398 // TODO(gsathya): Use a marker here instead of the actual then |
| 705 // callback, and check for the marker in PromiseResolveThenableJob | 399 // callback, and check for the marker in PromiseResolveThenableJob |
| 706 // and perform PromiseThen. | 400 // and perform PromiseThen. |
| 707 Node* const native_context = a->LoadNativeContext(context); | 401 Node* const native_context = LoadNativeContext(context); |
| 708 Node* const then = | 402 Node* const then = |
| 709 a->LoadContextElement(native_context, Context::PROMISE_THEN_INDEX); | 403 LoadContextElement(native_context, Context::PROMISE_THEN_INDEX); |
| 710 var_then.Bind(then); | 404 var_then.Bind(then); |
| 711 a->Goto(&do_enqueue); | 405 Goto(&do_enqueue); |
| 712 | 406 |
| 713 a->Bind(&if_isnotpending); | 407 Bind(&if_isnotpending); |
| 714 { | 408 { |
| 715 Label if_fulfilled(a), if_rejected(a); | 409 Label if_fulfilled(this), if_rejected(this); |
| 716 a->Branch(a->SmiEqual(a->SmiConstant(kPromiseFulfilled), thenable_status), | 410 Branch(SmiEqual(SmiConstant(kPromiseFulfilled), thenable_status), |
| 717 &if_fulfilled, &if_rejected); | 411 &if_fulfilled, &if_rejected); |
| 718 | 412 |
| 719 a->Bind(&if_fulfilled); | 413 Bind(&if_fulfilled); |
| 720 { | 414 { |
| 721 a->CallRuntime(Runtime::kPromiseFulfill, context, promise, | 415 CallRuntime(Runtime::kPromiseFulfill, context, promise, |
| 722 a->SmiConstant(kPromiseFulfilled), thenable_value); | 416 SmiConstant(kPromiseFulfilled), thenable_value); |
| 723 PromiseSetHasHandler(a, promise); | 417 PromiseSetHasHandler(promise); |
| 724 a->Goto(out); | 418 Goto(out); |
| 725 } | 419 } |
| 726 | 420 |
| 727 a->Bind(&if_rejected); | 421 Bind(&if_rejected); |
| 728 { | 422 { |
| 729 Label reject(a); | 423 Label reject(this); |
| 730 Node* const has_handler = PromiseHasHandler(a, result); | 424 Node* const has_handler = PromiseHasHandler(result); |
| 731 | 425 |
| 732 // Promise has already been rejected, but had no handler. | 426 // Promise has already been rejected, but had no handler. |
| 733 // Revoke previously triggered reject event. | 427 // Revoke previously triggered reject event. |
| 734 a->GotoIf(has_handler, &reject); | 428 GotoIf(has_handler, &reject); |
| 735 a->CallRuntime(Runtime::kPromiseRevokeReject, context, result); | 429 CallRuntime(Runtime::kPromiseRevokeReject, context, result); |
| 736 a->Goto(&reject); | 430 Goto(&reject); |
| 737 | 431 |
| 738 a->Bind(&reject); | 432 Bind(&reject); |
| 739 // Don't cause a debug event as this case is forwarding a rejection | 433 // Don't cause a debug event as this case is forwarding a rejection |
| 740 a->CallRuntime(Runtime::kPromiseReject, context, promise, | 434 CallRuntime(Runtime::kPromiseReject, context, promise, thenable_value, |
| 741 thenable_value, a->FalseConstant()); | 435 FalseConstant()); |
| 742 PromiseSetHasHandler(a, result); | 436 PromiseSetHasHandler(result); |
| 743 a->Goto(out); | 437 Goto(out); |
| 744 } | 438 } |
| 745 } | 439 } |
| 746 } | 440 } |
| 747 | 441 |
| 748 a->Bind(&if_notnativepromise); | 442 Bind(&if_notnativepromise); |
| 749 { | 443 { |
| 750 // 8. Let then be Get(resolution, "then"). | 444 // 8. Let then be Get(resolution, "then"). |
| 751 Node* const then_str = a->HeapConstant(isolate->factory()->then_string()); | 445 Node* const then_str = HeapConstant(isolate->factory()->then_string()); |
| 752 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); | 446 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
| 753 Node* const then = | 447 Node* const then = |
| 754 a->CallStub(getproperty_callable, context, result, then_str); | 448 CallStub(getproperty_callable, context, result, then_str); |
| 755 | 449 |
| 756 // 9. If then is an abrupt completion, then | 450 // 9. If then is an abrupt completion, then |
| 757 a->GotoIfException(then, &if_rejectpromise, &var_reason); | 451 GotoIfException(then, &if_rejectpromise, &var_reason); |
| 758 | 452 |
| 759 // 11. If IsCallable(thenAction) is false, then | 453 // 11. If IsCallable(thenAction) is false, then |
| 760 a->GotoIf(a->TaggedIsSmi(then), &fulfill); | 454 GotoIf(TaggedIsSmi(then), &fulfill); |
| 761 Node* const then_map = a->LoadMap(then); | 455 Node* const then_map = LoadMap(then); |
| 762 a->GotoUnless(a->IsCallableMap(then_map), &fulfill); | 456 GotoUnless(IsCallableMap(then_map), &fulfill); |
| 763 var_then.Bind(then); | 457 var_then.Bind(then); |
| 764 a->Goto(&do_enqueue); | 458 Goto(&do_enqueue); |
| 765 } | 459 } |
| 766 | 460 |
| 767 a->Bind(&do_enqueue); | 461 Bind(&do_enqueue); |
| 768 { | 462 { |
| 769 Label enqueue(a); | 463 Label enqueue(this); |
| 770 a->GotoUnless(a->IsDebugActive(), &enqueue); | 464 GotoUnless(IsDebugActive(), &enqueue); |
| 771 a->GotoIf(a->TaggedIsSmi(result), &enqueue); | 465 GotoIf(TaggedIsSmi(result), &enqueue); |
| 772 a->GotoUnless(a->HasInstanceType(result, JS_PROMISE_TYPE), &enqueue); | 466 GotoUnless(HasInstanceType(result, JS_PROMISE_TYPE), &enqueue); |
| 773 // Mark the dependency of the new promise on the resolution | 467 // Mark the dependency of the new promise on the resolution |
| 774 Node* const key = | 468 Node* const key = |
| 775 a->HeapConstant(isolate->factory()->promise_handled_by_symbol()); | 469 HeapConstant(isolate->factory()->promise_handled_by_symbol()); |
| 776 a->CallRuntime(Runtime::kSetProperty, context, result, key, promise, | 470 CallRuntime(Runtime::kSetProperty, context, result, key, promise, |
| 777 a->SmiConstant(STRICT)); | 471 SmiConstant(STRICT)); |
| 778 a->Goto(&enqueue); | 472 Goto(&enqueue); |
| 779 | 473 |
| 780 // 12. Perform EnqueueJob("PromiseJobs", | 474 // 12. Perform EnqueueJob("PromiseJobs", |
| 781 // PromiseResolveThenableJob, « promise, resolution, thenAction | 475 // PromiseResolveThenableJob, « promise, resolution, thenAction |
| 782 // »). | 476 // »). |
| 783 a->Bind(&enqueue); | 477 Bind(&enqueue); |
| 784 a->CallRuntime(Runtime::kEnqueuePromiseResolveThenableJob, context, promise, | 478 CallRuntime(Runtime::kEnqueuePromiseResolveThenableJob, context, promise, |
| 785 result, var_then.value()); | 479 result, var_then.value()); |
| 786 a->Goto(out); | 480 Goto(out); |
| 787 } | 481 } |
| 482 |
| 788 // 7.b Return FulfillPromise(promise, resolution). | 483 // 7.b Return FulfillPromise(promise, resolution). |
| 789 a->Bind(&fulfill); | 484 Bind(&fulfill); |
| 790 { | 485 { |
| 791 a->CallRuntime(Runtime::kPromiseFulfill, context, promise, | 486 CallRuntime(Runtime::kPromiseFulfill, context, promise, |
| 792 a->SmiConstant(kPromiseFulfilled), result); | 487 SmiConstant(kPromiseFulfilled), result); |
| 793 a->Goto(out); | 488 Goto(out); |
| 794 } | 489 } |
| 795 | 490 |
| 796 a->Bind(&if_cycle); | 491 Bind(&if_cycle); |
| 797 { | 492 { |
| 798 // 6.a Let selfResolutionError be a newly created TypeError object. | 493 // 6.a Let selfResolutionError be a newly created TypeError object. |
| 799 Node* const message_id = a->SmiConstant(MessageTemplate::kPromiseCyclic); | 494 Node* const message_id = SmiConstant(MessageTemplate::kPromiseCyclic); |
| 800 Node* const error = | 495 Node* const error = |
| 801 a->CallRuntime(Runtime::kNewTypeError, context, message_id, result); | 496 CallRuntime(Runtime::kNewTypeError, context, message_id, result); |
| 802 var_reason.Bind(error); | 497 var_reason.Bind(error); |
| 803 | 498 |
| 804 // 6.b Return RejectPromise(promise, selfResolutionError). | 499 // 6.b Return RejectPromise(promise, selfResolutionError). |
| 805 a->Goto(&if_rejectpromise); | 500 Goto(&if_rejectpromise); |
| 806 } | 501 } |
| 807 | 502 |
| 808 // 9.a Return RejectPromise(promise, then.[[Value]]). | 503 // 9.a Return RejectPromise(promise, then.[[Value]]). |
| 809 a->Bind(&if_rejectpromise); | 504 Bind(&if_rejectpromise); |
| 810 { | 505 { |
| 811 a->CallRuntime(Runtime::kPromiseReject, context, promise, | 506 CallRuntime(Runtime::kPromiseReject, context, promise, var_reason.value(), |
| 812 var_reason.value(), a->TrueConstant()); | 507 TrueConstant()); |
| 813 a->Goto(out); | 508 Goto(out); |
| 814 } | 509 } |
| 815 } | 510 } |
| 816 | 511 |
| 817 } // namespace | 512 // ES#sec-promise-reject-functions |
| 513 // Promise Reject Functions |
| 514 BUILTIN(PromiseRejectClosure) { |
| 515 HandleScope scope(isolate); |
| 516 |
| 517 Handle<Context> context(isolate->context(), isolate); |
| 518 |
| 519 if (PromiseUtils::HasAlreadyVisited(context)) { |
| 520 return isolate->heap()->undefined_value(); |
| 521 } |
| 522 |
| 523 PromiseUtils::SetAlreadyVisited(context); |
| 524 Handle<Object> value = args.atOrUndefined(isolate, 1); |
| 525 Handle<JSObject> promise = handle(PromiseUtils::GetPromise(context), isolate); |
| 526 Handle<Object> debug_event = |
| 527 handle(PromiseUtils::GetDebugEvent(context), isolate); |
| 528 MaybeHandle<Object> maybe_result; |
| 529 Handle<Object> argv[] = {promise, value, debug_event}; |
| 530 RETURN_FAILURE_ON_EXCEPTION( |
| 531 isolate, Execution::Call(isolate, isolate->promise_internal_reject(), |
| 532 isolate->factory()->undefined_value(), |
| 533 arraysize(argv), argv)); |
| 534 return isolate->heap()->undefined_value(); |
| 535 } |
| 536 |
| 537 // ES#sec-createresolvingfunctions |
| 538 // CreateResolvingFunctions ( promise ) |
| 539 BUILTIN(CreateResolvingFunctions) { |
| 540 HandleScope scope(isolate); |
| 541 DCHECK_EQ(3, args.length()); |
| 542 |
| 543 Handle<JSObject> promise = args.at<JSObject>(1); |
| 544 Handle<Object> debug_event = args.at<Object>(2); |
| 545 Handle<JSFunction> resolve, reject; |
| 546 |
| 547 PromiseUtils::CreateResolvingFunctions(isolate, promise, debug_event, |
| 548 &resolve, &reject); |
| 549 |
| 550 Handle<FixedArray> result = isolate->factory()->NewFixedArray(2); |
| 551 result->set(0, *resolve); |
| 552 result->set(1, *reject); |
| 553 |
| 554 return *isolate->factory()->NewJSArrayWithElements(result, FAST_ELEMENTS, 2, |
| 555 NOT_TENURED); |
| 556 } |
| 557 |
| 558 TF_BUILTIN(PromiseConstructor, PromiseBuiltinsAssembler) { |
| 559 Node* const executor = Parameter(1); |
| 560 Node* const new_target = Parameter(2); |
| 561 Node* const context = Parameter(4); |
| 562 Isolate* isolate = this->isolate(); |
| 563 |
| 564 Label if_targetisundefined(this, Label::kDeferred); |
| 565 |
| 566 GotoIf(IsUndefined(new_target), &if_targetisundefined); |
| 567 |
| 568 Label if_notcallable(this, Label::kDeferred); |
| 569 |
| 570 GotoIf(TaggedIsSmi(executor), &if_notcallable); |
| 571 |
| 572 Node* const executor_map = LoadMap(executor); |
| 573 GotoUnless(IsCallableMap(executor_map), &if_notcallable); |
| 574 |
| 575 Node* const native_context = LoadNativeContext(context); |
| 576 Node* const promise_fun = |
| 577 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); |
| 578 Node* const is_debug_active = IsDebugActive(); |
| 579 Label if_targetisnotmodified(this), |
| 580 if_targetismodified(this, Label::kDeferred), run_executor(this), |
| 581 debug_push(this, Label::kDeferred), init(this); |
| 582 |
| 583 Branch(WordEqual(promise_fun, new_target), &if_targetisnotmodified, |
| 584 &if_targetismodified); |
| 585 |
| 586 Variable var_result(this, MachineRepresentation::kTagged), |
| 587 var_reject_call(this, MachineRepresentation::kTagged), |
| 588 var_reason(this, MachineRepresentation::kTagged); |
| 589 |
| 590 Bind(&if_targetisnotmodified); |
| 591 { |
| 592 Node* const instance = AllocateJSPromise(context); |
| 593 var_result.Bind(instance); |
| 594 Goto(&init); |
| 595 } |
| 596 |
| 597 Bind(&if_targetismodified); |
| 598 { |
| 599 Callable fast_new_object_stub = CodeFactory::FastNewObject(isolate); |
| 600 Node* const instance = |
| 601 CallStub(fast_new_object_stub, context, promise_fun, new_target); |
| 602 |
| 603 var_result.Bind(instance); |
| 604 Goto(&init); |
| 605 } |
| 606 |
| 607 Bind(&init); |
| 608 { |
| 609 PromiseInit(var_result.value()); |
| 610 Branch(is_debug_active, &debug_push, &run_executor); |
| 611 } |
| 612 |
| 613 Bind(&debug_push); |
| 614 { |
| 615 CallRuntime(Runtime::kDebugPushPromise, context, var_result.value()); |
| 616 Goto(&run_executor); |
| 617 } |
| 618 |
| 619 Bind(&run_executor); |
| 620 { |
| 621 Label out(this), if_rejectpromise(this), debug_pop(this, Label::kDeferred); |
| 622 |
| 623 // TODO(gsathya): Move this to TF. |
| 624 Node* const resolving_functions = CallRuntime( |
| 625 Runtime::kCreateResolvingFunctions, context, var_result.value()); |
| 626 Node* const resolve = |
| 627 LoadFixedArrayElement(resolving_functions, IntPtrConstant(0)); |
| 628 Node* const reject = |
| 629 LoadFixedArrayElement(resolving_functions, IntPtrConstant(1)); |
| 630 Callable call_callable = CodeFactory::Call(isolate); |
| 631 |
| 632 Node* const maybe_exception = CallJS(call_callable, context, executor, |
| 633 UndefinedConstant(), resolve, reject); |
| 634 |
| 635 GotoIfException(maybe_exception, &if_rejectpromise, &var_reason); |
| 636 Branch(is_debug_active, &debug_pop, &out); |
| 637 |
| 638 Bind(&if_rejectpromise); |
| 639 { |
| 640 Callable call_callable = CodeFactory::Call(isolate); |
| 641 CallJS(call_callable, context, reject, UndefinedConstant(), |
| 642 var_reason.value()); |
| 643 Branch(is_debug_active, &debug_pop, &out); |
| 644 } |
| 645 |
| 646 Bind(&debug_pop); |
| 647 { |
| 648 CallRuntime(Runtime::kDebugPopPromise, context); |
| 649 Goto(&out); |
| 650 } |
| 651 Bind(&out); |
| 652 Return(var_result.value()); |
| 653 } |
| 654 |
| 655 // 1. If NewTarget is undefined, throw a TypeError exception. |
| 656 Bind(&if_targetisundefined); |
| 657 { |
| 658 Node* const message_id = SmiConstant(MessageTemplate::kNotAPromise); |
| 659 CallRuntime(Runtime::kThrowTypeError, context, message_id, new_target); |
| 660 Return(UndefinedConstant()); // Never reached. |
| 661 } |
| 662 |
| 663 // 2. If IsCallable(executor) is false, throw a TypeError exception. |
| 664 Bind(&if_notcallable); |
| 665 { |
| 666 Node* const message_id = |
| 667 SmiConstant(MessageTemplate::kResolverNotAFunction); |
| 668 CallRuntime(Runtime::kThrowTypeError, context, message_id, executor); |
| 669 Return(UndefinedConstant()); // Never reached. |
| 670 } |
| 671 } |
| 672 |
| 673 TF_BUILTIN(PromiseInternalConstructor, PromiseBuiltinsAssembler) { |
| 674 Node* const context = Parameter(3); |
| 675 Node* const instance = AllocateJSPromise(context); |
| 676 PromiseInit(instance); |
| 677 Return(instance); |
| 678 } |
| 679 |
| 680 TF_BUILTIN(PromiseCreateAndSet, PromiseBuiltinsAssembler) { |
| 681 Node* const status = Parameter(1); |
| 682 Node* const result = Parameter(2); |
| 683 Node* const context = Parameter(5); |
| 684 |
| 685 Node* const instance = AllocateJSPromise(context); |
| 686 PromiseSet(instance, status, result); |
| 687 Return(instance); |
| 688 } |
| 689 |
| 690 TF_BUILTIN(IsPromise, PromiseBuiltinsAssembler) { |
| 691 Node* const maybe_promise = Parameter(1); |
| 692 Label if_notpromise(this, Label::kDeferred); |
| 693 |
| 694 GotoIf(TaggedIsSmi(maybe_promise), &if_notpromise); |
| 695 |
| 696 Node* const result = |
| 697 SelectBooleanConstant(HasInstanceType(maybe_promise, JS_PROMISE_TYPE)); |
| 698 Return(result); |
| 699 |
| 700 Bind(&if_notpromise); |
| 701 Return(FalseConstant()); |
| 702 } |
| 703 |
| 704 TF_BUILTIN(PerformPromiseThen, PromiseBuiltinsAssembler) { |
| 705 Node* const promise = Parameter(1); |
| 706 Node* const on_resolve = Parameter(2); |
| 707 Node* const on_reject = Parameter(3); |
| 708 Node* const deferred = Parameter(4); |
| 709 Node* const context = Parameter(7); |
| 710 |
| 711 Node* const result = InternalPerformPromiseThen(context, promise, on_resolve, |
| 712 on_reject, deferred); |
| 713 |
| 714 // TODO(gsathya): This is unused, but value is returned according to spec. |
| 715 Return(result); |
| 716 } |
| 717 |
| 718 TF_BUILTIN(PromiseThen, PromiseBuiltinsAssembler) { |
| 719 // 1. Let promise be the this value. |
| 720 Node* const promise = Parameter(0); |
| 721 Node* const on_resolve = Parameter(1); |
| 722 Node* const on_reject = Parameter(2); |
| 723 Node* const context = Parameter(5); |
| 724 Isolate* isolate = this->isolate(); |
| 725 |
| 726 // 2. If IsPromise(promise) is false, throw a TypeError exception. |
| 727 ThrowIfNotInstanceType(context, promise, JS_PROMISE_TYPE, |
| 728 "Promise.prototype.then"); |
| 729 |
| 730 Node* const native_context = LoadNativeContext(context); |
| 731 Node* const promise_fun = |
| 732 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); |
| 733 |
| 734 // 3. Let C be ? SpeciesConstructor(promise, %Promise%). |
| 735 Node* constructor = SpeciesConstructor(context, promise, promise_fun); |
| 736 |
| 737 // 4. Let resultCapability be ? NewPromiseCapability(C). |
| 738 Callable call_callable = CodeFactory::Call(isolate); |
| 739 Label fast_promise_capability(this), promise_capability(this), |
| 740 perform_promise_then(this); |
| 741 Variable var_deferred(this, MachineRepresentation::kTagged); |
| 742 |
| 743 Branch(WordEqual(promise_fun, constructor), &fast_promise_capability, |
| 744 &promise_capability); |
| 745 |
| 746 // TODO(gsathya): Remove deferred object and move |
| 747 // NewPromiseCapabability functions to TF. |
| 748 Bind(&fast_promise_capability); |
| 749 { |
| 750 // TODO(gsathya): Move this to TF. |
| 751 Node* const promise_internal_capability = LoadContextElement( |
| 752 native_context, Context::INTERNAL_PROMISE_CAPABILITY_INDEX); |
| 753 Node* const capability = |
| 754 CallJS(call_callable, context, promise_internal_capability, |
| 755 UndefinedConstant()); |
| 756 var_deferred.Bind(capability); |
| 757 Goto(&perform_promise_then); |
| 758 } |
| 759 |
| 760 Bind(&promise_capability); |
| 761 { |
| 762 // TODO(gsathya): Move this to TF. |
| 763 Node* const new_promise_capability = LoadContextElement( |
| 764 native_context, Context::NEW_PROMISE_CAPABILITY_INDEX); |
| 765 Node* const capability = |
| 766 CallJS(call_callable, context, new_promise_capability, |
| 767 UndefinedConstant(), constructor); |
| 768 var_deferred.Bind(capability); |
| 769 Goto(&perform_promise_then); |
| 770 } |
| 771 |
| 772 // 5. Return PerformPromiseThen(promise, onFulfilled, onRejected, |
| 773 // resultCapability). |
| 774 Bind(&perform_promise_then); |
| 775 Node* const result = InternalPerformPromiseThen( |
| 776 context, promise, on_resolve, on_reject, var_deferred.value()); |
| 777 Return(result); |
| 778 } |
| 818 | 779 |
| 819 // ES#sec-promise-resolve-functions | 780 // ES#sec-promise-resolve-functions |
| 820 // Promise Resolve Functions | 781 // Promise Resolve Functions |
| 821 void Builtins::Generate_PromiseResolveClosure( | 782 TF_BUILTIN(PromiseResolveClosure, PromiseBuiltinsAssembler) { |
| 822 compiler::CodeAssemblerState* state) { | 783 Node* const value = Parameter(1); |
| 823 CodeStubAssembler a(state); | 784 Node* const context = Parameter(4); |
| 824 typedef compiler::Node Node; | 785 |
| 825 typedef CodeStubAssembler::Label Label; | 786 Label out(this); |
| 826 | |
| 827 Node* const value = a.Parameter(1); | |
| 828 Node* const context = a.Parameter(4); | |
| 829 | |
| 830 Label out(&a); | |
| 831 | 787 |
| 832 // 3. Let alreadyResolved be F.[[AlreadyResolved]]. | 788 // 3. Let alreadyResolved be F.[[AlreadyResolved]]. |
| 833 Node* const has_already_visited_slot = | 789 Node* const has_already_visited_slot = |
| 834 a.IntPtrConstant(PromiseUtils::kAlreadyVisitedSlot); | 790 IntPtrConstant(PromiseUtils::kAlreadyVisitedSlot); |
| 835 | 791 |
| 836 Node* const has_already_visited = | 792 Node* const has_already_visited = |
| 837 a.LoadFixedArrayElement(context, has_already_visited_slot); | 793 LoadFixedArrayElement(context, has_already_visited_slot); |
| 838 | 794 |
| 839 // 4. If alreadyResolved.[[Value]] is true, return undefined. | 795 // 4. If alreadyResolved.[[Value]] is true, return undefined. |
| 840 a.GotoIf(a.SmiEqual(has_already_visited, a.SmiConstant(1)), &out); | 796 GotoIf(SmiEqual(has_already_visited, SmiConstant(1)), &out); |
| 841 | 797 |
| 842 // 5.Set alreadyResolved.[[Value]] to true. | 798 // 5.Set alreadyResolved.[[Value]] to true. |
| 843 a.StoreFixedArrayElement(context, has_already_visited_slot, a.SmiConstant(1)); | 799 StoreFixedArrayElement(context, has_already_visited_slot, SmiConstant(1)); |
| 844 | 800 |
| 845 // 2. Let promise be F.[[Promise]]. | 801 // 2. Let promise be F.[[Promise]]. |
| 846 Node* const promise = a.LoadFixedArrayElement( | 802 Node* const promise = LoadFixedArrayElement( |
| 847 context, a.IntPtrConstant(PromiseUtils::kPromiseSlot)); | 803 context, IntPtrConstant(PromiseUtils::kPromiseSlot)); |
| 848 | 804 |
| 849 InternalResolvePromise(&a, context, promise, value, &out); | 805 InternalResolvePromise(context, promise, value, &out); |
| 850 | 806 |
| 851 a.Bind(&out); | 807 Bind(&out); |
| 852 a.Return(a.UndefinedConstant()); | 808 Return(UndefinedConstant()); |
| 853 } | 809 } |
| 854 | 810 |
| 855 void Builtins::Generate_ResolvePromise(compiler::CodeAssemblerState* state) { | 811 TF_BUILTIN(ResolvePromise, PromiseBuiltinsAssembler) { |
| 856 CodeStubAssembler a(state); | 812 Node* const promise = Parameter(1); |
| 857 typedef compiler::Node Node; | 813 Node* const result = Parameter(2); |
| 858 typedef CodeStubAssembler::Label Label; | 814 Node* const context = Parameter(5); |
| 859 | 815 |
| 860 Node* const promise = a.Parameter(1); | 816 Label out(this); |
| 861 Node* const result = a.Parameter(2); | 817 InternalResolvePromise(context, promise, result, &out); |
| 862 Node* const context = a.Parameter(5); | 818 |
| 863 | 819 Bind(&out); |
| 864 Label out(&a); | 820 Return(UndefinedConstant()); |
| 865 InternalResolvePromise(&a, context, promise, result, &out); | 821 } |
| 866 | 822 |
| 867 a.Bind(&out); | 823 TF_BUILTIN(PromiseHandleReject, PromiseBuiltinsAssembler) { |
| 868 a.Return(a.UndefinedConstant()); | |
| 869 } | |
| 870 | |
| 871 void Builtins::Generate_PromiseHandleReject( | |
| 872 compiler::CodeAssemblerState* state) { | |
| 873 CodeStubAssembler a(state); | |
| 874 typedef compiler::Node Node; | |
| 875 typedef CodeStubAssembler::Label Label; | |
| 876 typedef CodeStubAssembler::Variable Variable; | |
| 877 typedef PromiseHandleRejectDescriptor Descriptor; | 824 typedef PromiseHandleRejectDescriptor Descriptor; |
| 878 | 825 |
| 879 Node* const promise = a.Parameter(Descriptor::kPromise); | 826 Node* const promise = Parameter(Descriptor::kPromise); |
| 880 Node* const on_reject = a.Parameter(Descriptor::kOnReject); | 827 Node* const on_reject = Parameter(Descriptor::kOnReject); |
| 881 Node* const exception = a.Parameter(Descriptor::kException); | 828 Node* const exception = Parameter(Descriptor::kException); |
| 882 Node* const context = a.Parameter(Descriptor::kContext); | 829 Node* const context = Parameter(Descriptor::kContext); |
| 883 Isolate* isolate = a.isolate(); | 830 |
| 884 | 831 Callable call_callable = CodeFactory::Call(isolate()); |
| 885 Callable call_callable = CodeFactory::Call(isolate); | 832 Variable var_unused(this, MachineRepresentation::kTagged); |
| 886 Variable var_unused(&a, MachineRepresentation::kTagged); | 833 |
| 887 | 834 Label if_internalhandler(this), if_customhandler(this, Label::kDeferred); |
| 888 Label if_internalhandler(&a), if_customhandler(&a, Label::kDeferred); | 835 Branch(IsUndefined(on_reject), &if_internalhandler, &if_customhandler); |
| 889 a.Branch(a.IsUndefined(on_reject), &if_internalhandler, &if_customhandler); | 836 |
| 890 | 837 Bind(&if_internalhandler); |
| 891 a.Bind(&if_internalhandler); | 838 { |
| 892 { | 839 CallRuntime(Runtime::kPromiseReject, context, promise, exception, |
| 893 a.CallRuntime(Runtime::kPromiseReject, context, promise, exception, | 840 FalseConstant()); |
| 894 a.FalseConstant()); | 841 Return(UndefinedConstant()); |
| 895 a.Return(a.UndefinedConstant()); | 842 } |
| 896 } | 843 |
| 897 | 844 Bind(&if_customhandler); |
| 898 a.Bind(&if_customhandler); | 845 { |
| 899 { | 846 CallJS(call_callable, context, on_reject, UndefinedConstant(), exception); |
| 900 a.CallJS(call_callable, context, on_reject, a.UndefinedConstant(), | 847 Return(UndefinedConstant()); |
| 901 exception); | 848 } |
| 902 a.Return(a.UndefinedConstant()); | 849 } |
| 903 } | 850 |
| 904 } | 851 TF_BUILTIN(PromiseHandle, PromiseBuiltinsAssembler) { |
| 905 | 852 Node* const value = Parameter(2); |
| 906 void Builtins::Generate_PromiseHandle(compiler::CodeAssemblerState* state) { | 853 Node* const handler = Parameter(3); |
| 907 CodeStubAssembler a(state); | 854 Node* const deferred = Parameter(4); |
| 908 typedef compiler::Node Node; | 855 Node* const context = Parameter(7); |
| 909 typedef CodeStubAssembler::Label Label; | 856 Isolate* isolate = this->isolate(); |
| 910 typedef CodeStubAssembler::Variable Variable; | |
| 911 | |
| 912 Node* const value = a.Parameter(2); | |
| 913 Node* const handler = a.Parameter(3); | |
| 914 Node* const deferred = a.Parameter(4); | |
| 915 Node* const context = a.Parameter(7); | |
| 916 Isolate* isolate = a.isolate(); | |
| 917 | 857 |
| 918 // Get promise from deferred | 858 // Get promise from deferred |
| 919 // TODO(gsathya): Remove this lookup by getting rid of the deferred object. | 859 // TODO(gsathya): Remove this lookup by getting rid of the deferred object. |
| 920 Callable getproperty_callable = CodeFactory::GetProperty(isolate); | 860 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
| 921 Node* const key = a.HeapConstant(isolate->factory()->promise_string()); | 861 Node* const key = HeapConstant(isolate->factory()->promise_string()); |
| 922 Node* const deferred_promise = | 862 Node* const deferred_promise = |
| 923 a.CallStub(getproperty_callable, context, deferred, key); | 863 CallStub(getproperty_callable, context, deferred, key); |
| 924 | 864 |
| 925 Variable var_reason(&a, MachineRepresentation::kTagged); | 865 Variable var_reason(this, MachineRepresentation::kTagged); |
| 926 | 866 |
| 927 Node* const is_debug_active = a.IsDebugActive(); | 867 Node* const is_debug_active = IsDebugActive(); |
| 928 Label run_handler(&a), if_rejectpromise(&a), debug_push(&a, Label::kDeferred), | 868 Label run_handler(this), if_rejectpromise(this), |
| 929 debug_pop(&a, Label::kDeferred); | 869 debug_push(this, Label::kDeferred), debug_pop(this, Label::kDeferred); |
| 930 a.Branch(is_debug_active, &debug_push, &run_handler); | 870 Branch(is_debug_active, &debug_push, &run_handler); |
| 931 | 871 |
| 932 a.Bind(&debug_push); | 872 Bind(&debug_push); |
| 933 { | 873 { |
| 934 a.CallRuntime(Runtime::kDebugPushPromise, context, deferred_promise); | 874 CallRuntime(Runtime::kDebugPushPromise, context, deferred_promise); |
| 935 a.Goto(&run_handler); | 875 Goto(&run_handler); |
| 936 } | 876 } |
| 937 | 877 |
| 938 a.Bind(&run_handler); | 878 Bind(&run_handler); |
| 939 { | 879 { |
| 940 Callable call_callable = CodeFactory::Call(isolate); | 880 Callable call_callable = CodeFactory::Call(isolate); |
| 941 | 881 |
| 942 Node* const result = | 882 Node* const result = |
| 943 a.CallJS(call_callable, context, handler, a.UndefinedConstant(), value); | 883 CallJS(call_callable, context, handler, UndefinedConstant(), value); |
| 944 | 884 |
| 945 a.GotoIfException(result, &if_rejectpromise, &var_reason); | 885 GotoIfException(result, &if_rejectpromise, &var_reason); |
| 946 | 886 |
| 947 // TODO(gsathya): Remove this lookup by getting rid of the deferred object. | 887 // TODO(gsathya): Remove this lookup by getting rid of the deferred object. |
| 948 Node* const key = a.HeapConstant(isolate->factory()->resolve_string()); | 888 Node* const key = HeapConstant(isolate->factory()->resolve_string()); |
| 949 Node* const on_resolve = | 889 Node* const on_resolve = |
| 950 a.CallStub(getproperty_callable, context, deferred, key); | 890 CallStub(getproperty_callable, context, deferred, key); |
| 951 | 891 |
| 952 Label if_internalhandler(&a), if_customhandler(&a, Label::kDeferred); | 892 Label if_internalhandler(this), if_customhandler(this, Label::kDeferred); |
| 953 a.Branch(a.IsUndefined(on_resolve), &if_internalhandler, &if_customhandler); | 893 Branch(IsUndefined(on_resolve), &if_internalhandler, &if_customhandler); |
| 954 | 894 |
| 955 a.Bind(&if_internalhandler); | 895 Bind(&if_internalhandler); |
| 956 InternalResolvePromise(&a, context, deferred_promise, result, &debug_pop); | 896 InternalResolvePromise(context, deferred_promise, result, &debug_pop); |
| 957 | 897 |
| 958 a.Bind(&if_customhandler); | 898 Bind(&if_customhandler); |
| 959 { | 899 { |
| 960 Node* const maybe_exception = a.CallJS(call_callable, context, on_resolve, | 900 Node* const maybe_exception = CallJS(call_callable, context, on_resolve, |
| 961 a.UndefinedConstant(), result); | 901 UndefinedConstant(), result); |
| 962 a.GotoIfException(maybe_exception, &if_rejectpromise, &var_reason); | 902 GotoIfException(maybe_exception, &if_rejectpromise, &var_reason); |
| 963 a.Goto(&debug_pop); | 903 Goto(&debug_pop); |
| 964 } | 904 } |
| 965 } | 905 } |
| 966 | 906 |
| 967 a.Bind(&if_rejectpromise); | 907 Bind(&if_rejectpromise); |
| 968 { | 908 { |
| 969 // TODO(gsathya): Remove this lookup by getting rid of the deferred object. | 909 // TODO(gsathya): Remove this lookup by getting rid of the deferred object. |
| 970 Node* const key = a.HeapConstant(isolate->factory()->reject_string()); | 910 Node* const key = HeapConstant(isolate->factory()->reject_string()); |
| 971 Node* const on_reject = | 911 Node* const on_reject = |
| 972 a.CallStub(getproperty_callable, context, deferred, key); | 912 CallStub(getproperty_callable, context, deferred, key); |
| 973 | 913 |
| 974 Callable promise_handle_reject = CodeFactory::PromiseHandleReject(isolate); | 914 Callable promise_handle_reject = CodeFactory::PromiseHandleReject(isolate); |
| 975 a.CallStub(promise_handle_reject, context, deferred_promise, on_reject, | 915 CallStub(promise_handle_reject, context, deferred_promise, on_reject, |
| 976 var_reason.value()); | 916 var_reason.value()); |
| 977 a.Goto(&debug_pop); | 917 Goto(&debug_pop); |
| 978 } | 918 } |
| 979 | 919 |
| 980 a.Bind(&debug_pop); | 920 Bind(&debug_pop); |
| 981 { | 921 { |
| 982 Label out(&a); | 922 Label out(this); |
| 983 | 923 |
| 984 a.GotoUnless(is_debug_active, &out); | 924 GotoUnless(is_debug_active, &out); |
| 985 a.CallRuntime(Runtime::kDebugPopPromise, context); | 925 CallRuntime(Runtime::kDebugPopPromise, context); |
| 986 a.Goto(&out); | 926 Goto(&out); |
| 987 | 927 |
| 988 a.Bind(&out); | 928 Bind(&out); |
| 989 a.Return(a.UndefinedConstant()); | 929 Return(UndefinedConstant()); |
| 990 } | 930 } |
| 991 } | 931 } |
| 992 | 932 |
| 993 } // namespace internal | 933 } // namespace internal |
| 994 } // namespace v8 | 934 } // namespace v8 |
| OLD | NEW |