OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "src/builtins/builtins-async.h" |
| 6 #include "src/builtins/builtins-utils.h" |
| 7 #include "src/builtins/builtins.h" |
| 8 #include "src/code-factory.h" |
| 9 #include "src/code-stub-assembler.h" |
| 10 #include "src/frames-inl.h" |
| 11 |
| 12 namespace v8 { |
| 13 namespace internal { |
| 14 |
| 15 // Describe fields of Context associated with the AsyncIterator unwrap closure. |
| 16 class ValueUnwrapContext { |
| 17 public: |
| 18 enum Fields { kDoneSlot = Context::MIN_CONTEXT_SLOTS, kLength }; |
| 19 }; |
| 20 |
| 21 class AsyncFromSyncBuiltinsAssembler : public AsyncBuiltinsAssembler { |
| 22 public: |
| 23 explicit AsyncFromSyncBuiltinsAssembler(CodeAssemblerState* state) |
| 24 : AsyncBuiltinsAssembler(state) {} |
| 25 |
| 26 std::pair<Node*, Node*> LoadIteratorResult(Node* const context, |
| 27 Node* const native_context, |
| 28 Node* const iter_result, |
| 29 Label* if_exception, |
| 30 Variable* var_exception); |
| 31 }; |
| 32 |
| 33 std::pair<Node*, Node*> AsyncFromSyncBuiltinsAssembler::LoadIteratorResult( |
| 34 Node* const context, Node* const native_context, Node* const iter_result, |
| 35 Label* if_exception, Variable* var_exception) { |
| 36 Label if_fastpath(this), if_slowpath(this), merge(this), to_boolean(this), |
| 37 done(this); |
| 38 GotoIf(TaggedIsSmi(iter_result), &if_slowpath); |
| 39 |
| 40 Node* const fast_iter_result_map = |
| 41 LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX); |
| 42 Node* const iter_result_map = LoadMap(iter_result); |
| 43 |
| 44 Variable var_value(this, MachineRepresentation::kTagged); |
| 45 Variable var_done(this, MachineRepresentation::kTagged); |
| 46 Branch(WordEqual(iter_result_map, fast_iter_result_map), &if_fastpath, |
| 47 &if_slowpath); |
| 48 |
| 49 Bind(&if_fastpath); |
| 50 { |
| 51 var_value.Bind( |
| 52 LoadObjectField(iter_result, JSIteratorResult::kValueOffset)); |
| 53 var_done.Bind(LoadObjectField(iter_result, JSIteratorResult::kDoneOffset)); |
| 54 Goto(&merge); |
| 55 } |
| 56 |
| 57 Bind(&if_slowpath); |
| 58 { |
| 59 // Let nextValue be IteratorValue(nextResult). |
| 60 // IfAbruptRejectPromise(nextValue, promiseCapability). |
| 61 Node* const value = |
| 62 CallStub(CodeFactory::GetProperty(isolate()), context, iter_result, |
| 63 HeapConstant(factory()->value_string())); |
| 64 GotoIfException(value, if_exception, var_exception); |
| 65 |
| 66 // Let nextDone be IteratorComplete(nextResult). |
| 67 // IfAbruptRejectPromise(nextDone, promiseCapability). |
| 68 Node* const done = |
| 69 CallStub(CodeFactory::GetProperty(isolate()), context, iter_result, |
| 70 HeapConstant(factory()->done_string())); |
| 71 GotoIfException(done, if_exception, var_exception); |
| 72 |
| 73 var_value.Bind(value); |
| 74 var_done.Bind(done); |
| 75 Goto(&merge); |
| 76 } |
| 77 |
| 78 Bind(&merge); |
| 79 // Ensure `iterResult.done` is a Boolean. |
| 80 GotoIf(TaggedIsSmi(var_done.value()), &to_boolean); |
| 81 Branch(IsBooleanMap(LoadMap(var_done.value())), &done, &to_boolean); |
| 82 |
| 83 Bind(&to_boolean); |
| 84 { |
| 85 Node* const result = |
| 86 CallStub(CodeFactory::ToBoolean(isolate()), context, var_done.value()); |
| 87 var_done.Bind(result); |
| 88 Goto(&done); |
| 89 } |
| 90 |
| 91 Bind(&done); |
| 92 return std::make_pair(var_value.value(), var_done.value()); |
| 93 } |
| 94 |
| 95 // https://tc39.github.io/proposal-async-iteration/ |
| 96 // Section #sec-%asyncfromsynciteratorprototype%.next |
| 97 TF_BUILTIN(AsyncFromSyncIteratorPrototypeNext, AsyncFromSyncBuiltinsAssembler) { |
| 98 const char* method_name = "[Async-from-Sync Iterator].prototype.next"; |
| 99 |
| 100 Node* const iterator = Parameter(0); |
| 101 Node* const parameter = Parameter(1); |
| 102 Node* const context = Parameter(4); |
| 103 |
| 104 Node* const native_context = LoadNativeContext(context); |
| 105 Node* const promise = AllocateAndInitJSPromise(context); |
| 106 |
| 107 Variable var_parent(this, MachineRepresentation::kTagged); |
| 108 Variable var_exception(this, MachineRepresentation::kTagged); |
| 109 var_exception.Bind(UndefinedConstant()); |
| 110 Label reject_promise(this, Label::kDeferred); |
| 111 Label if_receiverisincompatible(this, Label::kDeferred); |
| 112 |
| 113 GotoIf(TaggedIsSmi(iterator), &if_receiverisincompatible); |
| 114 GotoUnless(HasInstanceType(iterator, JS_ASYNC_FROM_SYNC_ITERATOR_TYPE), |
| 115 &if_receiverisincompatible); |
| 116 |
| 117 Node* const sync_iterator = |
| 118 LoadObjectField(iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset); |
| 119 |
| 120 // Let nextResult be IteratorNext(syncIterator, value). |
| 121 // IfAbruptRejectPromise(nextResult, promiseCapability). |
| 122 Node* next_method = |
| 123 CallStub(CodeFactory::GetProperty(isolate()), context, sync_iterator, |
| 124 HeapConstant(factory()->next_string())); |
| 125 Node* const iter_result = CallJS(CodeFactory::Call(isolate()), context, |
| 126 next_method, sync_iterator, parameter); |
| 127 GotoIfException(iter_result, &reject_promise, &var_exception); |
| 128 |
| 129 Node* value; |
| 130 Node* done; |
| 131 std::tie(value, done) = LoadIteratorResult( |
| 132 context, native_context, iter_result, &reject_promise, &var_exception); |
| 133 |
| 134 // Let valueWrapperCapability be ! NewPromiseCapability(%Promise%). |
| 135 Node* const wrapper = AllocateAndInitJSPromise(context); |
| 136 |
| 137 // Perform ! Call(valueWrapperCapability.[[Resolve]], undefined, |
| 138 // « nextValue »). |
| 139 InternalResolvePromise(context, wrapper, value); |
| 140 |
| 141 // Let onFulfilled be a new built-in function object as defined in |
| 142 // Async Iterator Value Unwrap Functions. |
| 143 // Set onFulfilled.[[Done]] to nextDone. |
| 144 Node* const map = LoadContextElement( |
| 145 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); |
| 146 Node* const on_resolve_shared = LoadContextElement( |
| 147 native_context, Context::ASYNC_ITERATOR_VALUE_UNWRAP_SHARED_FUN); |
| 148 CSA_ASSERT(this, |
| 149 HasInstanceType(on_resolve_shared, SHARED_FUNCTION_INFO_TYPE)); |
| 150 Node* const closure_context = |
| 151 AllocateAsyncIteratorValueUnwrapContext(native_context, done); |
| 152 Node* const on_resolve = AllocateFunctionWithMapAndContext( |
| 153 map, on_resolve_shared, closure_context); |
| 154 |
| 155 // Perform ! PerformPromiseThen(valueWrapperCapability.[[Promise]], |
| 156 // onFulfilled, undefined, promiseCapability). |
| 157 Node* const undefined = UndefinedConstant(); |
| 158 InternalPerformPromiseThen(context, wrapper, on_resolve, undefined, promise, |
| 159 undefined, undefined); |
| 160 |
| 161 // TODO(caitp): Use TailCallStub once it's possible for a JS-linkage builtin |
| 162 // to tailcall a CS-linkage builtin when an arguments adaptor frame is used. |
| 163 Return(promise); |
| 164 |
| 165 Bind(&if_receiverisincompatible); |
| 166 { |
| 167 // If Type(O) is not Object, or if O does not have a [[SyncIterator]] |
| 168 // internal slot, then |
| 169 |
| 170 // Let badIteratorError be a new TypeError exception. |
| 171 Node* const error = |
| 172 MakeTypeError(MessageTemplate::kIncompatibleMethodReceiver, context, |
| 173 CStringConstant(method_name), iterator); |
| 174 |
| 175 // Perform ! Call(promiseCapability.[[Reject]], undefined, |
| 176 // « badIteratorError »). |
| 177 var_exception.Bind(error); |
| 178 Goto(&reject_promise); |
| 179 } |
| 180 |
| 181 Bind(&reject_promise); |
| 182 { |
| 183 Node* const promise = AllocateAndInitJSPromise(context); |
| 184 Node* const exception = var_exception.value(); |
| 185 InternalPromiseReject(context, promise, exception, TrueConstant()); |
| 186 |
| 187 // Return promiseCapability.[[Promise]]. |
| 188 Return(promise); |
| 189 } |
| 190 } |
| 191 |
| 192 // https://tc39.github.io/proposal-async-iteration/ |
| 193 // Section #sec-%asyncfromsynciteratorprototype%.return |
| 194 TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturn, |
| 195 AsyncFromSyncBuiltinsAssembler) { |
| 196 const char* method_name = "[Async-from-Sync Iterator].prototype.return"; |
| 197 |
| 198 Node* const iterator = Parameter(0); |
| 199 Node* const parameter = Parameter(1); |
| 200 Node* const context = Parameter(4); |
| 201 |
| 202 Node* const native_context = LoadNativeContext(context); |
| 203 Node* const promise = AllocateAndInitJSPromise(context); |
| 204 |
| 205 Variable var_exception(this, MachineRepresentation::kTagged); |
| 206 var_exception.Bind(UndefinedConstant()); |
| 207 Label reject_promise(this, Label::kDeferred); |
| 208 Label if_receiverisincompatible(this, Label::kDeferred); |
| 209 |
| 210 GotoIf(TaggedIsSmi(iterator), &if_receiverisincompatible); |
| 211 GotoUnless(HasInstanceType(iterator, JS_ASYNC_FROM_SYNC_ITERATOR_TYPE), |
| 212 &if_receiverisincompatible); |
| 213 |
| 214 Node* const sync_iterator = |
| 215 LoadObjectField(iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset); |
| 216 |
| 217 // Let return be GetMethod(syncIterator, "return"). |
| 218 // IfAbruptRejectPromise(return, promiseCapability). |
| 219 Node* const return_method = |
| 220 CallStub(CodeFactory::GetProperty(isolate()), context, sync_iterator, |
| 221 HeapConstant(factory()->return_string())); |
| 222 GotoIfException(return_method, &reject_promise, &var_exception); |
| 223 |
| 224 Variable var_value(this, MachineRepresentation::kTagged); |
| 225 Variable var_done(this, MachineRepresentation::kTagged); |
| 226 |
| 227 Label if_isundefined(this), if_isnotundefined(this), resolve_promise(this); |
| 228 |
| 229 Branch(IsUndefined(return_method), &if_isundefined, &if_isnotundefined); |
| 230 Bind(&if_isundefined); |
| 231 { |
| 232 // If return is undefined, then |
| 233 // Let iterResult be ! CreateIterResultObject(value, true) |
| 234 Node* const iter_result = |
| 235 CallStub(CodeFactory::CreateIterResultObject(isolate()), context, |
| 236 parameter, TrueConstant()); |
| 237 |
| 238 // Perform ! Call(promiseCapability.[[Resolve]], undefined, « iterResult »). |
| 239 // IfAbruptRejectPromise(nextDone, promiseCapability). |
| 240 // Return promiseCapability.[[Promise]]. |
| 241 PromiseFulfill(context, promise, iter_result, v8::Promise::kFulfilled); |
| 242 Return(promise); |
| 243 } |
| 244 |
| 245 Bind(&if_isnotundefined); |
| 246 { |
| 247 // Let returnResult be Call(return, syncIterator, « value »). |
| 248 Node* const iter_result = CallJS(CodeFactory::Call(isolate()), context, |
| 249 return_method, sync_iterator, parameter); |
| 250 // IfAbruptRejectPromise(returnResult, promiseCapability). |
| 251 GotoIfException(iter_result, &reject_promise, &var_exception); |
| 252 |
| 253 Node* value; |
| 254 Node* done; |
| 255 std::tie(value, done) = LoadIteratorResult( |
| 256 context, native_context, iter_result, &reject_promise, &var_exception); |
| 257 |
| 258 var_value.Bind(value); |
| 259 var_done.Bind(done); |
| 260 Goto(&resolve_promise); |
| 261 } |
| 262 |
| 263 Bind(&resolve_promise); |
| 264 { |
| 265 // Let valueWrapperCapability be ! NewPromiseCapability(%Promise%). |
| 266 Node* const wrapper = AllocateAndInitJSPromise(context); |
| 267 Node* const value = var_value.value(); |
| 268 Node* const done = var_done.value(); |
| 269 |
| 270 // Perform ! Call(valueWrapperCapability.[[Resolve]], undefined, |
| 271 // « nextValue »). |
| 272 InternalResolvePromise(context, wrapper, value); |
| 273 |
| 274 // Let onFulfilled be a new built-in function object as defined in |
| 275 // Async Iterator Value Unwrap Functions. |
| 276 // Set onFulfilled.[[Done]] to nextDone. |
| 277 Node* const map = LoadContextElement( |
| 278 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); |
| 279 Node* const on_resolve_shared = LoadContextElement( |
| 280 native_context, Context::ASYNC_ITERATOR_VALUE_UNWRAP_SHARED_FUN); |
| 281 CSA_ASSERT(this, |
| 282 HasInstanceType(on_resolve_shared, SHARED_FUNCTION_INFO_TYPE)); |
| 283 Node* const closure_context = |
| 284 AllocateAsyncIteratorValueUnwrapContext(native_context, done); |
| 285 Node* const on_resolve = AllocateFunctionWithMapAndContext( |
| 286 map, on_resolve_shared, closure_context); |
| 287 |
| 288 // Perform ! PerformPromiseThen(valueWrapperCapability.[[Promise]], |
| 289 // onFulfilled, undefined, promiseCapability). |
| 290 Node* const undefined = UndefinedConstant(); |
| 291 InternalPerformPromiseThen(context, wrapper, on_resolve, undefined, promise, |
| 292 undefined, undefined); |
| 293 Return(promise); |
| 294 } |
| 295 |
| 296 Bind(&if_receiverisincompatible); |
| 297 { |
| 298 // If Type(O) is not Object, or if O does not have a [[SyncIterator]] |
| 299 // internal slot, then |
| 300 |
| 301 // Let badIteratorError be a new TypeError exception. |
| 302 Node* const error = |
| 303 MakeTypeError(MessageTemplate::kIncompatibleMethodReceiver, context, |
| 304 CStringConstant(method_name), iterator); |
| 305 |
| 306 // Perform ! Call(promiseCapability.[[Reject]], undefined, |
| 307 // « badIteratorError »). |
| 308 var_exception.Bind(error); |
| 309 Goto(&reject_promise); |
| 310 } |
| 311 |
| 312 Bind(&reject_promise); |
| 313 { |
| 314 Node* const exception = var_exception.value(); |
| 315 InternalPromiseReject(context, promise, exception, TrueConstant()); |
| 316 |
| 317 // Return promiseCapability.[[Promise]]. |
| 318 Return(promise); |
| 319 } |
| 320 } |
| 321 |
| 322 // https://tc39.github.io/proposal-async-iteration/ |
| 323 // Section #sec-%asyncfromsynciteratorprototype%.throw |
| 324 TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrow, |
| 325 AsyncFromSyncBuiltinsAssembler) { |
| 326 const char* method_name = "[Async-from-Sync Iterator].prototype.throw"; |
| 327 |
| 328 Node* const iterator = Parameter(0); |
| 329 Node* const parameter = Parameter(1); |
| 330 Node* const context = Parameter(4); |
| 331 |
| 332 Node* const native_context = LoadNativeContext(context); |
| 333 Node* const promise = AllocateAndInitJSPromise(context); |
| 334 |
| 335 Variable var_exception(this, MachineRepresentation::kTagged); |
| 336 var_exception.Bind(parameter); |
| 337 Variable var_value(this, MachineRepresentation::kTagged); |
| 338 Variable var_done(this, MachineRepresentation::kTagged); |
| 339 Label reject_promise(this, Label::kDeferred); |
| 340 Label if_receiverisincompatible(this, Label::kDeferred); |
| 341 |
| 342 GotoIf(TaggedIsSmi(iterator), &if_receiverisincompatible); |
| 343 GotoUnless(HasInstanceType(iterator, JS_ASYNC_FROM_SYNC_ITERATOR_TYPE), |
| 344 &if_receiverisincompatible); |
| 345 |
| 346 Node* const sync_iterator = |
| 347 LoadObjectField(iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset); |
| 348 |
| 349 // Let throw be GetMethod(syncIterator, "throw"). |
| 350 // IfAbruptRejectPromise(nextResult, promiseCapability). |
| 351 // IfAbruptRejectPromise(throw, promiseCapability). |
| 352 Node* const throw_method = |
| 353 CallStub(CodeFactory::GetProperty(isolate()), context, sync_iterator, |
| 354 HeapConstant(factory()->throw_string())); |
| 355 GotoIfException(throw_method, &reject_promise, &var_exception); |
| 356 |
| 357 Label if_isnotundefined(this), resolve_promise(this); |
| 358 |
| 359 // If throw is undefined, then |
| 360 // Perform ! Call(promiseCapability.[[Reject]], undefined, « value »). |
| 361 Branch(IsUndefined(throw_method), &reject_promise, &if_isnotundefined); |
| 362 |
| 363 Bind(&if_isnotundefined); |
| 364 { |
| 365 // Let throwResult be Call(throw, syncIterator, « value »). |
| 366 Node* const iter_result = CallJS(CodeFactory::Call(isolate()), context, |
| 367 throw_method, sync_iterator, parameter); |
| 368 GotoIfException(iter_result, &reject_promise, &var_exception); |
| 369 |
| 370 Node* value; |
| 371 Node* done; |
| 372 std::tie(value, done) = LoadIteratorResult( |
| 373 context, native_context, iter_result, &reject_promise, &var_exception); |
| 374 |
| 375 // Let valueWrapperCapability be ! NewPromiseCapability(%Promise%). |
| 376 Node* const wrapper = AllocateAndInitJSPromise(context); |
| 377 |
| 378 // Perform ! Call(valueWrapperCapability.[[Resolve]], undefined, « |
| 379 // throwValue »). |
| 380 InternalResolvePromise(context, wrapper, value); |
| 381 |
| 382 // Let onFulfilled be a new built-in function object as defined in |
| 383 // Async Iterator Value Unwrap Functions. |
| 384 // Set onFulfilled.[[Done]] to throwDone. |
| 385 Node* const map = LoadContextElement( |
| 386 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); |
| 387 Node* const on_resolve_shared = LoadContextElement( |
| 388 native_context, Context::ASYNC_ITERATOR_VALUE_UNWRAP_SHARED_FUN); |
| 389 CSA_ASSERT(this, |
| 390 HasInstanceType(on_resolve_shared, SHARED_FUNCTION_INFO_TYPE)); |
| 391 Node* const closure_context = |
| 392 AllocateAsyncIteratorValueUnwrapContext(native_context, done); |
| 393 Node* const on_resolve = AllocateFunctionWithMapAndContext( |
| 394 map, on_resolve_shared, closure_context); |
| 395 |
| 396 // Perform ! PerformPromiseThen(valueWrapperCapability.[[Promise]], |
| 397 // onFulfilled, undefined, promiseCapability). |
| 398 Node* const undefined = UndefinedConstant(); |
| 399 InternalPerformPromiseThen(context, wrapper, on_resolve, undefined, promise, |
| 400 undefined, undefined); |
| 401 Return(promise); |
| 402 } |
| 403 |
| 404 Bind(&if_receiverisincompatible); |
| 405 { |
| 406 // If Type(O) is not Object, or if O does not have a [[SyncIterator]] |
| 407 // internal slot, then |
| 408 |
| 409 // Let badIteratorError be a new TypeError exception. |
| 410 Node* const error = |
| 411 MakeTypeError(MessageTemplate::kIncompatibleMethodReceiver, context, |
| 412 CStringConstant(method_name), iterator); |
| 413 |
| 414 // Perform ! Call(promiseCapability.[[Reject]], undefined, |
| 415 // « badIteratorError »). |
| 416 var_exception.Bind(error); |
| 417 Goto(&reject_promise); |
| 418 } |
| 419 |
| 420 Bind(&reject_promise); |
| 421 { |
| 422 Node* const exception = var_exception.value(); |
| 423 InternalPromiseReject(context, promise, exception, TrueConstant()); |
| 424 |
| 425 // Return promiseCapability.[[Promise]]. |
| 426 Return(promise); |
| 427 } |
| 428 } |
| 429 |
| 430 TF_BUILTIN(AsyncIteratorValueUnwrap, AsyncBuiltinsAssembler) { |
| 431 Node* const value = Parameter(1); |
| 432 Node* const context = Parameter(4); |
| 433 |
| 434 Node* const done = LoadContextElement(context, ValueUnwrapContext::kDoneSlot); |
| 435 Node* const unwrapped_value = CallStub( |
| 436 CodeFactory::CreateIterResultObject(isolate()), context, value, done); |
| 437 |
| 438 Return(unwrapped_value); |
| 439 } |
| 440 |
| 441 Node* AsyncBuiltinsAssembler::AllocateAsyncIteratorValueUnwrapContext( |
| 442 Node* native_context, Node* done) { |
| 443 Node* const context = |
| 444 CreatePromiseContext(native_context, ValueUnwrapContext::kLength); |
| 445 StoreContextElementNoWriteBarrier(context, ValueUnwrapContext::kDoneSlot, |
| 446 done); |
| 447 return context; |
| 448 } |
| 449 |
| 450 } // namespace internal |
| 451 } // namespace v8 |
OLD | NEW |