From 03fc42624873421a0392fa4a63e449830912f89d Mon Sep 17 00:00:00 2001 From: croraf Date: Sun, 11 Oct 2020 20:03:56 +0200 Subject: [PATCH 01/10] Initial commit --- boa/src/builtins/array/mod.rs | 2 + boa/src/builtins/iterable/mod.rs | 11 ++- boa/src/builtins/map/map_iterator.rs | 136 +++++++++++++++++++++++++++ boa/src/builtins/map/mod.rs | 18 ++++ boa/src/builtins/mod.rs | 1 + boa/src/object/mod.rs | 11 +++ 6 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 boa/src/builtins/map/map_iterator.rs diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index 999ec1f8c35..79bfc179b0e 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -38,12 +38,14 @@ impl BuiltIn for Array { let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); let symbol_iterator = context.well_known_symbols().iterator_symbol(); + let values_function = FunctionBuilder::new(context, Self::values) .name("values") .length(0) .callable(true) .constructable(false) .build(); + let array = ConstructorBuilder::with_standard_object( context, Self::constructor, diff --git a/boa/src/builtins/iterable/mod.rs b/boa/src/builtins/iterable/mod.rs index 1680f0f9f15..c50c15ba3b9 100644 --- a/boa/src/builtins/iterable/mod.rs +++ b/boa/src/builtins/iterable/mod.rs @@ -1,6 +1,7 @@ use crate::{ builtins::string::string_iterator::StringIterator, builtins::ArrayIterator, + builtins::MapIterator, object::{GcObject, ObjectInitializer}, property::{Attribute, DataDescriptor}, BoaProfiler, Context, Result, Value, @@ -11,6 +12,7 @@ pub struct IteratorPrototypes { iterator_prototype: GcObject, array_iterator: GcObject, string_iterator: GcObject, + map_iterator: GcObject, } impl IteratorPrototypes { @@ -23,9 +25,12 @@ impl IteratorPrototypes { array_iterator: ArrayIterator::create_prototype(ctx, iterator_prototype.clone()) .as_gc_object() .expect("Array Iterator Prototype is not an object"), - string_iterator: StringIterator::create_prototype(ctx, iterator_prototype) + string_iterator: StringIterator::create_prototype(ctx, iterator_prototype.clone()) .as_gc_object() .expect("String Iterator Prototype is not an object"), + map_iterator: MapIterator::create_prototype(ctx, iterator_prototype.clone()) + .as_gc_object() + .expect("Map Iterator Prototype is not an object"), } } @@ -40,6 +45,10 @@ impl IteratorPrototypes { pub fn string_iterator(&self) -> GcObject { self.string_iterator.clone() } + + pub fn map_iterator(&self) -> GcObject { + self.map_iterator.clone() + } } /// CreateIterResultObject( value, done ) diff --git a/boa/src/builtins/map/map_iterator.rs b/boa/src/builtins/map/map_iterator.rs new file mode 100644 index 00000000000..ba61d38b8ea --- /dev/null +++ b/boa/src/builtins/map/map_iterator.rs @@ -0,0 +1,136 @@ +use crate::{ + builtins::{function::make_builtin_fn, iterable::create_iter_result_object, Map, Value}, + object::ObjectData, + property::{Attribute, DataDescriptor}, + BoaProfiler, Context, Result, +}; +use gc::{Finalize, Trace}; + +#[derive(Debug, Clone, Finalize, Trace)] +pub enum MapIterationKind { + Key, + Value, + KeyAndValue, +} + +/// The Map Iterator object represents an iteration over a map. It implements the iterator protocol. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: TODO https://tc39.es/ecma262/#sec-array-iterator-objects +#[derive(Debug, Clone, Finalize, Trace)] +pub struct MapIterator { + map: Value, + next_index: u32, + kind: MapIterationKind, +} + +impl MapIterator { + pub(crate) const NAME: &'static str = "MapIterator"; + + fn new(map: Value, kind: MapIterationKind) -> Self { + MapIterator { + map, + kind, + next_index: 0, + } + } + + /// Abstract operation CreateMapIterator( map, kind ) + /// + /// Creates a new iterator over the given map. + /// + /// More information: + /// - [ECMA reference][spec] + /// + /// [spec]: https://www.ecma-international.org/ecma-262/11.0/index.html#sec-createmapiterator + pub(crate) fn create_map_iterator( + ctx: &Context, + map: Value, + kind: MapIterationKind, + ) -> Result { + let map_iterator = Value::new_object(Some(ctx.global_object())); + map_iterator.set_data(ObjectData::MapIterator(Self::new(map, kind))); + map_iterator + .as_object_mut() + .expect("map iterator object") + .set_prototype_instance(ctx.iterator_prototypes().map_iterator().into()); + Ok(map_iterator) + } + + + /// %MapIteratorPrototype%.next( ) + /// + /// Gets the next result in the map. + /// + /// More information: + /// - [ECMA reference][spec] + /// + /// [spec]: TODO https://tc39.es/ecma262/#sec-%arrayiteratorprototype%.next + pub(crate) fn next(this: &Value, _args: &[Value], ctx: &mut Context) -> Result { + if let Value::Object(ref object) = this { + let mut object = object.borrow_mut(); + if let Some(map_iterator) = object.as_map_iterator_mut() { + let index = map_iterator.next_index; + if map_iterator.map.is_undefined() { + return Ok(create_iter_result_object(ctx, Value::undefined(), true)); + } + let len = map_iterator + .map + .get_field("length") + .as_number() + .ok_or_else(|| ctx.construct_type_error("Not a map"))? as u32; + if map_iterator.next_index >= len { + map_iterator.map = Value::undefined(); + return Ok(create_iter_result_object(ctx, Value::undefined(), true)); + } + map_iterator.next_index = index + 1; + match map_iterator.kind { + MapIterationKind::Key => Ok(create_iter_result_object(ctx, index.into(), false)), + MapIterationKind::Value => { + let element_value = map_iterator.map.get_field(index); + Ok(create_iter_result_object(ctx, element_value, false)) + } + MapIterationKind::KeyAndValue => { + let element_value = map_iterator.map.get_field(index); + let result = Map::constructor( + &Value::new_object(Some(ctx.global_object())), + &[index.into(), element_value], + ctx, + )?; + Ok(create_iter_result_object(ctx, result, false)) + } + } + } else { + ctx.throw_type_error("`this` is not an MapIterator") + } + } else { + ctx.throw_type_error("`this` is not an MapIterator") + } + } + + /// Create the %MapIteratorPrototype% object + /// + /// More information: + /// - [ECMA reference][spec] + /// + /// [spec]: TODO https://tc39.es/ecma262/#sec-%arrayiteratorprototype%-object + pub(crate) fn create_prototype(ctx: &mut Context, iterator_prototype: Value) -> Value { + let global = ctx.global_object(); + let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); + + // Create prototype + let map_iterator = Value::new_object(Some(global)); + make_builtin_fn(Self::next, "next", &map_iterator, 0, ctx); + map_iterator + .as_object_mut() + .expect("map iterator prototype object") + .set_prototype_instance(iterator_prototype); + + let to_string_tag = ctx.well_known_symbols().to_string_tag_symbol(); + let to_string_tag_property = DataDescriptor::new("Map Iterator", Attribute::CONFIGURABLE); + map_iterator.set_property(to_string_tag, to_string_tag_property); + map_iterator + } +} diff --git a/boa/src/builtins/map/mod.rs b/boa/src/builtins/map/mod.rs index 632da4de0e8..c5d1d5905ce 100644 --- a/boa/src/builtins/map/mod.rs +++ b/boa/src/builtins/map/mod.rs @@ -8,6 +8,9 @@ use crate::{ }; use ordered_map::OrderedMap; +pub mod map_iterator; +use map_iterator::{MapIterator, MapIterationKind}; + pub mod ordered_map; #[cfg(test)] mod tests; @@ -29,6 +32,7 @@ impl BuiltIn for Map { .name(Self::NAME) .length(Self::LENGTH) .method(Self::set, "set", 2) + .method(Self::entries, "entries", 0) .method(Self::delete, "delete", 1) .method(Self::get, "get", 1) .method(Self::clear, "clear", 0) @@ -98,6 +102,20 @@ impl Map { Ok(this.clone()) } + /// `Map.prototype.entries()` + /// + /// Returns a new Iterator object that contains the [key, value] pairs for each element in the Map object in insertion order. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://www.ecma-international.org/ecma-262/11.0/index.html#sec-map.prototype.entries + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries + pub(crate) fn entries(this: &Value, _: &[Value], ctx: &mut Context) -> Result { + MapIterator::create_map_iterator(ctx, this.clone(), MapIterationKind::KeyAndValue) + } + /// Helper function to set the size property. fn set_size(this: &Value, size: usize) { let size = DataDescriptor::new( diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index 89096330770..ae5af7f269e 100644 --- a/boa/src/builtins/mod.rs +++ b/boa/src/builtins/mod.rs @@ -32,6 +32,7 @@ pub(crate) use self::{ global_this::GlobalThis, infinity::Infinity, json::Json, + map::map_iterator::MapIterator, map::Map, math::Math, nan::NaN, diff --git a/boa/src/object/mod.rs b/boa/src/object/mod.rs index 65b72cd8eea..480db90c337 100644 --- a/boa/src/object/mod.rs +++ b/boa/src/object/mod.rs @@ -4,6 +4,7 @@ use crate::{ builtins::{ array::array_iterator::ArrayIterator, function::{BuiltInFunction, Function, FunctionFlags, NativeFunction}, + map::map_iterator::MapIterator, map::ordered_map::OrderedMap, string::string_iterator::StringIterator, BigInt, Date, RegExp, @@ -76,6 +77,7 @@ pub enum ObjectData { Array, ArrayIterator(ArrayIterator), Map(OrderedMap), + MapIterator(MapIterator), RegExp(Box), BigInt(RcBigInt), Boolean(bool), @@ -102,6 +104,7 @@ impl Display for ObjectData { Self::Function(_) => "Function", Self::RegExp(_) => "RegExp", Self::Map(_) => "Map", + Self::MapIterator(_) => "MapIterator", Self::String(_) => "String", Self::StringIterator(_) => "StringIterator", Self::Symbol(_) => "Symbol", @@ -327,6 +330,14 @@ impl Object { } } + #[inline] + pub fn as_map_iterator_mut(&mut self) -> Option<&mut MapIterator> { + match &mut self.data { + ObjectData::MapIterator(iter) => Some(iter), + _ => None, + } + } + /// Checks if it a `String` object. #[inline] pub fn is_string(&self) -> bool { From 2230589bb1d649dec3ae0fd717bc24a3984f99bb Mon Sep 17 00:00:00 2001 From: croraf Date: Tue, 13 Oct 2020 00:27:58 +0200 Subject: [PATCH 02/10] Improving on Map iterator --- boa/src/builtins/iterable/mod.rs | 2 +- boa/src/builtins/map/map_iterator.rs | 50 ++++++++++++++++------------ boa/src/value/mod.rs | 10 ++++++ 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/boa/src/builtins/iterable/mod.rs b/boa/src/builtins/iterable/mod.rs index c50c15ba3b9..0ac748bef53 100644 --- a/boa/src/builtins/iterable/mod.rs +++ b/boa/src/builtins/iterable/mod.rs @@ -28,7 +28,7 @@ impl IteratorPrototypes { string_iterator: StringIterator::create_prototype(ctx, iterator_prototype.clone()) .as_gc_object() .expect("String Iterator Prototype is not an object"), - map_iterator: MapIterator::create_prototype(ctx, iterator_prototype.clone()) + map_iterator: MapIterator::create_prototype(ctx, iterator_prototype) .as_gc_object() .expect("Map Iterator Prototype is not an object"), } diff --git a/boa/src/builtins/map/map_iterator.rs b/boa/src/builtins/map/map_iterator.rs index ba61d38b8ea..6505a172c63 100644 --- a/boa/src/builtins/map/map_iterator.rs +++ b/boa/src/builtins/map/map_iterator.rs @@ -21,9 +21,9 @@ pub enum MapIterationKind { /// [spec]: TODO https://tc39.es/ecma262/#sec-array-iterator-objects #[derive(Debug, Clone, Finalize, Trace)] pub struct MapIterator { - map: Value, - next_index: u32, - kind: MapIterationKind, + iterated_map: Value, + map_next_index: u32, + map_iteration_kind: MapIterationKind, } impl MapIterator { @@ -31,9 +31,9 @@ impl MapIterator { fn new(map: Value, kind: MapIterationKind) -> Self { MapIterator { - map, - kind, - next_index: 0, + iterated_map: map, + map_next_index: 0, + map_iteration_kind: kind, } } @@ -67,33 +67,41 @@ impl MapIterator { /// More information: /// - [ECMA reference][spec] /// - /// [spec]: TODO https://tc39.es/ecma262/#sec-%arrayiteratorprototype%.next + /// [spec]: https://tc39.es/ecma262/#sec-%mapiteratorprototype%.next pub(crate) fn next(this: &Value, _args: &[Value], ctx: &mut Context) -> Result { if let Value::Object(ref object) = this { let mut object = object.borrow_mut(); if let Some(map_iterator) = object.as_map_iterator_mut() { - let index = map_iterator.next_index; - if map_iterator.map.is_undefined() { + let m = &map_iterator.iterated_map; + let index = map_iterator.map_next_index; + let item_kind = &map_iterator.map_iteration_kind; + + if m.is_undefined() { return Ok(create_iter_result_object(ctx, Value::undefined(), true)); } - let len = map_iterator - .map - .get_field("length") - .as_number() - .ok_or_else(|| ctx.construct_type_error("Not a map"))? as u32; - if map_iterator.next_index >= len { - map_iterator.map = Value::undefined(); + + let entries = match m.get_data() { + ObjectData::Map(data) => data, + _ => {panic!("Trying to access map data on an Object that is not of ObjectData::Map type");} + }; + + let numEntries = entries.len(); + + Ok(Value::undefined()) + /* while + if map_iterator.map_next_index >= len { + map_iterator.iterated_map = Value::undefined(); return Ok(create_iter_result_object(ctx, Value::undefined(), true)); } - map_iterator.next_index = index + 1; - match map_iterator.kind { + map_iterator.map_next_index = index + 1; + match map_iterator.map_iteration_kind { MapIterationKind::Key => Ok(create_iter_result_object(ctx, index.into(), false)), MapIterationKind::Value => { - let element_value = map_iterator.map.get_field(index); + let element_value = map_iterator.iterated_map.get_field(index); Ok(create_iter_result_object(ctx, element_value, false)) } MapIterationKind::KeyAndValue => { - let element_value = map_iterator.map.get_field(index); + let element_value = map_iterator.iterated_map.get_field(index); let result = Map::constructor( &Value::new_object(Some(ctx.global_object())), &[index.into(), element_value], @@ -101,7 +109,7 @@ impl MapIterator { )?; Ok(create_iter_result_object(ctx, result, false)) } - } + } */ } else { ctx.throw_type_error("`this` is not an MapIterator") } diff --git a/boa/src/value/mod.rs b/boa/src/value/mod.rs index 132e6fec8b0..d4481acaa76 100644 --- a/boa/src/value/mod.rs +++ b/boa/src/value/mod.rs @@ -524,6 +524,16 @@ impl Value { } } + /// Get internal object data. + #[inline] + pub fn get_data(&self) -> ObjectData { + if let Self::Object(ref obj) = *self { + obj.borrow_mut().data; + } else { + panic!("Trying to access get_data on a Value that does not have it"); + } + } + /// Set the property in the value. #[inline] pub fn set_property(&self, key: K, property: P) From 8c5bd308d85120e29fa0fa1122da17ab68a65f6b Mon Sep 17 00:00:00 2001 From: croraf Date: Tue, 13 Oct 2020 02:21:38 +0200 Subject: [PATCH 03/10] Improvements on the iterator --- boa/src/builtins/map/map_iterator.rs | 236 ++++++++++++++------------- boa/src/builtins/map/ordered_map.rs | 6 + boa/src/value/mod.rs | 10 -- 3 files changed, 129 insertions(+), 123 deletions(-) diff --git a/boa/src/builtins/map/map_iterator.rs b/boa/src/builtins/map/map_iterator.rs index 6505a172c63..9cc46a6823a 100644 --- a/boa/src/builtins/map/map_iterator.rs +++ b/boa/src/builtins/map/map_iterator.rs @@ -1,16 +1,16 @@ use crate::{ - builtins::{function::make_builtin_fn, iterable::create_iter_result_object, Map, Value}, - object::ObjectData, - property::{Attribute, DataDescriptor}, - BoaProfiler, Context, Result, + builtins::{function::make_builtin_fn, iterable::create_iter_result_object, Value}, + object::ObjectData, + property::{Attribute, DataDescriptor}, + BoaProfiler, Context, Result, }; use gc::{Finalize, Trace}; #[derive(Debug, Clone, Finalize, Trace)] pub enum MapIterationKind { - Key, - Value, - KeyAndValue, + Key, + Value, + KeyAndValue, } /// The Map Iterator object represents an iteration over a map. It implements the iterator protocol. @@ -21,124 +21,134 @@ pub enum MapIterationKind { /// [spec]: TODO https://tc39.es/ecma262/#sec-array-iterator-objects #[derive(Debug, Clone, Finalize, Trace)] pub struct MapIterator { - iterated_map: Value, - map_next_index: u32, - map_iteration_kind: MapIterationKind, + iterated_map: Value, + map_next_index: usize, + map_iteration_kind: MapIterationKind, } impl MapIterator { - pub(crate) const NAME: &'static str = "MapIterator"; + pub(crate) const NAME: &'static str = "MapIterator"; - fn new(map: Value, kind: MapIterationKind) -> Self { - MapIterator { - iterated_map: map, - map_next_index: 0, - map_iteration_kind: kind, + fn new(map: Value, kind: MapIterationKind) -> Self { + MapIterator { + iterated_map: map, + map_next_index: 0, + map_iteration_kind: kind, + } } - } - - /// Abstract operation CreateMapIterator( map, kind ) - /// - /// Creates a new iterator over the given map. - /// - /// More information: - /// - [ECMA reference][spec] - /// - /// [spec]: https://www.ecma-international.org/ecma-262/11.0/index.html#sec-createmapiterator - pub(crate) fn create_map_iterator( - ctx: &Context, - map: Value, - kind: MapIterationKind, - ) -> Result { - let map_iterator = Value::new_object(Some(ctx.global_object())); - map_iterator.set_data(ObjectData::MapIterator(Self::new(map, kind))); - map_iterator - .as_object_mut() - .expect("map iterator object") - .set_prototype_instance(ctx.iterator_prototypes().map_iterator().into()); - Ok(map_iterator) - } - - - /// %MapIteratorPrototype%.next( ) - /// - /// Gets the next result in the map. - /// - /// More information: - /// - [ECMA reference][spec] - /// - /// [spec]: https://tc39.es/ecma262/#sec-%mapiteratorprototype%.next - pub(crate) fn next(this: &Value, _args: &[Value], ctx: &mut Context) -> Result { - if let Value::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 index = map_iterator.map_next_index; - let item_kind = &map_iterator.map_iteration_kind; - if m.is_undefined() { - return Ok(create_iter_result_object(ctx, Value::undefined(), true)); - } + /// Abstract operation CreateMapIterator( map, kind ) + /// + /// Creates a new iterator over the given map. + /// + /// More information: + /// - [ECMA reference][spec] + /// + /// [spec]: https://www.ecma-international.org/ecma-262/11.0/index.html#sec-createmapiterator + pub(crate) fn create_map_iterator( + ctx: &Context, + map: Value, + kind: MapIterationKind, + ) -> Result { + let map_iterator = Value::new_object(Some(ctx.global_object())); + map_iterator.set_data(ObjectData::MapIterator(Self::new(map, kind))); + map_iterator + .as_object_mut() + .expect("map iterator object") + .set_prototype_instance(ctx.iterator_prototypes().map_iterator().into()); + Ok(map_iterator) + } - let entries = match m.get_data() { - ObjectData::Map(data) => data, - _ => {panic!("Trying to access map data on an Object that is not of ObjectData::Map type");} - }; + /// %MapIteratorPrototype%.next( ) + /// + /// Gets the next result in the map. + /// + /// More information: + /// - [ECMA reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-%mapiteratorprototype%.next + pub(crate) fn next(this: &Value, _args: &[Value], ctx: &mut Context) -> Result { + if let Value::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 index = map_iterator.map_next_index; + let item_kind = &map_iterator.map_iteration_kind; - let numEntries = entries.len(); + if m.is_undefined() { + return Ok(create_iter_result_object(ctx, Value::undefined(), true)); + } - Ok(Value::undefined()) - /* while - if map_iterator.map_next_index >= len { - map_iterator.iterated_map = Value::undefined(); - return Ok(create_iter_result_object(ctx, Value::undefined(), true)); + if let Value::Object(ref object) = m { + if let Some(entries) = object.borrow().as_map_ref() { + let num_entries = entries.len(); + while index < num_entries { + let e = entries.get_index(index); + index += 1; + map_iterator.map_next_index = index; + // TODO handle itemKind and empty e.[[Key]] + let (_, result) = e.unwrap(); + return Ok(create_iter_result_object(ctx, result, false)); + } + map_iterator.iterated_map = Value::undefined(); + return Ok(create_iter_result_object(ctx, Value::undefined(), true)); + } else { + return Err(ctx.construct_type_error("'this' is not a Map")); + } + } else { + return Err(ctx.construct_type_error("'this' is not a Map")); + }; + /* while + if map_iterator.map_next_index >= len { + map_iterator.iterated_map = Value::undefined(); + return Ok(create_iter_result_object(ctx, Value::undefined(), true)); + } + map_iterator.map_next_index = index + 1; + match map_iterator.map_iteration_kind { + MapIterationKind::Key => Ok(create_iter_result_object(ctx, index.into(), false)), + MapIterationKind::Value => { + let element_value = map_iterator.iterated_map.get_field(index); + Ok(create_iter_result_object(ctx, element_value, false)) + } + MapIterationKind::KeyAndValue => { + let element_value = map_iterator.iterated_map.get_field(index); + let result = Map::constructor( + &Value::new_object(Some(ctx.global_object())), + &[index.into(), element_value], + ctx, + )?; + Ok(create_iter_result_object(ctx, result, false)) + } + } */ + } else { + ctx.throw_type_error("`this` is not an MapIterator") + } + } else { + ctx.throw_type_error("`this` is not an MapIterator") } - map_iterator.map_next_index = index + 1; - match map_iterator.map_iteration_kind { - MapIterationKind::Key => Ok(create_iter_result_object(ctx, index.into(), false)), - MapIterationKind::Value => { - let element_value = map_iterator.iterated_map.get_field(index); - Ok(create_iter_result_object(ctx, element_value, false)) - } - MapIterationKind::KeyAndValue => { - let element_value = map_iterator.iterated_map.get_field(index); - let result = Map::constructor( - &Value::new_object(Some(ctx.global_object())), - &[index.into(), element_value], - ctx, - )?; - Ok(create_iter_result_object(ctx, result, false)) - } - } */ - } else { - ctx.throw_type_error("`this` is not an MapIterator") - } - } else { - ctx.throw_type_error("`this` is not an MapIterator") } - } - /// Create the %MapIteratorPrototype% object - /// - /// More information: - /// - [ECMA reference][spec] - /// - /// [spec]: TODO https://tc39.es/ecma262/#sec-%arrayiteratorprototype%-object - pub(crate) fn create_prototype(ctx: &mut Context, iterator_prototype: Value) -> Value { - let global = ctx.global_object(); - let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); + /// Create the %MapIteratorPrototype% object + /// + /// More information: + /// - [ECMA reference][spec] + /// + /// [spec]: TODO https://tc39.es/ecma262/#sec-%arrayiteratorprototype%-object + pub(crate) fn create_prototype(ctx: &mut Context, iterator_prototype: Value) -> Value { + let global = ctx.global_object(); + let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); - // Create prototype - let map_iterator = Value::new_object(Some(global)); - make_builtin_fn(Self::next, "next", &map_iterator, 0, ctx); - map_iterator - .as_object_mut() - .expect("map iterator prototype object") - .set_prototype_instance(iterator_prototype); + // Create prototype + let map_iterator = Value::new_object(Some(global)); + make_builtin_fn(Self::next, "next", &map_iterator, 0, ctx); + map_iterator + .as_object_mut() + .expect("map iterator prototype object") + .set_prototype_instance(iterator_prototype); - let to_string_tag = ctx.well_known_symbols().to_string_tag_symbol(); - let to_string_tag_property = DataDescriptor::new("Map Iterator", Attribute::CONFIGURABLE); - map_iterator.set_property(to_string_tag, to_string_tag_property); - map_iterator - } + let to_string_tag = ctx.well_known_symbols().to_string_tag_symbol(); + let to_string_tag_property = DataDescriptor::new("Map Iterator", Attribute::CONFIGURABLE); + map_iterator.set_property(to_string_tag, to_string_tag_property); + map_iterator + } } diff --git a/boa/src/builtins/map/ordered_map.rs b/boa/src/builtins/map/ordered_map.rs index d29f9b556b2..63317606043 100644 --- a/boa/src/builtins/map/ordered_map.rs +++ b/boa/src/builtins/map/ordered_map.rs @@ -94,6 +94,12 @@ where self.0.get(key) } + /// TODO description + /// TODO return Record + pub fn get_index(&self, index: usize) -> Option<(&K, &V)> { + self.0.get_index(index) + } + /// Return an iterator over the key-value pairs of the map, in their order pub fn iter(&self) -> Iter<'_, K, V> { self.0.iter() diff --git a/boa/src/value/mod.rs b/boa/src/value/mod.rs index d4481acaa76..132e6fec8b0 100644 --- a/boa/src/value/mod.rs +++ b/boa/src/value/mod.rs @@ -524,16 +524,6 @@ impl Value { } } - /// Get internal object data. - #[inline] - pub fn get_data(&self) -> ObjectData { - if let Self::Object(ref obj) = *self { - obj.borrow_mut().data; - } else { - panic!("Trying to access get_data on a Value that does not have it"); - } - } - /// Set the property in the value. #[inline] pub fn set_property(&self, key: K, property: P) From cc73db75829ed9554a42748acbfa0e34677e38b6 Mon Sep 17 00:00:00 2001 From: croraf Date: Tue, 13 Oct 2020 03:50:50 +0200 Subject: [PATCH 04/10] Almost finish the next method of MapIterator --- boa/src/builtins/map/map_iterator.rs | 41 ++++++++-------------------- 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/boa/src/builtins/map/map_iterator.rs b/boa/src/builtins/map/map_iterator.rs index 9cc46a6823a..cc567549e7b 100644 --- a/boa/src/builtins/map/map_iterator.rs +++ b/boa/src/builtins/map/map_iterator.rs @@ -72,10 +72,10 @@ impl MapIterator { let mut object = object.borrow_mut(); if let Some(map_iterator) = object.as_map_iterator_mut() { let m = &map_iterator.iterated_map; - let index = map_iterator.map_next_index; + let mut index = map_iterator.map_next_index; let item_kind = &map_iterator.map_iteration_kind; - if m.is_undefined() { + if map_iterator.iterated_map.is_undefined() { return Ok(create_iter_result_object(ctx, Value::undefined(), true)); } @@ -86,40 +86,21 @@ impl MapIterator { let e = entries.get_index(index); index += 1; map_iterator.map_next_index = index; - // TODO handle itemKind and empty e.[[Key]] - let (_, result) = e.unwrap(); - return Ok(create_iter_result_object(ctx, result, false)); + if let Some((key, value)) = e { + // TODO result of e is a Record + // TODO handle itemKind + return Ok(create_iter_result_object(ctx, value.clone(), false)); + } } - map_iterator.iterated_map = Value::undefined(); - return Ok(create_iter_result_object(ctx, Value::undefined(), true)); } else { return Err(ctx.construct_type_error("'this' is not a Map")); } } else { return Err(ctx.construct_type_error("'this' is not a Map")); - }; - /* while - if map_iterator.map_next_index >= len { - map_iterator.iterated_map = Value::undefined(); - return Ok(create_iter_result_object(ctx, Value::undefined(), true)); - } - map_iterator.map_next_index = index + 1; - match map_iterator.map_iteration_kind { - MapIterationKind::Key => Ok(create_iter_result_object(ctx, index.into(), false)), - MapIterationKind::Value => { - let element_value = map_iterator.iterated_map.get_field(index); - Ok(create_iter_result_object(ctx, element_value, false)) - } - MapIterationKind::KeyAndValue => { - let element_value = map_iterator.iterated_map.get_field(index); - let result = Map::constructor( - &Value::new_object(Some(ctx.global_object())), - &[index.into(), element_value], - ctx, - )?; - Ok(create_iter_result_object(ctx, result, false)) - } - } */ + } + + map_iterator.iterated_map = Value::undefined(); + Ok(create_iter_result_object(ctx, Value::undefined(), true)) } else { ctx.throw_type_error("`this` is not an MapIterator") } From 63d11ae928bd6f76cad462fdfa6a443158b10514 Mon Sep 17 00:00:00 2001 From: croraf Date: Tue, 13 Oct 2020 04:34:56 +0200 Subject: [PATCH 05/10] Add different kinds to next --- boa/src/builtins/map/map_iterator.rs | 31 +++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/boa/src/builtins/map/map_iterator.rs b/boa/src/builtins/map/map_iterator.rs index cc567549e7b..2ef1471cdfb 100644 --- a/boa/src/builtins/map/map_iterator.rs +++ b/boa/src/builtins/map/map_iterator.rs @@ -1,5 +1,5 @@ use crate::{ - builtins::{function::make_builtin_fn, iterable::create_iter_result_object, Value}, + builtins::{function::make_builtin_fn, iterable::create_iter_result_object, Array, Value}, object::ObjectData, property::{Attribute, DataDescriptor}, BoaProfiler, Context, Result, @@ -61,7 +61,7 @@ impl MapIterator { /// %MapIteratorPrototype%.next( ) /// - /// Gets the next result in the map. + /// Advances the iterator and gets the next result in the map. /// /// More information: /// - [ECMA reference][spec] @@ -87,9 +87,30 @@ impl MapIterator { index += 1; map_iterator.map_next_index = index; if let Some((key, value)) = e { - // TODO result of e is a Record - // TODO handle itemKind - return Ok(create_iter_result_object(ctx, value.clone(), false)); + // TODO result of e is a Record, check impact + match item_kind { + MapIterationKind::Key => { + return Ok(create_iter_result_object( + ctx, + key.clone(), + false, + )); + } + MapIterationKind::Value => { + return Ok(create_iter_result_object( + ctx, + value.clone(), + false, + )); + } + MapIterationKind::KeyAndValue => { + let result = Array::construct_array( + &Array::new_array(ctx)?, + &[key.clone(), value.clone()], + )?; + return Ok(create_iter_result_object(ctx, result, false)); + } + } } } } else { From 5dcea4f27f36536e8b3862f59d133fd5fa8235d5 Mon Sep 17 00:00:00 2001 From: croraf Date: Tue, 13 Oct 2020 05:04:49 +0200 Subject: [PATCH 06/10] fmt --- boa/src/builtins/map/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boa/src/builtins/map/mod.rs b/boa/src/builtins/map/mod.rs index c5d1d5905ce..0987e8e68b8 100644 --- a/boa/src/builtins/map/mod.rs +++ b/boa/src/builtins/map/mod.rs @@ -9,7 +9,7 @@ use crate::{ use ordered_map::OrderedMap; pub mod map_iterator; -use map_iterator::{MapIterator, MapIterationKind}; +use map_iterator::{MapIterationKind, MapIterator}; pub mod ordered_map; #[cfg(test)] From d744c337dc4e68f46407bb5c63015afb30bc76a2 Mon Sep 17 00:00:00 2001 From: croraf Date: Tue, 13 Oct 2020 16:37:54 +0200 Subject: [PATCH 07/10] Add function description. Add test. --- boa/src/builtins/map/ordered_map.rs | 8 +++++-- boa/src/builtins/map/tests.rs | 35 +++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/boa/src/builtins/map/ordered_map.rs b/boa/src/builtins/map/ordered_map.rs index 63317606043..92b5b7efb04 100644 --- a/boa/src/builtins/map/ordered_map.rs +++ b/boa/src/builtins/map/ordered_map.rs @@ -94,8 +94,12 @@ where self.0.get(key) } - /// TODO description - /// TODO return Record + /// Get a key-value pair by index + /// Valid indices are 0 <= index < self.len() + /// Computes in O(1) time. + /// + /// TODO perhaps return Record as per + /// https://tc39.es/ecma262/#sec-%mapiteratorprototype%.next bullet 12.a. pub fn get_index(&self, index: usize) -> Option<(&K, &V)> { self.0.get_index(index) } diff --git a/boa/src/builtins/map/tests.rs b/boa/src/builtins/map/tests.rs index fa714cc896d..82d335e5673 100644 --- a/boa/src/builtins/map/tests.rs +++ b/boa/src/builtins/map/tests.rs @@ -43,6 +43,41 @@ fn clone() { assert_eq!(result, "2"); } +#[test] +fn entries() { + let mut engine = Context::new(); + let init = r#" + const map1 = new Map(); + map1.set('0', 'foo'); + map1.set(1, 'bar'); + const entriesIterator = map1.entries(); + let item1 = entriesIterator.next(); + let item2 = entriesIterator.next(); + let item3 = entriesIterator.next(); + "#; + forward(&mut engine, init); + let result = forward(&mut engine, "item1.value.length"); + assert_eq!(result, "2"); + let result = forward(&mut engine, "item1.value[0]"); + assert_eq!(result, "\"0\""); + let result = forward(&mut engine, "item1.value[1]"); + assert_eq!(result, "\"foo\""); + let result = forward(&mut engine, "item1.done"); + assert_eq!(result, "false"); + let result = forward(&mut engine, "item2.value.length"); + assert_eq!(result, "2"); + let result = forward(&mut engine, "item2.value[0]"); + assert_eq!(result, "1"); + let result = forward(&mut engine, "item2.value[1]"); + assert_eq!(result, "\"bar\""); + let result = forward(&mut engine, "item2.done"); + assert_eq!(result, "false"); + let result = forward(&mut engine, "item3.value"); + assert_eq!(result, "undefined"); + let result = forward(&mut engine, "item3.done"); + assert_eq!(result, "true"); +} + // TODO depends on the https://github.com/boa-dev/boa/issues/810 #[test] #[ignore] From 10cf5e1456078bbac6e560696aacb1f1172038fb Mon Sep 17 00:00:00 2001 From: croraf Date: Tue, 13 Oct 2020 18:39:55 +0200 Subject: [PATCH 08/10] Added symbol_iterator method. Refactor to use exactly the same function as "entries". Added test for it, unignored pending test. --- boa/src/builtins/map/mod.rs | 22 ++++++++++++++++++-- boa/src/builtins/map/tests.rs | 38 +++++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/boa/src/builtins/map/mod.rs b/boa/src/builtins/map/mod.rs index 0987e8e68b8..3bfe6de9c7b 100644 --- a/boa/src/builtins/map/mod.rs +++ b/boa/src/builtins/map/mod.rs @@ -2,7 +2,7 @@ use crate::{ builtins::BuiltIn, - object::{ConstructorBuilder, ObjectData, PROTOTYPE}, + object::{ConstructorBuilder, FunctionBuilder, ObjectData, PROTOTYPE}, property::{Attribute, DataDescriptor}, BoaProfiler, Context, Result, Value, }; @@ -28,11 +28,29 @@ impl BuiltIn for Map { fn init(context: &mut Context) -> (&'static str, Value, Attribute) { let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); + let iterator_symbol = context.well_known_symbols().iterator_symbol(); + + let entries_function = FunctionBuilder::new(context, Self::entries) + .name("entries") + .length(0) + .callable(true) + .constructable(false) + .build(); + let map_object = ConstructorBuilder::new(context, Self::constructor) .name(Self::NAME) .length(Self::LENGTH) + .property( + "entries", + entries_function.clone(), + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, + ) + .property( + iterator_symbol, + entries_function, + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, + ) .method(Self::set, "set", 2) - .method(Self::entries, "entries", 0) .method(Self::delete, "delete", 1) .method(Self::get, "get", 1) .method(Self::clear, "clear", 0) diff --git a/boa/src/builtins/map/tests.rs b/boa/src/builtins/map/tests.rs index 82d335e5673..481d3d8c1c6 100644 --- a/boa/src/builtins/map/tests.rs +++ b/boa/src/builtins/map/tests.rs @@ -43,6 +43,42 @@ fn clone() { assert_eq!(result, "2"); } +#[test] +fn symbol_iterator() { + let mut engine = Context::new(); + let init = r#" + const map1 = new Map(); + map1.set('0', 'foo'); + map1.set(1, 'bar'); + const iterator = map1[Symbol.iterator](); + let item1 = iterator.next(); + let item2 = iterator.next(); + let item3 = iterator.next(); + "#; + forward(&mut engine, init); + let result = forward(&mut engine, "item1.value.length"); + assert_eq!(result, "2"); + let result = forward(&mut engine, "item1.value[0]"); + assert_eq!(result, "\"0\""); + let result = forward(&mut engine, "item1.value[1]"); + assert_eq!(result, "\"foo\""); + let result = forward(&mut engine, "item1.done"); + assert_eq!(result, "false"); + let result = forward(&mut engine, "item2.value.length"); + assert_eq!(result, "2"); + let result = forward(&mut engine, "item2.value[0]"); + assert_eq!(result, "1"); + let result = forward(&mut engine, "item2.value[1]"); + assert_eq!(result, "\"bar\""); + let result = forward(&mut engine, "item2.done"); + assert_eq!(result, "false"); + let result = forward(&mut engine, "item3.value"); + assert_eq!(result, "undefined"); + let result = forward(&mut engine, "item3.done"); + assert_eq!(result, "true"); +} + +// Should behave the same as symbol_iterator #[test] fn entries() { let mut engine = Context::new(); @@ -78,9 +114,7 @@ fn entries() { assert_eq!(result, "true"); } -// TODO depends on the https://github.com/boa-dev/boa/issues/810 #[test] -#[ignore] fn merge() { let mut engine = Context::new(); let init = r#" From 19935ef0d545ee7de0ef56cd7ea7c48b3050f685 Mon Sep 17 00:00:00 2001 From: croraf Date: Wed, 14 Oct 2020 14:34:46 +0200 Subject: [PATCH 09/10] Remove TODOs --- boa/src/builtins/map/map_iterator.rs | 3 +-- boa/src/builtins/map/ordered_map.rs | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/boa/src/builtins/map/map_iterator.rs b/boa/src/builtins/map/map_iterator.rs index 2ef1471cdfb..e27a76e689b 100644 --- a/boa/src/builtins/map/map_iterator.rs +++ b/boa/src/builtins/map/map_iterator.rs @@ -87,7 +87,6 @@ impl MapIterator { index += 1; map_iterator.map_next_index = index; if let Some((key, value)) = e { - // TODO result of e is a Record, check impact match item_kind { MapIterationKind::Key => { return Ok(create_iter_result_object( @@ -135,7 +134,7 @@ impl MapIterator { /// More information: /// - [ECMA reference][spec] /// - /// [spec]: TODO https://tc39.es/ecma262/#sec-%arrayiteratorprototype%-object + /// [spec]: https://tc39.es/ecma262/#sec-%mapiteratorprototype%-object pub(crate) fn create_prototype(ctx: &mut Context, iterator_prototype: Value) -> Value { let global = ctx.global_object(); let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); diff --git a/boa/src/builtins/map/ordered_map.rs b/boa/src/builtins/map/ordered_map.rs index 92b5b7efb04..e1bbeafc710 100644 --- a/boa/src/builtins/map/ordered_map.rs +++ b/boa/src/builtins/map/ordered_map.rs @@ -97,9 +97,6 @@ where /// Get a key-value pair by index /// Valid indices are 0 <= index < self.len() /// Computes in O(1) time. - /// - /// TODO perhaps return Record as per - /// https://tc39.es/ecma262/#sec-%mapiteratorprototype%.next bullet 12.a. pub fn get_index(&self, index: usize) -> Option<(&K, &V)> { self.0.get_index(index) } From 51bce4b1b7aebe2566dd0f5d5217ecbac21f3dca Mon Sep 17 00:00:00 2001 From: croraf Date: Wed, 14 Oct 2020 20:56:52 +0200 Subject: [PATCH 10/10] [exec Map] keys and values prototype methods --- boa/src/builtins/map/mod.rs | 30 +++++++++++++++++++ boa/src/builtins/map/tests.rs | 54 +++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/boa/src/builtins/map/mod.rs b/boa/src/builtins/map/mod.rs index 3bfe6de9c7b..f60f0ea56d5 100644 --- a/boa/src/builtins/map/mod.rs +++ b/boa/src/builtins/map/mod.rs @@ -50,12 +50,14 @@ impl BuiltIn for Map { entries_function, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, ) + .method(Self::keys, "keys", 0) .method(Self::set, "set", 2) .method(Self::delete, "delete", 1) .method(Self::get, "get", 1) .method(Self::clear, "clear", 0) .method(Self::has, "has", 1) .method(Self::for_each, "forEach", 1) + .method(Self::values, "values", 0) .callable(false) .build(); @@ -134,6 +136,20 @@ impl Map { MapIterator::create_map_iterator(ctx, this.clone(), MapIterationKind::KeyAndValue) } + /// `Map.prototype.keys()` + /// + /// Returns a new Iterator object that contains the keys for each element in the Map object in insertion order. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [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: &Value, _: &[Value], ctx: &mut Context) -> Result { + MapIterator::create_map_iterator(ctx, this.clone(), MapIterationKind::Key) + } + /// Helper function to set the size property. fn set_size(this: &Value, size: usize) { let size = DataDescriptor::new( @@ -321,6 +337,20 @@ impl Map { Ok(Value::Undefined) } + /// `Map.prototype.values()` + /// + /// Returns a new Iterator object that contains the values for each element in the Map object in insertion order. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.values + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/values + pub(crate) fn values(this: &Value, _: &[Value], ctx: &mut Context) -> Result { + MapIterator::create_map_iterator(ctx, this.clone(), MapIterationKind::Value) + } + /// Helper function to get a key-value pair from an array. fn get_key_value(value: &Value) -> Option<(Value, Value)> { if let Value::Object(object) = value { diff --git a/boa/src/builtins/map/tests.rs b/boa/src/builtins/map/tests.rs index 481d3d8c1c6..1a145cdc311 100644 --- a/boa/src/builtins/map/tests.rs +++ b/boa/src/builtins/map/tests.rs @@ -210,6 +210,33 @@ fn has() { assert_eq!(result, "false"); } +#[test] +fn keys() { + let mut engine = Context::new(); + let init = r#" + const map1 = new Map(); + map1.set('0', 'foo'); + map1.set(1, 'bar'); + const keysIterator = map1.keys(); + let item1 = keysIterator.next(); + let item2 = keysIterator.next(); + let item3 = keysIterator.next(); + "#; + forward(&mut engine, init); + let result = forward(&mut engine, "item1.value"); + assert_eq!(result, "\"0\""); + let result = forward(&mut engine, "item1.done"); + assert_eq!(result, "false"); + let result = forward(&mut engine, "item2.value"); + assert_eq!(result, "1"); + let result = forward(&mut engine, "item2.done"); + assert_eq!(result, "false"); + let result = forward(&mut engine, "item3.value"); + assert_eq!(result, "undefined"); + let result = forward(&mut engine, "item3.done"); + assert_eq!(result, "true"); +} + #[test] fn for_each() { let mut engine = Context::new(); @@ -231,6 +258,33 @@ fn for_each() { assert_eq!(forward(&mut engine, "sizeSum"), "9"); } +#[test] +fn values() { + let mut engine = Context::new(); + let init = r#" + const map1 = new Map(); + map1.set('0', 'foo'); + map1.set(1, 'bar'); + const valuesIterator = map1.values(); + let item1 = valuesIterator.next(); + let item2 = valuesIterator.next(); + let item3 = valuesIterator.next(); + "#; + forward(&mut engine, init); + let result = forward(&mut engine, "item1.value"); + assert_eq!(result, "\"foo\""); + let result = forward(&mut engine, "item1.done"); + assert_eq!(result, "false"); + let result = forward(&mut engine, "item2.value"); + assert_eq!(result, "\"bar\""); + let result = forward(&mut engine, "item2.done"); + assert_eq!(result, "false"); + let result = forward(&mut engine, "item3.value"); + assert_eq!(result, "undefined"); + let result = forward(&mut engine, "item3.done"); + assert_eq!(result, "true"); +} + #[test] fn modify_key() { let mut engine = Context::new();