1use std::ffi::CString;
4
5use libquickjs_ng_sys as q;
6
7use crate::value::{JsFunction, OwnedJsValue};
8use crate::ValueError;
9
10pub(crate) fn deserialize_borrowed_str(
11 context: *mut q::JSContext,
12 value: &q::JSValue,
13) -> Result<&str, ValueError> {
14 let r = value;
15 let tag = unsafe { q::JS_Ext_ValueGetTag(*r) };
16
17 match tag {
18 q::JS_TAG_STRING => {
19 let ptr = unsafe { q::JS_ToCStringLen2(context, std::ptr::null_mut(), *r, false) };
20
21 if ptr.is_null() {
22 return Err(ValueError::Internal(
23 "Could not convert string: got a null pointer".into(),
24 ));
25 }
26
27 let cstr = unsafe { std::ffi::CStr::from_ptr(ptr) };
28
29 let s = cstr
30 .to_str()
31 .map_err(ValueError::InvalidString)?
32 .to_string()
33 .leak();
34
35 unsafe { q::JS_FreeCString(context, ptr) };
37
38 Ok(s)
39 }
40 _ => Err(ValueError::Internal(format!(
41 "Expected a string, got a {:?}",
42 tag
43 ))),
44 }
45}
46
47#[inline]
49pub fn make_cstring(value: impl Into<Vec<u8>>) -> Result<CString, ValueError> {
50 CString::new(value).map_err(ValueError::StringWithZeroBytes)
51}
52
53#[cfg(feature = "chrono")]
54pub fn js_date_constructor(context: *mut q::JSContext) -> q::JSValue {
55 let global = unsafe { q::JS_GetGlobalObject(context) };
56 let tag = unsafe { q::JS_Ext_ValueGetTag(global) };
57 assert_eq!(tag, q::JS_TAG_OBJECT);
58
59 let date_constructor = unsafe {
60 q::JS_GetPropertyStr(
61 context,
62 global,
63 std::ffi::CStr::from_bytes_with_nul(b"Date\0")
64 .unwrap()
65 .as_ptr(),
66 )
67 };
68 let tag = unsafe { q::JS_Ext_ValueGetTag(date_constructor) };
69 assert_eq!(tag, q::JS_TAG_OBJECT);
70 unsafe { q::JS_FreeValue(context, global) };
71 date_constructor
72}
73
74#[cfg(feature = "bigint")]
75fn js_create_bigint_function(context: *mut q::JSContext) -> q::JSValue {
76 let global = unsafe { q::JS_GetGlobalObject(context) };
77 let tag = unsafe { q::JS_Ext_ValueGetTag(global) };
78 assert_eq!(tag, q::JS_TAG_OBJECT);
79
80 let bigint_function = unsafe {
81 q::JS_GetPropertyStr(
82 context,
83 global,
84 std::ffi::CStr::from_bytes_with_nul(b"BigInt\0")
85 .unwrap()
86 .as_ptr(),
87 )
88 };
89 let tag = unsafe { q::JS_Ext_ValueGetTag(bigint_function) };
90 assert_eq!(tag, q::JS_TAG_OBJECT);
91 unsafe { q::JS_FreeValue(context, global) };
92 bigint_function
93}
94
95pub fn create_undefined() -> q::JSValue {
96 unsafe { q::JS_Ext_NewSpecialValue(q::JS_TAG_UNDEFINED, 0) }
97}
98
99pub fn create_null() -> q::JSValue {
100 unsafe { q::JS_Ext_NewSpecialValue(q::JS_TAG_NULL, 0) }
101}
102
103pub fn create_bool(context: *mut q::JSContext, value: bool) -> q::JSValue {
104 unsafe { q::JS_Ext_NewBool(context, value as u8) }
105}
106
107pub fn create_int(context: *mut q::JSContext, value: i32) -> q::JSValue {
108 unsafe { q::JS_Ext_NewInt32(context, value) }
109}
110
111pub fn create_float(context: *mut q::JSContext, value: f64) -> q::JSValue {
112 unsafe { q::JS_Ext_NewFloat64(context, value) }
113}
114
115pub fn create_string(context: *mut q::JSContext, value: &str) -> Result<q::JSValue, ValueError> {
116 let qval = unsafe { q::JS_NewStringLen(context, value.as_ptr() as *const _, value.len()) };
118
119 let tag = unsafe { q::JS_Ext_ValueGetTag(qval) };
120
121 if tag == q::JS_TAG_EXCEPTION {
122 return Err(ValueError::Internal(
123 "Could not create string in runtime".into(),
124 ));
125 }
126
127 Ok(qval)
128}
129
130pub fn create_empty_array(context: *mut q::JSContext) -> Result<q::JSValue, ValueError> {
131 let arr = unsafe { q::JS_NewArray(context) };
133 let tag = unsafe { q::JS_Ext_ValueGetTag(arr) };
134 if tag == q::JS_TAG_EXCEPTION {
135 return Err(ValueError::Internal(
136 "Could not create array in runtime".into(),
137 ));
138 }
139
140 Ok(arr)
141}
142
143pub fn add_array_element(
144 context: *mut q::JSContext,
145 array: q::JSValue,
146 index: u32,
147 value: q::JSValue,
148) -> Result<(), ValueError> {
149 let result = unsafe { q::JS_SetPropertyUint32(context, array, index, value) };
150 if result < 0 {
151 return Err(ValueError::Internal(
152 "Could not add element to array".into(),
153 ));
154 }
155
156 Ok(())
157}
158
159pub fn create_empty_object(context: *mut q::JSContext) -> Result<q::JSValue, ValueError> {
160 let obj = unsafe { q::JS_NewObject(context) };
161 let tag = unsafe { q::JS_Ext_ValueGetTag(obj) };
162 if tag == q::JS_TAG_EXCEPTION {
163 return Err(ValueError::Internal("Could not create object".into()));
164 }
165
166 Ok(obj)
167}
168
169pub fn add_object_property(
170 context: *mut q::JSContext,
171 object: q::JSValue,
172 key: &str,
173 value: q::JSValue,
174) -> Result<(), ValueError> {
175 let key = make_cstring(key)?;
176 let result = unsafe { q::JS_SetPropertyStr(context, object, key.as_ptr(), value) };
177 if result < 0 {
178 return Err(ValueError::Internal(
179 "Could not add property to object".into(),
180 ));
181 }
182
183 Ok(())
184}
185
186pub fn create_function(_: *mut q::JSContext, func: JsFunction) -> Result<q::JSValue, ValueError> {
187 let owned_value = func.into_value();
188 let v = unsafe { owned_value.extract() };
189 Ok(v)
190}
191
192#[cfg(feature = "chrono")]
193pub fn create_date(
194 context: *mut q::JSContext,
195 datetime: chrono::DateTime<chrono::Utc>,
196) -> Result<q::JSValue, ValueError> {
197 let date_constructor = js_date_constructor(context);
198
199 let f = datetime.timestamp_millis() as f64;
200
201 let timestamp = unsafe { q::JS_Ext_NewFloat64(context, f) };
202
203 let mut args = vec![timestamp];
204
205 let value = unsafe {
206 q::JS_CallConstructor(
207 context,
208 date_constructor,
209 args.len() as i32,
210 args.as_mut_ptr(),
211 )
212 };
213 unsafe {
214 q::JS_FreeValue(context, date_constructor);
215 }
216
217 let tag = unsafe { q::JS_Ext_ValueGetTag(value) };
218 if tag != q::JS_TAG_OBJECT {
219 return Err(ValueError::Internal(
220 "Could not construct Date object".into(),
221 ));
222 }
223 Ok(value)
224}
225
226#[cfg(feature = "bigint")]
227pub fn create_bigint(
228 context: *mut q::JSContext,
229 int: crate::BigInt,
230) -> Result<q::JSValue, ValueError> {
231 use std::ffi::c_char;
232
233 use crate::value::BigIntOrI64;
234
235 let val = match int.inner {
236 BigIntOrI64::Int(int) => unsafe { q::JS_NewBigInt64(context, int) },
237 BigIntOrI64::BigInt(bigint) => {
238 let bigint_string = bigint.to_str_radix(10);
239 let s = unsafe {
240 q::JS_NewStringLen(
241 context,
242 bigint_string.as_ptr() as *const c_char,
243 bigint_string.len(),
244 )
245 };
246
247 let s_tag = unsafe { q::JS_Ext_ValueGetTag(s) };
248 if s_tag != q::JS_TAG_STRING {
249 return Err(ValueError::Internal(
250 "Could not construct String object needed to create BigInt object".into(),
251 ));
252 }
253
254 let mut args = vec![s];
255
256 let bigint_function = js_create_bigint_function(context);
257
258 let null = create_null();
259 let js_bigint =
260 unsafe { q::JS_Call(context, bigint_function, null, 1, args.as_mut_ptr()) };
261
262 unsafe {
263 q::JS_FreeValue(context, s);
264 q::JS_FreeValue(context, bigint_function);
265 q::JS_FreeValue(context, null);
266 }
267
268 let js_bigint_tag = unsafe { q::JS_Ext_ValueGetTag(js_bigint) };
269
270 if js_bigint_tag != q::JS_TAG_BIG_INT {
271 return Err(ValueError::Internal(
272 "Could not construct BigInt object".into(),
273 ));
274 }
275
276 js_bigint
277 }
278 };
279
280 Ok(val)
281}
282
283pub fn create_symbol(_: *mut q::JSContext) -> Result<q::JSValue, ValueError> {
284 todo!("create symbol not implemented")
285}
286
287#[inline]
288pub fn own_raw_value(context: *mut q::JSContext, value: q::JSValue) -> OwnedJsValue {
289 OwnedJsValue::new(context, value)
290}
291
292#[macro_export]
293macro_rules! owned {
294 ($context:expr, $val:expr) => {
295 OwnedJsValue::from(($context, $val))
296 };
297}
298
299use crate::ExecutionError;
300
301pub(crate) fn get_exception(context: *mut q::JSContext) -> Option<ExecutionError> {
303 if unsafe { !q::JS_HasException(context) } {
304 return None;
305 }
306
307 let value = unsafe {
308 let raw = q::JS_GetException(context);
309 OwnedJsValue::new(context, raw)
310 };
311
312 if value.is_exception() {
313 Some(ExecutionError::Internal(
314 "Could get exception from runtime".into(),
315 ))
316 } else {
317 match value.js_to_string() {
318 Ok(strval) => {
319 if strval.contains("out of memory") {
320 Some(ExecutionError::OutOfMemory)
321 } else {
322 Some(ExecutionError::Exception(OwnedJsValue::new(
323 context,
324 create_string(context, &strval).unwrap(),
325 )))
326 }
327 }
328 Err(e) => Some(e),
329 }
330 }
331}
332
333pub(crate) fn ensure_no_excpetion(context: *mut q::JSContext) -> Result<(), ExecutionError> {
335 if let Some(e) = get_exception(context) {
336 Err(e)
337 } else {
338 Ok(())
339 }
340}