From c9759a8dc3fd51561f6cbd2d731e78dc2120fe5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Juli=C3=A1n=20Espina?= Date: Wed, 12 Apr 2023 14:20:01 +0000 Subject: [PATCH] Lift `InternalObjectMethods` from `Object` (#2790) This Pull Request lifts the `InternalObjectMethods` vtable from `Object`, which should technically improve performance, since we now won't need to call `borrow` to use any of the internal methods, but let's see what the benchmarks show. It changes the following: - Adds a new `VTableObject` struct, containing the old `GcRefCell` and the lifted `InternalObjectMethods`. - Changes the definition of `JsObject` to `Gc`. Note that this means the `InternalObjectMethods` are accessible through the `Gc` pointer. - Reestructures intrinsic initialization and initialization APIs to accommodate this change. --- boa_engine/src/builtins/array/mod.rs | 2 +- boa_engine/src/builtins/error/type.rs | 7 +- boa_engine/src/builtins/mod.rs | 207 +++-- boa_engine/src/builtins/regexp/mod.rs | 11 +- boa_engine/src/builtins/typed_array/mod.rs | 4 +- boa_engine/src/builtins/uri/mod.rs | 8 +- boa_engine/src/context/intrinsics.rs | 37 +- boa_engine/src/context/mod.rs | 2 +- boa_engine/src/object/builtins/jsfunction.rs | 26 +- boa_engine/src/object/internal_methods/mod.rs | 48 +- boa_engine/src/object/jsobject.rs | 93 +- boa_engine/src/object/mod.rs | 835 ++++++------------ .../src/vm/opcode/set/class_prototype.rs | 9 +- 13 files changed, 547 insertions(+), 742 deletions(-) diff --git a/boa_engine/src/builtins/array/mod.rs b/boa_engine/src/builtins/array/mod.rs index 0e3d39a5ad..ec398f5e06 100644 --- a/boa_engine/src/builtins/array/mod.rs +++ b/boa_engine/src/builtins/array/mod.rs @@ -54,7 +54,7 @@ impl IntrinsicObject for Array { let values_function = BuiltInBuilder::with_object( realm, - realm.intrinsics().objects().array_prototype_values(), + realm.intrinsics().objects().array_prototype_values().into(), ) .callable(Self::values) .name("values") diff --git a/boa_engine/src/builtins/error/type.rs b/boa_engine/src/builtins/error/type.rs index b94786c9e5..b2a480f78f 100644 --- a/boa_engine/src/builtins/error/type.rs +++ b/boa_engine/src/builtins/error/type.rs @@ -22,12 +22,11 @@ use crate::{ }, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, error::JsNativeError, - native_function::NativeFunction, - object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, + object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData, ObjectKind}, property::Attribute, realm::Realm, string::utf16, - Context, JsArgs, JsResult, JsValue, + Context, JsArgs, JsResult, JsValue, NativeFunction, }; use boa_profiler::Profiler; @@ -134,7 +133,7 @@ impl IntrinsicObject for ThrowTypeError { let mut obj = obj.borrow_mut(); obj.extensible = false; - obj.data = ObjectData::function(Function::new( + *obj.kind_mut() = ObjectKind::Function(Function::new( FunctionKind::Native { function: NativeFunction::from_fn_ptr(throw_type_error), constructor: None, diff --git a/boa_engine/src/builtins/mod.rs b/boa_engine/src/builtins/mod.rs index 3855d8eab6..c734e935c6 100644 --- a/boa_engine/src/builtins/mod.rs +++ b/boa_engine/src/builtins/mod.rs @@ -97,7 +97,8 @@ use crate::{ js_string, native_function::{NativeFunction, NativeFunctionPointer}, object::{ - FunctionBinding, JsFunction, JsObject, JsPrototype, ObjectData, CONSTRUCTOR, PROTOTYPE, + FunctionBinding, JsFunction, JsObject, JsPrototype, Object, ObjectData, CONSTRUCTOR, + PROTOTYPE, }, property::{Attribute, PropertyDescriptor, PropertyKey}, realm::Realm, @@ -390,6 +391,85 @@ pub(crate) fn set_default_global_bindings(context: &mut Context<'_>) -> JsResult // === Builder typestate === +#[derive(Debug)] +enum BuiltInObjectInitializer { + Shared(JsObject), + Unique { object: Object, data: ObjectData }, +} + +impl BuiltInObjectInitializer { + /// Inserts a new property descriptor into the builtin. + fn insert(&mut self, key: K, property: P) + where + K: Into, + P: Into, + { + match self { + BuiltInObjectInitializer::Shared(obj) => obj.borrow_mut().insert(key, property), + BuiltInObjectInitializer::Unique { object, .. } => object.insert(key, property), + }; + } + + /// Sets the prototype of the builtin + fn set_prototype(&mut self, prototype: JsObject) { + match self { + BuiltInObjectInitializer::Shared(obj) => { + let mut obj = obj.borrow_mut(); + obj.set_prototype(prototype); + } + BuiltInObjectInitializer::Unique { object, .. } => { + object.set_prototype(prototype); + } + } + } + + /// Sets the `ObjectData` of the builtin. + /// + /// # Panics + /// + /// Panics if the builtin is a shared builtin and the data's vtable is not the same as the + /// builtin's vtable. + fn set_data(&mut self, new_data: ObjectData) { + match self { + BuiltInObjectInitializer::Shared(obj) => { + assert!( + std::ptr::eq(obj.vtable(), new_data.internal_methods), + "intrinsic object's vtable didn't match with new data" + ); + *obj.borrow_mut().kind_mut() = new_data.kind; + } + BuiltInObjectInitializer::Unique { ref mut data, .. } => *data = new_data, + } + } + + /// Gets a shared object from the builtin, transitioning its state if it's necessary. + fn as_shared(&mut self) -> JsObject { + match std::mem::replace( + self, + BuiltInObjectInitializer::Unique { + object: Object::default(), + data: ObjectData::ordinary(), + }, + ) { + BuiltInObjectInitializer::Shared(obj) => { + *self = BuiltInObjectInitializer::Shared(obj.clone()); + obj + } + BuiltInObjectInitializer::Unique { mut object, data } => { + *object.kind_mut() = data.kind; + let obj = JsObject::from_object_and_vtable(object, data.internal_methods); + *self = BuiltInObjectInitializer::Shared(obj.clone()); + obj + } + } + } + + /// Converts the builtin into a shared object. + fn into_shared(mut self) -> JsObject { + self.as_shared() + } +} + /// Marker for a constructor function. struct Constructor { prototype: JsObject, @@ -434,77 +514,78 @@ struct OrdinaryObject; /// Applies the pending builder data to the object. trait ApplyToObject { - fn apply_to(self, object: &JsObject); + fn apply_to(self, object: &mut BuiltInObjectInitializer); } impl ApplyToObject for Constructor { - fn apply_to(self, object: &JsObject) { + fn apply_to(self, object: &mut BuiltInObjectInitializer) { + object.insert( + PROTOTYPE, + PropertyDescriptor::builder() + .value(self.prototype.clone()) + .writable(false) + .enumerable(false) + .configurable(false), + ); + + let object = object.as_shared(); + { let mut prototype = self.prototype.borrow_mut(); prototype.set_prototype(self.inherits); prototype.insert( CONSTRUCTOR, PropertyDescriptor::builder() - .value(object.clone()) + .value(object) .writable(self.attributes.writable()) .enumerable(self.attributes.enumerable()) .configurable(self.attributes.configurable()), ); } - let mut object = object.borrow_mut(); - object.insert( - PROTOTYPE, - PropertyDescriptor::builder() - .value(self.prototype) - .writable(false) - .enumerable(false) - .configurable(false), - ); } } impl ApplyToObject for ConstructorNoProto { - fn apply_to(self, _: &JsObject) {} + fn apply_to(self, _: &mut BuiltInObjectInitializer) {} } impl ApplyToObject for OrdinaryFunction { - fn apply_to(self, _: &JsObject) {} + fn apply_to(self, _: &mut BuiltInObjectInitializer) {} } impl ApplyToObject for Callable { - fn apply_to(self, object: &JsObject) { - self.kind.apply_to(object); - - let function = function::Function::new( + fn apply_to(self, object: &mut BuiltInObjectInitializer) { + let function = ObjectData::function(function::Function::new( function::FunctionKind::Native { function: NativeFunction::from_fn_ptr(self.function), constructor: S::IS_CONSTRUCTOR.then_some(function::ConstructorKind::Base), }, self.realm, + )); + object.set_data(function); + object.insert( + utf16!("length"), + PropertyDescriptor::builder() + .value(self.length) + .writable(false) + .enumerable(false) + .configurable(true), + ); + object.insert( + utf16!("name"), + PropertyDescriptor::builder() + .value(self.name) + .writable(false) + .enumerable(false) + .configurable(true), ); - let length = PropertyDescriptor::builder() - .value(self.length) - .writable(false) - .enumerable(false) - .configurable(true); - let name = PropertyDescriptor::builder() - .value(self.name) - .writable(false) - .enumerable(false) - .configurable(true); - - { - let mut constructor = object.borrow_mut(); - constructor.data = ObjectData::function(function); - constructor.insert(utf16!("length"), length); - constructor.insert(utf16!("name"), name); - } + self.kind.apply_to(object); } } impl ApplyToObject for OrdinaryObject { - fn apply_to(self, _: &JsObject) {} + fn apply_to(self, _: &mut BuiltInObjectInitializer) {} } /// Builder for creating built-in objects, like `Array`. @@ -515,7 +596,7 @@ impl ApplyToObject for OrdinaryObject { #[must_use = "You need to call the `build` method in order for this to correctly assign the inner data"] struct BuiltInBuilder<'ctx, Kind> { realm: &'ctx Realm, - object: JsObject, + object: BuiltInObjectInitializer, kind: Kind, prototype: JsObject, } @@ -524,7 +605,10 @@ impl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> { fn new(realm: &'ctx Realm) -> BuiltInBuilder<'ctx, OrdinaryObject> { BuiltInBuilder { realm, - object: JsObject::with_null_proto(), + object: BuiltInObjectInitializer::Unique { + object: Object::default(), + data: ObjectData::ordinary(), + }, kind: OrdinaryObject, prototype: realm.intrinsics().constructors().object().prototype(), } @@ -535,7 +619,7 @@ impl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> { ) -> BuiltInBuilder<'ctx, OrdinaryObject> { BuiltInBuilder { realm, - object: I::get(realm.intrinsics()), + object: BuiltInObjectInitializer::Shared(I::get(realm.intrinsics())), kind: OrdinaryObject, prototype: realm.intrinsics().constructors().object().prototype(), } @@ -544,7 +628,7 @@ impl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> { fn with_object(realm: &'ctx Realm, object: JsObject) -> BuiltInBuilder<'ctx, OrdinaryObject> { BuiltInBuilder { realm, - object, + object: BuiltInObjectInitializer::Shared(object), kind: OrdinaryObject, prototype: realm.intrinsics().constructors().object().prototype(), } @@ -583,7 +667,7 @@ impl<'ctx> BuiltInBuilder<'ctx, Callable> { let constructor = SC::STANDARD_CONSTRUCTOR(realm.intrinsics().constructors()); BuiltInBuilder { realm, - object: constructor.constructor(), + object: BuiltInObjectInitializer::Shared(constructor.constructor()), kind: Callable { function: SC::constructor, name: js_string!(SC::NAME), @@ -617,7 +701,12 @@ impl<'ctx> BuiltInBuilder<'ctx, Callable> { impl BuiltInBuilder<'_, T> { /// Adds a new static method to the builtin object. - fn static_method(self, function: NativeFunctionPointer, binding: B, length: usize) -> Self + fn static_method( + mut self, + function: NativeFunctionPointer, + binding: B, + length: usize, + ) -> Self where B: Into, { @@ -628,7 +717,7 @@ impl BuiltInBuilder<'_, T> { .length(length) .build(); - self.object.borrow_mut().insert( + self.object.insert( binding.binding, PropertyDescriptor::builder() .value(function) @@ -640,7 +729,7 @@ impl BuiltInBuilder<'_, T> { } /// Adds a new static data property to the builtin object. - fn static_property(self, key: K, value: V, attribute: Attribute) -> Self + fn static_property(mut self, key: K, value: V, attribute: Attribute) -> Self where K: Into, V: Into, @@ -650,13 +739,13 @@ impl BuiltInBuilder<'_, T> { .writable(attribute.writable()) .enumerable(attribute.enumerable()) .configurable(attribute.configurable()); - self.object.borrow_mut().insert(key, property); + self.object.insert(key, property); self } /// Adds a new static accessor property to the builtin object. fn static_accessor( - self, + mut self, key: K, get: Option, set: Option, @@ -670,7 +759,7 @@ impl BuiltInBuilder<'_, T> { .maybe_set(set) .enumerable(attribute.enumerable()) .configurable(attribute.configurable()); - self.object.borrow_mut().insert(key, property); + self.object.insert(key, property); self } @@ -779,28 +868,22 @@ impl BuiltInBuilder<'_, Callable> { impl BuiltInBuilder<'_, OrdinaryObject> { /// Build the builtin object. - fn build(self) -> JsObject { - self.kind.apply_to(&self.object); + fn build(mut self) -> JsObject { + self.kind.apply_to(&mut self.object); - { - let mut object = self.object.borrow_mut(); - object.set_prototype(self.prototype); - } + self.object.set_prototype(self.prototype); - self.object + self.object.into_shared() } } impl BuiltInBuilder<'_, Callable> { /// Build the builtin callable. - fn build(self) -> JsFunction { - self.kind.apply_to(&self.object); + fn build(mut self) -> JsFunction { + self.kind.apply_to(&mut self.object); - { - let mut object = self.object.borrow_mut(); - object.set_prototype(self.prototype); - } + self.object.set_prototype(self.prototype); - JsFunction::from_object_unchecked(self.object) + JsFunction::from_object_unchecked(self.object.into_shared()) } } diff --git a/boa_engine/src/builtins/regexp/mod.rs b/boa_engine/src/builtins/regexp/mod.rs index 5eaaed5870..2cbfa0ce75 100644 --- a/boa_engine/src/builtins/regexp/mod.rs +++ b/boa_engine/src/builtins/regexp/mod.rs @@ -14,7 +14,10 @@ use crate::{ context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, error::JsNativeError, js_string, - object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData, CONSTRUCTOR}, + object::{ + internal_methods::get_prototype_from_constructor, JsObject, ObjectData, ObjectKind, + CONSTRUCTOR, + }, property::{Attribute, PropertyDescriptorBuilder}, realm::Realm, string::{utf16, CodePoint}, @@ -311,7 +314,9 @@ impl RegExp { original_source: p, original_flags: f, }; - obj.borrow_mut().data = ObjectData::reg_exp(Box::new(regexp)); + + // Safe to directly initialize since previous assertions ensure `obj` is a `Regexp` object. + *obj.borrow_mut().kind_mut() = ObjectKind::RegExp(Box::new(regexp)); // 16. Perform ? Set(obj, "lastIndex", +0𝔽, true). obj.set(utf16!("lastIndex"), 0, true, context)?; @@ -367,7 +372,7 @@ impl RegExp { if JsObject::equals( object, - &context.intrinsics().constructors().regexp().prototype, + &context.intrinsics().constructors().regexp().prototype(), ) { return Ok(JsValue::undefined()); } diff --git a/boa_engine/src/builtins/typed_array/mod.rs b/boa_engine/src/builtins/typed_array/mod.rs index db3b97a743..4c3570dfc1 100644 --- a/boa_engine/src/builtins/typed_array/mod.rs +++ b/boa_engine/src/builtins/typed_array/mod.rs @@ -23,7 +23,7 @@ use crate::{ context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, error::JsNativeError, js_string, - object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, + object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData, ObjectKind}, property::{Attribute, PropertyNameKind}, realm::Realm, string::utf16, @@ -3349,7 +3349,7 @@ impl TypedArray { // 18. Set O.[[ByteOffset]] to 0. // 19. Set O.[[ArrayLength]] to elementLength. drop(o_obj); - o.borrow_mut().data = ObjectData::integer_indexed(IntegerIndexed::new( + *o.borrow_mut().kind_mut() = ObjectKind::IntegerIndexed(IntegerIndexed::new( Some(data), constructor_name, 0, diff --git a/boa_engine/src/builtins/uri/mod.rs b/boa_engine/src/builtins/uri/mod.rs index e1acdab88a..60585f05aa 100644 --- a/boa_engine/src/builtins/uri/mod.rs +++ b/boa_engine/src/builtins/uri/mod.rs @@ -50,10 +50,10 @@ pub struct UriFunctions { impl Default for UriFunctions { fn default() -> Self { Self { - decode_uri: JsFunction::from_object_unchecked(JsObject::default()), - decode_uri_component: JsFunction::from_object_unchecked(JsObject::default()), - encode_uri: JsFunction::from_object_unchecked(JsObject::default()), - encode_uri_component: JsFunction::from_object_unchecked(JsObject::default()), + decode_uri: JsFunction::empty_intrinsic_function(false), + decode_uri_component: JsFunction::empty_intrinsic_function(false), + encode_uri: JsFunction::empty_intrinsic_function(false), + encode_uri_component: JsFunction::empty_intrinsic_function(false), } } } diff --git a/boa_engine/src/context/intrinsics.rs b/boa_engine/src/context/intrinsics.rs index 01b6ca182c..ff0a09bcbb 100644 --- a/boa_engine/src/context/intrinsics.rs +++ b/boa_engine/src/context/intrinsics.rs @@ -36,14 +36,14 @@ impl Intrinsics { /// Store a builtin constructor (such as `Object`) and its corresponding prototype. #[derive(Debug, Trace, Finalize)] pub struct StandardConstructor { - pub(crate) constructor: JsObject, - pub(crate) prototype: JsObject, + constructor: JsFunction, + prototype: JsObject, } impl Default for StandardConstructor { fn default() -> Self { Self { - constructor: JsObject::with_null_proto(), + constructor: JsFunction::empty_intrinsic_function(true), prototype: JsObject::with_null_proto(), } } @@ -53,7 +53,7 @@ impl StandardConstructor { /// Build a constructor with a defined prototype. fn with_prototype(prototype: JsObject) -> Self { Self { - constructor: JsObject::with_null_proto(), + constructor: JsFunction::empty_intrinsic_function(true), prototype, } } @@ -71,7 +71,7 @@ impl StandardConstructor { /// This is the same as `Object`, `Array`, etc. #[inline] pub fn constructor(&self) -> JsObject { - self.constructor.clone() + self.constructor.clone().into() } } @@ -138,7 +138,10 @@ impl Default for StandardConstructors { object: StandardConstructor::default(), proxy: StandardConstructor::default(), date: StandardConstructor::default(), - function: StandardConstructor::default(), + function: StandardConstructor { + constructor: JsFunction::empty_intrinsic_function(true), + prototype: JsFunction::empty_intrinsic_function(false).into(), + }, async_function: StandardConstructor::default(), generator_function: StandardConstructor::default(), array: StandardConstructor::with_prototype(JsObject::from_proto_and_data( @@ -740,7 +743,7 @@ pub struct IntrinsicObjects { throw_type_error: JsFunction, /// [`%Array.prototype.values%`](https://tc39.es/ecma262/#sec-array.prototype.values) - array_prototype_values: JsObject, + array_prototype_values: JsFunction, /// Cached iterator prototypes. iterator_prototypes: IteratorPrototypes, @@ -788,21 +791,21 @@ impl Default for IntrinsicObjects { reflect: JsObject::default(), math: JsObject::default(), json: JsObject::default(), - throw_type_error: JsFunction::from_object_unchecked(JsObject::default()), - array_prototype_values: JsObject::default(), + throw_type_error: JsFunction::empty_intrinsic_function(false), + array_prototype_values: JsFunction::empty_intrinsic_function(false), iterator_prototypes: IteratorPrototypes::default(), generator: JsObject::default(), async_generator: JsObject::default(), - eval: JsFunction::from_object_unchecked(JsObject::default()), + eval: JsFunction::empty_intrinsic_function(false), uri_functions: UriFunctions::default(), - is_finite: JsFunction::from_object_unchecked(JsObject::default()), - is_nan: JsFunction::from_object_unchecked(JsObject::default()), - parse_float: JsFunction::from_object_unchecked(JsObject::default()), - parse_int: JsFunction::from_object_unchecked(JsObject::default()), + is_finite: JsFunction::empty_intrinsic_function(false), + is_nan: JsFunction::empty_intrinsic_function(false), + parse_float: JsFunction::empty_intrinsic_function(false), + parse_int: JsFunction::empty_intrinsic_function(false), #[cfg(feature = "annex-b")] - escape: JsFunction::from_object_unchecked(JsObject::default()), + escape: JsFunction::empty_intrinsic_function(false), #[cfg(feature = "annex-b")] - unescape: JsFunction::from_object_unchecked(JsObject::default()), + unescape: JsFunction::empty_intrinsic_function(false), #[cfg(feature = "intl")] intl: JsObject::default(), } @@ -822,7 +825,7 @@ impl IntrinsicObjects { /// /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.values #[inline] - pub fn array_prototype_values(&self) -> JsObject { + pub fn array_prototype_values(&self) -> JsFunction { self.array_prototype_values.clone() } diff --git a/boa_engine/src/context/mod.rs b/boa_engine/src/context/mod.rs index dfa7bcecf5..452d7344fc 100644 --- a/boa_engine/src/context/mod.rs +++ b/boa_engine/src/context/mod.rs @@ -587,7 +587,7 @@ impl std::fmt::Debug for ContextBuilder<'_, '_, '_> { impl<'icu, 'hooks, 'queue> ContextBuilder<'icu, 'hooks, 'queue> { /// Creates a new [`ContextBuilder`] with a default empty [`Interner`] - /// and a default [`BoaProvider`] if the `intl` feature is enabled. + /// and a default `BoaProvider` if the `intl` feature is enabled. #[must_use] pub fn new() -> Self { Self::default() diff --git a/boa_engine/src/object/builtins/jsfunction.rs b/boa_engine/src/object/builtins/jsfunction.rs index 0bd9574a05..522242acc6 100644 --- a/boa_engine/src/object/builtins/jsfunction.rs +++ b/boa_engine/src/object/builtins/jsfunction.rs @@ -1,6 +1,9 @@ //! A Rust API wrapper for Boa's `Function` Builtin ECMAScript Object use crate::{ - object::{JsObject, JsObjectType}, + object::{ + internal_methods::function::{CONSTRUCTOR_INTERNAL_METHODS, FUNCTION_INTERNAL_METHODS}, + JsObject, JsObjectType, Object, + }, value::TryFromJs, Context, JsNativeError, JsResult, JsValue, }; @@ -14,11 +17,30 @@ pub struct JsFunction { } impl JsFunction { + /// Creates a new `JsFunction` from an object, without checking if the object is callable. pub(crate) fn from_object_unchecked(object: JsObject) -> Self { Self { inner: object } } - /// Create a [`JsFunction`] from a [`JsObject`], or return `None` if the object is not a function. + /// Creates a new, empty intrinsic function object with only its function internal methods set. + /// + /// Mainly used to initialize objects before a [`Context`] is available to do so. + /// + /// [`Context`]: crate::Context + pub(crate) fn empty_intrinsic_function(constructor: bool) -> Self { + Self { + inner: JsObject::from_object_and_vtable( + Object::default(), + if constructor { + &CONSTRUCTOR_INTERNAL_METHODS + } else { + &FUNCTION_INTERNAL_METHODS + }, + ), + } + } + + /// Creates a [`JsFunction`] from a [`JsObject`], or returns `None` if the object is not a function. /// /// This does not clone the fields of the function, it only does a shallow clone of the object. #[inline] diff --git a/boa_engine/src/object/internal_methods/mod.rs b/boa_engine/src/object/internal_methods/mod.rs index 744415b5ab..9511173c07 100644 --- a/boa_engine/src/object/internal_methods/mod.rs +++ b/boa_engine/src/object/internal_methods/mod.rs @@ -35,8 +35,7 @@ impl JsObject { #[track_caller] pub(crate) fn __get_prototype_of__(&self, context: &mut Context<'_>) -> JsResult { let _timer = Profiler::global().start_event("Object::__get_prototype_of__", "object"); - let func = self.borrow().data.internal_methods.__get_prototype_of__; - func(self, context) + (self.vtable().__get_prototype_of__)(self, context) } /// Internal method `[[SetPrototypeOf]]` @@ -53,8 +52,7 @@ impl JsObject { context: &mut Context<'_>, ) -> JsResult { let _timer = Profiler::global().start_event("Object::__set_prototype_of__", "object"); - let func = self.borrow().data.internal_methods.__set_prototype_of__; - func(self, val, context) + (self.vtable().__set_prototype_of__)(self, val, context) } /// Internal method `[[IsExtensible]]` @@ -67,8 +65,7 @@ impl JsObject { /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-isextensible pub(crate) fn __is_extensible__(&self, context: &mut Context<'_>) -> JsResult { let _timer = Profiler::global().start_event("Object::__is_extensible__", "object"); - let func = self.borrow().data.internal_methods.__is_extensible__; - func(self, context) + (self.vtable().__is_extensible__)(self, context) } /// Internal method `[[PreventExtensions]]` @@ -81,8 +78,7 @@ impl JsObject { /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-preventextensions pub(crate) fn __prevent_extensions__(&self, context: &mut Context<'_>) -> JsResult { let _timer = Profiler::global().start_event("Object::__prevent_extensions__", "object"); - let func = self.borrow().data.internal_methods.__prevent_extensions__; - func(self, context) + (self.vtable().__prevent_extensions__)(self, context) } /// Internal method `[[GetOwnProperty]]` @@ -99,8 +95,7 @@ impl JsObject { context: &mut Context<'_>, ) -> JsResult> { let _timer = Profiler::global().start_event("Object::__get_own_property__", "object"); - let func = self.borrow().data.internal_methods.__get_own_property__; - func(self, key, context) + (self.vtable().__get_own_property__)(self, key, context) } /// Internal method `[[DefineOwnProperty]]` @@ -118,8 +113,7 @@ impl JsObject { context: &mut Context<'_>, ) -> JsResult { let _timer = Profiler::global().start_event("Object::__define_own_property__", "object"); - let func = self.borrow().data.internal_methods.__define_own_property__; - func(self, key, desc, context) + (self.vtable().__define_own_property__)(self, key, desc, context) } /// Internal method `[[hasProperty]]`. @@ -136,8 +130,7 @@ impl JsObject { context: &mut Context<'_>, ) -> JsResult { let _timer = Profiler::global().start_event("Object::__has_property__", "object"); - let func = self.borrow().data.internal_methods.__has_property__; - func(self, key, context) + (self.vtable().__has_property__)(self, key, context) } /// Internal method `[[Get]]` @@ -155,8 +148,7 @@ impl JsObject { context: &mut Context<'_>, ) -> JsResult { let _timer = Profiler::global().start_event("Object::__get__", "object"); - let func = self.borrow().data.internal_methods.__get__; - func(self, key, receiver, context) + (self.vtable().__get__)(self, key, receiver, context) } /// Internal method `[[Set]]` @@ -175,8 +167,7 @@ impl JsObject { context: &mut Context<'_>, ) -> JsResult { let _timer = Profiler::global().start_event("Object::__set__", "object"); - let func = self.borrow().data.internal_methods.__set__; - func(self, key, value, receiver, context) + (self.vtable().__set__)(self, key, value, receiver, context) } /// Internal method `[[Delete]]` @@ -193,8 +184,7 @@ impl JsObject { context: &mut Context<'_>, ) -> JsResult { let _timer = Profiler::global().start_event("Object::__delete__", "object"); - let func = self.borrow().data.internal_methods.__delete__; - func(self, key, context) + (self.vtable().__delete__)(self, key, context) } /// Internal method `[[OwnPropertyKeys]]` @@ -211,8 +201,7 @@ impl JsObject { context: &mut Context<'_>, ) -> JsResult> { let _timer = Profiler::global().start_event("Object::__own_property_keys__", "object"); - let func = self.borrow().data.internal_methods.__own_property_keys__; - func(self, context) + (self.vtable().__own_property_keys__)(self, context) } /// Internal method `[[Call]]` @@ -231,9 +220,9 @@ impl JsObject { context: &mut Context<'_>, ) -> JsResult { let _timer = Profiler::global().start_event("Object::__call__", "object"); - - let func = self.borrow().data.internal_methods.__call__; - func.expect("called `[[Call]]` for object without a `[[Call]]` internal method")( + self.vtable() + .__call__ + .expect("called `[[Call]]` for object without a `[[Call]]` internal method")( self, this, args, context, ) } @@ -254,9 +243,9 @@ impl JsObject { context: &mut Context<'_>, ) -> JsResult { let _timer = Profiler::global().start_event("Object::__construct__", "object"); - - let func = self.borrow().data.internal_methods.__construct__; - func.expect("called `[[Construct]]` for object without a `[[Construct]]` internal method")( + self.vtable() + .__construct__ + .expect("called `[[Construct]]` for object without a `[[Construct]]` internal method")( self, args, new_target, context, ) } @@ -379,8 +368,7 @@ pub(crate) fn ordinary_set_prototype_of( // 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 + else if proto.vtable().__get_prototype_of__ as usize != ordinary_get_prototype_of as usize { break; } diff --git a/boa_engine/src/object/jsobject.rs b/boa_engine/src/object/jsobject.rs index f81676ae55..b187dc1857 100644 --- a/boa_engine/src/object/jsobject.rs +++ b/boa_engine/src/object/jsobject.rs @@ -2,7 +2,9 @@ //! //! The `JsObject` is a garbage collected Object. -use super::{JsPrototype, NativeObject, Object, PropertyMap}; +use super::{ + internal_methods::InternalObjectMethods, JsPrototype, NativeObject, Object, PropertyMap, +}; use crate::{ context::intrinsics::Intrinsics, error::JsNativeError, @@ -29,12 +31,43 @@ pub type Ref<'a, T> = boa_gc::GcRef<'a, T>; pub type RefMut<'a, T, U> = boa_gc::GcRefMut<'a, T, U>; /// Garbage collected `Object`. -#[derive(Trace, Finalize, Clone, Default)] +#[derive(Trace, Finalize, Clone)] pub struct JsObject { - inner: Gc>, + inner: Gc, +} + +/// An `Object` that has an additional `vtable` with its internal methods. +// We have to skip implementing `Debug` for this because not using the +// implementation of `Debug` for `JsObject` could easily cause stack overflows, +// so we have to force our users to debug the `JsObject` instead. +#[allow(missing_debug_implementations)] +#[derive(Trace, Finalize)] +pub struct VTableObject { + object: GcRefCell, + #[unsafe_ignore_trace] + vtable: &'static InternalObjectMethods, +} + +impl Default for JsObject { + fn default() -> Self { + let data = ObjectData::ordinary(); + Self::from_object_and_vtable(Object::default(), data.internal_methods) + } } impl JsObject { + /// Creates a new `JsObject` from its inner object and its vtable. + pub(crate) fn from_object_and_vtable( + object: Object, + vtable: &'static InternalObjectMethods, + ) -> Self { + Self { + inner: Gc::new(VTableObject { + object: GcRefCell::new(object), + vtable, + }), + } + } /// Creates a new ordinary object with its prototype set to the `Object` prototype. /// /// This is equivalent to calling the specification's abstract operation @@ -71,13 +104,16 @@ impl JsObject { /// [`OrdinaryObjectCreate`]: https://tc39.es/ecma262/#sec-ordinaryobjectcreate pub fn from_proto_and_data>>(prototype: O, data: ObjectData) -> Self { Self { - inner: Gc::new(GcRefCell::new(Object { - data, - prototype: prototype.into(), - extensible: true, - properties: PropertyMap::default(), - private_elements: ThinVec::new(), - })), + inner: Gc::new(VTableObject { + object: GcRefCell::new(Object { + kind: data.kind, + properties: PropertyMap::default(), + prototype: prototype.into(), + extensible: true, + private_elements: ThinVec::new(), + }), + vtable: data.internal_methods, + }), } } @@ -116,7 +152,7 @@ impl JsObject { /// This is the non-panicking variant of [`borrow`](#method.borrow). #[inline] pub fn try_borrow(&self) -> StdResult, BorrowError> { - self.inner.try_borrow().map_err(|_| BorrowError) + self.inner.object.try_borrow().map_err(|_| BorrowError) } /// Mutably borrows the object, returning an error if the value is currently borrowed. @@ -127,7 +163,10 @@ impl JsObject { /// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut). #[inline] pub fn try_borrow_mut(&self) -> StdResult, BorrowMutError> { - self.inner.try_borrow_mut().map_err(|_| BorrowMutError) + self.inner + .object + .try_borrow_mut() + .map_err(|_| BorrowMutError) } /// Checks if the garbage collected memory is the same. @@ -852,7 +891,7 @@ Cannot both specify accessors and a value or writable attribute", #[inline] #[track_caller] pub fn is_callable(&self) -> bool { - self.borrow().data.internal_methods.__call__.is_some() + self.inner.vtable.__call__.is_some() } /// It determines if Object is a function object with a `[[Construct]]` internal method. @@ -864,21 +903,19 @@ Cannot both specify accessors and a value or writable attribute", #[inline] #[track_caller] pub fn is_constructor(&self) -> bool { - self.borrow().data.internal_methods.__construct__.is_some() + self.inner.vtable.__construct__.is_some() } /// Returns true if the `JsObject` is the global for a Realm pub fn is_global(&self) -> bool { - matches!( - self.borrow().data, - ObjectData { - kind: ObjectKind::Global, - .. - } - ) + matches!(self.inner.object.borrow().kind, ObjectKind::Global) } - pub(crate) const fn inner(&self) -> &Gc> { + pub(crate) fn vtable(&self) -> &'static InternalObjectMethods { + self.inner.vtable + } + + pub(crate) const fn inner(&self) -> &Gc { &self.inner } } @@ -886,13 +923,13 @@ Cannot both specify accessors and a value or writable attribute", impl AsRef> for JsObject { #[inline] fn as_ref(&self) -> &GcRefCell { - &self.inner + &self.inner.object } } -impl From>> for JsObject { +impl From> for JsObject { #[inline] - fn from(inner: Gc>) -> Self { + fn from(inner: Gc) -> Self { Self { inner } } } @@ -1014,6 +1051,7 @@ impl RecursionLimiter { impl Debug for JsObject { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { + let ptr: *const _ = self.vtable(); let limiter = RecursionLimiter::new(self); // Typically, using `!limiter.live` would be good enough here. @@ -1023,7 +1061,10 @@ impl Debug for JsObject { // Instead, we check if the object has appeared before in the entire graph. This means that objects will appear // at most once, hopefully making things a bit clearer. if !limiter.visited && !limiter.live { - f.debug_tuple("JsObject").field(&self.inner).finish() + f.debug_struct("JsObject") + .field("vtable", &ptr) + .field("object", &self.inner.object) + .finish() } else { f.write_str("{ ... }") } diff --git a/boa_engine/src/object/mod.rs b/boa_engine/src/object/mod.rs index a850e36d95..243e9b6cb6 100644 --- a/boa_engine/src/object/mod.rs +++ b/boa_engine/src/object/mod.rs @@ -55,7 +55,7 @@ use crate::{ Context, JsBigInt, JsString, JsSymbol, JsValue, }; -use boa_gc::{custom_trace, Finalize, GcRefCell, Trace, WeakGc}; +use boa_gc::{custom_trace, Finalize, Trace, WeakGc}; use std::{ any::Any, fmt::{self, Debug}, @@ -119,7 +119,7 @@ impl NativeObject for T { #[derive(Debug, Finalize)] pub struct Object { /// The type of the object. - pub data: ObjectData, + kind: ObjectKind, /// The collection of properties contained in the object properties: PropertyMap, /// Instance prototype `__proto__`. @@ -130,9 +130,21 @@ pub struct Object { private_elements: ThinVec<(PrivateName, PrivateElement)>, } +impl Default for Object { + fn default() -> Self { + Self { + kind: ObjectKind::Ordinary, + properties: PropertyMap::default(), + prototype: None, + extensible: true, + private_elements: ThinVec::new(), + } + } +} + unsafe impl Trace for Object { boa_gc::custom_trace!(this, { - mark(&this.data); + mark(&this.kind); mark(&this.properties); mark(&this.prototype); for (_, element) in &this.private_elements { @@ -161,10 +173,19 @@ pub enum PrivateElement { } /// Defines the kind of an object and its internal methods -#[derive(Trace, Finalize)] pub struct ObjectData { - kind: ObjectKind, - internal_methods: &'static InternalObjectMethods, + pub(crate) kind: ObjectKind, + pub(crate) internal_methods: &'static InternalObjectMethods, +} + +impl Debug for ObjectData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let ptr: *const _ = self.internal_methods; + f.debug_struct("ObjectData") + .field("kind", &self.kind) + .field("internal_methods", &ptr) + .finish() + } } /// Defines the different types of objects. @@ -270,13 +291,13 @@ pub enum ObjectKind { Promise(Promise), /// The `WeakRef` object kind. - WeakRef(WeakGc>), + WeakRef(WeakGc), /// The `WeakMap` object kind. - WeakMap(boa_gc::WeakMap, JsValue>), + WeakMap(boa_gc::WeakMap), /// The `WeakSet` object kind. - WeakSet(boa_gc::WeakMap, ()>), + WeakSet(boa_gc::WeakMap), /// The `Intl.Collator` object kind. #[cfg(feature = "intl")] @@ -622,7 +643,7 @@ impl ObjectData { } /// Creates the `WeakRef` object data - pub fn weak_ref(weak_ref: WeakGc>) -> Self { + pub fn weak_ref(weak_ref: WeakGc) -> Self { Self { kind: ObjectKind::WeakRef(weak_ref), internal_methods: &ORDINARY_INTERNAL_METHODS, @@ -631,7 +652,7 @@ impl ObjectData { /// Create the `WeakMap` object data #[must_use] - pub fn weak_map(weak_map: boa_gc::WeakMap, JsValue>) -> Self { + pub fn weak_map(weak_map: boa_gc::WeakMap) -> Self { Self { kind: ObjectKind::WeakMap(weak_map), internal_methods: &ORDINARY_INTERNAL_METHODS, @@ -640,7 +661,7 @@ impl ObjectData { /// Create the `WeakSet` object data #[must_use] - pub fn weak_set(weak_set: boa_gc::WeakMap, ()>) -> Self { + pub fn weak_set(weak_set: boa_gc::WeakMap) -> Self { Self { kind: ObjectKind::WeakSet(weak_set), internal_methods: &ORDINARY_INTERNAL_METHODS, @@ -756,55 +777,31 @@ impl Debug for ObjectKind { } } -impl Debug for ObjectData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ObjectData") - .field("kind", &self.kind) - .finish_non_exhaustive() - } -} - -impl Default for Object { - /// Return a new `ObjectData` struct, with `kind` set to Ordinary - #[inline] - fn default() -> Self { - Self { - data: ObjectData::ordinary(), - properties: PropertyMap::default(), - prototype: None, - extensible: true, - private_elements: ThinVec::default(), - } +impl Object { + /// Returns a mutable reference to the kind of an object. + pub(crate) fn kind_mut(&mut self) -> &mut ObjectKind { + &mut self.kind } -} -impl Object { /// Returns the kind of the object. #[inline] pub const fn kind(&self) -> &ObjectKind { - &self.data.kind + &self.kind } /// Checks if it's an `AsyncFromSyncIterator` object. #[inline] pub const fn is_async_from_sync_iterator(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::AsyncFromSyncIterator(_), - .. - } - ) + matches!(self.kind, ObjectKind::AsyncFromSyncIterator(_)) } /// Returns a reference to the `AsyncFromSyncIterator` data on the object. #[inline] pub const fn as_async_from_sync_iterator(&self) -> Option<&AsyncFromSyncIterator> { - match self.data { - ObjectData { - kind: ObjectKind::AsyncFromSyncIterator(ref async_from_sync_iterator), - .. - } => Some(async_from_sync_iterator), + match self.kind { + ObjectKind::AsyncFromSyncIterator(ref async_from_sync_iterator) => { + Some(async_from_sync_iterator) + } _ => None, } } @@ -812,23 +809,14 @@ impl Object { /// Checks if it's an `AsyncGenerator` object. #[inline] pub const fn is_async_generator(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::AsyncGenerator(_), - .. - } - ) + matches!(self.kind, ObjectKind::AsyncGenerator(_)) } /// Returns a reference to the async generator data on the object. #[inline] pub const fn as_async_generator(&self) -> Option<&AsyncGenerator> { - match self.data { - ObjectData { - kind: ObjectKind::AsyncGenerator(ref async_generator), - .. - } => Some(async_generator), + match self.kind { + ObjectKind::AsyncGenerator(ref async_generator) => Some(async_generator), _ => None, } } @@ -836,11 +824,8 @@ impl Object { /// Returns a mutable reference to the async generator data on the object. #[inline] pub fn as_async_generator_mut(&mut self) -> Option<&mut AsyncGenerator> { - match self.data { - ObjectData { - kind: ObjectKind::AsyncGenerator(ref mut async_generator), - .. - } => Some(async_generator), + match self.kind { + ObjectKind::AsyncGenerator(ref mut async_generator) => Some(async_generator), _ => None, } } @@ -848,13 +833,7 @@ impl Object { /// Checks if the object is a `Array` object. #[inline] pub const fn is_array(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::Array, - .. - } - ) + matches!(self.kind, ObjectKind::Array) } #[inline] @@ -865,35 +844,20 @@ impl Object { /// Checks if the object is a `DataView` object. #[inline] pub const fn is_data_view(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::DataView(_), - .. - } - ) + matches!(self.kind, ObjectKind::DataView(_)) } /// Checks if the object is a `ArrayBuffer` object. #[inline] pub const fn is_array_buffer(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::ArrayBuffer(_), - .. - } - ) + matches!(self.kind, ObjectKind::ArrayBuffer(_)) } /// Gets the array buffer data if the object is a `ArrayBuffer`. #[inline] pub const fn as_array_buffer(&self) -> Option<&ArrayBuffer> { - match &self.data { - ObjectData { - kind: ObjectKind::ArrayBuffer(buffer), - .. - } => Some(buffer), + match &self.kind { + ObjectKind::ArrayBuffer(buffer) => Some(buffer), _ => None, } } @@ -901,11 +865,8 @@ impl Object { /// Gets the mutable array buffer data if the object is a `ArrayBuffer`. #[inline] pub fn as_array_buffer_mut(&mut self) -> Option<&mut ArrayBuffer> { - match &mut self.data { - ObjectData { - kind: ObjectKind::ArrayBuffer(buffer), - .. - } => Some(buffer), + match &mut self.kind { + ObjectKind::ArrayBuffer(buffer) => Some(buffer), _ => None, } } @@ -913,23 +874,14 @@ impl Object { /// Checks if the object is a `ArrayIterator` object. #[inline] pub const fn is_array_iterator(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::ArrayIterator(_), - .. - } - ) + matches!(self.kind, ObjectKind::ArrayIterator(_)) } /// Gets the array-iterator data if the object is a `ArrayIterator`. #[inline] pub const fn as_array_iterator(&self) -> Option<&ArrayIterator> { - match self.data { - ObjectData { - kind: ObjectKind::ArrayIterator(ref iter), - .. - } => Some(iter), + match self.kind { + ObjectKind::ArrayIterator(ref iter) => Some(iter), _ => None, } } @@ -937,11 +889,8 @@ impl Object { /// Gets the mutable array-iterator data if the object is a `ArrayIterator`. #[inline] pub fn as_array_iterator_mut(&mut self) -> Option<&mut ArrayIterator> { - match &mut self.data { - ObjectData { - kind: ObjectKind::ArrayIterator(iter), - .. - } => Some(iter), + match &mut self.kind { + ObjectKind::ArrayIterator(iter) => Some(iter), _ => None, } } @@ -949,11 +898,8 @@ impl Object { /// Gets the mutable string-iterator data if the object is a `StringIterator`. #[inline] pub fn as_string_iterator_mut(&mut self) -> Option<&mut StringIterator> { - match &mut self.data { - ObjectData { - kind: ObjectKind::StringIterator(iter), - .. - } => Some(iter), + match &mut self.kind { + ObjectKind::StringIterator(iter) => Some(iter), _ => None, } } @@ -961,11 +907,8 @@ impl Object { /// Gets the mutable regexp-string-iterator data if the object is a `RegExpStringIterator`. #[inline] pub fn as_regexp_string_iterator_mut(&mut self) -> Option<&mut RegExpStringIterator> { - match &mut self.data { - ObjectData { - kind: ObjectKind::RegExpStringIterator(iter), - .. - } => Some(iter), + match &mut self.kind { + ObjectKind::RegExpStringIterator(iter) => Some(iter), _ => None, } } @@ -973,11 +916,8 @@ impl Object { /// Gets the for-in-iterator data if the object is a `ForInIterator`. #[inline] pub const fn as_for_in_iterator(&self) -> Option<&ForInIterator> { - match &self.data { - ObjectData { - kind: ObjectKind::ForInIterator(iter), - .. - } => Some(iter), + match &self.kind { + ObjectKind::ForInIterator(iter) => Some(iter), _ => None, } } @@ -985,11 +925,8 @@ impl Object { /// Gets the mutable for-in-iterator data if the object is a `ForInIterator`. #[inline] pub fn as_for_in_iterator_mut(&mut self) -> Option<&mut ForInIterator> { - match &mut self.data { - ObjectData { - kind: ObjectKind::ForInIterator(iter), - .. - } => Some(iter), + match &mut self.kind { + ObjectKind::ForInIterator(iter) => Some(iter), _ => None, } } @@ -997,23 +934,14 @@ impl Object { /// Checks if the object is a `Map` object. #[inline] pub const fn is_map(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::Map(_), - .. - } - ) + matches!(self.kind, ObjectKind::Map(_)) } /// Gets the map data if the object is a `Map`. #[inline] pub const fn as_map(&self) -> Option<&OrderedMap> { - match self.data { - ObjectData { - kind: ObjectKind::Map(ref map), - .. - } => Some(map), + match self.kind { + ObjectKind::Map(ref map) => Some(map), _ => None, } } @@ -1021,11 +949,8 @@ impl Object { /// Gets the mutable map data if the object is a `Map`. #[inline] pub fn as_map_mut(&mut self) -> Option<&mut OrderedMap> { - match &mut self.data { - ObjectData { - kind: ObjectKind::Map(map), - .. - } => Some(map), + match &mut self.kind { + ObjectKind::Map(map) => Some(map), _ => None, } } @@ -1033,23 +958,14 @@ impl Object { /// Checks if the object is a `MapIterator` object. #[inline] pub const fn is_map_iterator(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::MapIterator(_), - .. - } - ) + matches!(self.kind, ObjectKind::MapIterator(_)) } /// Gets the map iterator data if the object is a `MapIterator`. #[inline] pub const fn as_map_iterator_ref(&self) -> Option<&MapIterator> { - match &self.data { - ObjectData { - kind: ObjectKind::MapIterator(iter), - .. - } => Some(iter), + match &self.kind { + ObjectKind::MapIterator(iter) => Some(iter), _ => None, } } @@ -1057,11 +973,8 @@ impl Object { /// Gets the mutable map iterator data if the object is a `MapIterator`. #[inline] pub fn as_map_iterator_mut(&mut self) -> Option<&mut MapIterator> { - match &mut self.data { - ObjectData { - kind: ObjectKind::MapIterator(iter), - .. - } => Some(iter), + match &mut self.kind { + ObjectKind::MapIterator(iter) => Some(iter), _ => None, } } @@ -1069,23 +982,14 @@ impl Object { /// Checks if the object is a `Set` object. #[inline] pub const fn is_set(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::Set(_), - .. - } - ) + matches!(self.kind, ObjectKind::Set(_)) } /// Gets the set data if the object is a `Set`. #[inline] pub const fn as_set(&self) -> Option<&OrderedSet> { - match self.data { - ObjectData { - kind: ObjectKind::Set(ref set), - .. - } => Some(set), + match self.kind { + ObjectKind::Set(ref set) => Some(set), _ => None, } } @@ -1093,11 +997,8 @@ impl Object { /// Gets the mutable set data if the object is a `Set`. #[inline] pub fn as_set_mut(&mut self) -> Option<&mut OrderedSet> { - match &mut self.data { - ObjectData { - kind: ObjectKind::Set(set), - .. - } => Some(set), + match &mut self.kind { + ObjectKind::Set(set) => Some(set), _ => None, } } @@ -1105,23 +1006,14 @@ impl Object { /// Checks if the object is a `SetIterator` object. #[inline] pub const fn is_set_iterator(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::SetIterator(_), - .. - } - ) + matches!(self.kind, ObjectKind::SetIterator(_)) } /// Gets the mutable set iterator data if the object is a `SetIterator`. #[inline] pub fn as_set_iterator_mut(&mut self) -> Option<&mut SetIterator> { - match &mut self.data { - ObjectData { - kind: ObjectKind::SetIterator(iter), - .. - } => Some(iter), + match &mut self.kind { + ObjectKind::SetIterator(iter) => Some(iter), _ => None, } } @@ -1129,23 +1021,14 @@ impl Object { /// Checks if the object is a `String` object. #[inline] pub const fn is_string(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::String(_), - .. - } - ) + matches!(self.kind, ObjectKind::String(_)) } /// Gets the string data if the object is a `String`. #[inline] pub fn as_string(&self) -> Option { - match self.data { - ObjectData { - kind: ObjectKind::String(ref string), - .. - } => Some(string.clone()), + match self.kind { + ObjectKind::String(ref string) => Some(string.clone()), _ => None, } } @@ -1153,24 +1036,16 @@ impl Object { /// Checks if the object is a `Function` object. #[inline] pub const fn is_function(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::Function(_), - .. - } - ) + matches!(self.kind, ObjectKind::Function(_)) } /// Gets the function data if the object is a `Function`. #[inline] pub const fn as_function(&self) -> Option<&Function> { - match self.data { - ObjectData { - kind: - ObjectKind::Function(ref function) | ObjectKind::GeneratorFunction(ref function), - .. - } => Some(function), + match self.kind { + ObjectKind::Function(ref function) | ObjectKind::GeneratorFunction(ref function) => { + Some(function) + } _ => None, } } @@ -1178,13 +1053,9 @@ impl Object { /// Gets the mutable function data if the object is a `Function`. #[inline] pub fn as_function_mut(&mut self) -> Option<&mut Function> { - match self.data { - ObjectData { - kind: - ObjectKind::Function(ref mut function) - | ObjectKind::GeneratorFunction(ref mut function), - .. - } => Some(function), + match self.kind { + ObjectKind::Function(ref mut function) + | ObjectKind::GeneratorFunction(ref mut function) => Some(function), _ => None, } } @@ -1192,11 +1063,8 @@ impl Object { /// Gets the bound function data if the object is a `BoundFunction`. #[inline] pub const fn as_bound_function(&self) -> Option<&BoundFunction> { - match self.data { - ObjectData { - kind: ObjectKind::BoundFunction(ref bound_function), - .. - } => Some(bound_function), + match self.kind { + ObjectKind::BoundFunction(ref bound_function) => Some(bound_function), _ => None, } } @@ -1204,23 +1072,14 @@ impl Object { /// Checks if the object is a `Generator` object. #[inline] pub const fn is_generator(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::Generator(_), - .. - } - ) + matches!(self.kind, ObjectKind::Generator(_)) } /// Gets the generator data if the object is a `Generator`. #[inline] pub const fn as_generator(&self) -> Option<&Generator> { - match self.data { - ObjectData { - kind: ObjectKind::Generator(ref generator), - .. - } => Some(generator), + match self.kind { + ObjectKind::Generator(ref generator) => Some(generator), _ => None, } } @@ -1228,11 +1087,8 @@ impl Object { /// Gets the mutable generator data if the object is a `Generator`. #[inline] pub fn as_generator_mut(&mut self) -> Option<&mut Generator> { - match self.data { - ObjectData { - kind: ObjectKind::Generator(ref mut generator), - .. - } => Some(generator), + match self.kind { + ObjectKind::Generator(ref mut generator) => Some(generator), _ => None, } } @@ -1240,23 +1096,14 @@ impl Object { /// Checks if the object is a `Symbol` object. #[inline] pub const fn is_symbol(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::Symbol(_), - .. - } - ) + matches!(self.kind, ObjectKind::Symbol(_)) } /// Gets the error data if the object is a `Symbol`. #[inline] pub fn as_symbol(&self) -> Option { - match self.data { - ObjectData { - kind: ObjectKind::Symbol(ref symbol), - .. - } => Some(symbol.clone()), + match self.kind { + ObjectKind::Symbol(ref symbol) => Some(symbol.clone()), _ => None, } } @@ -1264,23 +1111,14 @@ impl Object { /// Checks if the object is a `Error` object. #[inline] pub const fn is_error(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::Error(_), - .. - } - ) + matches!(self.kind, ObjectKind::Error(_)) } /// Gets the error data if the object is a `Error`. #[inline] pub const fn as_error(&self) -> Option { - match self.data { - ObjectData { - kind: ObjectKind::Error(e), - .. - } => Some(e), + match self.kind { + ObjectKind::Error(e) => Some(e), _ => None, } } @@ -1288,23 +1126,14 @@ impl Object { /// Checks if the object is a `Boolean` object. #[inline] pub const fn is_boolean(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::Boolean(_), - .. - } - ) + matches!(self.kind, ObjectKind::Boolean(_)) } /// Gets the boolean data if the object is a `Boolean`. #[inline] pub const fn as_boolean(&self) -> Option { - match self.data { - ObjectData { - kind: ObjectKind::Boolean(boolean), - .. - } => Some(boolean), + match self.kind { + ObjectKind::Boolean(boolean) => Some(boolean), _ => None, } } @@ -1312,23 +1141,14 @@ impl Object { /// Checks if the object is a `Number` object. #[inline] pub const fn is_number(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::Number(_), - .. - } - ) + matches!(self.kind, ObjectKind::Number(_)) } /// Gets the number data if the object is a `Number`. #[inline] pub const fn as_number(&self) -> Option { - match self.data { - ObjectData { - kind: ObjectKind::Number(number), - .. - } => Some(number), + match self.kind { + ObjectKind::Number(number) => Some(number), _ => None, } } @@ -1336,23 +1156,14 @@ impl Object { /// Checks if the object is a `BigInt` object. #[inline] pub const fn is_bigint(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::BigInt(_), - .. - } - ) + matches!(self.kind, ObjectKind::BigInt(_)) } /// Gets the bigint data if the object is a `BigInt`. #[inline] pub const fn as_bigint(&self) -> Option<&JsBigInt> { - match self.data { - ObjectData { - kind: ObjectKind::BigInt(ref bigint), - .. - } => Some(bigint), + match self.kind { + ObjectKind::BigInt(ref bigint) => Some(bigint), _ => None, } } @@ -1360,23 +1171,14 @@ impl Object { /// Checks if the object is a `Date` object. #[inline] pub const fn is_date(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::Date(_), - .. - } - ) + matches!(self.kind, ObjectKind::Date(_)) } /// Gets the date data if the object is a `Date`. #[inline] pub const fn as_date(&self) -> Option<&Date> { - match self.data { - ObjectData { - kind: ObjectKind::Date(ref date), - .. - } => Some(date), + match self.kind { + ObjectKind::Date(ref date) => Some(date), _ => None, } } @@ -1384,11 +1186,8 @@ impl Object { /// Gets the mutable date data if the object is a `Date`. #[inline] pub fn as_date_mut(&mut self) -> Option<&mut Date> { - match self.data { - ObjectData { - kind: ObjectKind::Date(ref mut date), - .. - } => Some(date), + match self.kind { + ObjectKind::Date(ref mut date) => Some(date), _ => None, } } @@ -1396,23 +1195,14 @@ impl Object { /// Checks if it a `RegExp` object. #[inline] pub const fn is_regexp(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::RegExp(_), - .. - } - ) + matches!(self.kind, ObjectKind::RegExp(_)) } /// Gets the regexp data if the object is a regexp. #[inline] pub const fn as_regexp(&self) -> Option<&RegExp> { - match self.data { - ObjectData { - kind: ObjectKind::RegExp(ref regexp), - .. - } => Some(regexp), + match self.kind { + ObjectKind::RegExp(ref regexp) => Some(regexp), _ => None, } } @@ -1420,23 +1210,13 @@ impl Object { /// Checks if it a `TypedArray` object. #[inline] pub const fn is_typed_array(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::IntegerIndexed(_), - .. - } - ) + matches!(self.kind, ObjectKind::IntegerIndexed(_)) } /// Checks if it a `Uint8Array` object. #[inline] pub const fn is_typed_uint8_array(&self) -> bool { - if let ObjectData { - kind: ObjectKind::IntegerIndexed(ref int), - .. - } = self.data - { + if let ObjectKind::IntegerIndexed(ref int) = self.kind { matches!(int.typed_array_name(), TypedArrayKind::Uint8) } else { false @@ -1446,11 +1226,7 @@ impl Object { /// Checks if it a `Int8Array` object. #[inline] pub const fn is_typed_int8_array(&self) -> bool { - if let ObjectData { - kind: ObjectKind::IntegerIndexed(ref int), - .. - } = self.data - { + if let ObjectKind::IntegerIndexed(ref int) = self.kind { matches!(int.typed_array_name(), TypedArrayKind::Int8) } else { false @@ -1460,11 +1236,7 @@ impl Object { /// Checks if it a `Uint16Array` object. #[inline] pub const fn is_typed_uint16_array(&self) -> bool { - if let ObjectData { - kind: ObjectKind::IntegerIndexed(ref int), - .. - } = self.data - { + if let ObjectKind::IntegerIndexed(ref int) = self.kind { matches!(int.typed_array_name(), TypedArrayKind::Uint16) } else { false @@ -1474,11 +1246,7 @@ impl Object { /// Checks if it a `Int16Array` object. #[inline] pub const fn is_typed_int16_array(&self) -> bool { - if let ObjectData { - kind: ObjectKind::IntegerIndexed(ref int), - .. - } = self.data - { + if let ObjectKind::IntegerIndexed(ref int) = self.kind { matches!(int.typed_array_name(), TypedArrayKind::Int16) } else { false @@ -1488,11 +1256,7 @@ impl Object { /// Checks if it a `Uint32Array` object. #[inline] pub const fn is_typed_uint32_array(&self) -> bool { - if let ObjectData { - kind: ObjectKind::IntegerIndexed(ref int), - .. - } = self.data - { + if let ObjectKind::IntegerIndexed(ref int) = self.kind { matches!(int.typed_array_name(), TypedArrayKind::Uint32) } else { false @@ -1502,11 +1266,7 @@ impl Object { /// Checks if it a `Int32Array` object. #[inline] pub const fn is_typed_int32_array(&self) -> bool { - if let ObjectData { - kind: ObjectKind::IntegerIndexed(ref int), - .. - } = self.data - { + if let ObjectKind::IntegerIndexed(ref int) = self.kind { matches!(int.typed_array_name(), TypedArrayKind::Int32) } else { false @@ -1516,11 +1276,7 @@ impl Object { /// Checks if it a `Float32Array` object. #[inline] pub const fn is_typed_float32_array(&self) -> bool { - if let ObjectData { - kind: ObjectKind::IntegerIndexed(ref int), - .. - } = self.data - { + if let ObjectKind::IntegerIndexed(ref int) = self.kind { matches!(int.typed_array_name(), TypedArrayKind::Float32) } else { false @@ -1530,11 +1286,7 @@ impl Object { /// Checks if it a `Float64Array` object. #[inline] pub const fn is_typed_float64_array(&self) -> bool { - if let ObjectData { - kind: ObjectKind::IntegerIndexed(ref int), - .. - } = self.data - { + if let ObjectKind::IntegerIndexed(ref int) = self.kind { matches!(int.typed_array_name(), TypedArrayKind::Float64) } else { false @@ -1544,11 +1296,8 @@ impl Object { /// Gets the data view data if the object is a `DataView`. #[inline] pub const fn as_data_view(&self) -> Option<&DataView> { - match &self.data { - ObjectData { - kind: ObjectKind::DataView(data_view), - .. - } => Some(data_view), + match &self.kind { + ObjectKind::DataView(data_view) => Some(data_view), _ => None, } } @@ -1556,11 +1305,8 @@ impl Object { /// Gets the mutable data view data if the object is a `DataView`. #[inline] pub fn as_data_view_mut(&mut self) -> Option<&mut DataView> { - match &mut self.data { - ObjectData { - kind: ObjectKind::DataView(data_view), - .. - } => Some(data_view), + match &mut self.kind { + ObjectKind::DataView(data_view) => Some(data_view), _ => None, } } @@ -1568,23 +1314,14 @@ impl Object { /// Checks if it is an `Arguments` object. #[inline] pub const fn is_arguments(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::Arguments(_), - .. - } - ) + matches!(self.kind, ObjectKind::Arguments(_)) } /// Gets the mapped arguments data if this is a mapped arguments object. #[inline] pub const fn as_mapped_arguments(&self) -> Option<&ParameterMap> { - match self.data { - ObjectData { - kind: ObjectKind::Arguments(Arguments::Mapped(ref args)), - .. - } => Some(args), + match self.kind { + ObjectKind::Arguments(Arguments::Mapped(ref args)) => Some(args), _ => None, } } @@ -1592,11 +1329,8 @@ impl Object { /// Gets the mutable mapped arguments data if this is a mapped arguments object. #[inline] pub fn as_mapped_arguments_mut(&mut self) -> Option<&mut ParameterMap> { - match self.data { - ObjectData { - kind: ObjectKind::Arguments(Arguments::Mapped(ref mut args)), - .. - } => Some(args), + match self.kind { + ObjectKind::Arguments(Arguments::Mapped(ref mut args)) => Some(args), _ => None, } } @@ -1604,11 +1338,8 @@ impl Object { /// Gets the typed array data (integer indexed object) if this is a typed array. #[inline] pub const fn as_typed_array(&self) -> Option<&IntegerIndexed> { - match self.data { - ObjectData { - kind: ObjectKind::IntegerIndexed(ref integer_indexed_object), - .. - } => Some(integer_indexed_object), + match self.kind { + ObjectKind::IntegerIndexed(ref integer_indexed_object) => Some(integer_indexed_object), _ => None, } } @@ -1616,11 +1347,10 @@ impl Object { /// Gets the typed array data (integer indexed object) if this is a typed array. #[inline] pub fn as_typed_array_mut(&mut self) -> Option<&mut IntegerIndexed> { - match self.data { - ObjectData { - kind: ObjectKind::IntegerIndexed(ref mut integer_indexed_object), - .. - } => Some(integer_indexed_object), + match self.kind { + ObjectKind::IntegerIndexed(ref mut integer_indexed_object) => { + Some(integer_indexed_object) + } _ => None, } } @@ -1628,35 +1358,20 @@ impl Object { /// Checks if it an ordinary object. #[inline] pub const fn is_ordinary(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::Ordinary, - .. - } - ) + matches!(self.kind, ObjectKind::Ordinary) } /// Checks if it's an proxy object. #[inline] pub const fn is_proxy(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::Proxy(_), - .. - } - ) + matches!(self.kind, ObjectKind::Proxy(_)) } /// Gets the proxy data if the object is a `Proxy`. #[inline] pub const fn as_proxy(&self) -> Option<&Proxy> { - match self.data { - ObjectData { - kind: ObjectKind::Proxy(ref proxy), - .. - } => Some(proxy), + match self.kind { + ObjectKind::Proxy(ref proxy) => Some(proxy), _ => None, } } @@ -1664,59 +1379,44 @@ impl Object { /// Gets the mutable proxy data if the object is a `Proxy`. #[inline] pub fn as_proxy_mut(&mut self) -> Option<&mut Proxy> { - match self.data { - ObjectData { - kind: ObjectKind::Proxy(ref mut proxy), - .. - } => Some(proxy), + match self.kind { + ObjectKind::Proxy(ref mut proxy) => Some(proxy), _ => None, } } /// Gets the weak map data if the object is a `WeakMap`. #[inline] - pub const fn as_weak_map(&self) -> Option<&boa_gc::WeakMap, JsValue>> { - match self.data { - ObjectData { - kind: ObjectKind::WeakMap(ref weak_map), - .. - } => Some(weak_map), + pub const fn as_weak_map(&self) -> Option<&boa_gc::WeakMap> { + match self.kind { + ObjectKind::WeakMap(ref weak_map) => Some(weak_map), _ => None, } } /// Gets the mutable weak map data if the object is a `WeakMap`. #[inline] - pub fn as_weak_map_mut(&mut self) -> Option<&mut boa_gc::WeakMap, JsValue>> { - match self.data { - ObjectData { - kind: ObjectKind::WeakMap(ref mut weak_map), - .. - } => Some(weak_map), + pub fn as_weak_map_mut(&mut self) -> Option<&mut boa_gc::WeakMap> { + match self.kind { + ObjectKind::WeakMap(ref mut weak_map) => Some(weak_map), _ => None, } } /// Gets the weak set data if the object is a `WeakSet`. #[inline] - pub const fn as_weak_set(&self) -> Option<&boa_gc::WeakMap, ()>> { - match self.data { - ObjectData { - kind: ObjectKind::WeakSet(ref weak_set), - .. - } => Some(weak_set), + pub const fn as_weak_set(&self) -> Option<&boa_gc::WeakMap> { + match self.kind { + ObjectKind::WeakSet(ref weak_set) => Some(weak_set), _ => None, } } /// Gets the mutable weak set data if the object is a `WeakSet`. #[inline] - pub fn as_weak_set_mut(&mut self) -> Option<&mut boa_gc::WeakMap, ()>> { - match self.data { - ObjectData { - kind: ObjectKind::WeakSet(ref mut weak_set), - .. - } => Some(weak_set), + pub fn as_weak_set_mut(&mut self) -> Option<&mut boa_gc::WeakMap> { + match self.kind { + ObjectKind::WeakSet(ref mut weak_set) => Some(weak_set), _ => None, } } @@ -1748,23 +1448,14 @@ impl Object { /// Returns `true` if it holds an Rust type that implements `NativeObject`. #[inline] pub const fn is_native_object(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::NativeObject(_), - .. - } - ) + matches!(self.kind, ObjectKind::NativeObject(_)) } /// Gets the native object data if the object is a `NativeObject`. #[inline] pub fn as_native_object(&self) -> Option<&dyn NativeObject> { - match self.data { - ObjectData { - kind: ObjectKind::NativeObject(ref object), - .. - } => Some(object.as_ref()), + match self.kind { + ObjectKind::NativeObject(ref object) => Some(object.as_ref()), _ => None, } } @@ -1772,23 +1463,14 @@ impl Object { /// Checks if it is a `Promise` object. #[inline] pub const fn is_promise(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::Promise(_), - .. - } - ) + matches!(self.kind, ObjectKind::Promise(_)) } /// Gets the promise data if the object is a `Promise`. #[inline] pub const fn as_promise(&self) -> Option<&Promise> { - match self.data { - ObjectData { - kind: ObjectKind::Promise(ref promise), - .. - } => Some(promise), + match self.kind { + ObjectKind::Promise(ref promise) => Some(promise), _ => None, } } @@ -1796,23 +1478,17 @@ impl Object { /// Gets the mutable promise data if the object is a `Promise`. #[inline] pub fn as_promise_mut(&mut self) -> Option<&mut Promise> { - match self.data { - ObjectData { - kind: ObjectKind::Promise(ref mut promise), - .. - } => Some(promise), + match self.kind { + ObjectKind::Promise(ref mut promise) => Some(promise), _ => None, } } /// Gets the `WeakRef` data if the object is a `WeakRef`. #[inline] - pub const fn as_weak_ref(&self) -> Option<&WeakGc>> { - match self.data { - ObjectData { - kind: ObjectKind::WeakRef(ref weak_ref), - .. - } => Some(weak_ref), + pub const fn as_weak_ref(&self) -> Option<&WeakGc> { + match self.kind { + ObjectKind::WeakRef(ref weak_ref) => Some(weak_ref), _ => None, } } @@ -1821,11 +1497,8 @@ impl Object { #[inline] #[cfg(feature = "intl")] pub const fn as_collator(&self) -> Option<&Collator> { - match self.data { - ObjectData { - kind: ObjectKind::Collator(ref collator), - .. - } => Some(collator), + match self.kind { + ObjectKind::Collator(ref collator) => Some(collator), _ => None, } } @@ -1834,11 +1507,8 @@ impl Object { #[inline] #[cfg(feature = "intl")] pub fn as_collator_mut(&mut self) -> Option<&mut Collator> { - match self.data { - ObjectData { - kind: ObjectKind::Collator(ref mut collator), - .. - } => Some(collator), + match self.kind { + ObjectKind::Collator(ref mut collator) => Some(collator), _ => None, } } @@ -1847,24 +1517,15 @@ impl Object { #[inline] #[cfg(feature = "intl")] pub const fn is_locale(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::Locale(_), - .. - } - ) + matches!(self.kind, ObjectKind::Locale(_)) } /// Gets the `Locale` data if the object is a `Locale`. #[inline] #[cfg(feature = "intl")] pub const fn as_locale(&self) -> Option<&icu_locid::Locale> { - match self.data { - ObjectData { - kind: ObjectKind::Locale(ref locale), - .. - } => Some(locale), + match self.kind { + ObjectKind::Locale(ref locale) => Some(locale), _ => None, } } @@ -1873,11 +1534,8 @@ impl Object { #[inline] #[cfg(feature = "intl")] pub const fn as_list_format(&self) -> Option<&ListFormat> { - match self.data { - ObjectData { - kind: ObjectKind::ListFormat(ref lf), - .. - } => Some(lf), + match self.kind { + ObjectKind::ListFormat(ref lf) => Some(lf), _ => None, } } @@ -1887,11 +1545,8 @@ impl Object { where T: NativeObject, { - match self.data { - ObjectData { - kind: ObjectKind::NativeObject(ref object), - .. - } => object.deref().as_any().is::(), + match self.kind { + ObjectKind::NativeObject(ref object) => object.deref().as_any().is::(), _ => false, } } @@ -1902,11 +1557,8 @@ impl Object { where T: NativeObject, { - match self.data { - ObjectData { - kind: ObjectKind::NativeObject(ref object), - .. - } => object.deref().as_any().downcast_ref::(), + match self.kind { + ObjectKind::NativeObject(ref object) => object.deref().as_any().downcast_ref::(), _ => None, } } @@ -1917,11 +1569,10 @@ impl Object { where T: NativeObject, { - match self.data { - ObjectData { - kind: ObjectKind::NativeObject(ref mut object), - .. - } => object.deref_mut().as_mut_any().downcast_mut::(), + match self.kind { + ObjectKind::NativeObject(ref mut object) => { + object.deref_mut().as_mut_any().downcast_mut::() + } _ => None, } } @@ -2258,13 +1909,13 @@ impl<'ctx, 'host> ObjectInitializer<'ctx, 'host> { pub struct ConstructorBuilder<'ctx, 'host> { context: &'ctx mut Context<'host>, function: NativeFunction, - object: JsObject, + constructor_object: Object, has_prototype_property: bool, - prototype: JsObject, + prototype: Object, name: JsString, length: usize, callable: bool, - constructor: Option, + kind: Option, inherit: Option, custom_prototype: Option, } @@ -2279,12 +1930,24 @@ impl<'ctx, 'host> ConstructorBuilder<'ctx, 'host> { Self { context, function, - object: JsObject::with_null_proto(), - prototype: JsObject::with_null_proto(), + constructor_object: Object { + kind: ObjectKind::Ordinary, + properties: PropertyMap::default(), + prototype: None, + extensible: true, + private_elements: ThinVec::new(), + }, + prototype: Object { + kind: ObjectKind::Ordinary, + properties: PropertyMap::default(), + prototype: None, + extensible: true, + private_elements: ThinVec::new(), + }, length: 0, name: js_string!(), callable: true, - constructor: Some(ConstructorKind::Base), + kind: Some(ConstructorKind::Base), inherit: None, custom_prototype: None, has_prototype_property: true, @@ -2303,7 +1966,7 @@ impl<'ctx, 'host> ConstructorBuilder<'ctx, 'host> { .constructor(false) .build(); - self.prototype.borrow_mut().insert( + self.prototype.insert( binding.binding, PropertyDescriptor::builder() .value(function) @@ -2331,7 +1994,7 @@ impl<'ctx, 'host> ConstructorBuilder<'ctx, 'host> { .constructor(false) .build(); - self.object.borrow_mut().insert( + self.constructor_object.insert( binding.binding, PropertyDescriptor::builder() .value(function) @@ -2353,7 +2016,7 @@ impl<'ctx, 'host> ConstructorBuilder<'ctx, 'host> { .writable(attribute.writable()) .enumerable(attribute.enumerable()) .configurable(attribute.configurable()); - self.prototype.borrow_mut().insert(key, property); + self.prototype.insert(key, property); self } @@ -2368,7 +2031,7 @@ impl<'ctx, 'host> ConstructorBuilder<'ctx, 'host> { .writable(attribute.writable()) .enumerable(attribute.enumerable()) .configurable(attribute.configurable()); - self.object.borrow_mut().insert(key, property); + self.constructor_object.insert(key, property); self } @@ -2388,7 +2051,7 @@ impl<'ctx, 'host> ConstructorBuilder<'ctx, 'host> { .maybe_set(set) .enumerable(attribute.enumerable()) .configurable(attribute.configurable()); - self.prototype.borrow_mut().insert(key, property); + self.prototype.insert(key, property); self } @@ -2408,7 +2071,7 @@ impl<'ctx, 'host> ConstructorBuilder<'ctx, 'host> { .maybe_set(set) .enumerable(attribute.enumerable()) .configurable(attribute.configurable()); - self.object.borrow_mut().insert(key, property); + self.constructor_object.insert(key, property); self } @@ -2419,7 +2082,7 @@ impl<'ctx, 'host> ConstructorBuilder<'ctx, 'host> { P: Into, { let property = property.into(); - self.prototype.borrow_mut().insert(key, property); + self.prototype.insert(key, property); self } @@ -2430,7 +2093,7 @@ impl<'ctx, 'host> ConstructorBuilder<'ctx, 'host> { P: Into, { let property = property.into(); - self.object.borrow_mut().insert(key, property); + self.constructor_object.insert(key, property); self } @@ -2468,7 +2131,7 @@ impl<'ctx, 'host> ConstructorBuilder<'ctx, 'host> { /// Default is `true` #[inline] pub fn constructor(&mut self, constructor: bool) -> &mut Self { - self.constructor = constructor.then_some(ConstructorKind::Base); + self.kind = constructor.then_some(ConstructorKind::Base); self } @@ -2510,7 +2173,7 @@ impl<'ctx, 'host> ConstructorBuilder<'ctx, 'host> { let function = Function::new( FunctionKind::Native { function: self.function, - constructor: self.constructor, + constructor: self.kind, }, self.context.realm().clone(), ); @@ -2526,11 +2189,29 @@ impl<'ctx, 'host> ConstructorBuilder<'ctx, 'host> { .enumerable(false) .configurable(true); - { - let mut constructor = self.object.borrow_mut(); - constructor.data = ObjectData::function(function); + let prototype = { + if let Some(proto) = self.inherit.take() { + self.prototype.set_prototype(proto); + } else { + self.prototype.set_prototype( + self.context + .intrinsics() + .constructors() + .object() + .prototype(), + ); + } + + JsObject::from_object_and_vtable(self.prototype, &ORDINARY_INTERNAL_METHODS) + }; + + let constructor = { + let mut constructor = self.constructor_object; constructor.insert(utf16!("length"), length); constructor.insert(utf16!("name"), name); + let data = ObjectData::function(function); + + constructor.kind = data.kind; if let Some(proto) = self.custom_prototype.take() { constructor.set_prototype(proto); @@ -2548,38 +2229,28 @@ impl<'ctx, 'host> ConstructorBuilder<'ctx, 'host> { constructor.insert( PROTOTYPE, PropertyDescriptor::builder() - .value(self.prototype.clone()) + .value(prototype.clone()) .writable(false) .enumerable(false) .configurable(false), ); } - } + + JsObject::from_object_and_vtable(constructor, data.internal_methods) + }; { - let mut prototype = self.prototype.borrow_mut(); + let mut prototype = prototype.borrow_mut(); prototype.insert( CONSTRUCTOR, PropertyDescriptor::builder() - .value(self.object.clone()) + .value(constructor.clone()) .writable(true) .enumerable(false) .configurable(true), ); - - if let Some(proto) = self.inherit.take() { - prototype.set_prototype(proto); - } else { - prototype.set_prototype( - self.context - .intrinsics() - .constructors() - .object() - .prototype(), - ); - } } - JsFunction::from_object_unchecked(self.object) + JsFunction::from_object_unchecked(constructor) } } diff --git a/boa_engine/src/vm/opcode/set/class_prototype.rs b/boa_engine/src/vm/opcode/set/class_prototype.rs index c8a4753404..b1ece9f5eb 100644 --- a/boa_engine/src/vm/opcode/set/class_prototype.rs +++ b/boa_engine/src/vm/opcode/set/class_prototype.rs @@ -21,14 +21,7 @@ impl Operation for SetClassPrototype { let prototype = match &prototype_value { JsValue::Object(proto) => Some(proto.clone()), JsValue::Null => None, - JsValue::Undefined => Some( - context - .intrinsics() - .constructors() - .object() - .prototype - .clone(), - ), + JsValue::Undefined => Some(context.intrinsics().constructors().object().prototype()), _ => unreachable!(), };