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}