1use crate::jsutils::JsError;
4use crate::quickjs_utils;
5use crate::quickjs_utils::functions::new_native_function_q;
6use crate::quickjs_utils::objects::{get_property, set_property2_q};
7use crate::quickjs_utils::primitives::from_string;
8use crate::quickjs_utils::{atoms, errors, functions, objects, parse_args, primitives};
9use crate::quickjsrealmadapter::QuickJsRealmAdapter;
10use crate::quickjsruntimeadapter::QuickJsRuntimeAdapter;
11use crate::quickjsvalueadapter::QuickJsValueAdapter;
12use libquickjs_sys as q;
13use log::trace;
14use rand::{thread_rng, Rng};
15use std::cell::RefCell;
16use std::collections::HashMap;
17use std::os::raw::{c_char, c_void};
18use std::rc::Rc;
19
20pub type JsProxyInstanceId = usize;
21
22pub mod eventtarget;
23
24pub type ProxyConstructor = dyn Fn(
25 &QuickJsRuntimeAdapter,
26 &QuickJsRealmAdapter,
27 usize,
28 &[QuickJsValueAdapter],
29 ) -> Result<(), JsError>
30 + 'static;
31pub type ProxyFinalizer = dyn Fn(&QuickJsRuntimeAdapter, &QuickJsRealmAdapter, usize) + 'static;
32pub type ProxyMethod = dyn Fn(
33 &QuickJsRuntimeAdapter,
34 &QuickJsRealmAdapter,
35 &usize,
36 &[QuickJsValueAdapter],
37 ) -> Result<QuickJsValueAdapter, JsError>
38 + 'static;
39pub type ProxyNativeMethod = q::JSCFunction;
40pub type ProxyStaticMethod = dyn Fn(
41 &QuickJsRuntimeAdapter,
42 &QuickJsRealmAdapter,
43 &[QuickJsValueAdapter],
44 ) -> Result<QuickJsValueAdapter, JsError>
45 + 'static;
46pub type ProxyStaticNativeMethod = q::JSCFunction;
47pub type ProxyStaticGetter = dyn Fn(&QuickJsRuntimeAdapter, &QuickJsRealmAdapter) -> Result<QuickJsValueAdapter, JsError>
48 + 'static;
49pub type ProxyStaticSetter = dyn Fn(&QuickJsRuntimeAdapter, &QuickJsRealmAdapter, QuickJsValueAdapter) -> Result<(), JsError>
50 + 'static;
51pub type ProxyStaticCatchAllGetter = dyn Fn(&QuickJsRuntimeAdapter, &QuickJsRealmAdapter, &str) -> Result<QuickJsValueAdapter, JsError>
52 + 'static;
53pub type ProxyStaticCatchAllSetter = dyn Fn(
54 &QuickJsRuntimeAdapter,
55 &QuickJsRealmAdapter,
56 &str,
57 QuickJsValueAdapter,
58 ) -> Result<(), JsError>
59 + 'static;
60pub type ProxyGetter = dyn Fn(&QuickJsRuntimeAdapter, &QuickJsRealmAdapter, &usize) -> Result<QuickJsValueAdapter, JsError>
61 + 'static;
62pub type ProxyCatchAllGetter = dyn Fn(
63 &QuickJsRuntimeAdapter,
64 &QuickJsRealmAdapter,
65 &usize,
66 &str,
67 ) -> Result<QuickJsValueAdapter, JsError>
68 + 'static;
69pub type ProxySetter = dyn Fn(
70 &QuickJsRuntimeAdapter,
71 &QuickJsRealmAdapter,
72 &usize,
73 QuickJsValueAdapter,
74 ) -> Result<(), JsError>
75 + 'static;
76pub type ProxyCatchAllSetter = dyn Fn(
77 &QuickJsRuntimeAdapter,
78 &QuickJsRealmAdapter,
79 &usize,
80 &str,
81 QuickJsValueAdapter,
82 ) -> Result<(), JsError>
83 + 'static;
84
85static CNAME: &str = "ProxyInstanceClass\0";
86static SCNAME: &str = "ProxyStaticClass\0";
87
88thread_local! {
89
90 #[cfg(feature = "quickjs-ng")]
91 static PROXY_STATIC_EXOTIC: RefCell<q::JSClassExoticMethods> = RefCell::new(q::JSClassExoticMethods {
92 get_own_property: None,
93 get_own_property_names: None,
94 delete_property: None,
95 define_own_property: None,
96 has_property: Some(proxy_static_has_prop),
97 get_property: Some(proxy_static_get_prop),
98 set_property: Some(proxy_static_set_prop),
99 });
100 #[cfg(feature = "bellard")]
101 static PROXY_STATIC_EXOTIC: RefCell<q::JSClassExoticMethods> = RefCell::new(q::JSClassExoticMethods {
102 get_own_property: None,
103 get_own_property_names: None,
104 delete_property: None,
105 define_own_property: None,
106 has_property: Some(proxy_static_has_prop),
107 get_property: Some(proxy_static_get_prop),
108 set_property: Some(proxy_static_set_prop),
109 get_prototype: None,
110 is_extensible: None,
111 prevent_extensions: None,
112 set_prototype: None
113 });
114
115 #[cfg(feature = "quickjs-ng")]
116 static PROXY_INSTANCE_EXOTIC: RefCell<q::JSClassExoticMethods> = RefCell::new(q::JSClassExoticMethods {
117 get_own_property: None,
118 get_own_property_names: None,
119 delete_property: None,
120 define_own_property: None,
121 has_property: Some(proxy_instance_has_prop),
122 get_property: Some(proxy_instance_get_prop),
123 set_property: Some(proxy_instance_set_prop),
124 });
125
126 #[cfg(feature = "bellard")]
127 static PROXY_INSTANCE_EXOTIC: RefCell<q::JSClassExoticMethods> = RefCell::new(q::JSClassExoticMethods {
128 get_own_property: None,
129 get_own_property_names: None,
130 delete_property: None,
131 define_own_property: None,
132 has_property: Some(proxy_instance_has_prop),
133 get_property: Some(proxy_instance_get_prop),
134 set_property: Some(proxy_instance_set_prop),
135 get_prototype: None,
136 is_extensible: None,
137 prevent_extensions: None,
138 set_prototype: None
139 });
140
141 static PROXY_STATIC_CLASS_DEF: RefCell<q::JSClassDef> = {
142 PROXY_STATIC_EXOTIC.with(|e_rc|{
143 let exotic = &mut *e_rc.borrow_mut();
144 RefCell::new(q::JSClassDef {
145 class_name: SCNAME.as_ptr() as *const c_char,
146 finalizer: None,
147 gc_mark: None,
148 call: None,
149 exotic,
150 })
151 })
152 };
153
154 static PROXY_INSTANCE_CLASS_DEF: RefCell<q::JSClassDef> = {
155 PROXY_INSTANCE_EXOTIC.with(|e_rc|{
156 let exotic = &mut *e_rc.borrow_mut();
157 RefCell::new(q::JSClassDef {
158 class_name: CNAME.as_ptr() as *const c_char,
159 finalizer: Some(finalizer),
160 gc_mark: None,
161 call: None,
162 exotic,
163 })
164 })
165 };
166 pub static PROXY_STATIC_CLASS_ID: RefCell<u32> = {
167
168 let class_id: u32 =
169 QuickJsRuntimeAdapter::do_with(|q_js_rt| {
170 q_js_rt.new_class_id()
171 });
172
173 log::trace!("got static class id {}", class_id);
174
175 PROXY_STATIC_CLASS_DEF.with(|cd_rc| {
176 let class_def = &*cd_rc.borrow();
177 QuickJsRuntimeAdapter::do_with(|q_js_rt| {
178 let res = unsafe { q::JS_NewClass(q_js_rt.runtime, class_id, class_def) };
179 log::trace!("new static class res {}", res);
180 });
182 });
183
184 RefCell::new(class_id)
185 };
186 pub static PROXY_INSTANCE_CLASS_ID: RefCell<u32> = {
187
188 let class_id: u32 =
189 QuickJsRuntimeAdapter::do_with(|q_js_rt| {
190 q_js_rt.new_class_id()
191 });
192 log::trace!("got class id {}", class_id);
193
194 PROXY_INSTANCE_CLASS_DEF.with(|cd_rc| {
195 let class_def = &*cd_rc.borrow();
196 QuickJsRuntimeAdapter::do_with(|q_js_rt| {
197 let res = unsafe { q::JS_NewClass(q_js_rt.runtime, class_id, class_def) };
198
199 log::trace!("new class res {}", res);
200 });
202 });
203
204 RefCell::new(class_id)
205 };
206}
207
208const MAX_INSTANCE_NUM: usize = u32::MAX as usize;
209
210pub(crate) fn init_statics() {
211 PROXY_INSTANCE_CLASS_ID.with(|_rc| {
212 });
214}
215
216fn next_id(proxy: &Proxy) -> usize {
217 let mappings = &*proxy.proxy_instance_id_mappings.borrow();
218 if mappings.len() == MAX_INSTANCE_NUM {
219 panic!("too many instances"); }
221 let mut rng = thread_rng();
222 let mut r: usize = rng.r#gen();
223 while mappings.contains_key(&r) {
224 r += 1;
225 }
226 r
227}
228
229pub struct Proxy {
315 name: Option<String>,
316 namespace: Option<Vec<String>>,
317 pub(crate) constructor: Option<Box<ProxyConstructor>>,
318 finalizers: Vec<Box<ProxyFinalizer>>,
319 methods: HashMap<String, Box<ProxyMethod>>,
320 native_methods: HashMap<String, ProxyNativeMethod>,
321 static_methods: HashMap<String, Box<ProxyStaticMethod>>,
322 static_native_methods: HashMap<String, ProxyStaticNativeMethod>,
323 static_getters_setters: HashMap<String, (Box<ProxyStaticGetter>, Box<ProxyStaticSetter>)>,
324 getters_setters: HashMap<String, (Box<ProxyGetter>, Box<ProxySetter>)>,
325 catch_all: Option<(Box<ProxyCatchAllGetter>, Box<ProxyCatchAllSetter>)>,
326 static_catch_all: Option<(
327 Box<ProxyStaticCatchAllGetter>,
328 Box<ProxyStaticCatchAllSetter>,
329 )>,
330 is_event_target: bool,
331 is_static_event_target: bool,
332 pub(crate) proxy_instance_id_mappings: RefCell<HashMap<usize, Box<ProxyInstanceInfo>>>,
333}
334
335impl Default for crate::reflection::Proxy {
336 fn default() -> Self {
337 Self::new()
338 }
339}
340
341pub fn get_proxy(q_ctx: &QuickJsRealmAdapter, class_name: &str) -> Option<Rc<Proxy>> {
343 let registry = &*q_ctx.proxy_registry.borrow();
344 registry.get(class_name).cloned()
345}
346
347impl Proxy {
348 #[allow(dead_code)]
349 pub fn new() -> Self {
350 Proxy {
351 name: None,
352 namespace: None,
353 constructor: None,
354 finalizers: Default::default(),
355 methods: Default::default(),
356 native_methods: Default::default(),
357 static_methods: Default::default(),
358 static_native_methods: Default::default(),
359 static_getters_setters: Default::default(),
360 getters_setters: Default::default(),
361 catch_all: None,
362 static_catch_all: None,
363 is_event_target: false,
364 is_static_event_target: false,
365 proxy_instance_id_mappings: RefCell::new(Default::default()),
366 }
367 }
368
369 pub fn name(mut self, name: &str) -> Self {
372 self.name = Some(name.to_string());
373 self
374 }
375 pub fn namespace(mut self, namespace: &[&str]) -> Self {
386 if namespace.is_empty() {
387 self.namespace = None;
388 } else {
389 self.namespace = Some(namespace.iter().map(|s| s.to_string()).collect());
390 }
391 self
392 }
393 pub fn get_class_name(&self) -> String {
401 let cn = if let Some(n) = self.name.as_ref() {
402 n.as_str()
403 } else {
404 "__nameless_class__"
405 };
406 if self.namespace.is_some() {
407 format!("{}.{}", self.namespace.as_ref().unwrap().join("."), cn)
408 } else {
409 cn.to_string()
410 }
411 }
412 pub fn constructor<C>(mut self, constructor: C) -> Self
416 where
417 C: Fn(
418 &QuickJsRuntimeAdapter,
419 &QuickJsRealmAdapter,
420 usize,
421 &[QuickJsValueAdapter],
422 ) -> Result<(), JsError>
423 + 'static,
424 {
425 self.constructor = Some(Box::new(constructor));
426 self
427 }
428 pub fn finalizer<C>(mut self, finalizer: C) -> Self
431 where
432 C: Fn(&QuickJsRuntimeAdapter, &QuickJsRealmAdapter, usize) + 'static,
433 {
434 self.finalizers.push(Box::new(finalizer));
435 self
436 }
437 pub fn method<M>(mut self, name: &str, method: M) -> Self
439 where
440 M: Fn(
441 &QuickJsRuntimeAdapter,
442 &QuickJsRealmAdapter,
443 &usize,
444 &[QuickJsValueAdapter],
445 ) -> Result<QuickJsValueAdapter, JsError>
446 + 'static,
447 {
448 self.methods.insert(name.to_string(), Box::new(method));
449 self
450 }
451 pub fn native_method(mut self, name: &str, method: ProxyNativeMethod) -> Self {
453 self.native_methods.insert(name.to_string(), method);
454 self
455 }
456 pub fn static_method<M>(mut self, name: &str, method: M) -> Self
458 where
459 M: Fn(
460 &QuickJsRuntimeAdapter,
461 &QuickJsRealmAdapter,
462 &[QuickJsValueAdapter],
463 ) -> Result<QuickJsValueAdapter, JsError>
464 + 'static,
465 {
466 self.static_methods
467 .insert(name.to_string(), Box::new(method));
468 self
469 }
470 pub fn static_native_method(mut self, name: &str, method: ProxyStaticNativeMethod) -> Self {
472 self.static_native_methods.insert(name.to_string(), method);
473 self
474 }
475
476 pub fn static_getter_setter<G, S>(mut self, name: &str, getter: G, setter: S) -> Self
478 where
479 G: Fn(&QuickJsRuntimeAdapter, &QuickJsRealmAdapter) -> Result<QuickJsValueAdapter, JsError>
480 + 'static,
481 S: Fn(
482 &QuickJsRuntimeAdapter,
483 &QuickJsRealmAdapter,
484 QuickJsValueAdapter,
485 ) -> Result<(), JsError>
486 + 'static,
487 {
488 self.static_getters_setters
489 .insert(name.to_string(), (Box::new(getter), Box::new(setter)));
490 self
491 }
492 pub fn static_catch_all_getter_setter<G, S>(mut self, getter: G, setter: S) -> Self
494 where
495 G: Fn(
496 &QuickJsRuntimeAdapter,
497 &QuickJsRealmAdapter,
498 &str,
499 ) -> Result<QuickJsValueAdapter, JsError>
500 + 'static,
501 S: Fn(
502 &QuickJsRuntimeAdapter,
503 &QuickJsRealmAdapter,
504 &str,
505 QuickJsValueAdapter,
506 ) -> Result<(), JsError>
507 + 'static,
508 {
509 self.static_catch_all = Some((Box::new(getter), Box::new(setter)));
510 self
511 }
512 pub fn getter_setter<G, S>(mut self, name: &str, getter: G, setter: S) -> Self
514 where
515 G: Fn(
516 &QuickJsRuntimeAdapter,
517 &QuickJsRealmAdapter,
518 &usize,
519 ) -> Result<QuickJsValueAdapter, JsError>
520 + 'static,
521 S: Fn(
522 &QuickJsRuntimeAdapter,
523 &QuickJsRealmAdapter,
524 &usize,
525 QuickJsValueAdapter,
526 ) -> Result<(), JsError>
527 + 'static,
528 {
529 self.getters_setters
530 .insert(name.to_string(), (Box::new(getter), Box::new(setter)));
531 self
532 }
533 pub fn getter<G>(self, name: &str, getter: G) -> Self
535 where
536 G: Fn(
537 &QuickJsRuntimeAdapter,
538 &QuickJsRealmAdapter,
539 &usize,
540 ) -> Result<QuickJsValueAdapter, JsError>
541 + 'static,
542 {
543 self.getter_setter(name, getter, |_rt, _realm, _id, _val| Ok(()))
544 }
545 pub fn catch_all_getter_setter<G, S>(mut self, getter: G, setter: S) -> Self
547 where
548 G: Fn(
549 &QuickJsRuntimeAdapter,
550 &QuickJsRealmAdapter,
551 &usize,
552 &str,
553 ) -> Result<QuickJsValueAdapter, JsError>
554 + 'static,
555 S: Fn(
556 &QuickJsRuntimeAdapter,
557 &QuickJsRealmAdapter,
558 &usize,
559 &str,
560 QuickJsValueAdapter,
561 ) -> Result<(), JsError>
562 + 'static,
563 {
564 self.catch_all = Some((Box::new(getter), Box::new(setter)));
565
566 self
567 }
568 pub fn event_target(mut self) -> Self {
570 self.is_event_target = true;
571 self
572 }
573 pub fn static_event_target(mut self) -> Self {
575 self.is_static_event_target = true;
576 self
577 }
578 pub fn install(
580 mut self,
581 q_ctx: &QuickJsRealmAdapter,
582 add_variable_to_global: bool,
583 ) -> Result<QuickJsValueAdapter, JsError> {
584 if self.name.is_none() {
585 return Err(JsError::new_str("Proxy needs a name"));
586 }
587
588 let prim_cn = self.get_class_name();
589 let prim_cn2 = prim_cn.clone();
590
591 self = self.method("Symbol.toPrimitive", move |_rt, q_ctx, id, _args| {
593 let prim = primitives::from_string_q(
594 q_ctx,
595 format!("Proxy::instance({id})::{prim_cn}").as_str(),
596 )?;
597 Ok(prim)
598 });
599 let prim_cn = self.get_class_name();
600 self = self.static_method("Symbol.hasInstance", move |_rt, realm, args| {
601 if args.len() == 1 {
602 let instance = &args[0];
603 if instance.is_proxy_instance() {
604 let info = realm.get_proxy_instance_info(instance)?;
605 if info.0.eq(prim_cn2.as_str()) {
606 return realm.create_boolean(true);
607 }
608 }
609 }
610 realm.create_boolean(false)
611 });
612 self = self.static_method("Symbol.toPrimitive", move |_rt, q_ctx, _args| {
613 let prim = primitives::from_string_q(q_ctx, format!("Proxy::{prim_cn}").as_str())?;
614 Ok(prim)
615 });
616
617 let ret = self.install_class_prop(q_ctx, add_variable_to_global)?;
618 eventtarget::impl_event_target(self).install_move_to_registry(q_ctx);
619
620 Ok(ret)
621 }
622
623 fn install_move_to_registry(self, q_ctx: &QuickJsRealmAdapter) {
624 let proxy = self;
625 let reg_map = &mut *q_ctx.proxy_registry.borrow_mut();
626 reg_map.insert(proxy.get_class_name(), Rc::new(proxy));
627 }
628 fn install_class_prop(
629 &mut self,
630 q_ctx: &QuickJsRealmAdapter,
631 add_variable_to_global: bool,
632 ) -> Result<QuickJsValueAdapter, JsError> {
633 log::trace!("reflection::Proxy::install_class_prop / 1");
636
637 let static_class_id = PROXY_STATIC_CLASS_ID.with(|rc| *rc.borrow());
638
639 log::trace!("reflection::Proxy::install_class_prop / 2");
640
641 let constructor_ref = new_native_function_q(
642 q_ctx,
643 self.name.as_ref().unwrap().as_str(),
644 Some(constructor),
645 1,
646 true,
647 )?;
648
649 log::trace!("reflection::Proxy::install_class_prop / 3");
650
651 let class_val: q::JSValue =
652 unsafe { q::JS_NewObjectClass(q_ctx.context, static_class_id as i32) };
653
654 log::trace!("reflection::Proxy::install_class_prop / 4");
655
656 let class_val_ref = QuickJsValueAdapter::new(
657 q_ctx.context,
658 class_val,
659 false,
660 true,
661 "reflection::Proxy::install_class_prop class_val",
662 );
663
664 #[cfg(feature = "bellard")]
665 assert_eq!(1, class_val_ref.get_ref_count());
666
667 log::trace!("reflection::Proxy::install_class_prop / 5");
668
669 if class_val_ref.is_exception() {
670 return if let Some(e) = unsafe { QuickJsRealmAdapter::get_exception(q_ctx.context) } {
671 Err(e)
672 } else {
673 Err(JsError::new_string(format!(
674 "could not create class:{}",
675 self.get_class_name()
676 )))
677 };
678 }
679
680 log::trace!("reflection::Proxy::install_class_prop / 6");
681
682 unsafe {
683 let res = q::JS_SetPrototype(
684 q_ctx.context,
685 *constructor_ref.borrow_value(),
686 *class_val_ref.borrow_value(),
687 );
688 if res < 0 {
689 return if let Some(err) = QuickJsRealmAdapter::get_exception(q_ctx.context) {
690 Err(err)
691 } else {
692 Err(JsError::new_str("could not set class proto"))
693 };
694 }
695 }
696
697 #[cfg(feature = "bellard")]
698 assert_eq!(2, class_val_ref.get_ref_count());
699
700 log::trace!("reflection::Proxy::install_class_prop / 7");
701
702 objects::set_property2_q(
703 q_ctx,
704 &constructor_ref,
705 "name",
706 &primitives::from_string_q(q_ctx, &self.get_class_name())?,
707 0,
708 )?;
709
710 if add_variable_to_global {
712 log::trace!("reflection::Proxy::install_class_prop / 8");
713 let ns = if let Some(namespace) = &self.namespace {
714 let ns_str = namespace.iter().map(|s| s.as_str()).collect::<Vec<&str>>();
715 objects::get_namespace_q(q_ctx, ns_str.as_slice(), true)?
716 } else {
717 quickjs_utils::get_global_q(q_ctx)
718 };
719
720 log::trace!("reflection::Proxy::install_class_prop / 9");
721
722 objects::set_property2_q(
723 q_ctx,
724 &ns,
725 self.name.as_ref().unwrap().as_str(),
726 &constructor_ref,
727 0,
728 )?;
729 }
730 log::trace!("reflection::Proxy::install_class_prop / 10");
731
732 let proxy_constructor_refs = &mut *q_ctx.proxy_constructor_refs.borrow_mut();
733 proxy_constructor_refs.insert(self.get_class_name(), constructor_ref.clone());
734
735 log::trace!("install_class_prop done");
736
737 Ok(constructor_ref)
738 }
739}
740
741pub fn get_proxy_instance_proxy_and_instance_id_q(
742 q_ctx: &QuickJsRealmAdapter,
743 obj: &QuickJsValueAdapter,
744) -> Option<(Rc<Proxy>, usize)> {
745 if !is_proxy_instance_q(q_ctx, obj) {
746 None
747 } else {
748 let info = get_proxy_instance_info(obj.borrow_value());
749 let cn = info.class_name.as_str();
750 let registry = &*q_ctx.proxy_registry.borrow();
751 registry.get(cn).cloned().map(|proxy| (proxy, info.id))
752 }
753}
754
755pub fn get_proxy_instance_id_q(
756 q_ctx: &QuickJsRealmAdapter,
757 obj: &QuickJsValueAdapter,
758) -> Option<usize> {
759 if !is_proxy_instance_q(q_ctx, obj) {
760 None
761 } else {
762 let info = get_proxy_instance_info(obj.borrow_value());
763 Some(info.id)
764 }
765}
766
767pub unsafe fn get_proxy_instance_id(
771 ctx: *mut libquickjs_sys::JSContext,
772 obj: &QuickJsValueAdapter,
773) -> Option<usize> {
774 if !is_proxy_instance(ctx, obj) {
775 None
776 } else {
777 let info = get_proxy_instance_info(obj.borrow_value());
778 Some(info.id)
779 }
780}
781
782pub fn is_proxy_instance_q(q_ctx: &QuickJsRealmAdapter, obj: &QuickJsValueAdapter) -> bool {
783 unsafe { is_proxy_instance(q_ctx.context, obj) }
784}
785
786pub unsafe fn is_proxy_instance(ctx: *mut q::JSContext, obj: &QuickJsValueAdapter) -> bool {
790 if !obj.is_object() {
791 false
792 } else {
793 let prop_res = get_property(ctx, obj, "__proxy__");
795 if let Ok(prop) = prop_res {
796 if prop.is_bool() && prop.to_bool() {
797 return true;
798 }
799 }
800
801 let class_id = PROXY_INSTANCE_CLASS_ID.with(|rc| *rc.borrow());
802 let proxy_class_proto: q::JSValue = q::JS_GetClassProto(ctx, class_id);
803 let proxy_class_proto_obj = q::JS_GetPrototype(ctx, proxy_class_proto);
806 let res = q::JS_IsInstanceOf(ctx, *obj.borrow_value(), proxy_class_proto_obj);
807
808 if res == -1 {
809 if let Some(ex) = QuickJsRealmAdapter::get_exception(ctx) {
811 log::error!("is_proxy_instance failed: {}", ex);
812 } else {
813 log::error!("is_proxy_instance failed");
814 }
815 }
816
817 res > 0
818 }
819}
820
821pub fn new_instance2(
822 proxy: &Proxy,
823 q_ctx: &QuickJsRealmAdapter,
824) -> Result<(usize, QuickJsValueAdapter), JsError> {
825 let instance_id = next_id(proxy);
826 Ok((instance_id, new_instance3(proxy, instance_id, q_ctx)?))
827}
828
829pub(crate) fn new_instance3(
830 proxy: &Proxy,
831 instance_id: usize,
832 q_ctx: &QuickJsRealmAdapter,
833) -> Result<QuickJsValueAdapter, JsError> {
834 let ctx = q_ctx.context;
835 let class_id = PROXY_INSTANCE_CLASS_ID.with(|rc| *rc.borrow());
836
837 let class_val: q::JSValue = unsafe { q::JS_NewObjectClass(ctx, class_id as i32) };
838
839 let class_name = proxy.get_class_name();
840
841 trace!("creating new instance {} of {}", instance_id, class_name);
842
843 let class_val_ref = QuickJsValueAdapter::new(
844 q_ctx.context,
845 class_val,
846 false,
847 true,
848 format!("reflection::Proxy; cn={class_name}").as_str(),
849 );
850
851 if class_val_ref.is_exception() {
852 return if let Some(e) = q_ctx.get_exception_ctx() {
853 Err(JsError::new_string(format!(
854 "could not create class:{class_name} due to: {e}"
855 )))
856 } else {
857 Err(JsError::new_string(format!(
858 "could not create class:{class_name}"
859 )))
860 };
861 }
862
863 let mappings = &mut *proxy.proxy_instance_id_mappings.borrow_mut();
864 assert!(!mappings.contains_key(&instance_id));
865
866 let mut bx = Box::new(ProxyInstanceInfo {
867 id: instance_id,
868 class_name: proxy.get_class_name(),
869 context_id: q_ctx.id.clone(),
870 });
871
872 let ibp: &mut ProxyInstanceInfo = &mut bx;
873 let info_ptr = ibp as *mut _ as *mut c_void;
874
875 mappings.insert(instance_id, bx);
876 unsafe { q::JS_SetOpaque(*class_val_ref.borrow_value(), info_ptr) };
877
878 set_property2_q(
880 q_ctx,
881 &class_val_ref,
882 "__proxy__",
883 &primitives::from_bool(true),
884 0,
885 )?;
886
887 let proxy_constructor_refs = &*q_ctx.proxy_constructor_refs.borrow();
888
889 let constructor = proxy_constructor_refs
890 .get(&class_name)
891 .expect("proxy was not installed properly");
892
893 set_property2_q(q_ctx, &class_val_ref, "constructor", constructor, 0)?;
894
895 Ok(class_val_ref)
896}
897
898pub fn new_instance(
899 class_name: &str,
900 q_ctx: &QuickJsRealmAdapter,
901) -> Result<(usize, QuickJsValueAdapter), JsError> {
902 let registry = &*q_ctx.proxy_registry.borrow();
905
906 if let Some(proxy) = registry.get(class_name) {
907 new_instance2(proxy, q_ctx)
910 } else {
911 Err(JsError::new_str("no such proxy"))
912 }
913}
914
915#[allow(dead_code)]
916unsafe extern "C" fn constructor(
917 context: *mut q::JSContext,
918 this_val: q::JSValue,
919 argc: ::std::os::raw::c_int,
920 argv: *mut q::JSValue,
921) -> q::JSValue {
922 log::trace!("constructor called, this_tag={}", this_val.tag);
923
924 let this_ref = QuickJsValueAdapter::new(
927 context,
928 this_val,
929 false,
930 false,
931 "reflection::constructor this_val",
932 );
933 QuickJsRuntimeAdapter::do_with(|q_js_rt| {
934 let name_ref = objects::get_property(context, &this_ref, "name").expect("name get failed");
935 let class_name =
936 functions::call_to_string(context, &name_ref).expect("name.toString failed");
937
938 let q_ctx = q_js_rt.get_quickjs_context(context);
939
940 let registry = &*q_ctx.proxy_registry.borrow();
941 if let Some(proxy) = registry.get(&class_name) {
942 if let Some(constructor) = &proxy.constructor {
943 let args_vec = parse_args(context, argc, argv);
946 let instance_id = next_id(proxy);
947 let constructor_res = constructor(q_js_rt, q_ctx, instance_id, &args_vec);
948
949 match constructor_res {
950 Ok(()) => {
951 let instance_ref_res = new_instance3(proxy, instance_id, q_ctx);
952
953 match instance_ref_res {
954 Ok(instance_ref) => instance_ref.clone_value_incr_rc(),
955
956 Err(e) => q_ctx.report_ex(
957 format!(
958 "could not create proxy instance for {class_name} due to {e}"
959 )
960 .as_str(),
961 ),
962 }
963 }
964 Err(es_err) => q_ctx.report_ex(
965 format!("constructor for {class_name} failed with {es_err}").as_str(),
966 ),
967 }
968 } else {
969 q_ctx.report_ex("not a constructor")
970 }
971 } else {
972 q_ctx.report_ex("no such proxy")
973 }
974 })
975}
976
977pub(crate) struct ProxyInstanceInfo {
978 id: usize,
979 class_name: String, context_id: String, }
982
983fn get_proxy_instance_info(val: &q::JSValue) -> &ProxyInstanceInfo {
984 let class_id = PROXY_INSTANCE_CLASS_ID.with(|rc| *rc.borrow());
985 let info_ptr: *mut c_void = unsafe { q::JS_GetOpaque(*val, class_id) };
986 let info: &mut ProxyInstanceInfo = unsafe { &mut *(info_ptr as *mut ProxyInstanceInfo) };
987 info
988}
989
990#[allow(dead_code)]
991unsafe extern "C" fn finalizer(_rt: *mut q::JSRuntime, val: q::JSValue) {
992 log::trace!("finalizer called");
993
994 let info: &ProxyInstanceInfo = get_proxy_instance_info(&val);
995 trace!(
996 "finalize id:{} class:{} context:{}",
997 info.id,
998 info.class_name,
999 info.context_id
1000 );
1001
1002 QuickJsRuntimeAdapter::do_with(|q_js_rt| {
1003 let q_ctx = q_js_rt.get_context(&info.context_id);
1004 log::trace!("finalizer called, got q_ctx");
1005 let registry = &*q_ctx.proxy_registry.borrow();
1006 let proxy = registry.get(&info.class_name).unwrap();
1007
1008 for finalizer in &proxy.finalizers {
1009 log::trace!("calling Proxy's finalizer");
1010 finalizer(q_js_rt, q_ctx, info.id);
1011 log::trace!("after calling Proxy's finalizer");
1012 }
1013
1014 {
1015 log::trace!("reflection::finalizer: remove from INSTANCE_ID_MAPPINGS");
1016 let id_map = &mut *proxy.proxy_instance_id_mappings.borrow_mut();
1017 let _ = id_map.remove(&info.id).expect("no such id to finalize");
1018 log::trace!("reflection::finalizer: remove from INSTANCE_ID_MAPPINGS -> done");
1019 }
1020 log::trace!("reflection::finalizer: 2");
1021
1022 log::trace!("reflection::finalizer: 3, exit");
1023 });
1024}
1025
1026#[allow(dead_code)]
1027unsafe extern "C" fn proxy_static_get_prop(
1028 context: *mut q::JSContext,
1029 obj: q::JSValue,
1030 atom: q::JSAtom,
1031 receiver: q::JSValue,
1032) -> q::JSValue {
1033 trace!("proxy_static_get_prop");
1035
1036 let _obj_ref = QuickJsValueAdapter::new(
1037 context,
1038 obj,
1039 false,
1040 false,
1041 "reflection::proxy_static_get_prop obj",
1042 );
1043 let receiver_ref = QuickJsValueAdapter::new(
1044 context,
1045 receiver,
1046 false,
1047 false,
1048 "reflection::proxy_static_get_prop receiver",
1049 );
1050
1051 QuickJsRuntimeAdapter::do_with(|q_js_rt| {
1052 let q_ctx = q_js_rt.get_quickjs_context(context);
1053
1054 let proxy_name_ref = objects::get_property(context, &receiver_ref, "name")
1055 .ok()
1056 .unwrap();
1057 let proxy_name = primitives::to_string(context, &proxy_name_ref)
1058 .ok()
1059 .unwrap();
1060 trace!("proxy_static_get_prop: {}", proxy_name);
1061
1062 let prop_name = atoms::to_string2(context, &atom).expect("could not get name");
1063 trace!("proxy_static_get_prop: prop: {}", prop_name);
1064
1065 let registry = &*q_ctx.proxy_registry.borrow();
1066 if let Some(proxy) = registry.get(proxy_name.as_str()) {
1067 if proxy.static_methods.contains_key(prop_name.as_str()) {
1068 trace!("found method for {}", prop_name);
1069
1070 let function_data_ref = from_string(context, prop_name.as_str())
1071 .expect("could not create function_data_ref");
1072
1073 let func_ref = functions::new_native_function_data(
1074 context,
1075 Some(proxy_static_method),
1076 prop_name.as_str(),
1077 1,
1078 function_data_ref,
1079 )
1080 .expect("could not create func");
1081
1082 objects::set_property(context, &receiver_ref, prop_name.as_str(), &func_ref)
1083 .expect("set_property 9656738 failed");
1084
1085 func_ref.clone_value_incr_rc()
1086 } else if let Some(native_static_method) =
1087 proxy.static_native_methods.get(prop_name.as_str())
1088 {
1089 trace!("found static native method for {}", prop_name);
1090
1091 let func_ref = functions::new_native_function(
1092 context,
1093 prop_name.as_str(),
1094 *native_static_method,
1095 1,
1096 false,
1097 )
1098 .expect("could not create func");
1099
1100 objects::set_property(context, &receiver_ref, prop_name.as_str(), &func_ref)
1101 .expect("set_property 36099 failed");
1102
1103 func_ref.clone_value_incr_rc()
1104 } else if let Some(getter_setter) = proxy.static_getters_setters.get(prop_name.as_str())
1105 {
1106 let getter = &getter_setter.0;
1108 let res: Result<QuickJsValueAdapter, JsError> = getter(q_js_rt, q_ctx);
1109 match res {
1110 Ok(g_val) => g_val.clone_value_incr_rc(),
1111 Err(e) => {
1112 let es = format!("proxy_static_get_prop failed: {e}");
1113 q_ctx.report_ex(es.as_str())
1114 }
1115 }
1116 } else if let Some(catch_all_getter_setter) = &proxy.static_catch_all {
1117 let getter = &catch_all_getter_setter.0;
1119 let res: Result<QuickJsValueAdapter, JsError> =
1120 getter(q_js_rt, q_ctx, prop_name.as_str());
1121 match res {
1122 Ok(g_val) => g_val.clone_value_incr_rc(),
1123 Err(e) => {
1124 let es = format!("proxy_static_get_prop failed: {e}");
1125 q_ctx.report_ex(es.as_str())
1126 }
1127 }
1128 } else {
1129 quickjs_utils::new_undefined()
1130 }
1131 } else {
1132 q_ctx.report_ex("proxy class not found")
1133 }
1134 })
1135}
1136
1137#[allow(dead_code)]
1138unsafe extern "C" fn proxy_instance_get_prop(
1139 context: *mut q::JSContext,
1140 obj: q::JSValue,
1141 atom: q::JSAtom,
1142 receiver: q::JSValue,
1143) -> q::JSValue {
1144 trace!("proxy_instance_get_prop");
1145
1146 let _obj_ref = QuickJsValueAdapter::new(
1147 context,
1148 obj,
1149 false,
1150 false,
1151 "reflection::proxy_instance_get_prop obj",
1152 );
1153 let receiver_ref = QuickJsValueAdapter::new(
1154 context,
1155 receiver,
1156 false,
1157 false,
1158 "reflection::proxy_instance_get_prop receiver",
1159 );
1160
1161 QuickJsRuntimeAdapter::do_with(|q_js_rt| {
1162 let q_ctx = q_js_rt.get_quickjs_context(context);
1163
1164 let prop_name = atoms::to_string2(context, &atom).expect("could not get name");
1165 trace!("proxy_instance_get_prop: {}", prop_name);
1166
1167 let info = get_proxy_instance_info(&obj);
1168
1169 trace!("obj_ref.classname = {}", info.class_name);
1170
1171 let registry = &*q_ctx.proxy_registry.borrow();
1174 let proxy = registry.get(&info.class_name).unwrap();
1175 if proxy.methods.contains_key(prop_name.as_str()) {
1176 trace!("found method for {}", prop_name);
1177
1178 let function_data_ref = from_string(context, prop_name.as_str())
1179 .expect("could not create function_data_ref");
1180
1181 let func_ref = functions::new_native_function_data(
1182 context,
1183 Some(proxy_instance_method),
1184 prop_name.as_str(),
1185 1,
1186 function_data_ref,
1187 )
1188 .expect("could not create func");
1189
1190 objects::set_property(context, &receiver_ref, prop_name.as_str(), &func_ref)
1191 .expect("set_property 96385 failed"); func_ref.clone_value_incr_rc()
1194 } else if let Some(native_method) = proxy.native_methods.get(prop_name.as_str()) {
1195 trace!("found native method for {}", prop_name);
1196
1197 let func_ref = functions::new_native_function(
1198 context,
1199 prop_name.as_str(),
1200 *native_method,
1201 1,
1202 false,
1203 )
1204 .expect("could not create func"); objects::set_property(context, &receiver_ref, prop_name.as_str(), &func_ref)
1207 .expect("set_property 49671 failed"); func_ref.clone_value_incr_rc()
1210 } else if let Some(getter_setter) = proxy.getters_setters.get(prop_name.as_str()) {
1211 let getter = &getter_setter.0;
1213 let res: Result<QuickJsValueAdapter, JsError> = getter(q_js_rt, q_ctx, &info.id);
1214 match res {
1215 Ok(g_val) => g_val.clone_value_incr_rc(),
1216 Err(e) => {
1217 let msg = format!("proxy_instance_get failed: {}", e.get_message());
1218 let nat_stack = format!(
1219 " at Proxy instance getter [{}]\n{}",
1220 prop_name,
1221 e.get_stack()
1222 );
1223 let err =
1224 errors::new_error(context, e.get_name(), msg.as_str(), nat_stack.as_str())
1225 .expect("create error failed");
1226 errors::throw(context, err)
1227 }
1228 }
1229 } else if let Some(catch_all_getter_setter) = &proxy.catch_all {
1230 let getter = &catch_all_getter_setter.0;
1232 let res: Result<QuickJsValueAdapter, JsError> =
1233 getter(q_js_rt, q_ctx, &info.id, prop_name.as_str());
1234 match res {
1235 Ok(g_val) => g_val.clone_value_incr_rc(),
1236 Err(e) => {
1237 let msg = format!("proxy_instance_catch_all_get failed: {}", e.get_message());
1238 let nat_stack = format!(
1239 " at Proxy instance getter [{}]\n{}",
1240 prop_name,
1241 e.get_stack()
1242 );
1243 let err =
1244 errors::new_error(context, e.get_name(), msg.as_str(), nat_stack.as_str())
1245 .expect("create error failed");
1246 errors::throw(context, err)
1247 }
1248 }
1249 } else {
1250 quickjs_utils::new_undefined()
1252 }
1253 })
1254
1255 }
1260#[allow(dead_code)]
1261unsafe extern "C" fn proxy_instance_has_prop(
1262 _context: *mut q::JSContext,
1263 _obj: q::JSValue,
1264 _atom: q::JSAtom,
1265) -> ::std::os::raw::c_int {
1266 todo!()
1267}
1268#[allow(dead_code)]
1269unsafe extern "C" fn proxy_static_has_prop(
1270 _context: *mut q::JSContext,
1271 _obj: q::JSValue,
1272 _atom: q::JSAtom,
1273) -> ::std::os::raw::c_int {
1274 todo!()
1275}
1276
1277unsafe extern "C" fn proxy_instance_method(
1278 context: *mut q::JSContext,
1279 this_val: q::JSValue,
1280 argc: ::std::os::raw::c_int,
1281 argv: *mut q::JSValue,
1282 _magic: ::std::os::raw::c_int,
1283 func_data: *mut q::JSValue,
1284) -> q::JSValue {
1285 trace!("proxy_instance_method");
1286 QuickJsRuntimeAdapter::do_with(|q_js_rt| {
1287 let q_ctx = q_js_rt.get_quickjs_context(context);
1288
1289 let proxy_instance_info: &ProxyInstanceInfo = get_proxy_instance_info(&this_val);
1290
1291 let args_vec = parse_args(context, argc, argv);
1292
1293 let func_name_ref = QuickJsValueAdapter::new(
1294 context,
1295 *func_data,
1296 false,
1297 false,
1298 "reflection::proxy_instance_method func_data",
1299 );
1300 let func_name = primitives::to_string(context, &func_name_ref)
1301 .expect("could not to_string func_name_ref");
1302
1303 trace!("proxy_instance_method: {}", func_name);
1304
1305 let registry = &*q_ctx.proxy_registry.borrow();
1306 let proxy = registry
1307 .get(proxy_instance_info.class_name.as_str())
1308 .unwrap();
1309 if let Some(method) = proxy.methods.get(func_name.as_str()) {
1310 let m_res: Result<QuickJsValueAdapter, JsError> =
1312 method(q_js_rt, q_ctx, &proxy_instance_info.id, &args_vec);
1313
1314 match m_res {
1315 Ok(m_res_ref) => m_res_ref.clone_value_incr_rc(),
1316 Err(e) => {
1317 let msg = format!("proxy_instance_method failed: {}", e.get_message());
1318 let nat_stack = format!(
1319 " at Proxy instance method [{}]\n{}",
1320 func_name,
1321 e.get_stack()
1322 );
1323 let err =
1324 errors::new_error(context, e.get_name(), msg.as_str(), nat_stack.as_str())
1325 .expect("create error failed");
1326 errors::throw(context, err)
1327 }
1328 }
1329 } else {
1330 quickjs_utils::new_undefined()
1332 }
1333 })
1334}
1335
1336#[allow(dead_code)]
1337unsafe extern "C" fn proxy_static_method(
1338 context: *mut q::JSContext,
1339 this_val: q::JSValue,
1340 argc: ::std::os::raw::c_int,
1341 argv: *mut q::JSValue,
1342 _magic: ::std::os::raw::c_int,
1343 func_data: *mut q::JSValue,
1344) -> q::JSValue {
1345 trace!("proxy_static_method");
1346 QuickJsRuntimeAdapter::do_with(|q_js_rt| {
1347 let q_ctx = q_js_rt.get_quickjs_context(context);
1348 let this_ref = QuickJsValueAdapter::new(
1349 context,
1350 this_val,
1351 false,
1352 false,
1353 "reflection::proxy_static_method this_val",
1354 );
1355
1356 let proxy_name_ref = objects::get_property(context, &this_ref, "name")
1357 .ok()
1358 .unwrap();
1359 let proxy_name =
1360 primitives::to_string(context, &proxy_name_ref).expect("could not to_string classname");
1361
1362 let args_vec = parse_args(context, argc, argv);
1363
1364 let func_name_ref = QuickJsValueAdapter::new(
1365 context,
1366 *func_data,
1367 false,
1368 false,
1369 "reflection::proxy_static_method func_data",
1370 );
1371 let func_name = primitives::to_string(context, &func_name_ref)
1372 .expect("could not to_string func_name_ref");
1373
1374 trace!("proxy_static_method: {}", func_name);
1375
1376 let registry = &*q_ctx.proxy_registry.borrow();
1377 let proxy = registry.get(proxy_name.as_str()).unwrap();
1378 if let Some(method) = proxy.static_methods.get(func_name.as_str()) {
1379 let m_res: Result<QuickJsValueAdapter, JsError> = method(q_js_rt, q_ctx, &args_vec);
1380 match m_res {
1381 Ok(m_res_ref) => m_res_ref.clone_value_incr_rc(),
1382 Err(e) => {
1383 let msg = format!("proxy_static_method failed: {}", e.get_message());
1384 let nat_stack = format!(
1385 " at Proxy static method [{}]\n{}",
1386 func_name,
1387 e.get_stack()
1388 );
1389 let err =
1390 errors::new_error(context, e.get_name(), msg.as_str(), nat_stack.as_str())
1391 .expect("create error failed");
1392 errors::throw(context, err)
1393 }
1394 }
1395 } else {
1396 quickjs_utils::new_undefined()
1398 }
1399 })
1400}
1401
1402unsafe extern "C" fn proxy_static_set_prop(
1403 context: *mut q::JSContext,
1404 _obj: q::JSValue,
1405 atom: q::JSAtom,
1406 value: q::JSValue,
1407 receiver: q::JSValue,
1408 _flags: ::std::os::raw::c_int,
1409) -> ::std::os::raw::c_int {
1410 trace!("proxy_static_set_prop");
1411
1412 let value_ref = QuickJsValueAdapter::new(
1413 context,
1414 value,
1415 false,
1416 false,
1417 "reflection::proxy_static_set_prop value",
1418 );
1419 let receiver_ref = QuickJsValueAdapter::new(
1420 context,
1421 receiver,
1422 false,
1423 false,
1424 "reflection::proxy_static_set_prop value",
1425 );
1426
1427 QuickJsRuntimeAdapter::do_with(|rt| {
1428 let realm = rt.get_quickjs_context(context);
1429
1430 let prop_name = atoms::to_string2(context, &atom).expect("could not get name");
1431 trace!("proxy_static_set_prop: {}", prop_name);
1432
1433 let proxy_name_ref = objects::get_property(context, &receiver_ref, "name")
1436 .ok()
1437 .unwrap();
1438 let proxy_name = primitives::to_string(context, &proxy_name_ref)
1439 .ok()
1440 .unwrap();
1441 trace!("proxy_static_set_prop: {}", proxy_name);
1442
1443 let registry = &*realm.proxy_registry.borrow();
1444 if let Some(proxy) = registry.get(proxy_name.as_str()) {
1445 if let Some(getter_setter) = proxy.static_getters_setters.get(prop_name.as_str()) {
1446 let setter = &getter_setter.1;
1448 let res: Result<(), JsError> = setter(rt, realm, value_ref);
1449 match res {
1450 Ok(_) => 0,
1451 Err(e) => {
1452 let err = format!("proxy_static_set_prop failed: {e}");
1454 log::error!("{}", err);
1455 let _ = realm.report_ex(err.as_str());
1456 -1
1457 }
1458 }
1459 } else if let Some(catch_all_getter_setter) = &proxy.static_catch_all {
1460 let setter = &catch_all_getter_setter.1;
1462 let res: Result<(), JsError> = setter(rt, realm, prop_name.as_str(), value_ref);
1463 match res {
1464 Ok(_) => 0,
1465 Err(e) => {
1466 let err = format!("proxy_static_set_prop failed: {e}");
1468 log::error!("{}", err);
1469 let _ = realm.report_ex(err.as_str());
1470 -1
1471 }
1472 }
1473 } else {
1474 let receiver_ref = QuickJsValueAdapter::new(
1475 context,
1476 receiver,
1477 false,
1478 false,
1479 "reflection::proxy_static_set_prop receiver",
1480 );
1481
1482 match realm.set_object_property(&receiver_ref, prop_name.as_str(), &value_ref) {
1483 Ok(()) => 0,
1484 Err(e) => {
1485 let err = format!("proxy_static_set_prop failed, {}", e);
1486 log::error!("{}", err);
1487 let _ = realm.report_ex(err.as_str());
1488 -1
1489 }
1490 }
1491 }
1499 } else {
1500 let err = "proxy_static_set_prop failed, no proxy found";
1501 log::error!("{}", err);
1502 let _ = realm.report_ex(err);
1503 -1
1504 }
1505 })
1506}
1507
1508unsafe extern "C" fn proxy_instance_set_prop(
1509 context: *mut q::JSContext,
1510 obj: q::JSValue,
1511 atom: q::JSAtom,
1512 value: q::JSValue,
1513 receiver: q::JSValue,
1514 _flags: ::std::os::raw::c_int,
1515) -> ::std::os::raw::c_int {
1516 trace!("proxy_instance_set_prop");
1517
1518 let value_ref = QuickJsValueAdapter::new(
1519 context,
1520 value,
1521 false,
1522 false,
1523 "reflection::proxy_instance_set_prop value",
1524 );
1525
1526 QuickJsRuntimeAdapter::do_with(|rt| {
1527 let realm = rt.get_quickjs_context(context);
1528
1529 let prop_name = atoms::to_string2(context, &atom).expect("could not get name");
1530 trace!("proxy_instance_set_prop: {}", prop_name);
1531
1532 let info = get_proxy_instance_info(&obj);
1533
1534 trace!("obj_ref.classname = {}", info.class_name);
1535
1536 let registry = &*realm.proxy_registry.borrow();
1539 let proxy = registry.get(&info.class_name).unwrap();
1540
1541 if let Some(getter_setter) = proxy.getters_setters.get(prop_name.as_str()) {
1542 let setter = &getter_setter.1;
1544 let res: Result<(), JsError> = setter(rt, realm, &info.id, value_ref);
1545 match res {
1546 Ok(_) => 0,
1547 Err(e) => {
1548 let err = format!("proxy_instance_set_prop failed: {e}");
1550 log::error!("{}", err);
1551 let _ = realm.report_ex(err.as_str());
1552 -1
1553 }
1554 }
1555 } else if let Some(catch_all_getter_setter) = &proxy.catch_all {
1556 let setter = &catch_all_getter_setter.1;
1558 let res: Result<(), JsError> =
1559 setter(rt, realm, &info.id, prop_name.as_str(), value_ref);
1560 match res {
1561 Ok(_) => 0,
1562 Err(e) => {
1563 let err = format!("proxy_instance_set_prop failed: {e}");
1565 log::error!("{}", err);
1566 let _ = realm.report_ex(err.as_str());
1567 -1
1568 }
1569 }
1570 } else {
1571 let receiver_ref = QuickJsValueAdapter::new(
1574 context,
1575 receiver,
1576 false,
1577 false,
1578 "reflection::proxy_instance_set_prop receiver",
1579 );
1580
1581 match realm.set_object_property(&receiver_ref, prop_name.as_str(), &value_ref) {
1582 Ok(()) => 0,
1583 Err(e) => {
1584 let err = format!("proxy_instance_set_prop failed, {}", e);
1585 log::error!("{}", err);
1586 let _ = realm.report_ex(err.as_str());
1587 -1
1588 }
1589 }
1590 }
1601 })
1602}
1603
1604#[cfg(test)]
1605pub mod tests {
1606 use crate::facades::tests::init_test_rt;
1607 use crate::jsutils::JsError;
1608 use crate::jsutils::Script;
1609 use crate::quickjs_utils::objects::create_object_q;
1610 use crate::quickjs_utils::{functions, primitives};
1611 use crate::reflection::{
1612 get_proxy_instance_proxy_and_instance_id_q, is_proxy_instance_q, Proxy,
1613 PROXY_INSTANCE_CLASS_ID,
1614 };
1615 use libquickjs_sys as q;
1616 use log::trace;
1617 use std::cell::RefCell;
1618 use std::collections::HashMap;
1619 use std::panic;
1620 use std::time::Duration;
1621
1622 thread_local! {
1623 static TEST_INSTANCES: RefCell<HashMap<usize, String>> = RefCell::new(HashMap::new())
1624 }
1625
1626 #[test]
1627 pub fn test_proxy1() {
1628 log::info!("> test_proxy");
1629
1630 let rt = init_test_rt();
1631 rt.exe_rt_task_in_event_loop(|q_js_rt| {
1632 q_js_rt.gc();
1633 let q_ctx = q_js_rt.get_main_realm();
1634 let _ = Proxy::new()
1635 .constructor(|_q_js_rt, _q_ctx, _id, _args| Ok(()))
1636 .name("Test")
1637 .install(q_ctx, true);
1638 q_ctx
1639 .eval(Script::new("test.es", "let t = new Test();"))
1640 .expect("script failed");
1641 });
1642 }
1643
1644 #[test]
1645 pub fn test_proxy_ex() {
1646 log::info!("> test_proxy");
1647
1648 let rt = init_test_rt();
1649 let err = rt.exe_rt_task_in_event_loop(|q_js_rt| {
1650 q_js_rt.gc();
1651 let q_ctx = q_js_rt.get_main_realm();
1652 let _ = Proxy::new()
1653 .constructor(|_q_js_rt, _q_ctx, _id, _args| Ok(()))
1654 .method("run", |_rt, _realm, _instance_id, _args| {
1655 Err(JsError::new_str("cant run"))
1656 })
1657 .name("Test")
1658 .install(q_ctx, true);
1659 let err = q_ctx
1660 .eval(Script::new("test.es", "let t = new Test(); \nt.run();"))
1661 .expect_err("script failed");
1662
1663 format!("{err}")
1664 });
1665
1666 assert!(err.contains("test.es:2"));
1667 assert!(err.contains("at Proxy instance method [run]"));
1668 assert!(err.contains("cant run"));
1669 }
1670
1671 #[test]
1672 pub fn test_proxy_instanceof() {
1673 log::info!("> test_proxy_instanceof");
1674
1675 let rt = init_test_rt();
1676 rt.exe_rt_task_in_event_loop(|q_js_rt| {
1677 q_js_rt.gc();
1678 let q_ctx = q_js_rt.get_main_realm();
1679 let _ = Proxy::new()
1680 .constructor(|_rt, _q_ctx, _id, _args| Ok(()))
1681 .namespace(&["com", "company"])
1682 .name("Test")
1683 .install(q_ctx, true);
1684 let res = q_ctx
1685 .eval(Script::new("test_tostring.es", "new com.company.Test()"))
1686 .expect("script failed");
1687 assert!(is_proxy_instance_q(q_ctx, &res));
1688 let info = get_proxy_instance_proxy_and_instance_id_q(q_ctx, &res)
1689 .expect("could not get info");
1690 let id = info.1;
1691 let p = info.0;
1692 println!("id={id}");
1693 assert_eq!(p.get_class_name().as_str(), "com.company.Test");
1694
1695 let some_obj = create_object_q(q_ctx).expect("could not create obj");
1696 assert!(some_obj.is_object());
1697
1698 let class_id = PROXY_INSTANCE_CLASS_ID.with(|rc| *rc.borrow());
1699 let proxy_class_proto: q::JSValue =
1700 unsafe { q::JS_GetClassProto(q_ctx.context, class_id) };
1701 let res = unsafe {
1703 q::JS_IsInstanceOf(q_ctx.context, *some_obj.borrow_value(), proxy_class_proto) != 0
1704 };
1705 println!("res = {res}");
1706 let res2 = is_proxy_instance_q(q_ctx, &some_obj);
1707 println!("res2 = {res2}");
1708 assert!(!res2);
1709 });
1710 }
1711
1712 #[test]
1713 pub fn test_rest_props() {
1714 log::info!("> test_rest_props");
1715
1716 let rt = init_test_rt();
1717 rt.exe_rt_task_in_event_loop(|q_js_rt| {
1718 q_js_rt.gc();
1719 let realm = q_js_rt.get_main_realm();
1720 let _ = Proxy::new()
1721 .constructor(|_rt, _q_ctx, _id, _args| Ok(()))
1722 .namespace(&["com", "company"])
1723 .name("Test")
1724 .install(realm, true);
1725 match realm.eval(Script::new(
1726 "test_tostring.js",
1727 r#"
1728 let t = new com.company.Test();
1729 t.foo = "bar";
1730 com.company.Test.sfoo = "sbar"
1731 t.foo + "_" + com.company.Test.sfoo;
1732 "#,
1733 )) {
1734 Ok(res) => {
1735 let s = res.to_string().expect("could not to_str");
1736 assert_eq!(s, "bar_sbar");
1737 }
1738 Err(e) => {
1739 panic!("e: {}", e);
1740 }
1741 }
1742 });
1743 }
1744
1745 #[test]
1746 pub fn test_instance_of() {
1747 log::info!("> test_instance_of");
1748
1749 let rt = init_test_rt();
1750 rt.exe_rt_task_in_event_loop(|q_js_rt| {
1751 q_js_rt.gc();
1752 let q_ctx = q_js_rt.get_main_realm();
1753 let _ = Proxy::new()
1754 .constructor(|_rt, _q_ctx, _id, _args| Ok(()))
1755 .namespace(&["com", "company"])
1756 .name("Test")
1757 .install(q_ctx, true);
1758 match q_ctx.eval(Script::new(
1759 "test_tostring.js",
1760 r#"
1761 let t = new com.company.Test();
1762 t instanceof com.company.Test
1763 "#,
1764 )) {
1765 Ok(res) => {
1766 let bln = res.to_bool();
1767 assert!(bln);
1768 }
1769 Err(e) => {
1770 panic!("e: {}", e);
1771 }
1772 }
1773 });
1774 }
1775
1776 #[test]
1777 pub fn test_to_string() {
1778 log::info!("> test_proxy");
1779
1780 let rt = init_test_rt();
1781 rt.exe_rt_task_in_event_loop(|q_js_rt| {
1782 q_js_rt.gc();
1783 let q_ctx = q_js_rt.get_main_realm();
1784 let _ = Proxy::new()
1785 .constructor(|_rt, _q_ctx, _id, _args| Ok(()))
1786 .namespace(&["com", "company"])
1787 .name("Test")
1788 .install(q_ctx, true);
1789 let res = q_ctx
1790 .eval(Script::new(
1791 "test_tostring.es",
1792 "com.company.Test + '-' + new com.company.Test()",
1793 ))
1794 .expect("script failed");
1795 let str = primitives::to_string_q(q_ctx, &res).expect("could not tostring");
1796 assert!(str.starts_with("Proxy::com.company.Test-Proxy::instance("));
1797 assert!(str.ends_with(")::com.company.Test"));
1798 });
1799 }
1800
1801 #[test]
1802 pub fn test_proxy() {
1803 log::info!("> test_proxy");
1804
1805 let rt = init_test_rt();
1806 rt.exe_rt_task_in_event_loop(|q_js_rt| {
1807 let q_ctx = q_js_rt.get_main_realm();
1808 let res = Proxy::new()
1809 .name("TestClass1")
1810 .constructor(|_rt, _context, id, _args| {
1811 TEST_INSTANCES.with(|rc| {
1812 let map = &mut *rc.borrow_mut();
1813 map.insert(id, "hi".to_string())
1814 });
1815 Ok(())
1816 })
1817 .method("doIt", |_rt, _context, _obj_id, _args| {
1818 Ok(primitives::from_i32(531))
1819 })
1820 .method("doIt2", |_rt, _context, _obj_id, _args| {
1821 Err(JsError::new_str("aaargh"))
1822 })
1823 .getter_setter(
1824 "gVar",
1825 |_rt, _context, _id| Ok(primitives::from_i32(147)),
1826 |_rt, _context, _id, _val| Ok(()),
1827 )
1828 .static_method("sDoIt", |_rt, _context, _args| {
1829 Ok(primitives::from_i32(9876))
1830 })
1831 .static_method("sDoIt2", |_rt, _context, _args| {
1832 Ok(primitives::from_i32(140))
1833 })
1834 .static_getter_setter(
1835 "someThing",
1836 |_rt, _context| {
1837 trace!("static getter called, returning 754");
1838 Ok(primitives::from_i32(754))
1839 },
1840 |_rt, q_ctx, val| {
1841 trace!(
1842 "static setter called, set to {}",
1843 functions::call_to_string_q(q_ctx, &val)?
1844 );
1845 Ok(())
1846 },
1847 )
1848 .finalizer(|_rt, _context, id| {
1849 TEST_INSTANCES.with(|rc| {
1850 let map = &mut *rc.borrow_mut();
1851 let _ = map.remove(&id);
1852 });
1853 log::trace!("ran finalizer: {}", id);
1854 })
1855 .install(q_ctx, true);
1856
1857 match res {
1858 Ok(_) => {}
1859 Err(e) => panic!("could not install proxy: {}", e),
1860 }
1861 });
1862
1863 let i2_res = rt.eval_sync(None, Script::new(
1864 "test_proxy.es",
1865 "let tc2 = new TestClass1(1, true, 'abc'); let r2 = tc2.doIt(1, true, 'abc'); console.log('< setting tc2 to null'); tc2 = null; console.log('> setting tc2 to null'); r2;"
1866 ,
1867 ));
1868 log::debug!("test_proxy.es done, ok = {}", i2_res.is_ok());
1869 match i2_res {
1870 Ok(i2) => {
1871 assert!(i2.is_i32());
1872 assert_eq!(i2.get_i32(), 531);
1873 }
1874 Err(e) => {
1875 log::error!("test_proxy.es failed with: {}", e);
1876 panic!("test_proxy.es failed");
1877 }
1878 }
1879
1880 let i = rt.eval_sync(None, Script::new(
1881 "test_proxy2.es",
1882 "let tc1 = new TestClass1(1, true, 'abc'); let r = tc1.doIt(1, true, 'abc'); r = tc1.doIt(1, true, 'abc'); tc1 = null; r;"
1883 ))
1884 .ok()
1885 .expect("script failed");
1886
1887 assert!(i.is_i32());
1888 assert_eq!(i.get_i32(), 531);
1889
1890 let i3_res = rt.eval_sync(None, Script::new("test_proxy.es", "TestClass1.sDoIt();"));
1891
1892 if i3_res.is_err() {
1893 panic!("script failed: {}", i3_res.err().unwrap());
1894 }
1895 let i3 = i3_res.ok().unwrap();
1896
1897 assert!(i3.is_i32());
1898 assert_eq!(i3.get_i32(), 9876);
1899
1900 let i4 = rt
1901 .eval_sync(
1902 None,
1903 Script::new(
1904 "test_proxy.es",
1905 "TestClass1.someThing = 1; TestClass1.someThing;",
1906 ),
1907 )
1908 .expect("script failed");
1909
1910 assert!(i4.is_i32());
1911 assert_eq!(i4.get_i32(), 754);
1912
1913 let i5 = rt
1914 .eval_sync(
1915 None,
1916 Script::new(
1917 "test_proxy.es",
1918 "let tc5 = new TestClass1(); let r5 = tc5.gVar; tc5 = null; r5;",
1919 ),
1920 )
1921 .expect("script failed");
1922
1923 assert!(i5.is_i32());
1924 assert_eq!(i5.get_i32(), 147);
1925
1926 let i6_res = rt.eval_sync(
1927 None,
1928 Script::new(
1929 "test_proxy.es",
1930 "let tc6 = new TestClass1(); let r6 = tc6.doIt2(); tc6 = null; r6;",
1931 ),
1932 );
1933 assert!(i6_res.is_err());
1934 let e = i6_res.err().unwrap();
1935 let e_msg = e.get_message();
1936 assert_eq!(e_msg, "proxy_instance_method failed: aaargh");
1937
1938 assert!(e.get_stack().contains("[doIt2]"));
1939
1940 rt.gc_sync();
1941
1942 std::thread::sleep(Duration::from_secs(1));
1943
1944 log::info!("< test_proxy");
1945 }
1946
1947 #[test]
1948 pub fn test_constructor() {
1949 let rt = init_test_rt();
1952 rt.loop_realm_sync(None, |_rt, realm| {
1953 Proxy::new()
1954 .name("TestClass")
1955 .namespace(&["com", "company"])
1956 .constructor(|_rt, _realm, _id, _args| Ok(()))
1957 .finalizer(|_rt, _realm, _id| {
1958 })
1960 .install(realm, true)
1961 .expect("poof");
1962
1963 let constr_str = realm
1964 .eval(Script::new(
1965 "test_constr.js",
1966 r#"
1967 let instance = new com.company.TestClass();
1968 const ret = "" + instance.constructor.name;
1969 instance = null;
1970 ret
1971 "#,
1972 ))
1973 .expect("script failed");
1974
1975 println!(
1976 "cons str = {}",
1977 constr_str.to_string().expect("not a string")
1978 );
1979 });
1980 }
1981}