diff --git a/boa_engine/src/builtins/function/mod.rs b/boa_engine/src/builtins/function/mod.rs index cc4af912b7..2fc49995a7 100644 --- a/boa_engine/src/builtins/function/mod.rs +++ b/boa_engine/src/builtins/function/mod.rs @@ -44,6 +44,14 @@ mod tests; /// /// Native functions need to have this signature in order to /// be callable from Javascript. +/// +/// # Arguments +/// +/// - The first argument represents the `this` variable of every Javascript function. +/// +/// - The second argument represents a list of all arguments passed to the function. +/// +/// - The last argument is the [`Context`] of the engine. pub type NativeFunctionSignature = fn(&JsValue, &[JsValue], &mut Context) -> JsResult; // Allows restricting closures to only `Copy` ones. diff --git a/boa_engine/src/builtins/proxy/mod.rs b/boa_engine/src/builtins/proxy/mod.rs index 5f580f74bf..91f58fa272 100644 --- a/boa_engine/src/builtins/proxy/mod.rs +++ b/boa_engine/src/builtins/proxy/mod.rs @@ -12,13 +12,12 @@ use crate::{ builtins::{BuiltIn, JsArgs}, - object::{ConstructorBuilder, FunctionBuilder, JsObject, ObjectData}, + object::{ConstructorBuilder, FunctionBuilder, JsFunction, JsObject, ObjectData}, Context, JsResult, JsValue, }; use boa_gc::{Finalize, Trace}; use boa_profiler::Profiler; use tap::{Conv, Pipe}; - /// Javascript `Proxy` object. #[derive(Debug, Clone, Trace, Finalize)] pub struct Proxy { @@ -50,7 +49,7 @@ impl BuiltIn for Proxy { impl Proxy { const LENGTH: usize = 2; - fn new(target: JsObject, handler: JsObject) -> Self { + pub(crate) fn new(target: JsObject, handler: JsObject) -> Self { Self { data: Some((target, handler)), } @@ -82,7 +81,7 @@ impl Proxy { } // 2. Return ? ProxyCreate(target, handler). - Self::create(args.get_or_undefined(0), args.get_or_undefined(1), context) + Self::create(args.get_or_undefined(0), args.get_or_undefined(1), context).map(JsValue::from) } // `10.5.14 ProxyCreate ( target, handler )` @@ -91,7 +90,11 @@ impl Proxy { // - [ECMAScript reference][spec] // // [spec]: https://tc39.es/ecma262/#sec-proxycreate - fn create(target: &JsValue, handler: &JsValue, context: &mut Context) -> JsResult { + pub(crate) fn create( + target: &JsValue, + handler: &JsValue, + context: &mut Context, + ) -> JsResult { // 1. If Type(target) is not Object, throw a TypeError exception. let target = target.as_object().ok_or_else(|| { context.construct_type_error("Proxy constructor called with non-object target") @@ -120,52 +123,49 @@ impl Proxy { ); // 8. Return P. - Ok(p.into()) + Ok(p) } - /// `28.2.2.1 Proxy.revocable ( target, handler )` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - /// [spec]: https://tc39.es/ecma262/#sec-proxy.revocable - fn revocable(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - // 1. Let p be ? ProxyCreate(target, handler). - let p = Self::create(args.get_or_undefined(0), args.get_or_undefined(1), context)?; - + pub(crate) fn revoker(proxy: JsObject, context: &mut Context) -> JsFunction { // 3. Let revoker be ! CreateBuiltinFunction(revokerClosure, 0, "", « [[RevocableProxy]] »). // 4. Set revoker.[[RevocableProxy]] to p. - let revoker = FunctionBuilder::closure_with_captures( + FunctionBuilder::closure_with_captures( context, |_, _, revocable_proxy, _| { // a. Let F be the active function object. // b. Let p be F.[[RevocableProxy]]. - // c. If p is null, return undefined. - if revocable_proxy.is_null() { - return Ok(JsValue::undefined()); - } - - let p = revocable_proxy - .as_object() - .expect("[[RevocableProxy]] must be an object or null"); - - // e. Assert: p is a Proxy object. - // f. Set p.[[ProxyTarget]] to null. - // g. Set p.[[ProxyHandler]] to null. - p.borrow_mut() - .as_proxy_mut() - .expect("[[RevocableProxy]] must be a proxy object") - .data = None; - // d. Set F.[[RevocableProxy]] to null. - *revocable_proxy = JsValue::Null; + if let Some(p) = revocable_proxy.take() { + // e. Assert: p is a Proxy object. + // f. Set p.[[ProxyTarget]] to null. + // g. Set p.[[ProxyHandler]] to null. + p.borrow_mut() + .as_proxy_mut() + .expect("[[RevocableProxy]] must be a proxy object") + .data = None; + } + // c. If p is null, return undefined. // h. Return undefined. Ok(JsValue::undefined()) }, - p.clone(), + Some(proxy), ) - .build(); + .build() + } + + /// `28.2.2.1 Proxy.revocable ( target, handler )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-proxy.revocable + fn revocable(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + // 1. Let p be ? ProxyCreate(target, handler). + let p = Self::create(args.get_or_undefined(0), args.get_or_undefined(1), context)?; + + // Revoker creation steps on `Proxy::revoker` + let revoker = Self::revoker(p.clone(), context); // 5. Let result be ! OrdinaryObjectCreate(%Object.prototype%). let result = context.construct_object(); diff --git a/boa_engine/src/object/internal_methods/bound_function.rs b/boa_engine/src/object/internal_methods/bound_function.rs index 3949cd93d3..d48d4ea3b9 100644 --- a/boa_engine/src/object/internal_methods/bound_function.rs +++ b/boa_engine/src/object/internal_methods/bound_function.rs @@ -11,7 +11,6 @@ use super::{InternalObjectMethods, ORDINARY_INTERNAL_METHODS}; pub(crate) static BOUND_FUNCTION_EXOTIC_INTERNAL_METHODS: InternalObjectMethods = InternalObjectMethods { __call__: Some(bound_function_exotic_call), - __construct__: None, ..ORDINARY_INTERNAL_METHODS }; diff --git a/boa_engine/src/object/internal_methods/proxy.rs b/boa_engine/src/object/internal_methods/proxy.rs index 5b5be57dbf..7ecb7e0d8a 100644 --- a/boa_engine/src/object/internal_methods/proxy.rs +++ b/boa_engine/src/object/internal_methods/proxy.rs @@ -32,36 +32,15 @@ pub(crate) static PROXY_EXOTIC_INTERNAL_METHODS_BASIC: InternalObjectMethods = pub(crate) static PROXY_EXOTIC_INTERNAL_METHODS_WITH_CALL: InternalObjectMethods = InternalObjectMethods { - __get_prototype_of__: proxy_exotic_get_prototype_of, - __set_prototype_of__: proxy_exotic_set_prototype_of, - __is_extensible__: proxy_exotic_is_extensible, - __prevent_extensions__: proxy_exotic_prevent_extensions, - __get_own_property__: proxy_exotic_get_own_property, - __define_own_property__: proxy_exotic_define_own_property, - __has_property__: proxy_exotic_has_property, - __get__: proxy_exotic_get, - __set__: proxy_exotic_set, - __delete__: proxy_exotic_delete, - __own_property_keys__: proxy_exotic_own_property_keys, __call__: Some(proxy_exotic_call), - __construct__: None, + ..PROXY_EXOTIC_INTERNAL_METHODS_BASIC }; pub(crate) static PROXY_EXOTIC_INTERNAL_METHODS_ALL: InternalObjectMethods = InternalObjectMethods { - __get_prototype_of__: proxy_exotic_get_prototype_of, - __set_prototype_of__: proxy_exotic_set_prototype_of, - __is_extensible__: proxy_exotic_is_extensible, - __prevent_extensions__: proxy_exotic_prevent_extensions, - __get_own_property__: proxy_exotic_get_own_property, - __define_own_property__: proxy_exotic_define_own_property, - __has_property__: proxy_exotic_has_property, - __get__: proxy_exotic_get, - __set__: proxy_exotic_set, - __delete__: proxy_exotic_delete, - __own_property_keys__: proxy_exotic_own_property_keys, __call__: Some(proxy_exotic_call), __construct__: Some(proxy_exotic_construct), + ..PROXY_EXOTIC_INTERNAL_METHODS_BASIC }; /// `10.5.1 [[GetPrototypeOf]] ( )` diff --git a/boa_engine/src/object/jsproxy.rs b/boa_engine/src/object/jsproxy.rs new file mode 100644 index 0000000000..5987da80eb --- /dev/null +++ b/boa_engine/src/object/jsproxy.rs @@ -0,0 +1,477 @@ +use boa_gc::{Finalize, Trace}; + +use crate::{ + builtins::{function::NativeFunctionSignature, Proxy}, + Context, JsResult, JsValue, +}; + +use super::{FunctionBuilder, JsFunction, JsObject, JsObjectType, ObjectData}; + +/// JavaScript [`Proxy`][proxy] rust object. +/// +/// This is a wrapper type for the [`Proxy`][proxy] API that allows customizing +/// essential behaviour for an object, like [property accesses][get] or the +/// [`delete`][delete] operator. +/// +/// The only way to construct this type is to use the [`JsProxyBuilder`] type; also +/// accessible from [`JsProxy::builder`]. +/// +/// [get]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/get +/// [delete]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/deleteProperty +/// [proxy]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy +#[derive(Debug, Clone, Trace, Finalize)] +pub struct JsProxy { + inner: JsObject, +} + +impl JsProxy { + pub fn builder(target: JsObject) -> JsProxyBuilder { + JsProxyBuilder::new(target) + } +} + +impl From for JsObject { + #[inline] + fn from(o: JsProxy) -> Self { + o.inner.clone() + } +} + +impl From for JsValue { + #[inline] + fn from(o: JsProxy) -> Self { + o.inner.clone().into() + } +} + +impl std::ops::Deref for JsProxy { + type Target = JsObject; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl JsObjectType for JsProxy {} + +/// JavaScript [`Proxy`][proxy] rust object that can be disabled. +/// +/// Safe interface for the [`Proxy.revocable`][revocable] method that creates a +/// proxy that can be disabled using the [`JsRevocableProxy::revoke`] method. +/// The internal proxy is accessible using the [`Deref`][`std::ops::Deref`] operator. +/// +/// The only way to construct this type is to use the [`JsProxyBuilder`] type; also +/// accessible from [`JsProxy::builder`]; with the [`JsProxyBuilder::build_revocable`] +/// method. +/// +/// [revocable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/revocable +#[derive(Debug, Trace, Finalize)] +pub struct JsRevocableProxy { + proxy: JsProxy, + revoker: JsFunction, +} + +impl JsRevocableProxy { + /// Disables the traps of the internal `proxy` object, essentially + /// making it unusable and throwing `TypeError`s for all the traps. + pub fn revoke(self, context: &mut Context) -> JsResult<()> { + self.revoker.call(&JsValue::undefined(), &[], context)?; + Ok(()) + } +} + +impl std::ops::Deref for JsRevocableProxy { + type Target = JsProxy; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.proxy + } +} + +/// Utility builder to create [`JsProxy`] objects from native functions. +/// +/// This builder can be used when you need to create [`Proxy`] objects +/// from Rust instead of Javascript, which should generate faster +/// trap functions than its Javascript counterparts. +#[must_use] +#[derive(Clone)] +pub struct JsProxyBuilder { + target: JsObject, + apply: Option, + construct: Option, + define_property: Option, + delete_property: Option, + get: Option, + get_own_property_descriptor: Option, + get_prototype_of: Option, + has: Option, + is_extensible: Option, + own_keys: Option, + prevent_extensions: Option, + set: Option, + set_prototype_of: Option, +} + +impl std::fmt::Debug for JsProxyBuilder { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + #[derive(Debug)] + struct NativeFunction; + f.debug_struct("ProxyBuilder") + .field("target", &self.target) + .field("apply", &self.apply.map(|_| NativeFunction)) + .field("construct", &self.construct.map(|_| NativeFunction)) + .field( + "define_property", + &self.define_property.map(|_| NativeFunction), + ) + .field( + "delete_property", + &self.delete_property.map(|_| NativeFunction), + ) + .field("get", &self.get.map(|_| NativeFunction)) + .field( + "get_own_property_descriptor", + &self.get_own_property_descriptor.map(|_| NativeFunction), + ) + .field( + "get_prototype_of", + &self.get_prototype_of.map(|_| NativeFunction), + ) + .field("has", &self.has.map(|_| NativeFunction)) + .field("is_extensible", &self.is_extensible.map(|_| NativeFunction)) + .field("own_keys", &self.own_keys.map(|_| NativeFunction)) + .field( + "prevent_extensions", + &self.prevent_extensions.map(|_| NativeFunction), + ) + .field("set", &self.set.map(|_| NativeFunction)) + .field( + "set_prototype_of", + &self.set_prototype_of.map(|_| NativeFunction), + ) + .finish() + } +} + +impl JsProxyBuilder { + /// Create a new `ProxyBuilder` with every trap set to `undefined`. + pub fn new(target: JsObject) -> Self { + Self { + target, + apply: None, + construct: None, + define_property: None, + delete_property: None, + get: None, + get_own_property_descriptor: None, + get_prototype_of: None, + has: None, + is_extensible: None, + own_keys: None, + prevent_extensions: None, + set: None, + set_prototype_of: None, + } + } + + /// Set the `apply` proxy trap to the specified native function. + /// + /// More information: + /// + /// - [MDN documentation][mdn] + /// + /// # Note + /// + /// If the `target` object is not a function, then `apply` will be ignored + /// when trying to call the proxy, which will throw a type error. + /// + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/apply + pub fn apply(mut self, apply: NativeFunctionSignature) -> Self { + self.apply = Some(apply); + self + } + + /// Set the `construct` proxy trap to the specified native function. + /// + /// More information: + /// + /// - [MDN documentation][mdn] + /// + /// # Note + /// + /// If the `target` object is not a constructor, then `construct` will be ignored + /// when trying to construct an object using the proxy, which will throw a type error. + /// + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/construct + pub fn construct(mut self, construct: NativeFunctionSignature) -> Self { + self.construct = Some(construct); + self + } + + /// Set the `defineProperty` proxy trap to the specified native function. + /// + /// More information: + /// + /// - [MDN documentation][mdn] + /// + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/defineProperty + pub fn define_property(mut self, define_property: NativeFunctionSignature) -> Self { + self.define_property = Some(define_property); + self + } + + /// Set the `deleteProperty` proxy trap to the specified native function. + /// + /// More information: + /// + /// - [MDN documentation][mdn] + /// + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/deleteProperty + pub fn delete_property(mut self, delete_property: NativeFunctionSignature) -> Self { + self.delete_property = Some(delete_property); + self + } + + /// Set the `get` proxy trap to the specified native function. + /// + /// More information: + /// + /// - [MDN documentation][mdn] + /// + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/get + pub fn get(mut self, get: NativeFunctionSignature) -> Self { + self.get = Some(get); + self + } + + /// Set the `getOwnPropertyDescriptor` proxy trap to the specified native function. + /// + /// More information: + /// + /// - [MDN documentation][mdn] + /// + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/getOwnPropertyDescriptor + pub fn get_own_property_descriptor( + mut self, + get_own_property_descriptor: NativeFunctionSignature, + ) -> Self { + self.get_own_property_descriptor = Some(get_own_property_descriptor); + self + } + + /// Set the `getPrototypeOf` proxy trap to the specified native function. + /// + /// More information: + /// + /// - [MDN documentation][mdn] + /// + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/getPrototypeOf + pub fn get_prototype_of(mut self, get_prototype_of: NativeFunctionSignature) -> Self { + self.get_prototype_of = Some(get_prototype_of); + self + } + + /// Set the `has` proxy trap to the specified native function. + /// + /// More information: + /// + /// - [MDN documentation][mdn] + /// + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/has + pub fn has(mut self, has: NativeFunctionSignature) -> Self { + self.has = Some(has); + self + } + + /// Set the `isExtensible` proxy trap to the specified native function. + /// + /// More information: + /// + /// - [MDN documentation][mdn] + /// + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/isExtensible + pub fn is_extensible(mut self, is_extensible: NativeFunctionSignature) -> Self { + self.is_extensible = Some(is_extensible); + self + } + + /// Set the `ownKeys` proxy trap to the specified native function. + /// + /// More information: + /// + /// - [MDN documentation][mdn] + /// + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/ownKeys + pub fn own_keys(mut self, own_keys: NativeFunctionSignature) -> Self { + self.own_keys = Some(own_keys); + self + } + + /// Set the `preventExtensions` proxy trap to the specified native function. + /// + /// More information: + /// + /// - [MDN documentation][mdn] + /// + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/preventExtensions + pub fn prevent_extensions(mut self, prevent_extensions: NativeFunctionSignature) -> Self { + self.prevent_extensions = Some(prevent_extensions); + self + } + + /// Set the `set` proxy trap to the specified native function. + /// + /// More information: + /// + /// - [MDN documentation][mdn] + /// + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/set + pub fn set(mut self, set: NativeFunctionSignature) -> Self { + self.set = Some(set); + self + } + + /// Set the `setPrototypeOf` proxy trap to the specified native function. + /// + /// More information: + /// + /// - [MDN documentation][mdn] + /// + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/setPrototypeOf + pub fn set_prototype_of(mut self, set_prototype_of: NativeFunctionSignature) -> Self { + self.set_prototype_of = Some(set_prototype_of); + self + } + + /// Build a [`JsObject`] of kind [`Proxy`]. + /// + /// Equivalent to the `Proxy ( target, handler )` constructor, but returns a + /// [`JsObject`] in case there's a need to manipulate the returned object + /// inside Rust code. + #[must_use] + pub fn build(self, context: &mut Context) -> JsProxy { + let handler = context.construct_object(); + + if let Some(apply) = self.apply { + let f = FunctionBuilder::native(context, apply).length(3).build(); + handler + .create_data_property_or_throw("apply", f, context) + .expect("new object should be writable"); + } + if let Some(construct) = self.construct { + let f = FunctionBuilder::native(context, construct) + .length(3) + .build(); + handler + .create_data_property_or_throw("construct", f, context) + .expect("new object should be writable"); + } + if let Some(define_property) = self.define_property { + let f = FunctionBuilder::native(context, define_property) + .length(3) + .build(); + handler + .create_data_property_or_throw("defineProperty", f, context) + .expect("new object should be writable"); + } + if let Some(delete_property) = self.delete_property { + let f = FunctionBuilder::native(context, delete_property) + .length(2) + .build(); + handler + .create_data_property_or_throw("deleteProperty", f, context) + .expect("new object should be writable"); + } + if let Some(get) = self.get { + let f = FunctionBuilder::native(context, get).length(3).build(); + handler + .create_data_property_or_throw("get", f, context) + .expect("new object should be writable"); + } + if let Some(get_own_property_descriptor) = self.get_own_property_descriptor { + let f = FunctionBuilder::native(context, get_own_property_descriptor) + .length(2) + .build(); + handler + .create_data_property_or_throw("getOwnPropertyDescriptor", f, context) + .expect("new object should be writable"); + } + if let Some(get_prototype_of) = self.get_prototype_of { + let f = FunctionBuilder::native(context, get_prototype_of) + .length(1) + .build(); + handler + .create_data_property_or_throw("getPrototypeOf", f, context) + .expect("new object should be writable"); + } + if let Some(has) = self.has { + let f = FunctionBuilder::native(context, has).length(2).build(); + handler + .create_data_property_or_throw("has", f, context) + .expect("new object should be writable"); + } + if let Some(is_extensible) = self.is_extensible { + let f = FunctionBuilder::native(context, is_extensible) + .length(1) + .build(); + handler + .create_data_property_or_throw("isExtensible", f, context) + .expect("new object should be writable"); + } + if let Some(own_keys) = self.own_keys { + let f = FunctionBuilder::native(context, own_keys).length(1).build(); + handler + .create_data_property_or_throw("ownKeys", f, context) + .expect("new object should be writable"); + } + if let Some(prevent_extensions) = self.prevent_extensions { + let f = FunctionBuilder::native(context, prevent_extensions) + .length(1) + .build(); + handler + .create_data_property_or_throw("preventExtensions", f, context) + .expect("new object should be writable"); + } + if let Some(set) = self.set { + let f = FunctionBuilder::native(context, set).length(4).build(); + handler + .create_data_property_or_throw("set", f, context) + .expect("new object should be writable"); + } + if let Some(set_prototype_of) = self.set_prototype_of { + let f = FunctionBuilder::native(context, set_prototype_of) + .length(2) + .build(); + handler + .create_data_property_or_throw("setPrototypeOf", f, context) + .expect("new object should be writable"); + } + + let callable = self.target.is_callable(); + let constructor = self.target.is_constructor(); + + let proxy = JsObject::from_proto_and_data( + context.intrinsics().constructors().object().prototype(), + ObjectData::proxy(Proxy::new(self.target, handler), callable, constructor), + ); + + JsProxy { inner: proxy } + } + + /// Builds a [`JsObject`] of kind [`Proxy`] and a [`JsFunction`] that, when + /// called, disables the proxy of the object. + /// + /// Equivalent to the `Proxy.revocable ( target, handler )` static method, + /// but returns a [`JsObject`] for the proxy and a [`JsFunction`] for the + /// revoker in case there's a need to manipulate the returned objects + /// inside Rust code. + #[must_use] + pub fn build_revocable(self, context: &mut Context) -> JsRevocableProxy { + let proxy = self.build(context); + let revoker = Proxy::revoker(proxy.inner.clone(), context); + + JsRevocableProxy { proxy, revoker } + } +} diff --git a/boa_engine/src/object/mod.rs b/boa_engine/src/object/mod.rs index 5b524d9ec3..efae88a17b 100644 --- a/boa_engine/src/object/mod.rs +++ b/boa_engine/src/object/mod.rs @@ -61,12 +61,14 @@ pub(crate) mod internal_methods; mod jsarray; mod jsfunction; mod jsobject; +mod jsproxy; mod jstypedarray; mod operations; mod property_map; pub use jsarray::*; pub use jsfunction::*; +pub use jsproxy::*; pub use jstypedarray::*; pub(crate) trait JsObjectType: diff --git a/boa_engine/src/string.rs b/boa_engine/src/string.rs index 7b4eb83722..d0d0b8c2a9 100644 --- a/boa_engine/src/string.rs +++ b/boa_engine/src/string.rs @@ -626,7 +626,7 @@ impl Inner { /// The `JsString` length and data is stored on the heap. and just an non-null /// pointer is kept, so its size is the size of a pointer. /// -/// We define some commonly used string constants in [`CONSTANTS_ARRAY`]. For these +/// We define some commonly used string constants in an interner. For these /// strings, we no longer allocate memory on the heap to reduce the overhead of /// memory allocation and reference counting. #[derive(Finalize)]