Skip to content

Commit

Permalink
Implement ProxyBuilder (#2076)
Browse files Browse the repository at this point in the history
This Pull Request fixes/closes #2075.

It changes the following:

- Implements a `ProxyBuilder` struct to be able to create `Proxy` objects from native function traps.
- Documents `ProxyBuilder` and adjusts some documentation.
- Fixes a clippy lint.
  • Loading branch information
jedel1043 committed May 21, 2022
1 parent 2933022 commit 406c642
Show file tree
Hide file tree
Showing 7 changed files with 527 additions and 62 deletions.
8 changes: 8 additions & 0 deletions boa_engine/src/builtins/function/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<JsValue>;

// Allows restricting closures to only `Copy` ones.
Expand Down
74 changes: 37 additions & 37 deletions boa_engine/src/builtins/proxy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)),
}
Expand Down Expand Up @@ -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 )`
Expand All @@ -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<JsValue> {
pub(crate) fn create(
target: &JsValue,
handler: &JsValue,
context: &mut Context,
) -> JsResult<JsObject> {
// 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")
Expand Down Expand Up @@ -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<JsValue> {
// 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<JsValue> {
// 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();
Expand Down
1 change: 0 additions & 1 deletion boa_engine/src/object/internal_methods/bound_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
};

Expand Down
25 changes: 2 additions & 23 deletions boa_engine/src/object/internal_methods/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]] ( )`
Expand Down
Loading

0 comments on commit 406c642

Please sign in to comment.