mirror of https://github.com/boa-dev/boa.git
Browse Source
* Implement `Proxy` object * Restucture `Proxy` struct fields * Apply some suggestionspull/1672/head
raskad
3 years ago
committed by
GitHub
13 changed files with 1432 additions and 72 deletions
@ -0,0 +1,188 @@
|
||||
//! This module implements the global `Proxy` object.
|
||||
//!
|
||||
//! The `Proxy` object enables you to create a proxy for another object,
|
||||
//! which can intercept and redefine fundamental operations for that object.
|
||||
//!
|
||||
//! More information:
|
||||
//! - [ECMAScript reference][spec]
|
||||
//! - [MDN documentation][mdn]
|
||||
//!
|
||||
//! [spec]: https://tc39.es/ecma262/#sec-proxy-objects
|
||||
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
|
||||
|
||||
use crate::{ |
||||
builtins::{BuiltIn, JsArgs}, |
||||
gc::{Finalize, Trace}, |
||||
object::{ConstructorBuilder, FunctionBuilder, JsObject, ObjectData}, |
||||
property::Attribute, |
||||
BoaProfiler, Context, JsResult, JsValue, |
||||
}; |
||||
|
||||
/// Javascript `Proxy` object.
|
||||
#[derive(Debug, Clone, Trace, Finalize)] |
||||
pub struct Proxy { |
||||
// (target, handler)
|
||||
data: Option<(JsObject, JsObject)>, |
||||
} |
||||
|
||||
impl BuiltIn for Proxy { |
||||
const NAME: &'static str = "Proxy"; |
||||
|
||||
const ATTRIBUTE: Attribute = Attribute::WRITABLE |
||||
.union(Attribute::NON_ENUMERABLE) |
||||
.union(Attribute::CONFIGURABLE); |
||||
|
||||
fn init(context: &mut Context) -> JsValue { |
||||
let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); |
||||
|
||||
ConstructorBuilder::with_standard_object( |
||||
context, |
||||
Self::constructor, |
||||
context.standard_objects().proxy_object().clone(), |
||||
) |
||||
.name(Self::NAME) |
||||
.length(Self::LENGTH) |
||||
.constructor_has_prototype(false) |
||||
.static_method(Self::revocable, "revocable", 2) |
||||
.build() |
||||
.into() |
||||
} |
||||
} |
||||
|
||||
impl Proxy { |
||||
const LENGTH: usize = 2; |
||||
|
||||
fn new(target: JsObject, handler: JsObject) -> Self { |
||||
Self { |
||||
data: Some((target, handler)), |
||||
} |
||||
} |
||||
|
||||
/// This is an internal method only built for usage in the proxy internal methods.
|
||||
///
|
||||
/// It returns the (target, handler) of the proxy.
|
||||
pub(crate) fn try_data(&self, context: &mut Context) -> JsResult<(JsObject, JsObject)> { |
||||
self.data.clone().ok_or_else(|| { |
||||
context.construct_type_error("Proxy object has empty handler and target") |
||||
}) |
||||
} |
||||
|
||||
/// `28.2.1.1 Proxy ( target, handler )`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-proxy-target-handler
|
||||
pub(crate) fn constructor( |
||||
new_target: &JsValue, |
||||
args: &[JsValue], |
||||
context: &mut Context, |
||||
) -> JsResult<JsValue> { |
||||
// 1. If NewTarget is undefined, throw a TypeError exception.
|
||||
if new_target.is_undefined() { |
||||
return context.throw_type_error("Proxy constructor called on undefined new target"); |
||||
} |
||||
|
||||
// 2. Return ? ProxyCreate(target, handler).
|
||||
Self::create(args.get_or_undefined(0), args.get_or_undefined(1), context) |
||||
} |
||||
|
||||
// `10.5.14 ProxyCreate ( target, handler )`
|
||||
//
|
||||
// More information:
|
||||
// - [ECMAScript reference][spec]
|
||||
//
|
||||
// [spec]: https://tc39.es/ecma262/#sec-proxycreate
|
||||
fn create(target: &JsValue, handler: &JsValue, context: &mut Context) -> JsResult<JsValue> { |
||||
// 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 handler") |
||||
})?; |
||||
|
||||
// 2. If Type(handler) is not Object, throw a TypeError exception.
|
||||
let handler = handler.as_object().ok_or_else(|| { |
||||
context.construct_type_error("Proxy constructor called with non-object handler") |
||||
})?; |
||||
|
||||
// 3. Let P be ! MakeBasicObject(« [[ProxyHandler]], [[ProxyTarget]] »).
|
||||
// 4. Set P's essential internal methods, except for [[Call]] and [[Construct]], to the definitions specified in 10.5.
|
||||
// 5. If IsCallable(target) is true, then
|
||||
// a. Set P.[[Call]] as specified in 10.5.12.
|
||||
// b. If IsConstructor(target) is true, then
|
||||
// i. Set P.[[Construct]] as specified in 10.5.13.
|
||||
// 6. Set P.[[ProxyTarget]] to target.
|
||||
// 7. Set P.[[ProxyHandler]] to handler.
|
||||
let p = JsObject::from_proto_and_data( |
||||
context.standard_objects().object_object().prototype(), |
||||
ObjectData::proxy( |
||||
Self::new(target.clone(), handler.clone()), |
||||
target.is_callable(), |
||||
target.is_constructor(), |
||||
), |
||||
); |
||||
|
||||
// 8. Return P.
|
||||
Ok(p.into()) |
||||
} |
||||
|
||||
/// `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)?; |
||||
|
||||
// 3. Let revoker be ! CreateBuiltinFunction(revokerClosure, 0, "", « [[RevocableProxy]] »).
|
||||
// 4. Set revoker.[[RevocableProxy]] to p.
|
||||
let revoker = 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; |
||||
|
||||
// h. Return undefined.
|
||||
Ok(JsValue::undefined()) |
||||
}, |
||||
p.clone(), |
||||
) |
||||
.build(); |
||||
|
||||
// 5. Let result be ! OrdinaryObjectCreate(%Object.prototype%).
|
||||
let result = context.construct_object(); |
||||
|
||||
// 6. Perform ! CreateDataPropertyOrThrow(result, "proxy", p).
|
||||
result |
||||
.create_data_property_or_throw("proxy", p, context) |
||||
.expect("CreateDataPropertyOrThrow cannot fail here"); |
||||
|
||||
// 7. Perform ! CreateDataPropertyOrThrow(result, "revoke", revoker).
|
||||
result |
||||
.create_data_property_or_throw("revoke", revoker, context) |
||||
.expect("CreateDataPropertyOrThrow cannot fail here"); |
||||
|
||||
// 8. Return result.
|
||||
Ok(result.into()) |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue