From 09e35a85a229c0f169b94137262c541c3e522fc9 Mon Sep 17 00:00:00 2001 From: raskad <32105367+raskad@users.noreply.github.com> Date: Fri, 16 Sep 2022 23:21:02 +0000 Subject: [PATCH] Implement Async-from-Sync Iterator Objects (#2234) This Pull Request changes the following: - Implement [Async-from-Sync Iterator Objects](https://tc39.es/ecma262/#sec-async-from-sync-iterator-objects) - Give the proper `async` hint to `GetIterator` when executing a delegate yield expression in an async generator function --- .../iterable/async_from_sync_iterator.rs | 440 ++++++++++++++++++ boa_engine/src/builtins/iterable/mod.rs | 25 +- boa_engine/src/bytecompiler/mod.rs | 6 +- boa_engine/src/object/mod.rs | 36 ++ boa_engine/src/vm/code_block.rs | 1 + boa_engine/src/vm/mod.rs | 9 +- boa_engine/src/vm/opcode.rs | 9 + 7 files changed, 520 insertions(+), 6 deletions(-) create mode 100644 boa_engine/src/builtins/iterable/async_from_sync_iterator.rs diff --git a/boa_engine/src/builtins/iterable/async_from_sync_iterator.rs b/boa_engine/src/builtins/iterable/async_from_sync_iterator.rs new file mode 100644 index 00000000000..9f1c3b1702a --- /dev/null +++ b/boa_engine/src/builtins/iterable/async_from_sync_iterator.rs @@ -0,0 +1,440 @@ +use crate::{ + builtins::{ + iterable::{create_iter_result_object, IteratorRecord, IteratorResult}, + promise::{if_abrupt_reject_promise, PromiseCapability}, + JsArgs, Promise, + }, + object::{FunctionBuilder, JsObject, ObjectData}, + property::PropertyDescriptor, + Context, JsResult, JsValue, +}; +use boa_gc::{Finalize, Trace}; +use boa_profiler::Profiler; + +/// Create the `%AsyncFromSyncIteratorPrototype%` object. +/// +/// More information: +/// - [ECMA reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-%asyncfromsynciteratorprototype%-object +pub(crate) fn create_async_from_sync_iterator_prototype(context: &mut Context) -> JsObject { + let _timer = Profiler::global().start_event("AsyncFromSyncIteratorPrototype", "init"); + + let prototype = JsObject::from_proto_and_data( + context + .intrinsics() + .objects() + .iterator_prototypes() + .async_iterator_prototype(), + ObjectData::ordinary(), + ); + + let next_function = FunctionBuilder::native(context, AsyncFromSyncIterator::next) + .name("next") + .length(1) + .build(); + let return_function = FunctionBuilder::native(context, AsyncFromSyncIterator::r#return) + .name("return") + .length(1) + .build(); + let throw_function = FunctionBuilder::native(context, AsyncFromSyncIterator::throw) + .name("throw") + .length(1) + .build(); + + { + let mut prototype_mut = prototype.borrow_mut(); + + prototype_mut.insert( + "next", + PropertyDescriptor::builder() + .value(next_function) + .writable(true) + .enumerable(false) + .configurable(true), + ); + prototype_mut.insert( + "return", + PropertyDescriptor::builder() + .value(return_function) + .writable(true) + .enumerable(false) + .configurable(true), + ); + prototype_mut.insert( + "throw", + PropertyDescriptor::builder() + .value(throw_function) + .writable(true) + .enumerable(false) + .configurable(true), + ); + } + + prototype +} + +/// The internal data for `%AsyncFromSyncIterator%` objects. +/// +/// More information: +/// - [ECMA reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-properties-of-async-from-sync-iterator-instances +#[derive(Clone, Debug, Finalize, Trace)] +pub struct AsyncFromSyncIterator { + // The [[SyncIteratorRecord]] internal slot. + sync_iterator_record: IteratorRecord, +} + +impl AsyncFromSyncIterator { + /// `CreateAsyncFromSyncIterator ( syncIteratorRecord )` + /// + /// More information: + /// - [ECMA reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-createasyncfromsynciterator + pub(crate) fn create( + sync_iterator_record: IteratorRecord, + context: &mut Context, + ) -> IteratorRecord { + // 1. Let asyncIterator be OrdinaryObjectCreate(%AsyncFromSyncIteratorPrototype%, « [[SyncIteratorRecord]] »). + // 2. Set asyncIterator.[[SyncIteratorRecord]] to syncIteratorRecord. + let async_iterator = JsObject::from_proto_and_data( + context + .intrinsics() + .objects() + .iterator_prototypes() + .async_from_sync_iterator_prototype(), + ObjectData::async_from_sync_iterator(Self { + sync_iterator_record, + }), + ); + + // 3. Let nextMethod be ! Get(asyncIterator, "next"). + let next_method = async_iterator + .get("next", context) + .expect("async from sync iterator prototype must have next method"); + + // 4. Let iteratorRecord be the Iterator Record { [[Iterator]]: asyncIterator, [[NextMethod]]: nextMethod, [[Done]]: false }. + // 5. Return iteratorRecord. + IteratorRecord::new(async_iterator, next_method, false) + } + + /// `%AsyncFromSyncIteratorPrototype%.next ( [ value ] )` + /// + /// More information: + /// - [ECMA reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-%asyncfromsynciteratorprototype%.next + fn next(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + // 1. Let O be the this value. + // 2. Assert: O is an Object that has a [[SyncIteratorRecord]] internal slot. + // 4. Let syncIteratorRecord be O.[[SyncIteratorRecord]]. + let sync_iterator_record = this + .as_object() + .expect("async from sync iterator prototype must be object") + .borrow() + .as_async_from_sync_iterator() + .expect("async from sync iterator prototype must be object") + .sync_iterator_record + .clone(); + + // 3. Let promiseCapability be ! NewPromiseCapability(%Promise%). + let promise_capability = PromiseCapability::new( + &context + .intrinsics() + .constructors() + .promise() + .constructor() + .into(), + context, + ) + .expect("cannot fail with promise constructor"); + + // 5. If value is present, then + // a. Let result be Completion(IteratorNext(syncIteratorRecord, value)). + // 6. Else, + // a. Let result be Completion(IteratorNext(syncIteratorRecord)). + let result = sync_iterator_record.next(args.get(0).cloned(), context); + + // 7. IfAbruptRejectPromise(result, promiseCapability). + if_abrupt_reject_promise!(result, promise_capability, context); + + // 8. Return AsyncFromSyncIteratorContinuation(result, promiseCapability). + Self::continuation(&result, &promise_capability, context) + } + + /// `%AsyncFromSyncIteratorPrototype%.return ( [ value ] )` + /// + /// More information: + /// - [ECMA reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-%asyncfromsynciteratorprototype%.return + fn r#return(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + // 1. Let O be the this value. + // 2. Assert: O is an Object that has a [[SyncIteratorRecord]] internal slot. + // 4. Let syncIterator be O.[[SyncIteratorRecord]].[[Iterator]]. + let sync_iterator = this + .as_object() + .expect("async from sync iterator prototype must be object") + .borrow() + .as_async_from_sync_iterator() + .expect("async from sync iterator prototype must be object") + .sync_iterator_record + .iterator() + .clone(); + + // 3. Let promiseCapability be ! NewPromiseCapability(%Promise%). + let promise_capability = PromiseCapability::new( + &context + .intrinsics() + .constructors() + .promise() + .constructor() + .into(), + context, + ) + .expect("cannot fail with promise constructor"); + + // 5. Let return be Completion(GetMethod(syncIterator, "return")). + let r#return = sync_iterator.get_method("return", context); + + // 6. IfAbruptRejectPromise(return, promiseCapability). + if_abrupt_reject_promise!(r#return, promise_capability, context); + + let result = match (r#return, args.get(0)) { + // 7. If return is undefined, then + (None, _) => { + // a. Let iterResult be CreateIterResultObject(value, true). + let iter_result = + create_iter_result_object(args.get_or_undefined(0).clone(), true, context); + + // b. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iterResult »). + promise_capability + .resolve() + .call(&JsValue::Undefined, &[iter_result], context) + .expect("cannot fail according to spec"); + + // c. Return promiseCapability.[[Promise]]. + return Ok(promise_capability.promise().clone().into()); + } + // 8. If value is present, then + (Some(r#return), Some(value)) => { + // a. Let result be Completion(Call(return, syncIterator, « value »)). + r#return.call(&sync_iterator.clone().into(), &[value.clone()], context) + } + // 9. Else, + (Some(r#return), None) => { + // a. Let result be Completion(Call(return, syncIterator)). + r#return.call(&sync_iterator.clone().into(), &[], context) + } + }; + + // 10. IfAbruptRejectPromise(result, promiseCapability). + if_abrupt_reject_promise!(result, promise_capability, context); + + // 11. If Type(result) is not Object, then + let result = + if let Some(result) = result.as_object() { + result + } else { + // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »). + promise_capability + .reject() + .call( + &JsValue::Undefined, + &[context + .construct_type_error("iterator return function returned non-object")], + context, + ) + .expect("cannot fail according to spec"); + + // b. Return promiseCapability.[[Promise]]. + return Ok(promise_capability.promise().clone().into()); + }; + + // 12. Return AsyncFromSyncIteratorContinuation(result, promiseCapability). + Self::continuation( + &IteratorResult { + object: result.clone(), + }, + &promise_capability, + context, + ) + } + + /// `%AsyncFromSyncIteratorPrototype%.throw ( [ value ] )` + /// + /// More information: + /// - [ECMA reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-%asyncfromsynciteratorprototype%.throw + fn throw(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + // 1. Let O be the this value. + // 2. Assert: O is an Object that has a [[SyncIteratorRecord]] internal slot. + // 4. Let syncIterator be O.[[SyncIteratorRecord]].[[Iterator]]. + let sync_iterator = this + .as_object() + .expect("async from sync iterator prototype must be object") + .borrow() + .as_async_from_sync_iterator() + .expect("async from sync iterator prototype must be object") + .sync_iterator_record + .iterator() + .clone(); + + // 3. Let promiseCapability be ! NewPromiseCapability(%Promise%). + let promise_capability = PromiseCapability::new( + &context + .intrinsics() + .constructors() + .promise() + .constructor() + .into(), + context, + ) + .expect("cannot fail with promise constructor"); + + // 5. Let throw be Completion(GetMethod(syncIterator, "throw")). + let throw = sync_iterator.get_method("throw", context); + + // 6. IfAbruptRejectPromise(throw, promiseCapability). + if_abrupt_reject_promise!(throw, promise_capability, context); + + let result = match (throw, args.get(0)) { + // 7. If throw is undefined, then + (None, _) => { + // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « value »). + promise_capability + .reject() + .call( + &JsValue::Undefined, + &[args.get_or_undefined(0).clone()], + context, + ) + .expect("cannot fail according to spec"); + + // b. Return promiseCapability.[[Promise]]. + return Ok(promise_capability.promise().clone().into()); + } + // 8. If value is present, then + (Some(throw), Some(value)) => { + // a. Let result be Completion(Call(throw, syncIterator, « value »)). + throw.call(&sync_iterator.clone().into(), &[value.clone()], context) + } + // 9. Else, + (Some(throw), None) => { + // a. Let result be Completion(Call(throw, syncIterator)). + throw.call(&sync_iterator.clone().into(), &[], context) + } + }; + + // 10. IfAbruptRejectPromise(result, promiseCapability). + if_abrupt_reject_promise!(result, promise_capability, context); + + // 11. If Type(result) is not Object, then + let result = if let Some(result) = result.as_object() { + result + } else { + // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »). + promise_capability + .reject() + .call( + &JsValue::Undefined, + &[context.construct_type_error("iterator throw function returned non-object")], + context, + ) + .expect("cannot fail according to spec"); + + // b. Return promiseCapability.[[Promise]]. + return Ok(promise_capability.promise().clone().into()); + }; + + // 12. Return AsyncFromSyncIteratorContinuation(result, promiseCapability). + Self::continuation( + &IteratorResult { + object: result.clone(), + }, + &promise_capability, + context, + ) + } + + /// `AsyncFromSyncIteratorContinuation ( result, promiseCapability )` + /// + /// More information: + /// - [ECMA reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-asyncfromsynciteratorcontinuation + fn continuation( + result: &IteratorResult, + promise_capability: &PromiseCapability, + context: &mut Context, + ) -> JsResult { + // 1. NOTE: Because promiseCapability is derived from the intrinsic %Promise%, + // the calls to promiseCapability.[[Reject]] entailed by the + // use IfAbruptRejectPromise below are guaranteed not to throw. + + // 2. Let done be Completion(IteratorComplete(result)). + let done = result.complete(context); + + // 3. IfAbruptRejectPromise(done, promiseCapability). + if_abrupt_reject_promise!(done, promise_capability, context); + + // 4. Let value be Completion(IteratorValue(result)). + let value = result.value(context); + + // 5. IfAbruptRejectPromise(value, promiseCapability). + if_abrupt_reject_promise!(value, promise_capability, context); + + // 6. Let valueWrapper be Completion(PromiseResolve(%Promise%, value)). + let value_wrapper = Promise::promise_resolve( + context.intrinsics().constructors().promise().constructor(), + value, + context, + ); + + // 7. IfAbruptRejectPromise(valueWrapper, promiseCapability). + if_abrupt_reject_promise!(value_wrapper, promise_capability, context); + + // 8. Let unwrap be a new Abstract Closure with parameters (value) + // that captures done and performs the following steps when called: + // 9. Let onFulfilled be CreateBuiltinFunction(unwrap, 1, "", « »). + let on_fulfilled = FunctionBuilder::closure_with_captures( + context, + |_this, args, done, context| { + // a. Return CreateIterResultObject(value, done). + Ok(create_iter_result_object( + args.get_or_undefined(0).clone(), + *done, + context, + )) + }, + done, + ) + .name("") + .length(1) + .build(); + + // 10. NOTE: onFulfilled is used when processing the "value" property of an + // IteratorResult object in order to wait for its value if it is a promise and + // re-package the result in a new "unwrapped" IteratorResult object. + + // 11. Perform PerformPromiseThen(valueWrapper, onFulfilled, undefined, promiseCapability). + value_wrapper + .as_object() + .expect("result of promise resolve must be promise") + .borrow_mut() + .as_promise_mut() + .expect("constructed promise must be a promise") + .perform_promise_then( + &on_fulfilled.into(), + &JsValue::Undefined, + Some(promise_capability.clone()), + context, + ); + + // 12. Return promiseCapability.[[Promise]]. + Ok(promise_capability.promise().clone().into()) + } +} diff --git a/boa_engine/src/builtins/iterable/mod.rs b/boa_engine/src/builtins/iterable/mod.rs index 78bbfc97f49..e65f02c9be7 100644 --- a/boa_engine/src/builtins/iterable/mod.rs +++ b/boa_engine/src/builtins/iterable/mod.rs @@ -1,3 +1,5 @@ +mod async_from_sync_iterator; + use crate::{ builtins::{ regexp::regexp_string_iterator::RegExpStringIterator, @@ -8,14 +10,20 @@ use crate::{ symbol::WellKnownSymbols, Context, JsResult, JsValue, }; +use async_from_sync_iterator::create_async_from_sync_iterator_prototype; +use boa_gc::{Finalize, Trace}; use boa_profiler::Profiler; +pub(crate) use async_from_sync_iterator::AsyncFromSyncIterator; + #[derive(Debug, Default)] pub struct IteratorPrototypes { /// %IteratorPrototype% iterator_prototype: JsObject, /// %AsyncIteratorPrototype% async_iterator_prototype: JsObject, + /// %AsyncFromSyncIteratorPrototype% + async_from_sync_iterator_prototype: JsObject, /// %MapIteratorPrototype% array_iterator: JsObject, /// %SetIteratorPrototype% @@ -36,6 +44,7 @@ impl IteratorPrototypes { let iterator_prototype = create_iterator_prototype(context); let async_iterator_prototype = create_async_iterator_prototype(context); + let async_from_sync_iterator_prototype = create_async_from_sync_iterator_prototype(context); Self { array_iterator: ArrayIterator::create_prototype(iterator_prototype.clone(), context), set_iterator: SetIterator::create_prototype(iterator_prototype.clone(), context), @@ -48,6 +57,7 @@ impl IteratorPrototypes { for_in_iterator: ForInIterator::create_prototype(iterator_prototype.clone(), context), iterator_prototype, async_iterator_prototype, + async_from_sync_iterator_prototype, } } @@ -66,6 +76,11 @@ impl IteratorPrototypes { self.async_iterator_prototype.clone() } + #[inline] + pub fn async_from_sync_iterator_prototype(&self) -> JsObject { + self.async_from_sync_iterator_prototype.clone() + } + #[inline] pub fn set_iterator(&self) -> JsObject { self.set_iterator.clone() @@ -154,11 +169,13 @@ impl JsValue { let sync_method = self .get_method(WellKnownSymbols::iterator(), context)? .map_or(Self::Undefined, Self::from); + // 2. Let syncIteratorRecord be ? GetIterator(obj, sync, syncMethod). - let _sync_iterator_record = - self.get_iterator(context, Some(IteratorHint::Sync), Some(sync_method)); + let sync_iterator_record = + self.get_iterator(context, Some(IteratorHint::Sync), Some(sync_method))?; + // 3. Return ! CreateAsyncFromSyncIterator(syncIteratorRecord). - todo!("CreateAsyncFromSyncIterator"); + return Ok(AsyncFromSyncIterator::create(sync_iterator_record, context)); } } else { // b. Otherwise, set method to ? GetMethod(obj, @@iterator). @@ -257,7 +274,7 @@ impl IteratorResult { /// - [ECMA reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-iterator-records -#[derive(Debug)] +#[derive(Clone, Debug, Finalize, Trace)] pub struct IteratorRecord { /// `[[Iterator]]` /// diff --git a/boa_engine/src/bytecompiler/mod.rs b/boa_engine/src/bytecompiler/mod.rs index 12884706b6e..b7be4ac51b2 100644 --- a/boa_engine/src/bytecompiler/mod.rs +++ b/boa_engine/src/bytecompiler/mod.rs @@ -1153,7 +1153,11 @@ impl<'b> ByteCompiler<'b> { } if r#yield.delegate() { - self.emit_opcode(Opcode::InitIterator); + if self.in_async_generator { + self.emit_opcode(Opcode::InitIteratorAsync); + } else { + self.emit_opcode(Opcode::InitIterator); + } self.emit_opcode(Opcode::PushUndefined); let start_address = self.next_opcode_location(); let start = self.emit_opcode_with_operand(Opcode::GeneratorNextDelegate); diff --git a/boa_engine/src/object/mod.rs b/boa_engine/src/object/mod.rs index 082d9264b4d..1e8087a7251 100644 --- a/boa_engine/src/object/mod.rs +++ b/boa_engine/src/object/mod.rs @@ -33,6 +33,7 @@ use crate::{ NativeFunctionSignature, }, generator::Generator, + iterable::AsyncFromSyncIterator, map::map_iterator::MapIterator, map::ordered_map::OrderedMap, object::for_in_iterator::ForInIterator, @@ -165,6 +166,7 @@ pub struct ObjectData { /// Defines the different types of objects. #[derive(Debug, Finalize)] pub enum ObjectKind { + AsyncFromSyncIterator(AsyncFromSyncIterator), AsyncGenerator(AsyncGenerator), AsyncGeneratorFunction(Function), Array, @@ -204,6 +206,7 @@ pub enum ObjectKind { unsafe impl Trace for ObjectKind { custom_trace! {this, { match this { + Self::AsyncFromSyncIterator(a) => mark(a), Self::ArrayIterator(i) => mark(i), Self::ArrayBuffer(b) => mark(b), Self::Map(m) => mark(m), @@ -241,6 +244,14 @@ unsafe impl Trace for ObjectKind { } impl ObjectData { + /// Create the `AsyncFromSyncIterator` object data + pub fn async_from_sync_iterator(async_from_sync_iterator: AsyncFromSyncIterator) -> Self { + Self { + kind: ObjectKind::AsyncFromSyncIterator(async_from_sync_iterator), + internal_methods: &ORDINARY_INTERNAL_METHODS, + } + } + /// Create the `AsyncGenerator` object data pub fn async_generator(async_generator: AsyncGenerator) -> Self { Self { @@ -536,6 +547,7 @@ impl ObjectData { impl Display for ObjectKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(match self { + Self::AsyncFromSyncIterator(_) => "AsyncFromSyncIterator", Self::AsyncGenerator(_) => "AsyncGenerator", Self::AsyncGeneratorFunction(_) => "AsyncGeneratorFunction", Self::Array => "Array", @@ -603,6 +615,30 @@ impl Object { &self.data.kind } + /// Checks if it's an `AsyncFromSyncIterator` object. + #[inline] + pub fn is_async_from_sync_iterator(&self) -> bool { + matches!( + self.data, + ObjectData { + kind: ObjectKind::AsyncFromSyncIterator(_), + .. + } + ) + } + + /// Returns a reference to the `AsyncFromSyncIterator` data on the object. + #[inline] + pub fn as_async_from_sync_iterator(&self) -> Option<&AsyncFromSyncIterator> { + match self.data { + ObjectData { + kind: ObjectKind::AsyncFromSyncIterator(ref async_from_sync_iterator), + .. + } => Some(async_from_sync_iterator), + _ => None, + } + } + /// Checks if it's an `AsyncGenerator` object. #[inline] pub fn is_async_generator(&self) -> bool { diff --git a/boa_engine/src/vm/code_block.rs b/boa_engine/src/vm/code_block.rs index efe70b5fbaf..208ee747ea4 100644 --- a/boa_engine/src/vm/code_block.rs +++ b/boa_engine/src/vm/code_block.rs @@ -350,6 +350,7 @@ impl CodeBlock { | Opcode::LoopContinue | Opcode::LoopEnd | Opcode::InitIterator + | Opcode::InitIteratorAsync | Opcode::IteratorNext | Opcode::IteratorClose | Opcode::IteratorToArray diff --git a/boa_engine/src/vm/mod.rs b/boa_engine/src/vm/mod.rs index 3685570651e..2b036924f5b 100644 --- a/boa_engine/src/vm/mod.rs +++ b/boa_engine/src/vm/mod.rs @@ -6,7 +6,7 @@ use crate::{ builtins::{ async_generator::{AsyncGenerator, AsyncGeneratorState}, function::{ConstructorKind, Function}, - iterable::IteratorRecord, + iterable::{IteratorHint, IteratorRecord}, Array, ForInIterator, JsArgs, Number, Promise, }, environments::EnvironmentSlots, @@ -2028,6 +2028,13 @@ impl Context { self.vm.push(iterator.next_method().clone()); self.vm.push(iterator.done()); } + Opcode::InitIteratorAsync => { + let object = self.vm.pop(); + let iterator = object.get_iterator(self, Some(IteratorHint::Async), None)?; + self.vm.push(iterator.iterator().clone()); + self.vm.push(iterator.next_method().clone()); + self.vm.push(iterator.done()); + } Opcode::IteratorNext => { let done = self .vm diff --git a/boa_engine/src/vm/opcode.rs b/boa_engine/src/vm/opcode.rs index 1e50fd42406..8d83986d545 100644 --- a/boa_engine/src/vm/opcode.rs +++ b/boa_engine/src/vm/opcode.rs @@ -1048,6 +1048,13 @@ pub enum Opcode { /// Stack: object **=>** iterator, next_method, done InitIterator, + /// Initialize an async iterator. + /// + /// Operands: + /// + /// Stack: object **=>** iterator, next_method, done + InitIteratorAsync, + /// Advance the iterator by one and put the value on the stack. /// /// Operands: @@ -1325,6 +1332,7 @@ impl Opcode { Self::LoopEnd => "LoopEnd", Self::ForInLoopInitIterator => "ForInLoopInitIterator", Self::InitIterator => "InitIterator", + Self::InitIteratorAsync => "InitIteratorAsync", Self::IteratorNext => "IteratorNext", Self::IteratorClose => "IteratorClose", Self::IteratorToArray => "IteratorToArray", @@ -1468,6 +1476,7 @@ impl Opcode { Self::LoopEnd => "INST - LoopEnd", Self::ForInLoopInitIterator => "INST - ForInLoopInitIterator", Self::InitIterator => "INST - InitIterator", + Self::InitIteratorAsync => "INST - InitIteratorAsync", Self::IteratorNext => "INST - IteratorNext", Self::IteratorClose => "INST - IteratorClose", Self::IteratorToArray => "INST - IteratorToArray",