Browse Source

Refactor iterator APIs to be on parity with the latest spec (#3962)

* The Great Refactoring Of Iterators

* Update core/engine/src/builtins/promise/mod.rs

Co-authored-by: raskad <32105367+raskad@users.noreply.github.com>

* Update core/engine/src/builtins/promise/mod.rs

Co-authored-by: raskad <32105367+raskad@users.noreply.github.com>

---------

Co-authored-by: raskad <32105367+raskad@users.noreply.github.com>
pull/3969/head
José Julián Espina 3 months ago committed by GitHub
parent
commit
25705ef5a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 42
      core/engine/src/builtins/array/mod.rs
  2. 15
      core/engine/src/builtins/error/aggregate.rs
  3. 27
      core/engine/src/builtins/intl/list_format/mod.rs
  4. 16
      core/engine/src/builtins/iterable/async_from_sync_iterator.rs
  5. 242
      core/engine/src/builtins/iterable/mod.rs
  6. 107
      core/engine/src/builtins/map/mod.rs
  7. 24
      core/engine/src/builtins/object/mod.rs
  8. 349
      core/engine/src/builtins/promise/mod.rs
  9. 25
      core/engine/src/builtins/set/mod.rs
  10. 28
      core/engine/src/builtins/temporal/mod.rs
  11. 5
      core/engine/src/builtins/typed_array/builtin.rs
  12. 12
      core/engine/src/builtins/typed_array/mod.rs
  13. 11
      core/engine/src/builtins/weak_map/mod.rs
  14. 19
      core/engine/src/builtins/weak_set/mod.rs
  15. 20
      core/engine/src/object/builtins/jsmap.rs
  16. 6
      core/engine/src/object/builtins/jsset.rs
  17. 4
      core/engine/src/vm/opcode/iteration/get.rs
  18. 3
      core/engine/src/vm/opcode/push/array.rs
  19. 2
      test262_config.toml

42
core/engine/src/builtins/array/mod.rs

@ -15,10 +15,7 @@ use boa_profiler::Profiler;
use thin_vec::ThinVec; use thin_vec::ThinVec;
use crate::{ use crate::{
builtins::{ builtins::{iterable::if_abrupt_close_iterator, BuiltInObject, Number},
iterable::{if_abrupt_close_iterator, IteratorHint},
BuiltInObject, Number,
},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
js_string, js_string,
@ -610,44 +607,41 @@ impl Array {
_ => Self::array_create(0, None, context)?, _ => Self::array_create(0, None, context)?,
}; };
// c. Let iteratorRecord be ? GetIterator(items, sync, usingIterator). // c. Let iteratorRecord be ? GetIteratorFromMethod(items, usingIterator).
let mut iterator_record = let mut iterator_record = items.get_iterator_from_method(&using_iterator, context)?;
items.get_iterator(context, Some(IteratorHint::Sync), Some(using_iterator))?;
// d. Let k be 0. // d. Let k be 0.
// e. Repeat, // e. Repeat,
// i. If k ≥ 2^53 - 1 (MAX_SAFE_INTEGER), then // i. If k ≥ 2^53 - 1 (MAX_SAFE_INTEGER), then
// ... // ...
// x. Set k to k + 1. // ix. Set k to k + 1.
for k in 0..9_007_199_254_740_991_u64 { for k in 0..9_007_199_254_740_991_u64 {
// iii. Let next be ? IteratorStep(iteratorRecord). // iii. Let next be ? IteratorStepValue(iteratorRecord).
if iterator_record.step(context)? { let Some(next) = iterator_record.step_value(context)? else {
// 1. Perform ? Set(A, "length", 𝔽(k), true). // iv. If next is done, then
// 1. Perform ? Set(A, "length", 𝔽(k), true).
a.set(StaticJsStrings::LENGTH, k, true, context)?; a.set(StaticJsStrings::LENGTH, k, true, context)?;
// 2. Return A. // 2. Return A.
return Ok(a.into()); return Ok(a.into());
} };
// iv. If next is false, then
// v. Let nextValue be ? IteratorValue(next).
let next_value = iterator_record.value(context)?;
// vi. If mapping is true, then // v. If mapping is true, then
let mapped_value = if let Some(mapfn) = mapping { let mapped_value = if let Some(mapfn) = mapping {
// 1. Let mappedValue be Call(mapfn, thisArg, « nextValue, 𝔽(k) »). // 1. Let mappedValue be Completion(Call(mapper, thisArg, « next, 𝔽(k) »)).
let mapped_value = mapfn.call(this_arg, &[next_value, k.into()], context); let mapped_value = mapfn.call(this_arg, &[next, k.into()], context);
// 2. IfAbruptCloseIterator(mappedValue, iteratorRecord). // 2. IfAbruptCloseIterator(mappedValue, iteratorRecord).
if_abrupt_close_iterator!(mapped_value, iterator_record, context) if_abrupt_close_iterator!(mapped_value, iterator_record, context)
} else { } else {
// vii. Else, let mappedValue be nextValue. // vi. Else,
next_value // 1. Let mappedValue be next.
next
}; };
// viii. Let defineStatus be CreateDataPropertyOrThrow(A, Pk, mappedValue). // vii. Let defineStatus be Completion(CreateDataPropertyOrThrow(A, Pk, mappedValue)).
let define_status = a.create_data_property_or_throw(k, mapped_value, context); let define_status = a.create_data_property_or_throw(k, mapped_value, context);
// ix. IfAbruptCloseIterator(defineStatus, iteratorRecord). // viii. IfAbruptCloseIterator(defineStatus, iteratorRecord).
if_abrupt_close_iterator!(define_status, iterator_record, context); if_abrupt_close_iterator!(define_status, iterator_record, context);
} }

15
core/engine/src/builtins/error/aggregate.rs

@ -9,7 +9,7 @@
use crate::{ use crate::{
builtins::{ builtins::{
iterable::iterable_to_list, Array, BuiltInBuilder, BuiltInConstructor, BuiltInObject, iterable::IteratorHint, Array, BuiltInBuilder, BuiltInConstructor, BuiltInObject,
IntrinsicObject, IntrinsicObject,
}, },
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
@ -56,7 +56,11 @@ impl BuiltInConstructor for AggregateError {
const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor = const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =
StandardConstructors::aggregate_error; StandardConstructors::aggregate_error;
/// Create a new aggregate error object. /// [`AggregateError ( errors, message [ , options ] )`][spec]
///
/// Creates a new aggregate error object.
///
/// [spec]: AggregateError ( errors, message [ , options ] )
fn constructor( fn constructor(
new_target: &JsValue, new_target: &JsValue,
args: &[JsValue], args: &[JsValue],
@ -102,9 +106,12 @@ impl BuiltInConstructor for AggregateError {
// 4. Perform ? InstallErrorCause(O, options). // 4. Perform ? InstallErrorCause(O, options).
Error::install_error_cause(&o, args.get_or_undefined(2), context)?; Error::install_error_cause(&o, args.get_or_undefined(2), context)?;
// 5. Let errorsList be ? IterableToList(errors). // 5. Let errorsList be ? IteratorToList(? GetIterator(errors, sync)).
let errors = args.get_or_undefined(0); let errors = args.get_or_undefined(0);
let errors_list = iterable_to_list(context, errors, None)?; let errors_list = errors
.get_iterator(IteratorHint::Sync, context)?
.into_list(context)?;
// 6. Perform ! DefinePropertyOrThrow(O, "errors", // 6. Perform ! DefinePropertyOrThrow(O, "errors",
// PropertyDescriptor { // PropertyDescriptor {
// [[Configurable]]: true, // [[Configurable]]: true,

27
core/engine/src/builtins/intl/list_format/mod.rs

@ -9,6 +9,7 @@ use icu_provider::DataLocale;
use crate::{ use crate::{
builtins::{ builtins::{
iterable::IteratorHint,
options::{get_option, get_options_object}, options::{get_option, get_options_object},
Array, BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject, OrdinaryObject, Array, BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject, OrdinaryObject,
}, },
@ -486,23 +487,20 @@ fn string_list_from_iterable(iterable: &JsValue, context: &mut Context) -> JsRes
return Ok(Vec::new()); return Ok(Vec::new());
} }
// 2. Let iteratorRecord be ? GetIterator(iterable). // 2. Let iteratorRecord be ? GetIterator(iterable, sync).
let mut iterator = iterable.get_iterator(context, None, None)?; let mut iterator = iterable.get_iterator(IteratorHint::Sync, context)?;
// 3. Let list be a new empty List. // 3. Let list be a new empty List.
let mut list = Vec::new(); let mut list = Vec::new();
// 4. Let next be true. // 4. Let next be true.
// 5. Repeat, while next is not false, // 5. Repeat, while next is not false,
// a. Set next to ? IteratorStep(iteratorRecord). // a. Let next be ? IteratorStepValue(iteratorRecord).
// b. If next is not false, then while let Some(next) = iterator.step_value(context)? {
while !iterator.step(context)? { // c. If next is not a String, then
let item = iterator.value(context)?; let Some(s) = next.as_string().cloned() else {
// i. Let nextValue be ? IteratorValue(next). // i. Let error be ThrowCompletion(a newly created TypeError object).
// ii. If Type(nextValue) is not String, then // ii. Return ? IteratorClose(iteratorRecord, error).
let Some(s) = item.as_string().cloned() else {
// 1. Let error be ThrowCompletion(a newly created TypeError object).
// 2. Return ? IteratorClose(iteratorRecord, error).
return Err(iterator return Err(iterator
.close( .close(
Err(JsNativeError::typ() Err(JsNativeError::typ()
@ -510,13 +508,14 @@ fn string_list_from_iterable(iterable: &JsValue, context: &mut Context) -> JsRes
.into()), .into()),
context, context,
) )
.expect_err("Should return the provided error")); .expect_err("`close` should return the provided error"));
}; };
// iii. Append nextValue to the end of the List list. // d. Append next to list.
list.push(s); list.push(s);
} }
// 6. Return list. // b. If next is done, then
// i. Return list.
Ok(list) Ok(list)
} }

16
core/engine/src/builtins/iterable/async_from_sync_iterator.rs

@ -98,7 +98,7 @@ impl AsyncFromSyncIterator {
// 1. Let O be the this value. // 1. Let O be the this value.
// 2. Assert: O is an Object that has a [[SyncIteratorRecord]] internal slot. // 2. Assert: O is an Object that has a [[SyncIteratorRecord]] internal slot.
// 4. Let syncIteratorRecord be O.[[SyncIteratorRecord]]. // 4. Let syncIteratorRecord be O.[[SyncIteratorRecord]].
let sync_iterator_record = this let mut sync_iterator_record = this
.as_object() .as_object()
.and_then(JsObject::downcast_ref::<Self>) .and_then(JsObject::downcast_ref::<Self>)
.expect("async from sync iterator prototype must be object") .expect("async from sync iterator prototype must be object")
@ -113,18 +113,10 @@ impl AsyncFromSyncIterator {
.expect("cannot fail with promise constructor"); .expect("cannot fail with promise constructor");
// 5. If value is present, then // 5. If value is present, then
// a. Let result be Completion(IteratorNext(syncIteratorRecord, value)). // a. Let result be Completion(IteratorNext(syncIteratorRecord, value)).
// 6. Else, // 6. Else,
// a. Let result be Completion(IteratorNext(syncIteratorRecord)). // a. Let result be Completion(IteratorNext(syncIteratorRecord)).
let iterator = sync_iterator_record.iterator().clone(); let result = sync_iterator_record.next(args.first(), context);
let next = sync_iterator_record.next_method();
let result = next
.call(
&iterator.into(),
args.first().map_or(&[], std::slice::from_ref),
context,
)
.and_then(IteratorResult::from_value);
// 7. IfAbruptRejectPromise(result, promiseCapability). // 7. IfAbruptRejectPromise(result, promiseCapability).
let result = if_abrupt_reject_promise!(result, promise_capability, context); let result = if_abrupt_reject_promise!(result, promise_capability, context);

242
core/engine/src/builtins/iterable/mod.rs

@ -225,7 +225,31 @@ pub enum IteratorHint {
} }
impl JsValue { impl JsValue {
/// `GetIterator ( obj [ , hint [ , method ] ] )` /// `GetIteratorFromMethod ( obj, method )`
///
/// More information:
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-getiteratorfrommethod
pub fn get_iterator_from_method(
&self,
method: &JsObject,
context: &mut Context,
) -> JsResult<IteratorRecord> {
// 1. Let iterator be ? Call(method, obj).
let iterator = method.call(self, &[], context)?;
// 2. If iterator is not an Object, throw a TypeError exception.
let iterator_obj = iterator.as_object().ok_or_else(|| {
JsNativeError::typ().with_message("returned iterator is not an object")
})?;
// 3. Let nextMethod be ? Get(iterator, "next").
let next_method = iterator_obj.get(js_str!("next"), context)?;
// 4. Let iteratorRecord be the Iterator Record { [[Iterator]]: iterator, [[NextMethod]]: nextMethod, [[Done]]: false }.
// 5. Return iteratorRecord.
Ok(IteratorRecord::new(iterator_obj.clone(), next_method))
}
/// `GetIterator ( obj, kind )`
/// ///
/// More information: /// More information:
/// - [ECMA reference][spec] /// - [ECMA reference][spec]
@ -233,60 +257,51 @@ impl JsValue {
/// [spec]: https://tc39.es/ecma262/#sec-getiterator /// [spec]: https://tc39.es/ecma262/#sec-getiterator
pub fn get_iterator( pub fn get_iterator(
&self, &self,
hint: IteratorHint,
context: &mut Context, context: &mut Context,
hint: Option<IteratorHint>,
method: Option<JsObject>,
) -> JsResult<IteratorRecord> { ) -> JsResult<IteratorRecord> {
// 1. If hint is not present, set hint to sync. let method = match hint {
let hint = hint.unwrap_or(IteratorHint::Sync); // 1. If kind is async, then
IteratorHint::Async => {
// 2. If method is not present, then // a. Let method be ? GetMethod(obj, %Symbol.asyncIterator%).
let method = if method.is_some() { let Some(method) = self.get_method(JsSymbol::async_iterator(), context)? else {
method // b. If method is undefined, then
} else { // i. Let syncMethod be ? GetMethod(obj, %Symbol.iterator%).
// a. If hint is async, then let sync_method =
if hint == IteratorHint::Async { self.get_method(JsSymbol::iterator(), context)?
// i. Set method to ? GetMethod(obj, @@asyncIterator). .ok_or_else(|| {
if let Some(method) = self.get_method(JsSymbol::async_iterator(), context)? { // ii. If syncMethod is undefined, throw a TypeError exception.
Some(method) JsNativeError::typ().with_message(format!(
} else { "value with type `{}` is not iterable",
// ii. If method is undefined, then self.type_of()
// 1. Let syncMethod be ? GetMethod(obj, @@iterator). ))
let sync_method = self.get_method(JsSymbol::iterator(), context)?; })?;
// iii. Let syncIteratorRecord be ? GetIteratorFromMethod(obj, syncMethod).
// 2. Let syncIteratorRecord be ? GetIterator(obj, sync, syncMethod).
let sync_iterator_record = let sync_iterator_record =
self.get_iterator(context, Some(IteratorHint::Sync), sync_method)?; self.get_iterator_from_method(&sync_method, context)?;
// iv. Return CreateAsyncFromSyncIterator(syncIteratorRecord).
// 3. Return ! CreateAsyncFromSyncIterator(syncIteratorRecord).
return Ok(AsyncFromSyncIterator::create(sync_iterator_record, context)); return Ok(AsyncFromSyncIterator::create(sync_iterator_record, context));
} };
} else {
// b. Otherwise, set method to ? GetMethod(obj, @@iterator). Some(method)
}
// 2. Else,
IteratorHint::Sync => {
// a. Let method be ? GetMethod(obj, %Symbol.iterator%).
self.get_method(JsSymbol::iterator(), context)? self.get_method(JsSymbol::iterator(), context)?
} }
} };
.ok_or_else(|| {
let method = method.ok_or_else(|| {
// 3. If method is undefined, throw a TypeError exception.
JsNativeError::typ().with_message(format!( JsNativeError::typ().with_message(format!(
"value with type `{}` is not iterable", "value with type `{}` is not iterable",
self.type_of() self.type_of()
)) ))
})?; })?;
// 3. Let iterator be ? Call(method, obj). // 4. Return ? GetIteratorFromMethod(obj, method).
let iterator = method.call(self, &[], context)?; self.get_iterator_from_method(&method, context)
// 4. If Type(iterator) is not Object, throw a TypeError exception.
let iterator_obj = iterator.as_object().ok_or_else(|| {
JsNativeError::typ().with_message("returned iterator is not an object")
})?;
// 5. Let nextMethod be ? GetV(iterator, "next").
let next_method = iterator.get_v(js_str!("next"), context)?;
// 6. Let iteratorRecord be the Record { [[Iterator]]: iterator, [[NextMethod]]: nextMethod, [[Done]]: false }.
// 7. Return iteratorRecord.
Ok(IteratorRecord::new(iterator_obj.clone(), next_method))
} }
} }
@ -461,28 +476,35 @@ impl IteratorRecord {
/// - [ECMA reference][spec] /// - [ECMA reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-iteratornext /// [spec]: https://tc39.es/ecma262/#sec-iteratornext
pub(crate) fn step_with( pub(crate) fn next(
&mut self, &mut self,
value: Option<&JsValue>, value: Option<&JsValue>,
context: &mut Context, context: &mut Context,
) -> JsResult<bool> { ) -> JsResult<IteratorResult> {
let _timer = Profiler::global().start_event("IteratorRecord::step_with", "iterator"); let _timer = Profiler::global().start_event("IteratorRecord::step_with", "iterator");
// 1. If value is not present, then
// a. Let result be Completion(Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]])).
// 2. Else,
// a. Let result be Completion(Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]], « value »)).
// 3. If result is a throw completion, then
// a. Set iteratorRecord.[[Done]] to true.
// b. Return ? result.
// 4. Set result to ! result.
// 5. If result is not an Object, then
// a. Set iteratorRecord.[[Done]] to true.
// b. Throw a TypeError exception.
// 6. Return result.
// NOTE: In this case, `set_done_on_err` does all the heavylifting for us, which
// simplifies the instructions below.
self.set_done_on_err(|iter| { self.set_done_on_err(|iter| {
// 1. If value is not present, then iter.next_method
// a. Let result be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]]). .call(
// 2. Else, &iter.iterator.clone().into(),
// a. Let result be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]], « value »). value.map_or(&[], std::slice::from_ref),
let result = iter.next_method.call( context,
&iter.iterator.clone().into(), )
value.map_or(&[], std::slice::from_ref), .and_then(IteratorResult::from_value)
context,
)?;
iter.update_result(result, context)?;
// 4. Return result.
Ok(iter.done)
}) })
} }
@ -497,7 +519,49 @@ impl IteratorRecord {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-iteratorstep /// [spec]: https://tc39.es/ecma262/#sec-iteratorstep
pub(crate) fn step(&mut self, context: &mut Context) -> JsResult<bool> { pub(crate) fn step(&mut self, context: &mut Context) -> JsResult<bool> {
self.step_with(None, context) self.set_done_on_err(|iter| {
// 1. Let result be ? IteratorNext(iteratorRecord).
let result = iter.next(None, context)?;
// 2. Let done be Completion(IteratorComplete(result)).
// 3. If done is a throw completion, then
// a. Set iteratorRecord.[[Done]] to true.
// b. Return ? done.
// 4. Set done to ! done.
// 5. If done is true, then
// a. Set iteratorRecord.[[Done]] to true.
// b. Return done.
iter.done = result.complete(context)?;
iter.last_result = result;
// 6. Return result.
Ok(iter.done)
})
}
/// `IteratorStepValue ( iteratorRecord )`
///
/// Updates the `IteratorRecord` and returns `Some(value)` if the next result record returned
/// `done: true`, otherwise returns `None`.
///
/// More information:
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-iteratorstepvalue
pub(crate) fn step_value(&mut self, context: &mut Context) -> JsResult<Option<JsValue>> {
// 1. Let result be ? IteratorStep(iteratorRecord).
if self.step(context)? {
// 2. If result is done, then
// a. Return done.
Ok(None)
} else {
// 3. Let value be Completion(IteratorValue(result)).
// 4. If value is a throw completion, then
// a. Set iteratorRecord.[[Done]] to true.
// 5. Return ? value.
self.value(context).map(Some)
}
} }
/// `IteratorClose ( iteratorRecord, completion )` /// `IteratorClose ( iteratorRecord, completion )`
@ -565,40 +629,28 @@ impl IteratorRecord {
.into()) .into())
} }
} }
}
/// `IterableToList ( items [ , method ] )` /// `IteratorToList ( iteratorRecord )`
/// ///
/// More information: /// More information:
/// - [ECMA reference][spec] /// - [ECMA reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-iterabletolist /// [spec]: https://tc39.es/ecma262/#sec-iteratortolist
pub(crate) fn iterable_to_list( pub(crate) fn into_list(mut self, context: &mut Context) -> JsResult<Vec<JsValue>> {
context: &mut Context, let _timer = Profiler::global().start_event("IteratorRecord::to_list", "iterator");
items: &JsValue,
method: Option<JsObject>, // 1. Let values be a new empty List.
) -> JsResult<Vec<JsValue>> { let mut values = Vec::new();
let _timer = Profiler::global().start_event("iterable_to_list", "iterator");
// 2. Repeat,
// 1. If method is present, then // a. Let next be ? IteratorStepValue(iteratorRecord).
// a. Let iteratorRecord be ? GetIterator(items, sync, method). while let Some(value) = self.step_value(context)? {
// 2. Else, // c. Append next to values.
// a. Let iteratorRecord be ? GetIterator(items, sync). values.push(value);
let mut iterator_record = items.get_iterator(context, Some(IteratorHint::Sync), method)?; }
// 3. Let values be a new empty List. // b. If next is done, then
let mut values = Vec::new(); // i. Return values.
Ok(values)
// 4. Let next be true. }
// 5. Repeat, while next is not false,
// a. Set next to ? IteratorStep(iteratorRecord).
// b. If next is not false, then
// i. Let nextValue be ? IteratorValue(next).
// ii. Append nextValue to the end of the List values.
while !iterator_record.step(context)? {
values.push(iterator_record.value(context)?);
}
// 6. Return values.
Ok(values)
} }

