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 #include "vm/AsyncIteration.h"
8 
9 #include "builtin/Array.h"
10 
11 #include "builtin/Promise.h"  // js::AsyncFromSyncIteratorMethod, js::AsyncGeneratorEnqueue
12 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
13 #include "js/PropertySpec.h"
14 #include "vm/FunctionFlags.h"  // js::FunctionFlags
15 #include "vm/GeneratorObject.h"
16 #include "vm/GlobalObject.h"
17 #include "vm/Interpreter.h"
18 #include "vm/PlainObject.h"    // js::PlainObject
19 #include "vm/PromiseObject.h"  // js::PromiseObject
20 #include "vm/Realm.h"
21 #include "vm/SelfHosting.h"
22 #include "vm/WellKnownAtom.h"  // js_*_str
23 
24 #include "vm/JSContext-inl.h"
25 #include "vm/JSObject-inl.h"
26 #include "vm/List-inl.h"
27 
28 using namespace js;
29 
30 // ES2019 draft rev 49b781ec80117b60f73327ef3054703a3111e40c
31 // 6.2.3.1.1 Await Fulfilled Functions
AsyncGeneratorAwaitedFulfilled(JSContext * cx,Handle<AsyncGeneratorObject * > asyncGenObj,HandleValue value)32 [[nodiscard]] bool js::AsyncGeneratorAwaitedFulfilled(
33     JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
34     HandleValue value) {
35   return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Normal, value);
36 }
37 
38 // ES2019 draft rev 49b781ec80117b60f73327ef3054703a3111e40c
39 // 6.2.3.1.2 Await Rejected Functions
AsyncGeneratorAwaitedRejected(JSContext * cx,Handle<AsyncGeneratorObject * > asyncGenObj,HandleValue reason)40 [[nodiscard]] bool js::AsyncGeneratorAwaitedRejected(
41     JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
42     HandleValue reason) {
43   return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Throw, reason);
44 }
45 
46 // ES2019 draft rev 49b781ec80117b60f73327ef3054703a3111e40c
47 // 25.5.3.7 AsyncGeneratorYield, step 8.e.
AsyncGeneratorYieldReturnAwaitedFulfilled(JSContext * cx,Handle<AsyncGeneratorObject * > asyncGenObj,HandleValue value)48 [[nodiscard]] bool js::AsyncGeneratorYieldReturnAwaitedFulfilled(
49     JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
50     HandleValue value) {
51   return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Return, value);
52 }
53 
54 // ES2019 draft rev 49b781ec80117b60f73327ef3054703a3111e40c
55 // 25.5.3.7 AsyncGeneratorYield, step 8.c.
AsyncGeneratorYieldReturnAwaitedRejected(JSContext * cx,Handle<AsyncGeneratorObject * > asyncGenObj,HandleValue reason)56 [[nodiscard]] bool js::AsyncGeneratorYieldReturnAwaitedRejected(
57     JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
58     HandleValue reason) {
59   return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Throw, reason);
60 }
61 
62 const JSClass AsyncFromSyncIteratorObject::class_ = {
63     "AsyncFromSyncIteratorObject",
64     JSCLASS_HAS_RESERVED_SLOTS(AsyncFromSyncIteratorObject::Slots)};
65 
66 // ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e
67 // 25.1.4.1 CreateAsyncFromSyncIterator
CreateAsyncFromSyncIterator(JSContext * cx,HandleObject iter,HandleValue nextMethod)68 JSObject* js::CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter,
69                                           HandleValue nextMethod) {
70   // Steps 1-3.
71   return AsyncFromSyncIteratorObject::create(cx, iter, nextMethod);
72 }
73 
74 // ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e
75 // 25.1.4.1 CreateAsyncFromSyncIterator
76 /* static */
create(JSContext * cx,HandleObject iter,HandleValue nextMethod)77 JSObject* AsyncFromSyncIteratorObject::create(JSContext* cx, HandleObject iter,
78                                               HandleValue nextMethod) {
79   // Step 1.
80   RootedObject proto(cx,
81                      GlobalObject::getOrCreateAsyncFromSyncIteratorPrototype(
82                          cx, cx->global()));
83   if (!proto) {
84     return nullptr;
85   }
86 
87   AsyncFromSyncIteratorObject* asyncIter =
88       NewObjectWithGivenProto<AsyncFromSyncIteratorObject>(cx, proto);
89   if (!asyncIter) {
90     return nullptr;
91   }
92 
93   // Step 2.
94   asyncIter->init(iter, nextMethod);
95 
96   // Step 3 (Call to 7.4.1 GetIterator).
97   // 7.4.1 GetIterator, steps 1-5 are a no-op (*).
98   // 7.4.1 GetIterator, steps 6-8 are implemented in bytecode.
99   //
100   // (*) With <https://github.com/tc39/ecma262/issues/1172> fixed.
101   return asyncIter;
102 }
103 
104 // ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e
105 // 25.1.4.2.1 %AsyncFromSyncIteratorPrototype%.next
AsyncFromSyncIteratorNext(JSContext * cx,unsigned argc,Value * vp)106 static bool AsyncFromSyncIteratorNext(JSContext* cx, unsigned argc, Value* vp) {
107   CallArgs args = CallArgsFromVp(argc, vp);
108   return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Normal);
109 }
110 
111 // ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e
112 // 25.1.4.2.2 %AsyncFromSyncIteratorPrototype%.return
AsyncFromSyncIteratorReturn(JSContext * cx,unsigned argc,Value * vp)113 static bool AsyncFromSyncIteratorReturn(JSContext* cx, unsigned argc,
114                                         Value* vp) {
115   CallArgs args = CallArgsFromVp(argc, vp);
116   return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Return);
117 }
118 
119 // ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e
120 // 25.1.4.2.3 %AsyncFromSyncIteratorPrototype%.throw
AsyncFromSyncIteratorThrow(JSContext * cx,unsigned argc,Value * vp)121 static bool AsyncFromSyncIteratorThrow(JSContext* cx, unsigned argc,
122                                        Value* vp) {
123   CallArgs args = CallArgsFromVp(argc, vp);
124   return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Throw);
125 }
126 
127 // ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e
128 // 25.5.1.2 AsyncGenerator.prototype.next
AsyncGeneratorNext(JSContext * cx,unsigned argc,Value * vp)129 bool js::AsyncGeneratorNext(JSContext* cx, unsigned argc, Value* vp) {
130   CallArgs args = CallArgsFromVp(argc, vp);
131 
132   // Steps 1-3.
133   return AsyncGeneratorEnqueue(cx, args.thisv(), CompletionKind::Normal,
134                                args.get(0), args.rval());
135 }
136 
137 // ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e
138 // 25.5.1.3 AsyncGenerator.prototype.return
AsyncGeneratorReturn(JSContext * cx,unsigned argc,Value * vp)139 bool js::AsyncGeneratorReturn(JSContext* cx, unsigned argc, Value* vp) {
140   CallArgs args = CallArgsFromVp(argc, vp);
141 
142   // Steps 1-3.
143   return AsyncGeneratorEnqueue(cx, args.thisv(), CompletionKind::Return,
144                                args.get(0), args.rval());
145 }
146 
147 // ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e
148 // 25.5.1.4 AsyncGenerator.prototype.throw
AsyncGeneratorThrow(JSContext * cx,unsigned argc,Value * vp)149 bool js::AsyncGeneratorThrow(JSContext* cx, unsigned argc, Value* vp) {
150   CallArgs args = CallArgsFromVp(argc, vp);
151 
152   // Steps 1-3.
153   return AsyncGeneratorEnqueue(cx, args.thisv(), CompletionKind::Throw,
154                                args.get(0), args.rval());
155 }
156 
157 const JSClass AsyncGeneratorObject::class_ = {
158     "AsyncGenerator",
159     JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorObject::Slots),
160     &classOps_,
161 };
162 
163 const JSClassOps AsyncGeneratorObject::classOps_ = {
164     nullptr,                                   // addProperty
165     nullptr,                                   // delProperty
166     nullptr,                                   // enumerate
167     nullptr,                                   // newEnumerate
168     nullptr,                                   // resolve
169     nullptr,                                   // mayResolve
170     nullptr,                                   // finalize
171     nullptr,                                   // call
172     nullptr,                                   // hasInstance
173     nullptr,                                   // construct
174     CallTraceMethod<AbstractGeneratorObject>,  // trace
175 };
176 
177 // ES 2017 draft 9.1.13.
178 // OrdinaryCreateFromConstructor specialized for AsyncGeneratorObjects.
OrdinaryCreateFromConstructorAsynGen(JSContext * cx,HandleFunction fun)179 static AsyncGeneratorObject* OrdinaryCreateFromConstructorAsynGen(
180     JSContext* cx, HandleFunction fun) {
181   // Step 1 (skipped).
182 
183   // Step 2.
184   RootedValue protoVal(cx);
185   if (!GetProperty(cx, fun, fun, cx->names().prototype, &protoVal)) {
186     return nullptr;
187   }
188 
189   RootedObject proto(cx, protoVal.isObject() ? &protoVal.toObject() : nullptr);
190   if (!proto) {
191     proto = GlobalObject::getOrCreateAsyncGeneratorPrototype(cx, cx->global());
192     if (!proto) {
193       return nullptr;
194     }
195   }
196 
197   // Step 3.
198   return NewObjectWithGivenProto<AsyncGeneratorObject>(cx, proto);
199 }
200 
201 /* static */
create(JSContext * cx,HandleFunction asyncGen)202 AsyncGeneratorObject* AsyncGeneratorObject::create(JSContext* cx,
203                                                    HandleFunction asyncGen) {
204   MOZ_ASSERT(asyncGen->isAsync() && asyncGen->isGenerator());
205 
206   AsyncGeneratorObject* asyncGenObj =
207       OrdinaryCreateFromConstructorAsynGen(cx, asyncGen);
208   if (!asyncGenObj) {
209     return nullptr;
210   }
211 
212   // ES2019 draft rev c2aad21fee7f5ddc89fdf7d3d305618ca3a13242
213   // 25.5.3.2 AsyncGeneratorStart.
214 
215   // Step 7.
216   asyncGenObj->setSuspendedStart();
217 
218   // Step 8.
219   asyncGenObj->clearSingleQueueRequest();
220 
221   asyncGenObj->clearCachedRequest();
222 
223   return asyncGenObj;
224 }
225 
226 /* static */
createRequest(JSContext * cx,Handle<AsyncGeneratorObject * > asyncGenObj,CompletionKind completionKind,HandleValue completionValue,Handle<PromiseObject * > promise)227 AsyncGeneratorRequest* AsyncGeneratorObject::createRequest(
228     JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
229     CompletionKind completionKind, HandleValue completionValue,
230     Handle<PromiseObject*> promise) {
231   if (!asyncGenObj->hasCachedRequest()) {
232     return AsyncGeneratorRequest::create(cx, completionKind, completionValue,
233                                          promise);
234   }
235 
236   AsyncGeneratorRequest* request = asyncGenObj->takeCachedRequest();
237   request->init(completionKind, completionValue, promise);
238   return request;
239 }
240 
enqueueRequest(JSContext * cx,Handle<AsyncGeneratorObject * > asyncGenObj,Handle<AsyncGeneratorRequest * > request)241 /* static */ [[nodiscard]] bool AsyncGeneratorObject::enqueueRequest(
242     JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
243     Handle<AsyncGeneratorRequest*> request) {
244   if (asyncGenObj->isSingleQueue()) {
245     if (asyncGenObj->isSingleQueueEmpty()) {
246       asyncGenObj->setSingleQueueRequest(request);
247       return true;
248     }
249 
250     Rooted<ListObject*> queue(cx, ListObject::create(cx));
251     if (!queue) {
252       return false;
253     }
254 
255     RootedValue requestVal(cx, ObjectValue(*asyncGenObj->singleQueueRequest()));
256     if (!queue->append(cx, requestVal)) {
257       return false;
258     }
259     requestVal = ObjectValue(*request);
260     if (!queue->append(cx, requestVal)) {
261       return false;
262     }
263 
264     asyncGenObj->setQueue(queue);
265     return true;
266   }
267 
268   Rooted<ListObject*> queue(cx, asyncGenObj->queue());
269   RootedValue requestVal(cx, ObjectValue(*request));
270   return queue->append(cx, requestVal);
271 }
272 
273 /* static */
dequeueRequest(JSContext * cx,Handle<AsyncGeneratorObject * > asyncGenObj)274 AsyncGeneratorRequest* AsyncGeneratorObject::dequeueRequest(
275     JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj) {
276   if (asyncGenObj->isSingleQueue()) {
277     AsyncGeneratorRequest* request = asyncGenObj->singleQueueRequest();
278     asyncGenObj->clearSingleQueueRequest();
279     return request;
280   }
281 
282   Rooted<ListObject*> queue(cx, asyncGenObj->queue());
283   return &queue->popFirstAs<AsyncGeneratorRequest>(cx);
284 }
285 
286 /* static */
peekRequest(Handle<AsyncGeneratorObject * > asyncGenObj)287 AsyncGeneratorRequest* AsyncGeneratorObject::peekRequest(
288     Handle<AsyncGeneratorObject*> asyncGenObj) {
289   if (asyncGenObj->isSingleQueue()) {
290     return asyncGenObj->singleQueueRequest();
291   }
292 
293   return &asyncGenObj->queue()->getAs<AsyncGeneratorRequest>(0);
294 }
295 
296 const JSClass AsyncGeneratorRequest::class_ = {
297     "AsyncGeneratorRequest",
298     JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorRequest::Slots)};
299 
300 // ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e
301 // 25.5.3.1 AsyncGeneratorRequest Records
302 /* static */
create(JSContext * cx,CompletionKind completionKind,HandleValue completionValue,Handle<PromiseObject * > promise)303 AsyncGeneratorRequest* AsyncGeneratorRequest::create(
304     JSContext* cx, CompletionKind completionKind, HandleValue completionValue,
305     Handle<PromiseObject*> promise) {
306   AsyncGeneratorRequest* request =
307       NewObjectWithGivenProto<AsyncGeneratorRequest>(cx, nullptr);
308   if (!request) {
309     return nullptr;
310   }
311 
312   request->init(completionKind, completionValue, promise);
313   return request;
314 }
315 
316 // ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e
317 // 25.5.3.2 AsyncGeneratorStart
AsyncGeneratorReturned(JSContext * cx,Handle<AsyncGeneratorObject * > asyncGenObj,HandleValue value)318 [[nodiscard]] static bool AsyncGeneratorReturned(
319     JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
320     HandleValue value) {
321   // Step 5.d.
322   asyncGenObj->setCompleted();
323 
324   // Step 5.e (done in bytecode).
325   // Step 5.f.i (implicit).
326 
327   // Step 5.g.
328   return AsyncGeneratorResolve(cx, asyncGenObj, value, true);
329 }
330 
331 // ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e
332 // 25.5.3.2 AsyncGeneratorStart
AsyncGeneratorThrown(JSContext * cx,Handle<AsyncGeneratorObject * > asyncGenObj)333 [[nodiscard]] static bool AsyncGeneratorThrown(
334     JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj) {
335   // Step 5.d.
336   asyncGenObj->setCompleted();
337 
338   // Not much we can do about uncatchable exceptions, so just bail.
339   if (!cx->isExceptionPending()) {
340     return false;
341   }
342 
343   // Step 5.f.i.
344   RootedValue value(cx);
345   if (!GetAndClearException(cx, &value)) {
346     return false;
347   }
348 
349   // Step 5.f.ii.
350   return AsyncGeneratorReject(cx, asyncGenObj, value);
351 }
352 
353 // ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e
354 // 25.5.3.7 AsyncGeneratorYield (partially)
355 // Most steps are done in generator.
AsyncGeneratorYield(JSContext * cx,Handle<AsyncGeneratorObject * > asyncGenObj,HandleValue value)356 [[nodiscard]] static bool AsyncGeneratorYield(
357     JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
358     HandleValue value) {
359   // Step 5 is done in bytecode.
360 
361   // Step 6.
362   asyncGenObj->setSuspendedYield();
363 
364   // Step 9.
365   return AsyncGeneratorResolve(cx, asyncGenObj, value, false);
366 }
367 
368 // ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e
369 // 6.2.3.1 Await, steps 2-9.
370 // 14.4.13 RS: Evaluation, yield*, steps 7.a.vi, 7.b.ii.7, 7.c.ix.
371 // 25.5.3.2 AsyncGeneratorStart, steps 5.d-g.
372 // 25.5.3.5 AsyncGeneratorResumeNext, steps 12-20.
373 // 25.5.3.7 AsyncGeneratorYield, steps 5-6, 9.
374 //
375 // Note: Execution context switching is handled in generator.
AsyncGeneratorResume(JSContext * cx,Handle<AsyncGeneratorObject * > asyncGenObj,CompletionKind completionKind,HandleValue argument)376 [[nodiscard]] bool js::AsyncGeneratorResume(
377     JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
378     CompletionKind completionKind, HandleValue argument) {
379   MOZ_ASSERT(!asyncGenObj->isClosed(),
380              "closed generator when resuming async generator");
381   MOZ_ASSERT(asyncGenObj->isSuspended(),
382              "non-suspended generator when resuming async generator");
383   MOZ_ASSERT(asyncGenObj->isExecuting(),
384              "async generator not set into 'executing' state");
385 
386   // 25.5.3.5, steps 12-14, 16-20.
387   HandlePropertyName funName = completionKind == CompletionKind::Normal
388                                    ? cx->names().AsyncGeneratorNext
389                                : completionKind == CompletionKind::Throw
390                                    ? cx->names().AsyncGeneratorThrow
391                                    : cx->names().AsyncGeneratorReturn;
392   FixedInvokeArgs<1> args(cx);
393   args[0].set(argument);
394   RootedValue thisOrRval(cx, ObjectValue(*asyncGenObj));
395   if (!CallSelfHostedFunction(cx, funName, thisOrRval, args, &thisOrRval)) {
396     // 25.5.3.2, steps 5.f, 5.g.
397     if (!asyncGenObj->isClosed()) {
398       asyncGenObj->setClosed();
399     }
400     return AsyncGeneratorThrown(cx, asyncGenObj);
401   }
402 
403   // 6.2.3.1, steps 2-9.
404   if (asyncGenObj->isAfterAwait()) {
405     return AsyncGeneratorAwait(cx, asyncGenObj, thisOrRval);
406   }
407 
408   // 25.5.3.7, steps 5-6, 9.
409   if (asyncGenObj->isAfterYield()) {
410     return AsyncGeneratorYield(cx, asyncGenObj, thisOrRval);
411   }
412 
413   // 25.5.3.2, steps 5.d-g.
414   return AsyncGeneratorReturned(cx, asyncGenObj, thisOrRval);
415 }
416 
417 static const JSFunctionSpec async_iterator_proto_methods[] = {
418     JS_SELF_HOSTED_SYM_FN(asyncIterator, "AsyncIteratorIdentity", 0, 0),
419     JS_FS_END};
420 
421 static const JSFunctionSpec async_iterator_proto_methods_with_helpers[] = {
422     JS_SELF_HOSTED_FN("map", "AsyncIteratorMap", 1, 0),
423     JS_SELF_HOSTED_FN("filter", "AsyncIteratorFilter", 1, 0),
424     JS_SELF_HOSTED_FN("take", "AsyncIteratorTake", 1, 0),
425     JS_SELF_HOSTED_FN("drop", "AsyncIteratorDrop", 1, 0),
426     JS_SELF_HOSTED_FN("asIndexedPairs", "AsyncIteratorAsIndexedPairs", 0, 0),
427     JS_SELF_HOSTED_FN("flatMap", "AsyncIteratorFlatMap", 1, 0),
428     JS_SELF_HOSTED_FN("reduce", "AsyncIteratorReduce", 1, 0),
429     JS_SELF_HOSTED_FN("toArray", "AsyncIteratorToArray", 0, 0),
430     JS_SELF_HOSTED_FN("forEach", "AsyncIteratorForEach", 1, 0),
431     JS_SELF_HOSTED_FN("some", "AsyncIteratorSome", 1, 0),
432     JS_SELF_HOSTED_FN("every", "AsyncIteratorEvery", 1, 0),
433     JS_SELF_HOSTED_FN("find", "AsyncIteratorFind", 1, 0),
434     JS_SELF_HOSTED_SYM_FN(asyncIterator, "AsyncIteratorIdentity", 0, 0),
435     JS_FS_END};
436 
437 static const JSFunctionSpec async_from_sync_iter_methods[] = {
438     JS_FN("next", AsyncFromSyncIteratorNext, 1, 0),
439     JS_FN("throw", AsyncFromSyncIteratorThrow, 1, 0),
440     JS_FN("return", AsyncFromSyncIteratorReturn, 1, 0), JS_FS_END};
441 
442 static const JSFunctionSpec async_generator_methods[] = {
443     JS_FN("next", js::AsyncGeneratorNext, 1, 0),
444     JS_FN("throw", js::AsyncGeneratorThrow, 1, 0),
445     JS_FN("return", js::AsyncGeneratorReturn, 1, 0), JS_FS_END};
446 
initAsyncIteratorProto(JSContext * cx,Handle<GlobalObject * > global)447 bool GlobalObject::initAsyncIteratorProto(JSContext* cx,
448                                           Handle<GlobalObject*> global) {
449   if (global->getReservedSlot(ASYNC_ITERATOR_PROTO).isObject()) {
450     return true;
451   }
452 
453   // 25.1.3 The %AsyncIteratorPrototype% Object
454   RootedObject asyncIterProto(
455       cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
456   if (!asyncIterProto) {
457     return false;
458   }
459   if (!DefinePropertiesAndFunctions(cx, asyncIterProto, nullptr,
460                                     async_iterator_proto_methods)) {
461     return false;
462   }
463 
464   global->setReservedSlot(ASYNC_ITERATOR_PROTO, ObjectValue(*asyncIterProto));
465   return true;
466 }
467 
initAsyncFromSyncIteratorProto(JSContext * cx,Handle<GlobalObject * > global)468 bool GlobalObject::initAsyncFromSyncIteratorProto(
469     JSContext* cx, Handle<GlobalObject*> global) {
470   if (global->getReservedSlot(ASYNC_FROM_SYNC_ITERATOR_PROTO).isObject()) {
471     return true;
472   }
473 
474   RootedObject asyncIterProto(
475       cx, GlobalObject::getOrCreateAsyncIteratorPrototype(cx, global));
476   if (!asyncIterProto) {
477     return false;
478   }
479 
480   // 25.1.4.2 The %AsyncFromSyncIteratorPrototype% Object
481   RootedObject asyncFromSyncIterProto(
482       cx, GlobalObject::createBlankPrototypeInheriting(cx, &PlainObject::class_,
483                                                        asyncIterProto));
484   if (!asyncFromSyncIterProto) {
485     return false;
486   }
487   if (!DefinePropertiesAndFunctions(cx, asyncFromSyncIterProto, nullptr,
488                                     async_from_sync_iter_methods) ||
489       !DefineToStringTag(cx, asyncFromSyncIterProto,
490                          cx->names().AsyncFromSyncIterator)) {
491     return false;
492   }
493 
494   global->setReservedSlot(ASYNC_FROM_SYNC_ITERATOR_PROTO,
495                           ObjectValue(*asyncFromSyncIterProto));
496   return true;
497 }
498 
CreateAsyncGeneratorFunction(JSContext * cx,JSProtoKey key)499 static JSObject* CreateAsyncGeneratorFunction(JSContext* cx, JSProtoKey key) {
500   RootedObject proto(
501       cx, GlobalObject::getOrCreateFunctionConstructor(cx, cx->global()));
502   if (!proto) {
503     return nullptr;
504   }
505   HandlePropertyName name = cx->names().AsyncGeneratorFunction;
506 
507   // 25.3.1 The AsyncGeneratorFunction Constructor
508   return NewFunctionWithProto(cx, AsyncGeneratorConstructor, 1,
509                               FunctionFlags::NATIVE_CTOR, nullptr, name, proto,
510                               gc::AllocKind::FUNCTION, TenuredObject);
511 }
512 
CreateAsyncGeneratorFunctionPrototype(JSContext * cx,JSProtoKey key)513 static JSObject* CreateAsyncGeneratorFunctionPrototype(JSContext* cx,
514                                                        JSProtoKey key) {
515   return NewTenuredObjectWithFunctionPrototype(cx, cx->global());
516 }
517 
AsyncGeneratorFunctionClassFinish(JSContext * cx,HandleObject asyncGenFunction,HandleObject asyncGenerator)518 static bool AsyncGeneratorFunctionClassFinish(JSContext* cx,
519                                               HandleObject asyncGenFunction,
520                                               HandleObject asyncGenerator) {
521   Handle<GlobalObject*> global = cx->global();
522 
523   // Change the "constructor" property to non-writable before adding any other
524   // properties, so it's still the last property and can be modified without a
525   // dictionary-mode transition.
526   MOZ_ASSERT(asyncGenerator->as<NativeObject>().getLastProperty().key() ==
527              NameToId(cx->names().constructor));
528   MOZ_ASSERT(!asyncGenerator->as<NativeObject>().inDictionaryMode());
529 
530   RootedValue asyncGenFunctionVal(cx, ObjectValue(*asyncGenFunction));
531   if (!DefineDataProperty(cx, asyncGenerator, cx->names().constructor,
532                           asyncGenFunctionVal, JSPROP_READONLY)) {
533     return false;
534   }
535   MOZ_ASSERT(!asyncGenerator->as<NativeObject>().inDictionaryMode());
536 
537   RootedObject asyncIterProto(
538       cx, GlobalObject::getOrCreateAsyncIteratorPrototype(cx, global));
539   if (!asyncIterProto) {
540     return false;
541   }
542 
543   // 25.5 AsyncGenerator Objects
544   RootedObject asyncGenProto(cx, GlobalObject::createBlankPrototypeInheriting(
545                                      cx, &PlainObject::class_, asyncIterProto));
546   if (!asyncGenProto) {
547     return false;
548   }
549   if (!DefinePropertiesAndFunctions(cx, asyncGenProto, nullptr,
550                                     async_generator_methods) ||
551       !DefineToStringTag(cx, asyncGenProto, cx->names().AsyncGenerator)) {
552     return false;
553   }
554 
555   // 25.3.3 Properties of the AsyncGeneratorFunction Prototype Object
556   if (!LinkConstructorAndPrototype(cx, asyncGenerator, asyncGenProto,
557                                    JSPROP_READONLY, JSPROP_READONLY) ||
558       !DefineToStringTag(cx, asyncGenerator,
559                          cx->names().AsyncGeneratorFunction)) {
560     return false;
561   }
562 
563   global->setAsyncGeneratorPrototype(asyncGenProto);
564 
565   return true;
566 }
567 
568 static const ClassSpec AsyncGeneratorFunctionClassSpec = {
569     CreateAsyncGeneratorFunction,
570     CreateAsyncGeneratorFunctionPrototype,
571     nullptr,
572     nullptr,
573     nullptr,
574     nullptr,
575     AsyncGeneratorFunctionClassFinish,
576     ClassSpec::DontDefineConstructor};
577 
578 const JSClass js::AsyncGeneratorFunctionClass = {
579     "AsyncGeneratorFunction", 0, JS_NULL_CLASS_OPS,
580     &AsyncGeneratorFunctionClassSpec};
581 
582 // https://tc39.es/proposal-iterator-helpers/#sec-asynciterator as of revision
583 // 8f10db5.
AsyncIteratorConstructor(JSContext * cx,unsigned argc,Value * vp)584 static bool AsyncIteratorConstructor(JSContext* cx, unsigned argc, Value* vp) {
585   CallArgs args = CallArgsFromVp(argc, vp);
586 
587   // Step 1.
588   if (!ThrowIfNotConstructing(cx, args, js_AsyncIterator_str)) {
589     return false;
590   }
591   // Throw TypeError if NewTarget is the active function object, preventing the
592   // Iterator constructor from being used directly.
593   if (args.callee() == args.newTarget().toObject()) {
594     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
595                               JSMSG_BOGUS_CONSTRUCTOR, js_AsyncIterator_str);
596     return false;
597   }
598 
599   // Step 2.
600   RootedObject proto(cx);
601   if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_AsyncIterator,
602                                           &proto)) {
603     return false;
604   }
605 
606   JSObject* obj = NewObjectWithClassProto<AsyncIteratorObject>(cx, proto);
607   if (!obj) {
608     return false;
609   }
610 
611   args.rval().setObject(*obj);
612   return true;
613 }
614 
615 static const ClassSpec AsyncIteratorObjectClassSpec = {
616     GenericCreateConstructor<AsyncIteratorConstructor, 0,
617                              gc::AllocKind::FUNCTION>,
618     GenericCreatePrototype<AsyncIteratorObject>,
619     nullptr,
620     nullptr,
621     async_iterator_proto_methods_with_helpers,
622     nullptr,
623     nullptr,
624 };
625 
626 const JSClass AsyncIteratorObject::class_ = {
627     js_AsyncIterator_str,
628     JSCLASS_HAS_CACHED_PROTO(JSProto_AsyncIterator),
629     JS_NULL_CLASS_OPS,
630     &AsyncIteratorObjectClassSpec,
631 };
632 
633 const JSClass AsyncIteratorObject::protoClass_ = {
634     "AsyncIterator.prototype",
635     JSCLASS_HAS_CACHED_PROTO(JSProto_AsyncIterator),
636     JS_NULL_CLASS_OPS,
637     &AsyncIteratorObjectClassSpec,
638 };
639 
640 // Iterator Helper proposal
641 static const JSFunctionSpec async_iterator_helper_methods[] = {
642     JS_SELF_HOSTED_FN("next", "AsyncIteratorHelperNext", 1, 0),
643     JS_SELF_HOSTED_FN("return", "AsyncIteratorHelperReturn", 1, 0),
644     JS_SELF_HOSTED_FN("throw", "AsyncIteratorHelperThrow", 1, 0),
645     JS_FS_END,
646 };
647 
648 static const JSClass AsyncIteratorHelperPrototypeClass = {
649     "Async Iterator Helper", 0};
650 
651 const JSClass AsyncIteratorHelperObject::class_ = {
652     "Async Iterator Helper",
653     JSCLASS_HAS_RESERVED_SLOTS(AsyncIteratorHelperObject::SlotCount),
654 };
655 
656 /* static */
getOrCreateAsyncIteratorHelperPrototype(JSContext * cx,Handle<GlobalObject * > global)657 NativeObject* GlobalObject::getOrCreateAsyncIteratorHelperPrototype(
658     JSContext* cx, Handle<GlobalObject*> global) {
659   return MaybeNativeObject(getOrCreateObject(
660       cx, global, ASYNC_ITERATOR_HELPER_PROTO, initAsyncIteratorHelperProto));
661 }
662 
663 /* static */
initAsyncIteratorHelperProto(JSContext * cx,Handle<GlobalObject * > global)664 bool GlobalObject::initAsyncIteratorHelperProto(JSContext* cx,
665                                                 Handle<GlobalObject*> global) {
666   if (global->getReservedSlot(ASYNC_ITERATOR_HELPER_PROTO).isObject()) {
667     return true;
668   }
669 
670   RootedObject asyncIterProto(
671       cx, GlobalObject::getOrCreateAsyncIteratorPrototype(cx, global));
672   if (!asyncIterProto) {
673     return false;
674   }
675 
676   RootedObject asyncIteratorHelperProto(
677       cx, GlobalObject::createBlankPrototypeInheriting(
678               cx, &AsyncIteratorHelperPrototypeClass, asyncIterProto));
679   if (!asyncIteratorHelperProto) {
680     return false;
681   }
682   if (!DefinePropertiesAndFunctions(cx, asyncIteratorHelperProto, nullptr,
683                                     async_iterator_helper_methods)) {
684     return false;
685   }
686 
687   global->setReservedSlot(ASYNC_ITERATOR_HELPER_PROTO,
688                           ObjectValue(*asyncIteratorHelperProto));
689   return true;
690 }
691 
NewAsyncIteratorHelper(JSContext * cx)692 AsyncIteratorHelperObject* js::NewAsyncIteratorHelper(JSContext* cx) {
693   RootedObject proto(cx, GlobalObject::getOrCreateAsyncIteratorHelperPrototype(
694                              cx, cx->global()));
695   if (!proto) {
696     return nullptr;
697   }
698   return NewObjectWithGivenProto<AsyncIteratorHelperObject>(cx, proto);
699 }
700