quickjs_runtime/quickjs_utils/
arrays.rs

1use crate::jsutils::JsError;
2use crate::quickjsrealmadapter::QuickJsRealmAdapter;
3use crate::quickjsvalueadapter::QuickJsValueAdapter;
4use libquickjs_sys as q;
5
6/// Check whether an object is an array
7/// # Example
8/// ```rust
9/// use quickjs_runtime::builder::QuickJsRuntimeBuilder;
10/// use quickjs_runtime::jsutils::Script;
11/// use quickjs_runtime::quickjs_utils::arrays;
12///
13/// let rt = QuickJsRuntimeBuilder::new().build();
14/// rt.exe_rt_task_in_event_loop(|q_js_rt| {
15///     let q_ctx = q_js_rt.get_main_realm();
16///     let obj_ref = q_ctx.eval(Script::new("is_array_test.es", "([1, 2, 3]);")).ok().expect("script failed");
17///     let is_array = arrays::is_array_q(q_ctx, &obj_ref);
18///     assert!(is_array);
19/// });
20/// ```
21pub fn is_array_q(q_ctx: &QuickJsRealmAdapter, obj_ref: &QuickJsValueAdapter) -> bool {
22    unsafe { is_array(q_ctx.context, obj_ref) }
23}
24
25/// # Safety
26/// When passing a context pointer please make sure the corresponding QuickJsContext is still valid
27#[allow(unused_variables)]
28pub unsafe fn is_array(context: *mut q::JSContext, obj_ref: &QuickJsValueAdapter) -> bool {
29    let r = obj_ref.borrow_value();
30
31    #[cfg(feature = "bellard")]
32    {
33        let val = q::JS_IsArray(context, *r);
34        val > 0
35    }
36    #[cfg(feature = "quickjs-ng")]
37    {
38        q::JS_IsArray(*r)
39    }
40}
41
42/// Get the length of an Array
43/// # Example
44/// ```rust
45/// use quickjs_runtime::builder::QuickJsRuntimeBuilder;
46/// use quickjs_runtime::jsutils::Script;
47/// use quickjs_runtime::quickjs_utils::arrays;
48///
49/// let rt = QuickJsRuntimeBuilder::new().build();
50/// rt.exe_rt_task_in_event_loop(|q_js_rt| {
51///     let q_ctx = q_js_rt.get_main_realm();
52///     let obj_ref = q_ctx.eval(Script::new("get_length_test.es", "([1, 2, 3]);")).ok().expect("script failed");
53///     let len = arrays::get_length_q(q_ctx, &obj_ref).ok().expect("could not get length");
54///     assert_eq!(len, 3);
55/// });
56/// ```
57pub fn get_length_q(
58    q_ctx: &QuickJsRealmAdapter,
59    arr_ref: &QuickJsValueAdapter,
60) -> Result<u32, JsError> {
61    unsafe { get_length(q_ctx.context, arr_ref) }
62}
63
64/// Get the length of an Array
65/// # Safety
66/// When passing a context pointer please make sure the corresponding QuickJsContext is still valid
67pub unsafe fn get_length(
68    context: *mut q::JSContext,
69    arr_ref: &QuickJsValueAdapter,
70) -> Result<u32, JsError> {
71    let len_ref = crate::quickjs_utils::objects::get_property(context, arr_ref, "length")?;
72
73    let len = crate::quickjs_utils::primitives::to_i32(&len_ref)?;
74
75    Ok(len as u32)
76}
77
78/// Create a new Array
79/// # Example
80/// ```rust
81/// use quickjs_runtime::builder::QuickJsRuntimeBuilder;
82/// use quickjs_runtime::jsutils::Script;
83/// use quickjs_runtime::quickjs_utils::{arrays, primitives, functions};
84/// use quickjs_runtime::quickjs_utils;
85///
86/// let rt = QuickJsRuntimeBuilder::new().build();
87/// rt.exe_rt_task_in_event_loop(|q_js_rt| {
88///     let q_ctx = q_js_rt.get_main_realm();
89///     // create a method to pass our new array to
90///     q_ctx.eval(Script::new("create_array_test.es", "this.create_array_func = function(arr){return arr.length;};")).ok().expect("script failed");
91///     // create a new array
92///     let arr_ref = arrays::create_array_q(q_ctx).ok().expect("could not create array");
93///     // add some values
94///     let val0 = primitives::from_i32(12);
95///     let val1 = primitives::from_i32(17);
96///     arrays::set_element_q(q_ctx, &arr_ref, 0, &val0).expect("could not set element");
97///     arrays::set_element_q(q_ctx, &arr_ref, 1, &val1).expect("could not set element");
98///     // call the function
99///     let result_ref = functions::invoke_member_function_q(q_ctx, &quickjs_utils::get_global_q(q_ctx), "create_array_func", &[arr_ref]).ok().expect("could not invoke function");
100///     let len = primitives::to_i32(&result_ref).ok().unwrap();
101///     assert_eq!(len, 2);
102/// });
103/// ```
104pub fn create_array_q(q_ctx: &QuickJsRealmAdapter) -> Result<QuickJsValueAdapter, JsError> {
105    unsafe { create_array(q_ctx.context) }
106}
107
108/// # Safety
109/// When passing a context pointer please make sure the corresponding QuickJsContext is still valid
110pub unsafe fn create_array(context: *mut q::JSContext) -> Result<QuickJsValueAdapter, JsError> {
111    let arr = q::JS_NewArray(context);
112    let arr_ref = QuickJsValueAdapter::new(context, arr, false, true, "create_array");
113    if arr_ref.is_exception() {
114        return Err(JsError::new_str("Could not create array in runtime"));
115    }
116    Ok(arr_ref)
117}
118
119/// Set a single element in an array
120/// # Example
121/// ```rust
122/// use quickjs_runtime::builder::QuickJsRuntimeBuilder;
123/// use quickjs_runtime::jsutils::Script;
124/// use quickjs_runtime::quickjs_utils::{arrays, primitives};
125/// use quickjs_runtime::quickjs_utils;
126///
127/// let rt = QuickJsRuntimeBuilder::new().build();
128/// rt.exe_rt_task_in_event_loop(|q_js_rt| {
129///     let q_ctx = q_js_rt.get_main_realm();
130///     // get an Array from script
131///     let arr_ref = q_ctx.eval(Script::new("set_element_test.es", "([1, 2, 3]);")).ok().expect("script failed");
132///     // add some values
133///     arrays::set_element_q(q_ctx, &arr_ref, 3, &primitives::from_i32(12)).expect("could not set element");
134///     arrays::set_element_q(q_ctx, &arr_ref, 4, &primitives::from_i32(17)).expect("could not set element");
135///     // get the length
136///     let len = arrays::get_length_q(q_ctx, &arr_ref).ok().unwrap();
137///     assert_eq!(len, 5);
138/// });
139/// ```
140pub fn set_element_q(
141    q_ctx: &QuickJsRealmAdapter,
142    array_ref: &QuickJsValueAdapter,
143    index: u32,
144    entry_value_ref: &QuickJsValueAdapter,
145) -> Result<(), JsError> {
146    unsafe { set_element(q_ctx.context, array_ref, index, entry_value_ref) }
147}
148
149/// # Safety
150/// When passing a context pointer please make sure the corresponding QuickJsContext is still valid
151pub unsafe fn set_element(
152    context: *mut q::JSContext,
153    array_ref: &QuickJsValueAdapter,
154    index: u32,
155    entry_value_ref: &QuickJsValueAdapter,
156) -> Result<(), JsError> {
157    let ret = q::JS_DefinePropertyValueUint32(
158        context,
159        *array_ref.borrow_value(),
160        index,
161        entry_value_ref.clone_value_incr_rc(),
162        q::JS_PROP_C_W_E as i32,
163    );
164    if ret < 0 {
165        return Err(JsError::new_str("Could not append element to array"));
166    }
167    Ok(())
168}
169
170/// Get a single element from an array
171/// # Example
172/// ```rust
173/// use quickjs_runtime::builder::QuickJsRuntimeBuilder;
174/// use quickjs_runtime::jsutils::Script;
175/// use quickjs_runtime::quickjs_utils::{arrays, primitives};
176/// use quickjs_runtime::quickjs_utils;
177///
178/// let rt = QuickJsRuntimeBuilder::new().build();
179/// rt.exe_rt_task_in_event_loop(|q_js_rt| {
180///     let q_ctx = q_js_rt.get_main_realm();
181///     // get an Array from script
182///     let arr_ref = q_ctx.eval(Script::new("get_element_test.es", "([1, 2, 3]);")).ok().expect("script failed");
183///     // get a value, the 3 in this case
184///     let val_ref = arrays::get_element_q(q_ctx, &arr_ref, 2).ok().unwrap();
185///     let val_i32 = primitives::to_i32(&val_ref).ok().unwrap();
186///     // get the length
187///     assert_eq!(val_i32, 3);
188/// });
189/// ```
190pub fn get_element_q(
191    q_ctx: &QuickJsRealmAdapter,
192    array_ref: &QuickJsValueAdapter,
193    index: u32,
194) -> Result<QuickJsValueAdapter, JsError> {
195    unsafe { get_element(q_ctx.context, array_ref, index) }
196}
197
198/// # Safety
199/// When passing a context pointer please make sure the corresponding QuickJsContext is still valid
200pub unsafe fn get_element(
201    context: *mut q::JSContext,
202    array_ref: &QuickJsValueAdapter,
203    index: u32,
204) -> Result<QuickJsValueAdapter, JsError> {
205    let value_raw = q::JS_GetPropertyUint32(context, *array_ref.borrow_value(), index);
206    let ret = QuickJsValueAdapter::new(
207        context,
208        value_raw,
209        false,
210        true,
211        format!("get_element[{index}]").as_str(),
212    );
213    if ret.is_exception() {
214        return Err(JsError::new_str("Could not build array"));
215    }
216    Ok(ret)
217}
218
219#[cfg(test)]
220pub mod tests {
221    use crate::facades::tests::init_test_rt;
222    use crate::quickjs_utils::arrays::{create_array_q, get_element_q, set_element_q};
223    use crate::quickjs_utils::objects;
224
225    #[test]
226    fn test_array() {
227        let rt = init_test_rt();
228        rt.exe_rt_task_in_event_loop(|q_js_rt| {
229            let q_ctx = q_js_rt.get_main_realm();
230            let arr = create_array_q(q_ctx).ok().unwrap();
231
232            #[cfg(feature = "bellard")]
233            assert_eq!(arr.get_ref_count(), 1);
234
235            let a = objects::create_object_q(q_ctx).ok().unwrap();
236
237            #[cfg(feature = "bellard")]
238            assert_eq!(1, a.get_ref_count());
239
240            set_element_q(q_ctx, &arr, 0, &a).ok().unwrap();
241
242            #[cfg(feature = "bellard")]
243            assert_eq!(2, a.get_ref_count());
244
245            let _a2 = get_element_q(q_ctx, &arr, 0).ok().unwrap();
246
247            #[cfg(feature = "bellard")]
248            assert_eq!(3, a.get_ref_count());
249            #[cfg(feature = "bellard")]
250            assert_eq!(3, _a2.get_ref_count());
251        });
252    }
253}