107
core/engine/src/builtins/map/mod.rs

@ -11,11 +11,11 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
use crate::{ use crate::{
builtins::BuiltInObject, builtins::{iterable::IteratorHint, BuiltInObject},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
js_string, js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject}, object::{internal_methods::get_prototype_from_constructor, JsFunction, JsObject},
property::{Attribute, PropertyNameKind}, property::{Attribute, PropertyNameKind},
realm::Realm, realm::Realm,
string::StaticJsStrings, string::StaticJsStrings,
@ -26,7 +26,9 @@ use boa_macros::js_str;
use boa_profiler::Profiler; use boa_profiler::Profiler;
use num_traits::Zero; use num_traits::Zero;
use super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject}; use super::{
iterable::if_abrupt_close_iterator, BuiltInBuilder, BuiltInConstructor, IntrinsicObject,
};
mod map_iterator; mod map_iterator;
pub(crate) use map_iterator::MapIterator; pub(crate) use map_iterator::MapIterator;
@ -149,9 +151,16 @@ impl BuiltInConstructor for Map {
}; };
// 5. Let adder be ? Get(map, "set"). // 5. Let adder be ? Get(map, "set").
let adder = map.get(js_str!("set"), context)?; // 6. If IsCallable(adder) is false, throw a TypeError exception.
let adder = map
// 6. Return ? AddEntriesFromIterable(map, iterable, adder). .get(js_str!("set"), context)?
.as_function()
.ok_or_else(|| {
JsNativeError::typ()
.with_message("Map: property `set` on new `Map` must be callable")
})?;
// 7. Return ? AddEntriesFromIterable(map, iterable, adder).
add_entries_from_iterable(&map, iterable, &adder, context) add_entries_from_iterable(&map, iterable, &adder, context)
} }
} }
@ -547,8 +556,8 @@ impl Map {
let mut groups: IndexMap<JsValue, Vec<JsValue>, BuildHasherDefault<FxHasher>> = let mut groups: IndexMap<JsValue, Vec<JsValue>, BuildHasherDefault<FxHasher>> =
IndexMap::default(); IndexMap::default();
// 4. Let iteratorRecord be ? GetIterator(items). // 4. Let iteratorRecord be ? GetIterator(items, sync).
let mut iterator = items.get_iterator(context, None, None)?; let mut iterator = items.get_iterator(IteratorHint::Sync, context)?;
// 5. Let k be 0. // 5. Let k be 0.
let mut k = 0u64; let mut k = 0u64;
@ -566,17 +575,15 @@ impl Map {
return iterator.close(Err(error), context); return iterator.close(Err(error), context);
} }
// b. Let next be ? IteratorStep(iteratorRecord). // b. Let next be ? IteratorStepValue(iteratorRecord).
let done = iterator.step(context)?; let Some(next) = iterator.step_value(context)? else {
// c. If next is false, then
// c. If next is false, then
if done {
// i. Return groups. // i. Return groups.
break; break;
} };
// d. Let value be ? IteratorValue(next). // d. Let value be next.
let value = iterator.value(context)?; let value = next;
// e. Let key be Completion(Call(callbackfn, undefined, « value, 𝔽(k) »)). // e. Let key be Completion(Call(callbackfn, undefined, « value, 𝔽(k) »)).
let key = callback.call(&JsValue::undefined(), &[value.clone(), k.into()], context); let key = callback.call(&JsValue::undefined(), &[value.clone(), k.into()], context);
@ -585,8 +592,8 @@ impl Map {
let mut key = if_abrupt_close_iterator!(key, iterator, context); let mut key = if_abrupt_close_iterator!(key, iterator, context);
// h. Else, // h. Else,
// i. Assert: keyCoercion is zero. // i. Assert: keyCoercion is collection.
// ii. If key is -0𝔽, set key to +0𝔽. // ii. Set key to CanonicalizeKeyedCollectionKey(key).
if key.as_number() == Some(-0.0) { if key.as_number() == Some(-0.0) {
key = 0.into(); key = 0.into();
} }
@ -632,31 +639,20 @@ impl Map {
pub(crate) fn add_entries_from_iterable( pub(crate) fn add_entries_from_iterable(
target: &JsObject, target: &JsObject,
iterable: &JsValue, iterable: &JsValue,
adder: &JsValue, adder: &JsFunction,
context: &mut Context, context: &mut Context,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
// 1. If IsCallable(adder) is false, throw a TypeError exception. // 1. Let iteratorRecord be ? GetIterator(iterable, sync).
let adder = adder.as_callable().ok_or_else(|| { let mut iterator_record = iterable.get_iterator(IteratorHint::Sync, context)?;
JsNativeError::typ().with_message("property `set` of `NewTarget` is not callable")
})?; // 2. Repeat,
// a. Let next be ? IteratorStepValue(iteratorRecord).
// 2. Let iteratorRecord be ? GetIterator(iterable). // b. If next is done, return target.
let mut iterator_record = iterable.get_iterator(context, None, None)?; while let Some(next) = iterator_record.step_value(context)? {
let Some(next) = next.as_object() else {
// 3. Repeat, // c. If next is not an Object, then
loop { // i. Let error be ThrowCompletion(a newly created TypeError object).
// a. Let next be ? IteratorStep(iteratorRecord). // ii. Return ? IteratorClose(iteratorRecord, error).
// b. If next is false, return target.
// c. Let nextItem be ? IteratorValue(next).
if iterator_record.step(context)? {
return Ok(target.clone().into());
};
let next_item = iterator_record.value(context)?;
let Some(next_item) = next_item.as_object() else {
// d. If Type(nextItem) is not Object, then
// i. Let error be ThrowCompletion(a newly created TypeError object).
let err = Err(JsNativeError::typ() let err = Err(JsNativeError::typ()
.with_message("cannot get key and value from primitive item of `iterable`") .with_message("cannot get key and value from primitive item of `iterable`")
.into()); .into());
@ -665,26 +661,19 @@ pub(crate) fn add_entries_from_iterable(
return iterator_record.close(err, context); return iterator_record.close(err, context);
}; };
// e. Let k be Get(nextItem, "0"). // d. Let k be Completion(Get(next, "0")).
// f. IfAbruptCloseIterator(k, iteratorRecord). // e. IfAbruptCloseIterator(k, iteratorRecord).
let key = match next_item.get(0, context) { let key = if_abrupt_close_iterator!(next.get(0, context), iterator_record, context);
Ok(val) => val,
err => return iterator_record.close(err, context),
};
// g. Let v be Get(nextItem, "1"). // f. Let v be Completion(Get(next, "1")).
// h. IfAbruptCloseIterator(v, iteratorRecord). // g. IfAbruptCloseIterator(v, iteratorRecord).
let value = match next_item.get(1, context) { let value = if_abrupt_close_iterator!(next.get(1, context), iterator_record, context);
Ok(val) => val,
err => return iterator_record.close(err, context),
};
// i. Let status be Call(adder, target, « k, v »). // h. Let status be Completion(Call(adder, target, « k, v »)).
// i. IfAbruptCloseIterator(status, iteratorRecord).
let status = adder.call(&target.clone().into(), &[key, value], context); let status = adder.call(&target.clone().into(), &[key, value], context);
if_abrupt_close_iterator!(status, iterator_record, context);
// j. IfAbruptCloseIterator(status, iteratorRecord).
if status.is_err() {
return iterator_record.close(status, context);
}
} }
Ok(target.clone().into())
} }

24
core/engine/src/builtins/object/mod.rs

@ -17,7 +17,7 @@ use super::{
error::ErrorObject, Array, BuiltInBuilder, BuiltInConstructor, Date, IntrinsicObject, RegExp, error::ErrorObject, Array, BuiltInBuilder, BuiltInConstructor, Date, IntrinsicObject, RegExp,
}; };
use crate::{ use crate::{
builtins::{map, BuiltInObject}, builtins::{iterable::IteratorHint, map, BuiltInObject},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
js_string, js_string,
@ -1339,7 +1339,7 @@ impl OrdinaryObject {
let adder = closure.length(2).name("").build(); let adder = closure.length(2).name("").build();
// 6. Return ? AddEntriesFromIterable(obj, iterable, adder). // 6. Return ? AddEntriesFromIterable(obj, iterable, adder).
map::add_entries_from_iterable(&obj, iterable, &adder.into(), context) map::add_entries_from_iterable(&obj, iterable, &adder, context)
} }
/// [`Object.groupBy ( items, callbackfn )`][spec] /// [`Object.groupBy ( items, callbackfn )`][spec]
@ -1362,7 +1362,7 @@ impl OrdinaryObject {
// 1. Let groups be ? GroupBy(items, callbackfn, property). // 1. Let groups be ? GroupBy(items, callbackfn, property).
// `GroupBy` // `GroupBy`
// https://tc39.es/proposal-array-grouping/#sec-group-by // https://tc39.es/ecma262/#sec-groupby
// inlined to change the key type. // inlined to change the key type.
// 1. Perform ? RequireObjectCoercible(items). // 1. Perform ? RequireObjectCoercible(items).
@ -1377,8 +1377,8 @@ impl OrdinaryObject {
let mut groups: IndexMap<PropertyKey, Vec<JsValue>, BuildHasherDefault<FxHasher>> = let mut groups: IndexMap<PropertyKey, Vec<JsValue>, BuildHasherDefault<FxHasher>> =
IndexMap::default(); IndexMap::default();
// 4. Let iteratorRecord be ? GetIterator(items). // 4. Let iteratorRecord be ? GetIterator(items, sync).
let mut iterator = items.get_iterator(context, None, None)?; let mut iterator = items.get_iterator(IteratorHint::Sync, context)?;
// 5. Let k be 0. // 5. Let k be 0.
let mut k = 0u64; let mut k = 0u64;
@ -1396,17 +1396,15 @@ impl OrdinaryObject {
return iterator.close(Err(error), context); return iterator.close(Err(error), context);
} }
// b. Let next be ? IteratorStep(iteratorRecord). // b. Let next be ? IteratorStepValue(iteratorRecord).
let done = iterator.step(context)?; let Some(next) = iterator.step_value(context)? else {
// c. If next is false, then
// c. If next is false, then
if done {
// i. Return groups. // i. Return groups.
break; break;
} };
// d. Let value be ? IteratorValue(next). // d. Let value be next.
let value = iterator.value(context)?; let value = next;
// e. Let key be Completion(Call(callbackfn, undefined, « value, 𝔽(k) »)). // e. Let key be Completion(Call(callbackfn, undefined, « value, 𝔽(k) »)).
let key = callback.call(&JsValue::undefined(), &[value.clone(), k.into()], context); let key = callback.call(&JsValue::undefined(), &[value.clone(), k.into()], context);

349
core/engine/src/builtins/promise/mod.rs

@ -3,7 +3,10 @@
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
use super::{iterable::IteratorRecord, BuiltInBuilder, BuiltInConstructor, IntrinsicObject}; use super::{
iterable::{IteratorHint, IteratorRecord},
BuiltInBuilder, BuiltInConstructor, IntrinsicObject,
};
use crate::{ use crate::{
builtins::{Array, BuiltInObject}, builtins::{Array, BuiltInObject},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
@ -581,8 +584,10 @@ impl Promise {
let promise_resolve = let promise_resolve =
if_abrupt_reject_promise!(promise_resolve, promise_capability, context); if_abrupt_reject_promise!(promise_resolve, promise_capability, context);
// 5. Let iteratorRecord be Completion(GetIterator(iterable)). // 5. Let iteratorRecord be Completion(GetIterator(iterable, sync)).
let iterator_record = args.get_or_undefined(0).get_iterator(context, None, None); let iterator_record = args
.get_or_undefined(0)
.get_iterator(IteratorHint::Sync, context);
// 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability). // 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability).
let mut iterator_record = let mut iterator_record =
@ -649,56 +654,22 @@ impl Promise {
let mut index = 0; let mut index = 0;
// 4. Repeat, // 4. Repeat,
loop { while let Some(next) = iterator_record.step_value(context)? {
// a. Let next be Completion(IteratorStep(iteratorRecord)). // c. Append undefined to values.
// b. If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
// c. ReturnIfAbrupt(next).
let done = iterator_record.step(context)?;
// d. If next is false, then
// i. Set iteratorRecord.[[Done]] to true.
if done {
// ii. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.
remaining_elements_count.set(remaining_elements_count.get() - 1);
// iii. If remainingElementsCount.[[Value]] is 0, then
if remaining_elements_count.get() == 0 {
// 1. Let valuesArray be CreateArrayFromList(values).
let values_array =
Array::create_array_from_list(values.borrow().iter().cloned(), context);
// 2. Perform ? Call(resultCapability.[[Resolve]], undefined, « valuesArray »).
result_capability.functions.resolve.call(
&JsValue::undefined(),
&[values_array.into()],
context,
)?;
}
// iv. Return resultCapability.[[Promise]].
return Ok(result_capability.promise.clone());
}
// e. Let nextValue be Completion(IteratorValue(next)).
// f. If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to true.
// g. ReturnIfAbrupt(nextValue).
let next_value = iterator_record.value(context)?;
// h. Append undefined to values.
values.borrow_mut().push(JsValue::Undefined); values.borrow_mut().push(JsValue::Undefined);
// i. Let nextPromise be ? Call(promiseResolve, constructor, « nextValue »). // d. Let nextPromise be ? Call(promiseResolve, constructor, « next »).
let next_promise = let next_promise =
promise_resolve.call(&constructor.clone().into(), &[next_value], context)?; promise_resolve.call(&constructor.clone().into(), &[next], context)?;
// j. Let steps be the algorithm steps defined in Promise.all Resolve Element Functions. // e. Let steps be the algorithm steps defined in Promise.all Resolve Element Functions.
// k. Let length be the number of non-optional parameters of the function definition in Promise.all Resolve Element Functions. // f. Let length be the number of non-optional parameters of the function definition in Promise.all Resolve Element Functions.
// l. Let onFulfilled be CreateBuiltinFunction(steps, length, "", « [[AlreadyCalled]], [[Index]], [[Values]], [[Capability]], [[RemainingElements]] »). // g. Let onFulfilled be CreateBuiltinFunction(steps, length, "", « [[AlreadyCalled]], [[Index]], [[Values]], [[Capability]], [[RemainingElements]] »).
// m. Set onFulfilled.[[AlreadyCalled]] to false. // h. Set onFulfilled.[[AlreadyCalled]] to false.
// n. Set onFulfilled.[[Index]] to index. // i. Set onFulfilled.[[Index]] to index.
// o. Set onFulfilled.[[Values]] to values. // j. Set onFulfilled.[[Values]] to values.
// p. Set onFulfilled.[[Capability]] to resultCapability. // k. Set onFulfilled.[[Capability]] to resultCapability.
// q. Set onFulfilled.[[RemainingElements]] to remainingElementsCount. // l. Set onFulfilled.[[RemainingElements]] to remainingElementsCount.
let on_fulfilled = FunctionObjectBuilder::new( let on_fulfilled = FunctionObjectBuilder::new(
context.realm(), context.realm(),
NativeFunction::from_copy_closure_with_captures( NativeFunction::from_copy_closure_with_captures(
@ -761,10 +732,10 @@ impl Promise {
.constructor(false) .constructor(false)
.build(); .build();
// r. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] + 1. // m. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] + 1.
remaining_elements_count.set(remaining_elements_count.get() + 1); remaining_elements_count.set(remaining_elements_count.get() + 1);
// s. Perform ? Invoke(nextPromise, "then", « onFulfilled, resultCapability.[[Reject]] »). // n. Perform ? Invoke(nextPromise, "then", « onFulfilled, resultCapability.[[Reject]] »).
next_promise.invoke( next_promise.invoke(
js_str!("then"), js_str!("then"),
&[ &[
@ -774,9 +745,30 @@ impl Promise {
context, context,
)?; )?;
// t. Set index to index + 1. // o. Set index to index + 1.
index += 1; index += 1;
} }
// b. If next is done, then
// i. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.
remaining_elements_count.set(remaining_elements_count.get() - 1);
// ii. If remainingElementsCount.[[Value]] = 0, then
if remaining_elements_count.get() == 0 {
// 1. Let valuesArray be CreateArrayFromList(values).
let values_array =
Array::create_array_from_list(values.borrow().iter().cloned(), context);
// 2. Perform ? Call(resultCapability.[[Resolve]], undefined, « valuesArray »).
result_capability.functions.resolve.call(
&JsValue::undefined(),
&[values_array.into()],
context,
)?;
}
// iii. Return resultCapability.[[Promise]].
Ok(result_capability.promise.clone())
} }
/// `Promise.allSettled ( iterable )` /// `Promise.allSettled ( iterable )`
@ -807,8 +799,10 @@ impl Promise {
let promise_resolve = let promise_resolve =
if_abrupt_reject_promise!(promise_resolve, promise_capability, context); if_abrupt_reject_promise!(promise_resolve, promise_capability, context);
// 5. Let iteratorRecord be Completion(GetIterator(iterable)). // 5. Let iteratorRecord be Completion(GetIterator(iterable, sync)).
let iterator_record = args.get_or_undefined(0).get_iterator(context, None, None); let iterator_record = args
.get_or_undefined(0)
.get_iterator(IteratorHint::Sync, context);
// 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability). // 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability).
let mut iterator_record = let mut iterator_record =
@ -875,59 +869,23 @@ impl Promise {
let mut index = 0; let mut index = 0;
// 4. Repeat, // 4. Repeat,
loop { while let Some(next) = iterator_record.step_value(context)? {
// a. Let next be Completion(IteratorStep(iteratorRecord)). // c. Append undefined to values.
// b. If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
// c. ReturnIfAbrupt(next).
let done = iterator_record.step(context)?;
// d. If next is false, then
if done {
// i. Set iteratorRecord.[[Done]] to true.
// ii. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.
remaining_elements_count.set(remaining_elements_count.get() - 1);
// iii. If remainingElementsCount.[[Value]] is 0, then
if remaining_elements_count.get() == 0 {
// 1. Let valuesArray be CreateArrayFromList(values).
let values_array = Array::create_array_from_list(
values.borrow().as_slice().iter().cloned(),
context,
);
// 2. Perform ? Call(resultCapability.[[Resolve]], undefined, « valuesArray »).
result_capability.functions.resolve.call(
&JsValue::undefined(),
&[values_array.into()],
context,
)?;
}
// iv. Return resultCapability.[[Promise]].
return Ok(result_capability.promise.clone());
}
// e. Let nextValue be Completion(IteratorValue(next)).
// f. If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to true.
// g. ReturnIfAbrupt(nextValue).
let next_value = iterator_record.value(context)?;
// h. Append undefined to values.
values.borrow_mut().push(JsValue::undefined()); values.borrow_mut().push(JsValue::undefined());
// i. Let nextPromise be ? Call(promiseResolve, constructor, « nextValue »). // d. Let nextPromise be ? Call(promiseResolve, constructor, « next »).
let next_promise = let next_promise =
promise_resolve.call(&constructor.clone().into(), &[next_value], context)?; promise_resolve.call(&constructor.clone().into(), &[next], context)?;
// j. Let stepsFulfilled be the algorithm steps defined in Promise.allSettled Resolve Element Functions. // e. Let stepsFulfilled be the algorithm steps defined in Promise.allSettled Resolve Element Functions.
// k. Let lengthFulfilled be the number of non-optional parameters of the function definition in Promise.allSettled Resolve Element Functions. // f. Let lengthFulfilled be the number of non-optional parameters of the function definition in Promise.allSettled Resolve Element Functions.
// l. Let onFulfilled be CreateBuiltinFunction(stepsFulfilled, lengthFulfilled, "", « [[AlreadyCalled]], [[Index]], [[Values]], [[Capability]], [[RemainingElements]] »). // g. Let onFulfilled be CreateBuiltinFunction(stepsFulfilled, lengthFulfilled, "", « [[AlreadyCalled]], [[Index]], [[Values]], [[Capability]], [[RemainingElements]] »).
// m. Let alreadyCalled be the Record { [[Value]]: false }. // h. Let alreadyCalled be the Record { [[Value]]: false }.
// n. Set onFulfilled.[[AlreadyCalled]] to alreadyCalled. // i. Set onFulfilled.[[AlreadyCalled]] to alreadyCalled.
// o. Set onFulfilled.[[Index]] to index. // j. Set onFulfilled.[[Index]] to index.
// p. Set onFulfilled.[[Values]] to values. // k. Set onFulfilled.[[Values]] to values.
// q. Set onFulfilled.[[Capability]] to resultCapability. // l. Set onFulfilled.[[Capability]] to resultCapability.
// r. Set onFulfilled.[[RemainingElements]] to remainingElementsCount. // m. Set onFulfilled.[[RemainingElements]] to remainingElementsCount.
let on_fulfilled = FunctionObjectBuilder::new( let on_fulfilled = FunctionObjectBuilder::new(
context.realm(), context.realm(),
NativeFunction::from_copy_closure_with_captures( NativeFunction::from_copy_closure_with_captures(
@ -1010,14 +968,14 @@ impl Promise {
.constructor(false) .constructor(false)
.build(); .build();
// s. Let stepsRejected be the algorithm steps defined in Promise.allSettled Reject Element Functions. // n. Let stepsRejected be the algorithm steps defined in Promise.allSettled Reject Element Functions.
// t. Let lengthRejected be the number of non-optional parameters of the function definition in Promise.allSettled Reject Element Functions. // o. Let lengthRejected be the number of non-optional parameters of the function definition in Promise.allSettled Reject Element Functions.
// u. Let onRejected be CreateBuiltinFunction(stepsRejected, lengthRejected, "", « [[AlreadyCalled]], [[Index]], [[Values]], [[Capability]], [[RemainingElements]] »). // p. Let onRejected be CreateBuiltinFunction(stepsRejected, lengthRejected, "", « [[AlreadyCalled]], [[Index]], [[Values]], [[Capability]], [[RemainingElements]] »).
// v. Set onRejected.[[AlreadyCalled]] to alreadyCalled. // q. Set onRejected.[[AlreadyCalled]] to alreadyCalled.
// w. Set onRejected.[[Index]] to index. // r. Set onRejected.[[Index]] to index.
// x. Set onRejected.[[Values]] to values. // s. Set onRejected.[[Values]] to values.
// y. Set onRejected.[[Capability]] to resultCapability. // t. Set onRejected.[[Capability]] to resultCapability.
// z. Set onRejected.[[RemainingElements]] to remainingElementsCount. // u. Set onRejected.[[RemainingElements]] to remainingElementsCount.
let on_rejected = FunctionObjectBuilder::new( let on_rejected = FunctionObjectBuilder::new(
context.realm(), context.realm(),
NativeFunction::from_copy_closure_with_captures( NativeFunction::from_copy_closure_with_captures(
@ -1100,19 +1058,40 @@ impl Promise {
.constructor(false) .constructor(false)
.build(); .build();
// aa. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] + 1. // v. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] + 1.
remaining_elements_count.set(remaining_elements_count.get() + 1); remaining_elements_count.set(remaining_elements_count.get() + 1);
// ab. Perform ? Invoke(nextPromise, "then", « onFulfilled, onRejected »). // w. Perform ? Invoke(nextPromise, "then", « onFulfilled, onRejected »).
next_promise.invoke( next_promise.invoke(
js_str!("then"), js_str!("then"),
&[on_fulfilled.into(), on_rejected.into()], &[on_fulfilled.into(), on_rejected.into()],
context, context,
)?; )?;
// ac. Set index to index + 1. // x. Set index to index + 1.
index += 1; index += 1;
} }
// b. If next is done, then
// i. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.
remaining_elements_count.set(remaining_elements_count.get() - 1);
// ii. If remainingElementsCount.[[Value]] = 0, then
if remaining_elements_count.get() == 0 {
// 1. Let valuesArray be CreateArrayFromList(values).
let values_array =
Array::create_array_from_list(values.borrow().as_slice().iter().cloned(), context);
// 2. Perform ? Call(resultCapability.[[Resolve]], undefined, « valuesArray »).
result_capability.functions.resolve.call(
&JsValue::undefined(),
&[values_array.into()],
context,
)?;
}
// iii. Return resultCapability.[[Promise]].
Ok(result_capability.promise.clone())
} }
/// `Promise.any ( iterable )` /// `Promise.any ( iterable )`
@ -1143,8 +1122,10 @@ impl Promise {
let promise_resolve = let promise_resolve =
if_abrupt_reject_promise!(promise_resolve, promise_capability, context); if_abrupt_reject_promise!(promise_resolve, promise_capability, context);
// 5. Let iteratorRecord be Completion(GetIterator(iterable)). // 5. Let iteratorRecord be Completion(GetIterator(iterable, sync)).
let iterator_record = args.get_or_undefined(0).get_iterator(context, None, None); let iterator_record = args
.get_or_undefined(0)
.get_iterator(IteratorHint::Sync, context);
// 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability). // 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability).
let mut iterator_record = let mut iterator_record =
@ -1211,61 +1192,23 @@ impl Promise {
let mut index = 0; let mut index = 0;
// 4. Repeat, // 4. Repeat,
loop { // a. Let next be ? IteratorStepValue(iteratorRecord).
// a. Let next be Completion(IteratorStep(iteratorRecord)). while let Some(next) = iterator_record.step_value(context)? {
// b. If next is an abrupt completion, set iteratorRecord.[[Done]] to true. // c. Append undefined to errors.
// c. ReturnIfAbrupt(next).
let done = iterator_record.step(context)?;
// d. If next is false, then
if done {
// i. Set iteratorRecord.[[Done]] to true.
// ii. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.
remaining_elements_count.set(remaining_elements_count.get() - 1);
// iii. If remainingElementsCount.[[Value]] is 0, then
if remaining_elements_count.get() == 0 {
// 1. Let error be a newly created AggregateError object.
// 2. Perform ! DefinePropertyOrThrow(error, "errors", PropertyDescriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: CreateArrayFromList(errors) }).
let error = JsNativeError::aggregate(
errors
.borrow()
.iter()
.cloned()
.map(JsError::from_opaque)
.collect(),
)
.with_message("no promise in Promise.any was fulfilled.");
// 3. Return ThrowCompletion(error).
return Err(error.into());
}
// iv. Return resultCapability.[[Promise]].
return Ok(result_capability.promise.clone());
}
// e. Let nextValue be Completion(IteratorValue(next)).
// f. If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to true.
// g. ReturnIfAbrupt(nextValue).
let next_value = iterator_record.value(context)?;
// h. Append undefined to errors.
errors.borrow_mut().push(JsValue::undefined()); errors.borrow_mut().push(JsValue::undefined());
// i. Let nextPromise be ? Call(promiseResolve, constructor, « nextValue »). // d. Let nextPromise be ? Call(promiseResolve, constructor, « next »).
let next_promise = let next_promise =
promise_resolve.call(&constructor.clone().into(), &[next_value], context)?; promise_resolve.call(&constructor.clone().into(), &[next], context)?;
// j. Let stepsRejected be the algorithm steps defined in Promise.any Reject Element Functions. // e. Let stepsRejected be the algorithm steps defined in Promise.any Reject Element Functions.
// k. Let lengthRejected be the number of non-optional parameters of the function definition in Promise.any Reject Element Functions. // f. Let lengthRejected be the number of non-optional parameters of the function definition in Promise.any Reject Element Functions.
// l. Let onRejected be CreateBuiltinFunction(stepsRejected, lengthRejected, "", « [[AlreadyCalled]], [[Index]], [[Errors]], [[Capability]], [[RemainingElements]] »). // g. Let onRejected be CreateBuiltinFunction(stepsRejected, lengthRejected, "", « [[AlreadyCalled]], [[Index]], [[Errors]], [[Capability]], [[RemainingElements]] »).
// m. Set onRejected.[[AlreadyCalled]] to false. // h. Set onRejected.[[AlreadyCalled]] to false.
// n. Set onRejected.[[Index]] to index. // i. Set onRejected.[[Index]] to index.
// o. Set onRejected.[[Errors]] to errors. // j. Set onRejected.[[Errors]] to errors.
// p. Set onRejected.[[Capability]] to resultCapability. // k. Set onRejected.[[Capability]] to resultCapability.
// q. Set onRejected.[[RemainingElements]] to remainingElementsCount. // l. Set onRejected.[[RemainingElements]] to remainingElementsCount.
let on_rejected = FunctionObjectBuilder::new( let on_rejected = FunctionObjectBuilder::new(
context.realm(), context.realm(),
NativeFunction::from_copy_closure_with_captures( NativeFunction::from_copy_closure_with_captures(
@ -1336,10 +1279,10 @@ impl Promise {
.constructor(false) .constructor(false)
.build(); .build();
// r. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] + 1. // m. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] + 1.
remaining_elements_count.set(remaining_elements_count.get() + 1); remaining_elements_count.set(remaining_elements_count.get() + 1);
// s. Perform ? Invoke(nextPromise, "then", « resultCapability.[[Resolve]], onRejected »). // n. Perform ? Invoke(nextPromise, "then", « resultCapability.[[Resolve]], onRejected »).
next_promise.invoke( next_promise.invoke(
js_str!("then"), js_str!("then"),
&[ &[
@ -1349,9 +1292,33 @@ impl Promise {
context, context,
)?; )?;
// t. Set index to index + 1. // o. Set index to index + 1.
index += 1; index += 1;
} }
// b. If next is done, then
// i. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.
remaining_elements_count.set(remaining_elements_count.get() - 1);
// ii. If remainingElementsCount.[[Value]] = 0, then
if remaining_elements_count.get() == 0 {
// 1. Let error be a newly created AggregateError object.
let error = JsNativeError::aggregate(
errors
.borrow()
.iter()
.cloned()
.map(JsError::from_opaque)
.collect(),
)
.with_message("no promise in Promise.any was fulfilled.");
// 2. Perform ! DefinePropertyOrThrow(error, "errors", PropertyDescriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: CreateArrayFromList(errors) }).
// 3. Return ThrowCompletion(error).
return Err(error.into());
}
// iii. Return resultCapability.[[Promise]].
Ok(result_capability.promise.clone())
} }
/// `Promise.race ( iterable )` /// `Promise.race ( iterable )`
@ -1387,8 +1354,8 @@ impl Promise {
let promise_resolve = let promise_resolve =
if_abrupt_reject_promise!(promise_resolve, promise_capability, context); if_abrupt_reject_promise!(promise_resolve, promise_capability, context);
// 5. Let iteratorRecord be Completion(GetIterator(iterable)). // 5. Let iteratorRecord be Completion(GetIterator(iterable, sync)).
let iterator_record = iterable.get_iterator(context, None, None); let iterator_record = iterable.get_iterator(IteratorHint::Sync, context);
// 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability). // 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability).
let mut iterator_record = let mut iterator_record =
@ -1440,29 +1407,13 @@ impl Promise {
context: &mut Context, context: &mut Context,
) -> JsResult<JsObject> { ) -> JsResult<JsObject> {
let constructor = constructor.clone().into(); let constructor = constructor.clone().into();
// 1. Repeat,
loop {
// a. Let next be Completion(IteratorStep(iteratorRecord)).
// b. If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
// c. ReturnIfAbrupt(next).
let done = iterator_record.step(context)?;
if done {
// d. If next is false, then
// i. Set iteratorRecord.[[Done]] to true.
// ii. Return resultCapability.[[Promise]].
return Ok(result_capability.promise.clone());
}
// e. Let nextValue be Completion(IteratorValue(next)).
// f. If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to true.
// g. ReturnIfAbrupt(nextValue).
let next_value = iterator_record.value(context)?;
// h. Let nextPromise be ? Call(promiseResolve, constructor, « nextValue »). // 1. Repeat,
let next_promise = promise_resolve.call(&constructor, &[next_value], context)?; // a. Let next be ? IteratorStepValue(iteratorRecord).
while let Some(next) = iterator_record.step_value(context)? {
// i. Perform ? Invoke(nextPromise, "then", « resultCapability.[[Resolve]], resultCapability.[[Reject]] »). // c. Let nextPromise be ? Call(promiseResolve, constructor, « next »).
let next_promise = promise_resolve.call(&constructor, &[next], context)?;
// d. Perform ? Invoke(nextPromise, "then", « resultCapability.[[Resolve]], resultCapability.[[Reject]] »).
next_promise.invoke( next_promise.invoke(
js_str!("then"), js_str!("then"),
&[ &[
@ -1472,6 +1423,10 @@ impl Promise {
context, context,
)?; )?;
} }
// b. If next is done, then
// i. Return resultCapability.[[Promise]].
Ok(result_capability.promise.clone())
} }
/// `Promise.reject ( r )` /// `Promise.reject ( r )`

25
core/engine/src/builtins/set/mod.rs

@ -36,6 +36,8 @@ use num_traits::Zero;
pub(crate) use set_iterator::SetIterator; pub(crate) use set_iterator::SetIterator;
use super::iterable::IteratorHint;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct Set; pub(crate) struct Set;
@ -110,6 +112,9 @@ impl BuiltInConstructor for Set {
const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor = const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =
StandardConstructors::set; StandardConstructors::set;
/// [`Set ( [ iterable ] )`][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-set-iterable
fn constructor( fn constructor(
new_target: &JsValue, new_target: &JsValue,
args: &[JsValue], args: &[JsValue],
@ -146,26 +151,20 @@ impl BuiltInConstructor for Set {
JsNativeError::typ().with_message("'add' of 'newTarget' is not a function") JsNativeError::typ().with_message("'add' of 'newTarget' is not a function")
})?; })?;
// 7. Let iteratorRecord be ? GetIterator(iterable). // 7. Let iteratorRecord be ? GetIterator(iterable, sync).
let mut iterator_record = iterable.clone().get_iterator(context, None, None)?; let mut iterator_record = iterable.clone().get_iterator(IteratorHint::Sync, context)?;
// 8. Repeat, // 8. Repeat,
// a. Let next be ? IteratorStep(iteratorRecord). // a. Let next be ? IteratorStepValue(iteratorRecord).
// b. If next is false, return set. while let Some(next) = iterator_record.step_value(context)? {
// c. Let nextValue be ? IteratorValue(next). // c. Let status be Completion(Call(adder, set, « next »)).
// d. Let status be Completion(Call(adder, set, « nextValue »)).
// e. IfAbruptCloseIterator(status, iteratorRecord).
while !iterator_record.step(context)? {
let next = iterator_record.value(context)?;
// c
// d, e
if let Err(status) = adder.call(&set.clone().into(), &[next], context) { if let Err(status) = adder.call(&set.clone().into(), &[next], context) {
// d. IfAbruptCloseIterator(status, iteratorRecord).
return iterator_record.close(Err(status), context); return iterator_record.close(Err(status), context);
} }
} }
// 8.b // b. If next is done, return set.
Ok(set.into()) Ok(set.into())
} }
} }

28
core/engine/src/builtins/temporal/mod.rs

@ -189,28 +189,26 @@ pub(crate) fn _iterator_to_list_of_types(
// 1. Let values be a new empty List. // 1. Let values be a new empty List.
let mut values = Vec::new(); let mut values = Vec::new();
// 2. Let next be true. // 2. Repeat,
// 3. Repeat, while next is not false, // a. Let next be ? IteratorStepValue(iteratorRecord).
// a. Set next to ? IteratorStep(iteratorRecord). while let Some(next) = iterator.step_value(context)? {
// b. If next is not false, then // c. If Type(next) is not an element of elementTypes, then
while iterator.step(context)? {
// i. Let nextValue be ? IteratorValue(next). if element_types.contains(&next.get_type()) {
let next_value = iterator.value(context)?; // i. Let completion be ThrowCompletion(a newly created TypeError object).
// ii. If Type(nextValue) is not an element of elementTypes, then
if element_types.contains(&next_value.get_type()) {
// 1. Let completion be ThrowCompletion(a newly created TypeError object).
let completion = JsNativeError::typ() let completion = JsNativeError::typ()
.with_message("IteratorNext is not within allowed type values."); .with_message("IteratorNext is not within allowed type values.");
// NOTE: The below should return as we are forcing a ThrowCompletion. // ii. Return ? IteratorClose(iteratorRecord, completion).
// 2. Return ? IteratorClose(iteratorRecord, completion).
let _never = iterator.close(Err(completion.into()), context)?; let _never = iterator.close(Err(completion.into()), context)?;
} }
// iii. Append nextValue to the end of the List values.
values.push(next_value); // d. Append next to the end of the List values.
values.push(next);
} }
// 4. Return values. // b. If next is done, then
// i. Return values.
Ok(values) Ok(values)
} }

5
core/engine/src/builtins/typed_array/builtin.rs

@ -16,7 +16,6 @@ use crate::{
utils::{memcpy, memmove, SliceRefMut}, utils::{memcpy, memmove, SliceRefMut},
ArrayBuffer, BufferObject, ArrayBuffer, BufferObject,
}, },
iterable::iterable_to_list,
Array, BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject, Array, BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,
}, },
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
@ -228,7 +227,9 @@ impl BuiltinTypedArray {
// 6. If usingIterator is not undefined, then // 6. If usingIterator is not undefined, then
if let Some(using_iterator) = using_iterator { if let Some(using_iterator) = using_iterator {
// a. Let values be ? IterableToList(source, usingIterator). // a. Let values be ? IterableToList(source, usingIterator).
let values = iterable_to_list(context, source, Some(using_iterator))?; let values = source
.get_iterator_from_method(&using_iterator, context)?
.into_list(context)?;
// b. Let len be the number of elements in values. // b. Let len be the number of elements in values.
// c. Let targetObj be ? TypedArrayCreate(C, « 𝔽(len) »). // c. Let targetObj be ? TypedArrayCreate(C, « 𝔽(len) »).

12
core/engine/src/builtins/typed_array/mod.rs

@ -13,10 +13,7 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray
use crate::{ use crate::{
builtins::{ builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},
iterable::iterable_to_list, BuiltInBuilder, BuiltInConstructor, BuiltInObject,
IntrinsicObject,
},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
js_string, js_string,
@ -199,13 +196,14 @@ impl<T: TypedArrayMarker> BuiltInConstructor for T {
// either a [[TypedArrayName]] or an [[ArrayBufferData]] internal slot. // either a [[TypedArrayName]] or an [[ArrayBufferData]] internal slot.
// 2. Let usingIterator be ? GetMethod(firstArgument, @@iterator). // 2. Let usingIterator be ? GetMethod(firstArgument, @@iterator).
let using_iterator = first_argument.get_method(JsSymbol::iterator(), context)?; let using_iterator = first_argument.get_method(JsSymbol::iterator(), context)?;
// 3. If usingIterator is not undefined, then // 3. If usingIterator is not undefined, then
if let Some(using_iterator) = using_iterator { if let Some(using_iterator) = using_iterator {
// a. Let values be ? IterableToList(firstArgument, usingIterator). // a. Let values be ? IteratorToList(? GetIteratorFromMethod(firstArgument, usingIterator)).
let values = iterable_to_list(context, &first_argument.into(), Some(using_iterator))?; let values = JsValue::from(first_argument.clone())
.get_iterator_from_method(&using_iterator, context)?
.into_list(context)?;
// b. Perform ? InitializeTypedArrayFromList(O, values). // b. Perform ? InitializeTypedArrayFromList(O, values).
BuiltinTypedArray::initialize_from_list::<T>(proto, values, context) BuiltinTypedArray::initialize_from_list::<T>(proto, values, context)

11
core/engine/src/builtins/weak_map/mod.rs

@ -101,14 +101,11 @@ impl BuiltInConstructor for WeakMap {
} }
// 5. Let adder be ? Get(map, "set"). // 5. Let adder be ? Get(map, "set").
let adder = map.get(js_str!("set"), context)?;
// 6. If IsCallable(adder) is false, throw a TypeError exception. // 6. If IsCallable(adder) is false, throw a TypeError exception.
if !adder.is_callable() { let adder = map
return Err(JsNativeError::typ() .get(js_str!("set"), context)?
.with_message("WeakMap: 'add' is not a function") .as_function()
.into()); .ok_or_else(|| JsNativeError::typ().with_message("WeakMap: 'add' is not a function"))?;
}
// 7. Return ? AddEntriesFromIterable(map, iterable, adder). // 7. Return ? AddEntriesFromIterable(map, iterable, adder).
add_entries_from_iterable(&map, iterable, &adder, context) add_entries_from_iterable(&map, iterable, &adder, context)

19
core/engine/src/builtins/weak_set/mod.rs

@ -22,6 +22,8 @@ use boa_gc::{Finalize, Trace};
use boa_macros::js_str; use boa_macros::js_str;
use boa_profiler::Profiler; use boa_profiler::Profiler;
use super::iterable::IteratorHint;
type NativeWeakSet = boa_gc::WeakMap<ErasedVTableObject, ()>; type NativeWeakSet = boa_gc::WeakMap<ErasedVTableObject, ()>;
#[derive(Debug, Trace, Finalize)] #[derive(Debug, Trace, Finalize)]
@ -104,23 +106,20 @@ impl BuiltInConstructor for WeakSet {
.as_callable() .as_callable()
.ok_or_else(|| JsNativeError::typ().with_message("WeakSet: 'add' is not a function"))?; .ok_or_else(|| JsNativeError::typ().with_message("WeakSet: 'add' is not a function"))?;
// 7. Let iteratorRecord be ? GetIterator(iterable). // 7. Let iteratorRecord be ? GetIterator(iterable, sync).
let mut iterator_record = iterable.clone().get_iterator(context, None, None)?; let mut iterator_record = iterable.clone().get_iterator(IteratorHint::Sync, context)?;
// 8. Repeat, // 8. Repeat,
// a. Let next be ? IteratorStep(iteratorRecord). // a. Let next be ? IteratorStepValue(iteratorRecord).
while !iterator_record.step(context)? { while let Some(next) = iterator_record.step_value(context)? {
// c. Let nextValue be ? IteratorValue(next). // c. Let status be Completion(Call(adder, set, « next »)).
let next = iterator_record.value(context)?;
// d. Let status be Completion(Call(adder, set, « nextValue »)).
// e. IfAbruptCloseIterator(status, iteratorRecord).
if let Err(status) = adder.call(&weak_set.clone().into(), &[next], context) { if let Err(status) = adder.call(&weak_set.clone().into(), &[next], context) {
// d. IfAbruptCloseIterator(status, iteratorRecord).
return iterator_record.close(Err(status), context); return iterator_record.close(Err(status), context);
} }
} }
// b. If next is false, return set. // b. If next is done, return set.
Ok(weak_set.into()) Ok(weak_set.into())
} }
} }

20
core/engine/src/object/builtins/jsmap.rs

@ -1,7 +1,10 @@
//! A Rust API wrapper for Boa's `Map` Builtin ECMAScript Object //! A Rust API wrapper for Boa's `Map` Builtin ECMAScript Object
use crate::{ use crate::{
builtins::map::{add_entries_from_iterable, ordered_map::OrderedMap}, builtins::{
builtins::Map, iterable::IteratorHint,
map::{add_entries_from_iterable, ordered_map::OrderedMap},
Map,
},
error::JsNativeError, error::JsNativeError,
object::{JsFunction, JsMapIterator, JsObject}, object::{JsFunction, JsMapIterator, JsObject},
value::TryFromJs, value::TryFromJs,
@ -126,8 +129,11 @@ impl JsMap {
// Let adder be Get(map, "set") per spec. This action should not fail with default map. // Let adder be Get(map, "set") per spec. This action should not fail with default map.
let adder = map let adder = map
.get(js_str!("set"), context) .get(js_str!("set"), context)?
.expect("creating a map with the default prototype must not fail"); .as_function()
.ok_or_else(|| {
JsNativeError::typ().with_message("property `set` on new `Map` must be callable")
})?;
let _completion_record = add_entries_from_iterable(&map, iterable, &adder, context)?; let _completion_record = add_entries_from_iterable(&map, iterable, &adder, context)?;
@ -199,7 +205,7 @@ impl JsMap {
#[inline] #[inline]
pub fn entries(&self, context: &mut Context) -> JsResult<JsMapIterator> { pub fn entries(&self, context: &mut Context) -> JsResult<JsMapIterator> {
let iterator_record = Map::entries(&self.inner.clone().into(), &[], context)? let iterator_record = Map::entries(&self.inner.clone().into(), &[], context)?
.get_iterator(context, None, None)?; .get_iterator(IteratorHint::Sync, context)?;
let map_iterator_object = iterator_record.iterator(); let map_iterator_object = iterator_record.iterator();
JsMapIterator::from_object(map_iterator_object.clone()) JsMapIterator::from_object(map_iterator_object.clone())
} }
@ -208,7 +214,7 @@ impl JsMap {
#[inline] #[inline]
pub fn keys(&self, context: &mut Context) -> JsResult<JsMapIterator> { pub fn keys(&self, context: &mut Context) -> JsResult<JsMapIterator> {
let iterator_record = Map::keys(&self.inner.clone().into(), &[], context)? let iterator_record = Map::keys(&self.inner.clone().into(), &[], context)?
.get_iterator(context, None, None)?; .get_iterator(IteratorHint::Sync, context)?;
let map_iterator_object = iterator_record.iterator(); let map_iterator_object = iterator_record.iterator();
JsMapIterator::from_object(map_iterator_object.clone()) JsMapIterator::from_object(map_iterator_object.clone())
} }
@ -400,7 +406,7 @@ impl JsMap {
#[inline] #[inline]
pub fn values(&self, context: &mut Context) -> JsResult<JsMapIterator> { pub fn values(&self, context: &mut Context) -> JsResult<JsMapIterator> {
let iterator_record = Map::values(&self.inner.clone().into(), &[], context)? let iterator_record = Map::values(&self.inner.clone().into(), &[], context)?
.get_iterator(context, None, None)?; .get_iterator(IteratorHint::Sync, context)?;
let map_iterator_object = iterator_record.iterator(); let map_iterator_object = iterator_record.iterator();
JsMapIterator::from_object(map_iterator_object.clone()) JsMapIterator::from_object(map_iterator_object.clone())
} }

6
core/engine/src/object/builtins/jsset.rs

@ -4,7 +4,7 @@ use std::ops::Deref;
use boa_gc::{Finalize, Trace}; use boa_gc::{Finalize, Trace};
use crate::{ use crate::{
builtins::{set::ordered_set::OrderedSet, Set}, builtins::{iterable::IteratorHint, set::ordered_set::OrderedSet, Set},
error::JsNativeError, error::JsNativeError,
object::{JsFunction, JsObject, JsSetIterator}, object::{JsFunction, JsObject, JsSetIterator},
value::TryFromJs, value::TryFromJs,
@ -104,7 +104,7 @@ impl JsSet {
#[inline] #[inline]
pub fn values(&self, context: &mut Context) -> JsResult<JsSetIterator> { pub fn values(&self, context: &mut Context) -> JsResult<JsSetIterator> {
let iterator_object = Set::values(&self.inner.clone().into(), &[JsValue::Null], context)? let iterator_object = Set::values(&self.inner.clone().into(), &[JsValue::Null], context)?
.get_iterator(context, None, None)?; .get_iterator(IteratorHint::Sync, context)?;
JsSetIterator::from_object(iterator_object.iterator().clone()) JsSetIterator::from_object(iterator_object.iterator().clone())
} }
@ -117,7 +117,7 @@ impl JsSet {
#[inline] #[inline]
pub fn keys(&self, context: &mut Context) -> JsResult<JsSetIterator> { pub fn keys(&self, context: &mut Context) -> JsResult<JsSetIterator> {
let iterator_object = Set::values(&self.inner.clone().into(), &[JsValue::Null], context)? let iterator_object = Set::values(&self.inner.clone().into(), &[JsValue::Null], context)?
.get_iterator(context, None, None)?; .get_iterator(IteratorHint::Sync, context)?;
JsSetIterator::from_object(iterator_object.iterator().clone()) JsSetIterator::from_object(iterator_object.iterator().clone())
} }

4
core/engine/src/vm/opcode/iteration/get.rs

@ -18,7 +18,7 @@ impl Operation for GetIterator {
fn execute(context: &mut Context) -> JsResult<CompletionType> { fn execute(context: &mut Context) -> JsResult<CompletionType> {
let object = context.vm.pop(); let object = context.vm.pop();
let iterator = object.get_iterator(context, None, None)?; let iterator = object.get_iterator(IteratorHint::Sync, context)?;
context.vm.frame_mut().iterators.push(iterator); context.vm.frame_mut().iterators.push(iterator);
Ok(CompletionType::Normal) Ok(CompletionType::Normal)
} }
@ -38,7 +38,7 @@ impl Operation for GetAsyncIterator {
fn execute(context: &mut Context) -> JsResult<CompletionType> { fn execute(context: &mut Context) -> JsResult<CompletionType> {
let object = context.vm.pop(); let object = context.vm.pop();
let iterator = object.get_iterator(context, Some(IteratorHint::Async), None)?; let iterator = object.get_iterator(IteratorHint::Async, context)?;
context.vm.frame_mut().iterators.push(iterator); context.vm.frame_mut().iterators.push(iterator);
Ok(CompletionType::Normal) Ok(CompletionType::Normal)
} }

3
core/engine/src/vm/opcode/push/array.rs

@ -101,8 +101,7 @@ impl Operation for PushIteratorToArray {
.expect("iterator stack should have at least an iterator"); .expect("iterator stack should have at least an iterator");
let array = context.vm.pop(); let array = context.vm.pop();
while !iterator.step(context)? { while let Some(next) = iterator.step_value(context)? {
let next = iterator.value(context)?;
Array::push(&array, &[next], context)?; Array::push(&array, &[next], context)?;
} }

2
test262_config.toml

@ -1,4 +1,4 @@
commit = "12307f5c20a4c4211e69823939fd1872212894c5" commit = "dde3050bdbfb8f425084077b6293563932d57ebc"
[ignored] [ignored]
# Not implemented yet: # Not implemented yet:

Loading…
Cancel
Save