From 510623b0e6ac29b86aa06a7b6a01679e1294e84c Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Wed, 6 Oct 2021 13:55:35 -0500 Subject: [PATCH] Change type of object prototypes to `Option` (#1640) --- boa/src/builtins/array_buffer/mod.rs | 2 +- boa/src/builtins/boolean/tests.rs | 10 +-- boa/src/builtins/console/mod.rs | 1 - boa/src/builtins/error/eval.rs | 2 +- boa/src/builtins/error/range.rs | 2 +- boa/src/builtins/error/reference.rs | 2 +- boa/src/builtins/error/syntax.rs | 2 +- boa/src/builtins/error/type.rs | 2 +- boa/src/builtins/error/uri.rs | 2 +- boa/src/builtins/json/tests.rs | 23 ++--- boa/src/builtins/object/for_in_iterator.rs | 5 +- boa/src/builtins/object/mod.rs | 44 +++++----- boa/src/builtins/reflect/mod.rs | 15 ++-- .../typed_array/integer_indexed_object.rs | 2 +- boa/src/builtins/typed_array/mod.rs | 4 +- .../function_environment_record.rs | 14 ++-- boa/src/exec/tests.rs | 9 +- boa/src/object/internal_methods/mod.rs | 84 +++++++++---------- boa/src/object/jsobject.rs | 63 ++++++++------ boa/src/object/mod.rs | 58 ++++++------- boa/src/value/display.rs | 6 +- boa/src/value/mod.rs | 6 +- 22 files changed, 182 insertions(+), 176 deletions(-) diff --git a/boa/src/builtins/array_buffer/mod.rs b/boa/src/builtins/array_buffer/mod.rs index 21d27ee457..8ac9d29fc7 100644 --- a/boa/src/builtins/array_buffer/mod.rs +++ b/boa/src/builtins/array_buffer/mod.rs @@ -307,7 +307,7 @@ impl ArrayBuffer { context, )?; let obj = context.construct_object(); - obj.set_prototype_instance(prototype.into()); + obj.set_prototype(prototype.into()); // 2. Let block be ? CreateByteDataBlock(byteLength). // TODO: for now just a arbitrary limit to not OOM. diff --git a/boa/src/builtins/boolean/tests.rs b/boa/src/builtins/boolean/tests.rs index b97fa65ea3..57360aae7a 100644 --- a/boa/src/builtins/boolean/tests.rs +++ b/boa/src/builtins/boolean/tests.rs @@ -1,4 +1,4 @@ -use crate::{forward, forward_val, Context, JsValue}; +use crate::{forward, forward_val, Context}; /// Test the correct type is returned from call and construct #[allow(clippy::unwrap_used)] @@ -58,8 +58,8 @@ fn instances_have_correct_proto_set() { let bool_instance = forward_val(&mut context, "boolInstance").expect("value expected"); let bool_prototype = forward_val(&mut context, "boolProto").expect("value expected"); - assert!(JsValue::same_value( - &bool_instance.as_object().unwrap().prototype_instance(), - &bool_prototype - )); + assert_eq!( + &*bool_instance.as_object().unwrap().prototype(), + &bool_prototype.as_object() + ); } diff --git a/boa/src/builtins/console/mod.rs b/boa/src/builtins/console/mod.rs index 06ba2cd9f1..3f074091de 100644 --- a/boa/src/builtins/console/mod.rs +++ b/boa/src/builtins/console/mod.rs @@ -568,7 +568,6 @@ impl Console { LogMessage::Info(display_obj(args.get_or_undefined(0), true)), context.console(), ); - Ok(JsValue::undefined()) } } diff --git a/boa/src/builtins/error/eval.rs b/boa/src/builtins/error/eval.rs index bd941a0c04..2c86ca61f2 100644 --- a/boa/src/builtins/error/eval.rs +++ b/boa/src/builtins/error/eval.rs @@ -46,7 +46,7 @@ impl BuiltIn for EvalError { ) .name(Self::NAME) .length(Self::LENGTH) - .inherit(error_prototype.into()) + .inherit(error_prototype) .property("name", Self::NAME, attribute) .property("message", "", attribute) .build(); diff --git a/boa/src/builtins/error/range.rs b/boa/src/builtins/error/range.rs index 1b62980b24..090c8f7f4c 100644 --- a/boa/src/builtins/error/range.rs +++ b/boa/src/builtins/error/range.rs @@ -43,7 +43,7 @@ impl BuiltIn for RangeError { ) .name(Self::NAME) .length(Self::LENGTH) - .inherit(error_prototype.into()) + .inherit(error_prototype) .property("name", Self::NAME, attribute) .property("message", "", attribute) .build(); diff --git a/boa/src/builtins/error/reference.rs b/boa/src/builtins/error/reference.rs index 663fee744c..e0afa38d36 100644 --- a/boa/src/builtins/error/reference.rs +++ b/boa/src/builtins/error/reference.rs @@ -42,7 +42,7 @@ impl BuiltIn for ReferenceError { ) .name(Self::NAME) .length(Self::LENGTH) - .inherit(error_prototype.into()) + .inherit(error_prototype) .property("name", Self::NAME, attribute) .property("message", "", attribute) .build(); diff --git a/boa/src/builtins/error/syntax.rs b/boa/src/builtins/error/syntax.rs index 027fd040d0..a5e18262e6 100644 --- a/boa/src/builtins/error/syntax.rs +++ b/boa/src/builtins/error/syntax.rs @@ -45,7 +45,7 @@ impl BuiltIn for SyntaxError { ) .name(Self::NAME) .length(Self::LENGTH) - .inherit(error_prototype.into()) + .inherit(error_prototype) .property("name", Self::NAME, attribute) .property("message", "", attribute) .build(); diff --git a/boa/src/builtins/error/type.rs b/boa/src/builtins/error/type.rs index 5a42856d9e..ddc47fd369 100644 --- a/boa/src/builtins/error/type.rs +++ b/boa/src/builtins/error/type.rs @@ -48,7 +48,7 @@ impl BuiltIn for TypeError { ) .name(Self::NAME) .length(Self::LENGTH) - .inherit(error_prototype.into()) + .inherit(error_prototype) .property("name", Self::NAME, attribute) .property("message", "", attribute) .build(); diff --git a/boa/src/builtins/error/uri.rs b/boa/src/builtins/error/uri.rs index b90c8934e9..07e600712e 100644 --- a/boa/src/builtins/error/uri.rs +++ b/boa/src/builtins/error/uri.rs @@ -44,7 +44,7 @@ impl BuiltIn for UriError { ) .name(Self::NAME) .length(Self::LENGTH) - .inherit(error_prototype.into()) + .inherit(error_prototype) .property("name", Self::NAME, attribute) .property("message", "", attribute) .build(); diff --git a/boa/src/builtins/json/tests.rs b/boa/src/builtins/json/tests.rs index 8a2f2dc1cd..1cc126580d 100644 --- a/boa/src/builtins/json/tests.rs +++ b/boa/src/builtins/json/tests.rs @@ -1,4 +1,4 @@ -use crate::{forward, forward_val, Context, JsValue}; +use crate::{forward, forward_val, Context}; #[test] fn json_sanity() { @@ -413,27 +413,22 @@ fn json_parse_sets_prototypes() { .unwrap() .as_object() .unwrap() - .prototype_instance(); + .prototype() + .clone(); let array_prototype = forward_val(&mut context, r#"jsonObj.arr"#) .unwrap() .as_object() .unwrap() - .prototype_instance(); - let global_object_prototype: JsValue = context + .prototype() + .clone(); + let global_object_prototype = context .standard_objects() .object_object() .prototype() .into(); - let global_array_prototype: JsValue = - context.standard_objects().array_object().prototype().into(); - assert!(JsValue::same_value( - &object_prototype, - &global_object_prototype - )); - assert!(JsValue::same_value( - &array_prototype, - &global_array_prototype - )); + let global_array_prototype = context.standard_objects().array_object().prototype().into(); + assert_eq!(object_prototype, global_object_prototype); + assert_eq!(array_prototype, global_array_prototype); } #[test] diff --git a/boa/src/builtins/object/for_in_iterator.rs b/boa/src/builtins/object/for_in_iterator.rs index dc85030d6c..6289208051 100644 --- a/boa/src/builtins/object/for_in_iterator.rs +++ b/boa/src/builtins/object/for_in_iterator.rs @@ -98,8 +98,9 @@ impl ForInIterator { } } } - match object.prototype_instance().to_object(context) { - Ok(o) => { + let proto = object.prototype().clone(); + match proto { + Some(o) => { object = o; } _ => { diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index fd901975c4..255a260e11 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -22,7 +22,7 @@ use crate::{ }, property::{Attribute, DescriptorKind, PropertyDescriptor, PropertyKey, PropertyNameKind}, symbol::WellKnownSymbols, - value::{JsValue, Type}, + value::JsValue, BoaProfiler, Context, JsResult, }; @@ -53,7 +53,7 @@ impl BuiltIn for Object { ) .name(Self::NAME) .length(Self::LENGTH) - .inherit(JsValue::null()) + .inherit(None) .method(Self::has_own_property, "hasOwnProperty", 0) .method(Self::property_is_enumerable, "propertyIsEnumerable", 0) .method(Self::to_string, "toString", 0) @@ -284,7 +284,9 @@ impl Object { let obj = args[0].clone().to_object(ctx)?; // 2. Return ? obj.[[GetPrototypeOf]](). - Ok(obj.prototype_instance()) + Ok(obj + .__get_prototype_of__(ctx)? + .map_or(JsValue::Null, JsValue::new)) } /// Set the `prototype` of an object. @@ -301,32 +303,32 @@ impl Object { } // 1. Set O to ? RequireObjectCoercible(O). - let obj = args + let o = args .get(0) .cloned() .unwrap_or_default() .require_object_coercible(ctx)? .clone(); - // 2. If Type(proto) is neither Object nor Null, throw a TypeError exception. - let proto = args.get_or_undefined(1); - if !matches!(proto.get_type(), Type::Object | Type::Null) { - return ctx.throw_type_error(format!( - "expected an object or null, got {}", - proto.type_of() - )); - } + let proto = match args.get_or_undefined(1) { + JsValue::Object(obj) => Some(obj.clone()), + JsValue::Null => None, + // 2. If Type(proto) is neither Object nor Null, throw a TypeError exception. + val => { + return ctx + .throw_type_error(format!("expected an object or null, got {}", val.type_of())) + } + }; - // 3. If Type(O) is not Object, return O. - if !obj.is_object() { - return Ok(obj); - } + let mut obj = if let Some(obj) = o.as_object() { + obj + } else { + // 3. If Type(O) is not Object, return O. + return Ok(o); + }; // 4. Let status be ? O.[[SetPrototypeOf]](proto). - let status = obj - .as_object() - .expect("obj was not an object") - .__set_prototype_of__(proto.clone(), ctx)?; + let status = obj.__set_prototype_of__(proto, ctx)?; // 5. If status is false, throw a TypeError exception. if !status { @@ -334,7 +336,7 @@ impl Object { } // 6. Return O. - Ok(obj) + Ok(o) } /// `Object.prototype.isPrototypeOf( proto )` diff --git a/boa/src/builtins/reflect/mod.rs b/boa/src/builtins/reflect/mod.rs index 7bed5e6de7..9982831cea 100644 --- a/boa/src/builtins/reflect/mod.rs +++ b/boa/src/builtins/reflect/mod.rs @@ -244,7 +244,9 @@ impl Reflect { .get(0) .and_then(|v| v.as_object()) .ok_or_else(|| context.construct_type_error("target must be an object"))?; - target.__get_prototype_of__(context) + Ok(target + .__get_prototype_of__(context)? + .map_or(JsValue::Null, JsValue::new)) } /// Returns `true` if the object has the property, `false` otherwise. @@ -377,10 +379,11 @@ impl Reflect { .get(0) .and_then(|v| v.as_object()) .ok_or_else(|| context.construct_type_error("target must be an object"))?; - let proto = args.get_or_undefined(1); - if !proto.is_null() && !proto.is_object() { - return context.throw_type_error("proto must be an object or null"); - } - Ok(target.__set_prototype_of__(proto.clone(), context)?.into()) + let proto = match args.get_or_undefined(1) { + JsValue::Object(obj) => Some(obj.clone()), + JsValue::Null => None, + _ => return context.throw_type_error("proto must be an object or null"), + }; + Ok(target.__set_prototype_of__(proto, context)?.into()) } } diff --git a/boa/src/builtins/typed_array/integer_indexed_object.rs b/boa/src/builtins/typed_array/integer_indexed_object.rs index 4541581d60..e4b7ab066f 100644 --- a/boa/src/builtins/typed_array/integer_indexed_object.rs +++ b/boa/src/builtins/typed_array/integer_indexed_object.rs @@ -77,7 +77,7 @@ impl IntegerIndexed { a.borrow_mut().data = ObjectData::integer_indexed(data); // 10. Set A.[[Prototype]] to prototype. - a.set_prototype_instance(prototype.into()); + a.set_prototype(prototype.into()); // 11. Return A. a diff --git a/boa/src/builtins/typed_array/mod.rs b/boa/src/builtins/typed_array/mod.rs index a3ea0cc57a..bdaba5b9db 100644 --- a/boa/src/builtins/typed_array/mod.rs +++ b/boa/src/builtins/typed_array/mod.rs @@ -82,8 +82,8 @@ macro_rules! typed_array { TypedArrayName::$ty.element_size(), Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ) - .custom_prototype(typed_array_constructor.into()) - .inherit(typed_array_constructor_proto.into()) + .custom_prototype(typed_array_constructor) + .inherit(typed_array_constructor_proto) .build() .into() } diff --git a/boa/src/environment/function_environment_record.rs b/boa/src/environment/function_environment_record.rs index 68ebef144e..2d7262e660 100644 --- a/boa/src/environment/function_environment_record.rs +++ b/boa/src/environment/function_environment_record.rs @@ -17,7 +17,7 @@ use crate::{ lexical_environment::{Environment, EnvironmentType, VariableScope}, }, gc::{empty_trace, Finalize, Trace}, - object::JsObject, + object::{JsObject, JsPrototype}, Context, JsResult, JsValue, }; @@ -114,21 +114,23 @@ impl FunctionEnvironmentRecord { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-getsuperbase - pub fn get_super_base(&self) -> JsValue { + pub fn get_super_base(&self, context: &mut Context) -> JsResult> { // 1. Let home be envRec.[[FunctionObject]].[[HomeObject]]. let home = &self.home_object; // 2. If home has the value undefined, return undefined. if home.is_undefined() { - JsValue::undefined() + Ok(None) } else { // 3. Assert: Type(home) is Object. assert!(home.is_object()); // 4. Return ? home.[[GetPrototypeOf]](). - home.as_object() - .expect("home_object must be an Object") - .prototype_instance() + Ok(Some( + home.as_object() + .expect("home_object must be an Object") + .__get_prototype_of__(context)?, + )) } } } diff --git a/boa/src/exec/tests.rs b/boa/src/exec/tests.rs index 217fc214e0..6439e4c9cf 100644 --- a/boa/src/exec/tests.rs +++ b/boa/src/exec/tests.rs @@ -791,9 +791,12 @@ mod in_operator { let bar_val = forward_val(&mut context, "bar").unwrap(); let bar_obj = bar_val.as_object().unwrap(); let foo_val = forward_val(&mut context, "Foo").unwrap(); - assert!(bar_obj - .prototype_instance() - .strict_equals(&foo_val.get_field("prototype", &mut context).unwrap())); + assert_eq!( + &*bar_obj.prototype(), + &foo_val + .as_object() + .and_then(|obj| obj.get("prototype", &mut context).unwrap().as_object()) + ); } } diff --git a/boa/src/object/internal_methods/mod.rs b/boa/src/object/internal_methods/mod.rs index 4c5cf7b085..abdfd355eb 100644 --- a/boa/src/object/internal_methods/mod.rs +++ b/boa/src/object/internal_methods/mod.rs @@ -13,7 +13,7 @@ use crate::{ BoaProfiler, Context, JsResult, }; -use super::PROTOTYPE; +use super::{JsPrototype, PROTOTYPE}; pub(super) mod arguments; pub(super) mod array; @@ -31,7 +31,7 @@ impl JsObject { /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getprototypeof #[inline] #[track_caller] - pub(crate) fn __get_prototype_of__(&self, context: &mut Context) -> JsResult { + pub(crate) fn __get_prototype_of__(&self, context: &mut Context) -> JsResult { let func = self.borrow().data.internal_methods.__get_prototype_of__; func(self, context) } @@ -47,7 +47,7 @@ impl JsObject { #[inline] pub(crate) fn __set_prototype_of__( &mut self, - val: JsValue, + val: JsPrototype, context: &mut Context, ) -> JsResult { let func = self.borrow().data.internal_methods.__set_prototype_of__; @@ -245,8 +245,8 @@ pub(crate) static ORDINARY_INTERNAL_METHODS: InternalObjectMethods = InternalObj /// For a guide on how to implement exotic internal methods, see `ORDINARY_INTERNAL_METHODS`. #[derive(Clone, Copy)] pub(crate) struct InternalObjectMethods { - pub(crate) __get_prototype_of__: fn(&JsObject, &mut Context) -> JsResult, - pub(crate) __set_prototype_of__: fn(&JsObject, JsValue, &mut Context) -> JsResult, + pub(crate) __get_prototype_of__: fn(&JsObject, &mut Context) -> JsResult, + pub(crate) __set_prototype_of__: fn(&JsObject, JsPrototype, &mut Context) -> JsResult, pub(crate) __is_extensible__: fn(&JsObject, &mut Context) -> JsResult, pub(crate) __prevent_extensions__: fn(&JsObject, &mut Context) -> JsResult, pub(crate) __get_own_property__: @@ -271,9 +271,9 @@ pub(crate) struct InternalObjectMethods { pub(crate) fn ordinary_get_prototype_of( obj: &JsObject, _context: &mut Context, -) -> JsResult { +) -> JsResult { // 1. Return O.[[Prototype]]. - Ok(obj.borrow().prototype.clone()) + Ok(obj.prototype().as_ref().cloned()) } /// Abstract operation `OrdinarySetPrototypeOf`. @@ -285,23 +285,23 @@ pub(crate) fn ordinary_get_prototype_of( #[inline] pub(crate) fn ordinary_set_prototype_of( obj: &JsObject, - val: JsValue, - context: &mut Context, + val: JsPrototype, + _: &mut Context, ) -> JsResult { // 1. Assert: Either Type(V) is Object or Type(V) is Null. - debug_assert!(val.is_object() || val.is_null()); + { + // 2. Let current be O.[[Prototype]]. + let current = obj.prototype(); - // 2. Let current be O.[[Prototype]]. - let current = obj.__get_prototype_of__(context)?; - - // 3. If SameValue(V, current) is true, return true. - if JsValue::same_value(¤t, &val) { - return Ok(true); + // 3. If SameValue(V, current) is true, return true. + if val == *current { + return Ok(true); + } } // 4. Let extensible be O.[[Extensible]]. // 5. If extensible is false, return false. - if !obj.__is_extensible__(context)? { + if !obj.extensible() { return Ok(false); } @@ -309,37 +309,29 @@ pub(crate) fn ordinary_set_prototype_of( let mut p = val.clone(); // 7. Let done be false. - let mut done = false; - // 8. Repeat, while done is false, - while !done { - match p { - // a. If p is null, set done to true. - JsValue::Null => done = true, - JsValue::Object(ref proto) => { - // b. Else if SameValue(p, O) is true, return false. - if JsObject::equals(proto, obj) { - return Ok(false); - } - // c. Else, - // i. If p.[[GetPrototypeOf]] is not the ordinary object internal method defined - // in 10.1.1, set done to true. - else if proto.borrow().data.internal_methods.__get_prototype_of__ as usize - != ordinary_get_prototype_of as usize - { - done = true; - } - // ii. Else, set p to p.[[Prototype]]. - else { - p = proto.__get_prototype_of__(context)?; - } - } - _ => unreachable!(), + // a. If p is null, set done to true. + while let Some(proto) = p { + // b. Else if SameValue(p, O) is true, return false. + if &proto == obj { + return Ok(false); + } + // c. Else, + // i. If p.[[GetPrototypeOf]] is not the ordinary object internal method defined + // in 10.1.1, set done to true. + else if proto.borrow().data.internal_methods.__get_prototype_of__ as usize + != ordinary_get_prototype_of as usize + { + break; + } + // ii. Else, set p to p.[[Prototype]]. + else { + p = proto.prototype().clone(); } } // 9. Set O.[[Prototype]] to V. - obj.borrow_mut().prototype = val; + obj.set_prototype(val); // 10. Return true. Ok(true) @@ -454,7 +446,7 @@ pub(crate) fn ordinary_has_property( let parent = obj.__get_prototype_of__(context)?; // 5. If parent is not null, then - if let JsValue::Object(ref object) = parent { + if let Some(object) = parent { // a. Return ? parent.[[HasProperty]](P). object.__has_property__(key, context) } else { @@ -483,7 +475,7 @@ pub(crate) fn ordinary_get( // If desc is undefined, then None => { // a. Let parent be ? O.[[GetPrototypeOf]](). - if let Some(parent) = obj.__get_prototype_of__(context)?.as_object() { + if let Some(parent) = obj.__get_prototype_of__(context)? { // c. Return ? parent.[[Get]](P, Receiver). parent.__get__(key, receiver, context) } @@ -537,7 +529,7 @@ pub(crate) fn ordinary_set( // 2. If ownDesc is undefined, then // a. Let parent be ? O.[[GetPrototypeOf]](). // b. If parent is not null, then - else if let Some(ref mut parent) = obj.__get_prototype_of__(context)?.as_object() { + else if let Some(parent) = obj.__get_prototype_of__(context)? { // i. Return ? parent.[[Set]](P, V, Receiver). return parent.__set__(key, value, receiver, context); } diff --git a/boa/src/object/jsobject.rs b/boa/src/object/jsobject.rs index 68d3f05e82..a881cf3e2c 100644 --- a/boa/src/object/jsobject.rs +++ b/boa/src/object/jsobject.rs @@ -2,7 +2,7 @@ //! //! The `JsObject` is a garbage collected Object. -use super::{NativeObject, Object}; +use super::{JsPrototype, NativeObject, Object}; use crate::{ object::{ObjectData, ObjectKind}, property::{PropertyDescriptor, PropertyKey}, @@ -40,21 +40,6 @@ pub type RefMut<'a, T, U> = GcCellRefMut<'a, T, U>; #[derive(Trace, Finalize, Clone, Default)] pub struct JsObject(Gc>); -/// 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 -#[cfg(not(feature = "vm"))] -enum FunctionBody { - BuiltInFunction(NativeFunctionSignature), - BuiltInConstructor(NativeFunctionSignature), - Closure { - function: Box, - captures: Captures, - }, - Ordinary(RcStatementList), -} - impl JsObject { /// Create a new `JsObject` from an internal `Object`. #[inline] @@ -76,7 +61,7 @@ impl JsObject { pub fn from_proto_and_data>>(prototype: O, data: ObjectData) -> Self { Self::from_object(Object { data, - prototype: prototype.into().map_or(JsValue::Null, JsValue::new), + prototype: prototype.into(), extensible: true, properties: Default::default(), }) @@ -160,6 +145,21 @@ impl JsObject { use super::internal_methods::get_prototype_from_constructor; + /// 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 + #[cfg(not(feature = "vm"))] + enum FunctionBody { + BuiltInFunction(NativeFunctionSignature), + BuiltInConstructor(NativeFunctionSignature), + Closure { + function: Box, + captures: Captures, + }, + Ordinary(RcStatementList), + } + let this_function_object = self.clone(); let mut has_parameter_expressions = false; @@ -515,8 +515,18 @@ impl JsObject { /// Panics if the object is currently mutably borrowed. #[inline] #[track_caller] - pub fn prototype_instance(&self) -> JsValue { - self.borrow().prototype_instance().clone() + pub fn prototype(&self) -> Ref<'_, JsPrototype> { + Ref::map(self.borrow(), |obj| obj.prototype()) + } + + /// Get the extensibility of the object. + /// + /// # Panics + /// + /// Panics if the object is currently mutably borrowed. + #[inline] + pub(crate) fn extensible(&self) -> bool { + self.borrow().extensible } /// Set the prototype of the object. @@ -524,11 +534,10 @@ impl JsObject { /// # 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(&self, prototype: JsValue) -> bool { - self.borrow_mut().set_prototype_instance(prototype) + pub fn set_prototype(&self, prototype: JsPrototype) -> bool { + self.borrow_mut().set_prototype(prototype) } /// Checks if it's an `Array` object. @@ -726,9 +735,9 @@ impl JsObject { // a. Set O to ? O.[[GetPrototypeOf]](). // b. If O is null, return false. let mut object = object.__get_prototype_of__(context)?; - while let Some(object_prototype) = object.as_object() { + while let Some(object_prototype) = object { // c. If SameValue(P, O) is true, return true. - if JsObject::equals(&prototype, &object_prototype) { + if prototype == object_prototype { return Ok(true); } // a. Set O to ? O.[[GetPrototypeOf]](). @@ -972,6 +981,12 @@ impl AsRef> for JsObject { } } +impl PartialEq for JsObject { + fn eq(&self, other: &Self) -> bool { + JsObject::equals(self, other) + } +} + /// An error returned by [`JsObject::try_borrow`](struct.JsObject.html#method.try_borrow). #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct BorrowError; diff --git a/boa/src/object/mod.rs b/boa/src/object/mod.rs index 261d02591b..00c93fb065 100644 --- a/boa/src/object/mod.rs +++ b/boa/src/object/mod.rs @@ -49,6 +49,8 @@ use self::internal_methods::{ /// Static `prototype`, usually set on constructors as a key to point to their respective prototype object. pub static PROTOTYPE: &str = "prototype"; +pub type JsPrototype = Option; + /// This trait allows Rust types to be passed around as objects. /// /// This is automatically implemented, when a type implements `Debug`, `Any` and `Trace`. @@ -72,14 +74,15 @@ impl NativeObject for T { } } -/// The internal representation of an JavaScript object. +/// The internal representation of a JavaScript object. #[derive(Debug, Trace, Finalize)] pub struct Object { /// The type of the object. pub data: ObjectData, + /// The collection of properties contained in the object properties: PropertyMap, /// Instance prototype `__proto__`. - prototype: JsValue, + prototype: JsPrototype, /// Whether it can have new properties added to it. extensible: bool, } @@ -365,7 +368,7 @@ impl Default for Object { Self { data: ObjectData::ordinary(), properties: PropertyMap::default(), - prototype: JsValue::null(), + prototype: None, extensible: true, } } @@ -936,7 +939,7 @@ impl Object { /// Gets the prototype instance of this object. #[inline] - pub fn prototype_instance(&self) -> &JsValue { + pub fn prototype(&self) -> &JsPrototype { &self.prototype } @@ -947,15 +950,15 @@ impl Object { /// [spec]: https://tc39.es/ecma262/#sec-invariants-of-the-essential-internal-methods #[inline] #[track_caller] - pub fn set_prototype_instance(&mut self, prototype: JsValue) -> bool { - assert!(prototype.is_null() || prototype.is_object()); + pub fn set_prototype>(&mut self, prototype: O) -> bool { + let prototype = prototype.into(); if self.extensible { self.prototype = prototype; true } else { // If target is non-extensible, [[SetPrototypeOf]] must return false // unless V is the SameValue as the target's observed [[GetPrototypeOf]] value. - JsValue::same_value(&prototype, &self.prototype) + self.prototype == prototype } } @@ -1264,13 +1267,7 @@ impl<'context> FunctionBuilder<'context> { pub(crate) fn build_function_prototype(&mut self, object: &JsObject) { let mut object = object.borrow_mut(); object.data = ObjectData::function(self.function.take().unwrap()); - object.set_prototype_instance( - self.context - .standard_objects() - .object_object() - .prototype() - .into(), - ); + object.set_prototype(self.context.standard_objects().object_object().prototype()); let property = PropertyDescriptor::builder() .writable(false) @@ -1387,8 +1384,8 @@ pub struct ConstructorBuilder<'context> { length: usize, callable: bool, constructable: bool, - inherit: Option, - custom_prototype: Option, + inherit: Option, + custom_prototype: Option, } impl Debug for ConstructorBuilder<'_> { @@ -1642,9 +1639,8 @@ impl<'context> ConstructorBuilder<'context> { /// /// Default is `Object.prototype` #[inline] - pub fn inherit(&mut self, prototype: JsValue) -> &mut Self { - assert!(prototype.is_object() || prototype.is_null()); - self.inherit = Some(prototype); + pub fn inherit>(&mut self, prototype: O) -> &mut Self { + self.inherit = Some(prototype.into()); self } @@ -1652,8 +1648,8 @@ impl<'context> ConstructorBuilder<'context> { /// /// Default is `Function.prototype` #[inline] - pub fn custom_prototype(&mut self, prototype: JsValue) -> &mut Self { - self.custom_prototype = Some(prototype); + pub fn custom_prototype>(&mut self, prototype: O) -> &mut Self { + self.custom_prototype = Some(prototype.into()); self } @@ -1688,15 +1684,14 @@ impl<'context> ConstructorBuilder<'context> { constructor.insert("length", length); constructor.insert("name", name); - if let Some(proto) = &self.custom_prototype { - constructor.set_prototype_instance(proto.clone()); + if let Some(proto) = self.custom_prototype.take() { + constructor.set_prototype(proto); } else { - constructor.set_prototype_instance( + constructor.set_prototype( self.context .standard_objects() .function_object() - .prototype() - .into(), + .prototype(), ); } constructor.insert_property( @@ -1721,15 +1716,10 @@ impl<'context> ConstructorBuilder<'context> { ); if let Some(proto) = self.inherit.take() { - prototype.set_prototype_instance(proto); + prototype.set_prototype(proto); } else { - prototype.set_prototype_instance( - self.context - .standard_objects() - .object_object() - .prototype() - .into(), - ); + prototype + .set_prototype(self.context.standard_objects().object_object().prototype()); } } diff --git a/boa/src/value/display.rs b/boa/src/value/display.rs index 0497a9c908..3f8a671131 100644 --- a/boa/src/value/display.rs +++ b/boa/src/value/display.rs @@ -31,18 +31,18 @@ macro_rules! print_obj_value { (internals of $obj:expr, $display_fn:ident, $indent:expr, $encounters:expr) => { { let object = $obj.borrow(); - if object.prototype_instance().is_object() { + if let Some(object) = object.prototype() { vec![format!( "{:>width$}: {}", "__proto__", - $display_fn(object.prototype_instance(), $encounters, $indent.wrapping_add(4), true), + $display_fn(&object.clone().into(), $encounters, $indent.wrapping_add(4), true), width = $indent, )] } else { vec![format!( "{:>width$}: {}", "__proto__", - object.prototype_instance().display(), + JsValue::Null.display(), width = $indent, )] } diff --git a/boa/src/value/mod.rs b/boa/src/value/mod.rs index 0426820172..7b3dfe327a 100644 --- a/boa/src/value/mod.rs +++ b/boa/src/value/mod.rs @@ -290,7 +290,11 @@ impl JsValue { return property; } - object.borrow().prototype_instance().get_property(key) + object + .prototype() + .as_ref() + .map_or(JsValue::Null, |obj| obj.clone().into()) + .get_property(key) } _ => None, }