1use crate::jsutils::JsError;
4use crate::quickjs_utils::properties::JSPropertyEnumRef;
5use crate::quickjs_utils::{atoms, functions, get_constructor, get_global};
6use crate::quickjsrealmadapter::QuickJsRealmAdapter;
7use crate::quickjsruntimeadapter::{make_cstring, QuickJsRuntimeAdapter};
8use crate::quickjsvalueadapter::QuickJsValueAdapter;
9use libquickjs_sys as q;
10
11pub fn get_namespace_q(
25 context: &QuickJsRealmAdapter,
26 namespace: &[&str],
27 create_if_absent: bool,
28) -> Result<QuickJsValueAdapter, JsError> {
29 unsafe { get_namespace(context.context, namespace, create_if_absent) }
30}
31
32pub unsafe fn get_namespace(
35 context: *mut q::JSContext,
36 namespace: &[&str],
37 create_if_absent: bool,
38) -> Result<QuickJsValueAdapter, JsError> {
39 log::trace!("objects::get_namespace({})", namespace.join("."));
40
41 let mut obj = get_global(context);
42 for p_name in namespace {
43 log::trace!("objects::get_namespace -> {}", p_name);
44 let mut sub = get_property(context, &obj, p_name)?;
45 if sub.is_null_or_undefined() {
46 log::trace!("objects::get_namespace -> is null");
47 if create_if_absent {
48 log::trace!("objects::get_namespace -> is null, creating");
49 sub = create_object(context)?;
51 set_property2(context, &obj, p_name, &sub, 0)?;
52 } else {
53 log::trace!("objects::get_namespace -> is null -> err");
54 return Err(JsError::new_string(format!(
55 "could not find namespace part: {p_name}"
56 )));
57 }
58 } else {
59 log::trace!("objects::get_namespace -> found");
60 }
61 obj = sub;
62 }
63
64 Ok(obj)
65}
66
67#[allow(dead_code)]
68pub unsafe fn construct_object(
72 ctx: *mut q::JSContext,
73 constructor_ref: &QuickJsValueAdapter,
74 args: &[&QuickJsValueAdapter],
75) -> Result<QuickJsValueAdapter, JsError> {
76 let arg_count = args.len() as i32;
77
78 let mut qargs = args.iter().map(|a| *a.borrow_value()).collect::<Vec<_>>();
79
80 let res = q::JS_CallConstructor(
81 ctx,
82 *constructor_ref.borrow_value(),
83 arg_count,
84 qargs.as_mut_ptr(),
85 );
86
87 let res_ref = QuickJsValueAdapter::new(ctx, res, false, true, "construct_object result");
88
89 if res_ref.is_exception() {
90 if let Some(ex) = QuickJsRealmAdapter::get_exception(ctx) {
91 Err(ex)
92 } else {
93 Err(JsError::new_str(
94 "construct_object failed but could not get ex",
95 ))
96 }
97 } else {
98 Ok(res_ref)
99 }
100}
101
102pub fn create_object_q(q_ctx: &QuickJsRealmAdapter) -> Result<QuickJsValueAdapter, JsError> {
104 unsafe { create_object(q_ctx.context) }
105}
106
107pub unsafe fn create_object(context: *mut q::JSContext) -> Result<QuickJsValueAdapter, JsError> {
111 let obj = q::JS_NewObject(context);
112 let obj_ref = QuickJsValueAdapter::new(context, obj, false, true, "objects::create_object");
113 if obj_ref.is_exception() {
114 return Err(JsError::new_str("Could not create object"));
115 }
116 Ok(obj_ref)
117}
118
119pub fn set_property_q(
121 q_ctx: &QuickJsRealmAdapter,
122 obj_ref: &QuickJsValueAdapter,
123 prop_name: &str,
124 prop_ref: &QuickJsValueAdapter,
125) -> Result<(), JsError> {
126 unsafe { set_property(q_ctx.context, obj_ref, prop_name, prop_ref) }
127}
128
129pub unsafe fn set_property(
133 context: *mut q::JSContext,
134 obj_ref: &QuickJsValueAdapter,
135 prop_name: &str,
136 prop_ref: &QuickJsValueAdapter,
137) -> Result<(), JsError> {
138 set_property2(
139 context,
140 obj_ref,
141 prop_name,
142 prop_ref,
143 q::JS_PROP_C_W_E as i32,
144 )
145}
146
147pub fn set_property2_q(
176 q_ctx: &QuickJsRealmAdapter,
177 obj_ref: &QuickJsValueAdapter,
178 prop_name: &str,
179 prop_ref: &QuickJsValueAdapter,
180 flags: i32,
181) -> Result<(), JsError> {
182 unsafe { set_property2(q_ctx.context, obj_ref, prop_name, prop_ref, flags) }
183}
184
185pub unsafe fn set_property2(
189 context: *mut q::JSContext,
190 obj_ref: &QuickJsValueAdapter,
191 prop_name: &str,
192 prop_ref: &QuickJsValueAdapter,
193 flags: i32,
194) -> Result<(), JsError> {
195 log::trace!("set_property2: {}", prop_name);
196
197 let ckey = make_cstring(prop_name)?;
198
199 log::trace!("set_property2 / 2");
213
214 let ret = q::JS_DefinePropertyValueStr(
215 context,
216 *obj_ref.borrow_value(),
217 ckey.as_ptr(),
218 prop_ref.clone_value_incr_rc(),
219 flags,
220 );
221 log::trace!("set_property2 / 3");
222 if ret < 0 {
223 return Err(JsError::new_str("Could not add property to object"));
224 }
225 log::trace!("set_property2 / 4");
226 Ok(())
227}
228
229pub fn define_getter_setter_q(
258 q_ctx: &QuickJsRealmAdapter,
259 obj_ref: &QuickJsValueAdapter,
260 prop_name: &str,
261 getter_func_ref: &QuickJsValueAdapter,
262 setter_func_ref: &QuickJsValueAdapter,
263) -> Result<(), JsError> {
264 unsafe {
265 define_getter_setter(
266 q_ctx.context,
267 obj_ref,
268 prop_name,
269 getter_func_ref,
270 setter_func_ref,
271 )
272 }
273}
274
275#[allow(dead_code)]
276pub unsafe fn define_getter_setter(
280 context: *mut q::JSContext,
281 obj_ref: &QuickJsValueAdapter,
282 prop_name: &str,
283 getter_func_ref: &QuickJsValueAdapter,
284 setter_func_ref: &QuickJsValueAdapter,
285) -> Result<(), JsError> {
286 log::trace!("objects::define_getter_setter 1");
298
299 debug_assert!(functions::is_function(context, getter_func_ref));
300 log::trace!("objects::define_getter_setter 2");
301 debug_assert!(functions::is_function(context, setter_func_ref));
302 log::trace!("objects::define_getter_setter 3");
303
304 let prop_atom = atoms::from_string(context, prop_name)?;
305
306 log::trace!("objects::define_getter_setter 4");
307
308 let res = q::JS_DefinePropertyGetSet(
309 context,
310 *obj_ref.borrow_value(),
311 prop_atom.get_atom(),
312 getter_func_ref.clone_value_incr_rc(),
313 setter_func_ref.clone_value_incr_rc(),
314 q::JS_PROP_C_W_E as i32,
315 );
316
317 log::trace!("objects::define_getter_setter 5 {}", res);
318
319 if res != 0 {
320 if let Some(err) = QuickJsRealmAdapter::get_exception(context) {
321 Err(err)
322 } else {
323 Err(JsError::new_str(
324 "Unknown error while creating getter setter",
325 ))
326 }
327 } else {
328 Ok(())
329 }
330}
331
332pub fn get_property_q(
334 q_ctx: &QuickJsRealmAdapter,
335 obj_ref: &QuickJsValueAdapter,
336 prop_name: &str,
337) -> Result<QuickJsValueAdapter, JsError> {
338 unsafe { get_property(q_ctx.context, obj_ref, prop_name) }
339}
340
341pub unsafe fn get_property(
345 context: *mut q::JSContext,
346 obj_ref: &QuickJsValueAdapter,
347 prop_name: &str,
348) -> Result<QuickJsValueAdapter, JsError> {
349 if obj_ref.is_null() || obj_ref.is_undefined() {
350 return Err(JsError::new_str(
351 "could not get prop from null or undefined",
352 ));
353 }
354
355 let c_prop_name = make_cstring(prop_name)?;
356
357 log::trace!("objects::get_property {}", prop_name);
358
359 let prop_val = q::JS_GetPropertyStr(context, *obj_ref.borrow_value(), c_prop_name.as_ptr());
360 let prop_ref = QuickJsValueAdapter::new(
361 context,
362 prop_val,
363 false,
364 true,
365 format!("object::get_property result: {prop_name}").as_str(),
366 );
367
368 Ok(prop_ref)
369}
370
371pub fn get_own_property_names_q(
373 q_ctx: &QuickJsRealmAdapter,
374 obj_ref: &QuickJsValueAdapter,
375) -> Result<JSPropertyEnumRef, JsError> {
376 unsafe { get_own_property_names(q_ctx.context, obj_ref) }
377}
378
379pub unsafe fn get_own_property_names(
383 context: *mut q::JSContext,
384 obj_ref: &QuickJsValueAdapter,
385) -> Result<JSPropertyEnumRef, JsError> {
386 let mut properties: *mut q::JSPropertyEnum = std::ptr::null_mut();
387 let mut count: u32 = 0;
388
389 let flags = (q::JS_GPN_STRING_MASK | q::JS_GPN_SYMBOL_MASK | q::JS_GPN_ENUM_ONLY) as i32;
390 let ret = q::JS_GetOwnPropertyNames(
391 context,
392 &mut properties,
393 &mut count,
394 *obj_ref.borrow_value(),
395 flags,
396 );
397 if ret != 0 {
398 return Err(JsError::new_str("Could not get object properties"));
399 }
400
401 let enum_ref = JSPropertyEnumRef::new(context, properties, count);
402 Ok(enum_ref)
403}
404
405pub fn get_property_names_q(
407 q_ctx: &QuickJsRealmAdapter,
408 obj_ref: &QuickJsValueAdapter,
409) -> Result<Vec<String>, JsError> {
410 unsafe { get_property_names(q_ctx.context, obj_ref) }
411}
412
413pub unsafe fn get_property_names(
417 context: *mut q::JSContext,
418 obj_ref: &QuickJsValueAdapter,
419) -> Result<Vec<String>, JsError> {
420 let enum_ref = get_own_property_names(context, obj_ref)?;
421
422 let mut names = vec![];
423
424 for index in 0..enum_ref.len() {
425 let name = enum_ref.get_name(index)?;
426 names.push(name);
427 }
428
429 Ok(names)
430}
431
432pub fn traverse_properties_q<V, R>(
433 q_ctx: &QuickJsRealmAdapter,
434 obj_ref: &QuickJsValueAdapter,
435 visitor: V,
436) -> Result<Vec<R>, JsError>
437where
438 V: Fn(&str, &QuickJsValueAdapter) -> Result<R, JsError>,
439{
440 unsafe { traverse_properties(q_ctx.context, obj_ref, visitor) }
441}
442
443pub fn traverse_properties_q_mut<V, R>(
444 q_ctx: &QuickJsRealmAdapter,
445 obj_ref: &QuickJsValueAdapter,
446 visitor: V,
447) -> Result<(), JsError>
448where
449 V: FnMut(&str, &QuickJsValueAdapter) -> Result<R, JsError>,
450{
451 unsafe { traverse_properties_mut(q_ctx.context, obj_ref, visitor) }
452}
453
454pub unsafe fn traverse_properties<V, R>(
457 context: *mut q::JSContext,
458 obj_ref: &QuickJsValueAdapter,
459 visitor: V,
460) -> Result<Vec<R>, JsError>
461where
462 V: Fn(&str, &QuickJsValueAdapter) -> Result<R, JsError>,
463{
464 let enum_ref = get_own_property_names(context, obj_ref)?;
465
466 let mut result = vec![];
467
468 for index in 0..enum_ref.len() {
469 let atom = enum_ref.get_atom_raw(index) as q::JSAtom;
470 let prop_name = atoms::to_str(context, &atom)?;
471
472 #[cfg(feature = "bellard")]
473 let raw_value = q::JS_GetPropertyInternal(
474 context,
475 *obj_ref.borrow_value(),
476 atom,
477 *obj_ref.borrow_value(),
478 0,
479 );
480 #[cfg(feature = "quickjs-ng")]
481 let raw_value = q::JS_GetProperty(context, *obj_ref.borrow_value(), atom);
482
483 let prop_val_ref = QuickJsValueAdapter::new(
484 context,
485 raw_value,
486 false,
487 true,
488 "objects::traverse_properties raw_value",
489 );
490 if prop_val_ref.is_exception() {
491 return Err(JsError::new_str("Could not get object property"));
492 }
493
494 let r = visitor(prop_name, &prop_val_ref)?;
495
496 result.push(r);
497 }
498
499 Ok(result)
500}
501
502pub unsafe fn traverse_properties_mut<V, R>(
505 context: *mut q::JSContext,
506 obj_ref: &QuickJsValueAdapter,
507 mut visitor: V,
508) -> Result<(), JsError>
509where
510 V: FnMut(&str, &QuickJsValueAdapter) -> Result<R, JsError>,
511{
512 let enum_ref = get_own_property_names(context, obj_ref)?;
513
514 for index in 0..enum_ref.len() {
515 let atom = enum_ref.get_atom_raw(index) as q::JSAtom;
516 let prop_name = atoms::to_str(context, &atom)?;
517
518 #[cfg(feature = "bellard")]
519 let raw_value = q::JS_GetPropertyInternal(
520 context,
521 *obj_ref.borrow_value(),
522 atom,
523 *obj_ref.borrow_value(),
524 0,
525 );
526 #[cfg(feature = "quickjs-ng")]
527 let raw_value = q::JS_GetProperty(context, *obj_ref.borrow_value(), atom);
528
529 let prop_val_ref = QuickJsValueAdapter::new(
530 context,
531 raw_value,
532 false,
533 true,
534 "objects::traverse_properties raw_value",
535 );
536 if prop_val_ref.is_exception() {
537 return Err(JsError::new_str("Could not get object property"));
538 }
539
540 visitor(prop_name, &prop_val_ref)?;
541 }
542
543 Ok(())
544}
545
546pub fn get_prototype_of_q(
547 q_ctx: &QuickJsRealmAdapter,
548 obj_ref: &QuickJsValueAdapter,
549) -> Result<QuickJsValueAdapter, JsError> {
550 unsafe { get_prototype_of(q_ctx.context, obj_ref) }
551}
552
553pub unsafe fn get_prototype_of(
557 ctx: *mut q::JSContext,
558 obj_ref: &QuickJsValueAdapter,
559) -> Result<QuickJsValueAdapter, JsError> {
560 let raw = q::JS_GetPrototype(ctx, *obj_ref.borrow_value());
561 let pt_ref = QuickJsValueAdapter::new(ctx, raw, false, true, "object::get_prototype_of_q");
562
563 if pt_ref.is_exception() {
564 if let Some(ex) = QuickJsRealmAdapter::get_exception(ctx) {
565 Err(ex)
566 } else {
567 Err(JsError::new_str(
568 "get_prototype_of_q failed but could not get ex",
569 ))
570 }
571 } else {
572 Ok(pt_ref)
573 }
574}
575
576pub fn is_instance_of_q(
577 q_ctx: &QuickJsRealmAdapter,
578 obj_ref: &QuickJsValueAdapter,
579 constructor_ref: &QuickJsValueAdapter,
580) -> bool {
581 unsafe { is_instance_of(q_ctx.context, obj_ref, constructor_ref) }
582}
583
584pub unsafe fn is_instance_of(
587 context: *mut q::JSContext,
588 obj_ref: &QuickJsValueAdapter,
589 constructor_ref: &QuickJsValueAdapter,
590) -> bool {
591 if !obj_ref.is_object() {
592 return false;
593 }
594
595 q::JS_IsInstanceOf(
596 context,
597 *obj_ref.borrow_value(),
598 *constructor_ref.borrow_value(),
599 ) > 0
600}
601
602pub fn is_instance_of_by_name_q(
603 context: &QuickJsRealmAdapter,
604 obj_ref: &QuickJsValueAdapter,
605 constructor_name: &str,
606) -> Result<bool, JsError> {
607 unsafe { is_instance_of_by_name(context.context, obj_ref, constructor_name) }
608}
609
610pub unsafe fn is_instance_of_by_name(
613 context: *mut q::JSContext,
614 obj_ref: &QuickJsValueAdapter,
615 constructor_name: &str,
616) -> Result<bool, JsError> {
617 if !obj_ref.is_object() {
618 return Ok(false);
619 }
620
621 let constructor_ref = get_constructor(context, constructor_name)?;
622 if !constructor_ref.is_object() {
623 return Ok(false);
624 }
625
626 if is_instance_of(context, obj_ref, &constructor_ref) {
627 Ok(true)
628 } else {
629 QuickJsRuntimeAdapter::do_with(|q_js_rt| {
631 let main_ctx = q_js_rt.get_main_realm();
632 let main_constructor_ref = get_constructor(main_ctx.context, constructor_name)?;
633 if is_instance_of(main_ctx.context, obj_ref, &main_constructor_ref) {
634 Ok(true)
635 } else {
636 Ok(false)
637 }
638 })
639 }
640}
641
642#[cfg(test)]
643pub mod tests {
644 use crate::facades::tests::init_test_rt;
645 use crate::jsutils::Script;
646 use crate::quickjs_utils::objects::{
647 create_object_q, get_property_names_q, get_property_q, set_property_q,
648 };
649 use crate::quickjs_utils::primitives::{from_i32, to_i32};
650 use crate::quickjs_utils::{get_global_q, primitives};
651
652 #[test]
653 fn test_get_refs() {
654 let rt = init_test_rt();
655 rt.exe_rt_task_in_event_loop(|q_js_rt| {
656 let q_ctx = q_js_rt.get_main_realm();
657 let obj = create_object_q(q_ctx).ok().expect("a");
658 let prop_ref = create_object_q(q_ctx).ok().expect("b");
659 let prop2_ref = create_object_q(q_ctx).ok().expect("c");
660 #[cfg(feature = "bellard")]
661 assert_eq!(obj.get_ref_count(), 1);
662 #[cfg(feature = "bellard")]
663 assert_eq!(prop_ref.get_ref_count(), 1);
664 set_property_q(q_ctx, &obj, "a", &prop_ref).ok().expect("d");
665 #[cfg(feature = "bellard")]
666 assert_eq!(prop_ref.get_ref_count(), 2);
667 set_property_q(q_ctx, &obj, "b", &prop_ref).ok().expect("e");
668 #[cfg(feature = "bellard")]
669 assert_eq!(prop_ref.get_ref_count(), 3);
670 set_property_q(q_ctx, &obj, "b", &prop2_ref)
671 .ok()
672 .expect("f");
673 #[cfg(feature = "bellard")]
674 assert_eq!(prop_ref.get_ref_count(), 2);
675 #[cfg(feature = "bellard")]
676 assert_eq!(prop2_ref.get_ref_count(), 2);
677
678 let p3 = get_property_q(q_ctx, &obj, "b").ok().expect("g");
679 #[cfg(feature = "bellard")]
680 assert_eq!(p3.get_ref_count(), 3);
681 #[cfg(feature = "bellard")]
682 assert_eq!(prop2_ref.get_ref_count(), 3);
683
684 drop(p3);
685 #[cfg(feature = "bellard")]
686 assert_eq!(prop2_ref.get_ref_count(), 2);
687
688 drop(obj);
689
690 q_js_rt.gc();
691 });
692 }
693
694 #[test]
695 fn test_get_n_drop() {
696 log::info!("> test_get_n_drop");
697
698 let rt = init_test_rt();
699 rt.exe_rt_task_in_event_loop(|q_js_rt| {
700 let q_ctx = q_js_rt.get_main_realm();
701
702 let obj_a = create_object_q(q_ctx).ok().unwrap();
703 let obj_b = create_object_q(q_ctx).ok().unwrap();
704 set_property_q(q_ctx, &obj_a, "b", &obj_b).ok().unwrap();
705
706 let b1 = get_property_q(q_ctx, &obj_a, "b").ok().unwrap();
707 set_property_q(q_ctx, &b1, "i", &primitives::from_i32(123))
708 .ok()
709 .unwrap();
710 drop(b1);
711 q_js_rt.gc();
712 let b2 = get_property_q(q_ctx, &obj_a, "b").ok().unwrap();
713 let i_ref = get_property_q(q_ctx, &b2, "i").ok().unwrap();
714 let i = to_i32(&i_ref).ok().unwrap();
715 drop(i_ref);
716 q_js_rt.gc();
717 let i_ref2 = get_property_q(q_ctx, &b2, "i").ok().unwrap();
718 let i2 = to_i32(&i_ref2).ok().unwrap();
719
720 assert_eq!(i, 123);
721 assert_eq!(i2, 123);
722 });
723
724 log::info!("< test_get_n_drop");
725 }
726
727 #[test]
728 fn test_propnames() {
729 log::info!("> test_propnames");
730
731 let rt = init_test_rt();
732 let io = rt.exe_rt_task_in_event_loop(|q_js_rt| {
733 let q_ctx = q_js_rt.get_main_realm();
734
735 let obj_ref = q_ctx
736 .eval(Script::new("test_propnames.es", "({one: 1, two: 2});"))
737 .ok()
738 .expect("could not get test obj");
739 let prop_names = get_property_names_q(q_ctx, &obj_ref)
740 .ok()
741 .expect("could not get prop names");
742
743 assert_eq!(prop_names.len(), 2);
744
745 assert!(prop_names.contains(&"one".to_string()));
746 assert!(prop_names.contains(&"two".to_string()));
747 true
748 });
749 assert!(io);
750
751 log::info!("< test_propnames");
752 }
753
754 #[test]
755 fn test_set_prop() {
756 log::info!("> test_set_prop");
757
758 let rt = init_test_rt();
759 let io = rt.exe_rt_task_in_event_loop(|q_js_rt| {
760 let q_ctx = q_js_rt.get_main_realm();
761
762 let obj_ref = create_object_q(q_ctx).ok().unwrap();
763
764 #[cfg(feature = "bellard")]
765 assert_eq!(obj_ref.get_ref_count(), 1);
766
767 let global_ref = get_global_q(q_ctx);
768 set_property_q(q_ctx, &global_ref, "test_obj", &obj_ref)
769 .ok()
770 .expect("could not set property 1");
771
772 #[cfg(feature = "bellard")]
773 assert_eq!(obj_ref.get_ref_count(), 2);
774
775 let prop_ref = from_i32(123);
776 let obj_ref = get_property_q(q_ctx, &global_ref, "test_obj")
777 .ok()
778 .expect("could not get test_obj");
779 set_property_q(q_ctx, &obj_ref, "test_prop", &prop_ref)
780 .ok()
781 .expect("could not set property 2");
782
783 drop(global_ref);
784
785 q_js_rt.gc();
786
787 let a = q_ctx
788 .eval(Script::new("test_set_prop.es", "(test_obj);"))
789 .ok()
790 .unwrap()
791 .is_object();
792 assert!(a);
793 let b = q_ctx
794 .eval(Script::new("test_set_prop.es", "(test_obj.test_prop);"))
795 .ok()
796 .unwrap()
797 .is_i32();
798 assert!(b);
799 a && b
800 });
801 assert!(io);
802
803 log::info!("< test_set_prop");
804 }
805}