Browse Source

Refactor builtin `Map` intrinsics to follow more closely the spec (#1572)

* Refactor builtin `Map` object and document methods

* Replace some calls to `is_function` with `is_callable`

* Remove `expect_map` methods and cleanup some `expect`s
pull/1573/head
jedel1043 3 years ago committed by GitHub
parent
commit
8ba500a26a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 23
      boa/src/builtins/array/array_iterator.rs
  2. 6
      boa/src/builtins/array/mod.rs
  3. 40
      boa/src/builtins/iterable/mod.rs
  4. 156
      boa/src/builtins/map/map_iterator.rs
  5. 457
      boa/src/builtins/map/mod.rs
  6. 9
      boa/src/builtins/map/ordered_map.rs
  7. 12
      boa/src/builtins/number/mod.rs
  8. 8
      boa/src/builtins/object/for_in_iterator.rs
  9. 10
      boa/src/builtins/regexp/regexp_string_iterator.rs
  10. 10
      boa/src/builtins/set/mod.rs
  11. 12
      boa/src/builtins/set/set_iterator.rs
  12. 2
      boa/src/builtins/string/mod.rs
  13. 10
      boa/src/builtins/string/string_iterator.rs
  14. 22
      boa/src/object/mod.rs
  15. 8
      boa/src/syntax/ast/node/array/mod.rs
  16. 8
      boa/src/syntax/ast/node/call/mod.rs
  17. 18
      boa/src/syntax/ast/node/declaration/mod.rs
  18. 10
      boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs
  19. 10
      boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs
  20. 8
      boa/src/syntax/ast/node/new/mod.rs
  21. 2
      boa/src/value/mod.rs

23
boa/src/builtins/array/array_iterator.rs

@ -40,9 +40,9 @@ impl ArrayIterator {
///
/// [spec]: https://tc39.es/ecma262/#sec-createarrayiterator
pub(crate) fn create_array_iterator(
context: &Context,
array: JsValue,
kind: PropertyNameKind,
context: &Context,
) -> JsValue {
let array_iterator = JsValue::new_object(context);
array_iterator.set_data(ObjectData::array_iterator(Self::new(array, kind)));
@ -68,9 +68,9 @@ impl ArrayIterator {
let index = array_iterator.next_index;
if array_iterator.array.is_undefined() {
return Ok(create_iter_result_object(
context,
JsValue::undefined(),
true,
context,
));
}
let len = array_iterator
@ -82,33 +82,30 @@ impl ArrayIterator {
if array_iterator.next_index >= len {
array_iterator.array = JsValue::undefined();
return Ok(create_iter_result_object(
context,
JsValue::undefined(),
true,
context,
));
}
array_iterator.next_index = index + 1;
match array_iterator.kind {
return match array_iterator.kind {
PropertyNameKind::Key => {
Ok(create_iter_result_object(context, index.into(), false))
Ok(create_iter_result_object(index.into(), false, context))
}
PropertyNameKind::Value => {
let element_value = array_iterator.array.get_field(index, context)?;
Ok(create_iter_result_object(context, element_value, false))
Ok(create_iter_result_object(element_value, false, context))
}
PropertyNameKind::KeyAndValue => {
let element_value = array_iterator.array.get_field(index, context)?;
let result =
Array::create_array_from_list([index.into(), element_value], context);
Ok(create_iter_result_object(context, result.into(), false))
Ok(create_iter_result_object(result.into(), false, context))
}
}
} else {
context.throw_type_error("`this` is not an ArrayIterator")
};
}
} else {
context.throw_type_error("`this` is not an ArrayIterator")
}
context.throw_type_error("`this` is not an ArrayIterator")
}
/// Create the %ArrayIteratorPrototype% object
@ -117,7 +114,7 @@ impl ArrayIterator {
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-%arrayiteratorprototype%-object
pub(crate) fn create_prototype(context: &mut Context, iterator_prototype: JsValue) -> JsObject {
pub(crate) fn create_prototype(iterator_prototype: JsValue, context: &mut Context) -> JsObject {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create prototype

6
boa/src/builtins/array/mod.rs

@ -2528,9 +2528,9 @@ impl Array {
context: &mut Context,
) -> JsResult<JsValue> {
Ok(ArrayIterator::create_array_iterator(
context,
this.clone(),
PropertyNameKind::Value,
context,
))
}
@ -2546,9 +2546,9 @@ impl Array {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values
pub(crate) fn keys(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Ok(ArrayIterator::create_array_iterator(
context,
this.clone(),
PropertyNameKind::Key,
context,
))
}
@ -2568,9 +2568,9 @@ impl Array {
context: &mut Context,
) -> JsResult<JsValue> {
Ok(ArrayIterator::create_array_iterator(
context,
this.clone(),
PropertyNameKind::KeyAndValue,
context,
))
}

40
boa/src/builtins/iterable/mod.rs

@ -25,22 +25,22 @@ impl IteratorPrototypes {
let iterator_prototype = create_iterator_prototype(context);
Self {
array_iterator: ArrayIterator::create_prototype(
context,
iterator_prototype.clone().into(),
context,
),
set_iterator: SetIterator::create_prototype(context, iterator_prototype.clone().into()),
set_iterator: SetIterator::create_prototype(iterator_prototype.clone().into(), context),
string_iterator: StringIterator::create_prototype(
context,
iterator_prototype.clone().into(),
context,
),
regexp_string_iterator: RegExpStringIterator::create_prototype(
context,
iterator_prototype.clone().into(),
context,
),
map_iterator: MapIterator::create_prototype(context, iterator_prototype.clone().into()),
map_iterator: MapIterator::create_prototype(iterator_prototype.clone().into(), context),
for_in_iterator: ForInIterator::create_prototype(
context,
iterator_prototype.clone().into(),
context,
),
iterator_prototype,
}
@ -85,7 +85,7 @@ impl IteratorPrototypes {
/// CreateIterResultObject( value, done )
///
/// Generates an object supporting the IteratorResult interface.
pub fn create_iter_result_object(context: &mut Context, value: JsValue, done: bool) -> JsValue {
pub fn create_iter_result_object(value: JsValue, done: bool, context: &mut Context) -> JsValue {
// 1. Assert: Type(done) is Boolean.
// 2. Let obj be ! OrdinaryObjectCreate(%Object.prototype%).
let obj = context.construct_object();
@ -101,12 +101,12 @@ pub fn create_iter_result_object(context: &mut Context, value: JsValue, done: bo
}
/// Get an iterator record
pub fn get_iterator(context: &mut Context, iterable: JsValue) -> JsResult<IteratorRecord> {
pub fn get_iterator(iterable: &JsValue, context: &mut Context) -> JsResult<IteratorRecord> {
let iterator_function = iterable.get_field(WellKnownSymbols::iterator(), context)?;
if iterator_function.is_null_or_undefined() {
return Err(context.construct_type_error("Not an iterable"));
}
let iterator_object = context.call(&iterator_function, &iterable, &[])?;
let iterator_object = context.call(&iterator_function, iterable, &[])?;
let next_function = iterator_object.get_field("next", context)?;
if next_function.is_null_or_undefined() {
return Err(context.construct_type_error("Could not find property `next`"));
@ -158,8 +158,8 @@ impl IteratorRecord {
let next = context.call(&self.next_function, &self.iterator_object, &[])?;
let done = next.get_field("done", context)?.to_boolean();
let next_result = next.get_field("value", context)?;
Ok(IteratorResult::new(next_result, done))
let value = next.get_field("value", context)?;
Ok(IteratorResult { value, done })
}
/// Cleanup the iterator
@ -203,20 +203,6 @@ impl IteratorRecord {
#[derive(Debug)]
pub struct IteratorResult {
value: JsValue,
done: bool,
}
impl IteratorResult {
fn new(value: JsValue, done: bool) -> Self {
Self { value, done }
}
pub fn is_done(&self) -> bool {
self.done
}
pub fn value(self) -> JsValue {
self.value
}
pub value: JsValue,
pub done: bool,
}

156
boa/src/builtins/map/map_iterator.rs

@ -7,7 +7,7 @@ use crate::{
};
use gc::{Finalize, Trace};
use super::{ordered_map::MapLock, Map};
use super::ordered_map::MapLock;
/// The Map Iterator object represents an iteration over a map. It implements the iterator protocol.
///
/// More information:
@ -16,7 +16,7 @@ use super::{ordered_map::MapLock, Map};
/// [spec]: https://tc39.es/ecma262/#sec-array-iterator-objects
#[derive(Debug, Clone, Finalize, Trace)]
pub struct MapIterator {
iterated_map: JsValue,
iterated_map: Option<JsObject>,
map_next_index: usize,
map_iteration_kind: PropertyNameKind,
lock: MapLock,
@ -25,17 +25,6 @@ pub struct MapIterator {
impl MapIterator {
pub(crate) const NAME: &'static str = "MapIterator";
/// Constructs a new `MapIterator`, that will iterate over `map`, starting at index 0
fn new(map: JsValue, kind: PropertyNameKind, context: &mut Context) -> JsResult<Self> {
let lock = Map::lock(&map, context)?;
Ok(MapIterator {
iterated_map: map,
map_next_index: 0,
map_iteration_kind: kind,
lock,
})
}
/// Abstract operation CreateMapIterator( map, kind )
///
/// Creates a new iterator over the given map.
@ -45,17 +34,29 @@ impl MapIterator {
///
/// [spec]: https://www.ecma-international.org/ecma-262/11.0/index.html#sec-createmapiterator
pub(crate) fn create_map_iterator(
context: &mut Context,
map: JsValue,
map: &JsValue,
kind: PropertyNameKind,
context: &mut Context,
) -> JsResult<JsValue> {
let map_iterator = JsValue::new_object(context);
map_iterator.set_data(ObjectData::map_iterator(Self::new(map, kind, context)?));
map_iterator
.as_object()
.expect("map iterator object")
.set_prototype_instance(context.iterator_prototypes().map_iterator().into());
Ok(map_iterator)
if let Some(map_obj) = map.as_object() {
if let Some(map) = map_obj.borrow_mut().as_map_mut() {
let lock = map.lock(map_obj.clone());
let iter = MapIterator {
iterated_map: Some(map_obj.clone()),
map_next_index: 0,
map_iteration_kind: kind,
lock,
};
let map_iterator = JsValue::new_object(context);
map_iterator.set_data(ObjectData::map_iterator(iter));
map_iterator
.as_object()
.expect("map iterator object")
.set_prototype_instance(context.iterator_prototypes().map_iterator().into());
return Ok(map_iterator);
}
}
context.throw_type_error("`this` is not a Map")
}
/// %MapIteratorPrototype%.next( )
@ -67,77 +68,56 @@ impl MapIterator {
///
/// [spec]: https://tc39.es/ecma262/#sec-%mapiteratorprototype%.next
pub(crate) fn next(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
if let JsValue::Object(ref object) = this {
let mut object = object.borrow_mut();
if let Some(map_iterator) = object.as_map_iterator_mut() {
let m = &map_iterator.iterated_map;
let mut index = map_iterator.map_next_index;
let item_kind = &map_iterator.map_iteration_kind;
let iterator_object = match this {
JsValue::Object(obj) if obj.borrow().is_map_iterator() => obj,
_ => return context.throw_type_error("`this` is not a MapIterator"),
};
if map_iterator.iterated_map.is_undefined() {
return Ok(create_iter_result_object(
context,
JsValue::undefined(),
true,
));
}
let mut iterator_object = iterator_object.borrow_mut();
let map_iterator = iterator_object
.as_map_iterator_mut()
.expect("checked that obj was a map iterator");
if let JsValue::Object(ref object) = m {
if let Some(entries) = object.borrow().as_map_ref() {
let num_entries = entries.full_len();
while index < num_entries {
let e = entries.get_index(index);
index += 1;
map_iterator.map_next_index = index;
if let Some((key, value)) = e {
match item_kind {
PropertyNameKind::Key => {
return Ok(create_iter_result_object(
context,
key.clone(),
false,
));
}
PropertyNameKind::Value => {
return Ok(create_iter_result_object(
context,
value.clone(),
false,
));
}
PropertyNameKind::KeyAndValue => {
let result = Array::create_array_from_list(
[key.clone(), value.clone()],
context,
);
return Ok(create_iter_result_object(
context,
result.into(),
false,
));
}
}
}
let mut index = map_iterator.map_next_index;
let item_kind = map_iterator.map_iteration_kind;
if let Some(obj) = map_iterator.iterated_map.take() {
let map = obj.borrow();
let entries = map.as_map_ref().expect("iterator should only iterate maps");
let num_entries = entries.full_len();
while index < num_entries {
let e = entries.get_index(index);
index += 1;
map_iterator.map_next_index = index;
if let Some((key, value)) = e {
let item = match item_kind {
PropertyNameKind::Key => {
Ok(create_iter_result_object(key.clone(), false, context))
}
PropertyNameKind::Value => {
Ok(create_iter_result_object(value.clone(), false, context))
}
} else {
return Err(context.construct_type_error("'this' is not a Map"));
}
} else {
return Err(context.construct_type_error("'this' is not a Map"));
PropertyNameKind::KeyAndValue => {
let result = Array::create_array_from_list(
[key.clone(), value.clone()],
context,
);
Ok(create_iter_result_object(result.into(), false, context))
}
};
drop(map);
map_iterator.iterated_map = Some(obj);
return item;
}
map_iterator.iterated_map = JsValue::undefined();
Ok(create_iter_result_object(
context,
JsValue::undefined(),
true,
))
} else {
context.throw_type_error("`this` is not an MapIterator")
}
} else {
context.throw_type_error("`this` is not an MapIterator")
}
Ok(create_iter_result_object(
JsValue::undefined(),
true,
context,
))
}
/// Create the %MapIteratorPrototype% object
@ -146,7 +126,7 @@ impl MapIterator {
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-%mapiteratorprototype%-object
pub(crate) fn create_prototype(context: &mut Context, iterator_prototype: JsValue) -> JsObject {
pub(crate) fn create_prototype(iterator_prototype: JsValue, context: &mut Context) -> JsObject {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create prototype

457
boa/src/builtins/map/mod.rs

@ -13,13 +13,16 @@
#![allow(clippy::mutable_key_type)]
use crate::{
builtins::BuiltIn,
builtins::{
iterable::{get_iterator, IteratorResult},
BuiltIn,
},
context::StandardObjects,
object::{
internal_methods::get_prototype_from_constructor, ConstructorBuilder, FunctionBuilder,
ObjectData,
JsObject, ObjectData,
},
property::{Attribute, PropertyDescriptor, PropertyNameKind},
property::{Attribute, PropertyNameKind},
symbol::WellKnownSymbols,
BoaProfiler, Context, JsResult, JsValue,
};
@ -28,8 +31,6 @@ use ordered_map::OrderedMap;
pub mod map_iterator;
use map_iterator::MapIterator;
use self::ordered_map::MapLock;
use super::JsArgs;
use num_traits::Zero;
@ -50,14 +51,17 @@ impl BuiltIn for Map {
fn init(context: &mut Context) -> (&'static str, JsValue, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let to_string_tag = WellKnownSymbols::to_string_tag();
let iterator_symbol = WellKnownSymbols::iterator();
let get_species = FunctionBuilder::native(context, Self::get_species)
.name("get [Symbol.species]")
.constructable(false)
.build();
let get_size = FunctionBuilder::native(context, Self::get_size)
.name("get size")
.length(0)
.constructable(false)
.build();
let entries_function = FunctionBuilder::native(context, Self::entries)
.name("entries")
.length(0)
@ -83,23 +87,24 @@ impl BuiltIn for Map {
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.property(
to_string_tag,
Self::NAME,
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.property(
iterator_symbol,
WellKnownSymbols::iterator(),
entries_function,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.method(Self::keys, "keys", 0)
.method(Self::set, "set", 2)
.property(
WellKnownSymbols::to_string_tag(),
Self::NAME,
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.method(Self::clear, "clear", 0)
.method(Self::delete, "delete", 1)
.method(Self::for_each, "forEach", 1)
.method(Self::get, "get", 1)
.method(Self::clear, "clear", 0)
.method(Self::has, "has", 1)
.method(Self::for_each, "forEach", 1)
.method(Self::keys, "keys", 0)
.method(Self::set, "set", 2)
.method(Self::values, "values", 0)
.accessor("size", Some(get_size), None, Attribute::CONFIGURABLE)
.build();
(Self::NAME, map_object.into(), Self::attribute())
@ -109,66 +114,47 @@ impl BuiltIn for Map {
impl Map {
pub(crate) const LENGTH: usize = 0;
/// Create a new map
/// `Map ( [ iterable ] )`
///
/// Constructor for `Map` objects.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-map-iterable
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/Map
pub(crate) fn constructor(
new_target: &JsValue,
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
// 1. If NewTarget is undefined, throw a TypeError exception.
if new_target.is_undefined() {
return context
.throw_type_error("calling a builtin Map constructor without new is forbidden");
}
// 2. Let map be ? OrdinaryCreateFromConstructor(NewTarget, "%Map.prototype%", « [[MapData]] »).
let prototype =
get_prototype_from_constructor(new_target, StandardObjects::map_object, context)?;
let map = context.construct_object();
map.set_prototype_instance(prototype.into());
let obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let this = JsValue::new(obj);
// add our arguments in
let data = match args.len() {
0 => OrderedMap::new(),
_ => match &args[0] {
JsValue::Object(object) => {
let object = object.borrow();
if let Some(map) = object.as_map_ref().cloned() {
map
} else if object.is_array() {
let mut map = OrderedMap::new();
let len = args[0].get_field("length", context)?.to_integer(context)? as i32;
for i in 0..len {
let val = &args[0].get_field(i, context)?;
let (key, value) =
Self::get_key_value(val, context)?.ok_or_else(|| {
context.construct_type_error(
"iterable for Map should have array-like objects",
)
})?;
map.insert(key, value);
}
map
} else {
return Err(context.construct_type_error(
"iterable for Map should have array-like objects",
));
}
}
_ => {
return Err(context
.construct_type_error("iterable for Map should have array-like objects"))
}
},
};
// 3. Set map.[[MapData]] to a new empty List.
map.borrow_mut().data = ObjectData::map(OrderedMap::new());
// finally create size property
Self::set_size(&this, data.len());
// 4. If iterable is either undefined or null, return map.
let iterable = match args.get_or_undefined(0) {
val if !val.is_null_or_undefined() => val,
_ => return Ok(map.into()),
};
// This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name)
this.set_data(ObjectData::map(data));
// 5. Let adder be ? Get(map, "set").
let adder = map.get("set", context)?;
Ok(this)
// 6. Return ? AddEntriesFromIterable(map, iterable, adder).
add_entries_from_iterable(&map, iterable, &adder, context)
}
/// `get Map [ @@species ]`
@ -201,7 +187,9 @@ impl Map {
_: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
MapIterator::create_map_iterator(context, this.clone(), PropertyNameKind::KeyAndValue)
// 1. Let M be the this value.
// 2. Return ? CreateMapIterator(M, key+value).
MapIterator::create_map_iterator(this, PropertyNameKind::KeyAndValue, context)
}
/// `Map.prototype.keys()`
@ -215,23 +203,14 @@ impl Map {
/// [spec]: https://tc39.es/ecma262/#sec-map.prototype.keys
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys
pub(crate) fn keys(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
MapIterator::create_map_iterator(context, this.clone(), PropertyNameKind::Key)
}
/// Helper function to set the size property.
fn set_size(this: &JsValue, size: usize) {
let size = PropertyDescriptor::builder()
.value(size)
.writable(false)
.enumerable(false)
.configurable(false);
this.set_property("size", size);
// 1. Let M be the this value.
// 2. Return ? CreateMapIterator(M, key).
MapIterator::create_map_iterator(this, PropertyNameKind::Key, context)
}
/// `Map.prototype.set( key, value )`
///
/// This method associates the value with the key. Returns the map object.
/// Inserts a new entry in the Map object.
///
/// More information:
/// - [ECMAScript reference][spec]
@ -247,10 +226,14 @@ impl Map {
let key = args.get_or_undefined(0);
let value = args.get_or_undefined(1);
let size = if let Some(object) = this.as_object() {
// 1. Let M be the this value.
if let Some(object) = this.as_object() {
// 2. Perform ? RequireInternalSlot(M, [[MapData]]).
// 3. Let entries be the List that is M.[[MapData]].
if let Some(map) = object.borrow_mut().as_map_mut() {
let key = match key {
JsValue::Rational(r) => {
// 5. If key is -0𝔽, set key to +0𝔽.
if r.is_zero() {
JsValue::Rational(0f64)
} else {
@ -259,22 +242,55 @@ impl Map {
}
_ => key.clone(),
};
// 4. For each Record { [[Key]], [[Value]] } p of entries, do
// a. If p.[[Key]] is not empty and SameValueZero(p.[[Key]], key) is true, then
// i. Set p.[[Value]] to value.
// 6. Let p be the Record { [[Key]]: key, [[Value]]: value }.
// 7. Append p as the last element of entries.
map.insert(key, value.clone());
map.len()
} else {
return Err(context.construct_type_error("'this' is not a Map"));
// ii. Return M.
// 8. Return M.
return Ok(this.clone());
}
} else {
return Err(context.construct_type_error("'this' is not a Map"));
};
}
context.throw_type_error("'this' is not a Map")
}
Self::set_size(this, size);
Ok(this.clone())
/// `get Map.prototype.size`
///
/// Obtains the size of the map, filtering empty keys to ensure it updates
/// while iterating.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-get-map.prototype.size
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/size
pub(crate) fn get_size(
this: &JsValue,
_: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
// 1. Let M be the this value.
if let Some(object) = this.as_object() {
// 2. Perform ? RequireInternalSlot(M, [[MapData]]).
// 3. Let entries be the List that is M.[[MapData]].
if let Some(map) = object.borrow_mut().as_map_mut() {
// 4. Let count be 0.
// 5. For each Record { [[Key]], [[Value]] } p of entries, do
// a. If p.[[Key]] is not empty, set count to count + 1.
// 6. Return 𝔽(count).
return Ok(map.len().into());
}
}
context.throw_type_error("'this' is not a Map")
}
/// `Map.prototype.delete( key )`
///
/// This method removes the element associated with the key, if it exists. Returns true if there was an element, false otherwise.
/// Removes the element associated with the key, if it exists.
/// Returns true if there was an element, and false otherwise.
///
/// More information:
/// - [ECMAScript reference][spec]
@ -289,23 +305,25 @@ impl Map {
) -> JsResult<JsValue> {
let key = args.get_or_undefined(0);
let (deleted, size) = if let Some(object) = this.as_object() {
// 1. Let M be the this value.
if let Some(object) = this.as_object() {
// 2. Perform ? RequireInternalSlot(M, [[MapData]]).
// 3. Let entries be the List that is M.[[MapData]].
if let Some(map) = object.borrow_mut().as_map_mut() {
let deleted = map.remove(key).is_some();
(deleted, map.len())
} else {
return Err(context.construct_type_error("'this' is not a Map"));
// a. If p.[[Key]] is not empty and SameValueZero(p.[[Key]], key) is true, then
// i. Set p.[[Key]] to empty.
// ii. Set p.[[Value]] to empty.
// iii. Return true.
// 5. Return false.
return Ok(map.remove(key).is_some().into());
}
} else {
return Err(context.construct_type_error("'this' is not a Map"));
};
Self::set_size(this, size);
Ok(deleted.into())
}
context.throw_type_error("'this' is not a Map")
}
/// `Map.prototype.get( key )`
///
/// This method returns the value associated with the key, or undefined if there is none.
/// Returns the value associated with the key, or undefined if there is none.
///
/// More information:
/// - [ECMAScript reference][spec]
@ -333,23 +351,24 @@ impl Map {
_ => key,
};
// 1. Let M be the this value.
if let JsValue::Object(ref object) = this {
let object = object.borrow();
if let Some(map) = object.as_map_ref() {
return Ok(if let Some(result) = map.get(key) {
result.clone()
} else {
JsValue::undefined()
});
// 2. Perform ? RequireInternalSlot(M, [[MapData]]).
// 3. Let entries be the List that is M.[[MapData]].
if let Some(map) = object.borrow().as_map_ref() {
// 4. For each Record { [[Key]], [[Value]] } p of entries, do
// a. If p.[[Key]] is not empty and SameValueZero(p.[[Key]], key) is true, return p.[[Value]].
// 5. Return undefined.
return Ok(map.get(key).cloned().unwrap_or_default());
}
}
Err(context.construct_type_error("'this' is not a Map"))
context.throw_type_error("'this' is not a Map")
}
/// `Map.prototype.clear( )`
///
/// This method removes all entries from the map.
/// Removes all entries from the map.
///
/// More information:
/// - [ECMAScript reference][spec]
@ -360,26 +379,24 @@ impl Map {
pub(crate) fn clear(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
// 1. Let M be the this value.
// 2. Perform ? RequireInternalSlot(M, [[MapData]]).
// 3. Let entries be the List that is M.[[MapData]].
// 4. For each Record { [[Key]], [[Value]] } p of entries, do
// a. Set p.[[Key]] to empty.
// b. Set p.[[Value]] to empty.
Self::set_size(this, 0);
if let Some(object) = this.as_object() {
// 3. Let entries be the List that is M.[[MapData]].
if let Some(map) = object.borrow_mut().as_map_mut() {
// 4. For each Record { [[Key]], [[Value]] } p of entries, do
// a. Set p.[[Key]] to empty.
// b. Set p.[[Value]] to empty.
map.clear();
} else {
return context.throw_type_error("'this' is not a Map");
// 5. Return undefined.
return Ok(JsValue::undefined());
}
} else {
return context.throw_type_error("'this' is not a Map");
}
Ok(JsValue::undefined())
context.throw_type_error("'this' is not a Map")
}
/// `Map.prototype.has( key )`
///
/// This method checks if the map contains an entry with the given key.
/// Checks if the map contains an entry with the given key.
///
/// More information:
/// - [ECMAScript reference][spec]
@ -394,19 +411,24 @@ impl Map {
) -> JsResult<JsValue> {
let key = args.get_or_undefined(0);
// 1. Let M be the this value.
if let JsValue::Object(ref object) = this {
let object = object.borrow();
if let Some(map) = object.as_map_ref() {
// 2. Perform ? RequireInternalSlot(M, [[MapData]]).
// 3. Let entries be the List that is M.[[MapData]].
if let Some(map) = object.borrow().as_map_ref() {
// 4. For each Record { [[Key]], [[Value]] } p of entries, do
// a. If p.[[Key]] is not empty and SameValueZero(p.[[Key]], key) is true, return true.
// 5. Return false.
return Ok(map.contains_key(key).into());
}
}
Err(context.construct_type_error("'this' is not a Map"))
context.throw_type_error("'this' is not a Map")
}
/// `Map.prototype.forEach( callbackFn [ , thisArg ] )`
///
/// This method executes the provided callback function for each key-value pair in the map.
/// Executes the provided callback function for each key-value pair in the map.
///
/// More information:
/// - [ECMAScript reference][spec]
@ -419,77 +441,63 @@ impl Map {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
if args.is_empty() {
return Err(JsValue::new("Missing argument for Map.prototype.forEach"));
}
let callback_arg = &args[0];
// 1. Let M be the this value.
// 2. Perform ? RequireInternalSlot(M, [[MapData]]).
let map = match this {
JsValue::Object(obj) if obj.is_map() => obj,
_ => return context.throw_type_error("`this` is not a Map"),
};
if !callback_arg.is_function() {
let name = callback_arg.to_string(context)?;
return context.throw_type_error(format!("{} is not a function", name));
}
// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
let callback = match args.get_or_undefined(0) {
JsValue::Object(obj) if obj.is_callable() => obj,
val => {
let name = val.to_string(context)?;
return context.throw_type_error(format!("{} is not a function", name));
}
};
let this_arg = args.get_or_undefined(1);
// NOTE:
//
// forEach does not directly mutate the object on which it is called but
// the object may be mutated by the calls to callbackfn. Each entry of a
// map's [[MapData]] is only visited once. New keys added after the call
// to forEach begins are visited. A key will be revisited if it is deleted
// after it has been visited and then re-added before the forEach call completes.
// Keys that are deleted after the call to forEach begins and before being visited
// are not visited unless the key is added again before the forEach call completes.
let _lock = map
.borrow_mut()
.as_map_mut()
.expect("checked that `this` was a map")
.lock(map.clone());
// 4. Let entries be the List that is M.[[MapData]].
// 5. For each Record { [[Key]], [[Value]] } e of entries, do
let mut index = 0;
let lock = Map::lock(this, context)?;
while index < Map::get_full_len(this, context)? {
let arguments = if let JsValue::Object(ref object) = this {
let object = object.borrow();
if let Some(map) = object.as_map_ref() {
if let Some((key, value)) = map.get_index(index) {
Some([value.clone(), key.clone(), this.clone()])
} else {
None
}
loop {
let arguments = {
let map = map.borrow();
let map = map.as_map_ref().expect("checked that `this` was a map");
if index < map.full_len() {
map.get_index(index)
.map(|(k, v)| [v.clone(), k.clone(), this.clone()])
} else {
return context.throw_type_error("'this' is not a Map");
// 6. Return undefined.
return Ok(JsValue::undefined());
}
} else {
return context.throw_type_error("'this' is not a Map");
};
// a. If e.[[Key]] is not empty, then
if let Some(arguments) = arguments {
context.call(callback_arg, this_arg, &arguments)?;
// i. Perform ? Call(callbackfn, thisArg, « e.[[Value]], e.[[Key]], M »).
callback.call(this_arg, &arguments, context)?;
}
index += 1;
}
drop(lock);
Ok(JsValue::undefined())
}
/// Helper function to get the full size of the map.
fn get_full_len(map: &JsValue, context: &mut Context) -> JsResult<usize> {
if let JsValue::Object(ref object) = map {
let object = object.borrow();
if let Some(map) = object.as_map_ref() {
Ok(map.full_len())
} else {
Err(context.construct_type_error("'this' is not a Map"))
}
} else {
Err(context.construct_type_error("'this' is not a Map"))
}
}
/// Helper function to lock the map.
fn lock(map: &JsValue, context: &mut Context) -> JsResult<MapLock> {
if let JsValue::Object(ref object) = map {
let mut map = object.borrow_mut();
if let Some(map) = map.as_map_mut() {
Ok(map.lock(object.clone()))
} else {
Err(context.construct_type_error("'this' is not a Map"))
}
} else {
Err(context.construct_type_error("'this' is not a Map"))
}
}
/// `Map.prototype.values()`
@ -507,28 +515,79 @@ impl Map {
_: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
MapIterator::create_map_iterator(context, this.clone(), PropertyNameKind::Value)
// 1. Let M be the this value.
// 2. Return ? CreateMapIterator(M, value).
MapIterator::create_map_iterator(this, PropertyNameKind::Value, context)
}
}
/// Helper function to get a key-value pair from an array.
fn get_key_value(
value: &JsValue,
context: &mut Context,
) -> JsResult<Option<(JsValue, JsValue)>> {
if let JsValue::Object(object) = value {
if object.is_array() {
let (key, value) =
match value.get_field("length", context)?.as_number().unwrap() as i32 {
0 => (JsValue::undefined(), JsValue::undefined()),
1 => (value.get_field("0", context)?, JsValue::undefined()),
_ => (
value.get_field("0", context)?,
value.get_field("1", context)?,
),
};
return Ok(Some((key, value)));
}
/// `AddEntriesFromIterable`
///
/// Allows adding entries to a map from any object that has a `@@Iterator` field.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-add-entries-from-iterable
pub(crate) fn add_entries_from_iterable(
target: &JsObject,
iterable: &JsValue,
adder: &JsValue,
context: &mut Context,
) -> JsResult<JsValue> {
// 1. If IsCallable(adder) is false, throw a TypeError exception.
let adder = match adder {
JsValue::Object(obj) if obj.is_callable() => obj,
_ => return context.throw_type_error("property `set` of `NewTarget` is not callable"),
};
// 2. Let iteratorRecord be ? GetIterator(iterable).
let iterator_record = get_iterator(iterable, context)?;
// 3. Repeat,
loop {
// a. Let next be ? IteratorStep(iteratorRecord).
// c. Let nextItem be ? IteratorValue(next).
let IteratorResult { value, done } = iterator_record.next(context)?;
// b. If next is false, return target.
if done {
return Ok(target.clone().into());
}
let next_item = if let Some(obj) = value.as_object() {
obj
}
// d. If Type(nextItem) is not Object, then
else {
// i. Let error be ThrowCompletion(a newly created TypeError object).
let err = context
.throw_type_error("cannot get key and value from primitive item of `iterable`");
// ii. Return ? IteratorClose(iteratorRecord, error).
return iterator_record.close(err, context);
};
// e. Let k be Get(nextItem, "0").
// f. IfAbruptCloseIterator(k, iteratorRecord).
let key = match next_item.get(0, context) {
Ok(val) => val,
err => return iterator_record.close(err, context),
};
// g. Let v be Get(nextItem, "1").
// h. IfAbruptCloseIterator(v, iteratorRecord).
let value = match next_item.get(1, context) {
Ok(val) => val,
err => return iterator_record.close(err, context),
};
// i. Let status be Call(adder, target, « k, v »).
let status = adder.call(&target.clone().into(), &[key, value], context);
// j. IfAbruptCloseIterator(status, iteratorRecord).
if status.is_err() {
return iterator_record.close(status, context);
}
Ok(None)
}
}

9
boa/src/builtins/map/ordered_map.rs

@ -142,9 +142,12 @@ impl<V> OrderedMap<V> {
}
}
/// Removes all elements from the map and resets the counter of
/// empty entries.
pub fn clear(&mut self) {
self.map.clear();
self.map.shrink_to_fit();
self.empty_count = 0
}
/// Return a reference to the value stored for `key`, if it is present,
@ -155,8 +158,10 @@ impl<V> OrderedMap<V> {
self.map.get(key).map(Option::as_ref).flatten()
}
/// Get a key-value pair by index
/// Valid indices are 0 <= index < self.full_len()
/// Get a key-value pair by index.
///
/// Valid indices are 0 <= index < self.full_len().
///
/// Computes in O(1) time.
pub fn get_index(&self, index: usize) -> Option<(&JsValue, &V)> {
if let (MapKey::Key(key), Some(value)) = self.map.get_index(index)? {

12
boa/src/builtins/number/mod.rs

@ -1074,17 +1074,7 @@ impl Number {
if a.is_nan() && b.is_nan() {
return true;
}
if a == 0.0 && b == 0.0 {
if (a.is_sign_negative() && b.is_sign_positive())
|| (a.is_sign_positive() && b.is_sign_negative())
{
return false;
};
true
} else {
a == b
}
a == b && a.signum() == b.signum()
}
/// The abstract operation Number::sameValueZero takes arguments

8
boa/src/builtins/object/for_in_iterator.rs

@ -45,7 +45,7 @@ impl ForInIterator {
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-createforiniterator
pub(crate) fn create_for_in_iterator(context: &Context, object: JsValue) -> JsValue {
pub(crate) fn create_for_in_iterator(object: JsValue, context: &Context) -> JsValue {
let for_in_iterator = JsValue::new_object(context);
for_in_iterator.set_data(ObjectData::for_in_iterator(Self::new(object)));
for_in_iterator
@ -92,9 +92,9 @@ impl ForInIterator {
iterator.visited_keys.insert(r.clone());
if desc.expect_enumerable() {
return Ok(create_iter_result_object(
context,
JsValue::new(r.to_string()),
false,
context,
));
}
}
@ -106,9 +106,9 @@ impl ForInIterator {
}
_ => {
return Ok(create_iter_result_object(
context,
JsValue::undefined(),
true,
context,
))
}
}
@ -129,7 +129,7 @@ impl ForInIterator {
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-%foriniteratorprototype%-object
pub(crate) fn create_prototype(context: &mut Context, iterator_prototype: JsValue) -> JsObject {
pub(crate) fn create_prototype(iterator_prototype: JsValue, context: &mut Context) -> JsObject {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create prototype

10
boa/src/builtins/regexp/regexp_string_iterator.rs

@ -92,9 +92,9 @@ impl RegExpStringIterator {
if let Some(iterator) = object.as_regexp_string_iterator_mut() {
if iterator.completed {
return Ok(create_iter_result_object(
context,
JsValue::undefined(),
true,
context,
));
}
@ -109,7 +109,7 @@ impl RegExpStringIterator {
// 1. Perform ? Yield(match).
// 2. Return undefined.
iterator.completed = true;
return Ok(create_iter_result_object(context, m.into(), false));
return Ok(create_iter_result_object(m.into(), false, context));
}
// iv. Let matchStr be ? ToString(? Get(match, "0")).
@ -137,14 +137,14 @@ impl RegExpStringIterator {
}
// vi. Perform ? Yield(match).
Ok(create_iter_result_object(context, m.into(), false))
Ok(create_iter_result_object(m.into(), false, context))
} else {
// ii. If match is null, return undefined.
iterator.completed = true;
Ok(create_iter_result_object(
context,
JsValue::undefined(),
true,
context,
))
}
} else {
@ -161,7 +161,7 @@ impl RegExpStringIterator {
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-%arrayiteratorprototype%-object
pub(crate) fn create_prototype(context: &mut Context, iterator_prototype: JsValue) -> JsObject {
pub(crate) fn create_prototype(iterator_prototype: JsValue, context: &mut Context) -> JsObject {
let _timer = BoaProfiler::global().start_event("RegExp String Iterator", "init");
// Create prototype

10
boa/src/builtins/set/mod.rs

@ -152,15 +152,15 @@ impl Set {
}
// 7
let iterator_record = get_iterator(context, iterable.clone())?;
let iterator_record = get_iterator(iterable, context)?;
// 8.a
let mut next = iterator_record.next(context)?;
// 8
while !next.is_done() {
while !next.done {
// c
let next_value = next.value();
let next_value = next.value;
// d, e
if let Err(status) = context.call(&adder, &set, &[next_value]) {
@ -305,9 +305,9 @@ impl Set {
}
Ok(SetIterator::create_set_iterator(
context,
this.clone(),
PropertyNameKind::KeyAndValue,
context,
))
}
@ -419,9 +419,9 @@ impl Set {
}
Ok(SetIterator::create_set_iterator(
context,
this.clone(),
PropertyNameKind::Value,
context,
))
}

12
boa/src/builtins/set/set_iterator.rs

@ -44,9 +44,9 @@ impl SetIterator {
///
/// [spec]: https://www.ecma-international.org/ecma-262/11.0/index.html#sec-createsetiterator
pub(crate) fn create_set_iterator(
context: &Context,
set: JsValue,
kind: PropertyNameKind,
context: &Context,
) -> JsValue {
let set_iterator = JsValue::new_object(context);
set_iterator.set_data(ObjectData::set_iterator(Self::new(set, kind)));
@ -75,9 +75,9 @@ impl SetIterator {
if set_iterator.iterated_set.is_undefined() {
return Ok(create_iter_result_object(
context,
JsValue::undefined(),
true,
context,
));
}
@ -92,9 +92,9 @@ impl SetIterator {
match item_kind {
PropertyNameKind::Value => {
return Ok(create_iter_result_object(
context,
value.clone(),
false,
context,
));
}
PropertyNameKind::KeyAndValue => {
@ -103,9 +103,9 @@ impl SetIterator {
context,
);
return Ok(create_iter_result_object(
context,
result.into(),
false,
context,
));
}
PropertyNameKind::Key => {
@ -123,9 +123,9 @@ impl SetIterator {
set_iterator.iterated_set = JsValue::undefined();
Ok(create_iter_result_object(
context,
JsValue::undefined(),
true,
context,
))
} else {
context.throw_type_error("`this` is not an SetIterator")
@ -141,7 +141,7 @@ impl SetIterator {
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-%setiteratorprototype%-object
pub(crate) fn create_prototype(context: &mut Context, iterator_prototype: JsValue) -> JsObject {
pub(crate) fn create_prototype(iterator_prototype: JsValue, context: &mut Context) -> JsObject {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create prototype

2
boa/src/builtins/string/mod.rs

@ -1777,7 +1777,7 @@ impl String {
_: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
StringIterator::create_string_iterator(context, this.clone())
StringIterator::create_string_iterator(this.clone(), context)
}
}

10
boa/src/builtins/string/string_iterator.rs

@ -23,7 +23,7 @@ impl StringIterator {
}
}
pub fn create_string_iterator(context: &mut Context, string: JsValue) -> JsResult<JsValue> {
pub fn create_string_iterator(string: JsValue, context: &mut Context) -> JsResult<JsValue> {
let string_iterator = JsValue::new_object(context);
string_iterator.set_data(ObjectData::string_iterator(Self::new(string)));
string_iterator
@ -39,9 +39,9 @@ impl StringIterator {
if let Some(string_iterator) = object.as_string_iterator_mut() {
if string_iterator.string.is_undefined() {
return Ok(create_iter_result_object(
context,
JsValue::undefined(),
true,
context,
));
}
let native_string = string_iterator.string.to_string(context)?;
@ -50,9 +50,9 @@ impl StringIterator {
if position >= len {
string_iterator.string = JsValue::undefined();
return Ok(create_iter_result_object(
context,
JsValue::undefined(),
true,
context,
));
}
let (_, code_unit_count, _) =
@ -63,7 +63,7 @@ impl StringIterator {
&[position.into(), string_iterator.next_index.into()],
context,
)?;
Ok(create_iter_result_object(context, result_string, false))
Ok(create_iter_result_object(result_string, false, context))
} else {
context.throw_type_error("`this` is not an ArrayIterator")
}
@ -78,7 +78,7 @@ impl StringIterator {
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-%arrayiteratorprototype%-object
pub(crate) fn create_prototype(context: &mut Context, iterator_prototype: JsValue) -> JsObject {
pub(crate) fn create_prototype(iterator_prototype: JsValue, context: &mut Context) -> JsObject {
let _timer = BoaProfiler::global().start_event("String Iterator", "init");
// Create prototype

22
boa/src/object/mod.rs

@ -603,6 +603,28 @@ impl Object {
}
}
#[inline]
pub fn is_map_iterator(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::MapIterator(_),
..
}
)
}
#[inline]
pub fn as_map_iterator_ref(&self) -> Option<&MapIterator> {
match &self.data {
ObjectData {
kind: ObjectKind::MapIterator(iter),
..
} => Some(iter),
_ => None,
}
}
#[inline]
pub fn as_map_iterator_mut(&mut self) -> Option<&mut MapIterator> {
match &mut self.data {

8
boa/src/syntax/ast/node/array/mod.rs

@ -46,18 +46,18 @@ impl Executable for ArrayDecl {
for elem in self.as_ref() {
if let Node::Spread(ref x) = elem {
let val = x.run(context)?;
let iterator_record = iterable::get_iterator(context, val)?;
let iterator_record = iterable::get_iterator(&val, context)?;
// TODO after proper internal Array representation as per https://github.com/boa-dev/boa/pull/811#discussion_r502460858
// next_index variable should be utilized here as per https://tc39.es/ecma262/#sec-runtime-semantics-arrayaccumulation
// let mut next_index = 0;
loop {
let next = iterator_record.next(context)?;
if next.is_done() {
if next.done {
break;
}
let next_value = next.value();
let next_value = next.value;
//next_index += 1;
elements.push(next_value.clone());
elements.push(next_value);
}
} else {
elements.push(elem.run(context)?);

8
boa/src/syntax/ast/node/call/mod.rs

@ -94,14 +94,14 @@ impl Executable for Call {
for arg in self.args() {
if let Node::Spread(ref x) = arg {
let val = x.run(context)?;
let iterator_record = iterable::get_iterator(context, val)?;
let iterator_record = iterable::get_iterator(&val, context)?;
loop {
let next = iterator_record.next(context)?;
if next.is_done() {
if next.done {
break;
}
let next_value = next.value();
v_args.push(next_value.clone());
let next_value = next.value;
v_args.push(next_value);
}
break; // after spread we don't accept any new arguments
} else {

18
boa/src/syntax/ast/node/declaration/mod.rs

@ -669,7 +669,7 @@ impl DeclarationPatternArray {
}
// 1. Let iteratorRecord be ? GetIterator(value).
let iterator = get_iterator(context, value)?;
let iterator = get_iterator(&value, context)?;
let mut result = Vec::new();
// 2. Let result be IteratorBindingInitialization of ArrayBindingPattern with arguments iteratorRecord and environment.
@ -705,7 +705,7 @@ impl DeclarationPatternArray {
// 3. If iteratorRecord.[[Done]] is false, then
// 4. If iteratorRecord.[[Done]] is true, let v be undefined.
let mut v = if !next.is_done() {
let mut v = if !next.done {
// a. Let next be IteratorStep(iteratorRecord).
// b. If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
// c. ReturnIfAbrupt(next).
@ -714,7 +714,7 @@ impl DeclarationPatternArray {
// i. Let v be IteratorValue(next).
// ii. If v is an abrupt completion, set iteratorRecord.[[Done]] to true.
// iii. ReturnIfAbrupt(v).
next.value()
next.value
} else {
JsValue::undefined()
};
@ -743,7 +743,7 @@ impl DeclarationPatternArray {
// 1. If iteratorRecord.[[Done]] is false, then
// 2. If iteratorRecord.[[Done]] is true, let v be undefined.
let v = if !next.is_done() {
let v = if !next.done {
// a. Let next be IteratorStep(iteratorRecord).
// b. If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
// c. ReturnIfAbrupt(next).
@ -752,7 +752,7 @@ impl DeclarationPatternArray {
// i. Let v be IteratorValue(next).
// ii. If v is an abrupt completion, set iteratorRecord.[[Done]] to true.
// iii. ReturnIfAbrupt(v).
Some(next.value())
Some(next.value)
} else {
None
};
@ -782,7 +782,7 @@ impl DeclarationPatternArray {
// iv. If next is false, set iteratorRecord.[[Done]] to true.
// b. If iteratorRecord.[[Done]] is true, then
if next.is_done() {
if next.done {
// i. If environment is undefined, return ? PutValue(lhs, A).
// ii. Return InitializeReferencedBinding(lhs, A).
break result.push((ident.clone(), a.clone().into()));
@ -794,7 +794,7 @@ impl DeclarationPatternArray {
// f. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), nextValue).
// g. Set n to n + 1.
Array::add_to_array_object(&a.clone().into(), &[next.value()], context)?;
Array::add_to_array_object(&a.clone().into(), &[next.value], context)?;
}
}
// BindingRestElement : ... BindingPattern
@ -814,7 +814,7 @@ impl DeclarationPatternArray {
let next = iterator.next(context)?;
// b. If iteratorRecord.[[Done]] is true, then
if next.is_done() {
if next.done {
// i. Return the result of performing BindingInitialization of BindingPattern with A and environment as the arguments.
break result
.append(&mut pattern.run(Some(a.clone().into()), context)?);
@ -825,7 +825,7 @@ impl DeclarationPatternArray {
// e. ReturnIfAbrupt(nextValue).
// f. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), nextValue).
// g. Set n to n + 1.
Array::add_to_array_object(&a.clone().into(), &[next.value()], context)?;
Array::add_to_array_object(&a.clone().into(), &[next.value], context)?;
}
}
}

10
boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs

@ -89,7 +89,7 @@ impl Executable for ForInLoop {
return Ok(result);
}
let object = object.to_object(context)?;
let for_in_iterator = ForInIterator::create_for_in_iterator(context, JsValue::new(object));
let for_in_iterator = ForInIterator::create_for_in_iterator(JsValue::new(object), context);
let next_function = for_in_iterator
.get_property("next")
.as_ref()
@ -104,24 +104,24 @@ impl Executable for ForInLoop {
context.push_environment(DeclarativeEnvironmentRecord::new(Some(env)));
}
let iterator_result = iterator.next(context)?;
if iterator_result.is_done() {
if iterator_result.done {
context.pop_environment();
break;
}
let next_result = iterator_result.value();
let next_result = iterator_result.value;
match self.variable() {
Node::Identifier(ref name) => {
if context.has_binding(name.as_ref()) {
// Binding already exists
context.set_mutable_binding(name.as_ref(), next_result.clone(), true)?;
context.set_mutable_binding(name.as_ref(), next_result, true)?;
} else {
context.create_mutable_binding(
name.as_ref().to_owned(),
true,
VariableScope::Function,
)?;
context.initialize_binding(name.as_ref(), next_result.clone())?;
context.initialize_binding(name.as_ref(), next_result)?;
}
}
Node::VarDeclList(ref list) => match list.as_ref() {

10
boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs

@ -83,7 +83,7 @@ impl Executable for ForOfLoop {
fn run(&self, context: &mut Context) -> JsResult<JsValue> {
let _timer = BoaProfiler::global().start_event("ForOf", "exec");
let iterable = self.iterable().run(context)?;
let iterator = get_iterator(context, iterable)?;
let iterator = get_iterator(&iterable, context)?;
let mut result = JsValue::undefined();
loop {
@ -92,24 +92,24 @@ impl Executable for ForOfLoop {
context.push_environment(DeclarativeEnvironmentRecord::new(Some(env)));
}
let iterator_result = iterator.next(context)?;
if iterator_result.is_done() {
if iterator_result.done {
context.pop_environment();
break;
}
let next_result = iterator_result.value();
let next_result = iterator_result.value;
match self.variable() {
Node::Identifier(ref name) => {
if context.has_binding(name.as_ref()) {
// Binding already exists
context.set_mutable_binding(name.as_ref(), next_result.clone(), true)?;
context.set_mutable_binding(name.as_ref(), next_result, true)?;
} else {
context.create_mutable_binding(
name.as_ref().to_owned(),
true,
VariableScope::Function,
)?;
context.initialize_binding(name.as_ref(), next_result.clone())?;
context.initialize_binding(name.as_ref(), next_result)?;
}
}
Node::VarDeclList(ref list) => match list.as_ref() {

8
boa/src/syntax/ast/node/new/mod.rs

@ -56,14 +56,14 @@ impl Executable for New {
for arg in self.args() {
if let Node::Spread(ref x) = arg {
let val = x.run(context)?;
let iterator_record = iterable::get_iterator(context, val)?;
let iterator_record = iterable::get_iterator(&val, context)?;
loop {
let next = iterator_record.next(context)?;
if next.is_done() {
if next.done {
break;
}
let next_value = next.value();
v_args.push(next_value.clone());
let next_value = next.value;
v_args.push(next_value);
}
break; // after spread we don't accept any new arguments
} else {

2
boa/src/value/mod.rs

@ -867,7 +867,7 @@ impl JsValue {
Self::Undefined => "undefined",
Self::BigInt(_) => "bigint",
Self::Object(ref object) => {
if object.is_function() {
if object.is_callable() {
"function"
} else {
"object"

Loading…
Cancel
Save