Skip to main content

quickjs_runtime/quickjs_utils/
primitives.rs

1use crate::jsutils::JsError;
2use crate::quickjsrealmadapter::QuickJsRealmAdapter;
3use crate::quickjsvalueadapter::QuickJsValueAdapter;
4use core::ptr;
5use libquickjs_sys as q;
6use std::os::raw::c_char;
7
8pub fn to_bool(value_ref: &QuickJsValueAdapter) -> Result<bool, JsError> {
9    if value_ref.is_bool() {
10        let r = value_ref.borrow_value();
11        #[cfg(feature = "bellard")]
12        let raw = unsafe { r.u.uint64 };
13        #[cfg(feature = "quickjs-ng")]
14        let raw = unsafe { r.u.int32 };
15        let val: bool = raw > 0;
16        Ok(val)
17    } else {
18        Err(JsError::new_str("value is not a boolean"))
19    }
20}
21
22pub fn from_bool(b: bool) -> QuickJsValueAdapter {
23    let raw = unsafe { q::JS_NewBool(ptr::null_mut(), b) };
24    QuickJsValueAdapter::new_no_context(raw, "primitives::from_bool")
25}
26
27pub fn to_f64(value_ref: &QuickJsValueAdapter) -> Result<f64, JsError> {
28    if value_ref.is_f64() {
29        let r = value_ref.borrow_value();
30        let val = unsafe { r.u.float64 };
31        Ok(val)
32    } else {
33        Err(JsError::new_str("value was not a float64"))
34    }
35}
36
37pub fn from_f64(f: f64) -> QuickJsValueAdapter {
38    #[cfg(feature = "bellard")]
39    let raw = unsafe { q::JS_NewFloat64(ptr::null_mut(), f) };
40
41    #[cfg(feature = "quickjs-ng")]
42    let raw = unsafe { q::JS_NewNumber(ptr::null_mut(), f) };
43
44    QuickJsValueAdapter::new_no_context(raw, "primitives::from_f64")
45}
46
47pub fn to_i32(value_ref: &QuickJsValueAdapter) -> Result<i32, JsError> {
48    if value_ref.is_i32() {
49        let r = value_ref.borrow_value();
50
51        #[cfg(feature = "bellard")]
52        let val: i32 = unsafe { q::JS_ValueGetInt(*r) };
53
54        #[cfg(feature = "quickjs-ng")]
55        let val: i32 = unsafe { r.u.int32 };
56
57        Ok(val)
58    } else {
59        Err(JsError::new_str("val is not an int"))
60    }
61}
62
63pub fn from_i32(i: i32) -> QuickJsValueAdapter {
64    let raw = unsafe { q::JS_NewInt32(ptr::null_mut(), i) };
65    QuickJsValueAdapter::new_no_context(raw, "primitives::from_i32")
66}
67
68pub fn to_string_q(
69    q_ctx: &QuickJsRealmAdapter,
70    value_ref: &QuickJsValueAdapter,
71) -> Result<String, JsError> {
72    unsafe { to_string(q_ctx.context, value_ref) }
73}
74/// # Safety
75/// When passing a context pointer please make sure the corresponding QuickJsContext is still valid
76pub unsafe fn to_string(
77    context: *mut q::JSContext,
78    value_ref: &QuickJsValueAdapter,
79) -> Result<String, JsError> {
80    //log::trace!("primitives::to_string on {}", value_ref.borrow_value().tag);
81
82    assert!(value_ref.is_string());
83
84    let mut len = 0;
85
86    #[cfg(feature = "bellard")]
87    let ptr: *const c_char = q::JS_ToCStringLen2(context, &mut len, *value_ref.borrow_value(), 0);
88    #[cfg(feature = "quickjs-ng")]
89    let ptr: *const c_char =
90        q::JS_ToCStringLen2(context, &mut len, *value_ref.borrow_value(), false);
91
92    if len == 0 {
93        return Ok("".to_string());
94    }
95
96    if ptr.is_null() {
97        return Err(JsError::new_str(
98            "Could not convert string: got a null pointer",
99        ));
100    }
101
102    let bytes = std::slice::from_raw_parts(ptr as *const u8, len);
103
104    // Convert to String (validate UTF-8).
105    let s = String::from_utf8_lossy(bytes).into_owned();
106
107    // Free the c string.
108    q::JS_FreeCString(context, ptr);
109
110    Ok(s)
111}
112
113pub fn from_string_q(q_ctx: &QuickJsRealmAdapter, s: &str) -> Result<QuickJsValueAdapter, JsError> {
114    unsafe { from_string(q_ctx.context, s) }
115}
116/// # Safety
117/// When passing a context pointer please make sure the corresponding QuickJsContext is still valid
118pub unsafe fn from_string(
119    context: *mut q::JSContext,
120    s: &str,
121) -> Result<QuickJsValueAdapter, JsError> {
122    let qval = q::JS_NewStringLen(context, s.as_ptr() as *const c_char, s.len() as _);
123    let ret = QuickJsValueAdapter::new(context, qval, false, true, "primitives::from_string qval");
124    if ret.is_exception() {
125        return Err(JsError::new_str("Could not create string in runtime"));
126    }
127
128    Ok(ret)
129}
130
131#[cfg(test)]
132pub mod tests {
133    use crate::facades::tests::init_test_rt;
134    use crate::jsutils::Script;
135
136    #[tokio::test]
137    async fn test_emoji() {
138        let rt = init_test_rt();
139
140        let res = rt.eval(None, Script::new("testEmoji.js", "'hi'")).await;
141
142        match res {
143            Ok(fac) => {
144                assert_eq!(fac.get_str(), "hi");
145            }
146            Err(e) => {
147                panic!("script failed: {}", e);
148            }
149        }
150
151        let res = rt.eval(None, Script::new("testEmoji.js", "'๐Ÿ‘'")).await;
152
153        match res {
154            Ok(fac) => {
155                assert_eq!(fac.get_str(), "๐Ÿ‘");
156            }
157            Err(e) => {
158                panic!("script failed: {}", e);
159            }
160        }
161
162        let res = rt.eval(None, Script::new("testEmoji.js", "'pre๐Ÿ‘'")).await;
163
164        match res {
165            Ok(fac) => {
166                assert_eq!(fac.get_str(), "pre๐Ÿ‘");
167            }
168            Err(e) => {
169                panic!("script failed: {}", e);
170            }
171        }
172
173        let res = rt.eval(None, Script::new("testEmoji.js", "'๐Ÿ‘post'")).await;
174
175        match res {
176            Ok(fac) => {
177                assert_eq!(fac.get_str(), "๐Ÿ‘post");
178            }
179            Err(e) => {
180                panic!("script failed: {}", e);
181            }
182        }
183
184        let res = rt
185            .eval(None, Script::new("testEmoji.js", "'pre๐Ÿ‘post'"))
186            .await;
187
188        match res {
189            Ok(fac) => {
190                assert_eq!(fac.get_str(), "pre๐Ÿ‘post");
191            }
192            Err(e) => {
193                panic!("script failed: {}", e);
194            }
195        }
196
197        let res = rt
198            .eval(
199                None,
200                Script::new("testEmoji.js", "JSON.stringify({c: '๐Ÿ‘'})"),
201            )
202            .await;
203
204        match res {
205            Ok(fac) => {
206                assert_eq!(fac.get_str(), "{\"c\":\"๐Ÿ‘\"}");
207            }
208            Err(e) => {
209                panic!("script failed: {}", e);
210            }
211        }
212    }
213}