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