diff --git a/boa/src/builtins/array/array_iterator.rs b/boa/src/builtins/array/array_iterator.rs index 641cb48dc7..be2de04be3 100644 --- a/boa/src/builtins/array/array_iterator.rs +++ b/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 diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index 80b7554206..e79683cb1a 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -2528,9 +2528,9 @@ impl Array { context: &mut Context, ) -> JsResult { 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 { Ok(ArrayIterator::create_array_iterator( - context, this.clone(), PropertyNameKind::Key, + context, )) } @@ -2568,9 +2568,9 @@ impl Array { context: &mut Context, ) -> JsResult { Ok(ArrayIterator::create_array_iterator( - context, this.clone(), PropertyNameKind::KeyAndValue, + context, )) } diff --git a/boa/src/builtins/iterable/mod.rs b/boa/src/builtins/iterable/mod.rs index b79d129d4e..a6d852d3a1 100644 --- a/boa/src/builtins/iterable/mod.rs +++ b/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 { +pub fn get_iterator(iterable: &JsValue, context: &mut Context) -> JsResult { 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, } diff --git a/boa/src/builtins/map/map_iterator.rs b/boa/src/builtins/map/map_iterator.rs index 84fc80565d..7b99905674 100644 --- a/boa/src/builtins/map/map_iterator.rs +++ b/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, 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 { - 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 { - 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 { - 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 diff --git a/boa/src/builtins/map/mod.rs b/boa/src/builtins/map/mod.rs index e1708cfe4d..31a553dd6e 100644 --- a/boa/src/builtins/map/mod.rs +++ b/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 { + // 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 { - 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 { - 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 { + // 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 { 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 { // 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 { 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 { - 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 { - 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 { - 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 { - 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> { - 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 { + // 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) } } diff --git a/boa/src/builtins/map/ordered_map.rs b/boa/src/builtins/map/ordered_map.rs index 3fbd0d6d0d..5d6d4637d6 100644 --- a/boa/src/builtins/map/ordered_map.rs +++ b/boa/src/builtins/map/ordered_map.rs @@ -142,9 +142,12 @@ impl OrderedMap { } } + /// 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 OrderedMap { 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)? { diff --git a/boa/src/builtins/number/mod.rs b/boa/src/builtins/number/mod.rs index da03083115..d5c76f90bb 100644 --- a/boa/src/builtins/number/mod.rs +++ b/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 diff --git a/boa/src/builtins/object/for_in_iterator.rs b/boa/src/builtins/object/for_in_iterator.rs index 4d8967f0fb..ac32a7100d 100644 --- a/boa/src/builtins/object/for_in_iterator.rs +++ b/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 diff --git a/boa/src/builtins/regexp/regexp_string_iterator.rs b/boa/src/builtins/regexp/regexp_string_iterator.rs index f896ea1f27..cc45d3a0a4 100644 --- a/boa/src/builtins/regexp/regexp_string_iterator.rs +++ b/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 diff --git a/boa/src/builtins/set/mod.rs b/boa/src/builtins/set/mod.rs index edd96e656c..971f3130e7 100644 --- a/boa/src/builtins/set/mod.rs +++ b/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, )) } diff --git a/boa/src/builtins/set/set_iterator.rs b/boa/src/builtins/set/set_iterator.rs index 52ad5cc6ab..ce012236fc 100644 --- a/boa/src/builtins/set/set_iterator.rs +++ b/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 diff --git a/boa/src/builtins/string/mod.rs b/boa/src/builtins/string/mod.rs index b280697794..303526718c 100644 --- a/boa/src/builtins/string/mod.rs +++ b/boa/src/builtins/string/mod.rs @@ -1777,7 +1777,7 @@ impl String { _: &[JsValue], context: &mut Context, ) -> JsResult { - StringIterator::create_string_iterator(context, this.clone()) + StringIterator::create_string_iterator(this.clone(), context) } } diff --git a/boa/src/builtins/string/string_iterator.rs b/boa/src/builtins/string/string_iterator.rs index 9e3ada044e..5f46c456f7 100644 --- a/boa/src/builtins/string/string_iterator.rs +++ b/boa/src/builtins/string/string_iterator.rs @@ -23,7 +23,7 @@ impl StringIterator { } } - pub fn create_string_iterator(context: &mut Context, string: JsValue) -> JsResult { + pub fn create_string_iterator(string: JsValue, context: &mut Context) -> JsResult { 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 diff --git a/boa/src/object/mod.rs b/boa/src/object/mod.rs index 8e2e82288a..c4a01fcc20 100644 --- a/boa/src/object/mod.rs +++ b/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 { diff --git a/boa/src/syntax/ast/node/array/mod.rs b/boa/src/syntax/ast/node/array/mod.rs index 484fab733c..b7ce44d05d 100644 --- a/boa/src/syntax/ast/node/array/mod.rs +++ b/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)?); diff --git a/boa/src/syntax/ast/node/call/mod.rs b/boa/src/syntax/ast/node/call/mod.rs index 3d082ffb6b..5a01310be9 100644 --- a/boa/src/syntax/ast/node/call/mod.rs +++ b/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 { diff --git a/boa/src/syntax/ast/node/declaration/mod.rs b/boa/src/syntax/ast/node/declaration/mod.rs index 1316c55c2f..4765602320 100644 --- a/boa/src/syntax/ast/node/declaration/mod.rs +++ b/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)?; } } } diff --git a/boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs b/boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs index 0b0a794481..bf08306227 100644 --- a/boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs +++ b/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() { diff --git a/boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs b/boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs index 5c8e3e9395..1cfc3fd492 100644 --- a/boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs +++ b/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 { 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() { diff --git a/boa/src/syntax/ast/node/new/mod.rs b/boa/src/syntax/ast/node/new/mod.rs index 897a3442df..c9fafe9cc8 100644 --- a/boa/src/syntax/ast/node/new/mod.rs +++ b/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 { diff --git a/boa/src/value/mod.rs b/boa/src/value/mod.rs index f3858ba1ba..6ec0a6c0e9 100644 --- a/boa/src/value/mod.rs +++ b/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"