From 5109d8352386ce367dddc3a46a1d9ca5ec547bd8 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Fri, 9 Oct 2020 17:03:18 +0200 Subject: [PATCH] Move `Object` methods to `GcObject` --- boa/src/builtins/array/array_iterator.rs | 4 +- boa/src/builtins/array/mod.rs | 6 +- boa/src/builtins/boolean/tests.rs | 6 +- boa/src/builtins/function/mod.rs | 61 +--- boa/src/builtins/iterable/mod.rs | 6 +- boa/src/builtins/json/mod.rs | 2 + boa/src/builtins/json/tests.rs | 6 +- boa/src/builtins/map/mod.rs | 4 +- boa/src/builtins/object/mod.rs | 43 +-- boa/src/builtins/regexp/mod.rs | 5 + boa/src/builtins/string/string_iterator.rs | 4 +- boa/src/class.rs | 2 +- boa/src/context.rs | 25 +- .../environment/global_environment_record.rs | 2 +- .../environment/object_environment_record.rs | 2 +- boa/src/object/gcobject.rs | 290 ++++++++++++++++-- boa/src/object/internal_methods.rs | 95 +++++- boa/src/object/mod.rs | 8 + boa/src/value/display.rs | 5 +- boa/src/value/mod.rs | 45 +-- boa/src/value/type.rs | 2 +- 21 files changed, 425 insertions(+), 198 deletions(-) diff --git a/boa/src/builtins/array/array_iterator.rs b/boa/src/builtins/array/array_iterator.rs index 00fe2329d36..13128a6f51f 100644 --- a/boa/src/builtins/array/array_iterator.rs +++ b/boa/src/builtins/array/array_iterator.rs @@ -53,7 +53,7 @@ impl ArrayIterator { let array_iterator = Value::new_object(Some(ctx.global_object())); array_iterator.set_data(ObjectData::ArrayIterator(Self::new(array, kind))); array_iterator - .as_object_mut() + .as_object() .expect("array iterator object") .set_prototype_instance(ctx.iterator_prototypes().array_iterator().into()); Ok(array_iterator) @@ -126,7 +126,7 @@ impl ArrayIterator { let array_iterator = Value::new_object(Some(global)); make_builtin_fn(Self::next, "next", &array_iterator, 0, ctx); array_iterator - .as_object_mut() + .as_object() .expect("array iterator prototype object") .set_prototype_instance(iterator_prototype); diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index 999ec1f8c35..7e13db56030 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -101,7 +101,7 @@ impl Array { // Set Prototype let prototype = context.standard_objects().array_object().prototype(); - this.as_object_mut() + this.as_object() .expect("this should be an array object") .set_prototype_instance(prototype.into()); // This value is used by console.log and other routines to match Object type @@ -150,7 +150,7 @@ impl Array { )); array.set_data(ObjectData::Array); array - .as_object_mut() + .as_object() .expect("array object") .set_prototype_instance(context.standard_objects().array_object().prototype().into()); array.set_field("length", Value::from(0)); @@ -218,7 +218,7 @@ impl Array { _interpreter: &mut Context, ) -> Result { match args.get(0).and_then(|x| x.as_object()) { - Some(object) => Ok(Value::from(object.is_array())), + Some(object) => Ok(Value::from(object.borrow().is_array())), None => Ok(Value::from(false)), } } diff --git a/boa/src/builtins/boolean/tests.rs b/boa/src/builtins/boolean/tests.rs index eef4101028a..10f5e80ec3b 100644 --- a/boa/src/builtins/boolean/tests.rs +++ b/boa/src/builtins/boolean/tests.rs @@ -59,11 +59,7 @@ fn instances_have_correct_proto_set() { let bool_prototype = forward_val(&mut engine, "boolProto").expect("value expected"); assert!(same_value( - &bool_instance - .as_object() - .unwrap() - .prototype_instance() - .clone(), + &bool_instance.as_object().unwrap().prototype_instance(), &bool_prototype )); } diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs index d343a0439d3..da40349421c 100644 --- a/boa/src/builtins/function/mod.rs +++ b/boa/src/builtins/function/mod.rs @@ -14,7 +14,7 @@ use crate::{ builtins::{Array, BuiltIn}, environment::lexical_environment::Environment, - object::{ConstructorBuilder, FunctionBuilder, Object, ObjectData, PROTOTYPE}, + object::{ConstructorBuilder, FunctionBuilder, GcObject, Object, ObjectData}, property::{Attribute, DataDescriptor}, syntax::ast::node::{FormalParameter, RcStatementList}, BoaProfiler, Context, Result, Value, @@ -172,7 +172,7 @@ impl Function { /// pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value { let len = arguments_list.len(); - let mut obj = Object::default(); + let mut obj = GcObject::new(Object::default()); // Set length let length = DataDescriptor::new( len, @@ -195,61 +195,6 @@ pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value { Value::from(obj) } -/// Creates a new constructor function -/// -/// This utility function handling linking the new Constructor to the prototype. -/// So far this is only used by internal functions -pub fn make_constructor_fn( - name: &str, - length: usize, - body: NativeFunction, - global: &Value, - prototype: Value, - constructable: bool, - callable: bool, -) -> Value { - let _timer = - BoaProfiler::global().start_event(&format!("make_constructor_fn: {}", name), "init"); - - // Create the native function - let function = Function::BuiltIn( - body.into(), - FunctionFlags::from_parameters(callable, constructable), - ); - - // Get reference to Function.prototype - // Create the function object and point its instance prototype to Function.prototype - let mut constructor = - Object::function(function, global.get_field("Function").get_field(PROTOTYPE)); - - let length = DataDescriptor::new( - length, - Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ); - constructor.insert("length", length); - - let name = DataDescriptor::new( - name, - Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ); - constructor.insert("name", name); - - let constructor = Value::from(constructor); - - prototype.as_object_mut().unwrap().insert_property( - "constructor", - constructor.clone(), - Attribute::all(), - ); - - constructor - .as_object_mut() - .expect("constructor object") - .insert_property(PROTOTYPE, prototype, Attribute::all()); - - constructor -} - /// Creates a new member function of a `Object` or `prototype`. /// /// A function registered using this macro can then be called from Javascript using: @@ -290,7 +235,7 @@ pub fn make_builtin_fn( function.insert_property("length", length, Attribute::all()); parent - .as_object_mut() + .as_object() .unwrap() .insert_property(name, function, Attribute::all()); } diff --git a/boa/src/builtins/iterable/mod.rs b/boa/src/builtins/iterable/mod.rs index 1680f0f9f15..00cef736169 100644 --- a/boa/src/builtins/iterable/mod.rs +++ b/boa/src/builtins/iterable/mod.rs @@ -18,13 +18,13 @@ impl IteratorPrototypes { let iterator_prototype = create_iterator_prototype(ctx); Self { iterator_prototype: iterator_prototype - .as_gc_object() + .as_object() .expect("Iterator prototype is not an object"), array_iterator: ArrayIterator::create_prototype(ctx, iterator_prototype.clone()) - .as_gc_object() + .as_object() .expect("Array Iterator Prototype is not an object"), string_iterator: StringIterator::create_prototype(ctx, iterator_prototype) - .as_gc_object() + .as_object() .expect("String Iterator Prototype is not an object"), } } diff --git a/boa/src/builtins/json/mod.rs b/boa/src/builtins/json/mod.rs index c9c9e7543f3..852c28e8c32 100644 --- a/boa/src/builtins/json/mod.rs +++ b/boa/src/builtins/json/mod.rs @@ -154,6 +154,7 @@ impl Json { .map(|obj| { let object_to_return = Value::new_object(None); for (key, val) in obj + .borrow() .iter() // FIXME: handle accessor descriptors .map(|(k, v)| (k, v.as_data_descriptor().unwrap().value())) @@ -176,6 +177,7 @@ impl Json { .ok_or_else(Value::undefined)? } else if replacer_as_object.is_array() { let mut obj_to_return = serde_json::Map::new(); + let replacer_as_object = replacer_as_object.borrow(); let fields = replacer_as_object.keys().filter_map(|key| { if key == "length" { None diff --git a/boa/src/builtins/json/tests.rs b/boa/src/builtins/json/tests.rs index 12b4fa0be32..db69badea76 100644 --- a/boa/src/builtins/json/tests.rs +++ b/boa/src/builtins/json/tests.rs @@ -270,14 +270,12 @@ fn json_parse_sets_prototypes() { .unwrap() .as_object() .unwrap() - .prototype_instance() - .clone(); + .prototype_instance(); let array_prototype = forward_val(&mut engine, r#"jsonObj.arr"#) .unwrap() .as_object() .unwrap() - .prototype_instance() - .clone(); + .prototype_instance(); let global_object_prototype = engine .global_object() .get_field("Object") diff --git a/boa/src/builtins/map/mod.rs b/boa/src/builtins/map/mod.rs index 632da4de0e8..9325e66eed0 100644 --- a/boa/src/builtins/map/mod.rs +++ b/boa/src/builtins/map/mod.rs @@ -49,7 +49,7 @@ impl Map { // Set Prototype let prototype = ctx.global_object().get_field("Map").get_field(PROTOTYPE); - this.as_object_mut() + this.as_object() .expect("this is map object") .set_prototype_instance(prototype); // This value is used by console.log and other routines to match Object type @@ -288,7 +288,7 @@ impl Map { /// Helper function to get a key-value pair from an array. fn get_key_value(value: &Value) -> Option<(Value, Value)> { if let Value::Object(object) = value { - if object.borrow().is_array() { + if object.is_array() { let (key, value) = match value.get_field("length").as_number().unwrap() as i32 { 0 => (Value::Undefined, Value::Undefined), 1 => (value.get_field("0"), Value::Undefined), diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index bf0b031fa9e..f95c545be73 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -120,16 +120,16 @@ impl Object { /// Get the `prototype` of an object. pub fn get_prototype_of(_: &Value, args: &[Value], _: &mut Context) -> Result { let obj = args.get(0).expect("Cannot get object"); - Ok(obj.as_object().map_or_else(Value::undefined, |object| { - object.prototype_instance().clone() - })) + Ok(obj + .as_object() + .map_or_else(Value::undefined, |object| object.prototype_instance())) } /// Set the `prototype` of an object. pub fn set_prototype_of(_: &Value, args: &[Value], _: &mut Context) -> Result { let obj = args.get(0).expect("Cannot get object").clone(); let proto = args.get(1).expect("Cannot get object").clone(); - obj.as_object_mut().unwrap().set_prototype_instance(proto); + obj.as_object().unwrap().set_prototype_instance(proto); Ok(obj) } @@ -162,11 +162,11 @@ impl Object { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties pub fn define_properties(_: &Value, args: &[Value], ctx: &mut Context) -> Result { let arg = args.get(0).cloned().unwrap_or_default(); - let arg_obj = arg.as_object_mut(); + let arg_obj = arg.as_object(); if let Some(mut obj) = arg_obj { let props = args.get(1).cloned().unwrap_or_else(Value::undefined); obj.define_properties(props, ctx)?; - Ok(arg.clone()) + Ok(arg) } else { ctx.throw_type_error("Expected an object") } @@ -188,19 +188,21 @@ impl Object { } else if this.is_null() { Ok("[object Null]".into()) } else { - let gc_o = this.to_object(ctx)?; - let o = gc_o.borrow(); - let builtin_tag = match &o.data { - ObjectData::Array => "Array", - // TODO: Arguments Exotic Objects are currently not supported - ObjectData::Function(_) => "Function", - ObjectData::Error => "Error", - ObjectData::Boolean(_) => "Boolean", - ObjectData::Number(_) => "Number", - ObjectData::String(_) => "String", - ObjectData::Date(_) => "Date", - ObjectData::RegExp(_) => "RegExp", - _ => "Object", + let o = this.to_object(ctx)?; + let builtin_tag = { + let o = o.borrow(); + match &o.data { + ObjectData::Array => "Array", + // TODO: Arguments Exotic Objects are currently not supported + ObjectData::Function(_) => "Function", + ObjectData::Error => "Error", + ObjectData::Boolean(_) => "Boolean", + ObjectData::Number(_) => "Number", + ObjectData::String(_) => "String", + ObjectData::Date(_) => "Date", + ObjectData::RegExp(_) => "RegExp", + _ => "Object", + } }; let tag = o.get(&ctx.well_known_symbols().to_string_tag_symbol().into()); @@ -230,7 +232,6 @@ impl Object { }; let own_property = this .as_object() - .as_deref() .expect("Cannot get THIS object") .get_own_property(&prop.expect("cannot get prop").into()); if own_property.is_none() { @@ -251,7 +252,7 @@ impl Object { }; let key = key.to_property_key(ctx)?; - let own_property = this.to_object(ctx)?.borrow().get_own_property(&key); + let own_property = this.to_object(ctx)?.get_own_property(&key); Ok(own_property.map_or(Value::from(false), |own_prop| { Value::from(own_prop.enumerable()) diff --git a/boa/src/builtins/regexp/mod.rs b/boa/src/builtins/regexp/mod.rs index eb86947fc75..3e2e0afa9b2 100644 --- a/boa/src/builtins/regexp/mod.rs +++ b/boa/src/builtins/regexp/mod.rs @@ -309,6 +309,7 @@ impl RegExp { .to_string(ctx)?; let mut last_index = this.get_field("lastIndex").to_index(ctx)?; let result = if let Some(object) = this.as_object() { + let object = object.borrow(); let regex = object.as_regexp().unwrap(); let result = if let Some(m) = regex.matcher.find_from(arg_str.as_str(), last_index).next() { @@ -349,6 +350,7 @@ impl RegExp { .to_string(ctx)?; let mut last_index = this.get_field("lastIndex").to_index(ctx)?; let result = if let Some(object) = this.as_object() { + let object = object.borrow(); let regex = object.as_regexp().unwrap(); let result = { if let Some(m) = regex.matcher.find_from(arg_str.as_str(), last_index).next() { @@ -401,6 +403,7 @@ impl RegExp { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@match pub(crate) fn r#match(this: &Value, arg: RcString, ctx: &mut Context) -> Result { let (matcher, flags) = if let Some(object) = this.as_object() { + let object = object.borrow(); let regex = object.as_regexp().unwrap(); (regex.matcher.clone(), regex.flags.clone()) } else { @@ -433,6 +436,7 @@ impl RegExp { #[allow(clippy::wrong_self_convention)] pub(crate) fn to_string(this: &Value, _: &[Value], context: &mut Context) -> Result { let (body, flags) = if let Some(object) = this.as_object() { + let object = object.borrow(); let regex = object.as_regexp().unwrap(); (regex.original_source.clone(), regex.flags.clone()) } else { @@ -457,6 +461,7 @@ impl RegExp { // TODO: it's returning an array, it should return an iterator pub(crate) fn match_all(this: &Value, arg_str: String) -> Result { let matches = if let Some(object) = this.as_object() { + let object = object.borrow(); let regex = object.as_regexp().unwrap(); let mut matches = Vec::new(); diff --git a/boa/src/builtins/string/string_iterator.rs b/boa/src/builtins/string/string_iterator.rs index 1b12d0065ed..4cefb10dbf6 100644 --- a/boa/src/builtins/string/string_iterator.rs +++ b/boa/src/builtins/string/string_iterator.rs @@ -25,7 +25,7 @@ impl StringIterator { let string_iterator = Value::new_object(Some(ctx.global_object())); string_iterator.set_data(ObjectData::StringIterator(Self::new(string))); string_iterator - .as_object_mut() + .as_object() .expect("array iterator object") .set_prototype_instance(ctx.iterator_prototypes().string_iterator().into()); Ok(string_iterator) @@ -76,7 +76,7 @@ impl StringIterator { let array_iterator = Value::new_object(Some(global)); make_builtin_fn(Self::next, "next", &array_iterator, 0, ctx); array_iterator - .as_object_mut() + .as_object() .expect("array iterator prototype object") .set_prototype_instance(iterator_prototype); diff --git a/boa/src/class.rs b/boa/src/class.rs index e6d33b5114b..bff30c0cbd7 100644 --- a/boa/src/class.rs +++ b/boa/src/class.rs @@ -43,7 +43,7 @@ //! class.method("speak", 0, |this, _args, _ctx| { //! if let Some(object) = this.as_object() { //! if let Some(animal) = object.downcast_ref::() { -//! match animal { +//! match &*animal { //! Self::Cat => println!("meow"), //! Self::Dog => println!("woof"), //! Self::Other => println!(r"¯\_(ツ)_/¯"), diff --git a/boa/src/context.rs b/boa/src/context.rs index e94b1f9eb59..37a12e901dd 100644 --- a/boa/src/context.rs +++ b/boa/src/context.rs @@ -482,15 +482,15 @@ impl Context { // Every new function has a prototype property pre-made let proto = Value::new_object(Some(self.global_object())); - let mut function = Object::function( + let mut function = GcObject::new(Object::function( Function::BuiltIn(body.into(), FunctionFlags::CALLABLE), function_prototype, - ); + )); function.set(PROTOTYPE.into(), proto); function.set("length".into(), length.into()); function.set("name".into(), name.into()); - Ok(GcObject::new(function)) + Ok(function) } /// Register a global function. @@ -532,16 +532,13 @@ impl Context { .expect("Could not get global object"), )); array.set_data(ObjectData::Array); - array - .as_object_mut() - .expect("object") - .set_prototype_instance( - self.realm() - .environment - .get_binding_value("Array") - .expect("Array was not initialized") - .get_field(PROTOTYPE), - ); + array.as_object().expect("object").set_prototype_instance( + self.realm() + .environment + .get_binding_value("Array") + .expect("Array was not initialized") + .get_field(PROTOTYPE), + ); array.set_field("0", key); array.set_field("1", value); array.set_field("length", Value::from(2)); @@ -610,7 +607,7 @@ impl Context { let class = class_builder.build(); let property = DataDescriptor::new(class, T::ATTRIBUTE); self.global_object() - .as_object_mut() + .as_object() .unwrap() .insert(T::NAME, property); Ok(()) diff --git a/boa/src/environment/global_environment_record.rs b/boa/src/environment/global_environment_record.rs index 43695d2bcc7..11c4176539b 100644 --- a/boa/src/environment/global_environment_record.rs +++ b/boa/src/environment/global_environment_record.rs @@ -83,7 +83,7 @@ impl GlobalEnvironmentRecord { }; global_object - .as_object_mut() + .as_object() .expect("global object") .insert(name, desc); } diff --git a/boa/src/environment/object_environment_record.rs b/boa/src/environment/object_environment_record.rs index 0983485a2e6..760371ce533 100644 --- a/boa/src/environment/object_environment_record.rs +++ b/boa/src/environment/object_environment_record.rs @@ -66,7 +66,7 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord { let mut property = DataDescriptor::new(value, Attribute::ENUMERABLE); property.set_configurable(strict); self.bindings - .as_object_mut() + .as_object() .expect("binding object") .insert(name, property); } diff --git a/boa/src/object/gcobject.rs b/boa/src/object/gcobject.rs index 70bfd46dd68..7458384ec65 100644 --- a/boa/src/object/gcobject.rs +++ b/boa/src/object/gcobject.rs @@ -2,7 +2,7 @@ //! //! The `GcObject` is a garbage collected Object. -use super::{Object, PROTOTYPE}; +use super::{NativeObject, Object, PROTOTYPE}; use crate::{ builtins::function::{ create_unmapped_arguments_object, BuiltInFunction, Function, NativeFunction, @@ -26,17 +26,19 @@ use std::{ }; /// A wrapper type for an immutably borrowed `Object`. -pub type Ref<'object> = GcCellRef<'object, Object>; +pub type Ref<'object, T> = GcCellRef<'object, T>; /// A wrapper type for a mutably borrowed `Object`. -pub type RefMut<'object> = GcCellRefMut<'object, Object>; +pub type RefMut<'object, T> = GcCellRefMut<'object, T>; /// Garbage collected `Object`. #[derive(Trace, Finalize, Clone, Default)] pub struct GcObject(Gc>); -// This is needed for the call method since we cannot mutate the function itself since we -// already borrow it so we get the function body clone it then drop the borrow and run the body +/// The body of a JavaScript function. +/// +/// This is needed for the call method since we cannot mutate the function itself since we +/// already borrow it so we get the function body clone it then drop the borrow and run the body enum FunctionBody { BuiltIn(NativeFunction), Ordinary(RcStatementList), @@ -51,27 +53,28 @@ impl GcObject { /// Immutably borrows the `Object`. /// - /// The borrow lasts until the returned `GcCellRef` exits scope. + /// The borrow lasts until the returned `Ref` exits scope. /// Multiple immutable borrows can be taken out at the same time. /// - ///# Panics + /// # Panics + /// /// Panics if the object is currently mutably borrowed. #[inline] #[track_caller] - pub fn borrow(&self) -> Ref<'_> { + pub fn borrow(&self) -> Ref<'_, Object> { self.try_borrow().expect("Object already mutably borrowed") } /// Mutably borrows the Object. /// - /// The borrow lasts until the returned `GcCellRefMut` exits scope. + /// The borrow lasts until the returned `RefMut` exits scope. /// The object cannot be borrowed while this borrow is active. /// ///# Panics /// Panics if the object is currently borrowed. #[inline] #[track_caller] - pub fn borrow_mut(&self) -> RefMut<'_> { + pub fn borrow_mut(&self) -> RefMut<'_, Object> { self.try_borrow_mut().expect("Object already borrowed") } @@ -82,7 +85,7 @@ impl GcObject { /// /// This is the non-panicking variant of [`borrow`](#method.borrow). #[inline] - pub fn try_borrow(&self) -> StdResult, BorrowError> { + pub fn try_borrow(&self) -> StdResult, BorrowError> { self.0.try_borrow().map_err(|_| BorrowError) } @@ -93,7 +96,7 @@ impl GcObject { /// /// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut). #[inline] - pub fn try_borrow_mut(&self) -> StdResult, BorrowMutError> { + pub fn try_borrow_mut(&self) -> StdResult, BorrowMutError> { self.0.try_borrow_mut().map_err(|_| BorrowMutError) } @@ -105,7 +108,8 @@ impl GcObject { /// Call this object. /// - ///# Panics + /// # Panics + /// /// Panics if the object is currently mutably borrowed. // // @@ -188,12 +192,13 @@ impl GcObject { /// Construct an instance of this object with the specified arguments. /// - ///# Panics + /// # Panics + /// /// Panics if the object is currently mutably borrowed. // #[track_caller] pub fn construct(&self, args: &[Value], ctx: &mut Context) -> Result { - let this: Value = Object::create(self.borrow().get(&PROTOTYPE.into())).into(); + let this: Value = Object::create(self.get(&PROTOTYPE.into())).into(); let this_function_object = self.clone(); let body = if let Some(function) = self.borrow().as_function() { @@ -348,7 +353,7 @@ impl GcObject { let rec_limiter = RecursionLimiter::new(self); if rec_limiter.live { Err(interpreter.construct_type_error("cyclic object value")) - } else if self.borrow().is_array() { + } else if self.is_array() { let mut keys: Vec = self.borrow().index_property_keys().cloned().collect(); keys.sort_unstable(); let mut arr: Vec = Vec::with_capacity(keys.len()); @@ -376,44 +381,45 @@ impl GcObject { } } + /// Convert the object to a `PropertyDescritptor` + /// + /// # Panics + /// + /// Panics if the object is currently mutably borrowed. 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() - { + if self.has_property(&enumerable_key) && self.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() - { + if self.has_property(&configurable_key) && self.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)); + if self.has_property(&value_key) { + value = Some(self.get(&value_key)); } let mut has_writable = false; let writable_key = PropertyKey::from("writable"); - if self.borrow().has_property(&writable_key) { + if self.has_property(&writable_key) { has_writable = true; - if self.borrow().get(&writable_key).to_boolean() { + if self.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); + if self.has_property(&get_key) { + let getter = self.get(&get_key); match getter { - Value::Object(ref object) if object.borrow().is_callable() => { + Value::Object(ref object) if object.is_callable() => { get = Some(object.clone()); } _ => { @@ -426,10 +432,10 @@ impl GcObject { let mut set = None; let set_key = PropertyKey::from("set"); - if self.borrow().has_property(&set_key) { - let setter = self.borrow().get(&set_key); + if self.has_property(&set_key) { + let setter = self.get(&set_key); match setter { - Value::Object(ref object) if object.borrow().is_callable() => { + Value::Object(ref object) if object.is_callable() => { set = Some(object.clone()); } _ => { @@ -450,6 +456,226 @@ impl GcObject { Ok(DataDescriptor::new(value.unwrap_or_else(Value::undefined), attribute).into()) } } + + /// Reeturn `true` if it is a native object and the native type is `T`. + /// + /// # Panics + /// + /// Panics if the object is currently mutably borrowed. + #[inline] + #[track_caller] + pub fn is(&self) -> bool + where + T: NativeObject, + { + self.borrow().is::() + } + + /// Downcast a reference to the object, + /// if the object is type native object type `T`. + /// + /// # Panics + /// + /// Panics if the object is currently mutably borrowed. + #[inline] + #[track_caller] + pub fn downcast_ref(&self) -> Option> + where + T: NativeObject, + { + let object = self.borrow(); + if object.is::() { + Some(Ref::map(object, |x| x.downcast_ref::().unwrap())) + } else { + None + } + } + + /// Downcast a mutable reference to the object, + /// if the object is type native object type `T`. + /// + /// # Panics + /// + /// Panics if the object is currently borrowed. + #[inline] + #[track_caller] + pub fn downcast_mut(&mut self) -> Option> + where + T: NativeObject, + { + let object = self.borrow_mut(); + if object.is::() { + Some(RefMut::map(object, |x| x.downcast_mut::().unwrap())) + } else { + None + } + } + + /// Get the prototype of the object. + /// + /// # Panics + /// + /// Panics if the object is currently mutably borrowed. + #[inline] + #[track_caller] + pub fn prototype_instance(&self) -> Value { + self.borrow().prototype_instance().clone() + } + + /// Set the prototype of the object. + /// + /// # Panics + /// + /// Panics if the object is currently mutably borrowed + /// or if th prototype is not an object or undefined. + #[inline] + #[track_caller] + pub fn set_prototype_instance(&mut self, prototype: Value) { + self.borrow_mut().set_prototype_instance(prototype) + } + + /// Checks if it an `Array` object. + /// + /// # Panics + /// + /// Panics if the object is currently mutably borrowed. + #[inline] + #[track_caller] + pub fn is_array(&self) -> bool { + self.borrow().is_array() + } + + /// Checks if it is an `ArrayIterator` object. + /// + /// # Panics + /// + /// Panics if the object is currently mutably borrowed. + #[inline] + #[track_caller] + pub fn is_array_iterator(&self) -> bool { + self.borrow().is_array_iterator() + } + + /// Checks if it is a `Map` object.pub + /// + /// # Panics + /// + /// Panics if the object is currently mutably borrowed. + #[inline] + #[track_caller] + pub fn is_map(&self) -> bool { + self.borrow().is_map() + } + + /// Checks if it a `String` object. + /// + /// # Panics + /// + /// Panics if the object is currently mutably borrowed. + #[inline] + #[track_caller] + pub fn is_string(&self) -> bool { + self.borrow().is_string() + } + + /// Checks if it a `Function` object. + /// + /// # Panics + /// + /// Panics if the object is currently mutably borrowed. + #[inline] + #[track_caller] + pub fn is_function(&self) -> bool { + self.borrow().is_function() + } + + /// Checks if it a Symbol object. + /// + /// # Panics + /// + /// Panics if the object is currently mutably borrowed. + #[inline] + #[track_caller] + pub fn is_symbol(&self) -> bool { + self.borrow().is_symbol() + } + + /// Checks if it an Error object. + /// + /// # Panics + /// + /// Panics if the object is currently mutably borrowed. + #[inline] + #[track_caller] + pub fn is_error(&self) -> bool { + self.borrow().is_error() + } + + /// Checks if it a Boolean object. + /// + /// # Panics + /// + /// Panics if the object is currently mutably borrowed. + #[inline] + #[track_caller] + pub fn is_boolean(&self) -> bool { + self.borrow().is_boolean() + } + + /// Checks if it a `Number` object. + /// + /// # Panics + /// + /// Panics if the object is currently mutably borrowed. + #[inline] + #[track_caller] + pub fn is_number(&self) -> bool { + self.borrow().is_number() + } + + /// Checks if it a `BigInt` object. + /// + /// # Panics + /// + /// Panics if the object is currently mutably borrowed. + #[inline] + #[track_caller] + pub fn is_bigint(&self) -> bool { + self.borrow().is_bigint() + } + + /// Checks if it a `RegExp` object. + /// + /// # Panics + /// + /// Panics if the object is currently mutably borrowed. + #[inline] + #[track_caller] + pub fn is_regexp(&self) -> bool { + self.borrow().is_regexp() + } + + /// Checks if it an ordinary object. + /// + /// # Panics + /// + /// Panics if the object is currently mutably borrowed. + #[inline] + #[track_caller] + pub fn is_ordinary(&self) -> bool { + self.borrow().is_ordinary() + } + + /// Returns `true` if it holds an Rust type that implements `NativeObject`. + /// + /// # Panics + /// + /// Panics if the object is currently mutably borrowed. + #[inline] + #[track_caller] + pub fn is_native_object(&self) -> bool { + self.borrow().is_native_object() + } } impl AsRef> for GcObject { diff --git a/boa/src/object/internal_methods.rs b/boa/src/object/internal_methods.rs index 4da063f3ae5..e30fd2ad88e 100644 --- a/boa/src/object/internal_methods.rs +++ b/boa/src/object/internal_methods.rs @@ -12,19 +12,20 @@ use crate::{ BoaProfiler, Context, Result, }; -impl Object { +impl GcObject { /// Check if object has property. /// /// More information: /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p + #[inline] 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(key) + object.has_property(key) } else { false }; @@ -40,7 +41,7 @@ impl Object { /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-isextensible #[inline] pub fn is_extensible(&self) -> bool { - self.extensible + self.borrow().extensible } /// Disable extensibility. @@ -51,15 +52,16 @@ impl Object { /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-preventextensions #[inline] pub fn prevent_extensions(&mut self) -> bool { - self.extensible = false; + self.borrow_mut().extensible = false; true } /// Delete property. + #[inline] pub fn delete(&mut self, key: &PropertyKey) -> bool { match self.get_own_property(key) { Some(desc) if desc.configurable() => { - self.remove_property(&key); + self.remove(&key); true } Some(_) => false, @@ -216,13 +218,15 @@ impl Object { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getownproperty-p + #[inline] pub fn get_own_property(&self, key: &PropertyKey) -> Option { let _timer = BoaProfiler::global().start_event("Object::get_own_property", "object"); + let object = self.borrow(); 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), + PropertyKey::Index(index) => object.indexed_properties.get(&index), + PropertyKey::String(ref st) => object.string_properties.get(st), + PropertyKey::Symbol(ref symbol) => object.symbol_properties.get(symbol), }; property.cloned() @@ -234,8 +238,9 @@ impl Object { /// - [ECMAScript reference][spec] /// /// [spec](https://tc39.es/ecma262/#table-essential-internal-methods) + #[inline] pub fn own_property_keys(&self) -> Vec { - self.keys().collect() + self.borrow().keys().collect() } /// The abstract operation ObjectDefineProperties @@ -244,15 +249,16 @@ impl Object { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-object.defineproperties + #[inline] 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 keys = props.own_property_keys(); let mut descriptors: Vec<(PropertyKey, PropertyDescriptor)> = Vec::new(); for next_key in keys { - if let Some(prop_desc) = props.borrow().get_own_property(&next_key) { + if let Some(prop_desc) = props.get_own_property(&next_key) { if prop_desc.enumerable() { - let desc_obj = props.borrow().get(&next_key); + let desc_obj = props.get(&next_key); let desc = desc_obj.to_property_descriptor(ctx)?; descriptors.push((next_key, desc)); } @@ -277,6 +283,7 @@ impl Object { /// /// [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 + #[inline] pub fn set_prototype_of(&mut self, _val: Value) -> bool { // debug_assert!(val.is_object() || val.is_null()); // let current = self.prototype.clone(); @@ -316,9 +323,69 @@ impl Object { /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getprototypeof #[inline] pub fn get_prototype_of(&self) -> Value { - self.prototype.clone() + self.borrow().prototype.clone() + } + + /// Helper function for property insertion. + #[inline] + pub(crate) fn insert(&mut self, key: K, property: P) -> Option + where + K: Into, + P: Into, + { + self.borrow_mut().insert(key, property) + } + + /// Helper function for property removal. + #[inline] + pub(crate) fn remove(&mut self, key: &PropertyKey) -> Option { + self.borrow_mut().remove(key) } + /// Inserts a field in the object `properties` without checking if it's writable. + /// + /// If a field was already in the object with the same name that a `Some` is returned + /// with that field, otherwise None is returned. + #[inline] + pub(crate) fn insert_property( + &mut self, + key: K, + value: V, + attribute: Attribute, + ) -> Option + where + K: Into, + V: Into, + { + self.insert(key.into(), DataDescriptor::new(value, attribute)) + } + + /// It determines if Object is a callable function with a [[Call]] internal method. + /// + /// More information: + /// - [EcmaScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-iscallable + #[inline] + #[track_caller] + pub fn is_callable(&self) -> bool { + self.borrow().is_callable() + } + + /// It determines if Object is a function object with a [[Construct]] internal method. + /// + /// More information: + /// - [EcmaScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-isconstructor + #[inline] + #[track_caller] + pub fn is_constructable(&self) -> bool { + self.borrow().is_constructable() + } +} + +impl Object { /// Helper function for property insertion. #[inline] pub(crate) fn insert(&mut self, key: K, property: P) -> Option @@ -340,7 +407,7 @@ impl Object { /// Helper function for property removal. #[inline] - pub(crate) fn remove_property(&mut self, key: &PropertyKey) -> Option { + pub(crate) fn remove(&mut self, key: &PropertyKey) -> Option { match key { PropertyKey::Index(index) => self.indexed_properties.remove(&index), PropertyKey::String(ref string) => self.string_properties.remove(string), diff --git a/boa/src/object/mod.rs b/boa/src/object/mod.rs index 65b72cd8eea..d6b43344cce 100644 --- a/boa/src/object/mod.rs +++ b/boa/src/object/mod.rs @@ -472,6 +472,14 @@ impl Object { matches!(self.data, ObjectData::NativeObject(_)) } + #[inline] + pub fn as_native_object(&self) -> Option<&dyn NativeObject> { + match self.data { + ObjectData::NativeObject(ref object) => Some(object.as_ref()), + _ => None, + } + } + /// Reeturn `true` if it is a native object and the native type is `T`. #[inline] pub fn is(&self) -> bool diff --git a/boa/src/value/display.rs b/boa/src/value/display.rs index 4e62b494a21..93900311291 100644 --- a/boa/src/value/display.rs +++ b/boa/src/value/display.rs @@ -93,7 +93,6 @@ pub(crate) fn log_string_from(x: &Value, print_internals: bool, print_children: } ObjectData::Array => { let len = v - .borrow() .get_own_property(&PropertyKey::from("length")) // TODO: do this in a better way `unwrap` .unwrap() @@ -114,8 +113,7 @@ pub(crate) fn log_string_from(x: &Value, print_internals: bool, print_children: // Introduce recursive call to stringify any objects // which are part of the Array log_string_from( - &v.borrow() - .get_own_property(&i.into()) + &v.get_own_property(&i.into()) // TODO: do this in a better way "unwrap" .unwrap() // FIXME: handle accessor descriptors @@ -136,7 +134,6 @@ pub(crate) fn log_string_from(x: &Value, print_internals: bool, print_children: } ObjectData::Map(ref map) => { let size = v - .borrow() .get_own_property(&PropertyKey::from("size")) // TODO: do this in a better way "unwrap" .unwrap() diff --git a/boa/src/value/mod.rs b/boa/src/value/mod.rs index 132e6fec8b0..e11cd837d0c 100644 --- a/boa/src/value/mod.rs +++ b/boa/src/value/mod.rs @@ -14,7 +14,7 @@ use crate::{ property::{Attribute, DataDescriptor, PropertyDescriptor, PropertyKey}, BoaProfiler, Context, Result, }; -use gc::{Finalize, GcCellRef, GcCellRefMut, Trace}; +use gc::{Finalize, Trace}; use serde_json::{Number as JSONNumber, Value as JSONValue}; use std::{ collections::HashSet, @@ -268,25 +268,9 @@ impl Value { } #[inline] - pub fn as_object(&self) -> Option> { + pub fn as_object(&self) -> Option { match *self { - Self::Object(ref o) => Some(o.borrow()), - _ => None, - } - } - - #[inline] - pub fn as_gc_object(&self) -> Option { - match self { - Self::Object(o) => Some(o.clone()), - _ => None, - } - } - - #[inline] - pub fn as_object_mut(&self) -> Option> { - match *self { - Self::Object(ref o) => Some(o.borrow_mut()), + Self::Object(ref o) => Some(o.clone()), _ => None, } } @@ -307,7 +291,7 @@ impl Value { /// Returns true if the value is a function #[inline] pub fn is_function(&self) -> bool { - matches!(self, Self::Object(o) if o.borrow().is_function()) + matches!(self, Self::Object(o) if o.is_function()) } /// Returns true if the value is undefined. @@ -434,8 +418,8 @@ impl Value { where Key: Into, { - self.as_object_mut() - .map(|mut x| x.remove_property(&key.into())) + self.as_object() + .map(|mut x| x.remove(&key.into())) .is_some() } @@ -450,13 +434,12 @@ impl Value { let _timer = BoaProfiler::global().start_event("Value::get_property", "value"); match self { Self::Object(ref object) => { - let object = object.borrow(); let property = object.get_own_property(&key); if property.is_some() { return property; } - object.prototype_instance().get_property(key) + object.borrow().prototype_instance().get_property(key) } _ => None, } @@ -504,14 +487,14 @@ impl Value { let _timer = BoaProfiler::global().start_event("Value::set_field", "value"); if let Self::Object(ref obj) = *self { if let PropertyKey::Index(index) = key { - if obj.borrow().is_array() { + if obj.is_array() { let len = self.get_field("length").as_number().unwrap() as u32; if len < index + 1 { self.set_field("length", index + 1); } } } - obj.borrow_mut().set(key, value.clone()); + obj.clone().set(key, value.clone()); } value } @@ -531,7 +514,7 @@ impl Value { K: Into, P: Into, { - if let Some(mut object) = self.as_object_mut() { + if let Some(mut object) = self.as_object() { object.insert(key.into(), property.into()); } } @@ -661,11 +644,13 @@ impl Value { Value::String(ref string) => { let prototype = ctx.standard_objects().string_object().prototype(); - let mut object = - Object::with_prototype(prototype.into(), ObjectData::String(string.clone())); + let mut object = GcObject::new(Object::with_prototype( + prototype.into(), + ObjectData::String(string.clone()), + )); // Make sure the correct length is set on our new string object object.set("length".into(), string.chars().count().into()); - Ok(GcObject::new(object)) + Ok(object) } Value::Symbol(ref symbol) => { let prototype = ctx.standard_objects().symbol_object().prototype(); diff --git a/boa/src/value/type.rs b/boa/src/value/type.rs index 6ebeac57520..27c6ce3dbb9 100644 --- a/boa/src/value/type.rs +++ b/boa/src/value/type.rs @@ -47,7 +47,7 @@ impl Value { Self::Undefined => Type::Undefined, Self::BigInt(_) => Type::BigInt, Self::Object(ref object) => { - if object.borrow().is_function() { + if object.is_function() { Type::Function } else { Type::Object