diff --git a/boa_engine/src/builtins/error/type.rs b/boa_engine/src/builtins/error/type.rs index f0166e7f6d3..384c87dfabe 100644 --- a/boa_engine/src/builtins/error/type.rs +++ b/boa_engine/src/builtins/error/type.rs @@ -17,7 +17,8 @@ use crate::{ builtins::{ - function::Function, BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject, + function::{Function, FunctionKind}, + BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject, }, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, error::JsNativeError, @@ -124,10 +125,13 @@ impl IntrinsicObject for ThrowTypeError { let mut obj = obj.borrow_mut(); - obj.data = ObjectData::function(Function::Native { - function: NativeFunction::from_fn_ptr(throw_type_error), - constructor: None, - }); + obj.data = ObjectData::function(Function::new( + FunctionKind::Native { + function: NativeFunction::from_fn_ptr(throw_type_error), + constructor: None, + }, + intrinsics.clone(), + )); } fn get(intrinsics: &Intrinsics) -> JsObject { diff --git a/boa_engine/src/builtins/function/mod.rs b/boa_engine/src/builtins/function/mod.rs index ca45f2206ea..7a6474612cc 100644 --- a/boa_engine/src/builtins/function/mod.rs +++ b/boa_engine/src/builtins/function/mod.rs @@ -142,14 +142,8 @@ unsafe impl Trace for ClassFieldDefinition { }} } -/// Boa representation of a Function Object. -/// -/// `FunctionBody` is specific to this interpreter, it will either be Rust code or JavaScript code -/// (AST Node). -/// -/// #[derive(Finalize)] -pub enum Function { +pub(crate) enum FunctionKind { /// A rust function. Native { /// The rust function. @@ -231,7 +225,34 @@ pub enum Function { }, } -unsafe impl Trace for Function { +impl fmt::Debug for FunctionKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + FunctionKind::Native { + function, + constructor, + } => f + .debug_struct("FunctionKind::Native") + .field("function", &function) + .field("constructor", &constructor) + .finish(), + FunctionKind::Ordinary { .. } => f + .debug_struct("FunctionKind::Ordinary") + .finish_non_exhaustive(), + FunctionKind::Async { .. } => f + .debug_struct("FunctionKind::Async") + .finish_non_exhaustive(), + FunctionKind::Generator { .. } => f + .debug_struct("FunctionKind::Generator") + .finish_non_exhaustive(), + FunctionKind::AsyncGenerator { .. } => f + .debug_struct("FunctionKind::AsyncGenerator") + .finish_non_exhaustive(), + } + } +} + +unsafe impl Trace for FunctionKind { custom_trace! {this, { match this { Self::Native { function, .. } => {mark(function)} @@ -265,27 +286,43 @@ unsafe impl Trace for Function { }} } -impl fmt::Debug for Function { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Function {{ ... }}") - } +/// Boa representation of a Function Object. +/// +/// `FunctionBody` is specific to this interpreter, it will either be Rust code or JavaScript code +/// (AST Node). +/// +/// +#[derive(Debug, Trace, Finalize)] +pub struct Function { + kind: FunctionKind, + realm_intrinsics: Intrinsics, } impl Function { /// Returns true if the function object is a constructor. pub fn is_constructor(&self) -> bool { - match self { - Self::Native { constructor, .. } => constructor.is_some(), - Self::Generator { .. } | Self::AsyncGenerator { .. } | Self::Async { .. } => false, - Self::Ordinary { code, .. } => !(code.this_mode == ThisMode::Lexical), + match &self.kind { + FunctionKind::Native { constructor, .. } => constructor.is_some(), + FunctionKind::Generator { .. } + | FunctionKind::AsyncGenerator { .. } + | FunctionKind::Async { .. } => false, + FunctionKind::Ordinary { code, .. } => !(code.this_mode == ThisMode::Lexical), + } + } + + /// Creates a new `Function`. + pub(crate) fn new(kind: FunctionKind, intrinsics: Intrinsics) -> Self { + Self { + kind, + realm_intrinsics: intrinsics, } } /// Returns true if the function object is a derived constructor. pub(crate) const fn is_derived_constructor(&self) -> bool { - if let Self::Ordinary { + if let FunctionKind::Ordinary { constructor_kind, .. - } = self + } = self.kind { constructor_kind.is_derived() } else { @@ -295,7 +332,7 @@ impl Function { /// Returns the `[[ClassFieldInitializerName]]` internal slot of the function. pub(crate) fn class_field_initializer_name(&self) -> Option { - if let Self::Ordinary { code, .. } = self { + if let FunctionKind::Ordinary { code, .. } = &self.kind { code.class_field_initializer_name } else { None @@ -304,29 +341,29 @@ impl Function { /// Returns a reference to the function `[[HomeObject]]` slot if present. pub(crate) const fn get_home_object(&self) -> Option<&JsObject> { - match self { - Self::Ordinary { home_object, .. } - | Self::Async { home_object, .. } - | Self::Generator { home_object, .. } - | Self::AsyncGenerator { home_object, .. } => home_object.as_ref(), - Self::Native { .. } => None, + match &self.kind { + FunctionKind::Ordinary { home_object, .. } + | FunctionKind::Async { home_object, .. } + | FunctionKind::Generator { home_object, .. } + | FunctionKind::AsyncGenerator { home_object, .. } => home_object.as_ref(), + FunctionKind::Native { .. } => None, } } /// Sets the `[[HomeObject]]` slot if present. pub(crate) fn set_home_object(&mut self, object: JsObject) { - match self { - Self::Ordinary { home_object, .. } - | Self::Async { home_object, .. } - | Self::Generator { home_object, .. } - | Self::AsyncGenerator { home_object, .. } => *home_object = Some(object), - Self::Native { .. } => {} + match &mut self.kind { + FunctionKind::Ordinary { home_object, .. } + | FunctionKind::Async { home_object, .. } + | FunctionKind::Generator { home_object, .. } + | FunctionKind::AsyncGenerator { home_object, .. } => *home_object = Some(object), + FunctionKind::Native { .. } => {} } } /// Returns the values of the `[[Fields]]` internal slot. pub(crate) fn get_fields(&self) -> &[ClassFieldDefinition] { - if let Self::Ordinary { fields, .. } = self { + if let FunctionKind::Ordinary { fields, .. } = &self.kind { fields } else { &[] @@ -335,23 +372,23 @@ impl Function { /// Pushes a value to the `[[Fields]]` internal slot if present. pub(crate) fn push_field(&mut self, key: PropertyKey, value: JsFunction) { - if let Self::Ordinary { fields, .. } = self { + if let FunctionKind::Ordinary { fields, .. } = &mut self.kind { fields.push(ClassFieldDefinition::Public(key, value)); } } /// Pushes a private value to the `[[Fields]]` internal slot if present. pub(crate) fn push_field_private(&mut self, key: PrivateName, value: JsFunction) { - if let Self::Ordinary { fields, .. } = self { + if let FunctionKind::Ordinary { fields, .. } = &mut self.kind { fields.push(ClassFieldDefinition::Private(key, value)); } } /// Returns the values of the `[[PrivateMethods]]` internal slot. pub(crate) fn get_private_methods(&self) -> &[(PrivateName, PrivateElement)] { - if let Self::Ordinary { + if let FunctionKind::Ordinary { private_methods, .. - } = self + } = &self.kind { private_methods } else { @@ -361,9 +398,9 @@ impl Function { /// Pushes a private method to the `[[PrivateMethods]]` internal slot if present. pub(crate) fn push_private_method(&mut self, name: PrivateName, method: PrivateElement) { - if let Self::Ordinary { + if let FunctionKind::Ordinary { private_methods, .. - } = self + } = &mut self.kind { private_methods.push((name, method)); } @@ -371,9 +408,9 @@ impl Function { /// Returns the promise capability if the function is an async function. pub(crate) const fn get_promise_capability(&self) -> Option<&PromiseCapability> { - if let Self::Async { + if let FunctionKind::Async { promise_capability, .. - } = self + } = &self.kind { Some(promise_capability) } else { @@ -383,14 +420,29 @@ impl Function { /// Sets the class object. pub(crate) fn set_class_object(&mut self, object: JsObject) { - match self { - Self::Ordinary { class_object, .. } - | Self::Async { class_object, .. } - | Self::Generator { class_object, .. } - | Self::AsyncGenerator { class_object, .. } => *class_object = Some(object), - Self::Native { .. } => {} + match &mut self.kind { + FunctionKind::Ordinary { class_object, .. } + | FunctionKind::Async { class_object, .. } + | FunctionKind::Generator { class_object, .. } + | FunctionKind::AsyncGenerator { class_object, .. } => *class_object = Some(object), + FunctionKind::Native { .. } => {} } } + + /// Gets the `Realm`'s intrinsic objects from where this function originates. + pub const fn realm_intrinsics(&self) -> &Intrinsics { + &self.realm_intrinsics + } + + /// Gets a reference to the [`FunctionKind`] of the `Function`. + pub(crate) const fn kind(&self) -> &FunctionKind { + &self.kind + } + + /// Gets a mutable reference to the [`FunctionKind`] of the `Function`. + pub(crate) fn kind_mut(&mut self) -> &mut FunctionKind { + &mut self.kind + } } /// The internal representation of a `Function` object. @@ -854,17 +906,17 @@ impl BuiltInFunctionObject { .filter(|n| !n.is_empty()) .unwrap_or_else(|| "anonymous".into()); - match function { - Function::Native { .. } | Function::Ordinary { .. } => { + match function.kind { + FunctionKind::Native { .. } | FunctionKind::Ordinary { .. } => { Ok(js_string!(utf16!("[Function: "), &name, utf16!("]")).into()) } - Function::Async { .. } => { + FunctionKind::Async { .. } => { Ok(js_string!(utf16!("[AsyncFunction: "), &name, utf16!("]")).into()) } - Function::Generator { .. } => { + FunctionKind::Generator { .. } => { Ok(js_string!(utf16!("[GeneratorFunction: "), &name, utf16!("]")).into()) } - Function::AsyncGenerator { .. } => { + FunctionKind::AsyncGenerator { .. } => { Ok(js_string!(utf16!("[AsyncGeneratorFunction: "), &name, utf16!("]")).into()) } } diff --git a/boa_engine/src/builtins/function/tests.rs b/boa_engine/src/builtins/function/tests.rs index ba12c71032d..9aece8f73fa 100644 --- a/boa_engine/src/builtins/function/tests.rs +++ b/boa_engine/src/builtins/function/tests.rs @@ -170,7 +170,8 @@ fn closure_capture_clone() { .name("closure") .build(); - ctx.register_global_property("closure", func, Attribute::default()).unwrap(); + ctx.register_global_property("closure", func, Attribute::default()) + .unwrap(); }), TestAction::assert_eq("closure()", "Hello world!"), ]); diff --git a/boa_engine/src/builtins/iterable/mod.rs b/boa_engine/src/builtins/iterable/mod.rs index a09c2c35985..28a5e96c4cf 100644 --- a/boa_engine/src/builtins/iterable/mod.rs +++ b/boa_engine/src/builtins/iterable/mod.rs @@ -39,7 +39,7 @@ macro_rules! if_abrupt_close_iterator { pub(crate) use if_abrupt_close_iterator; /// The built-in iterator prototypes. -#[derive(Debug, Default)] +#[derive(Debug, Default, Trace, Finalize)] pub struct IteratorPrototypes { /// The `IteratorPrototype` object. iterator: JsObject, diff --git a/boa_engine/src/builtins/mod.rs b/boa_engine/src/builtins/mod.rs index 4c714775daf..d464e195f7a 100644 --- a/boa_engine/src/builtins/mod.rs +++ b/boa_engine/src/builtins/mod.rs @@ -413,6 +413,7 @@ struct Callable { name: JsString, length: usize, kind: Kind, + intrinsics: Intrinsics, } /// Marker for an ordinary object. @@ -461,10 +462,13 @@ impl ApplyToObject for Callable { fn apply_to(self, object: &JsObject) { self.kind.apply_to(object); - let function = function::Function::Native { - function: NativeFunction::from_fn_ptr(self.function), - constructor: S::IS_CONSTRUCTOR.then_some(function::ConstructorKind::Base), - }; + let function = function::Function::new( + function::FunctionKind::Native { + function: NativeFunction::from_fn_ptr(self.function), + constructor: S::IS_CONSTRUCTOR.then_some(function::ConstructorKind::Base), + }, + self.intrinsics, + ); let length = PropertyDescriptor::builder() .value(self.length) @@ -550,6 +554,7 @@ impl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> { name: js_string!(""), length: 0, kind: OrdinaryFunction, + intrinsics: self.intrinsics.clone(), }, prototype: self.intrinsics.constructors().function().prototype(), } @@ -573,6 +578,7 @@ impl<'ctx> BuiltInBuilder<'ctx, Callable> { inherits: Some(intrinsics.constructors().object().prototype()), attributes: Attribute::WRITABLE | Attribute::CONFIGURABLE, }, + intrinsics: intrinsics.clone(), }, prototype: intrinsics.constructors().function().prototype(), } @@ -587,6 +593,7 @@ impl<'ctx> BuiltInBuilder<'ctx, Callable> { name: self.kind.name, length: self.kind.length, kind: ConstructorNoProto, + intrinsics: self.intrinsics.clone(), }, prototype: self.prototype, } diff --git a/boa_engine/src/builtins/promise/mod.rs b/boa_engine/src/builtins/promise/mod.rs index cf315c98478..5099690c9d7 100644 --- a/boa_engine/src/builtins/promise/mod.rs +++ b/boa_engine/src/builtins/promise/mod.rs @@ -105,25 +105,6 @@ pub struct ResolvingFunctions { pub reject: JsFunction, } -/// The internal `PromiseCapability` data type. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// -/// [spec]: https://tc39.es/ecma262/#sec-promisecapability-records -#[derive(Debug, Clone, Trace, Finalize)] -// TODO: make crate-only -pub struct PromiseCapability { - /// The `[[Promise]]` field. - promise: JsObject, - - /// The `[[Resolve]]` field. - resolve: JsFunction, - - /// The `[[Reject]]` field. - reject: JsFunction, -} - // ==================== Private API ==================== /// `IfAbruptRejectPromise ( value, capability )` @@ -156,6 +137,24 @@ macro_rules! if_abrupt_reject_promise { pub(crate) use if_abrupt_reject_promise; +/// The internal `PromiseCapability` data type. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-promisecapability-records +#[derive(Debug, Clone, Trace, Finalize)] +pub(crate) struct PromiseCapability { + /// The `[[Promise]]` field. + promise: JsObject, + + /// The `[[Resolve]]` field. + resolve: JsFunction, + + /// The `[[Reject]]` field. + reject: JsFunction, +} + /// The internal `PromiseReaction` data type. /// /// More information: diff --git a/boa_engine/src/builtins/regexp/mod.rs b/boa_engine/src/builtins/regexp/mod.rs index 1434b966854..77bdbe8c95b 100644 --- a/boa_engine/src/builtins/regexp/mod.rs +++ b/boa_engine/src/builtins/regexp/mod.rs @@ -327,10 +327,7 @@ impl RegExp { /// [spec]: https://tc39.es/ecma262/#sec-regexpcreate pub(crate) fn create(p: &JsValue, f: &JsValue, context: &mut Context<'_>) -> JsResult { // 1. Let obj be ? RegExpAlloc(%RegExp%). - let obj = Self::alloc( - &context.global_object().get(Self::NAME, context)?, - context, - )?; + let obj = Self::alloc(&context.global_object().get(Self::NAME, context)?, context)?; // 2. Return ? RegExpInitialize(obj, P, F). Self::initialize(obj, p, f, context) diff --git a/boa_engine/src/builtins/uri/mod.rs b/boa_engine/src/builtins/uri/mod.rs index e8350e044bb..1144140d6e4 100644 --- a/boa_engine/src/builtins/uri/mod.rs +++ b/boa_engine/src/builtins/uri/mod.rs @@ -12,6 +12,8 @@ mod consts; +use boa_gc::{Finalize, Trace}; + use self::consts::{ is_uri_reserved_or_number_sign, is_uri_reserved_or_uri_unescaped_or_number_sign, is_uri_unescaped, @@ -29,7 +31,7 @@ use crate::{ /// Intrinsics for the [`URI Handling Functions`][spec]. /// /// [spec]: https://tc39.es/ecma262/multipage/global-object.html#sec-uri-handling-functions -#[derive(Debug)] +#[derive(Debug, Trace, Finalize)] pub struct UriFunctions { /// %decodeURI% decode_uri: JsFunction, diff --git a/boa_engine/src/class.rs b/boa_engine/src/class.rs index bedcbec070f..2d3f64d196a 100644 --- a/boa_engine/src/class.rs +++ b/boa_engine/src/class.rs @@ -175,7 +175,7 @@ impl<'ctx, 'host> ClassBuilder<'ctx, 'host> { Self { builder } } - pub(crate) fn build(mut self) -> JsFunction { + pub(crate) fn build(self) -> JsFunction { JsFunction::from_object_unchecked(self.builder.build().into()) } diff --git a/boa_engine/src/context/intrinsics.rs b/boa_engine/src/context/intrinsics.rs index a301d66e9ff..df4ba5096dc 100644 --- a/boa_engine/src/context/intrinsics.rs +++ b/boa_engine/src/context/intrinsics.rs @@ -1,13 +1,32 @@ //! Data structures that contain intrinsic objects and constructors. +use boa_gc::{Finalize, Gc, Trace}; + use crate::{ builtins::{iterable::IteratorPrototypes, uri::UriFunctions}, object::{JsFunction, JsObject, ObjectData}, }; /// The intrinsic objects and constructors. -#[derive(Debug, Default)] +/// +/// `Intrinsics` is internally stored using a `Gc`, which makes it cheapily clonable +/// for multiple references to the same set of intrinsic objects. +#[derive(Default, Clone, Trace, Finalize)] pub struct Intrinsics { + inner: Gc, +} + +impl std::fmt::Debug for Intrinsics { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Intrinsics") + .field("constructors", self.constructors()) + .field("objects", self.objects()) + .finish() + } +} + +#[derive(Default, Trace, Finalize)] +struct Inner { /// Cached standard constructors pub(super) constructors: StandardConstructors, /// Cached intrinsic objects @@ -17,19 +36,19 @@ pub struct Intrinsics { impl Intrinsics { /// Return the cached intrinsic objects. #[inline] - pub const fn objects(&self) -> &IntrinsicObjects { - &self.objects + pub fn objects(&self) -> &IntrinsicObjects { + &self.inner.objects } /// Return the cached standard constructors. #[inline] - pub const fn constructors(&self) -> &StandardConstructors { - &self.constructors + pub fn constructors(&self) -> &StandardConstructors { + &self.inner.constructors } } /// Store a builtin constructor (such as `Object`) and its corresponding prototype. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Trace, Finalize)] pub struct StandardConstructor { pub(crate) constructor: JsObject, pub(crate) prototype: JsObject, @@ -71,7 +90,7 @@ impl StandardConstructor { } /// Cached core standard constructors. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Trace, Finalize)] pub struct StandardConstructors { object: StandardConstructor, proxy: StandardConstructor, @@ -720,7 +739,7 @@ impl StandardConstructors { } /// Cached intrinsic objects -#[derive(Debug)] +#[derive(Debug, Trace, Finalize)] pub struct IntrinsicObjects { /// [`%Reflect%`](https://tc39.es/ecma262/#sec-reflect) reflect: JsObject, diff --git a/boa_engine/src/context/mod.rs b/boa_engine/src/context/mod.rs index 49d1b1b5438..04b3ec1f1a0 100644 --- a/boa_engine/src/context/mod.rs +++ b/boa_engine/src/context/mod.rs @@ -423,7 +423,6 @@ impl Context<'_> { .configurable(T::ATTRIBUTES.configurable()); self.global_object() - .define_property_or_throw(T::NAME, property, self)?; Ok(()) @@ -441,13 +440,13 @@ impl Context<'_> { &mut self.interner } - /// Return the global object. + /// Returns the global object. #[inline] pub fn global_object(&self) -> JsObject { self.realm.global_object().clone() } - /// Return the intrinsic constructors and objects. + /// Returns the intrinsic constructors and objects. #[inline] pub const fn intrinsics(&self) -> &Intrinsics { &self.realm.intrinsics diff --git a/boa_engine/src/environments/compile.rs b/boa_engine/src/environments/compile.rs index 9d41aca9011..49b2ec9fea6 100644 --- a/boa_engine/src/environments/compile.rs +++ b/boa_engine/src/environments/compile.rs @@ -296,17 +296,15 @@ impl Context<'_> { .has_own_property(name_str.clone(), self) .unwrap_or_default() { - self.global_object() - .borrow_mut() - .insert( - name_str, - PropertyDescriptor::builder() - .value(JsValue::Undefined) - .writable(true) - .enumerable(true) - .configurable(configurable) - .build(), - ); + self.global_object().borrow_mut().insert( + name_str, + PropertyDescriptor::builder() + .value(JsValue::Undefined) + .writable(true) + .enumerable(true) + .configurable(configurable) + .build(), + ); } } } diff --git a/boa_engine/src/environments/runtime.rs b/boa_engine/src/environments/runtime.rs index 5f5d6c4af57..5647a1df97b 100644 --- a/boa_engine/src/environments/runtime.rs +++ b/boa_engine/src/environments/runtime.rs @@ -222,7 +222,7 @@ impl DeclarativeEnvironment { /// Environments themselves are garbage collected, /// because they must be preserved for function calls. #[derive(Clone, Debug, Trace, Finalize)] -pub struct DeclarativeEnvironmentStack { +pub(crate) struct DeclarativeEnvironmentStack { stack: Vec, } diff --git a/boa_engine/src/object/internal_methods/mod.rs b/boa_engine/src/object/internal_methods/mod.rs index cc890f66312..131d12e1e83 100644 --- a/boa_engine/src/object/internal_methods/mod.rs +++ b/boa_engine/src/object/internal_methods/mod.rs @@ -7,7 +7,7 @@ use super::{JsPrototype, PROTOTYPE}; use crate::{ - context::intrinsics::{StandardConstructor, StandardConstructors}, + context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, object::JsObject, property::{DescriptorKind, PropertyDescriptor, PropertyKey}, value::JsValue, @@ -937,14 +937,37 @@ where // The corresponding object must be an intrinsic that is intended to be used // as the [[Prototype]] value of an object. // 2. Let proto be ? Get(constructor, "prototype"). - if let Some(object) = constructor.as_object() { + let intrinsics = if let Some(object) = constructor.as_object() { if let Some(proto) = object.get(PROTOTYPE, context)?.as_object() { return Ok(proto.clone()); } + // 3. If Type(proto) is not Object, then + // a. Let realm be ? GetFunctionRealm(constructor). + // b. Set proto to realm's intrinsic object named intrinsicDefaultProto. + get_function_realm(object, context)? + } else { + context.intrinsics().clone() + }; + Ok(default(intrinsics.constructors()).prototype()) +} + +fn get_function_realm(constructor: &JsObject, context: &mut Context<'_>) -> JsResult { + let constructor = constructor.borrow(); + if let Some(fun) = constructor.as_function() { + return Ok(fun.realm_intrinsics().clone()); } - // 3. If Type(proto) is not Object, then - // TODO: handle realms - // a. Let realm be ? GetFunctionRealm(constructor). - // b. Set proto to realm's intrinsic object named intrinsicDefaultProto. - Ok(default(context.intrinsics().constructors()).prototype()) + + if let Some(bound) = constructor.as_bound_function() { + let fun = bound.target_function().clone(); + drop(constructor); + return get_function_realm(&fun, context); + } + + if let Some(proxy) = constructor.as_proxy() { + let (fun, _) = proxy.try_data()?; + drop(constructor); + return get_function_realm(&fun, context); + } + + Ok(context.intrinsics().clone()) } diff --git a/boa_engine/src/object/jsobject.rs b/boa_engine/src/object/jsobject.rs index 7ded2daa773..ddc67a370ca 100644 --- a/boa_engine/src/object/jsobject.rs +++ b/boa_engine/src/object/jsobject.rs @@ -4,12 +4,13 @@ use super::{JsPrototype, NativeObject, Object, PropertyMap}; use crate::{ + context::intrinsics::Intrinsics, error::JsNativeError, object::{ObjectData, ObjectKind}, property::{PropertyDescriptor, PropertyKey}, string::utf16, value::PreferredType, - Context, JsResult, JsValue, context::intrinsics::Intrinsics, + Context, JsResult, JsValue, }; use boa_gc::{self, Finalize, Gc, GcRefCell, Trace}; use std::{ diff --git a/boa_engine/src/object/mod.rs b/boa_engine/src/object/mod.rs index 6c6406d9758..1f3211b24a0 100644 --- a/boa_engine/src/object/mod.rs +++ b/boa_engine/src/object/mod.rs @@ -33,7 +33,7 @@ use crate::{ array_buffer::ArrayBuffer, async_generator::AsyncGenerator, error::ErrorKind, - function::arguments::Arguments, + function::{arguments::Arguments, FunctionKind}, function::{arguments::ParameterMap, BoundFunction, ConstructorKind, Function}, generator::Generator, iterable::AsyncFromSyncIterator, @@ -1938,7 +1938,8 @@ where #[derive(Debug)] pub struct FunctionObjectBuilder<'ctx, 'host> { context: &'ctx mut Context<'host>, - function: Function, + function: NativeFunction, + constructor: Option, name: JsString, length: usize, } @@ -1949,10 +1950,8 @@ impl<'ctx, 'host> FunctionObjectBuilder<'ctx, 'host> { pub fn new(context: &'ctx mut Context<'host>, function: NativeFunction) -> Self { Self { context, - function: Function::Native { - function, - constructor: None, - }, + function, + constructor: None, name: js_string!(), length: 0, } @@ -1987,20 +1986,7 @@ impl<'ctx, 'host> FunctionObjectBuilder<'ctx, 'host> { /// The default is `false`. #[must_use] pub fn constructor(mut self, yes: bool) -> Self { - match self.function { - Function::Native { - ref mut constructor, - .. - } => { - *constructor = yes.then_some(ConstructorKind::Base); - } - Function::Ordinary { .. } - | Function::Generator { .. } - | Function::AsyncGenerator { .. } - | Function::Async { .. } => { - unreachable!("function must be native or closure"); - } - } + self.constructor = yes.then_some(ConstructorKind::Base); self } @@ -2012,7 +1998,13 @@ impl<'ctx, 'host> FunctionObjectBuilder<'ctx, 'host> { .constructors() .function() .prototype(), - ObjectData::function(self.function), + ObjectData::function(Function::new( + FunctionKind::Native { + function: self.function, + constructor: self.constructor, + }, + self.context.intrinsics().clone(), + )), ); let property = PropertyDescriptor::builder() .writable(false) @@ -2373,12 +2365,15 @@ impl<'ctx, 'host> ConstructorBuilder<'ctx, 'host> { } /// Build the constructor function object. - pub fn build(&mut self) -> JsFunction { + pub fn build(mut self) -> JsFunction { // Create the native function - let function = Function::Native { - function: self.function.clone(), - constructor: self.constructor, - }; + let function = Function::new( + FunctionKind::Native { + function: self.function, + constructor: self.constructor, + }, + self.context.intrinsics().clone(), + ); let length = PropertyDescriptor::builder() .value(self.length) @@ -2445,6 +2440,6 @@ impl<'ctx, 'host> ConstructorBuilder<'ctx, 'host> { } } - JsFunction::from_object_unchecked(self.object.clone()) + JsFunction::from_object_unchecked(self.object) } } diff --git a/boa_engine/src/vm/code_block.rs b/boa_engine/src/vm/code_block.rs index f7a721585f6..bec0a15294c 100644 --- a/boa_engine/src/vm/code_block.rs +++ b/boa_engine/src/vm/code_block.rs @@ -5,7 +5,7 @@ use crate::{ builtins::{ async_generator::{AsyncGenerator, AsyncGeneratorState}, - function::{arguments::Arguments, ConstructorKind, Function, ThisMode}, + function::{arguments::Arguments, ConstructorKind, Function, FunctionKind, ThisMode}, generator::{Generator, GeneratorContext, GeneratorState}, promise::PromiseCapability, }, @@ -581,23 +581,29 @@ pub(crate) fn create_function_object( ) .expect("cannot fail per spec"); - Function::Async { - code, - environments: context.realm.environments.clone(), - home_object: None, - promise_capability, - class_object: None, - } + Function::new( + FunctionKind::Async { + code, + environments: context.realm.environments.clone(), + home_object: None, + promise_capability, + class_object: None, + }, + context.intrinsics().clone(), + ) } else { - Function::Ordinary { - code, - environments: context.realm.environments.clone(), - constructor_kind: ConstructorKind::Base, - home_object: None, - fields: ThinVec::new(), - private_methods: ThinVec::new(), - class_object: None, - } + Function::new( + FunctionKind::Ordinary { + code, + environments: context.realm.environments.clone(), + constructor_kind: ConstructorKind::Base, + home_object: None, + fields: ThinVec::new(), + private_methods: ThinVec::new(), + class_object: None, + }, + context.intrinsics().clone(), + ) }; let constructor = @@ -687,23 +693,29 @@ pub(crate) fn create_generator_function_object( ); let constructor = if r#async { - let function = Function::AsyncGenerator { - code, - environments: context.realm.environments.clone(), - home_object: None, - class_object: None, - }; + let function = Function::new( + FunctionKind::AsyncGenerator { + code, + environments: context.realm.environments.clone(), + home_object: None, + class_object: None, + }, + context.intrinsics().clone(), + ); JsObject::from_proto_and_data( function_prototype, ObjectData::async_generator_function(function), ) } else { - let function = Function::Generator { - code, - environments: context.realm.environments.clone(), - home_object: None, - class_object: None, - }; + let function = Function::new( + FunctionKind::Generator { + code, + environments: context.realm.environments.clone(), + home_object: None, + class_object: None, + }, + context.intrinsics().clone(), + ); JsObject::from_proto_and_data(function_prototype, ObjectData::generator_function(function)) }; @@ -747,8 +759,8 @@ impl JsObject { let object = self.borrow(); let function_object = object.as_function().expect("not a function"); - match function_object { - Function::Native { + match function_object.kind() { + FunctionKind::Native { function, constructor, } => { @@ -762,7 +774,7 @@ impl JsObject { function.call(this, args, context) } } - Function::Ordinary { + FunctionKind::Ordinary { code, environments, class_object, @@ -888,7 +900,7 @@ impl JsObject { record.consume() } - Function::Async { + FunctionKind::Async { code, environments, promise_capability, @@ -1010,7 +1022,7 @@ impl JsObject { Ok(promise.into()) } - Function::Generator { + FunctionKind::Generator { code, environments, class_object, @@ -1148,7 +1160,7 @@ impl JsObject { Ok(generator.into()) } - Function::AsyncGenerator { + FunctionKind::AsyncGenerator { code, environments, class_object, @@ -1325,8 +1337,8 @@ impl JsObject { let object = self.borrow(); let function_object = object.as_function().expect("not a function"); - match function_object { - Function::Native { + match function_object.kind() { + FunctionKind::Native { function, constructor, .. @@ -1350,7 +1362,7 @@ impl JsObject { } } } - Function::Ordinary { + FunctionKind::Ordinary { code, environments, constructor_kind, @@ -1495,9 +1507,9 @@ impl JsObject { .clone()) } } - Function::Generator { .. } - | Function::Async { .. } - | Function::AsyncGenerator { .. } => { + FunctionKind::Generator { .. } + | FunctionKind::Async { .. } + | FunctionKind::AsyncGenerator { .. } => { unreachable!("not a constructor") } } diff --git a/boa_engine/src/vm/opcode/call/mod.rs b/boa_engine/src/vm/opcode/call/mod.rs index 969f316cef2..8cb582af5af 100644 --- a/boa_engine/src/vm/opcode/call/mod.rs +++ b/boa_engine/src/vm/opcode/call/mod.rs @@ -1,5 +1,5 @@ use crate::{ - builtins::function::Function, + builtins::function::FunctionKind, error::JsNativeError, vm::{opcode::Operation, CompletionType}, Context, JsResult, JsValue, @@ -42,7 +42,11 @@ impl Operation for CallEval { }; // A native function with the name "eval" implies, that is this the built-in eval function. - let eval = matches!(object.borrow().as_function(), Some(Function::Native { .. })); + let eval = object + .borrow() + .as_function() + .map(|f| matches!(f.kind(), FunctionKind::Native { .. })) + .unwrap_or_default(); let strict = context.vm.frame().code_block.strict; @@ -104,7 +108,11 @@ impl Operation for CallEvalSpread { }; // A native function with the name "eval" implies, that is this the built-in eval function. - let eval = matches!(object.borrow().as_function(), Some(Function::Native { .. })); + let eval = object + .borrow() + .as_function() + .map(|f| matches!(f.kind(), FunctionKind::Native { .. })) + .unwrap_or_default(); let strict = context.vm.frame().code_block.strict; diff --git a/boa_engine/src/vm/opcode/define/mod.rs b/boa_engine/src/vm/opcode/define/mod.rs index f68cb2183aa..5f0bd367191 100644 --- a/boa_engine/src/vm/opcode/define/mod.rs +++ b/boa_engine/src/vm/opcode/define/mod.rs @@ -26,7 +26,7 @@ impl Operation for DefVar { let binding_locator = context.vm.frame().code_block.bindings[index as usize]; if binding_locator.is_global() { - // already initialized at compilation time + // already initialized at compile time } else { context.realm.environments.put_value_if_uninitialized( binding_locator.environment_index(), @@ -64,7 +64,12 @@ impl Operation for DefInitVar { .interner() .resolve_expect(binding_locator.name().sym()) .into_common::(false); - context.global_object().set(key, value, true, context)?; + context.global_object().set( + key, + value, + context.vm.frame().code_block.strict, + context, + )?; } } else { context.realm.environments.put_value( diff --git a/boa_engine/src/vm/opcode/push/class/mod.rs b/boa_engine/src/vm/opcode/push/class/mod.rs index 418705a1897..0c5438a1b30 100644 --- a/boa_engine/src/vm/opcode/push/class/mod.rs +++ b/boa_engine/src/vm/opcode/push/class/mod.rs @@ -1,5 +1,5 @@ use crate::{ - builtins::function::{ConstructorKind, Function}, + builtins::function::{ConstructorKind, FunctionKind}, error::JsNativeError, object::PROTOTYPE, vm::{opcode::Operation, CompletionType}, @@ -43,9 +43,9 @@ impl Operation for PushClassPrototype { let class_function = class_object_mut .as_function_mut() .expect("class must be function object"); - if let Function::Ordinary { + if let FunctionKind::Ordinary { constructor_kind, .. - } = class_function + } = class_function.kind_mut() { *constructor_kind = ConstructorKind::Derived; } diff --git a/boa_examples/src/bin/closures.rs b/boa_examples/src/bin/closures.rs index 94843312d2b..eb4b89705f8 100644 --- a/boa_examples/src/bin/closures.rs +++ b/boa_examples/src/bin/closures.rs @@ -22,19 +22,21 @@ fn main() -> Result<(), JsError> { let variable = 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1; // We register a global closure function that has the name 'closure' with length 0. - context.register_global_callable( - "closure", - 0, - NativeFunction::from_copy_closure(move |_, _, _| { - println!("Called `closure`"); - // `variable` is captured from the main function. - println!("variable = {variable}"); - println!(); - - // We return the moved variable as a `JsValue`. - Ok(JsValue::new(variable)) - }), - ).unwrap(); + context + .register_global_callable( + "closure", + 0, + NativeFunction::from_copy_closure(move |_, _, _| { + println!("Called `closure`"); + // `variable` is captured from the main function. + println!("variable = {variable}"); + println!(); + + // We return the moved variable as a `JsValue`. + Ok(JsValue::new(variable)) + }), + ) + .unwrap(); assert_eq!( context.eval_script(Source::from_bytes("closure()"))?, @@ -109,15 +111,17 @@ fn main() -> Result<(), JsError> { .build(); // We bind the newly constructed closure as a global property in Javascript. - context.register_global_property( - // We set the key to access the function the same as its name for - // consistency, but it may be different if needed. - "createMessage", - // We pass `js_function` as a property value. - js_function, - // We assign to the "createMessage" property the desired attributes. - Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ).unwrap(); + context + .register_global_property( + // We set the key to access the function the same as its name for + // consistency, but it may be different if needed. + "createMessage", + // We pass `js_function` as a property value. + js_function, + // We assign to the "createMessage" property the desired attributes. + Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, + ) + .unwrap(); assert_eq!( context.eval_script(Source::from_bytes("createMessage()"))?, @@ -142,32 +146,32 @@ fn main() -> Result<(), JsError> { let numbers = RefCell::new(Vec::new()); // We register a global closure that is not `Copy`. - context.register_global_callable( - "enumerate", - 0, - // Note that it is required to use `unsafe` code, since the compiler cannot verify that the - // types captured by the closure are not traceable. - unsafe { - NativeFunction::from_closure(move |_, _, context| { - println!("Called `enumerate`"); - // `index` is captured from the main function. - println!("index = {}", index.get()); - println!(); - - numbers.borrow_mut().push(index.get()); - index.set(index.get() + 1); - - // We return the moved variable as a `JsValue`. - Ok( - JsArray::from_iter( + context + .register_global_callable( + "enumerate", + 0, + // Note that it is required to use `unsafe` code, since the compiler cannot verify that the + // types captured by the closure are not traceable. + unsafe { + NativeFunction::from_closure(move |_, _, context| { + println!("Called `enumerate`"); + // `index` is captured from the main function. + println!("index = {}", index.get()); + println!(); + + numbers.borrow_mut().push(index.get()); + index.set(index.get() + 1); + + // We return the moved variable as a `JsValue`. + Ok(JsArray::from_iter( numbers.borrow().iter().cloned().map(JsValue::from), context, ) - .into(), - ) - }) - }, - ).unwrap(); + .into()) + }) + }, + ) + .unwrap(); // First call should return the array `[0]`. let result = context.eval_script(Source::from_bytes("enumerate()"))?; diff --git a/boa_examples/src/bin/futures.rs b/boa_examples/src/bin/futures.rs index b2cd01cda4e..bbd9b04c2b0 100644 --- a/boa_examples/src/bin/futures.rs +++ b/boa_examples/src/bin/futures.rs @@ -135,7 +135,9 @@ fn main() { let context = &mut ContextBuilder::new().job_queue(&queue).build().unwrap(); // Bind the defined async function to the ECMAScript function "delay". - context.register_global_builtin_callable("delay", 1, NativeFunction::from_async_fn(delay)).unwrap(); + context + .register_global_builtin_callable("delay", 1, NativeFunction::from_async_fn(delay)) + .unwrap(); // Multiple calls to multiple async timers. let script = r#" diff --git a/boa_examples/src/bin/jsarray.rs b/boa_examples/src/bin/jsarray.rs index 2af06ea9d87..7088d18d83a 100644 --- a/boa_examples/src/bin/jsarray.rs +++ b/boa_examples/src/bin/jsarray.rs @@ -113,7 +113,6 @@ fn main() -> JsResult<()> { context .global_object() - .set("myArray", array, true, context)?; Ok(()) diff --git a/boa_examples/src/bin/jsarraybuffer.rs b/boa_examples/src/bin/jsarraybuffer.rs index 56e4e6703f1..53aaf2792b8 100644 --- a/boa_examples/src/bin/jsarraybuffer.rs +++ b/boa_examples/src/bin/jsarraybuffer.rs @@ -50,11 +50,13 @@ fn main() -> JsResult<()> { assert_eq!(second_byte, 2_u8); // We can also register it as a global property - context.register_global_property( - "myArrayBuffer", - array_buffer, - Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, - ).unwrap(); + context + .register_global_property( + "myArrayBuffer", + array_buffer, + Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, + ) + .unwrap(); // We can also take the inner data from a JsArrayBuffer let data_block: Vec = (0..5).collect(); diff --git a/boa_examples/src/bin/jstypedarray.rs b/boa_examples/src/bin/jstypedarray.rs index 00682e16527..e5c31611c23 100644 --- a/boa_examples/src/bin/jstypedarray.rs +++ b/boa_examples/src/bin/jstypedarray.rs @@ -40,11 +40,13 @@ fn main() -> JsResult<()> { JsValue::new(sum) ); - context.register_global_property( - "myUint8Array", - array, - Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, - ).unwrap(); + context + .register_global_property( + "myUint8Array", + array, + Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, + ) + .unwrap(); Ok(()) } diff --git a/boa_tester/src/exec/js262.rs b/boa_tester/src/exec/js262.rs index 6e9b9cb1076..ae43ca7bbac 100644 --- a/boa_tester/src/exec/js262.rs +++ b/boa_tester/src/exec/js262.rs @@ -26,11 +26,13 @@ pub(super) fn register_js262(context: &mut Context<'_>) -> JsObject { // .property("agent", agent, Attribute::default()) .build(); - context.register_global_property( - "$262", - js262.clone(), - Attribute::WRITABLE | Attribute::CONFIGURABLE, - ).expect("shouldn't fail with the default global"); + context + .register_global_property( + "$262", + js262.clone(), + Attribute::WRITABLE | Attribute::CONFIGURABLE, + ) + .expect("shouldn't fail with the default global"); js262 } diff --git a/boa_tester/src/exec/mod.rs b/boa_tester/src/exec/mod.rs index 08be3359302..c12c2cc3f41 100644 --- a/boa_tester/src/exec/mod.rs +++ b/boa_tester/src/exec/mod.rs @@ -492,11 +492,13 @@ fn register_print_fn(context: &mut Context<'_>, async_result: AsyncResult) { .length(1) .build(); - context.register_global_property( - "print", - js_function, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ).expect("shouldn't fail with the default global"); + context + .register_global_property( + "print", + js_function, + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, + ) + .expect("shouldn't fail with the default global"); } /// A `Result` value that is possibly uninitialized.