quickjs_runtime/quickjs_utils/
mod.rs

1//! low level contains utils for calling the quickjs api
2
3use crate::quickjsruntimeadapter::QuickJsRuntimeAdapter;
4
5#[cfg(feature = "bellard")]
6pub mod class_ids {
7    pub const JS_CLASS_OBJECT: u32 = 1;
8    pub const JS_CLASS_ARRAY: u32 = 2;
9    pub const JS_CLASS_ERROR: u32 = 3;
10    pub const JS_CLASS_NUMBER: u32 = 4;
11    pub const JS_CLASS_STRING: u32 = 5;
12    pub const JS_CLASS_BOOLEAN: u32 = 6;
13    pub const JS_CLASS_SYMBOL: u32 = 7;
14    pub const JS_CLASS_ARGUMENTS: u32 = 8;
15    pub const JS_CLASS_MAPPED_ARGUMENTS: u32 = 9;
16    pub const JS_CLASS_DATE: u32 = 10;
17    pub const JS_CLASS_MODULE_NS: u32 = 11;
18    pub const JS_CLASS_C_FUNCTION: u32 = 12;
19    pub const JS_CLASS_BYTECODE_FUNCTION: u32 = 13;
20    pub const JS_CLASS_BOUND_FUNCTION: u32 = 14;
21    pub const JS_CLASS_C_FUNCTION_DATA: u32 = 15;
22    pub const JS_CLASS_GENERATOR_FUNCTION: u32 = 16;
23    pub const JS_CLASS_FOR_IN_ITERATOR: u32 = 17;
24    pub const JS_CLASS_REGEXP: u32 = 18;
25    pub const JS_CLASS_ARRAY_BUFFER: u32 = 19;
26    pub const JS_CLASS_SHARED_ARRAY_BUFFER: u32 = 20;
27    pub const JS_CLASS_UINT8C_ARRAY: u32 = 21;
28    pub const JS_CLASS_INT8_ARRAY: u32 = 22;
29    pub const JS_CLASS_UINT8_ARRAY: u32 = 23;
30    pub const JS_CLASS_INT16_ARRAY: u32 = 24;
31    pub const JS_CLASS_UINT16_ARRAY: u32 = 25;
32    pub const JS_CLASS_INT32_ARRAY: u32 = 26;
33    pub const JS_CLASS_UINT32_ARRAY: u32 = 27;
34    pub const JS_CLASS_BIG_INT64_ARRAY: u32 = 28;
35    pub const JS_CLASS_BIG_UINT64_ARRAY: u32 = 29;
36    pub const JS_CLASS_FLOAT16_ARRAY: u32 = 30;
37    pub const JS_CLASS_FLOAT32_ARRAY: u32 = 31;
38    pub const JS_CLASS_FLOAT64_ARRAY: u32 = 32;
39    pub const JS_CLASS_DATAVIEW: u32 = 33;
40    pub const JS_CLASS_BIG_INT: u32 = 34;
41    pub const JS_CLASS_MAP: u32 = 35;
42    pub const JS_CLASS_SET: u32 = 36;
43    pub const JS_CLASS_WEAKMAP: u32 = 37;
44    pub const JS_CLASS_WEAKSET: u32 = 38;
45    pub const JS_CLASS_MAP_ITERATOR: u32 = 39;
46    pub const JS_CLASS_SET_ITERATOR: u32 = 40;
47    pub const JS_CLASS_ARRAY_ITERATOR: u32 = 41;
48    pub const JS_CLASS_STRING_ITERATOR: u32 = 42;
49    pub const JS_CLASS_REGEXP_STRING_ITERATOR: u32 = 43;
50    pub const JS_CLASS_GENERATOR: u32 = 44;
51    pub const JS_CLASS_PROXY: u32 = 45;
52    pub const JS_CLASS_PROMISE: u32 = 46;
53    pub const JS_CLASS_PROMISE_RESOLVE_FUNCTION: u32 = 47;
54    pub const JS_CLASS_PROMISE_REJECT_FUNCTION: u32 = 48;
55    pub const JS_CLASS_ASYNC_FUNCTION: u32 = 49;
56    pub const JS_CLASS_ASYNC_FUNCTION_RESOLVE: u32 = 50;
57    pub const JS_CLASS_ASYNC_FUNCTION_REJECT: u32 = 51;
58    pub const JS_CLASS_ASYNC_FROM_SYNC_ITERATOR: u32 = 52;
59    pub const JS_CLASS_ASYNC_GENERATOR_FUNCTION: u32 = 53;
60    pub const JS_CLASS_ASYNC_GENERATOR: u32 = 54;
61    pub const JS_CLASS_WEAK_REF: u32 = 55;
62    pub const JS_CLASS_FINALIZATION_REGISTRY: u32 = 56;
63    pub const JS_CLASS_INIT_COUNT: u32 = 57;
64}
65#[cfg(feature = "quickjs-ng")]
66pub mod class_ids {
67    pub const JS_CLASS_OBJECT: u32 = 1;
68    pub const JS_CLASS_ARRAY: u32 = 2;
69    pub const JS_CLASS_ERROR: u32 = 3;
70    pub const JS_CLASS_NUMBER: u32 = 4;
71    pub const JS_CLASS_STRING: u32 = 5;
72    pub const JS_CLASS_BOOLEAN: u32 = 6;
73    pub const JS_CLASS_SYMBOL: u32 = 7;
74    pub const JS_CLASS_ARGUMENTS: u32 = 8;
75    pub const JS_CLASS_MAPPED_ARGUMENTS: u32 = 9;
76    pub const JS_CLASS_DATE: u32 = 10;
77    pub const JS_CLASS_MODULE_NS: u32 = 11;
78    pub const JS_CLASS_C_FUNCTION: u32 = 12;
79    pub const JS_CLASS_BYTECODE_FUNCTION: u32 = 13;
80    pub const JS_CLASS_BOUND_FUNCTION: u32 = 14;
81    pub const JS_CLASS_C_FUNCTION_DATA: u32 = 15;
82    pub const JS_CLASS_GENERATOR_FUNCTION: u32 = 16;
83    pub const JS_CLASS_FOR_IN_ITERATOR: u32 = 17;
84    pub const JS_CLASS_REGEXP: u32 = 18;
85    pub const JS_CLASS_ARRAY_BUFFER: u32 = 19;
86    pub const JS_CLASS_SHARED_ARRAY_BUFFER: u32 = 20;
87    pub const JS_CLASS_UINT8C_ARRAY: u32 = 21;
88    pub const JS_CLASS_INT8_ARRAY: u32 = 22;
89    pub const JS_CLASS_UINT8_ARRAY: u32 = 23;
90    pub const JS_CLASS_INT16_ARRAY: u32 = 24;
91    pub const JS_CLASS_UINT16_ARRAY: u32 = 25;
92    pub const JS_CLASS_INT32_ARRAY: u32 = 26;
93    pub const JS_CLASS_UINT32_ARRAY: u32 = 27;
94    pub const JS_CLASS_BIG_INT64_ARRAY: u32 = 28;
95    pub const JS_CLASS_BIG_UINT64_ARRAY: u32 = 29;
96    pub const JS_CLASS_FLOAT16_ARRAY: u32 = 30;
97    pub const JS_CLASS_FLOAT32_ARRAY: u32 = 31;
98    pub const JS_CLASS_FLOAT64_ARRAY: u32 = 32;
99    pub const JS_CLASS_DATAVIEW: u32 = 33;
100    pub const JS_CLASS_BIG_INT: u32 = 34;
101    pub const JS_CLASS_MAP: u32 = 35;
102    pub const JS_CLASS_SET: u32 = 36;
103    pub const JS_CLASS_WEAKMAP: u32 = 37;
104    pub const JS_CLASS_WEAKSET: u32 = 38;
105    pub const JS_CLASS_ITERATOR: u32 = 39;
106    pub const JS_CLASS_ITERATOR_HELPER: u32 = 40;
107    pub const JS_CLASS_ITERATOR_WRAP: u32 = 41;
108    pub const JS_CLASS_MAP_ITERATOR: u32 = 42;
109    pub const JS_CLASS_SET_ITERATOR: u32 = 43;
110    pub const JS_CLASS_ARRAY_ITERATOR: u32 = 44;
111    pub const JS_CLASS_STRING_ITERATOR: u32 = 45;
112    pub const JS_CLASS_REGEXP_STRING_ITERATOR: u32 = 46;
113    pub const JS_CLASS_GENERATOR: u32 = 47;
114    pub const JS_CLASS_PROXY: u32 = 48;
115    pub const JS_CLASS_PROMISE: u32 = 49;
116    pub const JS_CLASS_PROMISE_RESOLVE_FUNCTION: u32 = 50;
117    pub const JS_CLASS_PROMISE_REJECT_FUNCTION: u32 = 51;
118    pub const JS_CLASS_ASYNC_FUNCTION: u32 = 52;
119    pub const JS_CLASS_ASYNC_FUNCTION_RESOLVE: u32 = 53;
120    pub const JS_CLASS_ASYNC_FUNCTION_REJECT: u32 = 54;
121    pub const JS_CLASS_ASYNC_FROM_SYNC_ITERATOR: u32 = 55;
122    pub const JS_CLASS_ASYNC_GENERATOR_FUNCTION: u32 = 56;
123    pub const JS_CLASS_ASYNC_GENERATOR: u32 = 57;
124    pub const JS_CLASS_WEAK_REF: u32 = 58;
125    pub const JS_CLASS_FINALIZATION_REGISTRY: u32 = 59;
126    pub const JS_CLASS_CALL_SITE: u32 = 60;
127    pub const JS_CLASS_INIT_COUNT: u32 = 61;
128}
129
130pub mod arrays;
131pub mod atoms;
132pub mod bigints;
133pub mod compile;
134pub mod dates;
135pub mod errors;
136pub mod functions;
137pub mod interrupthandler;
138pub mod iterators;
139pub mod json;
140pub mod maps;
141pub mod modules;
142pub mod objects;
143pub mod primitives;
144pub mod promises;
145pub mod properties;
146pub mod runtime;
147pub mod sets;
148pub mod typedarrays;
149
150use crate::jsutils::JsError;
151use crate::quickjs_utils::atoms::JSAtomRef;
152use crate::quickjs_utils::objects::get_property;
153use crate::quickjsrealmadapter::QuickJsRealmAdapter;
154use crate::quickjsvalueadapter::{QuickJsValueAdapter, TAG_NULL, TAG_UNDEFINED};
155use libquickjs_sys as q;
156
157// todo
158// runtime and context in thread_local here
159// all function (where applicable) get an Option<QuickJSRuntime> which if None will be gotten from the thread_local
160// every function which returns a q::JSValue will return a OwnedValueRef to ensure values are freed on drop
161
162pub fn gc(q_js_rt: &QuickJsRuntimeAdapter) {
163    log::trace!("GC called");
164    unsafe { q::JS_RunGC(q_js_rt.runtime) }
165    log::trace!("GC done");
166}
167
168pub fn new_undefined_ref() -> QuickJsValueAdapter {
169    QuickJsValueAdapter::new_no_context(new_undefined(), "new_undefined_ref")
170}
171
172pub fn new_null() -> q::JSValue {
173    q::JSValue {
174        u: q::JSValueUnion { int32: 0 },
175        tag: TAG_NULL,
176    }
177}
178
179pub fn new_undefined() -> q::JSValue {
180    q::JSValue {
181        u: q::JSValueUnion { int32: 0 },
182        tag: TAG_UNDEFINED,
183    }
184}
185
186pub fn new_null_ref() -> QuickJsValueAdapter {
187    QuickJsValueAdapter::new_no_context(new_null(), "null_ref")
188}
189
190/// get the current filename
191pub fn get_script_or_module_name_q(ctx: &QuickJsRealmAdapter) -> Result<String, JsError> {
192    unsafe { get_script_or_module_name(ctx.context) }
193}
194
195/// get the current filename
196/// # Safety
197/// ensure the QuickJsContext has not been dropped
198pub unsafe fn get_script_or_module_name(context: *mut q::JSContext) -> Result<String, JsError> {
199    for x in 0..100 {
200        let atom = q::JS_GetScriptOrModuleName(context, x);
201        let atom_ref = JSAtomRef::new(context, atom);
202        let r = atoms::to_string(context, &atom_ref)?;
203        if !r.is_empty() {
204            return Ok(r);
205        }
206    }
207    Ok("".to_string())
208}
209
210pub fn get_global_q(context: &QuickJsRealmAdapter) -> QuickJsValueAdapter {
211    unsafe { get_global(context.context) }
212}
213/// # Safety
214/// When passing a context pointer please make sure the corresponding QuickJsContext is still valid
215pub unsafe fn get_global(context: *mut q::JSContext) -> QuickJsValueAdapter {
216    let global = q::JS_GetGlobalObject(context);
217    QuickJsValueAdapter::new(context, global, false, true, "global")
218}
219/// # Safety
220/// When passing a context pointer please make sure the corresponding QuickJsContext is still valid
221pub unsafe fn get_constructor(
222    context: *mut q::JSContext,
223    constructor_name: &str,
224) -> Result<QuickJsValueAdapter, JsError> {
225    let global_ref = get_global(context);
226
227    let constructor_ref = get_property(context, &global_ref, constructor_name)?;
228
229    if constructor_ref.is_null_or_undefined() {
230        Err(JsError::new_string(format!(
231            "not found: {constructor_name}"
232        )))
233    } else {
234        Ok(constructor_ref)
235    }
236}
237/// Calculate a runtimes memory usage
238/// # Safety
239/// runtime ref should be a valid existing runtime
240pub unsafe fn get_memory_usage(runtime: *mut q::JSRuntime) -> q::JSMemoryUsage {
241    let mut mu = q::JSMemoryUsage {
242        malloc_size: 0,
243        malloc_limit: 0,
244        memory_used_size: 0,
245        malloc_count: 0,
246        memory_used_count: 0,
247        atom_count: 0,
248        atom_size: 0,
249        str_count: 0,
250        str_size: 0,
251        obj_count: 0,
252        obj_size: 0,
253        prop_count: 0,
254        prop_size: 0,
255        shape_count: 0,
256        shape_size: 0,
257        js_func_count: 0,
258        js_func_size: 0,
259        js_func_code_size: 0,
260        js_func_pc2line_count: 0,
261        js_func_pc2line_size: 0,
262        c_func_count: 0,
263        array_count: 0,
264        fast_array_count: 0,
265        fast_array_elements: 0,
266        binary_object_count: 0,
267        binary_object_size: 0,
268    };
269    q::JS_ComputeMemoryUsage(runtime, &mut mu);
270
271    mu
272}
273
274/// # Safety
275/// When passing a context pointer please make sure the corresponding QuickJsContext is still valid
276pub unsafe fn parse_args(
277    context: *mut q::JSContext,
278    argc: ::std::os::raw::c_int,
279    argv: *mut q::JSValue,
280) -> Vec<QuickJsValueAdapter> {
281    let arg_slice = std::slice::from_raw_parts(argv, argc as usize);
282    arg_slice
283        .iter()
284        .map(|raw| QuickJsValueAdapter::new(context, *raw, true, true, "quickjs_utils::parse_args"))
285        .collect::<Vec<_>>()
286}
287
288#[cfg(test)]
289pub mod tests {
290    use crate::facades::tests::init_test_rt;
291    use crate::jsutils::Script;
292    use crate::quickjs_utils::{get_global_q, get_script_or_module_name_q};
293    use crate::values::JsValueConvertable;
294
295    #[test]
296    fn test_global() {
297        let rt = init_test_rt();
298        rt.exe_rt_task_in_event_loop(|q_js_rt| {
299            let q_ctx = q_js_rt.get_main_realm();
300
301            #[cfg(feature = "bellard")]
302            let ct = get_global_q(q_ctx).get_ref_count();
303            for _ in 0..5 {
304                let _global = get_global_q(q_ctx);
305                #[cfg(feature = "bellard")]
306                assert_eq!(_global.get_ref_count(), ct);
307            }
308        });
309    }
310
311    #[test]
312    fn test_script_name() {
313        let rt = init_test_rt();
314        rt.set_function(&[], "testName", |q_ctx, _args| {
315            let res = get_script_or_module_name_q(q_ctx)?.to_js_value_facade();
316            Ok(res)
317        })
318        .ok()
319        .expect("func set failed");
320        let name_esvf = rt
321            .eval_sync(
322                None,
323                Script::new("the_name.es", "(function(){return(testName());}())"),
324            )
325            .ok()
326            .expect("script failed");
327        assert_eq!(name_esvf.get_str(), "the_name.es");
328        let name_esvf = rt
329            .eval_sync(
330                None,
331                Script::new("https://githubstuff.org/tes.js", "(testName())"),
332            )
333            .ok()
334            .expect("script failed");
335        assert_eq!(name_esvf.get_str(), "https://githubstuff.org/tes.js");
336    }
337}