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_str(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) {
1068 trace!("found method for {}", prop_name);
1069
1070 let function_data_ref =
1071 from_string(context, prop_name).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,
1077 1,
1078 function_data_ref,
1079 )
1080 .expect("could not create func");
1081
1082 objects::set_property(context, &receiver_ref, prop_name, &func_ref)
1083 .expect("set_property 9656738 failed");
1084
1085 func_ref.clone_value_incr_rc()
1086 } else if let Some(native_static_method) = proxy.static_native_methods.get(prop_name) {
1087 trace!("found static native method for {}", prop_name);
1088
1089 let func_ref = functions::new_native_function(
1090 context,
1091 prop_name,
1092 *native_static_method,
1093 1,
1094 false,
1095 )
1096 .expect("could not create func");
1097
1098 objects::set_property(context, &receiver_ref, prop_name, &func_ref)
1099 .expect("set_property 36099 failed");
1100
1101 func_ref.clone_value_incr_rc()
1102 } else if let Some(getter_setter) = proxy.static_getters_setters.get(prop_name) {
1103 let getter = &getter_setter.0;
1105 let res: Result<QuickJsValueAdapter, JsError> = getter(q_js_rt, q_ctx);
1106 match res {
1107 Ok(g_val) => g_val.clone_value_incr_rc(),
1108 Err(e) => {
1109 let es = format!("proxy_static_get_prop failed: {e}");
1110 q_ctx.report_ex(es.as_str())
1111 }
1112 }
1113 } else if let Some(catch_all_getter_setter) = &proxy.static_catch_all {
1114 let getter = &catch_all_getter_setter.0;
1116 let res: Result<QuickJsValueAdapter, JsError> = getter(q_js_rt, q_ctx, prop_name);
1117 match res {
1118 Ok(g_val) => g_val.clone_value_incr_rc(),
1119 Err(e) => {
1120 let es = format!("proxy_static_get_prop failed: {e}");
1121 q_ctx.report_ex(es.as_str())
1122 }
1123 }
1124 } else {
1125 quickjs_utils::new_undefined()
1126 }
1127 } else {
1128 q_ctx.report_ex("proxy class not found")
1129 }
1130 })
1131}
1132
1133#[allow(dead_code)]
1134unsafe extern "C" fn proxy_instance_get_prop(
1135 context: *mut q::JSContext,
1136 obj: q::JSValue,
1137 atom: q::JSAtom,
1138 receiver: q::JSValue,
1139) -> q::JSValue {
1140 trace!("proxy_instance_get_prop");
1141
1142 let _obj_ref = QuickJsValueAdapter::new(
1143 context,
1144 obj,
1145 false,
1146 false,
1147 "reflection::proxy_instance_get_prop obj",
1148 );
1149 let receiver_ref = QuickJsValueAdapter::new(
1150 context,
1151 receiver,
1152 false,
1153 false,
1154 "reflection::proxy_instance_get_prop receiver",
1155 );
1156
1157 QuickJsRuntimeAdapter::do_with(|q_js_rt| {
1158 let q_ctx = q_js_rt.get_quickjs_context(context);
1159
1160 let prop_name = atoms::to_str(context, &atom).expect("could not get name");
1161 trace!("proxy_instance_get_prop: {}", prop_name);
1162
1163 let info = get_proxy_instance_info(&obj);
1164
1165 trace!("obj_ref.classname = {}", info.class_name);
1166
1167 let registry = &*q_ctx.proxy_registry.borrow();
1170 let proxy = registry.get(&info.class_name).unwrap();
1171 if proxy.methods.contains_key(prop_name) {
1172 trace!("found method for {}", prop_name);
1173
1174 let function_data_ref =
1175 from_string(context, prop_name).expect("could not create function_data_ref");
1176
1177 let func_ref = functions::new_native_function_data(
1178 context,
1179 Some(proxy_instance_method),
1180 prop_name,
1181 1,
1182 function_data_ref,
1183 )
1184 .expect("could not create func");
1185
1186 objects::set_property(context, &receiver_ref, prop_name, &func_ref)
1187 .expect("set_property 96385 failed"); func_ref.clone_value_incr_rc()
1190 } else if let Some(native_method) = proxy.native_methods.get(prop_name) {
1191 trace!("found native method for {}", prop_name);
1192
1193 let func_ref =
1194 functions::new_native_function(context, prop_name, *native_method, 1, false)
1195 .expect("could not create func"); objects::set_property(context, &receiver_ref, prop_name, &func_ref)
1198 .expect("set_property 49671 failed"); func_ref.clone_value_incr_rc()
1201 } else if let Some(getter_setter) = proxy.getters_setters.get(prop_name) {
1202 let getter = &getter_setter.0;
1204 let res: Result<QuickJsValueAdapter, JsError> = getter(q_js_rt, q_ctx, &info.id);
1205 match res {
1206 Ok(g_val) => g_val.clone_value_incr_rc(),
1207 Err(e) => {
1208 let msg = format!("proxy_instance_get failed: {}", e.get_message());
1209 let nat_stack = format!(
1210 " at Proxy instance getter [{}]\n{}",
1211 prop_name,
1212 e.get_stack()
1213 );
1214 let err =
1215 errors::new_error(context, e.get_name(), msg.as_str(), nat_stack.as_str())
1216 .expect("create error failed");
1217 errors::throw(context, err)
1218 }
1219 }
1220 } else if let Some(catch_all_getter_setter) = &proxy.catch_all {
1221 let getter = &catch_all_getter_setter.0;
1223 let res: Result<QuickJsValueAdapter, JsError> =
1224 getter(q_js_rt, q_ctx, &info.id, prop_name);
1225 match res {
1226 Ok(g_val) => g_val.clone_value_incr_rc(),
1227 Err(e) => {
1228 let msg = format!("proxy_instance_catch_all_get failed: {}", e.get_message());
1229 let nat_stack = format!(
1230 " at Proxy instance getter [{}]\n{}",
1231 prop_name,
1232 e.get_stack()
1233 );
1234 let err =
1235 errors::new_error(context, e.get_name(), msg.as_str(), nat_stack.as_str())
1236 .expect("create error failed");
1237 errors::throw(context, err)
1238 }
1239 }
1240 } else {
1241 quickjs_utils::new_undefined()
1243 }
1244 })
1245
1246 }
1251#[allow(dead_code)]
1252unsafe extern "C" fn proxy_instance_has_prop(
1253 _context: *mut q::JSContext,
1254 _obj: q::JSValue,
1255 _atom: q::JSAtom,
1256) -> ::std::os::raw::c_int {
1257 todo!()
1258}
1259#[allow(dead_code)]
1260unsafe extern "C" fn proxy_static_has_prop(
1261 _context: *mut q::JSContext,
1262 _obj: q::JSValue,
1263 _atom: q::JSAtom,
1264) -> ::std::os::raw::c_int {
1265 todo!()
1266}
1267
1268unsafe extern "C" fn proxy_instance_method(
1269 context: *mut q::JSContext,
1270 this_val: q::JSValue,
1271 argc: ::std::os::raw::c_int,
1272 argv: *mut q::JSValue,
1273 _magic: ::std::os::raw::c_int,
1274 func_data: *mut q::JSValue,
1275) -> q::JSValue {
1276 trace!("proxy_instance_method");
1277 QuickJsRuntimeAdapter::do_with(|q_js_rt| {
1278 let q_ctx = q_js_rt.get_quickjs_context(context);
1279
1280 let proxy_instance_info: &ProxyInstanceInfo = get_proxy_instance_info(&this_val);
1281
1282 let args_vec = parse_args(context, argc, argv);
1283
1284 let func_name_ref = QuickJsValueAdapter::new(
1285 context,
1286 *func_data,
1287 false,
1288 false,
1289 "reflection::proxy_instance_method func_data",
1290 );
1291 let func_name = primitives::to_string(context, &func_name_ref)
1292 .expect("could not to_string func_name_ref");
1293
1294 trace!("proxy_instance_method: {}", func_name);
1295
1296 let registry = &*q_ctx.proxy_registry.borrow();
1297 let proxy = registry
1298 .get(proxy_instance_info.class_name.as_str())
1299 .unwrap();
1300 if let Some(method) = proxy.methods.get(func_name.as_str()) {
1301 let m_res: Result<QuickJsValueAdapter, JsError> =
1303 method(q_js_rt, q_ctx, &proxy_instance_info.id, &args_vec);
1304
1305 match m_res {
1306 Ok(m_res_ref) => m_res_ref.clone_value_incr_rc(),
1307 Err(e) => {
1308 let msg = format!("proxy_instance_method failed: {}", e.get_message());
1309 let nat_stack = format!(
1310 " at Proxy instance method [{}]\n{}",
1311 func_name,
1312 e.get_stack()
1313 );
1314 let err =
1315 errors::new_error(context, e.get_name(), msg.as_str(), nat_stack.as_str())
1316 .expect("create error failed");
1317 errors::throw(context, err)
1318 }
1319 }
1320 } else {
1321 quickjs_utils::new_undefined()
1323 }
1324 })
1325}
1326
1327#[allow(dead_code)]
1328unsafe extern "C" fn proxy_static_method(
1329 context: *mut q::JSContext,
1330 this_val: q::JSValue,
1331 argc: ::std::os::raw::c_int,
1332 argv: *mut q::JSValue,
1333 _magic: ::std::os::raw::c_int,
1334 func_data: *mut q::JSValue,
1335) -> q::JSValue {
1336 trace!("proxy_static_method");
1337 QuickJsRuntimeAdapter::do_with(|q_js_rt| {
1338 let q_ctx = q_js_rt.get_quickjs_context(context);
1339 let this_ref = QuickJsValueAdapter::new(
1340 context,
1341 this_val,
1342 false,
1343 false,
1344 "reflection::proxy_static_method this_val",
1345 );
1346
1347 let proxy_name_ref = objects::get_property(context, &this_ref, "name")
1348 .ok()
1349 .unwrap();
1350 let proxy_name =
1351 primitives::to_string(context, &proxy_name_ref).expect("could not to_string classname");
1352
1353 let args_vec = parse_args(context, argc, argv);
1354
1355 let func_name_ref = QuickJsValueAdapter::new(
1356 context,
1357 *func_data,
1358 false,
1359 false,
1360 "reflection::proxy_static_method func_data",
1361 );
1362 let func_name = primitives::to_string(context, &func_name_ref)
1363 .expect("could not to_string func_name_ref");
1364
1365 trace!("proxy_static_method: {}", func_name);
1366
1367 let registry = &*q_ctx.proxy_registry.borrow();
1368 let proxy = registry.get(proxy_name.as_str()).unwrap();
1369 if let Some(method) = proxy.static_methods.get(func_name.as_str()) {
1370 let m_res: Result<QuickJsValueAdapter, JsError> = method(q_js_rt, q_ctx, &args_vec);
1371 match m_res {
1372 Ok(m_res_ref) => m_res_ref.clone_value_incr_rc(),
1373 Err(e) => {
1374 let msg = format!("proxy_static_method failed: {}", e.get_message());
1375 let nat_stack = format!(
1376 " at Proxy static method [{}]\n{}",
1377 func_name,
1378 e.get_stack()
1379 );
1380 let err =
1381 errors::new_error(context, e.get_name(), msg.as_str(), nat_stack.as_str())
1382 .expect("create error failed");
1383 errors::throw(context, err)
1384 }
1385 }
1386 } else {
1387 quickjs_utils::new_undefined()
1389 }
1390 })
1391}
1392
1393unsafe extern "C" fn proxy_static_set_prop(
1394 context: *mut q::JSContext,
1395 _obj: q::JSValue,
1396 atom: q::JSAtom,
1397 value: q::JSValue,
1398 receiver: q::JSValue,
1399 _flags: ::std::os::raw::c_int,
1400) -> ::std::os::raw::c_int {
1401 trace!("proxy_static_set_prop");
1402
1403 let value_ref = QuickJsValueAdapter::new(
1404 context,
1405 value,
1406 false,
1407 false,
1408 "reflection::proxy_static_set_prop value",
1409 );
1410 let receiver_ref = QuickJsValueAdapter::new(
1411 context,
1412 receiver,
1413 false,
1414 false,
1415 "reflection::proxy_static_set_prop value",
1416 );
1417
1418 QuickJsRuntimeAdapter::do_with(|rt| {
1419 let realm = rt.get_quickjs_context(context);
1420
1421 let prop_name = atoms::to_str(context, &atom).expect("could not get name");
1422 trace!("proxy_static_set_prop: {}", prop_name);
1423
1424 let proxy_name_ref = objects::get_property(context, &receiver_ref, "name")
1427 .ok()
1428 .unwrap();
1429 let proxy_name = primitives::to_string(context, &proxy_name_ref)
1430 .ok()
1431 .unwrap();
1432 trace!("proxy_static_set_prop: {}", proxy_name);
1433
1434 let registry = &*realm.proxy_registry.borrow();
1435 if let Some(proxy) = registry.get(proxy_name.as_str()) {
1436 if let Some(getter_setter) = proxy.static_getters_setters.get(prop_name) {
1437 let setter = &getter_setter.1;
1439 let res: Result<(), JsError> = setter(rt, realm, value_ref);
1440 match res {
1441 Ok(_) => 0,
1442 Err(e) => {
1443 let err = format!("proxy_static_set_prop failed: {e}");
1445 log::error!("{}", err);
1446 let _ = realm.report_ex(err.as_str());
1447 -1
1448 }
1449 }
1450 } else if let Some(catch_all_getter_setter) = &proxy.static_catch_all {
1451 let setter = &catch_all_getter_setter.1;
1453 let res: Result<(), JsError> = setter(rt, realm, prop_name, value_ref);
1454 match res {
1455 Ok(_) => 0,
1456 Err(e) => {
1457 let err = format!("proxy_static_set_prop failed: {e}");
1459 log::error!("{}", err);
1460 let _ = realm.report_ex(err.as_str());
1461 -1
1462 }
1463 }
1464 } else {
1465 let receiver_ref = QuickJsValueAdapter::new(
1466 context,
1467 receiver,
1468 false,
1469 false,
1470 "reflection::proxy_static_set_prop receiver",
1471 );
1472
1473 match realm.set_object_property(&receiver_ref, prop_name, &value_ref) {
1474 Ok(()) => 0,
1475 Err(e) => {
1476 let err = format!("proxy_static_set_prop failed, {}", e);
1477 log::error!("{}", err);
1478 let _ = realm.report_ex(err.as_str());
1479 -1
1480 }
1481 }
1482 }
1490 } else {
1491 let err = "proxy_static_set_prop failed, no proxy found";
1492 log::error!("{}", err);
1493 let _ = realm.report_ex(err);
1494 -1
1495 }
1496 })
1497}
1498
1499unsafe extern "C" fn proxy_instance_set_prop(
1500 context: *mut q::JSContext,
1501 obj: q::JSValue,
1502 atom: q::JSAtom,
1503 value: q::JSValue,
1504 receiver: q::JSValue,
1505 _flags: ::std::os::raw::c_int,
1506) -> ::std::os::raw::c_int {
1507 trace!("proxy_instance_set_prop");
1508
1509 let value_ref = QuickJsValueAdapter::new(
1510 context,
1511 value,
1512 false,
1513 false,
1514 "reflection::proxy_instance_set_prop value",
1515 );
1516
1517 QuickJsRuntimeAdapter::do_with(|rt| {
1518 let realm = rt.get_quickjs_context(context);
1519
1520 let prop_name = atoms::to_str(context, &atom).expect("could not get name");
1521 trace!("proxy_instance_set_prop: {}", prop_name);
1522
1523 let info = get_proxy_instance_info(&obj);
1524
1525 trace!("obj_ref.classname = {}", info.class_name);
1526
1527 let registry = &*realm.proxy_registry.borrow();
1530 let proxy = registry.get(&info.class_name).unwrap();
1531
1532 if let Some(getter_setter) = proxy.getters_setters.get(prop_name) {
1533 let setter = &getter_setter.1;
1535 let res: Result<(), JsError> = setter(rt, realm, &info.id, value_ref);
1536 match res {
1537 Ok(_) => 0,
1538 Err(e) => {
1539 let err = format!("proxy_instance_set_prop failed: {e}");
1541 log::error!("{}", err);
1542 let _ = realm.report_ex(err.as_str());
1543 -1
1544 }
1545 }
1546 } else if let Some(catch_all_getter_setter) = &proxy.catch_all {
1547 let setter = &catch_all_getter_setter.1;
1549 let res: Result<(), JsError> = setter(rt, realm, &info.id, prop_name, value_ref);
1550 match res {
1551 Ok(_) => 0,
1552 Err(e) => {
1553 let err = format!("proxy_instance_set_prop failed: {e}");
1555 log::error!("{}", err);
1556 let _ = realm.report_ex(err.as_str());
1557 -1
1558 }
1559 }
1560 } else {
1561 let receiver_ref = QuickJsValueAdapter::new(
1564 context,
1565 receiver,
1566 false,
1567 false,
1568 "reflection::proxy_instance_set_prop receiver",
1569 );
1570
1571 match realm.set_object_property(&receiver_ref, prop_name, &value_ref) {
1572 Ok(()) => 0,
1573 Err(e) => {
1574 let err = format!("proxy_instance_set_prop failed, {}", e);
1575 log::error!("{}", err);
1576 let _ = realm.report_ex(err.as_str());
1577 -1
1578 }
1579 }
1580 }
1591 })
1592}
1593
1594#[cfg(test)]
1595pub mod tests {
1596 use crate::facades::tests::init_test_rt;
1597 use crate::jsutils::JsError;
1598 use crate::jsutils::Script;
1599 use crate::quickjs_utils::objects::create_object_q;
1600 use crate::quickjs_utils::{functions, primitives};
1601 use crate::reflection::{
1602 get_proxy_instance_proxy_and_instance_id_q, is_proxy_instance_q, Proxy,
1603 PROXY_INSTANCE_CLASS_ID,
1604 };
1605 use libquickjs_sys as q;
1606 use log::trace;
1607 use std::cell::RefCell;
1608 use std::collections::HashMap;
1609 use std::panic;
1610 use std::time::Duration;
1611
1612 thread_local! {
1613 static TEST_INSTANCES: RefCell<HashMap<usize, String>> = RefCell::new(HashMap::new())
1614 }
1615
1616 #[test]
1617 pub fn test_proxy1() {
1618 log::info!("> test_proxy");
1619
1620 let rt = init_test_rt();
1621 rt.exe_rt_task_in_event_loop(|q_js_rt| {
1622 q_js_rt.gc();
1623 let q_ctx = q_js_rt.get_main_realm();
1624 let _ = Proxy::new()
1625 .constructor(|_q_js_rt, _q_ctx, _id, _args| Ok(()))
1626 .name("Test")
1627 .install(q_ctx, true);
1628 q_ctx
1629 .eval(Script::new("test.es", "let t = new Test();"))
1630 .expect("script failed");
1631 });
1632 }
1633
1634 #[test]
1635 pub fn test_proxy_ex() {
1636 log::info!("> test_proxy");
1637
1638 let rt = init_test_rt();
1639 let err = rt.exe_rt_task_in_event_loop(|q_js_rt| {
1640 q_js_rt.gc();
1641 let q_ctx = q_js_rt.get_main_realm();
1642 let _ = Proxy::new()
1643 .constructor(|_q_js_rt, _q_ctx, _id, _args| Ok(()))
1644 .method("run", |_rt, _realm, _instance_id, _args| {
1645 Err(JsError::new_str("cant run"))
1646 })
1647 .name("Test")
1648 .install(q_ctx, true);
1649 let err = q_ctx
1650 .eval(Script::new("test.es", "let t = new Test(); \nt.run();"))
1651 .expect_err("script failed");
1652
1653 format!("{err}")
1654 });
1655
1656 assert!(err.contains("test.es:2"));
1657 assert!(err.contains("at Proxy instance method [run]"));
1658 assert!(err.contains("cant run"));
1659 }
1660
1661 #[test]
1662 pub fn test_proxy_instanceof() {
1663 log::info!("> test_proxy_instanceof");
1664
1665 let rt = init_test_rt();
1666 rt.exe_rt_task_in_event_loop(|q_js_rt| {
1667 q_js_rt.gc();
1668 let q_ctx = q_js_rt.get_main_realm();
1669 let _ = Proxy::new()
1670 .constructor(|_rt, _q_ctx, _id, _args| Ok(()))
1671 .namespace(&["com", "company"])
1672 .name("Test")
1673 .install(q_ctx, true);
1674 let res = q_ctx
1675 .eval(Script::new("test_tostring.es", "new com.company.Test()"))
1676 .expect("script failed");
1677 assert!(is_proxy_instance_q(q_ctx, &res));
1678 let info = get_proxy_instance_proxy_and_instance_id_q(q_ctx, &res)
1679 .expect("could not get info");
1680 let id = info.1;
1681 let p = info.0;
1682 println!("id={id}");
1683 assert_eq!(p.get_class_name().as_str(), "com.company.Test");
1684
1685 let some_obj = create_object_q(q_ctx).expect("could not create obj");
1686 assert!(some_obj.is_object());
1687
1688 let class_id = PROXY_INSTANCE_CLASS_ID.with(|rc| *rc.borrow());
1689 let proxy_class_proto: q::JSValue =
1690 unsafe { q::JS_GetClassProto(q_ctx.context, class_id) };
1691 let res = unsafe {
1693 q::JS_IsInstanceOf(q_ctx.context, *some_obj.borrow_value(), proxy_class_proto) != 0
1694 };
1695 println!("res = {res}");
1696 let res2 = is_proxy_instance_q(q_ctx, &some_obj);
1697 println!("res2 = {res2}");
1698 assert!(!res2);
1699 });
1700 }
1701
1702 #[test]
1703 pub fn test_rest_props() {
1704 log::info!("> test_rest_props");
1705
1706 let rt = init_test_rt();
1707 rt.exe_rt_task_in_event_loop(|q_js_rt| {
1708 q_js_rt.gc();
1709 let realm = q_js_rt.get_main_realm();
1710 let _ = Proxy::new()
1711 .constructor(|_rt, _q_ctx, _id, _args| Ok(()))
1712 .namespace(&["com", "company"])
1713 .name("Test")
1714 .install(realm, true);
1715 match realm.eval(Script::new(
1716 "test_tostring.js",
1717 r#"
1718 let t = new com.company.Test();
1719 t.foo = "bar";
1720 com.company.Test.sfoo = "sbar"
1721 t.foo + "_" + com.company.Test.sfoo;
1722 "#,
1723 )) {
1724 Ok(res) => {
1725 let s = res.to_str().expect("could not to_str");
1726 assert_eq!(s, "bar_sbar");
1727 }
1728 Err(e) => {
1729 panic!("e: {}", e);
1730 }
1731 }
1732 });
1733 }
1734
1735 #[test]
1736 pub fn test_instance_of() {
1737 log::info!("> test_instance_of");
1738
1739 let rt = init_test_rt();
1740 rt.exe_rt_task_in_event_loop(|q_js_rt| {
1741 q_js_rt.gc();
1742 let q_ctx = q_js_rt.get_main_realm();
1743 let _ = Proxy::new()
1744 .constructor(|_rt, _q_ctx, _id, _args| Ok(()))
1745 .namespace(&["com", "company"])
1746 .name("Test")
1747 .install(q_ctx, true);
1748 match q_ctx.eval(Script::new(
1749 "test_tostring.js",
1750 r#"
1751 let t = new com.company.Test();
1752 t instanceof com.company.Test
1753 "#,
1754 )) {
1755 Ok(res) => {
1756 let bln = res.to_bool();
1757 assert!(bln);
1758 }
1759 Err(e) => {
1760 panic!("e: {}", e);
1761 }
1762 }
1763 });
1764 }
1765
1766 #[test]
1767 pub fn test_to_string() {
1768 log::info!("> test_proxy");
1769
1770 let rt = init_test_rt();
1771 rt.exe_rt_task_in_event_loop(|q_js_rt| {
1772 q_js_rt.gc();
1773 let q_ctx = q_js_rt.get_main_realm();
1774 let _ = Proxy::new()
1775 .constructor(|_rt, _q_ctx, _id, _args| Ok(()))
1776 .namespace(&["com", "company"])
1777 .name("Test")
1778 .install(q_ctx, true);
1779 let res = q_ctx
1780 .eval(Script::new(
1781 "test_tostring.es",
1782 "com.company.Test + '-' + new com.company.Test()",
1783 ))
1784 .expect("script failed");
1785 let str = primitives::to_string_q(q_ctx, &res).expect("could not tostring");
1786 assert!(str.starts_with("Proxy::com.company.Test-Proxy::instance("));
1787 assert!(str.ends_with(")::com.company.Test"));
1788 });
1789 }
1790
1791 #[test]
1792 pub fn test_proxy() {
1793 log::info!("> test_proxy");
1794
1795 let rt = init_test_rt();
1796 rt.exe_rt_task_in_event_loop(|q_js_rt| {
1797 let q_ctx = q_js_rt.get_main_realm();
1798 let res = Proxy::new()
1799 .name("TestClass1")
1800 .constructor(|_rt, _context, id, _args| {
1801 TEST_INSTANCES.with(|rc| {
1802 let map = &mut *rc.borrow_mut();
1803 map.insert(id, "hi".to_string())
1804 });
1805 Ok(())
1806 })
1807 .method("doIt", |_rt, _context, _obj_id, _args| {
1808 Ok(primitives::from_i32(531))
1809 })
1810 .method("doIt2", |_rt, _context, _obj_id, _args| {
1811 Err(JsError::new_str("aaargh"))
1812 })
1813 .getter_setter(
1814 "gVar",
1815 |_rt, _context, _id| Ok(primitives::from_i32(147)),
1816 |_rt, _context, _id, _val| Ok(()),
1817 )
1818 .static_method("sDoIt", |_rt, _context, _args| {
1819 Ok(primitives::from_i32(9876))
1820 })
1821 .static_method("sDoIt2", |_rt, _context, _args| {
1822 Ok(primitives::from_i32(140))
1823 })
1824 .static_getter_setter(
1825 "someThing",
1826 |_rt, _context| {
1827 trace!("static getter called, returning 754");
1828 Ok(primitives::from_i32(754))
1829 },
1830 |_rt, q_ctx, val| {
1831 trace!(
1832 "static setter called, set to {}",
1833 functions::call_to_string_q(q_ctx, &val)?
1834 );
1835 Ok(())
1836 },
1837 )
1838 .finalizer(|_rt, _context, id| {
1839 TEST_INSTANCES.with(|rc| {
1840 let map = &mut *rc.borrow_mut();
1841 let _ = map.remove(&id);
1842 });
1843 log::trace!("ran finalizer: {}", id);
1844 })
1845 .install(q_ctx, true);
1846
1847 match res {
1848 Ok(_) => {}
1849 Err(e) => panic!("could not install proxy: {}", e),
1850 }
1851 });
1852
1853 let i2_res = rt.eval_sync(None, Script::new(
1854 "test_proxy.es",
1855 "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;"
1856 ,
1857 ));
1858 log::debug!("test_proxy.es done, ok = {}", i2_res.is_ok());
1859 match i2_res {
1860 Ok(i2) => {
1861 assert!(i2.is_i32());
1862 assert_eq!(i2.get_i32(), 531);
1863 }
1864 Err(e) => {
1865 log::error!("test_proxy.es failed with: {}", e);
1866 panic!("test_proxy.es failed");
1867 }
1868 }
1869
1870 let i = rt.eval_sync(None, Script::new(
1871 "test_proxy2.es",
1872 "let tc1 = new TestClass1(1, true, 'abc'); let r = tc1.doIt(1, true, 'abc'); r = tc1.doIt(1, true, 'abc'); tc1 = null; r;"
1873 ))
1874 .ok()
1875 .expect("script failed");
1876
1877 assert!(i.is_i32());
1878 assert_eq!(i.get_i32(), 531);
1879
1880 let i3_res = rt.eval_sync(None, Script::new("test_proxy.es", "TestClass1.sDoIt();"));
1881
1882 if i3_res.is_err() {
1883 panic!("script failed: {}", i3_res.err().unwrap());
1884 }
1885 let i3 = i3_res.ok().unwrap();
1886
1887 assert!(i3.is_i32());
1888 assert_eq!(i3.get_i32(), 9876);
1889
1890 let i4 = rt
1891 .eval_sync(
1892 None,
1893 Script::new(
1894 "test_proxy.es",
1895 "TestClass1.someThing = 1; TestClass1.someThing;",
1896 ),
1897 )
1898 .expect("script failed");
1899
1900 assert!(i4.is_i32());
1901 assert_eq!(i4.get_i32(), 754);
1902
1903 let i5 = rt
1904 .eval_sync(
1905 None,
1906 Script::new(
1907 "test_proxy.es",
1908 "let tc5 = new TestClass1(); let r5 = tc5.gVar; tc5 = null; r5;",
1909 ),
1910 )
1911 .expect("script failed");
1912
1913 assert!(i5.is_i32());
1914 assert_eq!(i5.get_i32(), 147);
1915
1916 let i6_res = rt.eval_sync(
1917 None,
1918 Script::new(
1919 "test_proxy.es",
1920 "let tc6 = new TestClass1(); let r6 = tc6.doIt2(); tc6 = null; r6;",
1921 ),
1922 );
1923 assert!(i6_res.is_err());
1924 let e = i6_res.err().unwrap();
1925 let e_msg = e.get_message();
1926 assert_eq!(e_msg, "proxy_instance_method failed: aaargh");
1927
1928 assert!(e.get_stack().contains("[doIt2]"));
1929
1930 rt.gc_sync();
1931
1932 std::thread::sleep(Duration::from_secs(1));
1933
1934 log::info!("< test_proxy");
1935 }
1936
1937 #[test]
1938 pub fn test_constructor() {
1939 let rt = init_test_rt();
1942 rt.loop_realm_sync(None, |_rt, realm| {
1943 Proxy::new()
1944 .name("TestClass")
1945 .namespace(&["com", "company"])
1946 .constructor(|_rt, _realm, _id, _args| Ok(()))
1947 .finalizer(|_rt, _realm, _id| {
1948 })
1950 .install(realm, true)
1951 .expect("poof");
1952
1953 let constr_str = realm
1954 .eval(Script::new(
1955 "test_constr.js",
1956 r#"
1957 let instance = new com.company.TestClass();
1958 const ret = "" + instance.constructor.name;
1959 instance = null;
1960 ret
1961 "#,
1962 ))
1963 .expect("script failed");
1964
1965 println!(
1966 "cons str = {}",
1967 constr_str.to_string().expect("not a string")
1968 );
1969 });
1970 }
1971}