quickjs_rusty/value/
array.rs

1use std::ops::Deref;
2
3use libquickjs_ng_sys as q;
4
5use crate::{ExecutionError, ValueError};
6
7use super::OwnedJsValue;
8
9pub struct OwnedJsArray {
10    value: OwnedJsValue,
11}
12
13impl OwnedJsArray {
14    pub fn try_from_value(value: OwnedJsValue) -> Result<Self, ValueError> {
15        if !value.is_array() {
16            Err(ValueError::Internal("Expected an array".into()))
17        } else {
18            Ok(Self { value })
19        }
20    }
21
22    pub fn length(&self) -> u64 {
23        let mut next_index: i64 = 0;
24        unsafe {
25            q::JS_GetLength(
26                self.value.context(),
27                self.value.value,
28                &mut next_index as *mut _,
29            );
30        }
31
32        next_index as u64
33    }
34
35    pub fn get_index(&self, index: u32) -> Result<Option<OwnedJsValue>, ExecutionError> {
36        let value_raw =
37            unsafe { q::JS_GetPropertyUint32(self.value.context(), self.value.value, index) };
38        let tag = unsafe { q::JS_Ext_ValueGetTag(value_raw) };
39        if tag == q::JS_TAG_EXCEPTION {
40            return Err(ExecutionError::Internal("Could not build array".into()));
41        } else if tag == q::JS_TAG_UNDEFINED {
42            return Ok(None);
43        }
44
45        Ok(Some(OwnedJsValue::new(self.value.context(), value_raw)))
46    }
47
48    pub fn set_index(&self, index: u32, value: OwnedJsValue) -> Result<(), ExecutionError> {
49        unsafe {
50            // NOTE: SetPropertyStr takes ownership of the value.
51            // We do not, however, call OwnedJsValue::extract immediately, so
52            // the inner JSValue is still managed.
53            // `mem::forget` is called below only if SetProperty succeeds.
54            // This prevents leaks when an error occurs.
55            let ret =
56                q::JS_SetPropertyUint32(self.value.context(), self.value.value, index, value.value);
57
58            if ret < 0 {
59                Err(ExecutionError::Internal("Could not set property".into()))
60            } else {
61                // Now we can call forget to prevent calling the destructor.
62                std::mem::forget(value);
63                Ok(())
64            }
65        }
66    }
67
68    pub fn push(&self, value: OwnedJsValue) -> Result<(), ExecutionError> {
69        unsafe {
70            let mut next_index: i64 = 0;
71            q::JS_GetLength(
72                self.value.context(),
73                self.value.value,
74                &mut next_index as *mut _,
75            );
76            // NOTE: SetPropertyStr takes ownership of the value.
77            // We do not, however, call OwnedJsValue::extract immediately, so
78            // the inner JSValue is still managed.
79            // `mem::forget` is called below only if SetProperty succeeds.
80            // This prevents leaks when an error occurs.
81            let ret = q::JS_SetPropertyInt64(
82                self.value.context(),
83                self.value.value,
84                next_index,
85                value.value,
86            );
87
88            if ret < 0 {
89                Err(ExecutionError::Internal(
90                    "Could not set property".to_string(),
91                ))
92            } else {
93                // Now we can call forget to prevent calling the destructor.
94                std::mem::forget(value);
95                Ok(())
96            }
97        }
98    }
99
100    pub fn raw_elements(&self) -> Vec<q::JSValue> {
101        let mut ret = vec![];
102        let length = self.length() as u32;
103        for i in 0..length {
104            let value_raw =
105                unsafe { q::JS_GetPropertyUint32(self.value.context(), self.value.value, i) };
106            ret.push(value_raw);
107        }
108        ret
109    }
110}
111
112impl Deref for OwnedJsArray {
113    type Target = OwnedJsValue;
114
115    fn deref(&self) -> &Self::Target {
116        &self.value
117    }
118}