boa_engine/object/builtins/
jsproxy.rs1use super::JsFunction;
3use crate::{
4 Context, JsExpect, JsNativeError, JsResult, JsValue,
5 builtins::Proxy,
6 js_string,
7 native_function::{NativeFunction, NativeFunctionPointer},
8 object::{FunctionObjectBuilder, JsObject},
9 value::TryFromJs,
10};
11use boa_gc::{Finalize, Trace};
12
13#[derive(Debug, Clone, Trace, Finalize)]
26pub struct JsProxy {
27 inner: JsObject,
28}
29
30impl JsProxy {
31 pub fn builder(target: JsObject) -> JsProxyBuilder {
33 JsProxyBuilder::new(target)
34 }
35
36 #[inline]
39 pub fn from_object(object: JsObject) -> JsResult<Self> {
40 if object.is::<Proxy>() {
41 Ok(Self { inner: object })
42 } else {
43 Err(JsNativeError::typ()
44 .with_message("object is not a Proxy")
45 .into())
46 }
47 }
48}
49
50impl From<JsProxy> for JsObject {
51 #[inline]
52 fn from(o: JsProxy) -> Self {
53 o.inner.clone()
54 }
55}
56
57impl From<JsProxy> for JsValue {
58 #[inline]
59 fn from(o: JsProxy) -> Self {
60 o.inner.clone().into()
61 }
62}
63
64impl std::ops::Deref for JsProxy {
65 type Target = JsObject;
66
67 #[inline]
68 fn deref(&self) -> &Self::Target {
69 &self.inner
70 }
71}
72
73impl TryFromJs for JsProxy {
74 fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {
75 if let Some(o) = value.as_object() {
76 Self::from_object(o.clone())
77 } else {
78 Err(JsNativeError::typ()
79 .with_message("value is not a Proxy object")
80 .into())
81 }
82 }
83}
84
85#[derive(Debug, Trace, Finalize)]
98pub struct JsRevocableProxy {
99 proxy: JsProxy,
100 revoker: JsFunction,
101}
102
103impl JsRevocableProxy {
104 #[inline]
107 pub fn revoke(self, context: &mut Context) -> JsResult<()> {
108 self.revoker.call(&JsValue::undefined(), &[], context)?;
109 Ok(())
110 }
111}
112
113impl std::ops::Deref for JsRevocableProxy {
114 type Target = JsProxy;
115
116 #[inline]
117 fn deref(&self) -> &Self::Target {
118 &self.proxy
119 }
120}
121
122#[must_use]
128#[derive(Clone)]
129pub struct JsProxyBuilder {
130 target: JsObject,
131 apply: Option<NativeFunctionPointer>,
132 construct: Option<NativeFunctionPointer>,
133 define_property: Option<NativeFunctionPointer>,
134 delete_property: Option<NativeFunctionPointer>,
135 get: Option<NativeFunctionPointer>,
136 get_own_property_descriptor: Option<NativeFunctionPointer>,
137 get_prototype_of: Option<NativeFunctionPointer>,
138 has: Option<NativeFunctionPointer>,
139 is_extensible: Option<NativeFunctionPointer>,
140 own_keys: Option<NativeFunctionPointer>,
141 prevent_extensions: Option<NativeFunctionPointer>,
142 set: Option<NativeFunctionPointer>,
143 set_prototype_of: Option<NativeFunctionPointer>,
144}
145
146impl std::fmt::Debug for JsProxyBuilder {
147 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148 #[derive(Debug)]
149 struct NativeFunction;
150 f.debug_struct("ProxyBuilder")
151 .field("target", &self.target)
152 .field("apply", &self.apply.map(|_| NativeFunction))
153 .field("construct", &self.construct.map(|_| NativeFunction))
154 .field(
155 "define_property",
156 &self.define_property.map(|_| NativeFunction),
157 )
158 .field(
159 "delete_property",
160 &self.delete_property.map(|_| NativeFunction),
161 )
162 .field("get", &self.get.map(|_| NativeFunction))
163 .field(
164 "get_own_property_descriptor",
165 &self.get_own_property_descriptor.map(|_| NativeFunction),
166 )
167 .field(
168 "get_prototype_of",
169 &self.get_prototype_of.map(|_| NativeFunction),
170 )
171 .field("has", &self.has.map(|_| NativeFunction))
172 .field("is_extensible", &self.is_extensible.map(|_| NativeFunction))
173 .field("own_keys", &self.own_keys.map(|_| NativeFunction))
174 .field(
175 "prevent_extensions",
176 &self.prevent_extensions.map(|_| NativeFunction),
177 )
178 .field("set", &self.set.map(|_| NativeFunction))
179 .field(
180 "set_prototype_of",
181 &self.set_prototype_of.map(|_| NativeFunction),
182 )
183 .finish()
184 }
185}
186
187impl JsProxyBuilder {
188 #[inline]
190 pub fn new(target: JsObject) -> Self {
191 Self {
192 target,
193 apply: None,
194 construct: None,
195 define_property: None,
196 delete_property: None,
197 get: None,
198 get_own_property_descriptor: None,
199 get_prototype_of: None,
200 has: None,
201 is_extensible: None,
202 own_keys: None,
203 prevent_extensions: None,
204 set: None,
205 set_prototype_of: None,
206 }
207 }
208
209 #[inline]
222 pub fn apply(mut self, apply: NativeFunctionPointer) -> Self {
223 self.apply = Some(apply);
224 self
225 }
226
227 #[inline]
240 pub fn construct(mut self, construct: NativeFunctionPointer) -> Self {
241 self.construct = Some(construct);
242 self
243 }
244
245 #[inline]
253 pub fn define_property(mut self, define_property: NativeFunctionPointer) -> Self {
254 self.define_property = Some(define_property);
255 self
256 }
257
258 #[inline]
266 pub fn delete_property(mut self, delete_property: NativeFunctionPointer) -> Self {
267 self.delete_property = Some(delete_property);
268 self
269 }
270
271 #[inline]
279 pub fn get(mut self, get: NativeFunctionPointer) -> Self {
280 self.get = Some(get);
281 self
282 }
283
284 #[inline]
292 pub fn get_own_property_descriptor(
293 mut self,
294 get_own_property_descriptor: NativeFunctionPointer,
295 ) -> Self {
296 self.get_own_property_descriptor = Some(get_own_property_descriptor);
297 self
298 }
299
300 #[inline]
308 pub fn get_prototype_of(mut self, get_prototype_of: NativeFunctionPointer) -> Self {
309 self.get_prototype_of = Some(get_prototype_of);
310 self
311 }
312
313 #[inline]
321 pub fn has(mut self, has: NativeFunctionPointer) -> Self {
322 self.has = Some(has);
323 self
324 }
325
326 #[inline]
334 pub fn is_extensible(mut self, is_extensible: NativeFunctionPointer) -> Self {
335 self.is_extensible = Some(is_extensible);
336 self
337 }
338
339 #[inline]
347 pub fn own_keys(mut self, own_keys: NativeFunctionPointer) -> Self {
348 self.own_keys = Some(own_keys);
349 self
350 }
351
352 #[inline]
360 pub fn prevent_extensions(mut self, prevent_extensions: NativeFunctionPointer) -> Self {
361 self.prevent_extensions = Some(prevent_extensions);
362 self
363 }
364
365 #[inline]
373 pub fn set(mut self, set: NativeFunctionPointer) -> Self {
374 self.set = Some(set);
375 self
376 }
377
378 #[inline]
386 pub fn set_prototype_of(mut self, set_prototype_of: NativeFunctionPointer) -> Self {
387 self.set_prototype_of = Some(set_prototype_of);
388 self
389 }
390
391 pub fn build(self, context: &mut Context) -> JsResult<JsProxy> {
397 let handler = JsObject::with_object_proto(context.intrinsics());
398
399 if let Some(apply) = self.apply {
400 let f = FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(apply))
401 .length(3)
402 .build();
403 handler
404 .create_data_property_or_throw(js_string!("apply"), f, context)
405 .js_expect("new object should be writable")?;
406 }
407 if let Some(construct) = self.construct {
408 let f =
409 FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(construct))
410 .length(3)
411 .build();
412 handler
413 .create_data_property_or_throw(js_string!("construct"), f, context)
414 .js_expect("new object should be writable")?;
415 }
416 if let Some(define_property) = self.define_property {
417 let f = FunctionObjectBuilder::new(
418 context.realm(),
419 NativeFunction::from_fn_ptr(define_property),
420 )
421 .length(3)
422 .build();
423 handler
424 .create_data_property_or_throw(js_string!("defineProperty"), f, context)
425 .js_expect("new object should be writable")?;
426 }
427 if let Some(delete_property) = self.delete_property {
428 let f = FunctionObjectBuilder::new(
429 context.realm(),
430 NativeFunction::from_fn_ptr(delete_property),
431 )
432 .length(2)
433 .build();
434 handler
435 .create_data_property_or_throw(js_string!("deleteProperty"), f, context)
436 .js_expect("new object should be writable")?;
437 }
438 if let Some(get) = self.get {
439 let f = FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(get))
440 .length(3)
441 .build();
442 handler
443 .create_data_property_or_throw(js_string!("get"), f, context)
444 .js_expect("new object should be writable")?;
445 }
446 if let Some(get_own_property_descriptor) = self.get_own_property_descriptor {
447 let f = FunctionObjectBuilder::new(
448 context.realm(),
449 NativeFunction::from_fn_ptr(get_own_property_descriptor),
450 )
451 .length(2)
452 .build();
453 handler
454 .create_data_property_or_throw(js_string!("getOwnPropertyDescriptor"), f, context)
455 .js_expect("new object should be writable")?;
456 }
457 if let Some(get_prototype_of) = self.get_prototype_of {
458 let f = FunctionObjectBuilder::new(
459 context.realm(),
460 NativeFunction::from_fn_ptr(get_prototype_of),
461 )
462 .length(1)
463 .build();
464 handler
465 .create_data_property_or_throw(js_string!("getPrototypeOf"), f, context)
466 .js_expect("new object should be writable")?;
467 }
468 if let Some(has) = self.has {
469 let f = FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(has))
470 .length(2)
471 .build();
472 handler
473 .create_data_property_or_throw(js_string!("has"), f, context)
474 .js_expect("new object should be writable")?;
475 }
476 if let Some(is_extensible) = self.is_extensible {
477 let f = FunctionObjectBuilder::new(
478 context.realm(),
479 NativeFunction::from_fn_ptr(is_extensible),
480 )
481 .length(1)
482 .build();
483 handler
484 .create_data_property_or_throw(js_string!("isExtensible"), f, context)
485 .js_expect("new object should be writable")?;
486 }
487 if let Some(own_keys) = self.own_keys {
488 let f =
489 FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(own_keys))
490 .length(1)
491 .build();
492 handler
493 .create_data_property_or_throw(js_string!("ownKeys"), f, context)
494 .js_expect("new object should be writable")?;
495 }
496 if let Some(prevent_extensions) = self.prevent_extensions {
497 let f = FunctionObjectBuilder::new(
498 context.realm(),
499 NativeFunction::from_fn_ptr(prevent_extensions),
500 )
501 .length(1)
502 .build();
503 handler
504 .create_data_property_or_throw(js_string!("preventExtensions"), f, context)
505 .js_expect("new object should be writable")?;
506 }
507 if let Some(set) = self.set {
508 let f = FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(set))
509 .length(4)
510 .build();
511 handler
512 .create_data_property_or_throw(js_string!("set"), f, context)
513 .js_expect("new object should be writable")?;
514 }
515 if let Some(set_prototype_of) = self.set_prototype_of {
516 let f = FunctionObjectBuilder::new(
517 context.realm(),
518 NativeFunction::from_fn_ptr(set_prototype_of),
519 )
520 .length(2)
521 .build();
522 handler
523 .create_data_property_or_throw(js_string!("setPrototypeOf"), f, context)
524 .js_expect("new object should be writable")?;
525 }
526
527 let proxy = JsObject::from_proto_and_data_with_shared_shape(
528 context.root_shape(),
529 context.intrinsics().constructors().object().prototype(),
530 Proxy::new(self.target, handler),
531 )
532 .upcast();
533
534 Ok(JsProxy { inner: proxy })
535 }
536
537 pub fn build_revocable(self, context: &mut Context) -> JsResult<JsRevocableProxy> {
545 let proxy = self.build(context)?;
546 let revoker = Proxy::revoker(proxy.inner.clone(), context);
547
548 Ok(JsRevocableProxy { proxy, revoker })
549 }
550}