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