tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

AsyncIteration.h (20987B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef vm_AsyncIteration_h
      8 #define vm_AsyncIteration_h
      9 
     10 #include "builtin/Promise.h"  // js::PromiseHandler
     11 #include "builtin/SelfHostingDefines.h"
     12 #include "js/Class.h"
     13 #include "vm/GeneratorObject.h"
     14 #include "vm/JSObject.h"
     15 #include "vm/List.h"
     16 #include "vm/PromiseObject.h"
     17 
     18 // [SMDOC] Async generators
     19 //
     20 // # Start
     21 //
     22 // When an async generator is called, it synchronously runs until the
     23 // JSOp::InitialYield and then suspends, just like a sync generator, and returns
     24 // an async generator object (js::AsyncGeneratorObject).
     25 //
     26 //
     27 // # Request queue
     28 //
     29 // When next/return/throw is called on the async generator object:
     30 //   * AsyncGeneratorEnqueue creates a new AsyncGeneratorRequest and enqueues
     31 //     it in the generator object's request queue.
     32 //   * AsyncGeneratorResume resumes the generator with the oldest request,
     33 //     if the generator is suspended (see "Resume" section below)
     34 //
     35 // The returned promise is resolved when the resumption for the request
     36 // completes with yield/throw/return, in AsyncGeneratorCompleteStepNormal and
     37 // AsyncGeneratorCompleteStepThrow.
     38 // They correspond to AsyncGeneratorCompleteStep in the spec.
     39 //
     40 //
     41 // # Await
     42 //
     43 // Async generator's `await` is implemented differently than async function's
     44 // `await`.
     45 //
     46 // The bytecode is the following:
     47 // (ignoring CanSkipAwait; see the comment in AsyncFunction.h for more details)
     48 //
     49 // ```
     50 //   (operand here)                  # VALUE
     51 //   GetAliasedVar ".generator"      # VALUE .generator
     52 //   Await 0                         # RVAL GENERATOR RESUMEKIND
     53 //
     54 //   AfterYield                      # RVAL GENERATOR RESUMEKIND
     55 //   CheckResumeKind                 # RVAL
     56 // ```
     57 //
     58 // Async generators don't use JSOp::AsyncAwait, and that part is handled
     59 // in AsyncGeneratorResume, and AsyncGeneratorAwait called there.
     60 //
     61 // Both JSOp::Await and JSOp::Yield behave in the exactly same way,
     62 // and AsyncGeneratorResume checks the last opcode and branches for
     63 // await/yield/return cases.
     64 //
     65 //
     66 // # Reaction jobs and resume after await
     67 //
     68 // This is almost same as for async functions (see AsyncFunction.h).
     69 //
     70 // The reaction record for the job is marked as "this is for async generator"
     71 // (see AsyncGeneratorAwait), and handled specially in
     72 // PromiseReactionJob, which calls js::AsyncGeneratorPromiseReactionJob.
     73 //
     74 //
     75 // # Yield
     76 //
     77 // `yield` is implemented with the following bytecode sequence:
     78 // (Ignoring CanSkipAwait for simplicity)
     79 //
     80 // ```
     81 //   (operand here)                  # VALUE
     82 //   GetAliasedVar ".generator"      # VALUE .generator
     83 //   Await 1                         # RVAL GENERATOR RESUMEKIND
     84 //   AfterYield                      # RVAL GENERATOR RESUMEKIND
     85 //   CheckResumeKind                 # RVAL
     86 //
     87 //   GetAliasedVar ".generator"      # RVAL .generator
     88 //   Yield 2                         # RVAL2 GENERATOR RESUMEKIND
     89 //
     90 //   AfterYield                      # RVAL2 GENERATOR RESUMEKIND
     91 //   CheckResumeKind                 # RVAL2
     92 // ```
     93 //
     94 // The 1st part (JSOp::Await + JSOp::CheckResumeKind) performs an implicit
     95 // `await`, as specified in Yield step 2.
     96 //
     97 //   Yield ( value )
     98 //   https://tc39.es/ecma262/#sec-yield
     99 //
    100 //     2. If generatorKind is async, return
    101 //        ? AsyncGeneratorYield(? Await(value)).
    102 //
    103 // The 2nd part (JSOp::Yield) suspends execution and yields the result of
    104 // `await`, as specified in AsyncGeneratorYield.
    105 //
    106 //   AsyncGeneratorYield ( value )
    107 //   https://tc39.es/ecma262/#sec-asyncgeneratoryield
    108 //
    109 //     1. Let genContext be the running execution context.
    110 //     2. Assert: genContext is the execution context of a generator.
    111 //     3. Let generator be the value of the Generator component of genContext.
    112 //     4. Assert: GetGeneratorKind() is async.
    113 //     5. Let completion be NormalCompletion(value).
    114 //     6. Assert: The execution context stack has at least two elements.
    115 //     7. Let previousContext be the second to top element of the execution
    116 //        context stack.
    117 //     8. Let previousRealm be previousContext's Realm.
    118 //     9. Perform AsyncGeneratorCompleteStep(generator, completion, false,
    119 //        previousRealm).
    120 //     10. Let queue be generator.[[AsyncGeneratorQueue]].
    121 //     11. If queue is not empty, then
    122 //       a. NOTE: Execution continues without suspending the generator.
    123 //       b. Let toYield be the first element of queue.
    124 //       c. Let resumptionValue be Completion(toYield.[[Completion]]).
    125 //       d. Return ? AsyncGeneratorUnwrapYieldResumption(resumptionValue).
    126 //     12. Else,
    127 //       a. Set generator.[[AsyncGeneratorState]] to suspended-yield.
    128 //       b. Remove genContext from the execution context stack and restore the
    129 //          execution context that is at the top of the execution context stack
    130 //          as the running execution context.
    131 //       c. Let callerContext be the running execution context.
    132 //       d. Resume callerContext passing undefined. If genContext is ever
    133 //          resumed again, let resumptionValue be the Completion Record with
    134 //          which it is resumed.
    135 //       e. Assert: If control reaches here, then genContext is the running
    136 //          execution context again.
    137 //       f. Return ? AsyncGeneratorUnwrapYieldResumption(resumptionValue).
    138 //
    139 // The last part (JSOp::CheckResumeKind) checks the resumption type and
    140 // resumes/throws/returns the execution, as specified in
    141 // AsyncGeneratorUnwrapYieldResumption
    142 //
    143 //   AsyncGeneratorUnwrapYieldResumption ( resumptionValue )
    144 //   https://tc39.es/ecma262/#sec-asyncgeneratorunwrapyieldresumption
    145 //
    146 //     1. If resumptionValue is not a return completion,
    147 //        return ? resumptionValue.
    148 //     2. Let awaited be Completion(Await(resumptionValue.[[Value]])).
    149 //     3. If awaited is a throw completion, return ? awaited.
    150 //     4. Assert: awaited is a normal completion.
    151 //     5. Return ReturnCompletion(awaited.[[Value]]).
    152 //
    153 // Resumption with `AsyncGenerator.prototype.return` is handled differently.
    154 // See "Resumption with return" section below.
    155 //
    156 //
    157 // # Return
    158 //
    159 // `return` with operand is implemented with the following bytecode sequence:
    160 // (Ignoring CanSkipAwait for simplicity)
    161 //
    162 // ```
    163 //   (operand here)                  # VALUE
    164 //   GetAliasedVar ".generator"      # VALUE .generator
    165 //   Await 0                         # RVAL GENERATOR RESUMEKIND
    166 //   AfterYield                      # RVAL GENERATOR RESUMEKIND
    167 //   CheckResumeKind                 # RVAL
    168 //
    169 //   SetRval                         #
    170 //   GetAliasedVar ".generator"      # .generator
    171 //   FinalYieldRval                  #
    172 // ```
    173 //
    174 // The 1st part (JSOp::Await + JSOp::CheckResumeKind) performs implicit
    175 // `await`, as specified in ReturnStatement's Evaluation step 3.
    176 //
    177 //   ReturnStatement: return Expression;
    178 //   https://tc39.es/ecma262/#sec-return-statement-runtime-semantics-evaluation
    179 //
    180 //     3. If GetGeneratorKind() is async, set exprValue to ? Await(exprValue).
    181 //
    182 // And the 2nd part corresponds to AsyncGeneratorStart steps 4.g-l.
    183 //
    184 //   AsyncGeneratorStart ( generator, generatorBody )
    185 //   https://tc39.es/ecma262/#sec-asyncgeneratorstart
    186 //
    187 //     4. Let closure be a new Abstract Closure with no parameters that captures
    188 //        generatorBody and performs the following steps when called:
    189 //       ...
    190 //       g. Set acGenerator.[[AsyncGeneratorState]] to draining-queue.
    191 //       h. If result is a normal completion, set result to
    192 //          NormalCompletion(undefined).
    193 //       i. If result is a return completion, set result to
    194 //          NormalCompletion(result.[[Value]]).
    195 //       j. Perform AsyncGeneratorCompleteStep(acGenerator, result, true).
    196 //       k. Perform AsyncGeneratorDrainQueue(acGenerator).
    197 //       l. Return undefined.
    198 //
    199 // `return` without operand or implicit return is implicit with the following
    200 // bytecode sequence:
    201 //
    202 // ```
    203 //   Undefined                       # undefined
    204 //   SetRval                         #
    205 //   GetAliasedVar ".generator"      # .generator
    206 //   FinalYieldRval                  #
    207 // ```
    208 //
    209 // This is also AsyncGeneratorStart steps 4.g-l.
    210 //
    211 //
    212 // # Throw
    213 //
    214 // Unlike async function, async generator doesn't use implicit try-catch,
    215 // but the throw completion is handled by AsyncGeneratorResume,
    216 // and AsyncGeneratorThrown is called there.
    217 //
    218 //   AsyncGeneratorStart ( generator, generatorBody )
    219 //   https://tc39.es/ecma262/#sec-asyncgeneratorstart
    220 //
    221 //     4. Let closure be a new Abstract Closure with no parameters that captures
    222 //        generatorBody and performs the following steps when called:
    223 //       ...
    224 //       g. Set acGenerator.[[AsyncGeneratorState]] to draining-queue.
    225 //       h. If result is a normal completion, set result to
    226 //          NormalCompletion(undefined).
    227 //       i. If result is a return completion, set result to
    228 //          NormalCompletion(result.[[Value]]).
    229 //       j. Perform AsyncGeneratorCompleteStep(acGenerator, result, true).
    230 //       k. Perform AsyncGeneratorDrainQueue(acGenerator).
    231 //       l. Return undefined.
    232 //
    233 //   AsyncGeneratorCompleteStep ( generator, completion, done [ , realm ] )
    234 //   https://tc39.es/ecma262/#sec-asyncgeneratorcompletestep
    235 //
    236 //     1. Assert: generator.[[AsyncGeneratorQueue]] is not empty.
    237 //     2. Let next be the first element of generator.[[AsyncGeneratorQueue]].
    238 //     3. Remove the first element from generator.[[AsyncGeneratorQueue]].
    239 //     4. Let promiseCapability be next.[[Capability]].
    240 //     5. Let value be completion.[[Value]].
    241 //     6. If completion is a throw completion, then
    242 //       a. Perform ! Call(promiseCapability.[[Reject]], undefined, « value »).
    243 //
    244 //
    245 // # Resumption with return
    246 //
    247 // If the generator is in "suspended-yield" state, it doesn't immediately
    248 // resume the generator script itself, but it handles implicit `await` it in
    249 // AsyncGeneratorUnwrapYieldResumption.
    250 // (See PromiseHandler::AsyncGeneratorYieldReturnAwaitedFulfilled and
    251 // PromiseHandler::AsyncGeneratorYieldReturnAwaitedRejected), and resumes the
    252 // generator with the result of await.
    253 //
    254 // The return completion is finally handled in JSOp::CheckResumeKind
    255 // after JSOp::Yield.
    256 //
    257 //   AsyncGeneratorUnwrapYieldResumption ( resumptionValue )
    258 //   https://tc39.es/ecma262/#sec-asyncgeneratorunwrapyieldresumption
    259 //
    260 //     1. If resumptionValue is not a return completion, return ?
    261 //        resumptionValue.
    262 //     2. Let awaited be Completion(Await(resumptionValue.[[Value]])).
    263 //     3. If awaited is a throw completion, return ? awaited.
    264 //     4. Assert: awaited is a normal completion.
    265 //     5. Return ReturnCompletion(awaited.[[Value]]).
    266 //
    267 // If the generator is already completed, it awaits on the return value in
    268 // AsyncGeneratorAwaitReturn.
    269 // (See PromiseHandler::AsyncGeneratorAwaitReturnFulfilled and
    270 //  PromiseHandler::AsyncGeneratorAwaitReturnRejected), and resolves the
    271 // request's promise with the value.
    272 
    273 namespace js {
    274 
    275 class AsyncGeneratorObject;
    276 enum class CompletionKind : uint8_t;
    277 
    278 extern const JSClass AsyncGeneratorFunctionClass;
    279 
    280 [[nodiscard]] bool AsyncGeneratorPromiseReactionJob(
    281    JSContext* cx, PromiseHandler handler,
    282    Handle<AsyncGeneratorObject*> generator, HandleValue argument);
    283 
    284 bool AsyncGeneratorNext(JSContext* cx, unsigned argc, Value* vp);
    285 bool AsyncGeneratorReturn(JSContext* cx, unsigned argc, Value* vp);
    286 bool AsyncGeneratorThrow(JSContext* cx, unsigned argc, Value* vp);
    287 
    288 // AsyncGeneratorRequest record in the spec.
    289 // Stores the info from AsyncGenerator#{next,return,throw}.
    290 //
    291 // This object is reused across multiple requests as an optimization, and
    292 // stored in the Slot_CachedRequest slot.
    293 class AsyncGeneratorRequest : public NativeObject {
    294 private:
    295  enum AsyncGeneratorRequestSlots {
    296    // Int32 value with CompletionKind.
    297    //   Normal: next
    298    //   Return: return
    299    //   Throw:  throw
    300    Slot_CompletionKind = 0,
    301 
    302    // The value passed to AsyncGenerator#{next,return,throw}.
    303    Slot_CompletionValue,
    304 
    305    // The promise returned by AsyncGenerator#{next,return,throw}.
    306    Slot_Promise,
    307 
    308    Slots,
    309  };
    310 
    311  void init(CompletionKind completionKind, const Value& completionValue,
    312            PromiseObject* promise) {
    313    setFixedSlot(Slot_CompletionKind,
    314                 Int32Value(static_cast<int32_t>(completionKind)));
    315    setFixedSlot(Slot_CompletionValue, completionValue);
    316    setFixedSlot(Slot_Promise, ObjectValue(*promise));
    317  }
    318 
    319  // Clear the request data for reuse.
    320  void clearData() {
    321    setFixedSlot(Slot_CompletionValue, NullValue());
    322    setFixedSlot(Slot_Promise, NullValue());
    323  }
    324 
    325  friend AsyncGeneratorObject;
    326 
    327 public:
    328  static const JSClass class_;
    329 
    330  static AsyncGeneratorRequest* create(JSContext* cx,
    331                                       CompletionKind completionKind,
    332                                       HandleValue completionValue,
    333                                       Handle<PromiseObject*> promise);
    334 
    335  CompletionKind completionKind() const {
    336    return static_cast<CompletionKind>(
    337        getFixedSlot(Slot_CompletionKind).toInt32());
    338  }
    339  JS::Value completionValue() const {
    340    return getFixedSlot(Slot_CompletionValue);
    341  }
    342  PromiseObject* promise() const {
    343    return &getFixedSlot(Slot_Promise).toObject().as<PromiseObject>();
    344  }
    345 };
    346 
    347 class AsyncGeneratorObject : public AbstractGeneratorObject {
    348 private:
    349  enum AsyncGeneratorObjectSlots {
    350    // Int32 value containing one of the |State| fields from below.
    351    Slot_State = AbstractGeneratorObject::RESERVED_SLOTS,
    352 
    353    // * null value if this async generator has no requests
    354    // * AsyncGeneratorRequest if this async generator has only one request
    355    // * list object if this async generator has 2 or more requests
    356    Slot_QueueOrRequest,
    357 
    358    // Cached AsyncGeneratorRequest for later use.
    359    // undefined if there's no cache.
    360    Slot_CachedRequest,
    361 
    362    Slots
    363  };
    364 
    365 public:
    366  enum State {
    367    // "suspended-start" in the spec.
    368    // Suspended after invocation.
    369    State_SuspendedStart,
    370 
    371    // "suspended-yield" in the spec
    372    // Suspended with `yield` expression.
    373    State_SuspendedYield,
    374 
    375    // "executing" in the spec.
    376    // Resumed from initial suspend or yield, and either running the script
    377    // or awaiting for `await` expression.
    378    State_Executing,
    379 
    380    // Part of "executing" in the spec.
    381    // Awaiting on the value passed by AsyncGenerator#return which is called
    382    // while executing.
    383    State_Executing_AwaitingYieldReturn,
    384 
    385    // "draining-queue" in the spec.
    386    // It's performing AsyncGeneratorDrainQueue.
    387    State_DrainingQueue,
    388 
    389    // Part of "draining-queue" in the spec.
    390    // Awaiting on the value passed by AsyncGenerator#return which is called
    391    // after completed.
    392    State_DrainingQueue_AwaitingReturn,
    393 
    394    // "completed" in the spec.
    395    // The generator is completed.
    396    State_Completed
    397  };
    398 
    399  State state() const {
    400    return static_cast<State>(getFixedSlot(Slot_State).toInt32());
    401  }
    402  void setState(State state_) { setFixedSlot(Slot_State, Int32Value(state_)); }
    403 
    404 private:
    405  // Queue is implemented in 2 ways.  If only one request is queued ever,
    406  // request is stored directly to the slot.  Once 2 requests are queued, a
    407  // list is created and requests are appended into it, and the list is
    408  // stored to the slot.
    409 
    410  bool isSingleQueue() const {
    411    return getFixedSlot(Slot_QueueOrRequest).isNull() ||
    412           getFixedSlot(Slot_QueueOrRequest)
    413               .toObject()
    414               .is<AsyncGeneratorRequest>();
    415  }
    416  bool isSingleQueueEmpty() const {
    417    return getFixedSlot(Slot_QueueOrRequest).isNull();
    418  }
    419  void setSingleQueueRequest(AsyncGeneratorRequest* request) {
    420    setFixedSlot(Slot_QueueOrRequest, ObjectValue(*request));
    421  }
    422  void clearSingleQueueRequest() {
    423    setFixedSlot(Slot_QueueOrRequest, NullValue());
    424  }
    425  AsyncGeneratorRequest* singleQueueRequest() const {
    426    return &getFixedSlot(Slot_QueueOrRequest)
    427                .toObject()
    428                .as<AsyncGeneratorRequest>();
    429  }
    430 
    431  ListObject* queue() const {
    432    return &getFixedSlot(Slot_QueueOrRequest).toObject().as<ListObject>();
    433  }
    434  void setQueue(ListObject* queue_) {
    435    setFixedSlot(Slot_QueueOrRequest, ObjectValue(*queue_));
    436  }
    437 
    438 public:
    439  static const JSClass class_;
    440  static const JSClassOps classOps_;
    441 
    442  static AsyncGeneratorObject* create(JSContext* cx, HandleFunction asyncGen);
    443 
    444  bool isSuspendedStart() const { return state() == State_SuspendedStart; }
    445  bool isSuspendedYield() const { return state() == State_SuspendedYield; }
    446  bool isExecuting() const { return state() == State_Executing; }
    447  bool isExecuting_AwaitingYieldReturn() const {
    448    return state() == State_Executing_AwaitingYieldReturn;
    449  }
    450  bool isDrainingQueue() const { return state() == State_DrainingQueue; }
    451  bool isDrainingQueue_AwaitingReturn() const {
    452    return state() == State_DrainingQueue_AwaitingReturn;
    453  }
    454  bool isCompleted() const { return state() == State_Completed; }
    455 
    456  void setSuspendedStart() { setState(State_SuspendedStart); }
    457  void setSuspendedYield() { setState(State_SuspendedYield); }
    458  void setExecuting() { setState(State_Executing); }
    459  void setExecuting_AwaitingYieldReturn() {
    460    setState(State_Executing_AwaitingYieldReturn);
    461  }
    462  void setDrainingQueue() { setState(State_DrainingQueue); }
    463  void setDrainingQueue_AwaitingReturn() {
    464    setState(State_DrainingQueue_AwaitingReturn);
    465  }
    466  void setCompleted() { setState(State_Completed); }
    467 
    468  [[nodiscard]] static bool enqueueRequest(
    469      JSContext* cx, Handle<AsyncGeneratorObject*> generator,
    470      Handle<AsyncGeneratorRequest*> request);
    471 
    472  static AsyncGeneratorRequest* dequeueRequest(
    473      JSContext* cx, Handle<AsyncGeneratorObject*> generator);
    474 
    475  static AsyncGeneratorRequest* peekRequest(
    476      Handle<AsyncGeneratorObject*> generator);
    477 
    478  bool isQueueEmpty() const {
    479    if (isSingleQueue()) {
    480      return isSingleQueueEmpty();
    481    }
    482    return queue()->getDenseInitializedLength() == 0;
    483  }
    484 
    485 #ifdef DEBUG
    486  bool isQueueLengthOne() const {
    487    if (isSingleQueue()) {
    488      return !isSingleQueueEmpty();
    489    }
    490    return queue()->getDenseInitializedLength() == 1;
    491  }
    492 #endif
    493 
    494  // This function does either of the following:
    495  //   * return a cached request object with the slots updated
    496  //   * create a new request object with the slots set
    497  static AsyncGeneratorRequest* createRequest(
    498      JSContext* cx, Handle<AsyncGeneratorObject*> generator,
    499      CompletionKind completionKind, HandleValue completionValue,
    500      Handle<PromiseObject*> promise);
    501 
    502  // Stores the given request to the generator's cache after clearing its data
    503  // slots.  The cached request will be reused in the subsequent createRequest
    504  // call.
    505  void cacheRequest(AsyncGeneratorRequest* request) {
    506    if (hasCachedRequest()) {
    507      return;
    508    }
    509 
    510    request->clearData();
    511    setFixedSlot(Slot_CachedRequest, ObjectValue(*request));
    512  }
    513 
    514 private:
    515  bool hasCachedRequest() const {
    516    return getFixedSlot(Slot_CachedRequest).isObject();
    517  }
    518 
    519  AsyncGeneratorRequest* takeCachedRequest() {
    520    auto request = &getFixedSlot(Slot_CachedRequest)
    521                        .toObject()
    522                        .as<AsyncGeneratorRequest>();
    523    clearCachedRequest();
    524    return request;
    525  }
    526 
    527  void clearCachedRequest() { setFixedSlot(Slot_CachedRequest, NullValue()); }
    528 };
    529 
    530 JSObject* CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter,
    531                                      HandleValue nextMethod);
    532 
    533 class AsyncFromSyncIteratorObject : public NativeObject {
    534 private:
    535  enum AsyncFromSyncIteratorObjectSlots {
    536    // Object that implements the sync iterator protocol.
    537    Slot_Iterator = 0,
    538 
    539    // The `next` property of the iterator object.
    540    Slot_NextMethod = 1,
    541 
    542    Slots
    543  };
    544 
    545  void init(JSObject* iterator, const Value& nextMethod) {
    546    setFixedSlot(Slot_Iterator, ObjectValue(*iterator));
    547    setFixedSlot(Slot_NextMethod, nextMethod);
    548  }
    549 
    550 public:
    551  static const JSClass class_;
    552 
    553  static JSObject* create(JSContext* cx, HandleObject iter,
    554                          HandleValue nextMethod);
    555 
    556  JSObject* iterator() const { return &getFixedSlot(Slot_Iterator).toObject(); }
    557 
    558  const Value& nextMethod() const { return getFixedSlot(Slot_NextMethod); }
    559 };
    560 
    561 class AsyncIteratorObject : public NativeObject {
    562 public:
    563  static const JSClass class_;
    564  static const JSClass protoClass_;
    565 };
    566 
    567 // Iterator Helpers proposal
    568 class AsyncIteratorHelperObject : public NativeObject {
    569 public:
    570  static const JSClass class_;
    571 
    572  enum { GeneratorSlot, SlotCount };
    573 
    574  static_assert(GeneratorSlot == ASYNC_ITERATOR_HELPER_GENERATOR_SLOT,
    575                "GeneratorSlot must match self-hosting define for generator "
    576                "object slot.");
    577 };
    578 
    579 AsyncIteratorHelperObject* NewAsyncIteratorHelper(JSContext* cx);
    580 
    581 }  // namespace js
    582 
    583 #endif /* vm_AsyncIteration_h */