From 3b3ac97f20a1b402d6bf2d985c98fe62f58e8dbf Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Tue, 4 Aug 2020 15:06:21 +0200 Subject: [PATCH 01/27] Added index PropertyKey --- boa/src/builtins/array/mod.rs | 108 ++++++++-------- boa/src/builtins/boolean/tests.rs | 1 + boa/src/builtins/function/mod.rs | 9 +- boa/src/builtins/json/mod.rs | 28 +++-- boa/src/builtins/object/internal_methods.rs | 89 ++++++++----- boa/src/builtins/object/mod.rs | 52 +++++++- boa/src/builtins/property/mod.rs | 117 ++++++++++++++++-- boa/src/builtins/value/conversions.rs | 5 +- boa/src/builtins/value/display.rs | 4 +- boa/src/builtins/value/mod.rs | 102 ++++++--------- .../environment/global_environment_record.rs | 4 +- .../environment/object_environment_record.rs | 2 - boa/src/exec/call/mod.rs | 5 +- boa/src/exec/field/mod.rs | 6 +- boa/src/exec/tests.rs | 2 +- 15 files changed, 336 insertions(+), 198 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index 6976a17b9d2..8e54a40f160 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -71,7 +71,7 @@ impl Array { // Wipe existing contents of the array object let orig_length = i32::from(&array_obj.get_field("length")); for n in 0..orig_length { - array_obj_ptr.remove_property(&n.to_string()); + array_obj_ptr.remove_property(n); } // Create length @@ -82,7 +82,7 @@ impl Array { array_obj_ptr.set_property("length".to_string(), length); for (n, value) in array_contents.iter().enumerate() { - array_obj_ptr.set_field(n.to_string(), value); + array_obj_ptr.set_field(n, value); } Ok(array_obj_ptr) } @@ -94,7 +94,7 @@ impl Array { for (n, value) in add_values.iter().enumerate() { let new_index = orig_length.wrapping_add(n as i32); - array_ptr.set_field(new_index.to_string(), value); + array_ptr.set_field(new_index, value); } array_ptr.set_field( @@ -127,7 +127,7 @@ impl Array { length = i32::from(&args[0]); // TODO: It should not create an array of undefineds, but an empty array ("holy" array in V8) with length `n`. for n in 0..length { - this.set_field(n.to_string(), Value::undefined()); + this.set_field(n, Value::undefined()); } } 1 if args[0].is_double() => { @@ -135,7 +135,7 @@ impl Array { } _ => { for (n, value) in args.iter().enumerate() { - this.set_field(n.to_string(), value.clone()); + this.set_field(n, value.clone()); } } } @@ -197,13 +197,13 @@ impl Array { let this_length = i32::from(&this.get_field("length")); for n in 0..this_length { - new_values.push(this.get_field(n.to_string())); + new_values.push(this.get_field(n)); } for concat_array in args { let concat_length = i32::from(&concat_array.get_field("length")); for n in 0..concat_length { - new_values.push(concat_array.get_field(n.to_string())); + new_values.push(concat_array.get_field(n)); } } @@ -244,7 +244,7 @@ impl Array { } let pop_index = curr_length.wrapping_sub(1); let pop_value: Value = this.get_field(pop_index.to_string()); - this.remove_property(&pop_index.to_string()); + this.remove_property(pop_index); this.set_field("length", Value::from(pop_index)); Ok(pop_value) } @@ -274,7 +274,7 @@ impl Array { let length = i32::from(&this.get_field("length")); for i in 0..length { - let element = this.get_field(i.to_string()); + let element = this.get_field(i); let arguments = [element, Value::from(i), this.clone()]; interpreter.call(callback_arg, &this_arg, &arguments)?; @@ -306,7 +306,7 @@ impl Array { let mut elem_strs = Vec::new(); let length = i32::from(&this.get_field("length")); for n in 0..length { - let elem_str = ctx.to_string(&this.get_field(n.to_string()))?.to_string(); + let elem_str = ctx.to_string(&this.get_field(n))?.to_string(); elem_strs.push(elem_str); } @@ -373,21 +373,21 @@ impl Array { for lower in 0..middle { let upper = len.wrapping_sub(lower).wrapping_sub(1); - let upper_exists = this.has_field(&upper.to_string()); - let lower_exists = this.has_field(&lower.to_string()); + let upper_exists = this.has_field(upper); + let lower_exists = this.has_field(lower); - let upper_value = this.get_field(upper.to_string()); - let lower_value = this.get_field(lower.to_string()); + let upper_value = this.get_field(upper); + let lower_value = this.get_field(lower); if upper_exists && lower_exists { - this.set_field(upper.to_string(), lower_value); - this.set_field(lower.to_string(), upper_value); + this.set_field(upper, lower_value); + this.set_field(lower, upper_value); } else if upper_exists { - this.set_field(lower.to_string(), upper_value); - this.remove_property(&upper.to_string()); + this.set_field(lower, upper_value); + this.remove_property(upper); } else if lower_exists { - this.set_field(upper.to_string(), lower_value); - this.remove_property(&lower.to_string()); + this.set_field(upper, lower_value); + this.remove_property(lower); } } @@ -410,25 +410,25 @@ impl Array { if len == 0 { this.set_field("length", 0); // Since length is 0, this will be an Undefined value - return Ok(this.get_field(0.to_string())); + return Ok(this.get_field(0)); } - let first: Value = this.get_field(0.to_string()); + let first: Value = this.get_field(0); for k in 1..len { - let from = k.to_string(); - let to = (k.wrapping_sub(1)).to_string(); + let from = k; + let to = k.wrapping_sub(1); let from_value = this.get_field(from); if from_value.is_undefined() { - this.remove_property(&to); + this.remove_property(to); } else { this.set_field(to, from_value); } } let final_index = len.wrapping_sub(1); - this.remove_property(&(final_index).to_string()); + this.remove_property(final_index); this.set_field("length", Value::from(final_index)); Ok(first) @@ -452,19 +452,19 @@ impl Array { if arg_c > 0 { for k in (1..=len).rev() { - let from = (k.wrapping_sub(1)).to_string(); - let to = (k.wrapping_add(arg_c).wrapping_sub(1)).to_string(); + let from = k.wrapping_sub(1); + let to = k.wrapping_add(arg_c).wrapping_sub(1); let from_value = this.get_field(from); if from_value.is_undefined() { - this.remove_property(&to); + this.remove_property(to); } else { this.set_field(to, from_value); } } for j in 0..arg_c { this.set_field( - j.to_string(), + j, args.get(j as usize) .expect("Could not get argument") .clone(), @@ -510,7 +510,7 @@ impl Array { let max_len = i32::from(&this.get_field("length")); let mut len = max_len; while i < len { - let element = this.get_field(i.to_string()); + let element = this.get_field(i); let arguments = [element, Value::from(i), this.clone()]; let result = interpreter.call(callback, &this_arg, &arguments)?; if !result.to_boolean() { @@ -549,7 +549,7 @@ impl Array { let values: Vec = (0..length) .map(|idx| { - let element = this.get_field(idx.to_string()); + let element = this.get_field(idx); let args = [element, Value::from(idx), new.clone()]; interpreter @@ -603,7 +603,7 @@ impl Array { }; while idx < len { - let check_element = this.get_field(idx.to_string()).clone(); + let check_element = this.get_field(idx).clone(); if check_element.strict_equals(&search_element) { return Ok(Value::from(idx)); @@ -656,7 +656,7 @@ impl Array { }; while idx >= 0 { - let check_element = this.get_field(idx.to_string()).clone(); + let check_element = this.get_field(idx).clone(); if check_element.strict_equals(&search_element) { return Ok(Value::from(idx)); @@ -690,7 +690,7 @@ impl Array { let this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined); let len = i32::from(&this.get_field("length")); for i in 0..len { - let element = this.get_field(i.to_string()); + let element = this.get_field(i); let arguments = [element.clone(), Value::from(i), this.clone()]; let result = interpreter.call(callback, &this_arg, &arguments)?; if result.to_boolean() { @@ -730,7 +730,7 @@ impl Array { let length = i32::from(&this.get_field("length")); for i in 0..length { - let element = this.get_field(i.to_string()); + let element = this.get_field(i); let arguments = [element, Value::from(i), this.clone()]; let result = interpreter.call(predicate_arg, &this_arg, &arguments)?; @@ -777,7 +777,7 @@ impl Array { }; for i in start..fin { - this.set_field(i.to_string(), value.clone()); + this.set_field(i, value.clone()); } Ok(this.clone()) @@ -799,7 +799,7 @@ impl Array { let length = i32::from(&this.get_field("length")); for idx in 0..length { - let check_element = this.get_field(idx.to_string()).clone(); + let check_element = this.get_field(idx).clone(); if same_value_zero(&check_element, &search_element) { return Ok(Value::from(true)); @@ -854,7 +854,7 @@ impl Array { let span = max(to.wrapping_sub(from), 0); let mut new_array_len: i32 = 0; for i in from..from.wrapping_add(span) { - new_array.set_field(new_array_len.to_string(), this.get_field(i.to_string())); + new_array.set_field(new_array_len, this.get_field(i)); new_array_len = new_array_len.wrapping_add(1); } new_array.set_field("length", Value::from(new_array_len)); @@ -892,7 +892,7 @@ impl Array { let values = (0..length) .filter_map(|idx| { - let element = this.get_field(idx.to_string()); + let element = this.get_field(idx); let args = [element.clone(), Value::from(idx), new.clone()]; @@ -942,7 +942,7 @@ impl Array { let max_len = i32::from(&this.get_field("length")); let mut len = max_len; while i < len { - let element = this.get_field(i.to_string()); + let element = this.get_field(i); let arguments = [element, Value::from(i), this.clone()]; let result = interpreter.call(callback, &this_arg, &arguments)?; if result.to_boolean() { @@ -986,7 +986,7 @@ impl Array { let mut accumulator = if initial_value.is_undefined() { let mut k_present = false; while k < length { - if this.has_field(&k.to_string()) { + if this.has_field(k) { k_present = true; break; } @@ -997,20 +997,15 @@ impl Array { "Reduce was called on an empty array and with no initial value", ); } - let result = this.get_field(k.to_string()); + let result = this.get_field(k); k += 1; result } else { initial_value }; while k < length { - if this.has_field(&k.to_string()) { - let arguments = [ - accumulator, - this.get_field(k.to_string()), - Value::from(k), - this.clone(), - ]; + if this.has_field(k) { + let arguments = [accumulator, this.get_field(k), Value::from(k), this.clone()]; accumulator = interpreter.call(&callback, &Value::undefined(), &arguments)?; /* We keep track of possibly shortened length in order to prevent unnecessary iteration. It may also be necessary to do this since shortening the array length does not @@ -1059,7 +1054,7 @@ impl Array { let mut accumulator = if initial_value.is_undefined() { let mut k_present = false; loop { - if this.has_field(&k.to_string()) { + if this.has_field(k) { k_present = true; break; } @@ -1074,20 +1069,15 @@ impl Array { "reduceRight was called on an empty array and with no initial value", ); } - let result = this.get_field(k.to_string()); + let result = this.get_field(k); k -= 1; result } else { initial_value }; loop { - if this.has_field(&k.to_string()) { - let arguments = [ - accumulator, - this.get_field(k.to_string()), - Value::from(k), - this.clone(), - ]; + if this.has_field(k) { + let arguments = [accumulator, this.get_field(k), Value::from(k), this.clone()]; accumulator = interpreter.call(&callback, &Value::undefined(), &arguments)?; /* We keep track of possibly shortened length in order to prevent unnecessary iteration. It may also be necessary to do this since shortening the array length does not diff --git a/boa/src/builtins/boolean/tests.rs b/boa/src/builtins/boolean/tests.rs index c0eed466e39..c54dcb8e40d 100644 --- a/boa/src/builtins/boolean/tests.rs +++ b/boa/src/builtins/boolean/tests.rs @@ -49,6 +49,7 @@ fn constructor_gives_true_instance() { } #[test] +#[ignore] fn instances_have_correct_proto_set() { let realm = Realm::create(); let mut engine = Interpreter::new(realm); diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs index fb701dd6b10..484204159c5 100644 --- a/boa/src/builtins/function/mod.rs +++ b/boa/src/builtins/function/mod.rs @@ -14,8 +14,8 @@ use crate::{ builtins::{ object::{Object, ObjectData, PROTOTYPE}, - property::{Attribute, Property, PropertyKey}, - value::{RcString, ResultValue, Value}, + property::{Attribute, Property}, + value::{ResultValue, Value}, Array, }, environment::function_environment_record::BindingStatus, @@ -415,7 +415,7 @@ pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value { Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ); // Define length as a property - obj.define_own_property(&PropertyKey::from(RcString::from("length")), length); + obj.define_own_property("length".into(), length); let mut index: usize = 0; while index < len { let val = arguments_list.get(index).expect("Could not get argument"); @@ -424,8 +424,7 @@ pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value { Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, ); - obj.properties_mut() - .insert(RcString::from(index.to_string()), prop); + obj.insert_property(index, prop); index += 1; } diff --git a/boa/src/builtins/json/mod.rs b/boa/src/builtins/json/mod.rs index 1393b483856..764284f78b2 100644 --- a/boa/src/builtins/json/mod.rs +++ b/boa/src/builtins/json/mod.rs @@ -15,7 +15,7 @@ use crate::builtins::{ function::make_builtin_fn, - property::Property, + property::{Property, PropertyKey}, value::{ResultValue, Value}, }; use crate::{exec::Interpreter, BoaProfiler}; @@ -54,7 +54,7 @@ impl Json { Some(reviver) if reviver.is_function() => { let mut holder = Value::new_object(None); holder.set_field("", j); - Self::walk(reviver, ctx, &mut holder, Value::from("")) + Self::walk(reviver, ctx, &mut holder, &"".into()) } _ => Ok(j), } @@ -69,25 +69,30 @@ impl Json { /// for possible transformation. /// /// [polyfill]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse - fn walk(reviver: &Value, ctx: &mut Interpreter, holder: &mut Value, key: Value) -> ResultValue { + fn walk( + reviver: &Value, + ctx: &mut Interpreter, + holder: &mut Value, + key: &PropertyKey, + ) -> ResultValue { let mut value = holder.get_field(key.clone()); let obj = value.as_object().as_deref().cloned(); if let Some(obj) = obj { - for key in obj.properties().keys() { - let v = Self::walk(reviver, ctx, &mut value, Value::from(key.as_str())); + for key in obj.keys() { + let v = Self::walk(reviver, ctx, &mut value, &key); match v { Ok(v) if !v.is_undefined() => { - value.set_field(key.as_str(), v); + value.set_field(key.clone(), v); } Ok(_) => { - value.remove_property(key.as_str()); + value.remove_property(key.clone()); } Err(_v) => {} } } } - ctx.call(reviver, holder, &[key, value]) + ctx.call(reviver, holder, &[key.into(), value]) } /// `JSON.stringify( value[, replacer[, space]] )` @@ -128,7 +133,6 @@ impl Json { .map(|obj| { let object_to_return = Value::new_object(None); for (key, val) in obj - .properties() .iter() .filter_map(|(k, v)| v.value.as_ref().map(|value| (k, value))) { @@ -148,16 +152,16 @@ impl Json { } else if replacer_as_object.is_array() { let mut obj_to_return = serde_json::Map::with_capacity(replacer_as_object.properties().len() - 1); - let fields = replacer_as_object.properties().keys().filter_map(|key| { + let fields = replacer_as_object.keys().filter_map(|key| { if key == "length" { None } else { - Some(replacer.get_field(key.to_owned())) + Some(replacer.get_field(key)) } }); for field in fields { if let Some(value) = object - .get_property(&ctx.to_string(&field)?) + .get_property(ctx.to_string(&field)?) .and_then(|prop| prop.value.as_ref().map(|v| v.to_json(ctx))) .transpose()? { diff --git a/boa/src/builtins/object/internal_methods.rs b/boa/src/builtins/object/internal_methods.rs index c86aa6f7353..279dfe2bd5d 100644 --- a/boa/src/builtins/object/internal_methods.rs +++ b/boa/src/builtins/object/internal_methods.rs @@ -8,7 +8,7 @@ use crate::builtins::{ object::Object, property::{Attribute, Property, PropertyKey}, - value::{same_value, RcString, Value}, + value::{same_value, Value}, }; use crate::BoaProfiler; @@ -72,7 +72,7 @@ impl Object { return true; } if desc.configurable_or(false) { - self.remove_property(&property_key.to_string()); + self.remove_property(&property_key); return true; } @@ -96,9 +96,7 @@ impl Object { return Value::undefined(); } - let parent_obj = Object::from(&parent).expect("Failed to get object"); - - return parent_obj.get(property_key); + return parent.get_field(property_key.clone()); } if desc.is_data_descriptor() { @@ -116,11 +114,11 @@ impl Object { /// [[Set]] /// - pub fn set(&mut self, property_key: &PropertyKey, val: Value) -> bool { + pub fn set(&mut self, property_key: PropertyKey, val: Value) -> bool { let _timer = BoaProfiler::global().start_event("Object::set", "object"); // Fetch property key - let mut own_desc = self.get_own_property(property_key); + let mut own_desc = self.get_own_property(&property_key); // [2] if own_desc.is_none() { let parent = self.get_prototype_of(); @@ -158,10 +156,10 @@ impl Object { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-defineownproperty-p-desc - pub fn define_own_property(&mut self, property_key: &PropertyKey, desc: Property) -> bool { + pub fn define_own_property(&mut self, property_key: PropertyKey, desc: Property) -> bool { let _timer = BoaProfiler::global().start_event("Object::define_own_property", "object"); - let mut current = self.get_own_property(property_key); + let mut current = self.get_own_property(&property_key); let extensible = self.is_extensible(); // https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor @@ -211,6 +209,7 @@ impl Object { } self.insert_property(property_key, current); + return true; // 7 } else if current.is_data_descriptor() && desc.is_data_descriptor() { // a @@ -267,7 +266,12 @@ impl Object { // Prop could either be a String or Symbol match property_key { PropertyKey::String(ref st) => { - self.properties().get(st).map_or_else(Property::empty, |v| { + let property = if let Ok(index) = st.parse() { + self.indexed_properties.get(&index) + } else { + self.properties().get(st) + }; + property.map_or_else(Property::empty, |v| { let mut d = Property::empty(); if v.is_data_descriptor() { d.value = v.value.clone(); @@ -295,6 +299,22 @@ impl Object { d.attribute = v.attribute; d }), + PropertyKey::Index(index) => { + self.indexed_properties + .get(&index) + .map_or_else(Property::empty, |v| { + let mut d = Property::empty(); + if v.is_data_descriptor() { + d.value = v.value.clone(); + } else { + debug_assert!(v.is_accessor_descriptor()); + d.get = v.get.clone(); + d.set = v.set.clone(); + } + d.attribute = v.attribute; + d + }) + } } } @@ -352,17 +372,39 @@ impl Object { /// Helper function for property insertion. #[inline] - pub(crate) fn insert_property(&mut self, name: N, p: Property) + pub(crate) fn insert_property(&mut self, key: Key, property: Property) -> Option where - N: Into, + Key: Into, { - self.properties.insert(name.into(), p); + match key.into() { + PropertyKey::Index(index) => self.indexed_properties.insert(index, property), + PropertyKey::String(ref string) => { + if let Ok(index) = string.parse() { + self.indexed_properties.insert(index, property) + } else { + self.properties.insert(string.clone(), property) + } + } + PropertyKey::Symbol(ref symbol) => { + self.symbol_properties.insert(symbol.hash(), property) + } + } } /// Helper function for property removal. #[inline] - pub(crate) fn remove_property(&mut self, name: &str) { - self.properties.remove(name); + pub(crate) fn remove_property(&mut self, key: &PropertyKey) -> Option { + match key { + PropertyKey::Index(index) => self.indexed_properties.remove(&index), + PropertyKey::String(ref string) => { + if let Ok(index) = string.parse() { + self.indexed_properties.remove(&index) + } else { + self.properties.remove(string.as_str()) + } + } + PropertyKey::Symbol(ref symbol) => self.symbol_properties.remove(&symbol.hash()), + } } /// Inserts a field in the object `properties` without checking if it's writable. @@ -370,25 +412,16 @@ impl Object { /// If a field was already in the object with the same name that a `Some` is returned /// with that field, otherwise None is retuned. #[inline] - pub(crate) fn insert_field(&mut self, name: N, value: Value) -> Option + pub(crate) fn insert_field(&mut self, key: Key, value: Value) -> Option where - N: Into, + Key: Into, { - self.properties.insert( - name.into(), + self.insert_property( + key.into(), Property::data_descriptor( value, Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, ), ) } - - /// This function returns an Optional reference value to the objects field. - /// - /// if it exist `Some` is returned with a reference to that fields value. - /// Otherwise `None` is retuned. - #[inline] - pub fn get_field(&self, name: &str) -> Option<&Value> { - self.properties.get(name).and_then(|x| x.value.as_ref()) - } } diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 89d6af50942..1d6ae31eb25 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -17,7 +17,7 @@ use crate::{ builtins::{ function::Function, map::ordered_map::OrderedMap, - property::Property, + property::{Property, PropertyKey}, value::{RcBigInt, RcString, RcSymbol, ResultValue, Value}, BigInt, Date, RegExp, }, @@ -26,6 +26,7 @@ use crate::{ }; use gc::{Finalize, Trace}; use rustc_hash::FxHashMap; +use std::collections::hash_map; use std::fmt::{Debug, Display, Error, Formatter}; use super::function::{make_builtin_fn, make_constructor_fn}; @@ -52,6 +53,7 @@ pub static PROTOTYPE: &str = "prototype"; pub struct Object { /// The type of the object. pub data: ObjectData, + indexed_properties: FxHashMap, /// Properties properties: FxHashMap, /// Symbol Properties @@ -112,6 +114,7 @@ impl Default for Object { fn default() -> Self { Self { data: ObjectData::Ordinary, + indexed_properties: FxHashMap::default(), properties: FxHashMap::default(), symbol_properties: FxHashMap::default(), prototype: Value::null(), @@ -133,6 +136,7 @@ impl Object { Self { data: ObjectData::Function(function), + indexed_properties: FxHashMap::default(), properties: FxHashMap::default(), symbol_properties: FxHashMap::default(), prototype, @@ -158,6 +162,7 @@ impl Object { pub fn boolean(value: bool) -> Self { Self { data: ObjectData::Boolean(value), + indexed_properties: FxHashMap::default(), properties: FxHashMap::default(), symbol_properties: FxHashMap::default(), prototype: Value::null(), @@ -170,6 +175,7 @@ impl Object { pub fn number(value: f64) -> Self { Self { data: ObjectData::Number(value), + indexed_properties: FxHashMap::default(), properties: FxHashMap::default(), symbol_properties: FxHashMap::default(), prototype: Value::null(), @@ -185,6 +191,7 @@ impl Object { { Self { data: ObjectData::String(value.into()), + indexed_properties: FxHashMap::default(), properties: FxHashMap::default(), symbol_properties: FxHashMap::default(), prototype: Value::null(), @@ -197,6 +204,7 @@ impl Object { pub fn bigint(value: RcBigInt) -> Self { Self { data: ObjectData::BigInt(value), + indexed_properties: FxHashMap::default(), properties: FxHashMap::default(), symbol_properties: FxHashMap::default(), prototype: Value::null(), @@ -437,6 +445,48 @@ impl Object { assert!(prototype.is_null() || prototype.is_object()); self.prototype = prototype } + + pub fn iter(&self) -> Iter<'_> { + Iter { + indexed_properties: self.indexed_properties.iter(), + properties: self.properties.iter(), + } + } + + pub fn keys(&self) -> Keys<'_> { + Keys { inner: self.iter() } + } +} + +#[derive(Debug)] +pub struct Iter<'a> { + indexed_properties: hash_map::Iter<'a, u32, Property>, + properties: hash_map::Iter<'a, RcString, Property>, +} + +impl<'a> Iterator for Iter<'a> { + type Item = (PropertyKey, &'a Property); + fn next(&mut self) -> Option { + if let Some((key, value)) = self.indexed_properties.next() { + Some(((*key).into(), value)) + } else { + let (key, value) = self.properties.next()?; + Some((key.clone().into(), value)) + } + } +} + +#[derive(Debug)] +pub struct Keys<'a> { + inner: Iter<'a>, +} + +impl<'a> Iterator for Keys<'a> { + type Item = PropertyKey; + fn next(&mut self) -> Option { + let (key, _) = self.inner.next()?; + Some(key) + } } /// Create a new object. diff --git a/boa/src/builtins/property/mod.rs b/boa/src/builtins/property/mod.rs index 0cc272bcdaf..ed32cdd9960 100644 --- a/boa/src/builtins/property/mod.rs +++ b/boa/src/builtins/property/mod.rs @@ -18,6 +18,7 @@ use crate::builtins::value::RcString; use crate::builtins::value::RcSymbol; use crate::builtins::Value; use gc::{Finalize, Trace}; +use std::convert::TryFrom; use std::fmt; pub mod attribute; @@ -290,33 +291,50 @@ impl<'a> From<&'a Value> for Property { pub enum PropertyKey { String(RcString), Symbol(RcSymbol), + Index(u32), } impl From for PropertyKey { #[inline] fn from(string: RcString) -> PropertyKey { - PropertyKey::String(string) + if let Ok(index) = string.parse() { + PropertyKey::Index(index) + } else { + PropertyKey::String(string) + } } } impl From<&str> for PropertyKey { #[inline] fn from(string: &str) -> PropertyKey { - PropertyKey::String(string.into()) + if let Ok(index) = string.parse() { + PropertyKey::Index(index) + } else { + PropertyKey::String(string.into()) + } } } impl From for PropertyKey { #[inline] fn from(string: String) -> PropertyKey { - PropertyKey::String(string.into()) + if let Ok(index) = string.parse() { + PropertyKey::Index(index) + } else { + PropertyKey::String(string.into()) + } } } impl From> for PropertyKey { #[inline] fn from(string: Box) -> PropertyKey { - PropertyKey::String(string.into()) + if let Ok(index) = string.parse() { + PropertyKey::Index(index) + } else { + PropertyKey::String(string.into()) + } } } @@ -333,19 +351,21 @@ impl fmt::Display for PropertyKey { match self { PropertyKey::String(ref string) => string.fmt(f), PropertyKey::Symbol(ref symbol) => symbol.fmt(f), + PropertyKey::Index(index) => index.fmt(f), } } } -impl From<&PropertyKey> for RcString { - #[inline] - fn from(property_key: &PropertyKey) -> RcString { - match property_key { - PropertyKey::String(ref string) => string.clone(), - PropertyKey::Symbol(ref symbol) => symbol.to_string().into(), - } - } -} +// impl From<&PropertyKey> for RcString { +// #[inline] +// fn from(property_key: &PropertyKey) -> RcString { +// match property_key { +// PropertyKey::String(ref string) => string.clone(), +// PropertyKey::Symbol(ref symbol) => symbol.to_string().into(), +// PropertyKey:: +// } +// } +// } impl From<&PropertyKey> for Value { #[inline] @@ -353,6 +373,13 @@ impl From<&PropertyKey> for Value { match property_key { PropertyKey::String(ref string) => string.clone().into(), PropertyKey::Symbol(ref symbol) => symbol.clone().into(), + PropertyKey::Index(index) => { + if let Ok(integer) = i32::try_from(*index) { + Value::integer(integer) + } else { + Value::number(*index) + } + } } } } @@ -363,6 +390,70 @@ impl From for Value { match property_key { PropertyKey::String(ref string) => string.clone().into(), PropertyKey::Symbol(ref symbol) => symbol.clone().into(), + PropertyKey::Index(index) => { + if let Ok(integer) = i32::try_from(index) { + Value::integer(integer) + } else { + Value::number(index) + } + } + } + } +} + +impl From for PropertyKey { + fn from(value: u8) -> Self { + PropertyKey::Index(value.into()) + } +} + +impl From for PropertyKey { + fn from(value: u16) -> Self { + PropertyKey::Index(value.into()) + } +} + +impl From for PropertyKey { + fn from(value: u32) -> Self { + PropertyKey::Index(value) + } +} + +impl From for PropertyKey { + fn from(value: usize) -> Self { + if let Ok(index) = u32::try_from(value) { + PropertyKey::Index(index) + } else { + PropertyKey::String(RcString::from(value.to_string())) + } + } +} + +impl From for PropertyKey { + fn from(value: isize) -> Self { + if let Ok(index) = u32::try_from(value) { + PropertyKey::Index(index) + } else { + PropertyKey::String(RcString::from(value.to_string())) + } + } +} + +impl From for PropertyKey { + fn from(value: i32) -> Self { + if let Ok(index) = u32::try_from(value) { + PropertyKey::Index(index) + } else { + PropertyKey::String(RcString::from(value.to_string())) + } + } +} + +impl PartialEq<&str> for PropertyKey { + fn eq(&self, other: &&str) -> bool { + match self { + PropertyKey::String(ref string) => string == other, + _ => false, } } } diff --git a/boa/src/builtins/value/conversions.rs b/boa/src/builtins/value/conversions.rs index 3a0a04872fa..96b824d8d72 100644 --- a/boa/src/builtins/value/conversions.rs +++ b/boa/src/builtins/value/conversions.rs @@ -172,10 +172,7 @@ where fn from(value: Vec) -> Self { let mut array = Object::default(); for (i, item) in value.into_iter().enumerate() { - array.properties_mut().insert( - RcString::from(i.to_string()), - Property::default().value(item.into()), - ); + array.insert_property(i, Property::default().value(item.into())); } Value::from(array) } diff --git a/boa/src/builtins/value/display.rs b/boa/src/builtins/value/display.rs index 8b0f1e3a2cf..ac226f21653 100644 --- a/boa/src/builtins/value/display.rs +++ b/boa/src/builtins/value/display.rs @@ -107,9 +107,7 @@ pub(crate) fn log_string_from(x: &Value, print_internals: bool, print_children: // which are part of the Array log_string_from( &v.borrow() - .properties() - .get(i.to_string().as_str()) - .unwrap() + .get_own_property(&i.into()) .value .clone() .expect("Could not borrow value"), diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index b2016f31495..8ce52ccc94c 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -243,7 +243,7 @@ impl Value { Self::Object(ref obj) => { if obj.borrow().is_array() { let mut arr: Vec = Vec::new(); - for k in obj.borrow().properties().keys() { + for k in obj.borrow().keys() { if k != "length" { let value = self.get_field(k.to_string()); if value.is_undefined() || value.is_function() || value.is_symbol() { @@ -256,7 +256,7 @@ impl Value { Ok(JSONValue::Array(arr)) } else { let mut new_obj = Map::new(); - for k in obj.borrow().properties().keys() { + for k in obj.borrow().keys() { let key = k.clone(); let value = self.get_field(k.to_string()); if !value.is_undefined() && !value.is_function() && !value.is_symbol() { @@ -486,30 +486,33 @@ impl Value { /// Removes a property from a Value object. /// /// It will return a boolean based on if the value was removed, if there was no value to remove false is returned. - pub fn remove_property(&self, field: &str) -> bool { + pub fn remove_property(&self, key: Key) -> bool + where + Key: Into, + { self.as_object_mut() - .and_then(|mut x| x.properties_mut().remove(field)) + .map(|mut x| x.remove_property(&key.into())) .is_some() } /// Resolve the property in the object. /// /// A copy of the Property is returned. - pub fn get_property(&self, field: &str) -> Option { + pub fn get_property(&self, key: Key) -> Option + where + Key: Into, + { + let key = key.into(); let _timer = BoaProfiler::global().start_event("Value::get_property", "value"); - // Spidermonkey has its own GetLengthProperty: https://searchfox.org/mozilla-central/source/js/src/vm/Interpreter-inl.h#154 - // This is only for primitive strings, String() objects have their lengths calculated in string.rs match self { - Self::Undefined => None, - Self::String(ref s) if field == "length" => { - Some(Property::default().value(Value::from(s.chars().count()))) - } Self::Object(ref object) => { let object = object.borrow(); - match object.properties().get(field) { - Some(value) => Some(value.clone()), - None => object.prototype().get_property(field), + let property = object.get_own_property(&key); + if !property.is_none() { + return Some(property); } + + object.prototype().get_property(key) } _ => None, } @@ -523,49 +526,21 @@ impl Value { let _timer = BoaProfiler::global().start_event("Value::update_property", "value"); if let Some(ref mut object) = self.as_object_mut() { - // Use value, or walk up the prototype chain - if let Some(property) = object.properties_mut().get_mut(field) { - *property = new_property; - } + object.insert_property(field, new_property); } } /// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist /// get_field recieves a Property from get_prop(). It should then return the [[Get]] result value if that's set, otherwise fall back to [[Value]] /// TODO: this function should use the get Value if its set - pub fn get_field(&self, field: F) -> Self + pub fn get_field(&self, key: Key) -> Self where - F: Into, + Key: Into, { let _timer = BoaProfiler::global().start_event("Value::get_field", "value"); - match field.into() { - // Our field will either be a String or a Symbol - Self::String(ref s) => { - match self.get_property(s) { - Some(prop) => { - // If the Property has [[Get]] set to a function, we should run that and return the Value - let prop_getter = match prop.get { - Some(_) => None, - None => None, - }; - - // If the getter is populated, use that. If not use [[Value]] instead - if let Some(val) = prop_getter { - val - } else { - let val = prop - .value - .as_ref() - .expect("Could not get property as reference"); - val.clone() - } - } - None => Value::undefined(), - } - } - Self::Symbol(_) => unimplemented!(), - _ => Value::undefined(), - } + self.as_object() + .map(|object| object.get(&key.into())) + .unwrap_or_else(Value::undefined) } /// Check whether an object has an internal state set. @@ -630,9 +605,14 @@ impl Value { /// Check to see if the Value has the field, mainly used by environment records. #[inline] - pub fn has_field(&self, field: &str) -> bool { + pub fn has_field(&self, key: Key) -> bool + where + Key: Into, + { let _timer = BoaProfiler::global().start_event("Value::has_field", "value"); - self.get_property(field).is_some() + self.as_object() + .map(|object| object.has_property(&key.into())) + .unwrap_or(false) } /// Set the field in the value @@ -646,19 +626,15 @@ impl Value { let value = value.into(); let _timer = BoaProfiler::global().start_event("Value::set_field", "value"); if let Self::Object(ref obj) = *self { - if let PropertyKey::String(ref string) = field { + if let PropertyKey::Index(index) = field { if obj.borrow().is_array() { - if let Ok(num) = string.parse::() { - if num > 0 { - let len = i32::from(&self.get_field("length")); - if len < (num + 1) as i32 { - self.set_field("length", num + 1); - } - } + let len = i32::from(&self.get_field("length")) as u32; + if len < index + 1 { + self.set_field("length", index + 1); } } } - obj.borrow_mut().set(&field, value.clone()); + obj.borrow_mut().set(field, value.clone()); } value } @@ -672,14 +648,12 @@ impl Value { } /// Set the property in the value. - pub fn set_property(&self, field: S, property: Property) -> Property + pub fn set_property(&self, key: Key, property: Property) -> Property where - S: Into, + Key: Into, { if let Some(mut object) = self.as_object_mut() { - object - .properties_mut() - .insert(field.into(), property.clone()); + object.insert_property(key.into(), property.clone()); } property } diff --git a/boa/src/environment/global_environment_record.rs b/boa/src/environment/global_environment_record.rs index 312bb670ae3..24c6ce7e7db 100644 --- a/boa/src/environment/global_environment_record.rs +++ b/boa/src/environment/global_environment_record.rs @@ -56,7 +56,7 @@ impl GlobalEnvironmentRecord { pub fn create_global_var_binding(&mut self, name: String, deletion: bool) { let obj_rec = &mut self.object_record; let global_object = &obj_rec.bindings; - let has_property = global_object.has_field(&name); + let has_property = global_object.has_field(name.as_str()); let extensible = global_object.is_extensible(); if !has_property && extensible { obj_rec.create_mutable_binding(name.clone(), deletion); @@ -71,7 +71,7 @@ impl GlobalEnvironmentRecord { pub fn create_global_function_binding(&mut self, name: &str, value: Value, deletion: bool) { let global_object = &mut self.object_record.bindings; - let existing_prop = global_object.get_property(&name); + let existing_prop = global_object.get_property(name); if let Some(prop) = existing_prop { if prop.value.is_none() || prop.configurable_or(false) { let mut property = diff --git a/boa/src/environment/object_environment_record.rs b/boa/src/environment/object_environment_record.rs index e00f5044dbf..876241ad007 100644 --- a/boa/src/environment/object_environment_record.rs +++ b/boa/src/environment/object_environment_record.rs @@ -63,8 +63,6 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord { } fn set_mutable_binding(&mut self, name: &str, value: Value, strict: bool) { - debug_assert!(value.is_object() || value.is_function()); - let mut property = Property::data_descriptor(value, Attribute::ENUMERABLE); property.set_configurable(strict); self.bindings.update_property(name, property); diff --git a/boa/src/exec/call/mod.rs b/boa/src/exec/call/mod.rs index 363c165327a..14599d0cf15 100644 --- a/boa/src/exec/call/mod.rs +++ b/boa/src/exec/call/mod.rs @@ -21,7 +21,10 @@ impl Executable for Call { Node::GetField(ref get_field) => { let obj = get_field.obj().run(interpreter)?; let field = get_field.field().run(interpreter)?; - (obj.clone(), obj.get_field(field.to_string())) + ( + obj.clone(), + obj.get_field(interpreter.to_property_key(&field)?), + ) } _ => ( interpreter.realm().global_obj.clone(), diff --git a/boa/src/exec/field/mod.rs b/boa/src/exec/field/mod.rs index 45e0a2ab424..03e20ba9ca2 100644 --- a/boa/src/exec/field/mod.rs +++ b/boa/src/exec/field/mod.rs @@ -7,7 +7,7 @@ use crate::{ impl Executable for GetConstField { fn run(&self, interpreter: &mut Interpreter) -> ResultValue { let mut obj = self.obj().run(interpreter)?; - if obj.get_type() != Type::Object || obj.get_type() != Type::Symbol { + if obj.get_type() != Type::Object { obj = interpreter.to_object(&obj)?; } @@ -18,11 +18,11 @@ impl Executable for GetConstField { impl Executable for GetField { fn run(&self, interpreter: &mut Interpreter) -> ResultValue { let mut obj = self.obj().run(interpreter)?; - if obj.get_type() != Type::Object || obj.get_type() != Type::Symbol { + if obj.get_type() != Type::Object { obj = interpreter.to_object(&obj)?; } let field = self.field().run(interpreter)?; - Ok(obj.get_field(interpreter.to_string(&field)?)) + Ok(obj.get_field(interpreter.to_property_key(&field)?)) } } diff --git a/boa/src/exec/tests.rs b/boa/src/exec/tests.rs index 0a4b089e918..2ffc21c180c 100644 --- a/boa/src/exec/tests.rs +++ b/boa/src/exec/tests.rs @@ -622,7 +622,7 @@ fn unary_delete() { const c = delete a.c + ''; a.b + c "#; - assert_eq!(&exec(delete_not_existing_prop), "\"5false\""); + assert_eq!(&exec(delete_not_existing_prop), "\"5true\""); let delete_field = r#" const a = { b: 5 }; From ae95341bd06b3c2a0425d1a4086ddcfb0bf6f945 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 5 Aug 2020 12:34:52 +0200 Subject: [PATCH 02/27] Removed unnecessary checks --- boa/src/builtins/object/internal_methods.rs | 23 +++------------------ 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/boa/src/builtins/object/internal_methods.rs b/boa/src/builtins/object/internal_methods.rs index 279dfe2bd5d..e7d78495cb6 100644 --- a/boa/src/builtins/object/internal_methods.rs +++ b/boa/src/builtins/object/internal_methods.rs @@ -266,12 +266,7 @@ impl Object { // Prop could either be a String or Symbol match property_key { PropertyKey::String(ref st) => { - let property = if let Ok(index) = st.parse() { - self.indexed_properties.get(&index) - } else { - self.properties().get(st) - }; - property.map_or_else(Property::empty, |v| { + self.properties().get(st).map_or_else(Property::empty, |v| { let mut d = Property::empty(); if v.is_data_descriptor() { d.value = v.value.clone(); @@ -378,13 +373,7 @@ impl Object { { match key.into() { PropertyKey::Index(index) => self.indexed_properties.insert(index, property), - PropertyKey::String(ref string) => { - if let Ok(index) = string.parse() { - self.indexed_properties.insert(index, property) - } else { - self.properties.insert(string.clone(), property) - } - } + PropertyKey::String(ref string) => self.properties.insert(string.clone(), property), PropertyKey::Symbol(ref symbol) => { self.symbol_properties.insert(symbol.hash(), property) } @@ -396,13 +385,7 @@ impl Object { pub(crate) fn remove_property(&mut self, key: &PropertyKey) -> Option { match key { PropertyKey::Index(index) => self.indexed_properties.remove(&index), - PropertyKey::String(ref string) => { - if let Ok(index) = string.parse() { - self.indexed_properties.remove(&index) - } else { - self.properties.remove(string.as_str()) - } - } + PropertyKey::String(ref string) => self.properties.remove(string), PropertyKey::Symbol(ref symbol) => self.symbol_properties.remove(&symbol.hash()), } } From 3b8d887f795e1203b072f47675066576c7da5396 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 5 Aug 2020 12:48:24 +0200 Subject: [PATCH 03/27] reenabled ignored test --- boa/src/builtins/boolean/tests.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/boa/src/builtins/boolean/tests.rs b/boa/src/builtins/boolean/tests.rs index c54dcb8e40d..c0eed466e39 100644 --- a/boa/src/builtins/boolean/tests.rs +++ b/boa/src/builtins/boolean/tests.rs @@ -49,7 +49,6 @@ fn constructor_gives_true_instance() { } #[test] -#[ignore] fn instances_have_correct_proto_set() { let realm = Realm::create(); let mut engine = Interpreter::new(realm); From d6520e9ccaa882f97bc5898f786575dfbeaf5c21 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 5 Aug 2020 13:42:02 +0200 Subject: [PATCH 04/27] Reverted `.get_field()` implementation --- boa/src/builtins/value/mod.rs | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index 8ce52ccc94c..80e39ef0531 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -538,9 +538,34 @@ impl Value { Key: Into, { let _timer = BoaProfiler::global().start_event("Value::get_field", "value"); - self.as_object() - .map(|object| object.get(&key.into())) - .unwrap_or_else(Value::undefined) + let key = key.into(); + match key { + // Our field will either be a String or a Symbol + PropertyKey::String(_) | PropertyKey::Index(_) => { + match self.get_property(key) { + Some(prop) => { + // If the Property has [[Get]] set to a function, we should run that and return the Value + let prop_getter = match prop.get { + Some(_) => None, + None => None, + }; + + // If the getter is populated, use that. If not use [[Value]] instead + if let Some(val) = prop_getter { + val + } else { + let val = prop + .value + .as_ref() + .expect("Could not get property as reference"); + val.clone() + } + } + None => Value::undefined(), + } + } + PropertyKey::Symbol(_) => unimplemented!(), + } } /// Check whether an object has an internal state set. From c4a8759787901d2728de3e0d816a4869b9b6709a Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 5 Aug 2020 14:24:42 +0200 Subject: [PATCH 05/27] Added `values` method to Object --- boa/src/builtins/object/mod.rs | 49 ++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 1d6ae31eb25..fcc8cac8ba1 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -28,6 +28,7 @@ use gc::{Finalize, Trace}; use rustc_hash::FxHashMap; use std::collections::hash_map; use std::fmt::{Debug, Display, Error, Formatter}; +use std::iter::FusedIterator; use super::function::{make_builtin_fn, make_constructor_fn}; use crate::builtins::value::same_value; @@ -456,9 +457,13 @@ impl Object { pub fn keys(&self) -> Keys<'_> { Keys { inner: self.iter() } } + + pub fn values(&self) -> Values<'_> { + Values { inner: self.iter() } + } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Iter<'a> { indexed_properties: hash_map::Iter<'a, u32, Property>, properties: hash_map::Iter<'a, RcString, Property>, @@ -476,7 +481,16 @@ impl<'a> Iterator for Iter<'a> { } } -#[derive(Debug)] +impl ExactSizeIterator for Iter<'_> { + #[inline] + fn len(&self) -> usize { + self.indexed_properties.len() + self.properties.len() + } +} + +impl FusedIterator for Iter<'_> {} + +#[derive(Debug, Clone)] pub struct Keys<'a> { inner: Iter<'a>, } @@ -489,6 +503,37 @@ impl<'a> Iterator for Keys<'a> { } } +impl ExactSizeIterator for Keys<'_> { + #[inline] + fn len(&self) -> usize { + self.inner.len() + } +} + +impl FusedIterator for Keys<'_> {} + +#[derive(Debug, Clone)] +pub struct Values<'a> { + inner: Iter<'a>, +} + +impl<'a> Iterator for Values<'a> { + type Item = &'a Property; + fn next(&mut self) -> Option { + let (_, value) = self.inner.next()?; + Some(value) + } +} + +impl ExactSizeIterator for Values<'_> { + #[inline] + fn len(&self) -> usize { + self.inner.len() + } +} + +impl FusedIterator for Values<'_> {} + /// Create a new object. pub fn make_object(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { if let Some(arg) = args.get(0) { From 3f89c51ddc56d4ec83fe8e0c25353d1bf7e57e42 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 5 Aug 2020 14:41:41 +0200 Subject: [PATCH 06/27] Made `symbol_properties` contain a `RcSymbol` instead of just hash --- boa/src/builtins/object/internal_methods.rs | 35 +++++++++++---------- boa/src/builtins/object/mod.rs | 6 ++-- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/boa/src/builtins/object/internal_methods.rs b/boa/src/builtins/object/internal_methods.rs index e7d78495cb6..369104ca6f7 100644 --- a/boa/src/builtins/object/internal_methods.rs +++ b/boa/src/builtins/object/internal_methods.rs @@ -279,21 +279,22 @@ impl Object { d }) } - PropertyKey::Symbol(ref symbol) => self - .symbol_properties() - .get(&symbol.hash()) - .map_or_else(Property::empty, |v| { - let mut d = Property::empty(); - if v.is_data_descriptor() { - d.value = v.value.clone(); - } else { - debug_assert!(v.is_accessor_descriptor()); - d.get = v.get.clone(); - d.set = v.set.clone(); - } - d.attribute = v.attribute; - d - }), + PropertyKey::Symbol(ref symbol) => { + self.symbol_properties() + .get(symbol) + .map_or_else(Property::empty, |v| { + let mut d = Property::empty(); + if v.is_data_descriptor() { + d.value = v.value.clone(); + } else { + debug_assert!(v.is_accessor_descriptor()); + d.get = v.get.clone(); + d.set = v.set.clone(); + } + d.attribute = v.attribute; + d + }) + } PropertyKey::Index(index) => { self.indexed_properties .get(&index) @@ -375,7 +376,7 @@ impl Object { PropertyKey::Index(index) => self.indexed_properties.insert(index, property), PropertyKey::String(ref string) => self.properties.insert(string.clone(), property), PropertyKey::Symbol(ref symbol) => { - self.symbol_properties.insert(symbol.hash(), property) + self.symbol_properties.insert(symbol.clone(), property) } } } @@ -386,7 +387,7 @@ impl Object { match key { PropertyKey::Index(index) => self.indexed_properties.remove(&index), PropertyKey::String(ref string) => self.properties.remove(string), - PropertyKey::Symbol(ref symbol) => self.symbol_properties.remove(&symbol.hash()), + PropertyKey::Symbol(ref symbol) => self.symbol_properties.remove(symbol), } } diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index fcc8cac8ba1..e411ebf5fdf 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -58,7 +58,7 @@ pub struct Object { /// Properties properties: FxHashMap, /// Symbol Properties - symbol_properties: FxHashMap, + symbol_properties: FxHashMap, /// Instance prototype `__proto__`. prototype: Value, /// Some rust object that stores internal state @@ -419,12 +419,12 @@ impl Object { } #[inline] - pub fn symbol_properties(&self) -> &FxHashMap { + pub fn symbol_properties(&self) -> &FxHashMap { &self.symbol_properties } #[inline] - pub fn symbol_properties_mut(&mut self) -> &mut FxHashMap { + pub fn symbol_properties_mut(&mut self) -> &mut FxHashMap { &mut self.symbol_properties } From 17893eb50f1ec052c35e7b36585b282e7d59367b Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 5 Aug 2020 15:41:45 +0200 Subject: [PATCH 07/27] Added `symbols` to Object --- boa/src/builtins/object/iter.rs | 129 ++++++++++++++++++++++++++++++++ boa/src/builtins/object/mod.rs | 90 +--------------------- 2 files changed, 131 insertions(+), 88 deletions(-) create mode 100644 boa/src/builtins/object/iter.rs diff --git a/boa/src/builtins/object/iter.rs b/boa/src/builtins/object/iter.rs new file mode 100644 index 00000000000..8a383637cbe --- /dev/null +++ b/boa/src/builtins/object/iter.rs @@ -0,0 +1,129 @@ +use super::*; +use std::collections::hash_map; +use std::iter::FusedIterator; + +impl Object { + #[inline] + pub fn iter(&self) -> Iter<'_> { + Iter { + indexed_properties: self.indexed_properties.iter(), + properties: self.properties.iter(), + } + } + + #[inline] + pub fn keys(&self) -> Keys<'_> { + Keys { inner: self.iter() } + } + + #[inline] + pub fn values(&self) -> Values<'_> { + Values { inner: self.iter() } + } + + #[inline] + pub fn symbols(&self) -> Symbols<'_> { + Symbols { + symbols: self.symbol_properties.iter(), + } + } +} + +#[derive(Debug, Clone)] +pub struct Iter<'a> { + indexed_properties: hash_map::Iter<'a, u32, Property>, + properties: hash_map::Iter<'a, RcString, Property>, +} + +impl<'a> Iterator for Iter<'a> { + type Item = (PropertyKey, &'a Property); + fn next(&mut self) -> Option { + if let Some((key, value)) = self.indexed_properties.next() { + Some(((*key).into(), value)) + } else { + let (key, value) = self.properties.next()?; + Some((key.clone().into(), value)) + } + } +} + +impl ExactSizeIterator for Iter<'_> { + #[inline] + fn len(&self) -> usize { + self.indexed_properties.len() + self.properties.len() + } +} + +impl FusedIterator for Iter<'_> {} + +#[derive(Debug, Clone)] +pub struct Keys<'a> { + inner: Iter<'a>, +} + +impl<'a> Iterator for Keys<'a> { + type Item = PropertyKey; + fn next(&mut self) -> Option { + let (key, _) = self.inner.next()?; + Some(key) + } +} + +impl ExactSizeIterator for Keys<'_> { + #[inline] + fn len(&self) -> usize { + self.inner.len() + } +} + +impl FusedIterator for Keys<'_> {} + +#[derive(Debug, Clone)] +pub struct Values<'a> { + inner: Iter<'a>, +} + +impl<'a> Iterator for Values<'a> { + type Item = &'a Property; + fn next(&mut self) -> Option { + let (_, value) = self.inner.next()?; + Some(value) + } +} + +impl ExactSizeIterator for Values<'_> { + #[inline] + fn len(&self) -> usize { + self.inner.len() + } +} + +impl FusedIterator for Values<'_> {} + +#[derive(Debug, Clone)] +pub struct Symbols<'a> { + symbols: hash_map::Iter<'a, RcSymbol, Property>, +} + +impl<'a> Iterator for Symbols<'a> { + type Item = (&'a RcSymbol, &'a Property); + + #[inline] + fn next(&mut self) -> Option { + self.symbols.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.symbols.size_hint() + } +} + +impl ExactSizeIterator for Symbols<'_> { + #[inline] + fn len(&self) -> usize { + self.symbols.len() + } +} + +impl FusedIterator for Symbols<'_> {} diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index e411ebf5fdf..f29f2a3e1af 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -26,9 +26,7 @@ use crate::{ }; use gc::{Finalize, Trace}; use rustc_hash::FxHashMap; -use std::collections::hash_map; use std::fmt::{Debug, Display, Error, Formatter}; -use std::iter::FusedIterator; use super::function::{make_builtin_fn, make_constructor_fn}; use crate::builtins::value::same_value; @@ -37,8 +35,10 @@ pub use internal_state::{InternalState, InternalStateCell}; pub mod gcobject; pub mod internal_methods; mod internal_state; +mod iter; pub use gcobject::GcObject; +pub use iter::*; #[cfg(test)] mod tests; @@ -446,94 +446,8 @@ impl Object { assert!(prototype.is_null() || prototype.is_object()); self.prototype = prototype } - - pub fn iter(&self) -> Iter<'_> { - Iter { - indexed_properties: self.indexed_properties.iter(), - properties: self.properties.iter(), - } - } - - pub fn keys(&self) -> Keys<'_> { - Keys { inner: self.iter() } - } - - pub fn values(&self) -> Values<'_> { - Values { inner: self.iter() } - } -} - -#[derive(Debug, Clone)] -pub struct Iter<'a> { - indexed_properties: hash_map::Iter<'a, u32, Property>, - properties: hash_map::Iter<'a, RcString, Property>, -} - -impl<'a> Iterator for Iter<'a> { - type Item = (PropertyKey, &'a Property); - fn next(&mut self) -> Option { - if let Some((key, value)) = self.indexed_properties.next() { - Some(((*key).into(), value)) - } else { - let (key, value) = self.properties.next()?; - Some((key.clone().into(), value)) - } - } -} - -impl ExactSizeIterator for Iter<'_> { - #[inline] - fn len(&self) -> usize { - self.indexed_properties.len() + self.properties.len() - } -} - -impl FusedIterator for Iter<'_> {} - -#[derive(Debug, Clone)] -pub struct Keys<'a> { - inner: Iter<'a>, -} - -impl<'a> Iterator for Keys<'a> { - type Item = PropertyKey; - fn next(&mut self) -> Option { - let (key, _) = self.inner.next()?; - Some(key) - } -} - -impl ExactSizeIterator for Keys<'_> { - #[inline] - fn len(&self) -> usize { - self.inner.len() - } } -impl FusedIterator for Keys<'_> {} - -#[derive(Debug, Clone)] -pub struct Values<'a> { - inner: Iter<'a>, -} - -impl<'a> Iterator for Values<'a> { - type Item = &'a Property; - fn next(&mut self) -> Option { - let (_, value) = self.inner.next()?; - Some(value) - } -} - -impl ExactSizeIterator for Values<'_> { - #[inline] - fn len(&self) -> usize { - self.inner.len() - } -} - -impl FusedIterator for Values<'_> {} - /// Create a new object. pub fn make_object(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { if let Some(arg) = args.get(0) { From 912d98538671702e56d996a42f3f14275d302e34 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 5 Aug 2020 15:47:35 +0200 Subject: [PATCH 08/27] Added `symbol_keys` to object --- boa/src/builtins/object/iter.rs | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/boa/src/builtins/object/iter.rs b/boa/src/builtins/object/iter.rs index 8a383637cbe..c4e8a3b4c1d 100644 --- a/boa/src/builtins/object/iter.rs +++ b/boa/src/builtins/object/iter.rs @@ -27,6 +27,13 @@ impl Object { symbols: self.symbol_properties.iter(), } } + + #[inline] + pub fn symbol_keys(&self) -> SymbolKeys<'_> { + SymbolKeys { + symbols: self.symbol_properties.keys(), + } + } } #[derive(Debug, Clone)] @@ -127,3 +134,31 @@ impl ExactSizeIterator for Symbols<'_> { } impl FusedIterator for Symbols<'_> {} + +#[derive(Debug, Clone)] +pub struct SymbolKeys<'a> { + symbols: hash_map::Keys<'a, RcSymbol, Property>, +} + +impl<'a> Iterator for SymbolKeys<'a> { + type Item = &'a RcSymbol; + + #[inline] + fn next(&mut self) -> Option { + self.symbols.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.symbols.size_hint() + } +} + +impl ExactSizeIterator for SymbolKeys<'_> { + #[inline] + fn len(&self) -> usize { + self.symbols.len() + } +} + +impl FusedIterator for SymbolKeys<'_> {} From 9cb69310f9f15059dbbd7db464c5ca33ab13a7cd Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 5 Aug 2020 15:52:08 +0200 Subject: [PATCH 09/27] Added `symbol_values` to object --- boa/src/builtins/object/iter.rs | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/boa/src/builtins/object/iter.rs b/boa/src/builtins/object/iter.rs index c4e8a3b4c1d..f9815d1619f 100644 --- a/boa/src/builtins/object/iter.rs +++ b/boa/src/builtins/object/iter.rs @@ -34,6 +34,13 @@ impl Object { symbols: self.symbol_properties.keys(), } } + + #[inline] + pub fn symbol_values(&self) -> SymbolValues<'_> { + SymbolValues { + symbols: self.symbol_properties.values(), + } + } } #[derive(Debug, Clone)] @@ -162,3 +169,31 @@ impl ExactSizeIterator for SymbolKeys<'_> { } impl FusedIterator for SymbolKeys<'_> {} + +#[derive(Debug, Clone)] +pub struct SymbolValues<'a> { + symbols: hash_map::Values<'a, RcSymbol, Property>, +} + +impl<'a> Iterator for SymbolValues<'a> { + type Item = &'a Property; + + #[inline] + fn next(&mut self) -> Option { + self.symbols.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.symbols.size_hint() + } +} + +impl ExactSizeIterator for SymbolValues<'_> { + #[inline] + fn len(&self) -> usize { + self.symbols.len() + } +} + +impl FusedIterator for SymbolValues<'_> {} From 1ce5f75277f9c629f26a3c7d40e5847b5e5465ec Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 5 Aug 2020 16:00:13 +0200 Subject: [PATCH 10/27] Added `indexes` method to Object --- boa/src/builtins/object/iter.rs | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/boa/src/builtins/object/iter.rs b/boa/src/builtins/object/iter.rs index f9815d1619f..537e66c4c86 100644 --- a/boa/src/builtins/object/iter.rs +++ b/boa/src/builtins/object/iter.rs @@ -41,6 +41,13 @@ impl Object { symbols: self.symbol_properties.values(), } } + + #[inline] + pub fn indexes(&self) -> Indexes<'_> { + Indexes { + indexes: self.indexed_properties.iter(), + } + } } #[derive(Debug, Clone)] @@ -197,3 +204,31 @@ impl ExactSizeIterator for SymbolValues<'_> { } impl FusedIterator for SymbolValues<'_> {} + +#[derive(Debug, Clone)] +pub struct Indexes<'a> { + indexes: hash_map::Iter<'a, u32, Property>, +} + +impl<'a> Iterator for Indexes<'a> { + type Item = (&'a u32, &'a Property); + + #[inline] + fn next(&mut self) -> Option { + self.indexes.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.indexes.size_hint() + } +} + +impl ExactSizeIterator for Indexes<'_> { + #[inline] + fn len(&self) -> usize { + self.indexes.len() + } +} + +impl FusedIterator for Indexes<'_> {} From a6cd433ee204a9386baf78146b3feaa130f9301c Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 5 Aug 2020 16:11:46 +0200 Subject: [PATCH 11/27] Added `index_keys` method to object --- boa/src/builtins/object/iter.rs | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/boa/src/builtins/object/iter.rs b/boa/src/builtins/object/iter.rs index 537e66c4c86..07faf242bf8 100644 --- a/boa/src/builtins/object/iter.rs +++ b/boa/src/builtins/object/iter.rs @@ -48,6 +48,13 @@ impl Object { indexes: self.indexed_properties.iter(), } } + + #[inline] + pub fn indexe_keys(&self) -> IndexKeys<'_> { + IndexKeys { + indexes: self.indexed_properties.keys(), + } + } } #[derive(Debug, Clone)] @@ -232,3 +239,31 @@ impl ExactSizeIterator for Indexes<'_> { } impl FusedIterator for Indexes<'_> {} + +#[derive(Debug, Clone)] +pub struct IndexKeys<'a> { + indexes: hash_map::Keys<'a, u32, Property>, +} + +impl<'a> Iterator for IndexKeys<'a> { + type Item = &'a u32; + + #[inline] + fn next(&mut self) -> Option { + self.indexes.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.indexes.size_hint() + } +} + +impl ExactSizeIterator for IndexKeys<'_> { + #[inline] + fn len(&self) -> usize { + self.indexes.len() + } +} + +impl FusedIterator for IndexKeys<'_> {} From db734c5ddb2a396a20292a95d2fbfe38a8e73f8e Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 5 Aug 2020 16:14:24 +0200 Subject: [PATCH 12/27] Added \ method to Object --- boa/src/builtins/object/iter.rs | 37 ++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/boa/src/builtins/object/iter.rs b/boa/src/builtins/object/iter.rs index 07faf242bf8..da0113df8f1 100644 --- a/boa/src/builtins/object/iter.rs +++ b/boa/src/builtins/object/iter.rs @@ -50,11 +50,18 @@ impl Object { } #[inline] - pub fn indexe_keys(&self) -> IndexKeys<'_> { + pub fn index_keys(&self) -> IndexKeys<'_> { IndexKeys { indexes: self.indexed_properties.keys(), } } + + #[inline] + pub fn index_values(&self) -> IndexValues<'_> { + IndexValues { + indexes: self.indexed_properties.values(), + } + } } #[derive(Debug, Clone)] @@ -267,3 +274,31 @@ impl ExactSizeIterator for IndexKeys<'_> { } impl FusedIterator for IndexKeys<'_> {} + +#[derive(Debug, Clone)] +pub struct IndexValues<'a> { + indexes: hash_map::Values<'a, u32, Property>, +} + +impl<'a> Iterator for IndexValues<'a> { + type Item = &'a Property; + + #[inline] + fn next(&mut self) -> Option { + self.indexes.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.indexes.size_hint() + } +} + +impl ExactSizeIterator for IndexValues<'_> { + #[inline] + fn len(&self) -> usize { + self.indexes.len() + } +} + +impl FusedIterator for IndexValues<'_> {} From a8552c9ae887ade02eed1c360d829cb1ad9c8176 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 5 Aug 2020 16:47:15 +0200 Subject: [PATCH 13/27] Added string property iteration --- boa/src/builtins/object/iter.rs | 205 +++++++++++++++++++++----------- 1 file changed, 135 insertions(+), 70 deletions(-) diff --git a/boa/src/builtins/object/iter.rs b/boa/src/builtins/object/iter.rs index da0113df8f1..fad1c46500d 100644 --- a/boa/src/builtins/object/iter.rs +++ b/boa/src/builtins/object/iter.rs @@ -7,67 +7,70 @@ impl Object { pub fn iter(&self) -> Iter<'_> { Iter { indexed_properties: self.indexed_properties.iter(), - properties: self.properties.iter(), + string_properties: self.properties.iter(), } } #[inline] pub fn keys(&self) -> Keys<'_> { - Keys { inner: self.iter() } + Keys(self.iter()) } #[inline] pub fn values(&self) -> Values<'_> { - Values { inner: self.iter() } + Values(self.iter()) } #[inline] pub fn symbols(&self) -> Symbols<'_> { - Symbols { - symbols: self.symbol_properties.iter(), - } + Symbols(self.symbol_properties.iter()) } #[inline] pub fn symbol_keys(&self) -> SymbolKeys<'_> { - SymbolKeys { - symbols: self.symbol_properties.keys(), - } + SymbolKeys(self.symbol_properties.keys()) } #[inline] pub fn symbol_values(&self) -> SymbolValues<'_> { - SymbolValues { - symbols: self.symbol_properties.values(), - } + SymbolValues(self.symbol_properties.values()) } #[inline] pub fn indexes(&self) -> Indexes<'_> { - Indexes { - indexes: self.indexed_properties.iter(), - } + Indexes(self.indexed_properties.iter()) } #[inline] pub fn index_keys(&self) -> IndexKeys<'_> { - IndexKeys { - indexes: self.indexed_properties.keys(), - } + IndexKeys(self.indexed_properties.keys()) } #[inline] pub fn index_values(&self) -> IndexValues<'_> { - IndexValues { - indexes: self.indexed_properties.values(), - } + IndexValues(self.indexed_properties.values()) + } + + #[inline] + pub fn strings(&self) -> Strings<'_> { + Strings(self.properties.iter()) + } + + #[inline] + pub fn string_keys(&self) -> StringKeys<'_> { + StringKeys(self.properties.keys()) + } + + #[inline] + pub fn string_values(&self) -> StringValues<'_> { + StringValues(self.properties.values()) } } #[derive(Debug, Clone)] pub struct Iter<'a> { indexed_properties: hash_map::Iter<'a, u32, Property>, - properties: hash_map::Iter<'a, RcString, Property>, + string_properties: hash_map::Iter<'a, RcString, Property>, } impl<'a> Iterator for Iter<'a> { @@ -76,7 +79,7 @@ impl<'a> Iterator for Iter<'a> { if let Some((key, value)) = self.indexed_properties.next() { Some(((*key).into(), value)) } else { - let (key, value) = self.properties.next()?; + let (key, value) = self.string_properties.next()?; Some((key.clone().into(), value)) } } @@ -85,21 +88,19 @@ impl<'a> Iterator for Iter<'a> { impl ExactSizeIterator for Iter<'_> { #[inline] fn len(&self) -> usize { - self.indexed_properties.len() + self.properties.len() + self.indexed_properties.len() + self.string_properties.len() } } impl FusedIterator for Iter<'_> {} #[derive(Debug, Clone)] -pub struct Keys<'a> { - inner: Iter<'a>, -} +pub struct Keys<'a>(Iter<'a>); impl<'a> Iterator for Keys<'a> { type Item = PropertyKey; fn next(&mut self) -> Option { - let (key, _) = self.inner.next()?; + let (key, _) = self.0.next()?; Some(key) } } @@ -107,21 +108,19 @@ impl<'a> Iterator for Keys<'a> { impl ExactSizeIterator for Keys<'_> { #[inline] fn len(&self) -> usize { - self.inner.len() + self.0.len() } } impl FusedIterator for Keys<'_> {} #[derive(Debug, Clone)] -pub struct Values<'a> { - inner: Iter<'a>, -} +pub struct Values<'a>(Iter<'a>); impl<'a> Iterator for Values<'a> { type Item = &'a Property; fn next(&mut self) -> Option { - let (_, value) = self.inner.next()?; + let (_, value) = self.0.next()?; Some(value) } } @@ -129,176 +128,242 @@ impl<'a> Iterator for Values<'a> { impl ExactSizeIterator for Values<'_> { #[inline] fn len(&self) -> usize { - self.inner.len() + self.0.len() } } impl FusedIterator for Values<'_> {} #[derive(Debug, Clone)] -pub struct Symbols<'a> { - symbols: hash_map::Iter<'a, RcSymbol, Property>, -} +pub struct Symbols<'a>(hash_map::Iter<'a, RcSymbol, Property>); impl<'a> Iterator for Symbols<'a> { type Item = (&'a RcSymbol, &'a Property); #[inline] fn next(&mut self) -> Option { - self.symbols.next() + self.0.next() } #[inline] fn size_hint(&self) -> (usize, Option) { - self.symbols.size_hint() + self.0.size_hint() } } impl ExactSizeIterator for Symbols<'_> { #[inline] fn len(&self) -> usize { - self.symbols.len() + self.0.len() } } impl FusedIterator for Symbols<'_> {} #[derive(Debug, Clone)] -pub struct SymbolKeys<'a> { - symbols: hash_map::Keys<'a, RcSymbol, Property>, -} +pub struct SymbolKeys<'a>(hash_map::Keys<'a, RcSymbol, Property>); impl<'a> Iterator for SymbolKeys<'a> { type Item = &'a RcSymbol; #[inline] fn next(&mut self) -> Option { - self.symbols.next() + self.0.next() } #[inline] fn size_hint(&self) -> (usize, Option) { - self.symbols.size_hint() + self.0.size_hint() } } impl ExactSizeIterator for SymbolKeys<'_> { #[inline] fn len(&self) -> usize { - self.symbols.len() + self.0.len() } } impl FusedIterator for SymbolKeys<'_> {} #[derive(Debug, Clone)] -pub struct SymbolValues<'a> { - symbols: hash_map::Values<'a, RcSymbol, Property>, -} +pub struct SymbolValues<'a>(hash_map::Values<'a, RcSymbol, Property>); impl<'a> Iterator for SymbolValues<'a> { type Item = &'a Property; #[inline] fn next(&mut self) -> Option { - self.symbols.next() + self.0.next() } #[inline] fn size_hint(&self) -> (usize, Option) { - self.symbols.size_hint() + self.0.size_hint() } } impl ExactSizeIterator for SymbolValues<'_> { #[inline] fn len(&self) -> usize { - self.symbols.len() + self.0.len() } } impl FusedIterator for SymbolValues<'_> {} #[derive(Debug, Clone)] -pub struct Indexes<'a> { - indexes: hash_map::Iter<'a, u32, Property>, -} +pub struct Indexes<'a>(hash_map::Iter<'a, u32, Property>); impl<'a> Iterator for Indexes<'a> { type Item = (&'a u32, &'a Property); #[inline] fn next(&mut self) -> Option { - self.indexes.next() + self.0.next() } #[inline] fn size_hint(&self) -> (usize, Option) { - self.indexes.size_hint() + self.0.size_hint() } } impl ExactSizeIterator for Indexes<'_> { #[inline] fn len(&self) -> usize { - self.indexes.len() + self.0.len() } } impl FusedIterator for Indexes<'_> {} #[derive(Debug, Clone)] -pub struct IndexKeys<'a> { - indexes: hash_map::Keys<'a, u32, Property>, -} +pub struct IndexKeys<'a>(hash_map::Keys<'a, u32, Property>); impl<'a> Iterator for IndexKeys<'a> { type Item = &'a u32; #[inline] fn next(&mut self) -> Option { - self.indexes.next() + self.0.next() } #[inline] fn size_hint(&self) -> (usize, Option) { - self.indexes.size_hint() + self.0.size_hint() } } impl ExactSizeIterator for IndexKeys<'_> { #[inline] fn len(&self) -> usize { - self.indexes.len() + self.0.len() } } impl FusedIterator for IndexKeys<'_> {} #[derive(Debug, Clone)] -pub struct IndexValues<'a> { - indexes: hash_map::Values<'a, u32, Property>, -} +pub struct IndexValues<'a>(hash_map::Values<'a, u32, Property>); impl<'a> Iterator for IndexValues<'a> { type Item = &'a Property; #[inline] fn next(&mut self) -> Option { - self.indexes.next() + self.0.next() } #[inline] fn size_hint(&self) -> (usize, Option) { - self.indexes.size_hint() + self.0.size_hint() } } impl ExactSizeIterator for IndexValues<'_> { #[inline] fn len(&self) -> usize { - self.indexes.len() + self.0.len() } } impl FusedIterator for IndexValues<'_> {} + +#[derive(Debug, Clone)] +pub struct Strings<'a>(hash_map::Iter<'a, RcString, Property>); + +impl<'a> Iterator for Strings<'a> { + type Item = (&'a RcString, &'a Property); + + #[inline] + fn next(&mut self) -> Option { + self.0.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +impl ExactSizeIterator for Strings<'_> { + #[inline] + fn len(&self) -> usize { + self.0.len() + } +} + +impl FusedIterator for Strings<'_> {} + +#[derive(Debug, Clone)] +pub struct StringKeys<'a>(hash_map::Keys<'a, RcString, Property>); + +impl<'a> Iterator for StringKeys<'a> { + type Item = &'a RcString; + + #[inline] + fn next(&mut self) -> Option { + self.0.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +impl ExactSizeIterator for StringKeys<'_> { + #[inline] + fn len(&self) -> usize { + self.0.len() + } +} + +impl FusedIterator for StringKeys<'_> {} + +#[derive(Debug, Clone)] +pub struct StringValues<'a>(hash_map::Values<'a, RcString, Property>); + +impl<'a> Iterator for StringValues<'a> { + type Item = &'a Property; + + #[inline] + fn next(&mut self) -> Option { + self.0.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +impl ExactSizeIterator for StringValues<'_> { + #[inline] + fn len(&self) -> usize { + self.0.len() + } +} + +impl FusedIterator for StringValues<'_> {} From 0bc304ac14e96b1a14ba0eac8ac9ec5dfa70cdf6 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 5 Aug 2020 16:53:17 +0200 Subject: [PATCH 14/27] Made `iter` iterate over symbols too --- boa/src/builtins/object/iter.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/boa/src/builtins/object/iter.rs b/boa/src/builtins/object/iter.rs index fad1c46500d..abfc62d40cf 100644 --- a/boa/src/builtins/object/iter.rs +++ b/boa/src/builtins/object/iter.rs @@ -8,6 +8,7 @@ impl Object { Iter { indexed_properties: self.indexed_properties.iter(), string_properties: self.properties.iter(), + symbol_properties: self.symbol_properties.iter(), } } @@ -71,6 +72,7 @@ impl Object { pub struct Iter<'a> { indexed_properties: hash_map::Iter<'a, u32, Property>, string_properties: hash_map::Iter<'a, RcString, Property>, + symbol_properties: hash_map::Iter<'a, RcSymbol, Property>, } impl<'a> Iterator for Iter<'a> { @@ -78,8 +80,10 @@ impl<'a> Iterator for Iter<'a> { fn next(&mut self) -> Option { if let Some((key, value)) = self.indexed_properties.next() { Some(((*key).into(), value)) + } else if let Some((key, value)) = self.string_properties.next() { + Some((key.clone().into(), value)) } else { - let (key, value) = self.string_properties.next()?; + let (key, value) = self.symbol_properties.next()?; Some((key.clone().into(), value)) } } From 916ec4794dc0b7831216e3bf66a2f85ca1607d62 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 5 Aug 2020 17:17:59 +0200 Subject: [PATCH 15/27] Removed `properties` and `symbol_properties` from Object --- boa/src/builtins/json/mod.rs | 3 +-- boa/src/builtins/object/internal_methods.rs | 4 ++-- boa/src/builtins/object/iter.rs | 2 +- boa/src/builtins/object/mod.rs | 22 +-------------------- boa/src/builtins/value/conversions.rs | 5 +---- boa/src/builtins/value/display.rs | 6 +++--- 6 files changed, 9 insertions(+), 33 deletions(-) diff --git a/boa/src/builtins/json/mod.rs b/boa/src/builtins/json/mod.rs index 764284f78b2..7f408fd931c 100644 --- a/boa/src/builtins/json/mod.rs +++ b/boa/src/builtins/json/mod.rs @@ -150,8 +150,7 @@ impl Json { }) .ok_or_else(Value::undefined)? } else if replacer_as_object.is_array() { - let mut obj_to_return = - serde_json::Map::with_capacity(replacer_as_object.properties().len() - 1); + let mut obj_to_return = serde_json::Map::new(); let fields = replacer_as_object.keys().filter_map(|key| { if key == "length" { None diff --git a/boa/src/builtins/object/internal_methods.rs b/boa/src/builtins/object/internal_methods.rs index 369104ca6f7..8b384f40786 100644 --- a/boa/src/builtins/object/internal_methods.rs +++ b/boa/src/builtins/object/internal_methods.rs @@ -266,7 +266,7 @@ impl Object { // Prop could either be a String or Symbol match property_key { PropertyKey::String(ref st) => { - self.properties().get(st).map_or_else(Property::empty, |v| { + self.properties.get(st).map_or_else(Property::empty, |v| { let mut d = Property::empty(); if v.is_data_descriptor() { d.value = v.value.clone(); @@ -280,7 +280,7 @@ impl Object { }) } PropertyKey::Symbol(ref symbol) => { - self.symbol_properties() + self.symbol_properties .get(symbol) .map_or_else(Property::empty, |v| { let mut d = Property::empty(); diff --git a/boa/src/builtins/object/iter.rs b/boa/src/builtins/object/iter.rs index abfc62d40cf..303425f6e23 100644 --- a/boa/src/builtins/object/iter.rs +++ b/boa/src/builtins/object/iter.rs @@ -92,7 +92,7 @@ impl<'a> Iterator for Iter<'a> { impl ExactSizeIterator for Iter<'_> { #[inline] fn len(&self) -> usize { - self.indexed_properties.len() + self.string_properties.len() + self.indexed_properties.len() + self.string_properties.len() + self.symbol_properties.len() } } diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index f29f2a3e1af..289704daaf2 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -56,7 +56,7 @@ pub struct Object { pub data: ObjectData, indexed_properties: FxHashMap, /// Properties - properties: FxHashMap, + pub(crate) properties: FxHashMap, /// Symbol Properties symbol_properties: FxHashMap, /// Instance prototype `__proto__`. @@ -408,26 +408,6 @@ impl Object { matches!(self.data, ObjectData::Ordinary) } - #[inline] - pub fn properties(&self) -> &FxHashMap { - &self.properties - } - - #[inline] - pub fn properties_mut(&mut self) -> &mut FxHashMap { - &mut self.properties - } - - #[inline] - pub fn symbol_properties(&self) -> &FxHashMap { - &self.symbol_properties - } - - #[inline] - pub fn symbol_properties_mut(&mut self) -> &mut FxHashMap { - &mut self.symbol_properties - } - #[inline] pub fn state(&self) -> &Option { &self.state diff --git a/boa/src/builtins/value/conversions.rs b/boa/src/builtins/value/conversions.rs index 96b824d8d72..55dece7f99b 100644 --- a/boa/src/builtins/value/conversions.rs +++ b/boa/src/builtins/value/conversions.rs @@ -156,10 +156,7 @@ where fn from(value: &[T]) -> Self { let mut array = Object::default(); for (i, item) in value.iter().enumerate() { - array.properties_mut().insert( - RcString::from(i.to_string()), - Property::default().value(item.clone().into()), - ); + array.insert_property(i, Property::default().value(item.clone().into())); } Self::from(array) } diff --git a/boa/src/builtins/value/display.rs b/boa/src/builtins/value/display.rs index ac226f21653..afe3966c03d 100644 --- a/boa/src/builtins/value/display.rs +++ b/boa/src/builtins/value/display.rs @@ -61,7 +61,7 @@ macro_rules! print_obj_value { (impl $field:ident, $v:expr, $f:expr) => { $v .borrow() - .$field() + .$field .iter() .map($f) .collect::>() @@ -88,7 +88,7 @@ pub(crate) fn log_string_from(x: &Value, print_internals: bool, print_children: ObjectData::Array => { let len = i32::from( &v.borrow() - .properties() + .properties .get("length") .expect("Could not get Array's length property") .value @@ -126,7 +126,7 @@ pub(crate) fn log_string_from(x: &Value, print_internals: bool, print_children: ObjectData::Map(ref map) => { let size = i32::from( &v.borrow() - .properties() + .properties .get("size") .unwrap() .value From 05ec706d98bee73f160884cece3bcc20fff137f8 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 5 Aug 2020 18:06:02 +0200 Subject: [PATCH 16/27] Added symbol indexing support --- boa/src/builtins/value/mod.rs | 42 +++++++++++++++-------------------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index 80e39ef0531..aa0bd812a09 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -539,32 +539,26 @@ impl Value { { let _timer = BoaProfiler::global().start_event("Value::get_field", "value"); let key = key.into(); - match key { - // Our field will either be a String or a Symbol - PropertyKey::String(_) | PropertyKey::Index(_) => { - match self.get_property(key) { - Some(prop) => { - // If the Property has [[Get]] set to a function, we should run that and return the Value - let prop_getter = match prop.get { - Some(_) => None, - None => None, - }; - - // If the getter is populated, use that. If not use [[Value]] instead - if let Some(val) = prop_getter { - val - } else { - let val = prop - .value - .as_ref() - .expect("Could not get property as reference"); - val.clone() - } - } - None => Value::undefined(), + match self.get_property(key) { + Some(prop) => { + // If the Property has [[Get]] set to a function, we should run that and return the Value + let prop_getter = match prop.get { + Some(_) => None, + None => None, + }; + + // If the getter is populated, use that. If not use [[Value]] instead + if let Some(val) = prop_getter { + val + } else { + let val = prop + .value + .as_ref() + .expect("Could not get property as reference"); + val.clone() } } - PropertyKey::Symbol(_) => unimplemented!(), + None => Value::undefined(), } } From 47a4d510acfbcaacaac0b0c3562b5233cad07eaf Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 5 Aug 2020 19:13:20 +0200 Subject: [PATCH 17/27] Changed symbol set_field to define_own_property --- boa/src/builtins/function/mod.rs | 2 +- boa/src/builtins/object/internal_methods.rs | 14 ++-- boa/src/builtins/symbol/mod.rs | 77 ++++++++++++++++----- 3 files changed, 70 insertions(+), 23 deletions(-) diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs index 484204159c5..c47e947e54a 100644 --- a/boa/src/builtins/function/mod.rs +++ b/boa/src/builtins/function/mod.rs @@ -415,7 +415,7 @@ pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value { Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ); // Define length as a property - obj.define_own_property("length".into(), length); + obj.define_own_property("length", length); let mut index: usize = 0; while index < len { let val = arguments_list.get(index).expect("Could not get argument"); diff --git a/boa/src/builtins/object/internal_methods.rs b/boa/src/builtins/object/internal_methods.rs index 8b384f40786..114358c6294 100644 --- a/boa/src/builtins/object/internal_methods.rs +++ b/boa/src/builtins/object/internal_methods.rs @@ -156,10 +156,14 @@ impl Object { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-defineownproperty-p-desc - pub fn define_own_property(&mut self, property_key: PropertyKey, desc: Property) -> bool { + pub fn define_own_property(&mut self, key: Key, desc: Property) -> bool + where + Key: Into, + { let _timer = BoaProfiler::global().start_event("Object::define_own_property", "object"); - let mut current = self.get_own_property(&property_key); + let key = key.into(); + let mut current = self.get_own_property(&key); let extensible = self.is_extensible(); // https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor @@ -169,7 +173,7 @@ impl Object { return false; } - self.insert_property(property_key, desc); + self.insert_property(key, desc); return true; } // If every field is absent we don't need to set anything @@ -208,7 +212,7 @@ impl Object { current.set = None; } - self.insert_property(property_key, current); + self.insert_property(key, current); return true; // 7 } else if current.is_data_descriptor() && desc.is_data_descriptor() { @@ -248,7 +252,7 @@ impl Object { return true; } // 9 - self.insert_property(property_key, desc); + self.insert_property(key, desc); true } diff --git a/boa/src/builtins/symbol/mod.rs b/boa/src/builtins/symbol/mod.rs index 853aa90d869..aca62b55771 100644 --- a/boa/src/builtins/symbol/mod.rs +++ b/boa/src/builtins/symbol/mod.rs @@ -20,7 +20,10 @@ mod tests; use super::function::{make_builtin_fn, make_constructor_fn}; use crate::{ - builtins::value::{RcString, RcSymbol, ResultValue, Value}, + builtins::{ + property::{Attribute, Property}, + value::{RcString, RcSymbol, ResultValue, Value}, + }, exec::Interpreter, BoaProfiler, }; @@ -153,22 +156,62 @@ impl Symbol { true, ); - symbol_object.set_field("asyncIterator", Value::symbol(symbol_async_iterator)); - symbol_object.set_field("hasInstance", Value::symbol(symbol_has_instance)); - symbol_object.set_field( - "isConcatSpreadable", - Value::symbol(symbol_is_concat_spreadable), - ); - symbol_object.set_field("iterator", Value::symbol(symbol_iterator)); - symbol_object.set_field("match", Value::symbol(symbol_match)); - symbol_object.set_field("matchAll", Value::symbol(symbol_match_all)); - symbol_object.set_field("replace", Value::symbol(symbol_replace)); - symbol_object.set_field("search", Value::symbol(symbol_search)); - symbol_object.set_field("species", Value::symbol(symbol_species)); - symbol_object.set_field("split", Value::symbol(symbol_split)); - symbol_object.set_field("toPrimitive", Value::symbol(symbol_to_primitive)); - symbol_object.set_field("toStringTag", Value::symbol(symbol_to_string_tag)); - symbol_object.set_field("unscopables", Value::symbol(symbol_unscopables)); + { + let mut symbol_object = symbol_object.as_object_mut().unwrap(); + let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; + symbol_object.define_own_property( + "asyncIterator", + Property::data_descriptor(Value::symbol(symbol_async_iterator), attribute), + ); + symbol_object.define_own_property( + "hasInstance", + Property::data_descriptor(Value::symbol(symbol_has_instance), attribute), + ); + symbol_object.define_own_property( + "isConcatSpreadable", + Property::data_descriptor(Value::symbol(symbol_is_concat_spreadable), attribute), + ); + symbol_object.define_own_property( + "iterator", + Property::data_descriptor(Value::symbol(symbol_iterator), attribute), + ); + symbol_object.define_own_property( + "match", + Property::data_descriptor(Value::symbol(symbol_match), attribute), + ); + symbol_object.define_own_property( + "matchAll", + Property::data_descriptor(Value::symbol(symbol_match_all), attribute), + ); + symbol_object.define_own_property( + "replace", + Property::data_descriptor(Value::symbol(symbol_replace), attribute), + ); + symbol_object.define_own_property( + "search", + Property::data_descriptor(Value::symbol(symbol_search), attribute), + ); + symbol_object.define_own_property( + "species", + Property::data_descriptor(Value::symbol(symbol_species), attribute), + ); + symbol_object.define_own_property( + "split", + Property::data_descriptor(Value::symbol(symbol_split), attribute), + ); + symbol_object.define_own_property( + "toPrimitive", + Property::data_descriptor(Value::symbol(symbol_to_primitive), attribute), + ); + symbol_object.define_own_property( + "toStringTag", + Property::data_descriptor(Value::symbol(symbol_to_string_tag), attribute), + ); + symbol_object.define_own_property( + "unscopables", + Property::data_descriptor(Value::symbol(symbol_unscopables), attribute), + ); + } (Self::NAME, symbol_object) } From 70250d8c5ea61a98f17d597556059a290570e62e Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 5 Aug 2020 20:15:56 +0200 Subject: [PATCH 18/27] Added `.construct_symbol()` method to interpreter --- boa/src/builtins/symbol/mod.rs | 89 ++++++++++++++++------------------ boa/src/builtins/value/mod.rs | 6 +-- boa/src/exec/mod.rs | 9 +++- 3 files changed, 51 insertions(+), 53 deletions(-) diff --git a/boa/src/builtins/symbol/mod.rs b/boa/src/builtins/symbol/mod.rs index aca62b55771..661635d8f70 100644 --- a/boa/src/builtins/symbol/mod.rs +++ b/boa/src/builtins/symbol/mod.rs @@ -30,7 +30,16 @@ use crate::{ use gc::{Finalize, Trace}; #[derive(Debug, Finalize, Trace, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Symbol(Option, u32); +pub struct Symbol { + hash: u32, + description: Option, +} + +impl Symbol { + pub(crate) fn new(hash: u32, description: Option) -> Self { + Self { hash, description } + } +} impl Symbol { /// The name of the object. @@ -41,12 +50,12 @@ impl Symbol { /// Returns the `Symbol`s description. pub fn description(&self) -> Option<&str> { - self.0.as_deref() + self.description.as_deref() } /// Returns the `Symbol`s hash. pub fn hash(&self) -> u32 { - self.1 + self.hash } fn this_symbol_value(value: &Value, ctx: &mut Interpreter) -> Result { @@ -81,7 +90,7 @@ impl Symbol { _ => None, }; - Ok(Value::symbol(Symbol(description, ctx.generate_hash()))) + Ok(ctx.construct_symbol(description).into()) } /// `Symbol.prototype.toString()` @@ -106,37 +115,21 @@ impl Symbol { pub fn init(interpreter: &mut Interpreter) -> (&'static str, Value) { // Define the Well-Known Symbols // https://tc39.es/ecma262/#sec-well-known-symbols - let symbol_async_iterator = Symbol( - Some("Symbol.asyncIterator".into()), - interpreter.generate_hash(), - ); - let symbol_has_instance = Symbol( - Some("Symbol.hasInstance".into()), - interpreter.generate_hash(), - ); - let symbol_is_concat_spreadable = Symbol( - Some("Symbol.isConcatSpreadable".into()), - interpreter.generate_hash(), - ); - let symbol_iterator = Symbol(Some("Symbol.iterator".into()), interpreter.generate_hash()); - let symbol_match = Symbol(Some("Symbol.match".into()), interpreter.generate_hash()); - let symbol_match_all = Symbol(Some("Symbol.matchAll".into()), interpreter.generate_hash()); - let symbol_replace = Symbol(Some("Symbol.replace".into()), interpreter.generate_hash()); - let symbol_search = Symbol(Some("Symbol.search".into()), interpreter.generate_hash()); - let symbol_species = Symbol(Some("Symbol.species".into()), interpreter.generate_hash()); - let symbol_split = Symbol(Some("Symbol.split".into()), interpreter.generate_hash()); - let symbol_to_primitive = Symbol( - Some("Symbol.toPrimitive".into()), - interpreter.generate_hash(), - ); - let symbol_to_string_tag = Symbol( - Some("Symbol.toStringTag".into()), - interpreter.generate_hash(), - ); - let symbol_unscopables = Symbol( - Some("Symbol.unscopables".into()), - interpreter.generate_hash(), - ); + let symbol_async_iterator = + interpreter.construct_symbol(Some("Symbol.asyncIterator".into())); + let symbol_has_instance = interpreter.construct_symbol(Some("Symbol.hasInstance".into())); + let symbol_is_concat_spreadable = + interpreter.construct_symbol(Some("Symbol.isConcatSpreadable".into())); + let symbol_iterator = interpreter.construct_symbol(Some("Symbol.iterator".into())); + let symbol_match = interpreter.construct_symbol(Some("Symbol.match".into())); + let symbol_match_all = interpreter.construct_symbol(Some("Symbol.matchAll".into())); + let symbol_replace = interpreter.construct_symbol(Some("Symbol.replace".into())); + let symbol_search = interpreter.construct_symbol(Some("Symbol.search".into())); + let symbol_species = interpreter.construct_symbol(Some("Symbol.species".into())); + let symbol_split = interpreter.construct_symbol(Some("Symbol.split".into())); + let symbol_to_primitive = interpreter.construct_symbol(Some("Symbol.toPrimitive".into())); + let symbol_to_string_tag = interpreter.construct_symbol(Some("Symbol.toStringTag".into())); + let symbol_unscopables = interpreter.construct_symbol(Some("Symbol.unscopables".into())); let global = interpreter.global(); let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); @@ -161,55 +154,55 @@ impl Symbol { let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; symbol_object.define_own_property( "asyncIterator", - Property::data_descriptor(Value::symbol(symbol_async_iterator), attribute), + Property::data_descriptor(symbol_async_iterator.into(), attribute), ); symbol_object.define_own_property( "hasInstance", - Property::data_descriptor(Value::symbol(symbol_has_instance), attribute), + Property::data_descriptor(symbol_has_instance.into(), attribute), ); symbol_object.define_own_property( "isConcatSpreadable", - Property::data_descriptor(Value::symbol(symbol_is_concat_spreadable), attribute), + Property::data_descriptor(symbol_is_concat_spreadable.into(), attribute), ); symbol_object.define_own_property( "iterator", - Property::data_descriptor(Value::symbol(symbol_iterator), attribute), + Property::data_descriptor(symbol_iterator.into(), attribute), ); symbol_object.define_own_property( "match", - Property::data_descriptor(Value::symbol(symbol_match), attribute), + Property::data_descriptor(symbol_match.into(), attribute), ); symbol_object.define_own_property( "matchAll", - Property::data_descriptor(Value::symbol(symbol_match_all), attribute), + Property::data_descriptor(symbol_match_all.into(), attribute), ); symbol_object.define_own_property( "replace", - Property::data_descriptor(Value::symbol(symbol_replace), attribute), + Property::data_descriptor(symbol_replace.into(), attribute), ); symbol_object.define_own_property( "search", - Property::data_descriptor(Value::symbol(symbol_search), attribute), + Property::data_descriptor(symbol_search.into(), attribute), ); symbol_object.define_own_property( "species", - Property::data_descriptor(Value::symbol(symbol_species), attribute), + Property::data_descriptor(symbol_species.into(), attribute), ); symbol_object.define_own_property( "split", - Property::data_descriptor(Value::symbol(symbol_split), attribute), + Property::data_descriptor(symbol_split.into(), attribute), ); symbol_object.define_own_property( "toPrimitive", - Property::data_descriptor(Value::symbol(symbol_to_primitive), attribute), + Property::data_descriptor(symbol_to_primitive.into(), attribute), ); symbol_object.define_own_property( "toStringTag", - Property::data_descriptor(Value::symbol(symbol_to_string_tag), attribute), + Property::data_descriptor(symbol_to_string_tag.into(), attribute), ); symbol_object.define_own_property( "unscopables", - Property::data_descriptor(Value::symbol(symbol_unscopables), attribute), + Property::data_descriptor(symbol_unscopables.into(), attribute), ); } diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index aa0bd812a09..6da349de601 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -9,7 +9,7 @@ use crate::builtins::{ function::Function, object::{GcObject, InternalState, InternalStateCell, Object, ObjectData, PROTOTYPE}, property::{Attribute, Property, PropertyKey}, - BigInt, Symbol, + BigInt, }; use crate::exec::Interpreter; use crate::BoaProfiler; @@ -149,8 +149,8 @@ impl Value { /// Creates a new symbol value. #[inline] - pub(crate) fn symbol(symbol: Symbol) -> Self { - Self::Symbol(RcSymbol::from(symbol)) + pub fn symbol(symbol: RcSymbol) -> Self { + Self::Symbol(symbol) } /// Returns a new empty object diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index c33117cb748..afa3e8e12f5 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -29,8 +29,8 @@ use crate::{ number::{f64_to_int32, f64_to_uint32}, object::{Object, ObjectData, PROTOTYPE}, property::PropertyKey, - value::{RcBigInt, RcString, ResultValue, Type, Value}, - BigInt, Console, Number, + value::{RcBigInt, RcString, RcSymbol, ResultValue, Type, Value}, + BigInt, Console, Number, Symbol, }, realm::Realm, syntax::ast::{ @@ -675,6 +675,11 @@ impl Interpreter { pub(crate) fn console_mut(&mut self) -> &mut Console { &mut self.console } + + /// Construct a new `Symbol` with an optional description. + pub fn construct_symbol(&mut self, description: Option) -> RcSymbol { + RcSymbol::from(Symbol::new(self.generate_hash(), description)) + } } impl Executable for Node { From d3d9923099a8aa9c420a277519019f6afed462e4 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 5 Aug 2020 20:40:26 +0200 Subject: [PATCH 19/27] Added `.construct_object()` to interpreter --- boa/src/builtins/symbol/mod.rs | 26 +++++++++++++------------- boa/src/exec/mod.rs | 8 +++++++- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/boa/src/builtins/symbol/mod.rs b/boa/src/builtins/symbol/mod.rs index 661635d8f70..94826812275 100644 --- a/boa/src/builtins/symbol/mod.rs +++ b/boa/src/builtins/symbol/mod.rs @@ -152,55 +152,55 @@ impl Symbol { { let mut symbol_object = symbol_object.as_object_mut().unwrap(); let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; - symbol_object.define_own_property( + symbol_object.insert_property( "asyncIterator", Property::data_descriptor(symbol_async_iterator.into(), attribute), ); - symbol_object.define_own_property( + symbol_object.insert_property( "hasInstance", Property::data_descriptor(symbol_has_instance.into(), attribute), ); - symbol_object.define_own_property( + symbol_object.insert_property( "isConcatSpreadable", Property::data_descriptor(symbol_is_concat_spreadable.into(), attribute), ); - symbol_object.define_own_property( + symbol_object.insert_property( "iterator", Property::data_descriptor(symbol_iterator.into(), attribute), ); - symbol_object.define_own_property( + symbol_object.insert_property( "match", Property::data_descriptor(symbol_match.into(), attribute), ); - symbol_object.define_own_property( + symbol_object.insert_property( "matchAll", Property::data_descriptor(symbol_match_all.into(), attribute), ); - symbol_object.define_own_property( + symbol_object.insert_property( "replace", Property::data_descriptor(symbol_replace.into(), attribute), ); - symbol_object.define_own_property( + symbol_object.insert_property( "search", Property::data_descriptor(symbol_search.into(), attribute), ); - symbol_object.define_own_property( + symbol_object.insert_property( "species", Property::data_descriptor(symbol_species.into(), attribute), ); - symbol_object.define_own_property( + symbol_object.insert_property( "split", Property::data_descriptor(symbol_split.into(), attribute), ); - symbol_object.define_own_property( + symbol_object.insert_property( "toPrimitive", Property::data_descriptor(symbol_to_primitive.into(), attribute), ); - symbol_object.define_own_property( + symbol_object.insert_property( "toStringTag", Property::data_descriptor(symbol_to_string_tag.into(), attribute), ); - symbol_object.define_own_property( + symbol_object.insert_property( "unscopables", Property::data_descriptor(symbol_unscopables.into(), attribute), ); diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index afa3e8e12f5..aaff8e718b7 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -27,7 +27,7 @@ use crate::{ builtins::{ function::{Function as FunctionObject, FunctionBody, ThisMode}, number::{f64_to_int32, f64_to_uint32}, - object::{Object, ObjectData, PROTOTYPE}, + object::{GcObject, Object, ObjectData, PROTOTYPE}, property::PropertyKey, value::{RcBigInt, RcString, RcSymbol, ResultValue, Type, Value}, BigInt, Console, Number, Symbol, @@ -680,6 +680,12 @@ impl Interpreter { pub fn construct_symbol(&mut self, description: Option) -> RcSymbol { RcSymbol::from(Symbol::new(self.generate_hash(), description)) } + + /// Construct an empty object. + pub fn construct_object(&self) -> GcObject { + let object_prototype = self.global().get_field("Object").get_field(PROTOTYPE); + GcObject::new(Object::create(object_prototype)) + } } impl Executable for Node { From b8191a56b65f0878c1f1b43b6d5327a563fc189a Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Thu, 6 Aug 2020 07:22:07 +0200 Subject: [PATCH 20/27] Added inline to symbol and object constructors --- boa/src/builtins/object/internal_methods.rs | 65 ++++++--------------- boa/src/exec/mod.rs | 2 + 2 files changed, 19 insertions(+), 48 deletions(-) diff --git a/boa/src/builtins/object/internal_methods.rs b/boa/src/builtins/object/internal_methods.rs index 114358c6294..b6a8fdd1a4a 100644 --- a/boa/src/builtins/object/internal_methods.rs +++ b/boa/src/builtins/object/internal_methods.rs @@ -264,58 +264,27 @@ impl Object { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getownproperty-p - pub fn get_own_property(&self, property_key: &PropertyKey) -> Property { + pub fn get_own_property(&self, key: &PropertyKey) -> Property { let _timer = BoaProfiler::global().start_event("Object::get_own_property", "object"); // Prop could either be a String or Symbol - match property_key { - PropertyKey::String(ref st) => { - self.properties.get(st).map_or_else(Property::empty, |v| { - let mut d = Property::empty(); - if v.is_data_descriptor() { - d.value = v.value.clone(); - } else { - debug_assert!(v.is_accessor_descriptor()); - d.get = v.get.clone(); - d.set = v.set.clone(); - } - d.attribute = v.attribute; - d - }) - } - PropertyKey::Symbol(ref symbol) => { - self.symbol_properties - .get(symbol) - .map_or_else(Property::empty, |v| { - let mut d = Property::empty(); - if v.is_data_descriptor() { - d.value = v.value.clone(); - } else { - debug_assert!(v.is_accessor_descriptor()); - d.get = v.get.clone(); - d.set = v.set.clone(); - } - d.attribute = v.attribute; - d - }) - } - PropertyKey::Index(index) => { - self.indexed_properties - .get(&index) - .map_or_else(Property::empty, |v| { - let mut d = Property::empty(); - if v.is_data_descriptor() { - d.value = v.value.clone(); - } else { - debug_assert!(v.is_accessor_descriptor()); - d.get = v.get.clone(); - d.set = v.set.clone(); - } - d.attribute = v.attribute; - d - }) + let property = match key { + PropertyKey::Index(index) => self.indexed_properties.get(&index), + PropertyKey::String(ref st) => self.properties.get(st), + PropertyKey::Symbol(ref symbol) => self.symbol_properties.get(symbol), + }; + property.map_or_else(Property::empty, |v| { + let mut d = Property::empty(); + if v.is_data_descriptor() { + d.value = v.value.clone(); + } else { + debug_assert!(v.is_accessor_descriptor()); + d.get = v.get.clone(); + d.set = v.set.clone(); } - } + d.attribute = v.attribute; + d + }) } /// `Object.setPropertyOf(obj, prototype)` diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index aaff8e718b7..caf75a9901e 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -677,11 +677,13 @@ impl Interpreter { } /// Construct a new `Symbol` with an optional description. + #[inline] pub fn construct_symbol(&mut self, description: Option) -> RcSymbol { RcSymbol::from(Symbol::new(self.generate_hash(), description)) } /// Construct an empty object. + #[inline] pub fn construct_object(&self) -> GcObject { let object_prototype = self.global().get_field("Object").get_field(PROTOTYPE); GcObject::new(Object::create(object_prototype)) From 24afbac722023966747a0c39bea025d366c3c1c9 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Thu, 6 Aug 2020 09:54:48 +0200 Subject: [PATCH 21/27] removed unnecessary has_property --- boa/src/builtins/object/internal_methods.rs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/boa/src/builtins/object/internal_methods.rs b/boa/src/builtins/object/internal_methods.rs index b6a8fdd1a4a..49a47c7bc71 100644 --- a/boa/src/builtins/object/internal_methods.rs +++ b/boa/src/builtins/object/internal_methods.rs @@ -21,19 +21,14 @@ impl Object { /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p pub fn has_property(&self, property_key: &PropertyKey) -> bool { let prop = self.get_own_property(property_key); - if prop.value.is_none() { - let parent: Value = self.get_prototype_of(); - if !parent.is_null() { - // the parent value variant should be an object - // In the unlikely event it isn't return false - return match parent { - Value::Object(ref obj) => obj.borrow().has_property(property_key), - _ => false, - }; - } - return false; + if prop.is_none() { + let parent = self.get_prototype_of(); + return if let Value::Object(ref object) = parent { + object.borrow().has_property(property_key) + } else { + false + }; } - true } From 94091eb05cf0805d360ee1502021582026767b12 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Thu, 6 Aug 2020 10:14:48 +0200 Subject: [PATCH 22/27] Added test for symbol object access --- boa/src/builtins/symbol/tests.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/boa/src/builtins/symbol/tests.rs b/boa/src/builtins/symbol/tests.rs index 518632a4ef9..d3f6ee316e0 100644 --- a/boa/src/builtins/symbol/tests.rs +++ b/boa/src/builtins/symbol/tests.rs @@ -23,3 +23,20 @@ fn print_symbol_expect_description() { let sym = forward_val(&mut engine, "sym.toString()").unwrap(); assert_eq!(sym.to_string(), "\"Symbol(Hello)\""); } + +#[test] +fn symbol_access() { + let realm = Realm::create(); + let mut engine = Interpreter::new(realm); + let init = r#" + var x = {}; + var sym1 = Symbol("Hello"); + var sym2 = Symbol("Hello"); + x[sym1] = 10; + x[sym2] = 20; + "#; + forward_val(&mut engine, init).unwrap(); + assert_eq!(forward(&mut engine, "x[sym1]"), "10"); + assert_eq!(forward(&mut engine, "x[sym2]"), "20"); + assert_eq!(forward(&mut engine, "x['Symbol(Hello)']"), "undefined"); +} From 06ec632fb48d58d373b4b93b6d0cd3c06e157ea9 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Sat, 8 Aug 2020 11:58:54 +0200 Subject: [PATCH 23/27] Made iterator names more meaningful --- boa/src/builtins/object/iter.rs | 108 +++++++++--------- .../environment/object_environment_record.rs | 2 + 2 files changed, 56 insertions(+), 54 deletions(-) diff --git a/boa/src/builtins/object/iter.rs b/boa/src/builtins/object/iter.rs index 303425f6e23..8335d4a7491 100644 --- a/boa/src/builtins/object/iter.rs +++ b/boa/src/builtins/object/iter.rs @@ -23,48 +23,48 @@ impl Object { } #[inline] - pub fn symbols(&self) -> Symbols<'_> { - Symbols(self.symbol_properties.iter()) + pub fn symbol_properties(&self) -> SymbolProperties<'_> { + SymbolProperties(self.symbol_properties.iter()) } #[inline] - pub fn symbol_keys(&self) -> SymbolKeys<'_> { - SymbolKeys(self.symbol_properties.keys()) + pub fn symbol_property_keys(&self) -> SymbolPropertyKeys<'_> { + SymbolPropertyKeys(self.symbol_properties.keys()) } #[inline] - pub fn symbol_values(&self) -> SymbolValues<'_> { - SymbolValues(self.symbol_properties.values()) + pub fn symbol_property_values(&self) -> SymbolPropertyValues<'_> { + SymbolPropertyValues(self.symbol_properties.values()) } #[inline] - pub fn indexes(&self) -> Indexes<'_> { - Indexes(self.indexed_properties.iter()) + pub fn index_properties(&self) -> IndexProperties<'_> { + IndexProperties(self.indexed_properties.iter()) } #[inline] - pub fn index_keys(&self) -> IndexKeys<'_> { - IndexKeys(self.indexed_properties.keys()) + pub fn index_property_keys(&self) -> IndexPropertyKeys<'_> { + IndexPropertyKeys(self.indexed_properties.keys()) } #[inline] - pub fn index_values(&self) -> IndexValues<'_> { - IndexValues(self.indexed_properties.values()) + pub fn index_property_values(&self) -> IndexPropertyValues<'_> { + IndexPropertyValues(self.indexed_properties.values()) } #[inline] - pub fn strings(&self) -> Strings<'_> { - Strings(self.properties.iter()) + pub fn string_properties(&self) -> StringProperties<'_> { + StringProperties(self.properties.iter()) } #[inline] - pub fn string_keys(&self) -> StringKeys<'_> { - StringKeys(self.properties.keys()) + pub fn string_property_keys(&self) -> StringPropertyKeys<'_> { + StringPropertyKeys(self.properties.keys()) } #[inline] - pub fn string_values(&self) -> StringValues<'_> { - StringValues(self.properties.values()) + pub fn string_property_values(&self) -> StringPropertyValues<'_> { + StringPropertyValues(self.properties.values()) } } @@ -139,9 +139,9 @@ impl ExactSizeIterator for Values<'_> { impl FusedIterator for Values<'_> {} #[derive(Debug, Clone)] -pub struct Symbols<'a>(hash_map::Iter<'a, RcSymbol, Property>); +pub struct SymbolProperties<'a>(hash_map::Iter<'a, RcSymbol, Property>); -impl<'a> Iterator for Symbols<'a> { +impl<'a> Iterator for SymbolProperties<'a> { type Item = (&'a RcSymbol, &'a Property); #[inline] @@ -155,19 +155,19 @@ impl<'a> Iterator for Symbols<'a> { } } -impl ExactSizeIterator for Symbols<'_> { +impl ExactSizeIterator for SymbolProperties<'_> { #[inline] fn len(&self) -> usize { self.0.len() } } -impl FusedIterator for Symbols<'_> {} +impl FusedIterator for SymbolProperties<'_> {} #[derive(Debug, Clone)] -pub struct SymbolKeys<'a>(hash_map::Keys<'a, RcSymbol, Property>); +pub struct SymbolPropertyKeys<'a>(hash_map::Keys<'a, RcSymbol, Property>); -impl<'a> Iterator for SymbolKeys<'a> { +impl<'a> Iterator for SymbolPropertyKeys<'a> { type Item = &'a RcSymbol; #[inline] @@ -181,19 +181,19 @@ impl<'a> Iterator for SymbolKeys<'a> { } } -impl ExactSizeIterator for SymbolKeys<'_> { +impl ExactSizeIterator for SymbolPropertyKeys<'_> { #[inline] fn len(&self) -> usize { self.0.len() } } -impl FusedIterator for SymbolKeys<'_> {} +impl FusedIterator for SymbolPropertyKeys<'_> {} #[derive(Debug, Clone)] -pub struct SymbolValues<'a>(hash_map::Values<'a, RcSymbol, Property>); +pub struct SymbolPropertyValues<'a>(hash_map::Values<'a, RcSymbol, Property>); -impl<'a> Iterator for SymbolValues<'a> { +impl<'a> Iterator for SymbolPropertyValues<'a> { type Item = &'a Property; #[inline] @@ -207,19 +207,19 @@ impl<'a> Iterator for SymbolValues<'a> { } } -impl ExactSizeIterator for SymbolValues<'_> { +impl ExactSizeIterator for SymbolPropertyValues<'_> { #[inline] fn len(&self) -> usize { self.0.len() } } -impl FusedIterator for SymbolValues<'_> {} +impl FusedIterator for SymbolPropertyValues<'_> {} #[derive(Debug, Clone)] -pub struct Indexes<'a>(hash_map::Iter<'a, u32, Property>); +pub struct IndexProperties<'a>(hash_map::Iter<'a, u32, Property>); -impl<'a> Iterator for Indexes<'a> { +impl<'a> Iterator for IndexProperties<'a> { type Item = (&'a u32, &'a Property); #[inline] @@ -233,19 +233,19 @@ impl<'a> Iterator for Indexes<'a> { } } -impl ExactSizeIterator for Indexes<'_> { +impl ExactSizeIterator for IndexProperties<'_> { #[inline] fn len(&self) -> usize { self.0.len() } } -impl FusedIterator for Indexes<'_> {} +impl FusedIterator for IndexProperties<'_> {} #[derive(Debug, Clone)] -pub struct IndexKeys<'a>(hash_map::Keys<'a, u32, Property>); +pub struct IndexPropertyKeys<'a>(hash_map::Keys<'a, u32, Property>); -impl<'a> Iterator for IndexKeys<'a> { +impl<'a> Iterator for IndexPropertyKeys<'a> { type Item = &'a u32; #[inline] @@ -259,19 +259,19 @@ impl<'a> Iterator for IndexKeys<'a> { } } -impl ExactSizeIterator for IndexKeys<'_> { +impl ExactSizeIterator for IndexPropertyKeys<'_> { #[inline] fn len(&self) -> usize { self.0.len() } } -impl FusedIterator for IndexKeys<'_> {} +impl FusedIterator for IndexPropertyKeys<'_> {} #[derive(Debug, Clone)] -pub struct IndexValues<'a>(hash_map::Values<'a, u32, Property>); +pub struct IndexPropertyValues<'a>(hash_map::Values<'a, u32, Property>); -impl<'a> Iterator for IndexValues<'a> { +impl<'a> Iterator for IndexPropertyValues<'a> { type Item = &'a Property; #[inline] @@ -285,19 +285,19 @@ impl<'a> Iterator for IndexValues<'a> { } } -impl ExactSizeIterator for IndexValues<'_> { +impl ExactSizeIterator for IndexPropertyValues<'_> { #[inline] fn len(&self) -> usize { self.0.len() } } -impl FusedIterator for IndexValues<'_> {} +impl FusedIterator for IndexPropertyValues<'_> {} #[derive(Debug, Clone)] -pub struct Strings<'a>(hash_map::Iter<'a, RcString, Property>); +pub struct StringProperties<'a>(hash_map::Iter<'a, RcString, Property>); -impl<'a> Iterator for Strings<'a> { +impl<'a> Iterator for StringProperties<'a> { type Item = (&'a RcString, &'a Property); #[inline] @@ -311,19 +311,19 @@ impl<'a> Iterator for Strings<'a> { } } -impl ExactSizeIterator for Strings<'_> { +impl ExactSizeIterator for StringProperties<'_> { #[inline] fn len(&self) -> usize { self.0.len() } } -impl FusedIterator for Strings<'_> {} +impl FusedIterator for StringProperties<'_> {} #[derive(Debug, Clone)] -pub struct StringKeys<'a>(hash_map::Keys<'a, RcString, Property>); +pub struct StringPropertyKeys<'a>(hash_map::Keys<'a, RcString, Property>); -impl<'a> Iterator for StringKeys<'a> { +impl<'a> Iterator for StringPropertyKeys<'a> { type Item = &'a RcString; #[inline] @@ -337,19 +337,19 @@ impl<'a> Iterator for StringKeys<'a> { } } -impl ExactSizeIterator for StringKeys<'_> { +impl ExactSizeIterator for StringPropertyKeys<'_> { #[inline] fn len(&self) -> usize { self.0.len() } } -impl FusedIterator for StringKeys<'_> {} +impl FusedIterator for StringPropertyKeys<'_> {} #[derive(Debug, Clone)] -pub struct StringValues<'a>(hash_map::Values<'a, RcString, Property>); +pub struct StringPropertyValues<'a>(hash_map::Values<'a, RcString, Property>); -impl<'a> Iterator for StringValues<'a> { +impl<'a> Iterator for StringPropertyValues<'a> { type Item = &'a Property; #[inline] @@ -363,11 +363,11 @@ impl<'a> Iterator for StringValues<'a> { } } -impl ExactSizeIterator for StringValues<'_> { +impl ExactSizeIterator for StringPropertyValues<'_> { #[inline] fn len(&self) -> usize { self.0.len() } } -impl FusedIterator for StringValues<'_> {} +impl FusedIterator for StringPropertyValues<'_> {} diff --git a/boa/src/environment/object_environment_record.rs b/boa/src/environment/object_environment_record.rs index 876241ad007..e00f5044dbf 100644 --- a/boa/src/environment/object_environment_record.rs +++ b/boa/src/environment/object_environment_record.rs @@ -63,6 +63,8 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord { } fn set_mutable_binding(&mut self, name: &str, value: Value, strict: bool) { + debug_assert!(value.is_object() || value.is_function()); + let mut property = Property::data_descriptor(value, Attribute::ENUMERABLE); property.set_configurable(strict); self.bindings.update_property(name, property); From 61d357ecd3aaefac470dd9aa55641104c99f8981 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Sat, 8 Aug 2020 13:20:29 +0200 Subject: [PATCH 24/27] Added documentation to iterator methods --- boa/src/builtins/object/iter.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/boa/src/builtins/object/iter.rs b/boa/src/builtins/object/iter.rs index 8335d4a7491..86a5e893644 100644 --- a/boa/src/builtins/object/iter.rs +++ b/boa/src/builtins/object/iter.rs @@ -3,6 +3,7 @@ use std::collections::hash_map; use std::iter::FusedIterator; impl Object { + /// An iterator visiting all key-value pairs in arbitrary order. The iterator element type is `(PropertyKey, &'a Property)`. #[inline] pub fn iter(&self) -> Iter<'_> { Iter { @@ -12,62 +13,74 @@ impl Object { } } + /// An iterator visiting all keys in arbitrary order. The iterator element type is `PropertyKey`. #[inline] pub fn keys(&self) -> Keys<'_> { Keys(self.iter()) } + /// An iterator visiting all values in arbitrary order. The iterator element type is `&'a Property`. #[inline] pub fn values(&self) -> Values<'_> { Values(self.iter()) } + /// An iterator visiting all symbol key-value pairs in arbitrary order. The iterator element type is `(&'a RcSymbol, &'a Property)`. #[inline] pub fn symbol_properties(&self) -> SymbolProperties<'_> { SymbolProperties(self.symbol_properties.iter()) } + /// An iterator visiting all symbol keys in arbitrary order. The iterator element type is `&'a RcSymbol`. #[inline] pub fn symbol_property_keys(&self) -> SymbolPropertyKeys<'_> { SymbolPropertyKeys(self.symbol_properties.keys()) } + /// An iterator visiting all symbol values in arbitrary order. The iterator element type is `&'a Property`. #[inline] pub fn symbol_property_values(&self) -> SymbolPropertyValues<'_> { SymbolPropertyValues(self.symbol_properties.values()) } + /// An iterator visiting all indexed key-value pairs in arbitrary order. The iterator element type is `(&'a u32, &'a Property)`. #[inline] pub fn index_properties(&self) -> IndexProperties<'_> { IndexProperties(self.indexed_properties.iter()) } + /// An iterator visiting all index keys in arbitrary order. The iterator element type is `&'a u32`. #[inline] pub fn index_property_keys(&self) -> IndexPropertyKeys<'_> { IndexPropertyKeys(self.indexed_properties.keys()) } + /// An iterator visiting all index values in arbitrary order. The iterator element type is `&'a Property`. #[inline] pub fn index_property_values(&self) -> IndexPropertyValues<'_> { IndexPropertyValues(self.indexed_properties.values()) } + /// An iterator visiting all string key-value pairs in arbitrary order. The iterator element type is `(&'a RcString, &'a Property)`. #[inline] pub fn string_properties(&self) -> StringProperties<'_> { StringProperties(self.properties.iter()) } + /// An iterator visiting all string keys in arbitrary order. The iterator element type is `&'a RcString`. #[inline] pub fn string_property_keys(&self) -> StringPropertyKeys<'_> { StringPropertyKeys(self.properties.keys()) } + /// An iterator visiting all string values in arbitrary order. The iterator element type is `&'a Property`. #[inline] pub fn string_property_values(&self) -> StringPropertyValues<'_> { StringPropertyValues(self.properties.values()) } } +/// An iterator over the property entries of an `Object` #[derive(Debug, Clone)] pub struct Iter<'a> { indexed_properties: hash_map::Iter<'a, u32, Property>, @@ -98,6 +111,7 @@ impl ExactSizeIterator for Iter<'_> { impl FusedIterator for Iter<'_> {} +/// An iterator over the keys (`PropertyKey`) of an `Object`. #[derive(Debug, Clone)] pub struct Keys<'a>(Iter<'a>); @@ -118,6 +132,7 @@ impl ExactSizeIterator for Keys<'_> { impl FusedIterator for Keys<'_> {} +/// An iterator over the values (`Property`) of an `Object`. #[derive(Debug, Clone)] pub struct Values<'a>(Iter<'a>); @@ -138,6 +153,7 @@ impl ExactSizeIterator for Values<'_> { impl FusedIterator for Values<'_> {} +/// An iterator over the `Symbol` property entries of an `Object` #[derive(Debug, Clone)] pub struct SymbolProperties<'a>(hash_map::Iter<'a, RcSymbol, Property>); @@ -164,6 +180,7 @@ impl ExactSizeIterator for SymbolProperties<'_> { impl FusedIterator for SymbolProperties<'_> {} +/// An iterator over the keys (`RcSymbol`) of an `Object`. #[derive(Debug, Clone)] pub struct SymbolPropertyKeys<'a>(hash_map::Keys<'a, RcSymbol, Property>); @@ -190,6 +207,7 @@ impl ExactSizeIterator for SymbolPropertyKeys<'_> { impl FusedIterator for SymbolPropertyKeys<'_> {} +/// An iterator over the `Symbol` values (`Property`) of an `Object`. #[derive(Debug, Clone)] pub struct SymbolPropertyValues<'a>(hash_map::Values<'a, RcSymbol, Property>); @@ -216,6 +234,7 @@ impl ExactSizeIterator for SymbolPropertyValues<'_> { impl FusedIterator for SymbolPropertyValues<'_> {} +/// An iterator over the indexed property entries of an `Object` #[derive(Debug, Clone)] pub struct IndexProperties<'a>(hash_map::Iter<'a, u32, Property>); @@ -242,6 +261,7 @@ impl ExactSizeIterator for IndexProperties<'_> { impl FusedIterator for IndexProperties<'_> {} +/// An iterator over the index keys (`u32`) of an `Object`. #[derive(Debug, Clone)] pub struct IndexPropertyKeys<'a>(hash_map::Keys<'a, u32, Property>); @@ -268,6 +288,7 @@ impl ExactSizeIterator for IndexPropertyKeys<'_> { impl FusedIterator for IndexPropertyKeys<'_> {} +/// An iterator over the index values (`Property`) of an `Object`. #[derive(Debug, Clone)] pub struct IndexPropertyValues<'a>(hash_map::Values<'a, u32, Property>); @@ -294,6 +315,7 @@ impl ExactSizeIterator for IndexPropertyValues<'_> { impl FusedIterator for IndexPropertyValues<'_> {} +/// An iterator over the `String` property entries of an `Object` #[derive(Debug, Clone)] pub struct StringProperties<'a>(hash_map::Iter<'a, RcString, Property>); @@ -320,6 +342,7 @@ impl ExactSizeIterator for StringProperties<'_> { impl FusedIterator for StringProperties<'_> {} +/// An iterator over the string keys (`RcString`) of an `Object`. #[derive(Debug, Clone)] pub struct StringPropertyKeys<'a>(hash_map::Keys<'a, RcString, Property>); @@ -346,6 +369,7 @@ impl ExactSizeIterator for StringPropertyKeys<'_> { impl FusedIterator for StringPropertyKeys<'_> {} +/// An iterator over the string values (`Property`) of an `Object`. #[derive(Debug, Clone)] pub struct StringPropertyValues<'a>(hash_map::Values<'a, RcString, Property>); From 5ebcb371bec2e8e2d2a05b15b84a0d8da4d9909a Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Sat, 8 Aug 2020 13:38:36 +0200 Subject: [PATCH 25/27] Added more documentation to iterators --- boa/src/builtins/object/iter.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/boa/src/builtins/object/iter.rs b/boa/src/builtins/object/iter.rs index 86a5e893644..f20761439d0 100644 --- a/boa/src/builtins/object/iter.rs +++ b/boa/src/builtins/object/iter.rs @@ -4,6 +4,8 @@ use std::iter::FusedIterator; impl Object { /// An iterator visiting all key-value pairs in arbitrary order. The iterator element type is `(PropertyKey, &'a Property)`. + /// + /// This iterator does not recurse down the prototype chain. #[inline] pub fn iter(&self) -> Iter<'_> { Iter { @@ -14,66 +16,89 @@ impl Object { } /// An iterator visiting all keys in arbitrary order. The iterator element type is `PropertyKey`. + /// + /// This iterator does not recurse down the prototype chain. #[inline] pub fn keys(&self) -> Keys<'_> { Keys(self.iter()) } /// An iterator visiting all values in arbitrary order. The iterator element type is `&'a Property`. + /// + /// This iterator does not recurse down the prototype chain. #[inline] pub fn values(&self) -> Values<'_> { Values(self.iter()) } /// An iterator visiting all symbol key-value pairs in arbitrary order. The iterator element type is `(&'a RcSymbol, &'a Property)`. + /// + /// + /// This iterator does not recurse down the prototype chain. #[inline] pub fn symbol_properties(&self) -> SymbolProperties<'_> { SymbolProperties(self.symbol_properties.iter()) } /// An iterator visiting all symbol keys in arbitrary order. The iterator element type is `&'a RcSymbol`. + /// + /// This iterator does not recurse down the prototype chain. #[inline] pub fn symbol_property_keys(&self) -> SymbolPropertyKeys<'_> { SymbolPropertyKeys(self.symbol_properties.keys()) } /// An iterator visiting all symbol values in arbitrary order. The iterator element type is `&'a Property`. + /// + /// This iterator does not recurse down the prototype chain. #[inline] pub fn symbol_property_values(&self) -> SymbolPropertyValues<'_> { SymbolPropertyValues(self.symbol_properties.values()) } /// An iterator visiting all indexed key-value pairs in arbitrary order. The iterator element type is `(&'a u32, &'a Property)`. + /// + /// This iterator does not recurse down the prototype chain. #[inline] pub fn index_properties(&self) -> IndexProperties<'_> { IndexProperties(self.indexed_properties.iter()) } /// An iterator visiting all index keys in arbitrary order. The iterator element type is `&'a u32`. + /// + /// This iterator does not recurse down the prototype chain. #[inline] pub fn index_property_keys(&self) -> IndexPropertyKeys<'_> { IndexPropertyKeys(self.indexed_properties.keys()) } /// An iterator visiting all index values in arbitrary order. The iterator element type is `&'a Property`. + /// + /// This iterator does not recurse down the prototype chain. #[inline] pub fn index_property_values(&self) -> IndexPropertyValues<'_> { IndexPropertyValues(self.indexed_properties.values()) } /// An iterator visiting all string key-value pairs in arbitrary order. The iterator element type is `(&'a RcString, &'a Property)`. + /// + /// This iterator does not recurse down the prototype chain. #[inline] pub fn string_properties(&self) -> StringProperties<'_> { StringProperties(self.properties.iter()) } /// An iterator visiting all string keys in arbitrary order. The iterator element type is `&'a RcString`. + /// + /// This iterator does not recurse down the prototype chain. #[inline] pub fn string_property_keys(&self) -> StringPropertyKeys<'_> { StringPropertyKeys(self.properties.keys()) } /// An iterator visiting all string values in arbitrary order. The iterator element type is `&'a Property`. + /// + /// This iterator does not recurse down the prototype chain. #[inline] pub fn string_property_values(&self) -> StringPropertyValues<'_> { StringPropertyValues(self.properties.values()) From 548b295c2d72a1287018fce2856dab76979fa0b1 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Sat, 8 Aug 2020 18:02:06 +0200 Subject: [PATCH 26/27] Rustfmt --- boa/src/builtins/object/iter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boa/src/builtins/object/iter.rs b/boa/src/builtins/object/iter.rs index f20761439d0..a9e5f68bf41 100644 --- a/boa/src/builtins/object/iter.rs +++ b/boa/src/builtins/object/iter.rs @@ -33,7 +33,7 @@ impl Object { /// An iterator visiting all symbol key-value pairs in arbitrary order. The iterator element type is `(&'a RcSymbol, &'a Property)`. /// - /// + /// /// This iterator does not recurse down the prototype chain. #[inline] pub fn symbol_properties(&self) -> SymbolProperties<'_> { From ff818d409ac7f5dd800c4ea0bd26e56b19289314 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 12 Aug 2020 21:46:20 +0200 Subject: [PATCH 27/27] Optimized `.to_property_key()` --- boa/src/builtins/property/mod.rs | 11 +++++++++++ boa/src/exec/mod.rs | 22 +++++++++++++++------- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/boa/src/builtins/property/mod.rs b/boa/src/builtins/property/mod.rs index ed32cdd9960..cf539efb5a8 100644 --- a/boa/src/builtins/property/mod.rs +++ b/boa/src/builtins/property/mod.rs @@ -449,6 +449,17 @@ impl From for PropertyKey { } } +impl From for PropertyKey { + fn from(value: f64) -> Self { + use num_traits::cast::FromPrimitive; + if let Some(index) = u32::from_f64(value) { + return PropertyKey::Index(index); + } + + PropertyKey::String(ryu_js::Buffer::new().format(value).into()) + } +} + impl PartialEq<&str> for PropertyKey { fn eq(&self, other: &&str) -> bool { match self { diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index caf75a9901e..85e5fe8ef52 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -510,13 +510,21 @@ impl Interpreter { /// https://tc39.es/ecma262/#sec-topropertykey #[allow(clippy::wrong_self_convention)] pub(crate) fn to_property_key(&mut self, value: &Value) -> Result { - let key = self.to_primitive(value, PreferredType::String)?; - if let Value::Symbol(ref symbol) = key { - Ok(PropertyKey::from(symbol.clone())) - } else { - let string = self.to_string(&key)?; - Ok(PropertyKey::from(string)) - } + Ok(match *value { + // Fast path: + Value::Integer(integer) => integer.into(), + Value::Rational(rational) => rational.into(), + Value::String(ref string) => string.clone().into(), + Value::Symbol(ref symbol) => symbol.clone().into(), + // Slow path: + _ => match self.to_primitive(value, PreferredType::String)? { + Value::Integer(integer) => integer.into(), + Value::Rational(rational) => rational.into(), + Value::String(ref string) => string.clone().into(), + Value::Symbol(ref symbol) => symbol.clone().into(), + primitive => self.to_string(&primitive)?.into(), + }, + }) } /// https://tc39.es/ecma262/#sec-hasproperty