OLD | NEW |
(Empty) | |
| 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 |
| 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 namespace { |
| 16 |
| 17 // Describe fields of Context associated with AsyncGeneratorAwait resume |
| 18 // closures. |
| 19 class AwaitContext { |
| 20 public: |
| 21 enum Fields { kGeneratorSlot = Context::MIN_CONTEXT_SLOTS, kLength }; |
| 22 }; |
| 23 |
| 24 } // anonymous namespace |
| 25 |
| 26 // https://tc39.github.io/proposal-async-iteration/ |
| 27 // Section #sec-asyncgenerator-prototype-next |
| 28 TF_BUILTIN(AsyncGeneratorPrototypeNext, AsyncBuiltinsAssembler) { |
| 29 Node* const generator = Parameter(0); |
| 30 Node* const value = Parameter(1); |
| 31 Node* const context = Parameter(4); |
| 32 AsyncGeneratorEnqueue(context, generator, value, |
| 33 JSAsyncGeneratorObject::kNext, |
| 34 "[AsyncGenerator].prototype.next"); |
| 35 } |
| 36 |
| 37 // https://tc39.github.io/proposal-async-iteration/ |
| 38 // Section #sec-asyncgenerator-prototype-return |
| 39 TF_BUILTIN(AsyncGeneratorPrototypeReturn, AsyncBuiltinsAssembler) { |
| 40 Node* generator = Parameter(0); |
| 41 Node* value = Parameter(1); |
| 42 Node* context = Parameter(4); |
| 43 AsyncGeneratorEnqueue(context, generator, value, |
| 44 JSAsyncGeneratorObject::kReturn, |
| 45 "[AsyncGenerator].prototype.return"); |
| 46 } |
| 47 |
| 48 // https://tc39.github.io/proposal-async-iteration/ |
| 49 // Section #sec-asyncgenerator-prototype-throw |
| 50 TF_BUILTIN(AsyncGeneratorPrototypeThrow, AsyncBuiltinsAssembler) { |
| 51 Node* generator = Parameter(0); |
| 52 Node* value = Parameter(1); |
| 53 Node* context = Parameter(4); |
| 54 AsyncGeneratorEnqueue(context, generator, value, |
| 55 JSAsyncGeneratorObject::kThrow, |
| 56 "[AsyncGenerator].prototype.throw"); |
| 57 } |
| 58 |
| 59 TF_BUILTIN(AsyncGeneratorYield, AsyncBuiltinsAssembler) { |
| 60 Node* const generator = Parameter(0); |
| 61 Node* const value = Parameter(1); |
| 62 Node* const context = Parameter(4); |
| 63 |
| 64 CSA_ASSERT_JS_ARGC_EQ(this, 1); |
| 65 CSA_SLOW_ASSERT(this, |
| 66 HasInstanceType(generator, JS_ASYNC_GENERATOR_OBJECT_TYPE)); |
| 67 |
| 68 Node* request = TakeFirstAsyncGeneratorRequestFromQueue(generator); |
| 69 |
| 70 Node* const promise = |
| 71 LoadObjectField(request, AsyncGeneratorRequest::kPromiseOffset); |
| 72 |
| 73 // The request Promise must still be pending at this point. |
| 74 CSA_SLOW_ASSERT(this, |
| 75 WordEqual(LoadObjectField(promise, JSPromise::kStatusOffset), |
| 76 SmiConstant(v8::Promise::kPending))); |
| 77 |
| 78 // If a yield expression is reached, we must not be awaiting any value. |
| 79 CSA_SLOW_ASSERT(this, |
| 80 IsUndefined(LoadObjectField( |
| 81 request, AsyncGeneratorRequest::kAwaitedPromiseOffset))); |
| 82 |
| 83 Node* const iter_result = AllocateJSIteratorResult(context, value, false); |
| 84 |
| 85 InternalResolvePromise(context, promise, iter_result); |
| 86 |
| 87 AsyncGeneratorResumeNext(context, generator); |
| 88 |
| 89 Return(UndefinedConstant()); |
| 90 } |
| 91 |
| 92 TF_BUILTIN(AsyncGeneratorAwaitResolveClosure, AsyncBuiltinsAssembler) { |
| 93 Node* value = Parameter(1); |
| 94 Node* context = Parameter(4); |
| 95 AsyncGeneratorAwaitResumeClosure(context, value, |
| 96 JSAsyncGeneratorObject::kNext); |
| 97 Return(UndefinedConstant()); |
| 98 } |
| 99 |
| 100 TF_BUILTIN(AsyncGeneratorAwaitRejectClosure, AsyncBuiltinsAssembler) { |
| 101 Node* value = Parameter(1); |
| 102 Node* context = Parameter(4); |
| 103 AsyncGeneratorAwaitResumeClosure(context, value, |
| 104 JSAsyncGeneratorObject::kThrow); |
| 105 Return(UndefinedConstant()); |
| 106 } |
| 107 |
| 108 TF_BUILTIN(AsyncGeneratorAwaitUncaught, AsyncBuiltinsAssembler) { |
| 109 const bool kIsCatchable = false; |
| 110 AsyncGeneratorAwait(kIsCatchable); |
| 111 } |
| 112 |
| 113 TF_BUILTIN(AsyncGeneratorAwaitCaught, AsyncBuiltinsAssembler) { |
| 114 const bool kIsCatchable = true; |
| 115 AsyncGeneratorAwait(kIsCatchable); |
| 116 } |
| 117 |
| 118 // Shared implementation for the 3 Async Iterator protocol methods of Async |
| 119 // Generators. |
| 120 void AsyncBuiltinsAssembler::AsyncGeneratorEnqueue( |
| 121 Node* context, Node* generator, Node* value, |
| 122 JSAsyncGeneratorObject::ResumeMode resume_mode, const char* method_name) { |
| 123 // AsyncGeneratorEnqueue produces a new Promise, and appends it to the list |
| 124 // of async generator requests to be executed. If the generator is not |
| 125 // presently executing, then this method will loop through, processing each |
| 126 // request from front to back. |
| 127 // This loop is resides in AsyncGeneratorResumeNext. |
| 128 Node* promise = AllocateAndInitJSPromise(context); |
| 129 |
| 130 Label enqueue(this), if_receiverisincompatible(this, Label::kDeferred); |
| 131 |
| 132 GotoIf(TaggedIsSmi(generator), &if_receiverisincompatible); |
| 133 Branch(HasInstanceType(generator, JS_ASYNC_GENERATOR_OBJECT_TYPE), &enqueue, |
| 134 &if_receiverisincompatible); |
| 135 |
| 136 Bind(&enqueue); |
| 137 { |
| 138 Label done(this); |
| 139 Node* const req = |
| 140 AllocateAsyncGeneratorRequest(resume_mode, value, promise); |
| 141 |
| 142 AddAsyncGeneratorRequestToQueue(generator, req); |
| 143 |
| 144 // Let state be generator.[[AsyncGeneratorState]] |
| 145 // If state is not "executing", then |
| 146 // Perform AsyncGeneratorResumeNext(Generator) |
| 147 // Check if the {receiver} is running or already closed. |
| 148 Node* continuation = |
| 149 LoadObjectField(generator, JSAsyncGeneratorObject::kContinuationOffset); |
| 150 |
| 151 GotoIf(SmiEqual(continuation, |
| 152 SmiConstant(JSAsyncGeneratorObject::kGeneratorExecuting)), |
| 153 &done); |
| 154 |
| 155 AsyncGeneratorResumeNext(context, generator, continuation); |
| 156 |
| 157 Goto(&done); |
| 158 Bind(&done); |
| 159 Return(promise); |
| 160 } |
| 161 |
| 162 Bind(&if_receiverisincompatible); |
| 163 { |
| 164 Node* native_context = LoadNativeContext(context); |
| 165 Node* make_type_error = |
| 166 LoadContextElement(native_context, Context::MAKE_TYPE_ERROR_INDEX); |
| 167 Handle<String> method = |
| 168 factory()->NewStringFromAsciiChecked(method_name, TENURED); |
| 169 Node* error = |
| 170 CallJS(CodeFactory::Call(isolate()), context, make_type_error, |
| 171 UndefinedConstant(), |
| 172 SmiConstant(MessageTemplate::kIncompatibleMethodReceiver), |
| 173 HeapConstant(method), generator); |
| 174 |
| 175 CallRuntime(Runtime::kPromiseReject, context, promise, error, |
| 176 TrueConstant()); |
| 177 Return(promise); |
| 178 } |
| 179 } |
| 180 |
| 181 void AsyncBuiltinsAssembler::AsyncGeneratorResumeNext(Node* context, |
| 182 Node* generator, |
| 183 Node* continuation) { |
| 184 // Loop through each queued request, from first to last, and resuming the |
| 185 // generator. Will not resume generator if resumed with an abrupt completion |
| 186 // such as via the .throw() or .return() methods, or if an Await is |
| 187 // in progress. |
| 188 Variable var_request(this, MachineRepresentation::kTagged); |
| 189 Variable var_continuation(this, MachineRepresentation::kTaggedSigned); |
| 190 var_continuation.Bind(continuation); |
| 191 |
| 192 Variable* loop_vars[] = {&var_request, &var_continuation}; |
| 193 Label loop(this, arraysize(loop_vars), |
| 194 reinterpret_cast<Variable **>(loop_vars)), |
| 195 done(this); |
| 196 |
| 197 var_request.Bind( |
| 198 LoadObjectField(generator, JSAsyncGeneratorObject::kQueueOffset)); |
| 199 Branch(WordEqual(var_request.value(), UndefinedConstant()), &done, &loop); |
| 200 Bind(&loop); |
| 201 { |
| 202 AsyncGeneratorResumeNextStep(context, generator, var_request.value(), |
| 203 var_continuation.value(), &done); |
| 204 |
| 205 var_continuation.Bind(LoadObjectField( |
| 206 generator, JSAsyncGeneratorObject::kContinuationOffset)); |
| 207 var_request.Bind( |
| 208 LoadObjectField(generator, JSAsyncGeneratorObject::kQueueOffset)); |
| 209 |
| 210 // Continue loop if there is another request queued up. |
| 211 Branch(WordEqual(var_request.value(), UndefinedConstant()), &done, &loop); |
| 212 } |
| 213 |
| 214 Bind(&done); |
| 215 } |
| 216 |
| 217 void AsyncBuiltinsAssembler::AsyncGeneratorResumeNextStep(Node* context, |
| 218 Node* generator, |
| 219 Node* request, |
| 220 Node* continuation, |
| 221 Label* exit_loop) { |
| 222 Variable var_return(this, MachineRepresentation::kTagged); |
| 223 var_return.Bind(UndefinedConstant()); |
| 224 CSA_SLOW_ASSERT(this, HasInstanceType(request, ASYNC_GENERATOR_REQUEST_TYPE)); |
| 225 |
| 226 Label start(this), loop_next(this); |
| 227 |
| 228 // Stop consuming the AsyncGeneratorRequest queue and resuming the generator |
| 229 // if an Await is in progress. |
| 230 Node* awaited = |
| 231 LoadObjectField(request, AsyncGeneratorRequest::kAwaitedPromiseOffset); |
| 232 Branch(IsUndefined(awaited), &start, exit_loop); |
| 233 |
| 234 Bind(&start); |
| 235 |
| 236 // Let completion be next.[[Completion]] |
| 237 Node* resume_mode = |
| 238 LoadObjectField(request, AsyncGeneratorRequest::kResumeModeOffset); |
| 239 Node* value = LoadObjectField(request, AsyncGeneratorRequest::kValueOffset); |
| 240 |
| 241 // If completion is an abrupt completion, then |
| 242 Label if_abruptcompletion(this), check_ifcompleted(this), |
| 243 resume_generator(this), if_return(this, &var_return), if_throw(this); |
| 244 Branch(SmiEqual(resume_mode, SmiConstant(JSAsyncGeneratorObject::kNext)), |
| 245 &check_ifcompleted, &if_abruptcompletion); |
| 246 |
| 247 Bind(&if_abruptcompletion); |
| 248 { |
| 249 Label check_completed(this), if_completed(this); |
| 250 |
| 251 // If state is "suspendedStart", then |
| 252 // Set generator.[[AsyncGeneratorState]] to "completed" |
| 253 // Set state to "completed" |
| 254 GotoIf(WordNotEqual(continuation, SmiConstant(0)), &check_completed); |
| 255 StoreObjectField(generator, JSAsyncGeneratorObject::kContinuationOffset, |
| 256 SmiConstant(JSAsyncGeneratorObject::kGeneratorClosed)); |
| 257 Goto(&if_completed); |
| 258 |
| 259 Bind(&check_completed); |
| 260 Branch(WordEqual(continuation, |
| 261 SmiConstant(JSAsyncGeneratorObject::kGeneratorClosed)), |
| 262 &if_completed, &resume_generator); |
| 263 |
| 264 Bind(&if_completed); |
| 265 { |
| 266 // If state is "completed", then |
| 267 // If completion.[[Type]] is return, then return |
| 268 // ! AsyncGeneratorResolve(generator, completion.[[Value]], true). |
| 269 // Else, return ! AsyncGeneratorReject(generator, |
| 270 // completion.[[Value]]) |
| 271 var_return.Bind(value); |
| 272 Branch( |
| 273 WordEqual(resume_mode, SmiConstant(JSAsyncGeneratorObject::kReturn)), |
| 274 &if_return, &if_throw); |
| 275 } |
| 276 } |
| 277 |
| 278 Bind(&check_ifcompleted); |
| 279 { |
| 280 // Else if state is "completed", then return ! |
| 281 // AsyncGeneratorResolve(generator, undefined, true). |
| 282 Branch(WordEqual(continuation, |
| 283 SmiConstant(JSAsyncGeneratorObject::kGeneratorClosed)), |
| 284 &if_return, &resume_generator); |
| 285 } |
| 286 |
| 287 Bind(&resume_generator); |
| 288 { |
| 289 CSA_ASSERT(this, SmiAboveOrEqual(continuation, SmiConstant(0))); |
| 290 Node* result = CallStub(CodeFactory::ResumeGenerator(isolate()), context, |
| 291 value, generator, resume_mode); |
| 292 Goto(&loop_next); |
| 293 } |
| 294 |
| 295 Bind(&if_return); |
| 296 { |
| 297 Node* const request = TakeFirstAsyncGeneratorRequestFromQueue(generator); |
| 298 Node* const promise = |
| 299 LoadObjectField(request, AsyncGeneratorRequest::kPromiseOffset); |
| 300 Node* const result = |
| 301 AllocateJSIteratorResult(context, var_return.value(), true); |
| 302 PromiseFulfill(context, promise, result, v8::Promise::kFulfilled); |
| 303 Goto(&loop_next); |
| 304 } |
| 305 |
| 306 Bind(&if_throw); |
| 307 { |
| 308 Node* request = TakeFirstAsyncGeneratorRequestFromQueue(generator); |
| 309 |
| 310 // Let promiseCapability be next.[[Capability]]. |
| 311 Node* promise = |
| 312 LoadObjectField(request, AsyncGeneratorRequest::kPromiseOffset); |
| 313 |
| 314 // Perform ! Call(promiseCapability.[[Reject]], undefined, « exception »). |
| 315 CallRuntime(Runtime::kPromiseReject, context, promise, value, |
| 316 TrueConstant()); |
| 317 Goto(&loop_next); |
| 318 } |
| 319 |
| 320 Bind(&loop_next); |
| 321 } |
| 322 |
| 323 Node* AsyncBuiltinsAssembler::AllocateAsyncGeneratorRequest( |
| 324 JSAsyncGeneratorObject::ResumeMode resume_mode, Node* resume_value, |
| 325 Node* promise) { |
| 326 CSA_SLOW_ASSERT(this, HasInstanceType(promise, JS_PROMISE_TYPE)); |
| 327 Node* request = Allocate(AsyncGeneratorRequest::kSize); |
| 328 StoreMapNoWriteBarrier(request, Heap::kAsyncGeneratorRequestMapRootIndex); |
| 329 StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kNextOffset, |
| 330 UndefinedConstant()); |
| 331 StoreObjectFieldNoWriteBarrier(request, |
| 332 AsyncGeneratorRequest::kResumeModeOffset, |
| 333 SmiConstant(resume_mode)); |
| 334 StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kValueOffset, |
| 335 resume_value); |
| 336 StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kPromiseOffset, |
| 337 promise); |
| 338 StoreObjectFieldNoWriteBarrier(request, |
| 339 AsyncGeneratorRequest::kAwaitedPromiseOffset, |
| 340 UndefinedConstant()); |
| 341 return request; |
| 342 } |
| 343 |
| 344 void AsyncBuiltinsAssembler::AsyncGeneratorAwaitResumeClosure( |
| 345 Node* context, Node* value, |
| 346 JSAsyncGeneratorObject::ResumeMode resume_mode) { |
| 347 Label done(this); |
| 348 Node* const generator = |
| 349 LoadContextElement(context, AwaitContext::kGeneratorSlot); |
| 350 CSA_SLOW_ASSERT(this, |
| 351 HasInstanceType(generator, JS_ASYNC_GENERATOR_OBJECT_TYPE)); |
| 352 |
| 353 Node* const request = |
| 354 LoadObjectField(generator, JSAsyncGeneratorObject::kQueueOffset); |
| 355 CSA_SLOW_ASSERT(this, HasInstanceType(request, ASYNC_GENERATOR_REQUEST_TYPE)); |
| 356 |
| 357 Node* const promise = |
| 358 LoadObjectField(request, AsyncGeneratorRequest::kAwaitedPromiseOffset); |
| 359 CSA_SLOW_ASSERT(this, HasInstanceType(promise, JS_PROMISE_TYPE)); |
| 360 CSA_SLOW_ASSERT( |
| 361 this, WordNotEqual(LoadObjectField(promise, JSPromise::kStatusOffset), |
| 362 SmiConstant(v8::Promise::kPending))); |
| 363 |
| 364 StoreObjectField(request, AsyncGeneratorRequest::kAwaitedPromiseOffset, |
| 365 UndefinedConstant()); |
| 366 |
| 367 Node* const continuation = |
| 368 LoadObjectField(generator, JSAsyncGeneratorObject::kContinuationOffset); |
| 369 |
| 370 GotoUnless(SmiAboveOrEqual(continuation, SmiConstant(0)), &done); |
| 371 CallStub(CodeFactory::ResumeGenerator(isolate()), context, value, generator, |
| 372 SmiConstant(resume_mode)); |
| 373 |
| 374 Goto(&done); |
| 375 Bind(&done); |
| 376 AsyncGeneratorResumeNext(context, generator); |
| 377 } |
| 378 |
| 379 void AsyncBuiltinsAssembler::AsyncGeneratorAwait(bool is_catchable) { |
| 380 Node* generator = Parameter(1); |
| 381 Node* value = Parameter(2); |
| 382 Node* context = Parameter(5); |
| 383 |
| 384 CSA_ASSERT_JS_ARGC_EQ(this, 2); |
| 385 |
| 386 CSA_SLOW_ASSERT(this, |
| 387 HasInstanceType(generator, JS_ASYNC_GENERATOR_OBJECT_TYPE)); |
| 388 |
| 389 Node* const request = |
| 390 LoadObjectField(generator, JSAsyncGeneratorObject::kQueueOffset); |
| 391 CSA_SLOW_ASSERT(this, WordNotEqual(request, UndefinedConstant())); |
| 392 |
| 393 NodeGenerator1 closure_context = [&](Node* native_context) -> Node* { |
| 394 Node* const context = |
| 395 CreatePromiseContext(native_context, AwaitContext::kLength); |
| 396 StoreContextElementNoWriteBarrier(context, AwaitContext::kGeneratorSlot, |
| 397 generator); |
| 398 return context; |
| 399 }; |
| 400 |
| 401 Node* outer_promise = |
| 402 LoadObjectField(request, AsyncGeneratorRequest::kPromiseOffset); |
| 403 |
| 404 const int reject_index = Context::ASYNC_GENERATOR_AWAIT_REJECT_SHARED_FUN; |
| 405 STATIC_ASSERT((Context::ASYNC_GENERATOR_AWAIT_REJECT_SHARED_FUN + 1) == |
| 406 Context::ASYNC_GENERATOR_AWAIT_RESOLVE_SHARED_FUN); |
| 407 |
| 408 Node* promise = Await(context, generator, value, outer_promise, |
| 409 closure_context, reject_index, is_catchable); |
| 410 |
| 411 CSA_SLOW_ASSERT(this, |
| 412 IsUndefined(LoadObjectField( |
| 413 request, AsyncGeneratorRequest::kAwaitedPromiseOffset))); |
| 414 StoreObjectField(request, AsyncGeneratorRequest::kAwaitedPromiseOffset, |
| 415 promise); |
| 416 Return(UndefinedConstant()); |
| 417 } |
| 418 |
| 419 void AsyncBuiltinsAssembler::AddAsyncGeneratorRequestToQueue(Node* generator, |
| 420 Node* request) { |
| 421 Variable var_current(this, MachineRepresentation::kTagged); |
| 422 Label empty(this), loop(this, &var_current), done(this); |
| 423 |
| 424 var_current.Bind( |
| 425 LoadObjectField(generator, JSAsyncGeneratorObject::kQueueOffset)); |
| 426 Branch(IsUndefined(var_current.value()), &empty, &loop); |
| 427 |
| 428 Bind(&empty); |
| 429 { |
| 430 StoreObjectField(generator, JSAsyncGeneratorObject::kQueueOffset, request); |
| 431 Goto(&done); |
| 432 } |
| 433 |
| 434 Bind(&loop); |
| 435 { |
| 436 Label loop_next(this), next_empty(this); |
| 437 Node* current = var_current.value(); |
| 438 Node* next = LoadObjectField(current, AsyncGeneratorRequest::kNextOffset); |
| 439 |
| 440 Branch(IsUndefined(next), &next_empty, &loop_next); |
| 441 Bind(&next_empty); |
| 442 { |
| 443 StoreObjectField(current, AsyncGeneratorRequest::kNextOffset, request); |
| 444 Goto(&done); |
| 445 } |
| 446 |
| 447 Bind(&loop_next); |
| 448 { |
| 449 var_current.Bind(next); |
| 450 Goto(&loop); |
| 451 } |
| 452 } |
| 453 Bind(&done); |
| 454 } |
| 455 |
| 456 Node* AsyncBuiltinsAssembler::TakeFirstAsyncGeneratorRequestFromQueue( |
| 457 Node* generator) { |
| 458 // Removes and returns the first AsyncGeneratorRequest from a |
| 459 // JSAsyncGeneratorObject's queue. Asserts that the queue is not empty. |
| 460 CSA_SLOW_ASSERT(this, |
| 461 HasInstanceType(generator, JS_ASYNC_GENERATOR_OBJECT_TYPE)); |
| 462 |
| 463 Node* request = |
| 464 LoadObjectField(generator, JSAsyncGeneratorObject::kQueueOffset); |
| 465 CSA_SLOW_ASSERT(this, WordNotEqual(request, UndefinedConstant())); |
| 466 |
| 467 Node* next = LoadObjectField(request, AsyncGeneratorRequest::kNextOffset); |
| 468 |
| 469 StoreObjectField(generator, JSAsyncGeneratorObject::kQueueOffset, next); |
| 470 return request; |
| 471 } |
| 472 |
| 473 } // namespace internal |
| 474 } // namespace v8 |
OLD | NEW |