From 4a95fc8d77447d67212d1cd83a7aec2ce32ea1ab Mon Sep 17 00:00:00 2001 From: jasonwilliams Date: Fri, 30 Aug 2024 00:08:55 +0100 Subject: [PATCH 1/9] lazy builtins --- core/engine/src/context/intrinsics.rs | 15 +- core/engine/src/object/builtins/jsfunction.rs | 19 ++ .../src/object/builtins/lazy_builtin.rs | 260 ++++++++++++++++++ core/engine/src/object/builtins/mod.rs | 1 + .../engine/src/object/internal_methods/mod.rs | 4 +- 5 files changed, 295 insertions(+), 4 deletions(-) create mode 100644 core/engine/src/object/builtins/lazy_builtin.rs diff --git a/core/engine/src/context/intrinsics.rs b/core/engine/src/context/intrinsics.rs index a0d8519a7f7..1e72a28a60a 100644 --- a/core/engine/src/context/intrinsics.rs +++ b/core/engine/src/context/intrinsics.rs @@ -4,7 +4,9 @@ use boa_gc::{Finalize, Trace}; use boa_macros::js_str; use crate::{ - builtins::{iterable::IteratorPrototypes, uri::UriFunctions, Array, OrdinaryObject}, + builtins::{ + iterable::IteratorPrototypes, uri::UriFunctions, Array, IntrinsicObject, OrdinaryObject, + }, js_string, object::{ internal_methods::immutable_prototype::IMMUTABLE_PROTOTYPE_EXOTIC_INTERNAL_METHODS, @@ -12,6 +14,7 @@ use crate::{ JsFunction, JsObject, Object, CONSTRUCTOR, PROTOTYPE, }, property::{Attribute, PropertyKey}, + realm::Realm, JsSymbol, }; @@ -95,6 +98,14 @@ impl StandardConstructor { } } + /// Similar to `with_prototype`, but the prototype is lazily initialized. + fn with_lazy(init: fn(&Realm) -> ()) -> Self { + Self { + constructor: JsFunction::lazy_intrinsic_function(true, init), + prototype: JsObject::default(), + } + } + /// Build a constructor with a defined prototype. fn with_prototype(prototype: JsObject) -> Self { Self { @@ -219,7 +230,7 @@ impl Default for StandardConstructors { }, async_function: StandardConstructor::default(), generator_function: StandardConstructor::default(), - array: StandardConstructor::with_prototype(JsObject::from_proto_and_data(None, Array)), + array: StandardConstructor::with_lazy(Array::init), bigint: StandardConstructor::default(), number: StandardConstructor::with_prototype(JsObject::from_proto_and_data(None, 0.0)), boolean: StandardConstructor::with_prototype(JsObject::from_proto_and_data( diff --git a/core/engine/src/object/builtins/jsfunction.rs b/core/engine/src/object/builtins/jsfunction.rs index 16ff8fdd898..d83e8cfbfa8 100644 --- a/core/engine/src/object/builtins/jsfunction.rs +++ b/core/engine/src/object/builtins/jsfunction.rs @@ -1,12 +1,16 @@ //! A Rust API wrapper for Boa's `Function` Builtin ECMAScript Object +use crate::realm::Realm; use crate::{ builtins::function::ConstructorKind, native_function::NativeFunctionObject, object::JsObject, value::TryFromJs, Context, JsNativeError, JsResult, JsValue, NativeFunction, TryIntoJsResult, }; use boa_gc::{Finalize, Trace}; +use std::cell::Cell; use std::marker::PhantomData; use std::ops::Deref; +use super::lazy_builtin::{Builtin, BuiltinKind}; + /// A trait for converting a tuple of Rust values into a vector of `JsValue`, /// to be used as arguments for a JavaScript function. pub trait TryIntoJsArguments { @@ -135,6 +139,21 @@ impl JsFunction { } } + /// Creates a new, lazy intrinsic functionobject with only its function internal methods set. + /// When the function is accessed it will call init from the procided init function + pub(crate) fn lazy_intrinsic_function(constructor: bool, init: fn(&Realm)) -> Self { + Self { + inner: JsObject::from_proto_and_data( + None, + Builtin { + init, + is_initialized: Cell::new(false), + kind: BuiltinKind::Constructor(Self::empty_intrinsic_function(constructor)), + }, + ), + } + } + /// 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. diff --git a/core/engine/src/object/builtins/lazy_builtin.rs b/core/engine/src/object/builtins/lazy_builtin.rs new file mode 100644 index 00000000000..d6f6b0ff33b --- /dev/null +++ b/core/engine/src/object/builtins/lazy_builtin.rs @@ -0,0 +1,260 @@ +use super::JsFunction; +use crate::{ + gc::custom_trace, + object::{ + internal_methods::{ + non_existant_call, non_existant_construct, ordinary_define_own_property, + ordinary_delete, ordinary_get, ordinary_get_own_property, ordinary_get_prototype_of, + ordinary_has_property, ordinary_is_extensible, ordinary_own_property_keys, + ordinary_prevent_extensions, ordinary_set, ordinary_set_prototype_of, ordinary_try_get, + CallValue, InternalMethodContext, InternalObjectMethods, + }, + JsPrototype, + }, + property::{PropertyDescriptor, PropertyKey}, + realm::Realm, + Context, JsData, JsObject, JsResult, JsValue, +}; +use boa_gc::{Finalize, Trace}; +use std::cell::Cell; + +#[derive(Debug, Clone, Trace, Finalize)] +pub(crate) enum BuiltinKind { + Constructor(JsFunction), + Ordinary, +} + +/// A builtin function. Used for lazy initialization of builtins. + +#[derive(Debug, Clone, Finalize)] +pub(crate) struct Builtin { + pub init: fn(&Realm), + pub is_initialized: Cell, + pub kind: BuiltinKind, +} + +// SAFETY: Temporary, TODO move back to derived Trace when possible +unsafe impl Trace for Builtin { + custom_trace!(this, mark, { + mark(&this.kind); + }); +} + +// Implement the trait for JsData by overriding all internal_methods by calling init before calling into the underlying internel_method +impl JsData for Builtin { + fn internal_methods(&self) -> &'static InternalObjectMethods { + &LAZY_INTERNAL_METHODS + } +} + +pub(crate) static LAZY_INTERNAL_METHODS: InternalObjectMethods = InternalObjectMethods { + __get_prototype_of__: lazy_get_prototype_of, + __set_prototype_of__: lazy_set_prototype_of, + __is_extensible__: lazy_is_extensible, + __prevent_extensions__: lazy_prevent_extensions, + __get_own_property__: lazy_get_own_property, + __define_own_property__: lazy_define_own_property, + __has_property__: lazy_has_property, + __try_get__: lazy_try_get, + __get__: lazy_get, + __set__: lazy_set, + __delete__: lazy_delete, + __own_property_keys__: lazy_own_property_keys, + __call__: lazy_call, + __construct__: lazy_construct, +}; + +pub(crate) fn lazy_get_prototype_of( + obj: &JsObject, + context: &mut Context, +) -> JsResult { + let realm = context.realm(); + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if builtin.is_initialized.get() { + (builtin.init)(realm); + } + + ordinary_get_prototype_of(obj, context) +} + +pub(crate) fn lazy_set_prototype_of( + obj: &JsObject, + prototype: JsPrototype, + context: &mut Context, +) -> JsResult { + // Check if initialized, and set if not + let realm = context.realm(); + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if builtin.is_initialized.get() { + (builtin.init)(realm); + } + ordinary_set_prototype_of(obj, prototype, context) +} + +pub(crate) fn lazy_is_extensible(obj: &JsObject, context: &mut Context) -> JsResult { + let realm = context.realm(); + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if builtin.is_initialized.get() { + (builtin.init)(realm); + } + + ordinary_is_extensible(obj, context) +} + +pub(crate) fn lazy_prevent_extensions(obj: &JsObject, context: &mut Context) -> JsResult { + let realm = context.realm(); + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if builtin.is_initialized.get() { + (builtin.init)(realm); + } + + ordinary_prevent_extensions(obj, context) +} + +pub(crate) fn lazy_get_own_property( + obj: &JsObject, + key: &PropertyKey, + context: &mut InternalMethodContext<'_>, +) -> JsResult> { + let realm = context.realm(); + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if builtin.is_initialized.get() { + (builtin.init)(realm); + } + + ordinary_get_own_property(obj, key, context) +} + +pub(crate) fn lazy_define_own_property( + obj: &JsObject, + key: &PropertyKey, + desc: PropertyDescriptor, + context: &mut InternalMethodContext<'_>, +) -> JsResult { + let realm = context.realm(); + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if builtin.is_initialized.get() { + (builtin.init)(realm); + } + + ordinary_define_own_property(obj, key, desc, context) +} + +pub(crate) fn lazy_has_property( + obj: &JsObject, + key: &PropertyKey, + context: &mut InternalMethodContext<'_>, +) -> JsResult { + let realm = context.realm(); + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if builtin.is_initialized.get() { + (builtin.init)(realm); + } + + ordinary_has_property(obj, key, context) +} + +pub(crate) fn lazy_try_get( + obj: &JsObject, + key: &PropertyKey, + receiver: JsValue, + context: &mut InternalMethodContext<'_>, +) -> JsResult> { + let realm = context.realm(); + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if builtin.is_initialized.get() { + (builtin.init)(realm); + } + + ordinary_try_get(obj, key, receiver, context) +} + +pub(crate) fn lazy_get( + obj: &JsObject, + key: &PropertyKey, + receiver: JsValue, + context: &mut InternalMethodContext<'_>, +) -> JsResult { + let realm = context.realm(); + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if builtin.is_initialized.get() { + (builtin.init)(realm); + } + + ordinary_get(obj, key, receiver, context) +} + +pub(crate) fn lazy_set( + obj: &JsObject, + key: PropertyKey, + value: JsValue, + receiver: JsValue, + context: &mut InternalMethodContext<'_>, +) -> JsResult { + let realm = context.realm(); + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if builtin.is_initialized.get() { + (builtin.init)(realm); + } + + ordinary_set(obj, key, value, receiver, context) +} + +pub(crate) fn lazy_delete( + obj: &JsObject, + key: &PropertyKey, + context: &mut InternalMethodContext<'_>, +) -> JsResult { + let realm = context.realm(); + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if builtin.is_initialized.get() { + (builtin.init)(realm); + } + + ordinary_delete(obj, key, context) +} + +pub(crate) fn lazy_own_property_keys( + obj: &JsObject, + context: &mut Context, +) -> JsResult> { + let realm = context.realm(); + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if builtin.is_initialized.get() { + (builtin.init)(realm); + } + + ordinary_own_property_keys(obj, context) +} + +pub(crate) fn lazy_call( + obj: &JsObject, + argument_count: usize, + context: &mut Context, +) -> JsResult { + let realm = context.realm(); + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + (builtin.init)(realm); + + if let BuiltinKind::Constructor(constructor) = &builtin.kind { + return Ok(constructor.__call__(argument_count)); + } + + non_existant_call(obj, argument_count, context) +} + +pub(crate) fn lazy_construct( + obj: &JsObject, + argument_count: usize, + context: &mut Context, +) -> JsResult { + let realm = context.realm(); + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + (builtin.init)(realm); + + if let BuiltinKind::Constructor(constructor) = &builtin.kind { + return Ok(constructor.__construct__(argument_count)); + } + + non_existant_construct(obj, argument_count, context) +} diff --git a/core/engine/src/object/builtins/mod.rs b/core/engine/src/object/builtins/mod.rs index 923f5ad9fb1..82e7bd56472 100644 --- a/core/engine/src/object/builtins/mod.rs +++ b/core/engine/src/object/builtins/mod.rs @@ -17,6 +17,7 @@ mod jsset; mod jsset_iterator; mod jssharedarraybuffer; mod jstypedarray; +mod lazy_builtin; pub use jsarray::*; pub use jsarraybuffer::*; diff --git a/core/engine/src/object/internal_methods/mod.rs b/core/engine/src/object/internal_methods/mod.rs index ac671752f3c..3cdff643c19 100644 --- a/core/engine/src/object/internal_methods/mod.rs +++ b/core/engine/src/object/internal_methods/mod.rs @@ -1117,7 +1117,7 @@ where Ok(default(realm.intrinsics().constructors()).prototype()) } -fn non_existant_call( +pub(crate) fn non_existant_call( _obj: &JsObject, _argument_count: usize, context: &mut Context, @@ -1128,7 +1128,7 @@ fn non_existant_call( .into()) } -fn non_existant_construct( +pub(crate) fn non_existant_construct( _obj: &JsObject, _argument_count: usize, context: &mut Context, From 36b04c80dc9d6cecbfedddce3b542cd0c0feb111 Mon Sep 17 00:00:00 2001 From: jasonwilliams Date: Fri, 30 Aug 2024 00:37:32 +0100 Subject: [PATCH 2/9] updates --- core/engine/src/builtins/builder.rs | 10 +- core/engine/src/object/builtins/jsfunction.rs | 5 +- .../src/object/builtins/lazy_builtin.rs | 100 ++++++++++++------ core/engine/src/object/builtins/mod.rs | 1 + 4 files changed, 73 insertions(+), 43 deletions(-) diff --git a/core/engine/src/builtins/builder.rs b/core/engine/src/builtins/builder.rs index 459a5104d9a..ce3c5bbce43 100644 --- a/core/engine/src/builtins/builder.rs +++ b/core/engine/src/builtins/builder.rs @@ -5,7 +5,7 @@ use crate::{ native_function::{NativeFunctionObject, NativeFunctionPointer}, object::{ shape::{property_table::PropertyTableInner, slot::SlotAttributes}, - FunctionBinding, JsFunction, JsPrototype, CONSTRUCTOR, PROTOTYPE, + BuiltIn, FunctionBinding, JsFunction, JsPrototype, CONSTRUCTOR, PROTOTYPE, }, property::{Attribute, PropertyDescriptor, PropertyKey}, realm::Realm, @@ -391,12 +391,10 @@ impl BuiltInConstructorWithPrototype<'_> { } let mut object = self.object.borrow_mut(); - let function = object - .downcast_mut::() + let built_in = object + .downcast_mut::() .expect("Builtin must be a function object"); - function.f = NativeFunction::from_fn_ptr(self.function); - function.constructor = Some(ConstructorKind::Base); - function.realm = Some(self.realm.clone()); + built_in.realm = Some(self.realm.clone()); object .properties_mut() .shape diff --git a/core/engine/src/object/builtins/jsfunction.rs b/core/engine/src/object/builtins/jsfunction.rs index d83e8cfbfa8..de1624df42a 100644 --- a/core/engine/src/object/builtins/jsfunction.rs +++ b/core/engine/src/object/builtins/jsfunction.rs @@ -9,7 +9,7 @@ use std::cell::Cell; use std::marker::PhantomData; use std::ops::Deref; -use super::lazy_builtin::{Builtin, BuiltinKind}; +use super::lazy_builtin::{BuiltIn, BuiltinKind}; /// A trait for converting a tuple of Rust values into a vector of `JsValue`, /// to be used as arguments for a JavaScript function. @@ -145,10 +145,11 @@ impl JsFunction { Self { inner: JsObject::from_proto_and_data( None, - Builtin { + BuiltIn { init, is_initialized: Cell::new(false), kind: BuiltinKind::Constructor(Self::empty_intrinsic_function(constructor)), + realm: None, }, ), } diff --git a/core/engine/src/object/builtins/lazy_builtin.rs b/core/engine/src/object/builtins/lazy_builtin.rs index d6f6b0ff33b..593cd9416d7 100644 --- a/core/engine/src/object/builtins/lazy_builtin.rs +++ b/core/engine/src/object/builtins/lazy_builtin.rs @@ -27,21 +27,22 @@ pub(crate) enum BuiltinKind { /// A builtin function. Used for lazy initialization of builtins. #[derive(Debug, Clone, Finalize)] -pub(crate) struct Builtin { - pub init: fn(&Realm), - pub is_initialized: Cell, - pub kind: BuiltinKind, +pub struct BuiltIn { + pub(crate) init: fn(&Realm), + pub(crate) is_initialized: Cell, + pub(crate) kind: BuiltinKind, + pub(crate) realm: Option, } // SAFETY: Temporary, TODO move back to derived Trace when possible -unsafe impl Trace for Builtin { +unsafe impl Trace for BuiltIn { custom_trace!(this, mark, { mark(&this.kind); }); } // Implement the trait for JsData by overriding all internal_methods by calling init before calling into the underlying internel_method -impl JsData for Builtin { +impl JsData for BuiltIn { fn internal_methods(&self) -> &'static InternalObjectMethods { &LAZY_INTERNAL_METHODS } @@ -69,8 +70,10 @@ pub(crate) fn lazy_get_prototype_of( context: &mut Context, ) -> JsResult { let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if builtin.is_initialized.get() { + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if !builtin.is_initialized.get() { + builtin.is_initialized.set(true); + dbg!("initializing!"); (builtin.init)(realm); } @@ -84,8 +87,10 @@ pub(crate) fn lazy_set_prototype_of( ) -> JsResult { // Check if initialized, and set if not let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if builtin.is_initialized.get() { + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if !builtin.is_initialized.get() { + builtin.is_initialized.set(true); + dbg!("initializing!"); (builtin.init)(realm); } ordinary_set_prototype_of(obj, prototype, context) @@ -93,8 +98,10 @@ pub(crate) fn lazy_set_prototype_of( pub(crate) fn lazy_is_extensible(obj: &JsObject, context: &mut Context) -> JsResult { let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if builtin.is_initialized.get() { + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if !builtin.is_initialized.get() { + builtin.is_initialized.set(true); + dbg!("initializing!"); (builtin.init)(realm); } @@ -103,8 +110,10 @@ pub(crate) fn lazy_is_extensible(obj: &JsObject, context: &mut Context) -> JsRes pub(crate) fn lazy_prevent_extensions(obj: &JsObject, context: &mut Context) -> JsResult { let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if builtin.is_initialized.get() { + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if !builtin.is_initialized.get() { + builtin.is_initialized.set(true); + dbg!("initializing!"); (builtin.init)(realm); } @@ -117,8 +126,10 @@ pub(crate) fn lazy_get_own_property( context: &mut InternalMethodContext<'_>, ) -> JsResult> { let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if builtin.is_initialized.get() { + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if !builtin.is_initialized.get() { + builtin.is_initialized.set(true); + dbg!("initializing!"); (builtin.init)(realm); } @@ -132,8 +143,10 @@ pub(crate) fn lazy_define_own_property( context: &mut InternalMethodContext<'_>, ) -> JsResult { let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if builtin.is_initialized.get() { + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if !builtin.is_initialized.get() { + builtin.is_initialized.set(true); + dbg!("initializing!"); (builtin.init)(realm); } @@ -146,8 +159,10 @@ pub(crate) fn lazy_has_property( context: &mut InternalMethodContext<'_>, ) -> JsResult { let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if builtin.is_initialized.get() { + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if !builtin.is_initialized.get() { + builtin.is_initialized.set(true); + dbg!("initializing!"); (builtin.init)(realm); } @@ -161,8 +176,10 @@ pub(crate) fn lazy_try_get( context: &mut InternalMethodContext<'_>, ) -> JsResult> { let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if builtin.is_initialized.get() { + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if !builtin.is_initialized.get() { + builtin.is_initialized.set(true); + dbg!("initializing!"); (builtin.init)(realm); } @@ -176,8 +193,10 @@ pub(crate) fn lazy_get( context: &mut InternalMethodContext<'_>, ) -> JsResult { let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if builtin.is_initialized.get() { + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if !builtin.is_initialized.get() { + builtin.is_initialized.set(true); + dbg!("initializing!"); (builtin.init)(realm); } @@ -192,8 +211,10 @@ pub(crate) fn lazy_set( context: &mut InternalMethodContext<'_>, ) -> JsResult { let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if builtin.is_initialized.get() { + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if !builtin.is_initialized.get() { + builtin.is_initialized.set(true); + dbg!("initializing!"); (builtin.init)(realm); } @@ -206,8 +227,10 @@ pub(crate) fn lazy_delete( context: &mut InternalMethodContext<'_>, ) -> JsResult { let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if builtin.is_initialized.get() { + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if !builtin.is_initialized.get() { + builtin.is_initialized.set(true); + dbg!("initializing!"); (builtin.init)(realm); } @@ -219,8 +242,10 @@ pub(crate) fn lazy_own_property_keys( context: &mut Context, ) -> JsResult> { let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if builtin.is_initialized.get() { + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if !builtin.is_initialized.get() { + builtin.is_initialized.set(true); + dbg!("initializing!"); (builtin.init)(realm); } @@ -233,8 +258,11 @@ pub(crate) fn lazy_call( context: &mut Context, ) -> JsResult { let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - (builtin.init)(realm); + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if !builtin.is_initialized.get() { + builtin.is_initialized.set(true); + (builtin.init)(realm); + } if let BuiltinKind::Constructor(constructor) = &builtin.kind { return Ok(constructor.__call__(argument_count)); @@ -249,9 +277,11 @@ pub(crate) fn lazy_construct( context: &mut Context, ) -> JsResult { let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - (builtin.init)(realm); - + let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); + if !builtin.is_initialized.get() { + builtin.is_initialized.set(true); + (builtin.init)(realm); + } if let BuiltinKind::Constructor(constructor) = &builtin.kind { return Ok(constructor.__construct__(argument_count)); } diff --git a/core/engine/src/object/builtins/mod.rs b/core/engine/src/object/builtins/mod.rs index 82e7bd56472..3691ef279db 100644 --- a/core/engine/src/object/builtins/mod.rs +++ b/core/engine/src/object/builtins/mod.rs @@ -34,3 +34,4 @@ pub use jsset::*; pub use jsset_iterator::*; pub use jssharedarraybuffer::*; pub use jstypedarray::*; +pub use lazy_builtin::*; From 0de69191c29aac23c06e4d875f22aeaddcea609f Mon Sep 17 00:00:00 2001 From: jasonwilliams Date: Wed, 11 Sep 2024 23:31:06 +0100 Subject: [PATCH 3/9] some updates --- core/engine/src/builtins/mod.rs | 198 +++++++++--------- .../src/object/builtins/lazy_builtin.rs | 45 ++-- core/engine/src/realm.rs | 33 +-- 3 files changed, 136 insertions(+), 140 deletions(-) diff --git a/core/engine/src/builtins/mod.rs b/core/engine/src/builtins/mod.rs index b9a738f059e..9158c9e53ff 100644 --- a/core/engine/src/builtins/mod.rs +++ b/core/engine/src/builtins/mod.rs @@ -195,108 +195,108 @@ impl Realm { /// /// [spec]: https://tc39.es/ecma262/#sec-createintrinsics pub(crate) fn initialize(&self) { - BuiltInFunctionObject::init(self); - OrdinaryObject::init(self); - Iterator::init(self); - AsyncIterator::init(self); - AsyncFromSyncIterator::init(self); - ForInIterator::init(self); - Math::init(self); - Json::init(self); - Array::init(self); - ArrayIterator::init(self); - Proxy::init(self); - ArrayBuffer::init(self); - SharedArrayBuffer::init(self); - BigInt::init(self); - Boolean::init(self); - Date::init(self); - DataView::init(self); - Map::init(self); - MapIterator::init(self); - IsFinite::init(self); - IsNaN::init(self); - ParseInt::init(self); - ParseFloat::init(self); - Number::init(self); - Eval::init(self); - Set::init(self); - SetIterator::init(self); - String::init(self); - StringIterator::init(self); - RegExp::init(self); - RegExpStringIterator::init(self); - BuiltinTypedArray::init(self); - Int8Array::init(self); - Uint8Array::init(self); - Uint8ClampedArray::init(self); - Int16Array::init(self); - Uint16Array::init(self); - Int32Array::init(self); - Uint32Array::init(self); - BigInt64Array::init(self); - BigUint64Array::init(self); - Float32Array::init(self); - Float64Array::init(self); - Symbol::init(self); - Error::init(self); - RangeError::init(self); - ReferenceError::init(self); - TypeError::init(self); - ThrowTypeError::init(self); - SyntaxError::init(self); - EvalError::init(self); - UriError::init(self); - AggregateError::init(self); - Reflect::init(self); - Generator::init(self); - GeneratorFunction::init(self); - Promise::init(self); - AsyncFunction::init(self); - AsyncGenerator::init(self); - AsyncGeneratorFunction::init(self); - EncodeUri::init(self); - EncodeUriComponent::init(self); - DecodeUri::init(self); - DecodeUriComponent::init(self); - WeakRef::init(self); - WeakMap::init(self); - WeakSet::init(self); - Atomics::init(self); + // BuiltInFunctionObject::init(self); + // OrdinaryObject::init(self); + // Iterator::init(self); + // AsyncIterator::init(self); + // AsyncFromSyncIterator::init(self); + // ForInIterator::init(self); + // Math::init(self); + // Json::init(self); + // Array::init(self); + // ArrayIterator::init(self); + // Proxy::init(self); + // ArrayBuffer::init(self); + // SharedArrayBuffer::init(self); + // BigInt::init(self); + // Boolean::init(self); + // Date::init(self); + // DataView::init(self); + // Map::init(self); + // MapIterator::init(self); + // IsFinite::init(self); + // IsNaN::init(self); + // ParseInt::init(self); + // ParseFloat::init(self); + // Number::init(self); + // Eval::init(self); + // Set::init(self); + // SetIterator::init(self); + // String::init(self); + // StringIterator::init(self); + // RegExp::init(self); + // RegExpStringIterator::init(self); + // BuiltinTypedArray::init(self); + // Int8Array::init(self); + // Uint8Array::init(self); + // Uint8ClampedArray::init(self); + // Int16Array::init(self); + // Uint16Array::init(self); + // Int32Array::init(self); + // Uint32Array::init(self); + // BigInt64Array::init(self); + // BigUint64Array::init(self); + // Float32Array::init(self); + // Float64Array::init(self); + // Symbol::init(self); + // Error::init(self); + // RangeError::init(self); + // ReferenceError::init(self); + // TypeError::init(self); + // ThrowTypeError::init(self); + // SyntaxError::init(self); + // EvalError::init(self); + // UriError::init(self); + // AggregateError::init(self); + // Reflect::init(self); + // Generator::init(self); + // GeneratorFunction::init(self); + // Promise::init(self); + // AsyncFunction::init(self); + // AsyncGenerator::init(self); + // AsyncGeneratorFunction::init(self); + // EncodeUri::init(self); + // EncodeUriComponent::init(self); + // DecodeUri::init(self); + // DecodeUriComponent::init(self); + // WeakRef::init(self); + // WeakMap::init(self); + // WeakSet::init(self); + // Atomics::init(self); - #[cfg(feature = "annex-b")] - { - escape::Escape::init(self); - escape::Unescape::init(self); - } + // #[cfg(feature = "annex-b")] + // { + // escape::Escape::init(self); + // escape::Unescape::init(self); + // } - #[cfg(feature = "intl")] - { - intl::Intl::init(self); - intl::Collator::init(self); - intl::ListFormat::init(self); - intl::Locale::init(self); - intl::DateTimeFormat::init(self); - intl::Segmenter::init(self); - intl::segmenter::Segments::init(self); - intl::segmenter::SegmentIterator::init(self); - intl::PluralRules::init(self); - intl::NumberFormat::init(self); - } + // #[cfg(feature = "intl")] + // { + // intl::Intl::init(self); + // intl::Collator::init(self); + // intl::ListFormat::init(self); + // intl::Locale::init(self); + // intl::DateTimeFormat::init(self); + // intl::Segmenter::init(self); + // intl::segmenter::Segments::init(self); + // intl::segmenter::SegmentIterator::init(self); + // intl::PluralRules::init(self); + // intl::NumberFormat::init(self); + // } - #[cfg(feature = "temporal")] - { - temporal::Temporal::init(self); - temporal::Now::init(self); - temporal::Instant::init(self); - temporal::Duration::init(self); - temporal::PlainDate::init(self); - temporal::PlainTime::init(self); - temporal::PlainDateTime::init(self); - temporal::PlainMonthDay::init(self); - temporal::PlainYearMonth::init(self); - temporal::ZonedDateTime::init(self); - } + // #[cfg(feature = "temporal")] + // { + // temporal::Temporal::init(self); + // temporal::Now::init(self); + // temporal::Instant::init(self); + // temporal::Duration::init(self); + // temporal::PlainDate::init(self); + // temporal::PlainTime::init(self); + // temporal::PlainDateTime::init(self); + // temporal::PlainMonthDay::init(self); + // temporal::PlainYearMonth::init(self); + // temporal::ZonedDateTime::init(self); + // } } } diff --git a/core/engine/src/object/builtins/lazy_builtin.rs b/core/engine/src/object/builtins/lazy_builtin.rs index 593cd9416d7..63a2c58ff61 100644 --- a/core/engine/src/object/builtins/lazy_builtin.rs +++ b/core/engine/src/object/builtins/lazy_builtin.rs @@ -13,7 +13,7 @@ use crate::{ }, property::{PropertyDescriptor, PropertyKey}, realm::Realm, - Context, JsData, JsObject, JsResult, JsValue, + Context, JsData, JsNativeError, JsObject, JsResult, JsValue, }; use boa_gc::{Finalize, Trace}; use std::cell::Cell; @@ -44,6 +44,15 @@ unsafe impl Trace for BuiltIn { // Implement the trait for JsData by overriding all internal_methods by calling init before calling into the underlying internel_method impl JsData for BuiltIn { fn internal_methods(&self) -> &'static InternalObjectMethods { + static CONSTRUCTOR: InternalObjectMethods = InternalObjectMethods { + __construct__: lazy_construct, + ..LAZY_INTERNAL_METHODS + }; + + if let BuiltinKind::Constructor(_) = self.kind { + return &CONSTRUCTOR; + } + &LAZY_INTERNAL_METHODS } } @@ -61,8 +70,8 @@ pub(crate) static LAZY_INTERNAL_METHODS: InternalObjectMethods = InternalObjectM __set__: lazy_set, __delete__: lazy_delete, __own_property_keys__: lazy_own_property_keys, - __call__: lazy_call, - __construct__: lazy_construct, + __call__: non_existant_call, + __construct__: non_existant_construct, }; pub(crate) fn lazy_get_prototype_of( @@ -252,25 +261,6 @@ pub(crate) fn lazy_own_property_keys( ordinary_own_property_keys(obj, context) } -pub(crate) fn lazy_call( - obj: &JsObject, - argument_count: usize, - context: &mut Context, -) -> JsResult { - let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if !builtin.is_initialized.get() { - builtin.is_initialized.set(true); - (builtin.init)(realm); - } - - if let BuiltinKind::Constructor(constructor) = &builtin.kind { - return Ok(constructor.__call__(argument_count)); - } - - non_existant_call(obj, argument_count, context) -} - pub(crate) fn lazy_construct( obj: &JsObject, argument_count: usize, @@ -282,9 +272,12 @@ pub(crate) fn lazy_construct( builtin.is_initialized.set(true); (builtin.init)(realm); } - if let BuiltinKind::Constructor(constructor) = &builtin.kind { - return Ok(constructor.__construct__(argument_count)); - } - non_existant_construct(obj, argument_count, context) + match &builtin.kind { + BuiltinKind::Ordinary => Err(JsNativeError::typ() + .with_message("not a constructor") + .with_realm(context.realm().clone()) + .into()), + BuiltinKind::Constructor(constructor) => Ok(constructor.__construct__(argument_count)), + } } diff --git a/core/engine/src/realm.rs b/core/engine/src/realm.rs index b034420c2a9..f902d5d3cd8 100644 --- a/core/engine/src/realm.rs +++ b/core/engine/src/realm.rs @@ -80,19 +80,20 @@ impl Realm { pub fn create(hooks: &dyn HostHooks, root_shape: &RootShape) -> JsResult { let _timer = Profiler::global().start_event("Realm::create", "realm"); - let intrinsics = Intrinsics::uninit(root_shape).ok_or_else(|| { - JsNativeError::typ().with_message("failed to create the realm intrinsics") - })?; - - let global_object = hooks.create_global_object(&intrinsics); - let global_this = hooks - .create_global_this(&intrinsics) - .unwrap_or_else(|| global_object.clone()); - let environment = Gc::new(DeclarativeEnvironment::global()); - let scope = Scope::new_global(); - - let realm = Self { - inner: Gc::new(Inner { + // Use Gc::new_cyclic to create the Realm with a cyclic reference + let inner = Gc::new_cyclic(|weak_realm| { + // Initialize intrinsics with a reference to the weak_realm + let intrinsics = + Intrinsics::uninit(root_shape).expect("failed to create the realm intrinisics"); + + let global_object = hooks.create_global_object(&intrinsics); + let global_this = hooks + .create_global_this(&intrinsics) + .unwrap_or_else(|| global_object.clone()); + let environment = Gc::new(DeclarativeEnvironment::global()); + let scope = Scope::new_global(); + + Inner { intrinsics, environment, scope, @@ -102,8 +103,10 @@ impl Realm { loaded_modules: GcRefCell::default(), host_classes: GcRefCell::default(), host_defined: GcRefCell::default(), - }), - }; + } + }); + + let realm = Self { inner }; realm.initialize(); From 04e809d8c80544e26fde07d35ec02bb8f40ca98a Mon Sep 17 00:00:00 2001 From: jasonwilliams Date: Sun, 15 Sep 2024 15:24:40 +0100 Subject: [PATCH 4/9] Feeding realm through into the build function --- core/engine/src/builtins/builder.rs | 4 - core/engine/src/context/intrinsics.rs | 18 +- core/engine/src/object/builtins/jsfunction.rs | 12 +- .../src/object/builtins/lazy_builtin.rs | 299 +++++++++++++----- core/engine/src/realm.rs | 12 +- 5 files changed, 238 insertions(+), 107 deletions(-) diff --git a/core/engine/src/builtins/builder.rs b/core/engine/src/builtins/builder.rs index ce3c5bbce43..5e70f284a0f 100644 --- a/core/engine/src/builtins/builder.rs +++ b/core/engine/src/builtins/builder.rs @@ -391,10 +391,6 @@ impl BuiltInConstructorWithPrototype<'_> { } let mut object = self.object.borrow_mut(); - let built_in = object - .downcast_mut::() - .expect("Builtin must be a function object"); - built_in.realm = Some(self.realm.clone()); object .properties_mut() .shape diff --git a/core/engine/src/context/intrinsics.rs b/core/engine/src/context/intrinsics.rs index 1e72a28a60a..fb36c572ff3 100644 --- a/core/engine/src/context/intrinsics.rs +++ b/core/engine/src/context/intrinsics.rs @@ -1,6 +1,6 @@ //! Data structures that contain intrinsic objects and constructors. -use boa_gc::{Finalize, Trace}; +use boa_gc::{Finalize, Trace, WeakGc}; use boa_macros::js_str; use crate::{ @@ -14,7 +14,7 @@ use crate::{ JsFunction, JsObject, Object, CONSTRUCTOR, PROTOTYPE, }, property::{Attribute, PropertyKey}, - realm::Realm, + realm::{Realm, RealmInner}, JsSymbol, }; @@ -43,8 +43,8 @@ impl Intrinsics { /// To initialize all the intrinsics with their spec properties, see [`Realm::initialize`]. /// /// [`Realm::initialize`]: crate::realm::Realm::initialize - pub(crate) fn uninit(root_shape: &RootShape) -> Option { - let constructors = StandardConstructors::default(); + pub(crate) fn uninit(root_shape: &RootShape, realm_inner: WeakGc) -> Option { + let constructors = StandardConstructors::new(realm_inner); let templates = ObjectTemplates::new(root_shape, &constructors); Some(Self { @@ -99,9 +99,9 @@ impl StandardConstructor { } /// Similar to `with_prototype`, but the prototype is lazily initialized. - fn with_lazy(init: fn(&Realm) -> ()) -> Self { + fn with_lazy(init: fn(&Realm) -> (), realm_inner: WeakGc) -> Self { Self { - constructor: JsFunction::lazy_intrinsic_function(true, init), + constructor: JsFunction::lazy_intrinsic_function(true, init, realm_inner), prototype: JsObject::default(), } } @@ -214,8 +214,8 @@ pub struct StandardConstructors { calendar: StandardConstructor, } -impl Default for StandardConstructors { - fn default() -> Self { +impl StandardConstructors { + fn new(realm_inner: WeakGc) -> Self { Self { object: StandardConstructor::with_prototype(JsObject::from_object_and_vtable( Object::::default(), @@ -230,7 +230,7 @@ impl Default for StandardConstructors { }, async_function: StandardConstructor::default(), generator_function: StandardConstructor::default(), - array: StandardConstructor::with_lazy(Array::init), + array: StandardConstructor::with_lazy(Array::init, realm_inner), bigint: StandardConstructor::default(), number: StandardConstructor::with_prototype(JsObject::from_proto_and_data(None, 0.0)), boolean: StandardConstructor::with_prototype(JsObject::from_proto_and_data( diff --git a/core/engine/src/object/builtins/jsfunction.rs b/core/engine/src/object/builtins/jsfunction.rs index de1624df42a..b33304ea717 100644 --- a/core/engine/src/object/builtins/jsfunction.rs +++ b/core/engine/src/object/builtins/jsfunction.rs @@ -1,10 +1,10 @@ //! A Rust API wrapper for Boa's `Function` Builtin ECMAScript Object -use crate::realm::Realm; +use crate::realm::{Realm, RealmInner}; use crate::{ builtins::function::ConstructorKind, native_function::NativeFunctionObject, object::JsObject, value::TryFromJs, Context, JsNativeError, JsResult, JsValue, NativeFunction, TryIntoJsResult, }; -use boa_gc::{Finalize, Trace}; +use boa_gc::{Finalize, Trace, WeakGc}; use std::cell::Cell; use std::marker::PhantomData; use std::ops::Deref; @@ -141,7 +141,11 @@ impl JsFunction { /// Creates a new, lazy intrinsic functionobject with only its function internal methods set. /// When the function is accessed it will call init from the procided init function - pub(crate) fn lazy_intrinsic_function(constructor: bool, init: fn(&Realm)) -> Self { + pub(crate) fn lazy_intrinsic_function( + constructor: bool, + init: fn(&Realm), + realm_inner: WeakGc, + ) -> Self { Self { inner: JsObject::from_proto_and_data( None, @@ -149,7 +153,7 @@ impl JsFunction { init, is_initialized: Cell::new(false), kind: BuiltinKind::Constructor(Self::empty_intrinsic_function(constructor)), - realm: None, + realm_inner: Some(realm_inner), }, ), } diff --git a/core/engine/src/object/builtins/lazy_builtin.rs b/core/engine/src/object/builtins/lazy_builtin.rs index 63a2c58ff61..809be0b51da 100644 --- a/core/engine/src/object/builtins/lazy_builtin.rs +++ b/core/engine/src/object/builtins/lazy_builtin.rs @@ -12,10 +12,10 @@ use crate::{ JsPrototype, }, property::{PropertyDescriptor, PropertyKey}, - realm::Realm, + realm::{Realm, RealmInner}, Context, JsData, JsNativeError, JsObject, JsResult, JsValue, }; -use boa_gc::{Finalize, Trace}; +use boa_gc::{Finalize, Trace, WeakGc}; use std::cell::Cell; #[derive(Debug, Clone, Trace, Finalize)] @@ -26,12 +26,12 @@ pub(crate) enum BuiltinKind { /// A builtin function. Used for lazy initialization of builtins. -#[derive(Debug, Clone, Finalize)] +#[derive(Clone, Finalize)] pub struct BuiltIn { pub(crate) init: fn(&Realm), pub(crate) is_initialized: Cell, pub(crate) kind: BuiltinKind, - pub(crate) realm: Option, + pub(crate) realm_inner: Option>, } // SAFETY: Temporary, TODO move back to derived Trace when possible @@ -78,12 +78,22 @@ pub(crate) fn lazy_get_prototype_of( obj: &JsObject, context: &mut Context, ) -> JsResult { - let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if !builtin.is_initialized.get() { - builtin.is_initialized.set(true); - dbg!("initializing!"); - (builtin.init)(realm); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + if !builtin.borrow().data.is_initialized.get() { + let builtin_borrow = builtin.borrow_mut(); + let realm = &Realm { + inner: builtin_borrow + .data + .realm_inner + .as_ref() + .expect("realm_inner not set") + .upgrade() + .expect("realm_inner not set"), + }; + builtin_borrow.data.is_initialized.set(true); + let init_fn = builtin_borrow.data.init; + drop(builtin_borrow); + init_fn(realm); } ordinary_get_prototype_of(obj, context) @@ -94,36 +104,65 @@ pub(crate) fn lazy_set_prototype_of( prototype: JsPrototype, context: &mut Context, ) -> JsResult { - // Check if initialized, and set if not - let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if !builtin.is_initialized.get() { - builtin.is_initialized.set(true); - dbg!("initializing!"); - (builtin.init)(realm); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + if !builtin.borrow().data.is_initialized.get() { + let builtin_borrow = builtin.borrow_mut(); + let realm = &Realm { + inner: builtin_borrow + .data + .realm_inner + .as_ref() + .expect("realm_inner not set") + .upgrade() + .expect("realm_inner not set"), + }; + builtin_borrow.data.is_initialized.set(true); + let init_fn = builtin_borrow.data.init; + drop(builtin_borrow); + init_fn(realm); } + ordinary_set_prototype_of(obj, prototype, context) } - pub(crate) fn lazy_is_extensible(obj: &JsObject, context: &mut Context) -> JsResult { - let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if !builtin.is_initialized.get() { - builtin.is_initialized.set(true); - dbg!("initializing!"); - (builtin.init)(realm); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + if !builtin.borrow().data.is_initialized.get() { + let builtin_borrow = builtin.borrow_mut(); + let realm = &Realm { + inner: builtin_borrow + .data + .realm_inner + .as_ref() + .expect("realm_inner not set") + .upgrade() + .expect("realm_inner not set"), + }; + builtin_borrow.data.is_initialized.set(true); + let init_fn = builtin_borrow.data.init; + drop(builtin_borrow); + init_fn(realm); } ordinary_is_extensible(obj, context) } pub(crate) fn lazy_prevent_extensions(obj: &JsObject, context: &mut Context) -> JsResult { - let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if !builtin.is_initialized.get() { - builtin.is_initialized.set(true); - dbg!("initializing!"); - (builtin.init)(realm); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + if !builtin.borrow().data.is_initialized.get() { + let builtin_borrow = builtin.borrow_mut(); + let realm = &Realm { + inner: builtin_borrow + .data + .realm_inner + .as_ref() + .expect("realm_inner not set") + .upgrade() + .expect("realm_inner not set"), + }; + builtin_borrow.data.is_initialized.set(true); + let init_fn = builtin_borrow.data.init; + drop(builtin_borrow); + init_fn(realm); } ordinary_prevent_extensions(obj, context) @@ -134,12 +173,22 @@ pub(crate) fn lazy_get_own_property( key: &PropertyKey, context: &mut InternalMethodContext<'_>, ) -> JsResult> { - let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if !builtin.is_initialized.get() { - builtin.is_initialized.set(true); - dbg!("initializing!"); - (builtin.init)(realm); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + if !builtin.borrow().data.is_initialized.get() { + let builtin_borrow = builtin.borrow_mut(); + let realm = &Realm { + inner: builtin_borrow + .data + .realm_inner + .as_ref() + .expect("realm_inner not set") + .upgrade() + .expect("realm_inner not set"), + }; + builtin_borrow.data.is_initialized.set(true); + let init_fn = builtin_borrow.data.init; + drop(builtin_borrow); + init_fn(realm); } ordinary_get_own_property(obj, key, context) @@ -151,12 +200,22 @@ pub(crate) fn lazy_define_own_property( desc: PropertyDescriptor, context: &mut InternalMethodContext<'_>, ) -> JsResult { - let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if !builtin.is_initialized.get() { - builtin.is_initialized.set(true); - dbg!("initializing!"); - (builtin.init)(realm); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + if !builtin.borrow().data.is_initialized.get() { + let builtin_borrow = builtin.borrow_mut(); + let realm = &Realm { + inner: builtin_borrow + .data + .realm_inner + .as_ref() + .expect("realm_inner not set") + .upgrade() + .expect("realm_inner not set"), + }; + builtin_borrow.data.is_initialized.set(true); + let init_fn = builtin_borrow.data.init; + drop(builtin_borrow); + init_fn(realm); } ordinary_define_own_property(obj, key, desc, context) @@ -167,12 +226,22 @@ pub(crate) fn lazy_has_property( key: &PropertyKey, context: &mut InternalMethodContext<'_>, ) -> JsResult { - let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if !builtin.is_initialized.get() { - builtin.is_initialized.set(true); - dbg!("initializing!"); - (builtin.init)(realm); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + if !builtin.borrow().data.is_initialized.get() { + let builtin_borrow = builtin.borrow_mut(); + let realm = &Realm { + inner: builtin_borrow + .data + .realm_inner + .as_ref() + .expect("realm_inner not set") + .upgrade() + .expect("realm_inner not set"), + }; + builtin_borrow.data.is_initialized.set(true); + let init_fn = builtin_borrow.data.init; + drop(builtin_borrow); + init_fn(realm); } ordinary_has_property(obj, key, context) @@ -184,12 +253,22 @@ pub(crate) fn lazy_try_get( receiver: JsValue, context: &mut InternalMethodContext<'_>, ) -> JsResult> { - let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if !builtin.is_initialized.get() { - builtin.is_initialized.set(true); - dbg!("initializing!"); - (builtin.init)(realm); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + if !builtin.borrow().data.is_initialized.get() { + let builtin_borrow = builtin.borrow_mut(); + let realm = &Realm { + inner: builtin_borrow + .data + .realm_inner + .as_ref() + .expect("realm_inner not set") + .upgrade() + .expect("realm_inner not set"), + }; + builtin_borrow.data.is_initialized.set(true); + let init_fn = builtin_borrow.data.init; + drop(builtin_borrow); + init_fn(realm); } ordinary_try_get(obj, key, receiver, context) @@ -201,12 +280,22 @@ pub(crate) fn lazy_get( receiver: JsValue, context: &mut InternalMethodContext<'_>, ) -> JsResult { - let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if !builtin.is_initialized.get() { - builtin.is_initialized.set(true); - dbg!("initializing!"); - (builtin.init)(realm); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + if !builtin.borrow().data.is_initialized.get() { + let builtin_borrow = builtin.borrow_mut(); + let realm = &Realm { + inner: builtin_borrow + .data + .realm_inner + .as_ref() + .expect("realm_inner not set") + .upgrade() + .expect("realm_inner not set"), + }; + builtin_borrow.data.is_initialized.set(true); + let init_fn = builtin_borrow.data.init; + drop(builtin_borrow); + init_fn(realm); } ordinary_get(obj, key, receiver, context) @@ -219,12 +308,22 @@ pub(crate) fn lazy_set( receiver: JsValue, context: &mut InternalMethodContext<'_>, ) -> JsResult { - let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if !builtin.is_initialized.get() { - builtin.is_initialized.set(true); - dbg!("initializing!"); - (builtin.init)(realm); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + if !builtin.borrow().data.is_initialized.get() { + let builtin_borrow = builtin.borrow_mut(); + builtin_borrow.data.is_initialized.set(true); + let realm = &Realm { + inner: builtin_borrow + .data + .realm_inner + .as_ref() + .expect("realm_inner not set") + .upgrade() + .expect("realm_inner not set"), + }; + let init_fn = builtin_borrow.data.init; + drop(builtin_borrow); + init_fn(realm); } ordinary_set(obj, key, value, receiver, context) @@ -235,12 +334,22 @@ pub(crate) fn lazy_delete( key: &PropertyKey, context: &mut InternalMethodContext<'_>, ) -> JsResult { - let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if !builtin.is_initialized.get() { - builtin.is_initialized.set(true); - dbg!("initializing!"); - (builtin.init)(realm); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + if !builtin.borrow().data.is_initialized.get() { + let builtin_borrow = builtin.borrow_mut(); + let realm = &Realm { + inner: builtin_borrow + .data + .realm_inner + .as_ref() + .expect("realm_inner not set") + .upgrade() + .expect("realm_inner not set"), + }; + builtin_borrow.data.is_initialized.set(true); + let init_fn = builtin_borrow.data.init; + drop(builtin_borrow); + init_fn(realm); } ordinary_delete(obj, key, context) @@ -250,12 +359,22 @@ pub(crate) fn lazy_own_property_keys( obj: &JsObject, context: &mut Context, ) -> JsResult> { - let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if !builtin.is_initialized.get() { - builtin.is_initialized.set(true); - dbg!("initializing!"); - (builtin.init)(realm); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + if !builtin.borrow().data.is_initialized.get() { + let builtin_borrow = builtin.borrow_mut(); + let realm = &Realm { + inner: builtin_borrow + .data + .realm_inner + .as_ref() + .expect("realm_inner not set") + .upgrade() + .expect("realm_inner not set"), + }; + builtin_borrow.data.is_initialized.set(true); + let init_fn = builtin_borrow.data.init; + drop(builtin_borrow); + init_fn(realm); } ordinary_own_property_keys(obj, context) @@ -266,14 +385,26 @@ pub(crate) fn lazy_construct( argument_count: usize, context: &mut Context, ) -> JsResult { - let realm = context.realm(); - let builtin = obj.downcast_ref::().expect("obj is not a Builtin"); - if !builtin.is_initialized.get() { - builtin.is_initialized.set(true); - (builtin.init)(realm); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + let kind = &builtin.borrow().data.kind; + if !builtin.borrow().data.is_initialized.get() { + let builtin_borrow = builtin.borrow_mut(); + let realm = &Realm { + inner: builtin_borrow + .data + .realm_inner + .as_ref() + .expect("realm_inner not set") + .upgrade() + .expect("realm_inner not set"), + }; + builtin_borrow.data.is_initialized.set(true); + let init_fn = builtin_borrow.data.init; + drop(builtin_borrow); + init_fn(realm); } - match &builtin.kind { + match kind { BuiltinKind::Ordinary => Err(JsNativeError::typ() .with_message("not a constructor") .with_realm(context.realm().clone()) diff --git a/core/engine/src/realm.rs b/core/engine/src/realm.rs index f902d5d3cd8..5604d437778 100644 --- a/core/engine/src/realm.rs +++ b/core/engine/src/realm.rs @@ -20,7 +20,7 @@ use crate::{ environments::DeclarativeEnvironment, module::Module, object::shape::RootShape, - HostDefined, JsNativeError, JsObject, JsResult, JsString, + HostDefined, JsObject, JsResult, JsString, }; use boa_gc::{Finalize, Gc, GcRef, GcRefCell, GcRefMut, Trace}; use boa_profiler::Profiler; @@ -30,7 +30,7 @@ use boa_profiler::Profiler; /// In the specification these are called Realm Records. #[derive(Clone, Trace, Finalize)] pub struct Realm { - inner: Gc, + pub inner: Gc, } impl Eq for Realm {} @@ -53,7 +53,7 @@ impl std::fmt::Debug for Realm { } #[derive(Trace, Finalize)] -struct Inner { +pub struct RealmInner { intrinsics: Intrinsics, /// The global declarative environment of this realm. @@ -83,8 +83,8 @@ impl Realm { // Use Gc::new_cyclic to create the Realm with a cyclic reference let inner = Gc::new_cyclic(|weak_realm| { // Initialize intrinsics with a reference to the weak_realm - let intrinsics = - Intrinsics::uninit(root_shape).expect("failed to create the realm intrinisics"); + let intrinsics = Intrinsics::uninit(root_shape, weak_realm.to_owned()) + .expect("failed to create the realm intrinsics"); let global_object = hooks.create_global_object(&intrinsics); let global_this = hooks @@ -93,7 +93,7 @@ impl Realm { let environment = Gc::new(DeclarativeEnvironment::global()); let scope = Scope::new_global(); - Inner { + RealmInner { intrinsics, environment, scope, From 5bb0754a272cf3be0c79cda4cec6a21ed0270e3e Mon Sep 17 00:00:00 2001 From: jasonwilliams Date: Sun, 15 Sep 2024 17:22:00 +0100 Subject: [PATCH 5/9] Change constructor to function for builtIn Handle toString for lazyBuiltIn objects --- core/engine/src/builtins/builder.rs | 2 +- core/engine/src/builtins/function/mod.rs | 9 +- core/engine/src/builtins/mod.rs | 195 +++++++++--------- core/engine/src/context/intrinsics.rs | 16 +- core/engine/src/object/builtins/jsfunction.rs | 15 +- .../src/object/builtins/lazy_builtin.rs | 79 +++++-- core/engine/src/realm.rs | 2 +- 7 files changed, 186 insertions(+), 132 deletions(-) diff --git a/core/engine/src/builtins/builder.rs b/core/engine/src/builtins/builder.rs index 5e70f284a0f..af29d2eebb0 100644 --- a/core/engine/src/builtins/builder.rs +++ b/core/engine/src/builtins/builder.rs @@ -5,7 +5,7 @@ use crate::{ native_function::{NativeFunctionObject, NativeFunctionPointer}, object::{ shape::{property_table::PropertyTableInner, slot::SlotAttributes}, - BuiltIn, FunctionBinding, JsFunction, JsPrototype, CONSTRUCTOR, PROTOTYPE, + FunctionBinding, JsFunction, JsPrototype, CONSTRUCTOR, PROTOTYPE, }, property::{Attribute, PropertyDescriptor, PropertyKey}, realm::Realm, diff --git a/core/engine/src/builtins/function/mod.rs b/core/engine/src/builtins/function/mod.rs index 8c4967e5644..2ade79de338 100644 --- a/core/engine/src/builtins/function/mod.rs +++ b/core/engine/src/builtins/function/mod.rs @@ -26,7 +26,7 @@ use crate::{ get_prototype_from_constructor, CallValue, InternalObjectMethods, ORDINARY_INTERNAL_METHODS, }, - JsData, JsFunction, JsObject, PrivateElement, PrivateName, + JsData, JsFunction, JsObject, LazyBuiltIn, PrivateElement, PrivateName, }, property::{Attribute, PropertyDescriptor, PropertyKey}, realm::Realm, @@ -859,6 +859,13 @@ impl BuiltInFunctionObject { ); } else if object_borrow.is::() || object_borrow.is::() { return Ok(js_string!("function () { [native code] }").into()); + } else if object_borrow.is::() { + let name = object_borrow + .downcast_ref::() + .map_or_else(|| js_string!(), |built_in| built_in.name.clone()); + return Ok( + js_string!(js_str!("function "), &name, js_str!("() { [native code] }")).into(), + ); } let function = object_borrow diff --git a/core/engine/src/builtins/mod.rs b/core/engine/src/builtins/mod.rs index 9158c9e53ff..fdb5daeaa8f 100644 --- a/core/engine/src/builtins/mod.rs +++ b/core/engine/src/builtins/mod.rs @@ -195,108 +195,107 @@ impl Realm { /// /// [spec]: https://tc39.es/ecma262/#sec-createintrinsics pub(crate) fn initialize(&self) { - // BuiltInFunctionObject::init(self); - // OrdinaryObject::init(self); - // Iterator::init(self); - // AsyncIterator::init(self); - // AsyncFromSyncIterator::init(self); - // ForInIterator::init(self); - // Math::init(self); - // Json::init(self); - // Array::init(self); - // ArrayIterator::init(self); - // Proxy::init(self); - // ArrayBuffer::init(self); - // SharedArrayBuffer::init(self); - // BigInt::init(self); - // Boolean::init(self); + BuiltInFunctionObject::init(self); + OrdinaryObject::init(self); + Iterator::init(self); + AsyncIterator::init(self); + AsyncFromSyncIterator::init(self); + ForInIterator::init(self); + Math::init(self); + Json::init(self); + ArrayIterator::init(self); + Proxy::init(self); + ArrayBuffer::init(self); + SharedArrayBuffer::init(self); + BigInt::init(self); + Boolean::init(self); // Date::init(self); - // DataView::init(self); - // Map::init(self); - // MapIterator::init(self); - // IsFinite::init(self); - // IsNaN::init(self); - // ParseInt::init(self); - // ParseFloat::init(self); - // Number::init(self); - // Eval::init(self); - // Set::init(self); - // SetIterator::init(self); - // String::init(self); - // StringIterator::init(self); - // RegExp::init(self); - // RegExpStringIterator::init(self); - // BuiltinTypedArray::init(self); - // Int8Array::init(self); - // Uint8Array::init(self); - // Uint8ClampedArray::init(self); - // Int16Array::init(self); - // Uint16Array::init(self); - // Int32Array::init(self); - // Uint32Array::init(self); - // BigInt64Array::init(self); - // BigUint64Array::init(self); - // Float32Array::init(self); - // Float64Array::init(self); - // Symbol::init(self); - // Error::init(self); - // RangeError::init(self); - // ReferenceError::init(self); - // TypeError::init(self); - // ThrowTypeError::init(self); - // SyntaxError::init(self); - // EvalError::init(self); - // UriError::init(self); - // AggregateError::init(self); - // Reflect::init(self); - // Generator::init(self); - // GeneratorFunction::init(self); - // Promise::init(self); - // AsyncFunction::init(self); - // AsyncGenerator::init(self); - // AsyncGeneratorFunction::init(self); - // EncodeUri::init(self); - // EncodeUriComponent::init(self); - // DecodeUri::init(self); - // DecodeUriComponent::init(self); - // WeakRef::init(self); - // WeakMap::init(self); - // WeakSet::init(self); - // Atomics::init(self); + DataView::init(self); + Map::init(self); + MapIterator::init(self); + IsFinite::init(self); + IsNaN::init(self); + ParseInt::init(self); + ParseFloat::init(self); + Number::init(self); + Eval::init(self); + Set::init(self); + SetIterator::init(self); + String::init(self); + StringIterator::init(self); + RegExp::init(self); + RegExpStringIterator::init(self); + BuiltinTypedArray::init(self); + Int8Array::init(self); + Uint8Array::init(self); + Uint8ClampedArray::init(self); + Int16Array::init(self); + Uint16Array::init(self); + Int32Array::init(self); + Uint32Array::init(self); + BigInt64Array::init(self); + BigUint64Array::init(self); + Float32Array::init(self); + Float64Array::init(self); + Symbol::init(self); + Error::init(self); + RangeError::init(self); + ReferenceError::init(self); + TypeError::init(self); + ThrowTypeError::init(self); + SyntaxError::init(self); + EvalError::init(self); + UriError::init(self); + AggregateError::init(self); + Reflect::init(self); + Generator::init(self); + GeneratorFunction::init(self); + Promise::init(self); + AsyncFunction::init(self); + AsyncGenerator::init(self); + AsyncGeneratorFunction::init(self); + EncodeUri::init(self); + EncodeUriComponent::init(self); + DecodeUri::init(self); + DecodeUriComponent::init(self); + WeakRef::init(self); + WeakMap::init(self); + WeakSet::init(self); + Atomics::init(self); - // #[cfg(feature = "annex-b")] - // { - // escape::Escape::init(self); - // escape::Unescape::init(self); - // } + #[cfg(feature = "annex-b")] + { + escape::Escape::init(self); + escape::Unescape::init(self); + } - // #[cfg(feature = "intl")] - // { - // intl::Intl::init(self); - // intl::Collator::init(self); - // intl::ListFormat::init(self); - // intl::Locale::init(self); - // intl::DateTimeFormat::init(self); - // intl::Segmenter::init(self); - // intl::segmenter::Segments::init(self); - // intl::segmenter::SegmentIterator::init(self); - // intl::PluralRules::init(self); - // intl::NumberFormat::init(self); - // } + #[cfg(feature = "intl")] + { + intl::Intl::init(self); + intl::Collator::init(self); + intl::ListFormat::init(self); + intl::Locale::init(self); + intl::DateTimeFormat::init(self); + intl::Segmenter::init(self); + intl::segmenter::Segments::init(self); + intl::segmenter::SegmentIterator::init(self); + intl::PluralRules::init(self); + intl::NumberFormat::init(self); + } - // #[cfg(feature = "temporal")] - // { - // temporal::Temporal::init(self); - // temporal::Now::init(self); - // temporal::Instant::init(self); - // temporal::Duration::init(self); - // temporal::PlainDate::init(self); - // temporal::PlainTime::init(self); - // temporal::PlainDateTime::init(self); - // temporal::PlainMonthDay::init(self); - // temporal::PlainYearMonth::init(self); - // temporal::ZonedDateTime::init(self); - // } + #[cfg(feature = "temporal")] + { + temporal::Temporal::init(self); + temporal::Now::init(self); + temporal::Instant::init(self); + temporal::Duration::init(self); + temporal::PlainDate::init(self); + temporal::PlainTime::init(self); + temporal::PlainDateTime::init(self); + temporal::PlainMonthDay::init(self); + temporal::PlainYearMonth::init(self); + temporal::ZonedDateTime::init(self); + } } } diff --git a/core/engine/src/context/intrinsics.rs b/core/engine/src/context/intrinsics.rs index fb36c572ff3..d2292a8ba25 100644 --- a/core/engine/src/context/intrinsics.rs +++ b/core/engine/src/context/intrinsics.rs @@ -2,10 +2,12 @@ use boa_gc::{Finalize, Trace, WeakGc}; use boa_macros::js_str; +use boa_string::JsString; use crate::{ builtins::{ - iterable::IteratorPrototypes, uri::UriFunctions, Array, IntrinsicObject, OrdinaryObject, + iterable::IteratorPrototypes, uri::UriFunctions, Array, BuiltInObject, Date, + IntrinsicObject, OrdinaryObject, }, js_string, object::{ @@ -43,7 +45,7 @@ impl Intrinsics { /// To initialize all the intrinsics with their spec properties, see [`Realm::initialize`]. /// /// [`Realm::initialize`]: crate::realm::Realm::initialize - pub(crate) fn uninit(root_shape: &RootShape, realm_inner: WeakGc) -> Option { + pub(crate) fn uninit(root_shape: &RootShape, realm_inner: &WeakGc) -> Option { let constructors = StandardConstructors::new(realm_inner); let templates = ObjectTemplates::new(root_shape, &constructors); @@ -99,9 +101,9 @@ impl StandardConstructor { } /// Similar to `with_prototype`, but the prototype is lazily initialized. - fn with_lazy(init: fn(&Realm) -> (), realm_inner: WeakGc) -> Self { + fn lazy(init: fn(&Realm) -> (), name: JsString, realm_inner: WeakGc) -> Self { Self { - constructor: JsFunction::lazy_intrinsic_function(true, init, realm_inner), + constructor: JsFunction::lazy_intrinsic_function(true, init, name, realm_inner), prototype: JsObject::default(), } } @@ -215,7 +217,7 @@ pub struct StandardConstructors { } impl StandardConstructors { - fn new(realm_inner: WeakGc) -> Self { + fn new(realm_inner: &WeakGc) -> Self { Self { object: StandardConstructor::with_prototype(JsObject::from_object_and_vtable( Object::::default(), @@ -223,14 +225,14 @@ impl StandardConstructors { )), async_generator_function: StandardConstructor::default(), proxy: StandardConstructor::default(), - date: StandardConstructor::default(), + date: StandardConstructor::lazy(Date::init, Date::NAME, realm_inner.clone()), 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_lazy(Array::init, realm_inner), + array: StandardConstructor::lazy(Array::init, Array::NAME, realm_inner.clone()), bigint: StandardConstructor::default(), number: StandardConstructor::with_prototype(JsObject::from_proto_and_data(None, 0.0)), boolean: StandardConstructor::with_prototype(JsObject::from_proto_and_data( diff --git a/core/engine/src/object/builtins/jsfunction.rs b/core/engine/src/object/builtins/jsfunction.rs index b33304ea717..1784f1c9c77 100644 --- a/core/engine/src/object/builtins/jsfunction.rs +++ b/core/engine/src/object/builtins/jsfunction.rs @@ -5,11 +5,12 @@ use crate::{ value::TryFromJs, Context, JsNativeError, JsResult, JsValue, NativeFunction, TryIntoJsResult, }; use boa_gc::{Finalize, Trace, WeakGc}; +use boa_string::JsString; use std::cell::Cell; use std::marker::PhantomData; use std::ops::Deref; -use super::lazy_builtin::{BuiltIn, BuiltinKind}; +use super::lazy_builtin::{BuiltinKind, LazyBuiltIn}; /// A trait for converting a tuple of Rust values into a vector of `JsValue`, /// to be used as arguments for a JavaScript function. @@ -144,15 +145,23 @@ impl JsFunction { pub(crate) fn lazy_intrinsic_function( constructor: bool, init: fn(&Realm), + name: JsString, realm_inner: WeakGc, ) -> Self { + let kind = if constructor { + BuiltinKind::Function(Self::empty_intrinsic_function(constructor)) + } else { + BuiltinKind::Ordinary + }; + Self { inner: JsObject::from_proto_and_data( None, - BuiltIn { + LazyBuiltIn { init, is_initialized: Cell::new(false), - kind: BuiltinKind::Constructor(Self::empty_intrinsic_function(constructor)), + kind, + name, realm_inner: Some(realm_inner), }, ), diff --git a/core/engine/src/object/builtins/lazy_builtin.rs b/core/engine/src/object/builtins/lazy_builtin.rs index 809be0b51da..8c67c4d196c 100644 --- a/core/engine/src/object/builtins/lazy_builtin.rs +++ b/core/engine/src/object/builtins/lazy_builtin.rs @@ -16,41 +16,44 @@ use crate::{ Context, JsData, JsNativeError, JsObject, JsResult, JsValue, }; use boa_gc::{Finalize, Trace, WeakGc}; +use boa_string::JsString; use std::cell::Cell; #[derive(Debug, Clone, Trace, Finalize)] pub(crate) enum BuiltinKind { - Constructor(JsFunction), + Function(JsFunction), Ordinary, } /// A builtin function. Used for lazy initialization of builtins. #[derive(Clone, Finalize)] -pub struct BuiltIn { +pub struct LazyBuiltIn { pub(crate) init: fn(&Realm), pub(crate) is_initialized: Cell, pub(crate) kind: BuiltinKind, pub(crate) realm_inner: Option>, + pub(crate) name: JsString, } // SAFETY: Temporary, TODO move back to derived Trace when possible -unsafe impl Trace for BuiltIn { +unsafe impl Trace for LazyBuiltIn { custom_trace!(this, mark, { mark(&this.kind); }); } // Implement the trait for JsData by overriding all internal_methods by calling init before calling into the underlying internel_method -impl JsData for BuiltIn { +impl JsData for LazyBuiltIn { fn internal_methods(&self) -> &'static InternalObjectMethods { - static CONSTRUCTOR: InternalObjectMethods = InternalObjectMethods { + static FUNCTION: InternalObjectMethods = InternalObjectMethods { __construct__: lazy_construct, + __call__: lazy_call, ..LAZY_INTERNAL_METHODS }; - if let BuiltinKind::Constructor(_) = self.kind { - return &CONSTRUCTOR; + if let BuiltinKind::Function(_) = self.kind { + return &FUNCTION; } &LAZY_INTERNAL_METHODS @@ -78,7 +81,7 @@ pub(crate) fn lazy_get_prototype_of( obj: &JsObject, context: &mut Context, ) -> JsResult { - let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); if !builtin.borrow().data.is_initialized.get() { let builtin_borrow = builtin.borrow_mut(); let realm = &Realm { @@ -104,7 +107,7 @@ pub(crate) fn lazy_set_prototype_of( prototype: JsPrototype, context: &mut Context, ) -> JsResult { - let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); if !builtin.borrow().data.is_initialized.get() { let builtin_borrow = builtin.borrow_mut(); let realm = &Realm { @@ -125,7 +128,7 @@ pub(crate) fn lazy_set_prototype_of( ordinary_set_prototype_of(obj, prototype, context) } pub(crate) fn lazy_is_extensible(obj: &JsObject, context: &mut Context) -> JsResult { - let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); if !builtin.borrow().data.is_initialized.get() { let builtin_borrow = builtin.borrow_mut(); let realm = &Realm { @@ -147,7 +150,7 @@ pub(crate) fn lazy_is_extensible(obj: &JsObject, context: &mut Context) -> JsRes } pub(crate) fn lazy_prevent_extensions(obj: &JsObject, context: &mut Context) -> JsResult { - let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); if !builtin.borrow().data.is_initialized.get() { let builtin_borrow = builtin.borrow_mut(); let realm = &Realm { @@ -173,7 +176,7 @@ pub(crate) fn lazy_get_own_property( key: &PropertyKey, context: &mut InternalMethodContext<'_>, ) -> JsResult> { - let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); if !builtin.borrow().data.is_initialized.get() { let builtin_borrow = builtin.borrow_mut(); let realm = &Realm { @@ -200,7 +203,7 @@ pub(crate) fn lazy_define_own_property( desc: PropertyDescriptor, context: &mut InternalMethodContext<'_>, ) -> JsResult { - let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); if !builtin.borrow().data.is_initialized.get() { let builtin_borrow = builtin.borrow_mut(); let realm = &Realm { @@ -226,7 +229,7 @@ pub(crate) fn lazy_has_property( key: &PropertyKey, context: &mut InternalMethodContext<'_>, ) -> JsResult { - let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); if !builtin.borrow().data.is_initialized.get() { let builtin_borrow = builtin.borrow_mut(); let realm = &Realm { @@ -253,7 +256,7 @@ pub(crate) fn lazy_try_get( receiver: JsValue, context: &mut InternalMethodContext<'_>, ) -> JsResult> { - let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); if !builtin.borrow().data.is_initialized.get() { let builtin_borrow = builtin.borrow_mut(); let realm = &Realm { @@ -280,7 +283,7 @@ pub(crate) fn lazy_get( receiver: JsValue, context: &mut InternalMethodContext<'_>, ) -> JsResult { - let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); if !builtin.borrow().data.is_initialized.get() { let builtin_borrow = builtin.borrow_mut(); let realm = &Realm { @@ -308,7 +311,7 @@ pub(crate) fn lazy_set( receiver: JsValue, context: &mut InternalMethodContext<'_>, ) -> JsResult { - let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); if !builtin.borrow().data.is_initialized.get() { let builtin_borrow = builtin.borrow_mut(); builtin_borrow.data.is_initialized.set(true); @@ -334,7 +337,7 @@ pub(crate) fn lazy_delete( key: &PropertyKey, context: &mut InternalMethodContext<'_>, ) -> JsResult { - let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); if !builtin.borrow().data.is_initialized.get() { let builtin_borrow = builtin.borrow_mut(); let realm = &Realm { @@ -359,7 +362,7 @@ pub(crate) fn lazy_own_property_keys( obj: &JsObject, context: &mut Context, ) -> JsResult> { - let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); if !builtin.borrow().data.is_initialized.get() { let builtin_borrow = builtin.borrow_mut(); let realm = &Realm { @@ -385,7 +388,7 @@ pub(crate) fn lazy_construct( argument_count: usize, context: &mut Context, ) -> JsResult { - let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); let kind = &builtin.borrow().data.kind; if !builtin.borrow().data.is_initialized.get() { let builtin_borrow = builtin.borrow_mut(); @@ -409,6 +412,40 @@ pub(crate) fn lazy_construct( .with_message("not a constructor") .with_realm(context.realm().clone()) .into()), - BuiltinKind::Constructor(constructor) => Ok(constructor.__construct__(argument_count)), + BuiltinKind::Function(constructor) => Ok(constructor.__construct__(argument_count)), + } +} + +pub(crate) fn lazy_call( + obj: &JsObject, + argument_count: usize, + context: &mut Context, +) -> JsResult { + let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); + let kind = &builtin.borrow().data.kind; + if !builtin.borrow().data.is_initialized.get() { + let builtin_borrow = builtin.borrow_mut(); + let realm = &Realm { + inner: builtin_borrow + .data + .realm_inner + .as_ref() + .expect("realm_inner not set") + .upgrade() + .expect("realm_inner not set"), + }; + + builtin_borrow.data.is_initialized.set(true); + let init_fn = builtin_borrow.data.init; + drop(builtin_borrow); + init_fn(realm); + } + + match kind { + BuiltinKind::Ordinary => Err(JsNativeError::typ() + .with_message("not a constructor") + .with_realm(context.realm().clone()) + .into()), + BuiltinKind::Function(function) => Ok(function.__call__(argument_count)), } } diff --git a/core/engine/src/realm.rs b/core/engine/src/realm.rs index 5604d437778..4ddec7c72bc 100644 --- a/core/engine/src/realm.rs +++ b/core/engine/src/realm.rs @@ -83,7 +83,7 @@ impl Realm { // Use Gc::new_cyclic to create the Realm with a cyclic reference let inner = Gc::new_cyclic(|weak_realm| { // Initialize intrinsics with a reference to the weak_realm - let intrinsics = Intrinsics::uninit(root_shape, weak_realm.to_owned()) + let intrinsics = Intrinsics::uninit(root_shape, weak_realm) .expect("failed to create the realm intrinsics"); let global_object = hooks.create_global_object(&intrinsics); From aaa4bf35f5373e618b3f34d840a5420bd2f5ffa8 Mon Sep 17 00:00:00 2001 From: jasonwilliams Date: Sun, 6 Oct 2024 15:21:36 +0100 Subject: [PATCH 6/9] Fold init and realm into a single option type, no need for NAME to be passed through --- core/engine/src/builtins/function/mod.rs | 9 +- core/engine/src/context/intrinsics.rs | 13 +- core/engine/src/object/builtins/jsfunction.rs | 8 +- .../src/object/builtins/lazy_builtin.rs | 330 ++++++++---------- 4 files changed, 163 insertions(+), 197 deletions(-) diff --git a/core/engine/src/builtins/function/mod.rs b/core/engine/src/builtins/function/mod.rs index 2ade79de338..49787df0a02 100644 --- a/core/engine/src/builtins/function/mod.rs +++ b/core/engine/src/builtins/function/mod.rs @@ -843,7 +843,7 @@ impl BuiltInFunctionObject { }; let object_borrow = object.borrow(); - if object_borrow.is::() { + if object_borrow.is::() || object_borrow.is::() { let name = { // Is there a case here where if there is no name field on a value // name should default to None? Do all functions have names set? @@ -859,13 +859,6 @@ impl BuiltInFunctionObject { ); } else if object_borrow.is::() || object_borrow.is::() { return Ok(js_string!("function () { [native code] }").into()); - } else if object_borrow.is::() { - let name = object_borrow - .downcast_ref::() - .map_or_else(|| js_string!(), |built_in| built_in.name.clone()); - return Ok( - js_string!(js_str!("function "), &name, js_str!("() { [native code] }")).into(), - ); } let function = object_borrow diff --git a/core/engine/src/context/intrinsics.rs b/core/engine/src/context/intrinsics.rs index d2292a8ba25..9ea9afab07c 100644 --- a/core/engine/src/context/intrinsics.rs +++ b/core/engine/src/context/intrinsics.rs @@ -2,12 +2,11 @@ use boa_gc::{Finalize, Trace, WeakGc}; use boa_macros::js_str; -use boa_string::JsString; use crate::{ builtins::{ - iterable::IteratorPrototypes, uri::UriFunctions, Array, BuiltInObject, Date, - IntrinsicObject, OrdinaryObject, + iterable::IteratorPrototypes, uri::UriFunctions, Array, Date, IntrinsicObject, + OrdinaryObject, }, js_string, object::{ @@ -101,9 +100,9 @@ impl StandardConstructor { } /// Similar to `with_prototype`, but the prototype is lazily initialized. - fn lazy(init: fn(&Realm) -> (), name: JsString, realm_inner: WeakGc) -> Self { + fn lazy(init: fn(&Realm) -> (), realm_inner: WeakGc) -> Self { Self { - constructor: JsFunction::lazy_intrinsic_function(true, init, name, realm_inner), + constructor: JsFunction::lazy_intrinsic_function(true, init, realm_inner), prototype: JsObject::default(), } } @@ -225,14 +224,14 @@ impl StandardConstructors { )), async_generator_function: StandardConstructor::default(), proxy: StandardConstructor::default(), - date: StandardConstructor::lazy(Date::init, Date::NAME, realm_inner.clone()), + date: StandardConstructor::lazy(Date::init, realm_inner.clone()), 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::lazy(Array::init, Array::NAME, realm_inner.clone()), + array: StandardConstructor::lazy(Array::init, realm_inner.clone()), bigint: StandardConstructor::default(), number: StandardConstructor::with_prototype(JsObject::from_proto_and_data(None, 0.0)), boolean: StandardConstructor::with_prototype(JsObject::from_proto_and_data( diff --git a/core/engine/src/object/builtins/jsfunction.rs b/core/engine/src/object/builtins/jsfunction.rs index 1784f1c9c77..ada2a4c6df8 100644 --- a/core/engine/src/object/builtins/jsfunction.rs +++ b/core/engine/src/object/builtins/jsfunction.rs @@ -5,8 +5,6 @@ use crate::{ value::TryFromJs, Context, JsNativeError, JsResult, JsValue, NativeFunction, TryIntoJsResult, }; use boa_gc::{Finalize, Trace, WeakGc}; -use boa_string::JsString; -use std::cell::Cell; use std::marker::PhantomData; use std::ops::Deref; @@ -145,7 +143,6 @@ impl JsFunction { pub(crate) fn lazy_intrinsic_function( constructor: bool, init: fn(&Realm), - name: JsString, realm_inner: WeakGc, ) -> Self { let kind = if constructor { @@ -158,11 +155,8 @@ impl JsFunction { inner: JsObject::from_proto_and_data( None, LazyBuiltIn { - init, - is_initialized: Cell::new(false), + init_and_realm: Some((init, realm_inner)), kind, - name, - realm_inner: Some(realm_inner), }, ), } diff --git a/core/engine/src/object/builtins/lazy_builtin.rs b/core/engine/src/object/builtins/lazy_builtin.rs index 8c67c4d196c..cceb38702f7 100644 --- a/core/engine/src/object/builtins/lazy_builtin.rs +++ b/core/engine/src/object/builtins/lazy_builtin.rs @@ -16,8 +16,6 @@ use crate::{ Context, JsData, JsNativeError, JsObject, JsResult, JsValue, }; use boa_gc::{Finalize, Trace, WeakGc}; -use boa_string::JsString; -use std::cell::Cell; #[derive(Debug, Clone, Trace, Finalize)] pub(crate) enum BuiltinKind { @@ -29,11 +27,8 @@ pub(crate) enum BuiltinKind { #[derive(Clone, Finalize)] pub struct LazyBuiltIn { - pub(crate) init: fn(&Realm), - pub(crate) is_initialized: Cell, + pub(crate) init_and_realm: Option<(fn(&Realm), WeakGc)>, pub(crate) kind: BuiltinKind, - pub(crate) realm_inner: Option>, - pub(crate) name: JsString, } // SAFETY: Temporary, TODO move back to derived Trace when possible @@ -82,21 +77,20 @@ pub(crate) fn lazy_get_prototype_of( context: &mut Context, ) -> JsResult { let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); - if !builtin.borrow().data.is_initialized.get() { - let builtin_borrow = builtin.borrow_mut(); + // If there is a value in here, we need to initialize the builtin + if builtin.borrow().data.init_and_realm.is_some() { + let mut builtin_borrow = builtin.borrow_mut(); + let (init, realm_inner) = builtin_borrow + .data + .init_and_realm + .take() + .expect("init_and_realm not set"); + let realm = &Realm { - inner: builtin_borrow - .data - .realm_inner - .as_ref() - .expect("realm_inner not set") - .upgrade() - .expect("realm_inner not set"), + inner: realm_inner.upgrade().expect("realm_inner not set"), }; - builtin_borrow.data.is_initialized.set(true); - let init_fn = builtin_borrow.data.init; drop(builtin_borrow); - init_fn(realm); + init(realm); } ordinary_get_prototype_of(obj, context) @@ -108,42 +102,40 @@ pub(crate) fn lazy_set_prototype_of( context: &mut Context, ) -> JsResult { let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); - if !builtin.borrow().data.is_initialized.get() { - let builtin_borrow = builtin.borrow_mut(); + // If there is a value in here, we need to initialize the builtin + if builtin.borrow().data.init_and_realm.is_some() { + let mut builtin_borrow = builtin.borrow_mut(); + let (init, realm_inner) = builtin_borrow + .data + .init_and_realm + .take() + .expect("init_and_realm not set"); + let realm = &Realm { - inner: builtin_borrow - .data - .realm_inner - .as_ref() - .expect("realm_inner not set") - .upgrade() - .expect("realm_inner not set"), + inner: realm_inner.upgrade().expect("realm_inner not set"), }; - builtin_borrow.data.is_initialized.set(true); - let init_fn = builtin_borrow.data.init; drop(builtin_borrow); - init_fn(realm); + init(realm); } ordinary_set_prototype_of(obj, prototype, context) } pub(crate) fn lazy_is_extensible(obj: &JsObject, context: &mut Context) -> JsResult { let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); - if !builtin.borrow().data.is_initialized.get() { - let builtin_borrow = builtin.borrow_mut(); + // If there is a value in here, we need to initialize the builtin + if builtin.borrow().data.init_and_realm.is_some() { + let mut builtin_borrow = builtin.borrow_mut(); + let (init, realm_inner) = builtin_borrow + .data + .init_and_realm + .take() + .expect("init_and_realm not set"); + let realm = &Realm { - inner: builtin_borrow - .data - .realm_inner - .as_ref() - .expect("realm_inner not set") - .upgrade() - .expect("realm_inner not set"), + inner: realm_inner.upgrade().expect("realm_inner not set"), }; - builtin_borrow.data.is_initialized.set(true); - let init_fn = builtin_borrow.data.init; drop(builtin_borrow); - init_fn(realm); + init(realm); } ordinary_is_extensible(obj, context) @@ -151,21 +143,20 @@ pub(crate) fn lazy_is_extensible(obj: &JsObject, context: &mut Context) -> JsRes pub(crate) fn lazy_prevent_extensions(obj: &JsObject, context: &mut Context) -> JsResult { let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); - if !builtin.borrow().data.is_initialized.get() { - let builtin_borrow = builtin.borrow_mut(); + // If there is a value in here, we need to initialize the builtin + if builtin.borrow().data.init_and_realm.is_some() { + let mut builtin_borrow = builtin.borrow_mut(); + let (init, realm_inner) = builtin_borrow + .data + .init_and_realm + .take() + .expect("init_and_realm not set"); + let realm = &Realm { - inner: builtin_borrow - .data - .realm_inner - .as_ref() - .expect("realm_inner not set") - .upgrade() - .expect("realm_inner not set"), + inner: realm_inner.upgrade().expect("realm_inner not set"), }; - builtin_borrow.data.is_initialized.set(true); - let init_fn = builtin_borrow.data.init; drop(builtin_borrow); - init_fn(realm); + init(realm); } ordinary_prevent_extensions(obj, context) @@ -177,21 +168,20 @@ pub(crate) fn lazy_get_own_property( context: &mut InternalMethodContext<'_>, ) -> JsResult> { let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); - if !builtin.borrow().data.is_initialized.get() { - let builtin_borrow = builtin.borrow_mut(); + // If there is a value in here, we need to initialize the builtin + if builtin.borrow().data.init_and_realm.is_some() { + let mut builtin_borrow = builtin.borrow_mut(); + let (init, realm_inner) = builtin_borrow + .data + .init_and_realm + .take() + .expect("init_and_realm not set"); + let realm = &Realm { - inner: builtin_borrow - .data - .realm_inner - .as_ref() - .expect("realm_inner not set") - .upgrade() - .expect("realm_inner not set"), + inner: realm_inner.upgrade().expect("realm_inner not set"), }; - builtin_borrow.data.is_initialized.set(true); - let init_fn = builtin_borrow.data.init; drop(builtin_borrow); - init_fn(realm); + init(realm); } ordinary_get_own_property(obj, key, context) @@ -204,21 +194,20 @@ pub(crate) fn lazy_define_own_property( context: &mut InternalMethodContext<'_>, ) -> JsResult { let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); - if !builtin.borrow().data.is_initialized.get() { - let builtin_borrow = builtin.borrow_mut(); + // If there is a value in here, we need to initialize the builtin + if builtin.borrow().data.init_and_realm.is_some() { + let mut builtin_borrow = builtin.borrow_mut(); + let (init, realm_inner) = builtin_borrow + .data + .init_and_realm + .take() + .expect("init_and_realm not set"); + let realm = &Realm { - inner: builtin_borrow - .data - .realm_inner - .as_ref() - .expect("realm_inner not set") - .upgrade() - .expect("realm_inner not set"), + inner: realm_inner.upgrade().expect("realm_inner not set"), }; - builtin_borrow.data.is_initialized.set(true); - let init_fn = builtin_borrow.data.init; drop(builtin_borrow); - init_fn(realm); + init(realm); } ordinary_define_own_property(obj, key, desc, context) @@ -230,21 +219,20 @@ pub(crate) fn lazy_has_property( context: &mut InternalMethodContext<'_>, ) -> JsResult { let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); - if !builtin.borrow().data.is_initialized.get() { - let builtin_borrow = builtin.borrow_mut(); + // If there is a value in here, we need to initialize the builtin + if builtin.borrow().data.init_and_realm.is_some() { + let mut builtin_borrow = builtin.borrow_mut(); + let (init, realm_inner) = builtin_borrow + .data + .init_and_realm + .take() + .expect("init_and_realm not set"); + let realm = &Realm { - inner: builtin_borrow - .data - .realm_inner - .as_ref() - .expect("realm_inner not set") - .upgrade() - .expect("realm_inner not set"), + inner: realm_inner.upgrade().expect("realm_inner not set"), }; - builtin_borrow.data.is_initialized.set(true); - let init_fn = builtin_borrow.data.init; drop(builtin_borrow); - init_fn(realm); + init(realm); } ordinary_has_property(obj, key, context) @@ -257,21 +245,20 @@ pub(crate) fn lazy_try_get( context: &mut InternalMethodContext<'_>, ) -> JsResult> { let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); - if !builtin.borrow().data.is_initialized.get() { - let builtin_borrow = builtin.borrow_mut(); + // If there is a value in here, we need to initialize the builtin + if builtin.borrow().data.init_and_realm.is_some() { + let mut builtin_borrow = builtin.borrow_mut(); + let (init, realm_inner) = builtin_borrow + .data + .init_and_realm + .take() + .expect("init_and_realm not set"); + let realm = &Realm { - inner: builtin_borrow - .data - .realm_inner - .as_ref() - .expect("realm_inner not set") - .upgrade() - .expect("realm_inner not set"), + inner: realm_inner.upgrade().expect("realm_inner not set"), }; - builtin_borrow.data.is_initialized.set(true); - let init_fn = builtin_borrow.data.init; drop(builtin_borrow); - init_fn(realm); + init(realm); } ordinary_try_get(obj, key, receiver, context) @@ -284,21 +271,20 @@ pub(crate) fn lazy_get( context: &mut InternalMethodContext<'_>, ) -> JsResult { let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); - if !builtin.borrow().data.is_initialized.get() { - let builtin_borrow = builtin.borrow_mut(); + // If there is a value in here, we need to initialize the builtin + if builtin.borrow().data.init_and_realm.is_some() { + let mut builtin_borrow = builtin.borrow_mut(); + let (init, realm_inner) = builtin_borrow + .data + .init_and_realm + .take() + .expect("init_and_realm not set"); + let realm = &Realm { - inner: builtin_borrow - .data - .realm_inner - .as_ref() - .expect("realm_inner not set") - .upgrade() - .expect("realm_inner not set"), + inner: realm_inner.upgrade().expect("realm_inner not set"), }; - builtin_borrow.data.is_initialized.set(true); - let init_fn = builtin_borrow.data.init; drop(builtin_borrow); - init_fn(realm); + init(realm); } ordinary_get(obj, key, receiver, context) @@ -312,21 +298,20 @@ pub(crate) fn lazy_set( context: &mut InternalMethodContext<'_>, ) -> JsResult { let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); - if !builtin.borrow().data.is_initialized.get() { - let builtin_borrow = builtin.borrow_mut(); - builtin_borrow.data.is_initialized.set(true); + // If there is a value in here, we need to initialize the builtin + if builtin.borrow().data.init_and_realm.is_some() { + let mut builtin_borrow = builtin.borrow_mut(); + let (init, realm_inner) = builtin_borrow + .data + .init_and_realm + .take() + .expect("init_and_realm not set"); + let realm = &Realm { - inner: builtin_borrow - .data - .realm_inner - .as_ref() - .expect("realm_inner not set") - .upgrade() - .expect("realm_inner not set"), + inner: realm_inner.upgrade().expect("realm_inner not set"), }; - let init_fn = builtin_borrow.data.init; drop(builtin_borrow); - init_fn(realm); + init(realm); } ordinary_set(obj, key, value, receiver, context) @@ -338,21 +323,20 @@ pub(crate) fn lazy_delete( context: &mut InternalMethodContext<'_>, ) -> JsResult { let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); - if !builtin.borrow().data.is_initialized.get() { - let builtin_borrow = builtin.borrow_mut(); + // If there is a value in here, we need to initialize the builtin + if builtin.borrow().data.init_and_realm.is_some() { + let mut builtin_borrow = builtin.borrow_mut(); + let (init, realm_inner) = builtin_borrow + .data + .init_and_realm + .take() + .expect("init_and_realm not set"); + let realm = &Realm { - inner: builtin_borrow - .data - .realm_inner - .as_ref() - .expect("realm_inner not set") - .upgrade() - .expect("realm_inner not set"), + inner: realm_inner.upgrade().expect("realm_inner not set"), }; - builtin_borrow.data.is_initialized.set(true); - let init_fn = builtin_borrow.data.init; drop(builtin_borrow); - init_fn(realm); + init(realm); } ordinary_delete(obj, key, context) @@ -363,21 +347,20 @@ pub(crate) fn lazy_own_property_keys( context: &mut Context, ) -> JsResult> { let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); - if !builtin.borrow().data.is_initialized.get() { - let builtin_borrow = builtin.borrow_mut(); + // If there is a value in here, we need to initialize the builtin + if builtin.borrow().data.init_and_realm.is_some() { + let mut builtin_borrow = builtin.borrow_mut(); + let (init, realm_inner) = builtin_borrow + .data + .init_and_realm + .take() + .expect("init_and_realm not set"); + let realm = &Realm { - inner: builtin_borrow - .data - .realm_inner - .as_ref() - .expect("realm_inner not set") - .upgrade() - .expect("realm_inner not set"), + inner: realm_inner.upgrade().expect("realm_inner not set"), }; - builtin_borrow.data.is_initialized.set(true); - let init_fn = builtin_borrow.data.init; drop(builtin_borrow); - init_fn(realm); + init(realm); } ordinary_own_property_keys(obj, context) @@ -390,21 +373,20 @@ pub(crate) fn lazy_construct( ) -> JsResult { let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); let kind = &builtin.borrow().data.kind; - if !builtin.borrow().data.is_initialized.get() { - let builtin_borrow = builtin.borrow_mut(); + // If there is a value in here, we need to initialize the builtin + if builtin.borrow().data.init_and_realm.is_some() { + let mut builtin_borrow = builtin.borrow_mut(); + let (init, realm_inner) = builtin_borrow + .data + .init_and_realm + .take() + .expect("init_and_realm not set"); + let realm = &Realm { - inner: builtin_borrow - .data - .realm_inner - .as_ref() - .expect("realm_inner not set") - .upgrade() - .expect("realm_inner not set"), + inner: realm_inner.upgrade().expect("realm_inner not set"), }; - builtin_borrow.data.is_initialized.set(true); - let init_fn = builtin_borrow.data.init; drop(builtin_borrow); - init_fn(realm); + init(realm); } match kind { @@ -423,22 +405,20 @@ pub(crate) fn lazy_call( ) -> JsResult { let builtin: JsObject = obj.clone().downcast().expect("obj is not a Builtin"); let kind = &builtin.borrow().data.kind; - if !builtin.borrow().data.is_initialized.get() { - let builtin_borrow = builtin.borrow_mut(); + // If there is a value in here, we need to initialize the builtin + if builtin.borrow().data.init_and_realm.is_some() { + let mut builtin_borrow = builtin.borrow_mut(); + let (init, realm_inner) = builtin_borrow + .data + .init_and_realm + .take() + .expect("init_and_realm not set"); + let realm = &Realm { - inner: builtin_borrow - .data - .realm_inner - .as_ref() - .expect("realm_inner not set") - .upgrade() - .expect("realm_inner not set"), + inner: realm_inner.upgrade().expect("realm_inner not set"), }; - - builtin_borrow.data.is_initialized.set(true); - let init_fn = builtin_borrow.data.init; drop(builtin_borrow); - init_fn(realm); + init(realm); } match kind { From d697af1f6b148ce0b75253f705e73ba79251cef1 Mon Sep 17 00:00:00 2001 From: jasonwilliams Date: Sun, 6 Oct 2024 15:36:16 +0100 Subject: [PATCH 7/9] add debug annoatations --- core/engine/src/object/builtins/lazy_builtin.rs | 3 ++- core/engine/src/realm.rs | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/core/engine/src/object/builtins/lazy_builtin.rs b/core/engine/src/object/builtins/lazy_builtin.rs index cceb38702f7..03cfbb21a25 100644 --- a/core/engine/src/object/builtins/lazy_builtin.rs +++ b/core/engine/src/object/builtins/lazy_builtin.rs @@ -25,7 +25,8 @@ pub(crate) enum BuiltinKind { /// A builtin function. Used for lazy initialization of builtins. -#[derive(Clone, Finalize)] +#[derive(Clone, Finalize, Debug)] +#[allow(clippy::type_complexity)] pub struct LazyBuiltIn { pub(crate) init_and_realm: Option<(fn(&Realm), WeakGc)>, pub(crate) kind: BuiltinKind, diff --git a/core/engine/src/realm.rs b/core/engine/src/realm.rs index 4ddec7c72bc..323f946cee5 100644 --- a/core/engine/src/realm.rs +++ b/core/engine/src/realm.rs @@ -70,10 +70,23 @@ pub struct RealmInner { template_map: GcRefCell>, loaded_modules: GcRefCell>, host_classes: GcRefCell>, - host_defined: GcRefCell, } +#[allow(clippy::missing_fields_in_debug)] +impl std::fmt::Debug for RealmInner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("RealmInner") + .field("intrinsics", &self.intrinsics) + .field("environment", &self.environment) + .field("global_object", &self.global_object) + .field("global_this", &self.global_this) + .field("template_map", &self.template_map) + .field("loaded_modules", &self.loaded_modules) + .field("host_classes", &self.host_classes) + .finish() + } +} impl Realm { /// Create a new [`Realm`]. #[inline] From 5ceedffdb432e9a2eb243261626887e64156c90e Mon Sep 17 00:00:00 2001 From: jasonwilliams Date: Sun, 6 Oct 2024 16:38:16 +0100 Subject: [PATCH 8/9] Refactor built-in constructor and function initialization --- core/engine/src/builtins/builder.rs | 21 ++++++++++++- core/engine/src/object/builtins/jsfunction.rs | 12 +++---- .../src/object/builtins/lazy_builtin.rs | 31 ++++++++++++++++--- 3 files changed, 51 insertions(+), 13 deletions(-) diff --git a/core/engine/src/builtins/builder.rs b/core/engine/src/builtins/builder.rs index af29d2eebb0..e135f36ec00 100644 --- a/core/engine/src/builtins/builder.rs +++ b/core/engine/src/builtins/builder.rs @@ -5,7 +5,7 @@ use crate::{ native_function::{NativeFunctionObject, NativeFunctionPointer}, object::{ shape::{property_table::PropertyTableInner, slot::SlotAttributes}, - FunctionBinding, JsFunction, JsPrototype, CONSTRUCTOR, PROTOTYPE, + FunctionBinding, JsFunction, JsPrototype, LazyBuiltIn, CONSTRUCTOR, PROTOTYPE, }, property::{Attribute, PropertyDescriptor, PropertyKey}, realm::Realm, @@ -391,6 +391,25 @@ impl BuiltInConstructorWithPrototype<'_> { } let mut object = self.object.borrow_mut(); + if object.is::() { + let function = object + .downcast_mut::() + .expect("Builtin must be a function object"); + function.f = NativeFunction::from_fn_ptr(self.function); + function.constructor = Some(ConstructorKind::Base); + function.realm = Some(self.realm.clone()); + } else if object.is::() { + let lazy = object + .downcast_mut::() + .expect("Builtin must be a lazy object"); + lazy.set_constructor( + NativeFunction::from_fn_ptr(self.function), + self.realm.clone(), + ); + } else { + unreachable!("The object must be a function or a lazy object"); + } + object .properties_mut() .shape diff --git a/core/engine/src/object/builtins/jsfunction.rs b/core/engine/src/object/builtins/jsfunction.rs index ada2a4c6df8..9c0b3f8e470 100644 --- a/core/engine/src/object/builtins/jsfunction.rs +++ b/core/engine/src/object/builtins/jsfunction.rs @@ -145,18 +145,16 @@ impl JsFunction { init: fn(&Realm), realm_inner: WeakGc, ) -> Self { - let kind = if constructor { - BuiltinKind::Function(Self::empty_intrinsic_function(constructor)) - } else { - BuiltinKind::Ordinary - }; - Self { inner: JsObject::from_proto_and_data( None, LazyBuiltIn { init_and_realm: Some((init, realm_inner)), - kind, + kind: BuiltinKind::Function(NativeFunctionObject { + f: NativeFunction::from_fn_ptr(|_, _, _| Ok(JsValue::undefined())), + constructor: constructor.then_some(ConstructorKind::Base), + realm: None, + }), }, ), } diff --git a/core/engine/src/object/builtins/lazy_builtin.rs b/core/engine/src/object/builtins/lazy_builtin.rs index 03cfbb21a25..3c1de85dfac 100644 --- a/core/engine/src/object/builtins/lazy_builtin.rs +++ b/core/engine/src/object/builtins/lazy_builtin.rs @@ -1,6 +1,7 @@ -use super::JsFunction; use crate::{ + builtins::function::ConstructorKind, gc::custom_trace, + native_function::NativeFunctionObject, object::{ internal_methods::{ non_existant_call, non_existant_construct, ordinary_define_own_property, @@ -13,13 +14,13 @@ use crate::{ }, property::{PropertyDescriptor, PropertyKey}, realm::{Realm, RealmInner}, - Context, JsData, JsNativeError, JsObject, JsResult, JsValue, + Context, JsData, JsNativeError, JsObject, JsResult, JsValue, NativeFunction, }; use boa_gc::{Finalize, Trace, WeakGc}; #[derive(Debug, Clone, Trace, Finalize)] pub(crate) enum BuiltinKind { - Function(JsFunction), + Function(NativeFunctionObject), Ordinary, } @@ -32,6 +33,18 @@ pub struct LazyBuiltIn { pub(crate) kind: BuiltinKind, } +impl LazyBuiltIn { + pub(crate) fn set_constructor(&mut self, function: NativeFunction, realm: Realm) { + if let BuiltinKind::Function(ref mut native_function) = self.kind { + native_function.f = function; + native_function.constructor = Some(ConstructorKind::Base); + native_function.realm = Some(realm); + } else { + panic!("Expected BuiltinKind::Function"); + } + } +} + // SAFETY: Temporary, TODO move back to derived Trace when possible unsafe impl Trace for LazyBuiltIn { custom_trace!(this, mark, { @@ -395,7 +408,11 @@ pub(crate) fn lazy_construct( .with_message("not a constructor") .with_realm(context.realm().clone()) .into()), - BuiltinKind::Function(constructor) => Ok(constructor.__construct__(argument_count)), + BuiltinKind::Function(constructor) => Ok((constructor.internal_methods().__construct__)( + obj, + argument_count, + context, + )?), } } @@ -427,6 +444,10 @@ pub(crate) fn lazy_call( .with_message("not a constructor") .with_realm(context.realm().clone()) .into()), - BuiltinKind::Function(function) => Ok(function.__call__(argument_count)), + BuiltinKind::Function(function) => Ok((function.internal_methods().__call__)( + obj, + argument_count, + context, + )?), } } From a373d78bc08c8b29eff1007f4ee321cc2c8da3e3 Mon Sep 17 00:00:00 2001 From: jasonwilliams Date: Sun, 6 Oct 2024 17:05:12 +0100 Subject: [PATCH 9/9] Proof of concept of ordinary function being initialized. plus tidy up and documentation --- core/engine/src/builtins/mod.rs | 2 +- core/engine/src/context/intrinsics.rs | 8 ++++---- core/engine/src/object/jsobject.rs | 16 ++++++++++++++-- core/engine/src/realm.rs | 7 +++++++ 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/core/engine/src/builtins/mod.rs b/core/engine/src/builtins/mod.rs index fdb5daeaa8f..844b4aa58bc 100644 --- a/core/engine/src/builtins/mod.rs +++ b/core/engine/src/builtins/mod.rs @@ -201,7 +201,7 @@ impl Realm { AsyncIterator::init(self); AsyncFromSyncIterator::init(self); ForInIterator::init(self); - Math::init(self); + // Math::init(self); Json::init(self); ArrayIterator::init(self); Proxy::init(self); diff --git a/core/engine/src/context/intrinsics.rs b/core/engine/src/context/intrinsics.rs index 9ea9afab07c..4854adfe34c 100644 --- a/core/engine/src/context/intrinsics.rs +++ b/core/engine/src/context/intrinsics.rs @@ -5,7 +5,7 @@ use boa_macros::js_str; use crate::{ builtins::{ - iterable::IteratorPrototypes, uri::UriFunctions, Array, Date, IntrinsicObject, + iterable::IteratorPrototypes, uri::UriFunctions, Array, Date, IntrinsicObject, Math, OrdinaryObject, }, js_string, @@ -50,7 +50,7 @@ impl Intrinsics { Some(Self { constructors, - objects: IntrinsicObjects::uninit()?, + objects: IntrinsicObjects::uninit(realm_inner)?, templates, }) } @@ -1132,10 +1132,10 @@ impl IntrinsicObjects { /// /// [`Realm::initialize`]: crate::realm::Realm::initialize #[allow(clippy::unnecessary_wraps)] - pub(crate) fn uninit() -> Option { + pub(crate) fn uninit(realm_inner: &WeakGc) -> Option { Some(Self { reflect: JsObject::default(), - math: JsObject::default(), + math: JsObject::lazy(Math::init, realm_inner), json: JsObject::default(), throw_type_error: JsFunction::empty_intrinsic_function(false), array_prototype_values: JsFunction::empty_intrinsic_function(false), diff --git a/core/engine/src/object/jsobject.rs b/core/engine/src/object/jsobject.rs index eccb1f81383..9cc466d0a19 100644 --- a/core/engine/src/object/jsobject.rs +++ b/core/engine/src/object/jsobject.rs @@ -5,7 +5,7 @@ use super::{ internal_methods::{InternalMethodContext, InternalObjectMethods, ORDINARY_INTERNAL_METHODS}, shape::RootShape, - JsPrototype, NativeObject, Object, PrivateName, PropertyMap, + BuiltinKind, JsPrototype, LazyBuiltIn, NativeObject, Object, PrivateName, PropertyMap, }; use crate::{ builtins::{ @@ -17,10 +17,11 @@ use crate::{ error::JsNativeError, js_string, property::{PropertyDescriptor, PropertyKey}, + realm::{Realm, RealmInner}, value::PreferredType, Context, JsResult, JsString, JsValue, }; -use boa_gc::{self, Finalize, Gc, GcBox, GcRefCell, Trace}; +use boa_gc::{self, Finalize, Gc, GcBox, GcRefCell, Trace, WeakGc}; use boa_macros::js_str; use std::{ cell::RefCell, @@ -92,6 +93,17 @@ impl JsObject { inner: coerce_gc(gc), } } + /// Creates a new lazy `JsObject` from its inner object and its vtable. + /// This is used for built-in objects that are lazily initialized. + pub(crate) fn lazy(init: fn(&Realm) -> (), realm_inner: &WeakGc) -> Self { + Self::from_proto_and_data( + None, + LazyBuiltIn { + init_and_realm: Some((init, realm_inner.clone())), + kind: BuiltinKind::Ordinary, + }, + ) + } /// Creates a new ordinary object with its prototype set to the `Object` prototype. /// diff --git a/core/engine/src/realm.rs b/core/engine/src/realm.rs index 323f946cee5..50f65104b18 100644 --- a/core/engine/src/realm.rs +++ b/core/engine/src/realm.rs @@ -30,6 +30,8 @@ use boa_profiler::Profiler; /// In the specification these are called Realm Records. #[derive(Clone, Trace, Finalize)] pub struct Realm { + /// The inner data of the realm, which includes the intrinsics, environment, + /// global object, and other realm-specific information. pub inner: Gc, } @@ -53,6 +55,11 @@ impl std::fmt::Debug for Realm { } #[derive(Trace, Finalize)] + +/// The inner data of a Realm. +/// +/// This struct contains all the realm-specific information, including the intrinsics, +/// environment, global object, and other necessary data for the execution context. pub struct RealmInner { intrinsics: Intrinsics,