quickjs_runtime/quickjs_utils/
objects.rs

1//! Utils for working with objects
2
3use 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
11/// get a namespace object
12/// this is used to get nested object properties which are used as namespaces
13/// # Example
14/// ```rust
15/// use quickjs_runtime::builder::QuickJsRuntimeBuilder;
16/// use quickjs_runtime::quickjs_utils::objects::get_namespace_q;
17/// let rt = QuickJsRuntimeBuilder::new().build();
18/// rt.exe_rt_task_in_event_loop(|q_js_rt| {
19///     let q_ctx = q_js_rt.get_main_realm();
20///     let ns_obj = get_namespace_q(q_ctx, &["com", "hirofa", "examplepackage"], true).ok().unwrap();
21///     assert!(ns_obj.is_object())
22/// })
23/// ```
24pub 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
32/// # Safety
33/// when passing a context ptr please be sure that the corresponding QuickJsContext is still active
34pub 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                // create
50                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)]
68/// construct a new instance of a constructor
69/// # Safety
70/// please ensure the passed JSContext is still valid
71pub 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
102/// create a new simple object, e.g. `let obj = {};`
103pub fn create_object_q(q_ctx: &QuickJsRealmAdapter) -> Result<QuickJsValueAdapter, JsError> {
104    unsafe { create_object(q_ctx.context) }
105}
106
107/// create a new simple object, e.g. `let obj = {};`
108/// # Safety
109/// when passing a context ptr please be sure that the corresponding QuickJsContext is still active
110pub 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
119/// set a property in an object, like `obj[propName] = val;`
120pub 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
129/// set a property in an object, like `obj[propName] = val;`
130/// # Safety
131/// when passing a context ptr please be sure that the corresponding QuickJsContext is still active
132pub 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
147/// set a property with specific flags
148/// set_property applies the default flag JS_PROP_C_W_E (Configurable, Writable, Enumerable)
149/// flags you can use here are
150/// * q::JS_PROP_CONFIGURABLE
151/// * q::JS_PROP_WRITABLE
152/// * q::JS_PROP_ENUMERABLE          
153/// * q::JS_PROP_C_W_E         
154/// * q::JS_PROP_LENGTH        
155/// * q::JS_PROP_TMASK         
156/// * q::JS_PROP_NORMAL         
157/// * q::JS_PROP_GETSET         
158/// * q::JS_PROP_VARREF
159/// * q::JS_PROP_AUTOINIT
160/// # Example
161/// ```rust
162/// use quickjs_runtime::builder::QuickJsRuntimeBuilder;
163/// use quickjs_runtime::quickjs_utils::objects::{create_object_q, set_property2_q};
164/// use quickjs_runtime::quickjs_utils::primitives::from_i32;
165/// use libquickjs_sys as q;
166/// let rt = QuickJsRuntimeBuilder::new().build();
167/// rt.exe_rt_task_in_event_loop(|q_js_rt| {
168///    let q_ctx = q_js_rt.get_main_realm();
169///    let obj = create_object_q(q_ctx).ok().unwrap();
170///    let prop = from_i32(785);
171///    // not enumerable
172///    set_property2_q(q_ctx, &obj, "someProp", &prop, (q::JS_PROP_CONFIGURABLE | q::JS_PROP_WRITABLE) as i32).ok().unwrap();
173/// })
174/// ```                         
175pub 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
185/// set a property with specific flags
186/// # Safety
187/// When passing a context pointer please make sure the corresponding QuickJsContext is still valid
188pub 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    /*
200        pub const JS_PROP_CONFIGURABLE: u32 = 1;
201    pub const JS_PROP_WRITABLE: u32 = 2;
202    pub const JS_PROP_ENUMERABLE: u32 = 4;
203    pub const JS_PROP_C_W_E: u32 = 7;
204    pub const JS_PROP_LENGTH: u32 = 8;
205    pub const JS_PROP_TMASK: u32 = 48;
206    pub const JS_PROP_NORMAL: u32 = 0;
207    pub const JS_PROP_GETSET: u32 = 16;
208    pub const JS_PROP_VARREF: u32 = 32;
209    pub const JS_PROP_AUTOINIT: u32 = 48;
210        */
211
212    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
229/// define a getter/setter property
230/// # Example
231/// ```dontrun
232/// use quickjs_runtime::esruntimebuilder::EsRuntimeBuilder;
233/// use quickjs_runtime::quickjs_utils::objects::{create_object_q, define_getter_setter_q, set_property_q};
234/// use quickjs_runtime::quickjs_utils::functions::new_function_q;
235/// use quickjs_runtime::quickjs_utils::primitives::from_i32;
236/// use quickjs_runtime::quickjs_utils::{new_null_ref, get_global_q};
237/// use quickjs_runtime::jsutils::Script;
238/// use quickjs_runtime::JsError::JsError;
239/// let rt = EsRuntimeBuilder::new().build();
240/// rt.add_to_event_queue_sync(|q_js_rt| {
241///     let q_ctx = q_js_rt.get_main_context();
242///     let obj = create_object_q(q_ctx).ok().expect("create obj failed");
243///     let getter_func = new_function_q(q_ctx, "getter", |_q_ctx, _this_ref, _args| {Ok(from_i32(13))}, 0).ok().expect("new_function_q getter failed");
244///     let setter_func = new_function_q(q_ctx, "setter", |_q_ctx, _this_ref, args| {
245///         log::debug!("setting someProperty to {:?}", &args[0]);
246///         Ok(new_null_ref())
247///     }, 1).ok().expect("new_function_q setter failed");
248///     let res = define_getter_setter_q(q_ctx, &obj, "someProperty", &getter_func, &setter_func);
249///     match res {
250///         Ok(_) => {},
251///         Err(e) => {panic!("define_getter_setter_q fail: {}", e)}}
252///     let global = get_global_q(q_ctx);
253///     set_property_q(q_ctx, &global, "testObj431", &obj).ok().expect("set prop on global failed");
254/// });
255/// rt.eval_sync(Script::new("define_getter_setter_q.es", "testObj431.someProperty = 'hello prop';")).ok().expect("script failed");
256/// ```
257pub 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)]
276/// define a getter/setter property
277/// # Safety
278/// When passing a context pointer please make sure the corresponding QuickJsContext is still valid
279pub 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    /*
287     pub fn JS_DefinePropertyGetSet(
288        ctx: *mut JSContext,
289        this_obj: JSValue,
290        prop: JSAtom,
291        getter: JSValue,
292        setter: JSValue,
293        flags: ::std::os::raw::c_int,
294    ) -> ::std::os::raw::c_int;
295     */
296
297    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
332/// get a property from an object by name
333pub 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
341/// get a property from an object by name
342/// # Safety
343/// when passing a context please ensure the corresponding QuickJsContext is still valid
344pub 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
371/// get the property names of an object
372pub 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
379/// get the property names of an object
380/// # Safety
381/// When passing a context pointer please make sure the corresponding QuickJsContext is still valid
382pub 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
405/// get the names of all properties of an object
406pub 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
413/// get the names of all properties of an object
414/// # Safety
415/// When passing a context pointer please make sure the corresponding QuickJsContext is still valid
416pub 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
454/// # Safety
455/// When passing a context pointer please make sure the corresponding QuickJsContext is still valid
456pub 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
502/// # Safety
503/// When passing a context pointer please make sure the corresponding QuickJsContext is still valid
504pub 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
553/// Object.prototypeOf
554/// # Safety
555/// please ensure the JSContext is valid and remains valid while using this function
556pub 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
584/// # Safety
585/// When passing a context pointer please make sure the corresponding QuickJsContext is still valid
586pub 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
610/// # Safety
611/// When passing a context pointer please make sure the corresponding QuickJsContext is still valid
612pub 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        // todo check if context is not __main__
630        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}