diff --git a/boa/src/builtins/array/array_iterator.rs b/boa/src/builtins/array/array_iterator.rs index 55593ce96a9..00fe2329d36 100644 --- a/boa/src/builtins/array/array_iterator.rs +++ b/boa/src/builtins/array/array_iterator.rs @@ -1,7 +1,7 @@ use crate::{ builtins::{function::make_builtin_fn, iterable::create_iter_result_object, Array, Value}, object::ObjectData, - property::{Attribute, Property}, + property::{Attribute, DataDescriptor}, BoaProfiler, Context, Result, }; use gc::{Finalize, Trace}; @@ -131,8 +131,7 @@ impl ArrayIterator { .set_prototype_instance(iterator_prototype); let to_string_tag = ctx.well_known_symbols().to_string_tag_symbol(); - let to_string_tag_property = - Property::data_descriptor(Value::string("Array Iterator"), Attribute::CONFIGURABLE); + let to_string_tag_property = DataDescriptor::new("Array Iterator", Attribute::CONFIGURABLE); array_iterator.set_property(to_string_tag, to_string_tag_property); array_iterator } diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index f25bbd30413..fb1e8b196e9 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -17,7 +17,7 @@ use crate::{ builtins::array::array_iterator::{ArrayIterationKind, ArrayIterator}, builtins::BuiltIn, object::{ConstructorBuilder, FunctionBuilder, ObjectData, PROTOTYPE}, - property::{Attribute, Property}, + property::{Attribute, DataDescriptor}, value::{same_value_zero, Value}, BoaProfiler, Context, Result, }; @@ -129,8 +129,8 @@ impl Array { } // finally create length property - let length = Property::data_descriptor( - length.into(), + let length = DataDescriptor::new( + length, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ); @@ -171,8 +171,8 @@ impl Array { } // Create length - let length = Property::data_descriptor( - array_contents.len().into(), + let length = DataDescriptor::new( + array_contents.len(), Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ); array_obj_ptr.set_property("length".to_string(), length); diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs index 365b0ffb8a7..d343a0439d3 100644 --- a/boa/src/builtins/function/mod.rs +++ b/boa/src/builtins/function/mod.rs @@ -15,7 +15,7 @@ use crate::{ builtins::{Array, BuiltIn}, environment::lexical_environment::Environment, object::{ConstructorBuilder, FunctionBuilder, Object, ObjectData, PROTOTYPE}, - property::{Attribute, Property}, + property::{Attribute, DataDescriptor}, syntax::ast::node::{FormalParameter, RcStatementList}, BoaProfiler, Context, Result, Value, }; @@ -174,16 +174,16 @@ pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value { let len = arguments_list.len(); let mut obj = Object::default(); // Set length - let length = Property::data_descriptor( - len.into(), + let length = DataDescriptor::new( + len, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ); // Define length as a property - obj.define_own_property("length", length); + obj.define_own_property("length", length.into()); let mut index: usize = 0; while index < len { let val = arguments_list.get(index).expect("Could not get argument"); - let prop = Property::data_descriptor( + let prop = DataDescriptor::new( val.clone(), Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, ); @@ -222,14 +222,14 @@ pub fn make_constructor_fn( let mut constructor = Object::function(function, global.get_field("Function").get_field(PROTOTYPE)); - let length = Property::data_descriptor( - length.into(), + let length = DataDescriptor::new( + length, Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ); constructor.insert("length", length); - let name = Property::data_descriptor( - name.into(), + let name = DataDescriptor::new( + name, Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ); constructor.insert("name", name); diff --git a/boa/src/builtins/iterable/mod.rs b/boa/src/builtins/iterable/mod.rs index 3f3ab44209a..43f52bf2f3e 100644 --- a/boa/src/builtins/iterable/mod.rs +++ b/boa/src/builtins/iterable/mod.rs @@ -2,7 +2,7 @@ use crate::{ builtins::string::string_iterator::StringIterator, builtins::ArrayIterator, object::{GcObject, ObjectInitializer}, - property::Property, + property::{Attribute, DataDescriptor}, BoaProfiler, Context, Result, Value, }; @@ -47,8 +47,9 @@ impl IteratorPrototypes { /// Generates an object supporting the IteratorResult interface. pub fn create_iter_result_object(ctx: &mut Context, value: Value, done: bool) -> Value { let object = Value::new_object(Some(ctx.global_object())); - let value_property = Property::default().value(value); - let done_property = Property::default().value(Value::boolean(done)); + // TODO: Fix attributes of value and done + let value_property = DataDescriptor::new(value, Attribute::all()); + let done_property = DataDescriptor::new(done, Attribute::all()); object.set_property("value", value_property); object.set_property("done", done_property); object diff --git a/boa/src/builtins/json/mod.rs b/boa/src/builtins/json/mod.rs index 6bcf0e6b530..d1c6107010d 100644 --- a/boa/src/builtins/json/mod.rs +++ b/boa/src/builtins/json/mod.rs @@ -16,7 +16,7 @@ use crate::{ builtins::BuiltIn, object::ObjectInitializer, - property::{Attribute, Property, PropertyKey}, + property::{Attribute, DataDescriptor, PropertyKey}, BoaProfiler, Context, Result, Value, }; use serde_json::{self, Value as JSONValue}; @@ -160,11 +160,14 @@ impl Json { let this_arg = object.clone(); object_to_return.set_property( key.to_owned(), - Property::default().value(ctx.call( - replacer, - &this_arg, - &[Value::from(key.clone()), val.clone()], - )?), + DataDescriptor::new( + ctx.call( + replacer, + &this_arg, + &[Value::from(key.clone()), val.clone()], + )?, + Attribute::all(), + ), ); } Ok(Value::from(object_to_return.to_json(ctx)?.to_string())) diff --git a/boa/src/builtins/map/mod.rs b/boa/src/builtins/map/mod.rs index cf925ba40f9..632da4de0e8 100644 --- a/boa/src/builtins/map/mod.rs +++ b/boa/src/builtins/map/mod.rs @@ -3,7 +3,7 @@ use crate::{ builtins::BuiltIn, object::{ConstructorBuilder, ObjectData, PROTOTYPE}, - property::{Attribute, Property}, + property::{Attribute, DataDescriptor}, BoaProfiler, Context, Result, Value, }; use ordered_map::OrderedMap; @@ -100,8 +100,8 @@ impl Map { /// Helper function to set the size property. fn set_size(this: &Value, size: usize) { - let size = Property::data_descriptor( - size.into(), + let size = DataDescriptor::new( + size, Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ); diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index 594b54f8f25..89096330770 100644 --- a/boa/src/builtins/mod.rs +++ b/boa/src/builtins/mod.rs @@ -43,7 +43,7 @@ pub(crate) use self::{ undefined::Undefined, }; use crate::{ - property::{Attribute, Property}, + property::{Attribute, DataDescriptor}, Context, Value, }; @@ -96,7 +96,7 @@ pub fn init(context: &mut Context) { for init in &globals { let (name, value, attribute) = init(context); - let property = Property::data_descriptor(value, attribute); + let property = DataDescriptor::new(value, attribute); global_object.borrow_mut().insert(name, property); } } diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 011fc2d6074..bf0b031fa9e 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -16,7 +16,7 @@ use crate::{ builtins::BuiltIn, object::{ConstructorBuilder, Object as BuiltinObject, ObjectData}, - property::{Attribute, Property}, + property::Attribute, value::{same_value, Value}, BoaProfiler, Context, Result, }; @@ -134,13 +134,18 @@ impl Object { } /// Define a property in an object - pub fn define_property(_: &Value, args: &[Value], ctx: &mut Context) -> Result { + pub fn define_property(_: &Value, args: &[Value], context: &mut Context) -> Result { let obj = args.get(0).expect("Cannot get object"); let prop = args .get(1) .expect("Cannot get object") - .to_property_key(ctx)?; - let desc = Property::from(args.get(2).expect("Cannot get object")); + .to_property_key(context)?; + + let desc = if let Value::Object(ref object) = args.get(2).cloned().unwrap_or_default() { + object.to_property_descriptor(context)? + } else { + return context.throw_type_error("Property description must be an object"); + }; obj.set_property(prop, desc); Ok(Value::undefined()) } @@ -246,12 +251,10 @@ impl Object { }; let key = key.to_property_key(ctx)?; - let own_property = this - .to_object(ctx) - .map(|obj| obj.borrow().get_own_property(&key)); + let own_property = this.to_object(ctx)?.borrow().get_own_property(&key); Ok(own_property.map_or(Value::from(false), |own_prop| { - Value::from(own_prop.enumerable_or(false)) + Value::from(own_prop.enumerable()) })) } } diff --git a/boa/src/builtins/regexp/mod.rs b/boa/src/builtins/regexp/mod.rs index 6e6a3e68fb5..eb86947fc75 100644 --- a/boa/src/builtins/regexp/mod.rs +++ b/boa/src/builtins/regexp/mod.rs @@ -13,7 +13,7 @@ use crate::{ builtins::BuiltIn, gc::{empty_trace, Finalize, Trace}, object::{ConstructorBuilder, ObjectData}, - property::{Attribute, Property}, + property::{Attribute, DataDescriptor}, value::{RcString, Value}, BoaProfiler, Context, Result, }; @@ -370,9 +370,9 @@ impl RegExp { let result = Value::from(result); result.set_property( "index", - Property::default().value(Value::from(m.total().start)), + DataDescriptor::new(m.total().start, Attribute::all()), ); - result.set_property("input", Property::default().value(Value::from(arg_str))); + result.set_property("input", DataDescriptor::new(arg_str, Attribute::all())); result } else { if regex.use_last_index { @@ -473,11 +473,11 @@ impl RegExp { match_val.set_property( "index", - Property::default().value(Value::from(mat.total().start)), + DataDescriptor::new(mat.total().start, Attribute::all()), ); match_val.set_property( "input", - Property::default().value(Value::from(arg_str.clone())), + DataDescriptor::new(arg_str.clone(), Attribute::all()), ); matches.push(match_val); diff --git a/boa/src/builtins/string/string_iterator.rs b/boa/src/builtins/string/string_iterator.rs index c75f6b679ce..1b12d0065ed 100644 --- a/boa/src/builtins/string/string_iterator.rs +++ b/boa/src/builtins/string/string_iterator.rs @@ -2,7 +2,7 @@ use crate::builtins::string::code_point_at; use crate::{ builtins::{function::make_builtin_fn, iterable::create_iter_result_object}, object::ObjectData, - property::{Attribute, Property}, + property::{Attribute, DataDescriptor}, BoaProfiler, Context, Result, Value, }; use gc::{Finalize, Trace}; @@ -82,7 +82,7 @@ impl StringIterator { let to_string_tag = ctx.well_known_symbols().to_string_tag_symbol(); let to_string_tag_property = - Property::data_descriptor(Value::string("String Iterator"), Attribute::CONFIGURABLE); + DataDescriptor::new("String Iterator", Attribute::CONFIGURABLE); array_iterator.set_property(to_string_tag, to_string_tag_property); array_iterator } diff --git a/boa/src/context.rs b/boa/src/context.rs index 314c37da4a6..e94b1f9eb59 100644 --- a/boa/src/context.rs +++ b/boa/src/context.rs @@ -10,7 +10,7 @@ use crate::{ class::{Class, ClassBuilder}, exec::Interpreter, object::{GcObject, Object, ObjectData, PROTOTYPE}, - property::{Property, PropertyKey}, + property::{DataDescriptor, PropertyKey}, realm::Realm, syntax::{ ast::{ @@ -608,7 +608,7 @@ impl Context { T::init(&mut class_builder)?; let class = class_builder.build(); - let property = Property::data_descriptor(class.into(), T::ATTRIBUTE); + let property = DataDescriptor::new(class, T::ATTRIBUTE); self.global_object() .as_object_mut() .unwrap() diff --git a/boa/src/environment/global_environment_record.rs b/boa/src/environment/global_environment_record.rs index d0be6cadb61..2e88ec6f02c 100644 --- a/boa/src/environment/global_environment_record.rs +++ b/boa/src/environment/global_environment_record.rs @@ -14,7 +14,7 @@ use crate::{ lexical_environment::{Environment, EnvironmentType}, object_environment_record::ObjectEnvironmentRecord, }, - property::{Attribute, Property}, + property::{Attribute, DataDescriptor}, Value, }; use gc::{Finalize, Trace}; @@ -71,16 +71,16 @@ impl GlobalEnvironmentRecord { let global_object = &mut self.object_record.bindings; let existing_prop = global_object.get_property(name); if let Some(prop) = existing_prop { - if prop.value.is_none() || prop.configurable_or(false) { + if prop.value.is_none() || prop.configurable() { let mut property = - Property::data_descriptor(value, Attribute::WRITABLE | Attribute::ENUMERABLE); + DataDescriptor::new(value, Attribute::WRITABLE | Attribute::ENUMERABLE); property.set_configurable(deletion); global_object.update_property(name, property); } } else { let mut property = - Property::data_descriptor(value, Attribute::WRITABLE | Attribute::ENUMERABLE); + DataDescriptor::new(value, Attribute::WRITABLE | Attribute::ENUMERABLE); property.set_configurable(deletion); global_object.update_property(name, property); diff --git a/boa/src/environment/object_environment_record.rs b/boa/src/environment/object_environment_record.rs index 0741a39f9cf..4e9e1f4aa7f 100644 --- a/boa/src/environment/object_environment_record.rs +++ b/boa/src/environment/object_environment_record.rs @@ -11,7 +11,7 @@ use crate::{ environment_record_trait::EnvironmentRecordTrait, lexical_environment::{Environment, EnvironmentType}, }, - property::{Attribute, Property}, + property::{Attribute, DataDescriptor}, Value, }; use gc::{Finalize, Trace}; @@ -39,7 +39,7 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord { // TODO: could save time here and not bother generating a new undefined object, // only for it to be replace with the real value later. We could just add the name to a Vector instead let bindings = &mut self.bindings; - let mut prop = Property::data_descriptor( + let mut prop = DataDescriptor::new( Value::undefined(), Attribute::WRITABLE | Attribute::ENUMERABLE, ); @@ -63,7 +63,7 @@ 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); + let mut property = DataDescriptor::new(value, Attribute::ENUMERABLE); property.set_configurable(strict); self.bindings.update_property(name, property); } diff --git a/boa/src/object/gcobject.rs b/boa/src/object/gcobject.rs index 3776ff12376..18fd06a2ae8 100644 --- a/boa/src/object/gcobject.rs +++ b/boa/src/object/gcobject.rs @@ -10,6 +10,7 @@ use crate::{ environment::{ function_environment_record::BindingStatus, lexical_environment::new_function_environment, }, + property::{AccessorDescriptor, Attribute, DataDescriptor, PropertyDescriptor, PropertyKey}, syntax::ast::node::RcStatementList, value::PreferredType, Context, Executable, Result, Value, @@ -374,6 +375,81 @@ impl GcObject { Ok(JSONValue::Object(new_obj)) } } + + pub fn to_property_descriptor(&self, context: &mut Context) -> Result { + let mut attribute = Attribute::empty(); + + let enumerable_key = PropertyKey::from("enumerable"); + if self.borrow().has_property(&enumerable_key) + && self.borrow().get(&enumerable_key).to_boolean() + { + attribute |= Attribute::ENUMERABLE; + } + + let configurable_key = PropertyKey::from("configurable"); + if self.borrow().has_property(&configurable_key) + && self.borrow().get(&configurable_key).to_boolean() + { + attribute |= Attribute::CONFIGURABLE; + } + + let mut value = None; + let value_key = PropertyKey::from("value"); + if self.borrow().has_property(&value_key) { + value = Some(self.borrow().get(&value_key)); + } + + let mut has_writable = false; + let writable_key = PropertyKey::from("writable"); + if self.borrow().has_property(&writable_key) { + has_writable = true; + if self.borrow().get(&writable_key).to_boolean() { + attribute |= Attribute::WRITABLE; + } + } + + let mut get = None; + let get_key = PropertyKey::from("get"); + if self.borrow().has_property(&get_key) { + let getter = self.borrow().get(&get_key); + match getter { + Value::Object(ref object) if object.borrow().is_callable() => { + get = Some(object.clone()); + } + _ => { + return Err( + context.construct_type_error("Property descriptor getter must be callable") + ); + } + } + } + + let mut set = None; + let set_key = PropertyKey::from("set"); + if self.borrow().has_property(&set_key) { + let setter = self.borrow().get(&set_key); + match setter { + Value::Object(ref object) if object.borrow().is_callable() => { + set = Some(object.clone()); + } + _ => { + return Err( + context.construct_type_error("Property descriptor setter must be callable") + ); + } + }; + } + + if get.is_some() || set.is_some() { + if value.is_some() || has_writable { + return Err(context.construct_type_error("Invalid property descriptor. Cannot both specify accessors and a value or writable attribute")); + } + + Ok(AccessorDescriptor::new(get, set, attribute).into()) + } else { + Ok(DataDescriptor::new(value.unwrap_or_else(Value::undefined), attribute).into()) + } + } } impl AsRef> for GcObject { diff --git a/boa/src/object/internal_methods.rs b/boa/src/object/internal_methods.rs index dbb086ac9c0..60c081c2a6e 100644 --- a/boa/src/object/internal_methods.rs +++ b/boa/src/object/internal_methods.rs @@ -7,7 +7,7 @@ use crate::{ object::Object, - property::{Attribute, Property, PropertyKey}, + property::{Attribute, DataDescriptor, PropertyDescriptor, PropertyKey}, value::{same_value, Value}, BoaProfiler, Context, Result, }; @@ -19,12 +19,12 @@ impl Object { /// - [ECMAScript reference][spec] /// /// [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); + pub fn has_property(&self, key: &PropertyKey) -> bool { + let prop = self.get_own_property(key); if prop.is_none() { let parent = self.get_prototype_of(); return if let Value::Object(ref object) = parent { - object.borrow().has_property(property_key) + object.borrow().has_property(key) } else { false }; @@ -56,75 +56,66 @@ impl Object { } /// Delete property. - pub fn delete(&mut self, property_key: &PropertyKey) -> bool { - let desc = self.get_own_property(property_key); - if desc - .value - .clone() - .expect("unable to get value") - .is_undefined() - { - return true; - } - if desc.configurable_or(false) { - self.remove_property(&property_key); - return true; + pub fn delete(&mut self, key: &PropertyKey) -> bool { + match self.get_own_property(key) { + Some(desc) if desc.configurable() => { + self.remove_property(&key); + true + } + Some(_) => false, + None => true, } - - false } /// [[Get]] /// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-get-p-receiver - pub fn get(&self, property_key: &PropertyKey) -> Value { - let desc = self.get_own_property(property_key); - if desc.value.clone().is_none() - || desc - .value - .clone() - .expect("Failed to get object") - .is_undefined() - { - // parent will either be null or an Object - let parent = self.get_prototype_of(); - if parent.is_null() { - return Value::undefined(); - } + pub fn get(&self, key: &PropertyKey) -> Value { + match self.get_own_property(key) { + None => { + // parent will either be null or an Object + let parent = self.get_prototype_of(); + if parent.is_null() { + return Value::undefined(); + } - return parent.get_field(property_key.clone()); - } + parent.get_field(key.clone()) + } + Some(desc) => { + if desc.is_data_descriptor() { + return desc.value.clone().expect("failed to extract value"); + } - if desc.is_data_descriptor() { - return desc.value.clone().expect("failed to extract value"); - } + let getter = desc.get.clone(); + if getter.is_none() || getter.expect("Failed to get object").is_undefined() { + return Value::undefined(); + } - let getter = desc.get.clone(); - if getter.is_none() || getter.expect("Failed to get object").is_undefined() { - return Value::undefined(); + // TODO: Call getter from here! + Value::undefined() + } } - - // TODO: Call getter from here! - Value::undefined() } /// [[Set]] /// - pub fn set(&mut self, property_key: PropertyKey, val: Value) -> bool { + pub fn set(&mut self, 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); - // [2] - if own_desc.is_none() { + let mut own_desc = if let Some(desc) = self.get_own_property(&key) { + desc + } else { let parent = self.get_prototype_of(); if !parent.is_null() { // TODO: come back to this } - own_desc = Property::data_descriptor( + DataDescriptor::new( Value::undefined(), Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, - ); - } + ) + .into() + }; + // [3] if own_desc.is_data_descriptor() { if !own_desc.writable() { @@ -132,8 +123,8 @@ impl Object { } // Change value on the current descriptor - own_desc = own_desc.value(val); - return self.define_own_property(property_key, own_desc); + own_desc.value = Some(val); + return self.define_own_property(key, own_desc); } // [4] debug_assert!(own_desc.is_accessor_descriptor()); @@ -151,38 +142,33 @@ 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, key: K, desc: Property) -> bool + pub fn define_own_property(&mut self, key: K, desc: PropertyDescriptor) -> bool where K: Into, { let _timer = BoaProfiler::global().start_event("Object::define_own_property", "object"); let key = key.into(); - let mut current = self.get_own_property(&key); let extensible = self.is_extensible(); - // https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor - // There currently isn't a property, lets create a new one - if current.value.is_none() || current.value.as_ref().expect("failed").is_undefined() { + let mut current = if let Some(desc) = self.get_own_property(&key) { + desc + } else { if !extensible { return false; } self.insert(key, desc); return true; - } - // If every field is absent we don't need to set anything - if desc.is_none() { - return true; - } + }; // 4 - if !current.configurable_or(false) { - if desc.configurable_or(false) { + if !current.configurable() { + if desc.configurable() { return false; } - if desc.enumerable_or(false) != current.enumerable_or(false) { + if desc.enumerable() != current.enumerable() { return false; } } @@ -213,7 +199,7 @@ impl Object { } else if current.is_data_descriptor() && desc.is_data_descriptor() { // a if !current.configurable() && !current.writable() { - if desc.writable_or(false) { + if desc.writable() { return false; } @@ -259,27 +245,16 @@ 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, key: &PropertyKey) -> Property { + pub fn get_own_property(&self, key: &PropertyKey) -> Option { let _timer = BoaProfiler::global().start_event("Object::get_own_property", "object"); - // Prop could either be a String or Symbol let property = match key { PropertyKey::Index(index) => self.indexed_properties.get(&index), PropertyKey::String(ref st) => self.string_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 - }) + + property.cloned() } /// Essential internal method OwnPropertyKeys @@ -301,14 +276,15 @@ impl Object { pub fn define_properties(&mut self, props: Value, ctx: &mut Context) -> Result<()> { let props = props.to_object(ctx)?; let keys = props.borrow().own_property_keys(); - let mut descriptors: Vec<(PropertyKey, Property)> = Vec::new(); + let mut descriptors: Vec<(PropertyKey, PropertyDescriptor)> = Vec::new(); for next_key in keys { - let prop_desc = props.borrow().get_own_property(&next_key); - if prop_desc.enumerable() { - let desc_obj = props.borrow().get(&next_key); - let desc = Property::from(&desc_obj); - descriptors.push((next_key, desc)); + if let Some(prop_desc) = props.borrow().get_own_property(&next_key) { + if prop_desc.enumerable() { + let desc_obj = props.borrow().get(&next_key); + let desc = desc_obj.to_property_descriptor(ctx)?; + descriptors.push((next_key, desc)); + } } } @@ -319,45 +295,46 @@ impl Object { Ok(()) } - // /// `Object.setPropertyOf(obj, prototype)` - // /// - // /// This method sets the prototype (i.e., the internal `[[Prototype]]` property) - // /// of a specified object to another object or `null`. - // /// - // /// More information: - // /// - [ECMAScript reference][spec] - // /// - [MDN documentation][mdn] - // /// - // /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-setprototypeof-v - // /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf - // pub fn set_prototype_of(&mut self, val: Value) -> bool { - // debug_assert!(val.is_object() || val.is_null()); - // let current = self.prototype.clone(); - // if same_value(¤t, &val) { - // return true; - // } - // if !self.is_extensible() { - // return false; - // } - // let mut p = val.clone(); - // let mut done = false; - // while !done { - // if p.is_null() { - // done = true - // } else if same_value(&Value::from(self.clone()), &p) { - // return false; - // } else { - // let prototype = p - // .as_object() - // .expect("prototype should be null or object") - // .prototype - // .clone(); - // p = prototype; - // } - // } - // self.prototype = val; - // true - // } + /// `Object.setPropertyOf(obj, prototype)` + /// + /// This method sets the prototype (i.e., the internal `[[Prototype]]` property) + /// of a specified object to another object or `null`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-setprototypeof-v + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf + pub fn set_prototype_of(&mut self, _val: Value) -> bool { + // debug_assert!(val.is_object() || val.is_null()); + // let current = self.prototype.clone(); + // if same_value(¤t, &val) { + // return true; + // } + // if !self.is_extensible() { + // return false; + // } + // let mut p = val.clone(); + // let mut done = false; + // while !done { + // if p.is_null() { + // done = true + // } else if same_value(&Value::from(self.clone()), &p) { + // return false; + // } else { + // let prototype = p + // .as_object() + // .expect("prototype should be null or object") + // .prototype + // .clone(); + // p = prototype; + // } + // } + // self.prototype = val; + // true + todo!("Object.setPropertyOf(obj, prototype)") + } /// Returns either the prototype or null /// @@ -373,10 +350,12 @@ impl Object { /// Helper function for property insertion. #[inline] - pub(crate) fn insert(&mut self, key: K, property: Property) -> Option + pub(crate) fn insert(&mut self, key: K, property: P) -> Option where K: Into, + P: Into, { + let property = property.into(); match key.into() { PropertyKey::Index(index) => self.indexed_properties.insert(index, property), PropertyKey::String(ref string) => { @@ -390,7 +369,7 @@ impl Object { /// Helper function for property removal. #[inline] - pub(crate) fn remove_property(&mut self, key: &PropertyKey) -> Option { + pub(crate) fn remove_property(&mut self, key: &PropertyKey) -> Option { match key { PropertyKey::Index(index) => self.indexed_properties.remove(&index), PropertyKey::String(ref string) => self.string_properties.remove(string), @@ -408,20 +387,11 @@ impl Object { key: K, value: V, attribute: Attribute, - ) -> Option + ) -> Option where K: Into, V: Into, { - self.insert( - key.into(), - Property::data_descriptor( - value.into(), - attribute - | Attribute::HAS_WRITABLE - | Attribute::HAS_ENUMERABLE - | Attribute::HAS_CONFIGURABLE, - ), - ) + self.insert(key.into(), DataDescriptor::new(value, attribute)) } } diff --git a/boa/src/object/iter.rs b/boa/src/object/iter.rs index 504f88c2945..9eb8cefe04f 100644 --- a/boa/src/object/iter.rs +++ b/boa/src/object/iter.rs @@ -1,4 +1,4 @@ -use super::{Object, Property, PropertyKey}; +use super::{Object, PropertyDescriptor, PropertyKey}; use crate::value::{RcString, RcSymbol}; use std::{collections::hash_map, iter::FusedIterator}; @@ -108,13 +108,13 @@ impl Object { /// An iterator over the property entries of an `Object` #[derive(Debug, Clone)] 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>, + indexed_properties: hash_map::Iter<'a, u32, PropertyDescriptor>, + string_properties: hash_map::Iter<'a, RcString, PropertyDescriptor>, + symbol_properties: hash_map::Iter<'a, RcSymbol, PropertyDescriptor>, } impl<'a> Iterator for Iter<'a> { - type Item = (PropertyKey, &'a Property); + type Item = (PropertyKey, &'a PropertyDescriptor); fn next(&mut self) -> Option { if let Some((key, value)) = self.indexed_properties.next() { Some(((*key).into(), value)) @@ -162,7 +162,7 @@ impl FusedIterator for Keys<'_> {} pub struct Values<'a>(Iter<'a>); impl<'a> Iterator for Values<'a> { - type Item = &'a Property; + type Item = &'a PropertyDescriptor; fn next(&mut self) -> Option { let (_, value) = self.0.next()?; Some(value) @@ -180,10 +180,10 @@ 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>); +pub struct SymbolProperties<'a>(hash_map::Iter<'a, RcSymbol, PropertyDescriptor>); impl<'a> Iterator for SymbolProperties<'a> { - type Item = (&'a RcSymbol, &'a Property); + type Item = (&'a RcSymbol, &'a PropertyDescriptor); #[inline] fn next(&mut self) -> Option { @@ -207,7 +207,7 @@ 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>); +pub struct SymbolPropertyKeys<'a>(hash_map::Keys<'a, RcSymbol, PropertyDescriptor>); impl<'a> Iterator for SymbolPropertyKeys<'a> { type Item = &'a RcSymbol; @@ -234,10 +234,10 @@ 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>); +pub struct SymbolPropertyValues<'a>(hash_map::Values<'a, RcSymbol, PropertyDescriptor>); impl<'a> Iterator for SymbolPropertyValues<'a> { - type Item = &'a Property; + type Item = &'a PropertyDescriptor; #[inline] fn next(&mut self) -> Option { @@ -261,10 +261,10 @@ 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>); +pub struct IndexProperties<'a>(hash_map::Iter<'a, u32, PropertyDescriptor>); impl<'a> Iterator for IndexProperties<'a> { - type Item = (&'a u32, &'a Property); + type Item = (&'a u32, &'a PropertyDescriptor); #[inline] fn next(&mut self) -> Option { @@ -288,7 +288,7 @@ 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>); +pub struct IndexPropertyKeys<'a>(hash_map::Keys<'a, u32, PropertyDescriptor>); impl<'a> Iterator for IndexPropertyKeys<'a> { type Item = &'a u32; @@ -315,10 +315,10 @@ 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>); +pub struct IndexPropertyValues<'a>(hash_map::Values<'a, u32, PropertyDescriptor>); impl<'a> Iterator for IndexPropertyValues<'a> { - type Item = &'a Property; + type Item = &'a PropertyDescriptor; #[inline] fn next(&mut self) -> Option { @@ -342,10 +342,10 @@ 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>); +pub struct StringProperties<'a>(hash_map::Iter<'a, RcString, PropertyDescriptor>); impl<'a> Iterator for StringProperties<'a> { - type Item = (&'a RcString, &'a Property); + type Item = (&'a RcString, &'a PropertyDescriptor); #[inline] fn next(&mut self) -> Option { @@ -369,7 +369,7 @@ 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>); +pub struct StringPropertyKeys<'a>(hash_map::Keys<'a, RcString, PropertyDescriptor>); impl<'a> Iterator for StringPropertyKeys<'a> { type Item = &'a RcString; @@ -396,10 +396,10 @@ 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>); +pub struct StringPropertyValues<'a>(hash_map::Values<'a, RcString, PropertyDescriptor>); impl<'a> Iterator for StringPropertyValues<'a> { - type Item = &'a Property; + type Item = &'a PropertyDescriptor; #[inline] fn next(&mut self) -> Option { diff --git a/boa/src/object/mod.rs b/boa/src/object/mod.rs index 655ac5161ef..65b72cd8eea 100644 --- a/boa/src/object/mod.rs +++ b/boa/src/object/mod.rs @@ -10,7 +10,7 @@ use crate::{ }, context::StandardConstructor, gc::{Finalize, Trace}, - property::{Attribute, Property, PropertyKey}, + property::{Attribute, DataDescriptor, PropertyDescriptor, PropertyKey}, value::{RcBigInt, RcString, RcSymbol, Value}, BoaProfiler, Context, }; @@ -59,11 +59,11 @@ impl NativeObject for T { pub struct Object { /// The type of the object. pub data: ObjectData, - indexed_properties: FxHashMap, + indexed_properties: FxHashMap, /// Properties - string_properties: FxHashMap, + string_properties: FxHashMap, /// Symbol Properties - symbol_properties: FxHashMap, + symbol_properties: FxHashMap, /// Instance prototype `__proto__`. prototype: Value, /// Whether it can have new properties added to it. @@ -760,7 +760,7 @@ impl<'context> ObjectInitializer<'context> { K: Into, V: Into, { - let property = Property::data_descriptor(value.into(), attribute); + let property = DataDescriptor::new(value, attribute); self.object.borrow_mut().insert(key, property); self } @@ -891,7 +891,7 @@ impl<'context> ConstructorBuilder<'context> { K: Into, V: Into, { - let property = Property::data_descriptor(value.into(), attribute); + let property = DataDescriptor::new(value, attribute); self.prototype.borrow_mut().insert(key, property); self } @@ -903,7 +903,7 @@ impl<'context> ConstructorBuilder<'context> { K: Into, V: Into, { - let property = Property::data_descriptor(value.into(), attribute); + let property = DataDescriptor::new(value, attribute); self.constructor_object.borrow_mut().insert(key, property); self } @@ -971,15 +971,12 @@ impl<'context> ConstructorBuilder<'context> { FunctionFlags::from_parameters(self.callable, self.constructable), ); - let length = Property::data_descriptor( - self.length.into(), + let length = DataDescriptor::new( + self.length, Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ); - let name = Property::data_descriptor( - self.name - .take() - .unwrap_or_else(|| String::from("[object]")) - .into(), + let name = DataDescriptor::new( + self.name.take().unwrap_or_else(|| String::from("[object]")), Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ); diff --git a/boa/src/property/attribute/mod.rs b/boa/src/property/attribute/mod.rs index 476e262fd4f..5a1f9dbff52 100644 --- a/boa/src/property/attribute/mod.rs +++ b/boa/src/property/attribute/mod.rs @@ -18,32 +18,22 @@ bitflags! { #[derive(Finalize)] pub struct Attribute: u8 { /// The `Writable` attribute decides whether the value associated with the property can be changed or not, from its initial value. - const WRITABLE = 0b0000_0011; - - /// The property is not writable. - const READONLY = 0b0000_0010; - - /// Is the `Writable` flag defined. - const HAS_WRITABLE = 0b0000_0010; + const WRITABLE = 0b0000_0001; /// If the property can be enumerated by a `for-in` loop. - const ENUMERABLE = 0b0000_1100; - - /// The property can not be enumerated in a `for-in` loop. - const NON_ENUMERABLE = 0b0000_1000; - - /// Is the `Enumerable` flag defined. - const HAS_ENUMERABLE = 0b0000_1000; + const ENUMERABLE = 0b0000_0010; /// If the property descriptor can be changed later. - const CONFIGURABLE = 0b0011_0000; + const CONFIGURABLE = 0b0000_0100; - /// The property descriptor cannot be changed. - const PERMANENT = 0b0010_0000; + /// The property is not writable. + const READONLY = 0b0000_0000; - /// Is the `Configurable` flag defined. - const HAS_CONFIGURABLE = 0b0010_0000; + /// The property can not be enumerated in a `for-in` loop. + const NON_ENUMERABLE = 0b0000_0000; + /// The property descriptor cannot be changed. + const PERMANENT = 0b0000_0000; } } @@ -63,12 +53,6 @@ impl Attribute { self.bits = 0; } - /// Is the `writable` flag defined. - #[inline] - pub fn has_writable(self) -> bool { - self.contains(Self::HAS_WRITABLE) - } - /// Sets the `writable` flag. #[inline] pub fn set_writable(&mut self, value: bool) { @@ -82,16 +66,9 @@ impl Attribute { /// Gets the `writable` flag. #[inline] pub fn writable(self) -> bool { - debug_assert!(self.has_writable()); self.contains(Self::WRITABLE) } - /// Is the `enumerable` flag defined. - #[inline] - pub fn has_enumerable(self) -> bool { - self.contains(Self::HAS_ENUMERABLE) - } - /// Sets the `enumerable` flag. #[inline] pub fn set_enumerable(&mut self, value: bool) { @@ -105,16 +82,9 @@ impl Attribute { /// Gets the `enumerable` flag. #[inline] pub fn enumerable(self) -> bool { - debug_assert!(self.has_enumerable()); self.contains(Self::ENUMERABLE) } - /// Is the `configurable` flag defined. - #[inline] - pub fn has_configurable(self) -> bool { - self.contains(Self::HAS_CONFIGURABLE) - } - /// Sets the `configurable` flag. #[inline] pub fn set_configurable(&mut self, value: bool) { @@ -128,7 +98,6 @@ impl Attribute { /// Gets the `configurable` flag. #[inline] pub fn configurable(self) -> bool { - debug_assert!(self.has_configurable()); self.contains(Self::CONFIGURABLE) } } diff --git a/boa/src/property/attribute/tests.rs b/boa/src/property/attribute/tests.rs index e9ced879177..64f8d174318 100644 --- a/boa/src/property/attribute/tests.rs +++ b/boa/src/property/attribute/tests.rs @@ -4,7 +4,6 @@ use super::Attribute; fn writable() { let attribute = Attribute::WRITABLE; - assert!(attribute.has_writable()); assert!(attribute.writable()); } @@ -12,7 +11,6 @@ fn writable() { fn enumerable() { let attribute = Attribute::ENUMERABLE; - assert!(attribute.has_enumerable()); assert!(attribute.enumerable()); } @@ -20,7 +18,6 @@ fn enumerable() { fn configurable() { let attribute = Attribute::CONFIGURABLE; - assert!(attribute.has_configurable()); assert!(attribute.configurable()); } @@ -28,23 +25,17 @@ fn configurable() { fn writable_and_enumerable() { let attribute = Attribute::WRITABLE | Attribute::ENUMERABLE; - assert!(attribute.has_writable()); assert!(attribute.writable()); - assert!(attribute.has_enumerable()); assert!(attribute.enumerable()); - - assert!(!attribute.has_configurable()); } #[test] fn enumerable_configurable() { let attribute = Attribute::ENUMERABLE | Attribute::CONFIGURABLE; - assert!(!attribute.has_writable()); + assert!(!attribute.writable()); - assert!(attribute.has_enumerable()); assert!(attribute.enumerable()); - assert!(attribute.has_configurable()); assert!(attribute.configurable()); } @@ -52,11 +43,8 @@ fn enumerable_configurable() { fn writable_enumerable_configurable() { let attribute = Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE; - assert!(attribute.has_writable()); assert!(attribute.writable()); - assert!(attribute.has_enumerable()); assert!(attribute.enumerable()); - assert!(attribute.has_configurable()); assert!(attribute.configurable()); } @@ -64,9 +52,9 @@ fn writable_enumerable_configurable() { fn default() { let attribute = Attribute::default(); - assert!(attribute.has_writable()); - assert!(attribute.has_enumerable()); - assert!(attribute.has_configurable()); + assert!(!attribute.writable()); + assert!(!attribute.enumerable()); + assert!(!attribute.configurable()); } #[test] @@ -75,9 +63,9 @@ fn clear() { attribute.clear(); - assert!(!attribute.has_writable()); - assert!(!attribute.has_enumerable()); - assert!(!attribute.has_configurable()); + assert!(!attribute.writable()); + assert!(!attribute.enumerable()); + assert!(!attribute.configurable()); assert!(attribute.is_empty()); } @@ -88,11 +76,8 @@ fn set_writable_to_true() { attribute.set_writable(true); - assert!(attribute.has_writable()); assert!(attribute.writable()); - assert!(attribute.has_enumerable()); assert!(!attribute.enumerable()); - assert!(attribute.has_configurable()); assert!(!attribute.configurable()); } @@ -102,11 +87,8 @@ fn set_writable_to_false() { attribute.set_writable(false); - assert!(attribute.has_writable()); assert!(!attribute.writable()); - assert!(attribute.has_enumerable()); assert!(!attribute.enumerable()); - assert!(attribute.has_configurable()); assert!(!attribute.configurable()); } @@ -116,11 +98,8 @@ fn set_enumerable_to_true() { attribute.set_enumerable(true); - assert!(attribute.has_writable()); assert!(!attribute.writable()); - assert!(attribute.has_enumerable()); assert!(attribute.enumerable()); - assert!(attribute.has_configurable()); assert!(!attribute.configurable()); } @@ -130,11 +109,8 @@ fn set_enumerable_to_false() { attribute.set_enumerable(false); - assert!(attribute.has_writable()); assert!(!attribute.writable()); - assert!(attribute.has_enumerable()); assert!(!attribute.enumerable()); - assert!(attribute.has_configurable()); assert!(!attribute.configurable()); } @@ -144,11 +120,8 @@ fn set_configurable_to_true() { attribute.set_configurable(true); - assert!(attribute.has_writable()); assert!(!attribute.writable()); - assert!(attribute.has_enumerable()); assert!(!attribute.enumerable()); - assert!(attribute.has_configurable()); assert!(attribute.configurable()); } @@ -158,10 +131,7 @@ fn set_configurable_to_false() { attribute.set_configurable(false); - assert!(attribute.has_writable()); assert!(!attribute.writable()); - assert!(attribute.has_enumerable()); assert!(!attribute.enumerable()); - assert!(attribute.has_configurable()); assert!(!attribute.configurable()); } diff --git a/boa/src/property/mod.rs b/boa/src/property/mod.rs index 17e36ad3075..9dbdaa13d9f 100644 --- a/boa/src/property/mod.rs +++ b/boa/src/property/mod.rs @@ -14,81 +14,41 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty //! [section]: https://tc39.es/ecma262/#sec-property-attributes -use crate::value::{RcString, RcSymbol, Value}; -use gc::{Finalize, Trace}; -use std::convert::TryFrom; -use std::fmt; +use crate::{ + gc::{Finalize, Trace}, + object::GcObject, + value::{RcString, RcSymbol, Value}, +}; +use std::{convert::TryFrom, fmt}; mod attribute; - pub use attribute::Attribute; -/// This represents a Javascript Property AKA The Property Descriptor. -/// -/// Property descriptors present in objects come in two main flavors: -/// - data descriptors -/// - accessor descriptors -/// -/// A data descriptor is a property that has a value, which may or may not be writable. -/// An accessor descriptor is a property described by a getter-setter pair of functions. -/// A descriptor must be one of these two flavors; it cannot be both. -/// -/// Any field in a JavaScript Property may be present or absent. -/// -/// More information: -/// - [MDN documentation][mdn] -/// - [ECMAScript reference][spec] -/// -/// [spec]: https://tc39.es/ecma262/#sec-property-descriptor-specification-type -/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty -#[derive(Trace, Finalize, Clone, Debug)] -pub struct Property { - pub(crate) attribute: Attribute, - /// The value associated with the property - pub value: Option, - /// The function serving as getter - pub get: Option, - /// The function serving as setter - pub set: Option, +#[derive(Debug, Clone)] +pub struct DataDescriptor { + value: Value, + attribute: Attribute, } -impl Property { - /// Make a new property with the given value - /// The difference between New and Default: - /// - /// New: zeros everything to make an empty object - /// Default: Defaults according to the spec - #[inline] - pub fn new() -> Self { +impl DataDescriptor { + pub fn new(value: V, attribute: Attribute) -> Self + where + V: Into, + { Self { - attribute: Default::default(), - value: None, - get: None, - set: None, + value: value.into(), + attribute, } } - #[inline] - pub fn empty() -> Self { - Self { - attribute: Attribute::empty(), - value: None, - get: None, - set: None, - } + pub fn value(&self) -> Value { + self.value.clone() } - #[inline] - pub fn data_descriptor(value: Value, attribute: Attribute) -> Self { - Self { - attribute, - value: Some(value), - get: None, - set: None, - } + pub fn attributes(&self) -> Attribute { + self.attribute } - /// Get the #[inline] pub fn configurable(&self) -> bool { self.attribute.configurable() @@ -99,15 +59,6 @@ impl Property { self.attribute.set_configurable(configurable) } - #[inline] - pub fn configurable_or(&self, value: bool) -> bool { - if self.attribute.has_configurable() { - self.attribute.configurable() - } else { - value - } - } - /// Set enumerable #[inline] pub fn enumerable(&self) -> bool { @@ -115,69 +66,148 @@ impl Property { } #[inline] - pub fn enumerable_or(&self, value: bool) -> bool { - if self.attribute.has_enumerable() { - self.attribute.enumerable() - } else { - value - } + pub fn set_enumerable(&mut self, enumerable: bool) { + self.attribute.set_enumerable(enumerable) } - /// Set writable #[inline] pub fn writable(&self) -> bool { self.attribute.writable() } #[inline] - pub fn writable_or(&self, value: bool) -> bool { - if self.attribute.has_writable() { - self.attribute.writable() - } else { - value + pub fn set_writable(&mut self, writable: bool) { + self.attribute.set_writable(writable) + } +} + +impl From for PropertyDescriptor { + fn from(value: DataDescriptor) -> Self { + Self { + attribute: value.attributes(), + value: Some(value.value()), + get: None, + set: None, + } + } +} + +#[derive(Debug, Clone)] +pub struct AccessorDescriptor { + /// The function serving as getter + get: Option, + /// The function serving as setter + set: Option, + attribute: Attribute, +} + +impl AccessorDescriptor { + pub fn new(get: Option, set: Option, mut attribute: Attribute) -> Self { + // Accessors can not have writable attribute. + attribute.remove(Attribute::WRITABLE); + Self { + get, + set, + attribute, } } - /// Set value + pub fn get(&self) -> Option { + self.get.clone() + } + + pub fn set(&self) -> Option { + self.get.clone() + } + + pub fn attributes(&self) -> Attribute { + self.attribute + } + #[inline] - pub fn value(mut self, value: Value) -> Self { - self.value = Some(value); - self + pub fn configurable(&self) -> bool { + self.attribute.configurable() } - /// Set get #[inline] - pub fn get(mut self, get: Value) -> Self { - self.get = Some(get); - self + pub fn set_configurable(&mut self, configurable: bool) { + self.attribute.set_configurable(configurable) } + /// Set enumerable #[inline] - pub fn has_get(&self) -> bool { - self.get.is_some() + pub fn enumerable(&self) -> bool { + self.attribute.enumerable() } - /// Set set #[inline] - pub fn set(mut self, set: Value) -> Self { - self.set = Some(set); - self + pub fn set_enumerable(&mut self, enumerable: bool) { + self.attribute.set_enumerable(enumerable) } +} + +impl From for PropertyDescriptor { + fn from(value: AccessorDescriptor) -> Self { + Self { + attribute: value.attributes(), + get: value.get().map(Into::into), + set: value.get().map(Into::into), + value: None, + } + } +} + +/// This represents a Javascript Property AKA The Property Descriptor. +/// +/// Property descriptors present in objects come in two main flavors: +/// - data descriptors +/// - accessor descriptors +/// +/// A data descriptor is a property that has a value, which may or may not be writable. +/// An accessor descriptor is a property described by a getter-setter pair of functions. +/// A descriptor must be one of these two flavors; it cannot be both. +/// +/// Any field in a JavaScript Property may be present or absent. +/// +/// More information: +/// - [MDN documentation][mdn] +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-property-descriptor-specification-type +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty +#[derive(Trace, Finalize, Clone, Debug)] +pub struct PropertyDescriptor { + pub(crate) attribute: Attribute, + /// The value associated with the property + pub value: Option, + /// The function serving as getter + pub get: Option, + /// The function serving as setter + pub set: Option, +} +impl PropertyDescriptor { + /// Get the #[inline] - pub fn has_set(&self) -> bool { - self.set.is_some() + pub fn configurable(&self) -> bool { + self.attribute.configurable() } - /// Is this an empty Property? - /// - /// `true` if all fields are set to none #[inline] - pub fn is_none(&self) -> bool { - self.value.is_none() - && self.attribute.is_empty() - && self.get.is_none() - && self.set.is_none() + pub fn set_configurable(&mut self, configurable: bool) { + self.attribute.set_configurable(configurable) + } + + /// Set enumerable + #[inline] + pub fn enumerable(&self) -> bool { + self.attribute.enumerable() + } + + /// Set writable + #[inline] + pub fn writable(&self) -> bool { + self.attribute.writable() } /// An accessor Property Descriptor is one that includes any fields named either [[Get]] or [[Set]]. @@ -199,7 +229,7 @@ impl Property { /// [spec]: https://tc39.es/ecma262/#sec-isdatadescriptor #[inline] pub fn is_data_descriptor(&self) -> bool { - self.value.is_some() || self.attribute.has_writable() + self.value.is_some() || self.attribute.writable() } /// Check if a property is generic descriptor. @@ -214,71 +244,6 @@ impl Property { } } -impl Default for Property { - /// Make a default property - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - /// [spec]: https://tc39.es/ecma262/#table-default-attribute-values - #[inline] - fn default() -> Self { - Self::new() - } -} - -impl From<&Property> for Value { - fn from(value: &Property) -> Value { - let property = Value::new_object(None); - if value.attribute.has_writable() { - property.set_field("writable", value.attribute.writable()); - } - - if value.attribute.has_enumerable() { - property.set_field("enumerable", value.attribute.enumerable()); - } - - if value.attribute.has_configurable() { - property.set_field("configurable", value.attribute.configurable()); - } - - property.set_field("value", value.value.clone().unwrap_or_else(Value::null)); - property.set_field("get", value.get.clone().unwrap_or_else(Value::null)); - property.set_field("set", value.set.clone().unwrap_or_else(Value::null)); - property - } -} - -impl<'a> From<&'a Value> for Property { - /// Attempt to fetch values "configurable", "enumerable", "writable" from the value, - /// if they're not there default to false - fn from(value: &Value) -> Self { - let mut attribute = Attribute::empty(); - - let writable = value.get_field("writable"); - if !writable.is_undefined() { - attribute.set_writable(bool::from(&writable)); - } - - let enumerable = value.get_field("enumerable"); - if !enumerable.is_undefined() { - attribute.set_enumerable(bool::from(&enumerable)); - } - - let configurable = value.get_field("configurable"); - if !configurable.is_undefined() { - attribute.set_configurable(bool::from(&configurable)); - } - - Self { - attribute, - value: Some(value.get_field("value")), - get: Some(value.get_field("get")), - set: Some(value.get_field("set")), - } - } -} - /// This abstracts away the need for IsPropertyKey by transforming the PropertyKey /// values into an enum with both valid types: String and Symbol /// diff --git a/boa/src/value/conversions.rs b/boa/src/value/conversions.rs index ca4dfaf1439..1a1ae39d442 100644 --- a/boa/src/value/conversions.rs +++ b/boa/src/value/conversions.rs @@ -127,7 +127,7 @@ where fn from(value: &[T]) -> Self { let mut array = Object::default(); for (i, item) in value.iter().enumerate() { - array.insert(i, Property::default().value(item.clone().into())); + array.insert(i, DataDescriptor::new(item.clone(), Attribute::all())); } Self::from(array) } @@ -140,7 +140,7 @@ where fn from(value: Vec) -> Self { let mut array = Object::default(); for (i, item) in value.into_iter().enumerate() { - array.insert(i, Property::default().value(item.into())); + array.insert(i, DataDescriptor::new(item, Attribute::all())); } Value::from(array) } diff --git a/boa/src/value/display.rs b/boa/src/value/display.rs index 9fc535faaf6..6ea10af5287 100644 --- a/boa/src/value/display.rs +++ b/boa/src/value/display.rs @@ -94,6 +94,8 @@ pub(crate) fn log_string_from(x: &Value, print_internals: bool, print_children: let len = v .borrow() .get_own_property(&PropertyKey::from("length")) + // TODO: do this in a better way `unwrap` + .unwrap() .value .clone() .expect("Could not borrow value") @@ -112,6 +114,8 @@ pub(crate) fn log_string_from(x: &Value, print_internals: bool, print_children: log_string_from( &v.borrow() .get_own_property(&i.into()) + // TODO: do this in a better way "unwrap" + .unwrap() .value .clone() .expect("Could not borrow value"), @@ -131,6 +135,8 @@ pub(crate) fn log_string_from(x: &Value, print_internals: bool, print_children: let size = v .borrow() .get_own_property(&PropertyKey::from("size")) + // TODO: do this in a better way "unwrap" + .unwrap() .value .clone() .expect("Could not borrow value") diff --git a/boa/src/value/mod.rs b/boa/src/value/mod.rs index 154f1042511..3125f3526b2 100644 --- a/boa/src/value/mod.rs +++ b/boa/src/value/mod.rs @@ -11,7 +11,7 @@ use crate::{ BigInt, Number, }, object::{GcObject, Object, ObjectData, PROTOTYPE}, - property::{Attribute, Property, PropertyKey}, + property::{Attribute, DataDescriptor, PropertyDescriptor, PropertyKey}, BoaProfiler, Context, Result, }; use gc::{Finalize, GcCellRef, GcCellRefMut, Trace}; @@ -183,7 +183,7 @@ impl Value { for (idx, json) in vs.into_iter().enumerate() { new_obj.set_property( idx.to_string(), - Property::data_descriptor( + DataDescriptor::new( Self::from_json(json, context), Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, ), @@ -191,7 +191,8 @@ impl Value { } new_obj.set_property( "length".to_string(), - Property::default().value(Self::from(length)), + // TODO: Fix length attribute + DataDescriptor::new(length, Attribute::all()), ); new_obj } @@ -201,7 +202,7 @@ impl Value { let value = Self::from_json(json, context); new_obj.set_property( key, - Property::data_descriptor( + DataDescriptor::new( value, Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, ), @@ -441,7 +442,7 @@ impl Value { /// Resolve the property in the object. /// /// A copy of the Property is returned. - pub fn get_property(&self, key: Key) -> Option + pub fn get_property(&self, key: Key) -> Option where Key: Into, { @@ -451,8 +452,8 @@ impl Value { Self::Object(ref object) => { let object = object.borrow(); let property = object.get_own_property(&key); - if !property.is_none() { - return Some(property); + if property.is_some() { + return property; } object.prototype_instance().get_property(key) @@ -465,11 +466,14 @@ impl Value { /// Set_prop, which will overwrite prop with a new Property /// /// Mostly used internally for now - pub(crate) fn update_property(&self, field: &str, new_property: Property) { + pub(crate) fn update_property

(&self, field: &str, new_property: P) + where + P: Into, + { let _timer = BoaProfiler::global().start_event("Value::update_property", "value"); if let Some(ref mut object) = self.as_object_mut() { - object.insert(field, new_property); + object.insert(field, new_property.into()); } } @@ -550,14 +554,15 @@ impl Value { } /// Set the property in the value. - pub fn set_property(&self, key: K, property: Property) -> Property + #[inline] + pub fn set_property(&self, key: K, property: P) where K: Into, + P: Into, { if let Some(mut object) = self.as_object_mut() { - object.insert(key.into(), property.clone()); + object.insert(key.into(), property.into()); } - property } /// The abstract operation ToPrimitive takes an input argument and an optional argument PreferredType. @@ -884,6 +889,15 @@ impl Value { Ok(self) } } + + #[inline] + pub fn to_property_descriptor(&self, context: &mut Context) -> Result { + if let Self::Object(ref object) = self { + object.to_property_descriptor(context) + } else { + Err(context.construct_type_error("Property description must be an object")) + } + } } impl Default for Value {