diff --git a/boa_engine/src/builtins/proxy/mod.rs b/boa_engine/src/builtins/proxy/mod.rs index 3532851326a..91f58fa2721 100644 --- a/boa_engine/src/builtins/proxy/mod.rs +++ b/boa_engine/src/builtins/proxy/mod.rs @@ -18,9 +18,6 @@ use crate::{ use boa_gc::{Finalize, Trace}; use boa_profiler::Profiler; use tap::{Conv, Pipe}; - -use super::function::NativeFunctionSignature; - /// Javascript `Proxy` object. #[derive(Debug, Clone, Trace, Finalize)] pub struct Proxy { @@ -52,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)), } @@ -93,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") @@ -125,7 +126,7 @@ impl Proxy { Ok(p) } - fn revoker(proxy: JsObject, context: &mut Context) -> JsFunction { + pub(crate) fn revoker(proxy: JsObject, context: &mut Context) -> JsFunction { // 3. Let revoker be ! CreateBuiltinFunction(revokerClosure, 0, "", « [[RevocableProxy]] »). // 4. Set revoker.[[RevocableProxy]] to p. FunctionBuilder::closure_with_captures( @@ -183,389 +184,3 @@ impl Proxy { Ok(result.into()) } } - -/// Utility builder to create [`Proxy`] 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 JS counterpart. -#[must_use] -#[derive(Clone)] -pub struct ProxyBuilder { - 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 ProxyBuilder { - 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 ProxyBuilder { - /// Create a new `ProxyBuilder` structure 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) -> JsObject { - 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), - ); - - 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) -> (JsObject, JsFunction) { - let proxy = self.build(context); - let revoker = Proxy::revoker(proxy.clone(), context); - - (proxy, revoker) - } -} diff --git a/boa_engine/src/object/jsproxy.rs b/boa_engine/src/object/jsproxy.rs new file mode 100644 index 00000000000..5987da80ebc --- /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 5b524d9ec34..efae88a17b5 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: