diff --git a/boa/src/builtins/map/mod.rs b/boa/src/builtins/map/mod.rs index 9912d18844..e1708cfe4d 100644 --- a/boa/src/builtins/map/mod.rs +++ b/boa/src/builtins/map/mod.rs @@ -1,4 +1,4 @@ -//! This module implements the global `Map` objest. +//! This module implements the global `Map` object. //! //! The JavaScript `Map` class is a global object that is used in the construction of maps; which //! are high-level, key-value stores. @@ -31,6 +31,7 @@ use map_iterator::MapIterator; use self::ordered_map::MapLock; use super::JsArgs; +use num_traits::Zero; pub mod ordered_map; #[cfg(test)] @@ -248,7 +249,17 @@ impl Map { let size = if let Some(object) = this.as_object() { if let Some(map) = object.borrow_mut().as_map_mut() { - map.insert(key.clone(), value.clone()); + let key = match key { + JsValue::Rational(r) => { + if r.is_zero() { + JsValue::Rational(0f64) + } else { + key.clone() + } + } + _ => key.clone(), + }; + map.insert(key, value.clone()); map.len() } else { return Err(context.construct_type_error("'this' is not a Map")); @@ -309,6 +320,19 @@ impl Map { ) -> JsResult { let key = args.get_or_undefined(0); + const JS_ZERO: &JsValue = &JsValue::Rational(0f64); + + let key = match key { + JsValue::Rational(r) => { + if r.is_zero() { + JS_ZERO + } else { + key + } + } + _ => key, + }; + if let JsValue::Object(ref object) = this { let object = object.borrow(); if let Some(map) = object.as_map_ref() { @@ -333,11 +357,23 @@ impl Map { /// /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.clear /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear - pub(crate) fn clear(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - this.set_data(ObjectData::map(OrderedMap::new())); - + 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() { + if let Some(map) = object.borrow_mut().as_map_mut() { + map.clear(); + } else { + return context.throw_type_error("'this' is not a Map"); + } + } else { + return context.throw_type_error("'this' is not a Map"); + } Ok(JsValue::undefined()) } @@ -388,6 +424,12 @@ impl Map { } let callback_arg = &args[0]; + + if !callback_arg.is_function() { + let name = callback_arg.to_string(context)?; + return context.throw_type_error(format!("{} is not a function", name)); + } + let this_arg = args.get_or_undefined(1); let mut index = 0; diff --git a/boa/src/builtins/map/ordered_map.rs b/boa/src/builtins/map/ordered_map.rs index dc97d5c397..3fbd0d6d0d 100644 --- a/boa/src/builtins/map/ordered_map.rs +++ b/boa/src/builtins/map/ordered_map.rs @@ -142,6 +142,11 @@ impl OrderedMap { } } + pub fn clear(&mut self) { + self.map.clear(); + self.map.shrink_to_fit(); + } + /// Return a reference to the value stored for `key`, if it is present, /// else `None`. ///