1use std::iter::FusedIterator;
8use std::ops::{Deref, DerefMut};
9
10use mozjs::gc::RootedVec;
11use mozjs::jsapi::{GetArrayLength, HandleValueArray, IsArray, JSObject, NewArrayObject, NewArrayObject1};
12use mozjs::jsval::{JSVal, ObjectValue};
13use mozjs::rooted;
14
15use crate::conversions::{FromValue, ToValue};
16use crate::flags::{IteratorFlags, PropertyFlags};
17use crate::object::object::ObjectKeysIter;
18use crate::{Context, Local, Object, PropertyDescriptor, Result, Value};
19
20#[derive(Debug)]
23pub struct Array<'a> {
24 arr: Object<'a>,
25}
26
27impl<'a> Array<'a> {
28 pub fn new(cx: &'a Context) -> Array<'a> {
30 Array::new_with_length(cx, 0)
31 }
32
33 pub fn new_with_length(cx: &'a Context, length: usize) -> Array<'a> {
35 Array {
36 arr: cx.root(unsafe { NewArrayObject1(cx.as_ptr(), length) }).into(),
37 }
38 }
39
40 pub fn from_rooted_vec(cx: &'a Context, vec: &RootedVec<'_, JSVal>) -> Array<'a> {
42 Array::from_handle(cx, HandleValueArray::from(vec))
43 }
44
45 pub fn from_handle(cx: &'a Context, handle: HandleValueArray) -> Array<'a> {
47 Array {
48 arr: cx.root(unsafe { NewArrayObject(cx.as_ptr(), &raw const handle) }).into(),
49 }
50 }
51
52 pub fn from(cx: &Context, object: Local<'a, *mut JSObject>) -> Option<Array<'a>> {
56 Array::is_array(cx, &object).then(|| Array { arr: object.into() })
57 }
58
59 pub unsafe fn from_unchecked(object: Local<'a, *mut JSObject>) -> Array<'a> {
64 Array { arr: object.into() }
65 }
66
67 pub fn to_vec<'cx>(&self, cx: &'cx Context) -> Vec<Value<'cx>> {
70 let value = cx.root(ObjectValue(self.arr.handle().get())).into();
71 Vec::from_value(cx, &value, true, ()).unwrap_or_default()
72 }
73
74 pub fn to_object(&self, cx: &'a Context) -> Object<'a> {
76 Object::from(cx.root(self.arr.handle().get()))
77 }
78
79 pub fn len(&self, cx: &Context) -> u32 {
81 let mut length = 0;
82 unsafe {
83 GetArrayLength(cx.as_ptr(), self.handle().into(), &raw mut length);
84 }
85 length
86 }
87
88 pub fn is_empty(&self, cx: &Context) -> bool {
90 self.len(cx) == 0
91 }
92
93 pub fn has(&self, cx: &Context, index: u32) -> bool {
95 self.arr.has(cx, index)
96 }
97
98 pub fn get<'cx>(&self, cx: &'cx Context, index: u32) -> Result<Option<Value<'cx>>> {
101 self.arr.get(cx, index)
102 }
103
104 pub fn get_as<'cx, T: FromValue<'cx>>(
107 &self, cx: &'cx Context, index: u32, strict: bool, config: T::Config,
108 ) -> Result<Option<T>> {
109 self.arr.get_as(cx, index, strict, config)
110 }
111
112 pub fn get_descriptor<'cx>(&self, cx: &'cx Context, index: u32) -> Result<Option<PropertyDescriptor<'cx>>> {
115 self.arr.get_descriptor(cx, index)
116 }
117
118 pub fn set(&self, cx: &Context, index: u32, value: &Value) -> bool {
121 self.arr.set(cx, index, value)
122 }
123
124 pub fn set_as<'cx, T: ToValue<'cx> + ?Sized>(&self, cx: &'cx Context, index: u32, value: &T) -> bool {
127 self.arr.set_as(cx, index, value)
128 }
129
130 pub fn define(&self, cx: &Context, index: u32, value: &Value, attrs: PropertyFlags) -> bool {
133 self.arr.define(cx, index, value, attrs)
134 }
135
136 pub fn define_as<'cx, T: ToValue<'cx> + ?Sized>(
139 &self, cx: &'cx Context, index: u32, value: &T, attrs: PropertyFlags,
140 ) -> bool {
141 self.arr.define_as(cx, index, value, attrs)
142 }
143
144 pub fn delete(&self, cx: &Context, index: u32) -> bool {
147 self.arr.delete(cx, index)
148 }
149
150 pub fn indices<'cx>(&self, cx: &'cx Context, flags: Option<IteratorFlags>) -> ArrayIndicesIter<'cx> {
151 ArrayIndicesIter(self.arr.keys(cx, flags))
152 }
153
154 pub fn iter<'cx, 's>(&'s self, cx: &'cx Context, flags: Option<IteratorFlags>) -> ArrayIter<'cx, 's>
155 where
156 'a: 'cx,
157 {
158 ArrayIter::new(cx, self, self.indices(cx, flags))
159 }
160
161 pub fn is_array_raw(cx: &Context, object: *mut JSObject) -> bool {
163 rooted!(in(cx.as_ptr()) let object = object);
164 let mut is_array = false;
165 unsafe { IsArray(cx.as_ptr(), object.handle().into(), &raw mut is_array) && is_array }
166 }
167
168 pub fn is_array(cx: &Context, object: &Local<*mut JSObject>) -> bool {
170 let mut is_array = false;
171 unsafe { IsArray(cx.as_ptr(), object.handle().into(), &raw mut is_array) && is_array }
172 }
173
174 pub fn as_object(&self) -> &Object<'a> {
175 &self.arr
176 }
177
178 pub fn into_local(self) -> Local<'a, *mut JSObject> {
179 self.arr.into_local()
180 }
181}
182
183impl<'a> Deref for Array<'a> {
184 type Target = Local<'a, *mut JSObject>;
185
186 fn deref(&self) -> &Self::Target {
187 &self.arr
188 }
189}
190
191impl DerefMut for Array<'_> {
192 fn deref_mut(&mut self) -> &mut Self::Target {
193 &mut self.arr
194 }
195}
196
197pub struct ArrayIndicesIter<'cx>(ObjectKeysIter<'cx>);
198
199impl Iterator for ArrayIndicesIter<'_> {
200 type Item = u32;
201
202 fn next(&mut self) -> Option<u32> {
203 for key in self.0.by_ref() {
204 let key = key.get();
205 if key.is_int() {
206 return Some(u32::try_from(key.to_int()).unwrap());
207 }
208 }
209 None
210 }
211
212 fn size_hint(&self) -> (usize, Option<usize>) {
213 (0, self.0.size_hint().1)
214 }
215}
216
217impl DoubleEndedIterator for ArrayIndicesIter<'_> {
218 fn next_back(&mut self) -> Option<u32> {
219 while let Some(key) = self.0.next_back() {
220 let key = key.get();
221 if key.is_int() {
222 return Some(u32::try_from(key.to_int()).unwrap());
223 }
224 }
225 None
226 }
227}
228
229impl FusedIterator for ArrayIndicesIter<'_> {}
230
231pub struct ArrayIter<'cx, 'a> {
232 cx: &'cx Context,
233 array: &'a Array<'cx>,
234 indices: ArrayIndicesIter<'cx>,
235}
236
237impl<'cx, 'a> ArrayIter<'cx, 'a> {
238 fn new(cx: &'cx Context, array: &'a Array<'cx>, indices: ArrayIndicesIter<'cx>) -> ArrayIter<'cx, 'a> {
239 ArrayIter { cx, array, indices }
240 }
241}
242
243impl<'cx> Iterator for ArrayIter<'cx, '_> {
244 type Item = (u32, Result<Value<'cx>>);
245
246 fn next(&mut self) -> Option<Self::Item> {
247 self.indices.next().map(|index| {
248 let value = self.array.get(self.cx, index).transpose().unwrap();
249 (index, value)
250 })
251 }
252
253 fn size_hint(&self) -> (usize, Option<usize>) {
254 self.indices.size_hint()
255 }
256}
257
258impl DoubleEndedIterator for ArrayIter<'_, '_> {
259 fn next_back(&mut self) -> Option<Self::Item> {
260 self.indices.next_back().map(|index| {
261 let value = self.array.get(self.cx, index).transpose().unwrap();
262 (index, value)
263 })
264 }
265}
266
267impl FusedIterator for ArrayIter<'_, '_> {}
268
269#[cfg(test)]
270mod tests {
271 use crate::flags::PropertyFlags;
272 use crate::utils::test::TestRuntime;
273 use crate::{Array, Result, Value};
274
275 #[test]
276 fn new() {
277 let rt = TestRuntime::new();
278 let cx = &rt.cx;
279
280 let array = Array::new(cx);
281 assert!(!array.handle().get().is_null());
282 assert!(array.is_empty(cx));
283
284 let array = Array::new_with_length(cx, 8);
285 assert!(!array.handle().get().is_null());
286 assert_eq!(array.len(cx), 8);
287 }
288
289 #[test]
290 fn get_set() {
291 let rt = TestRuntime::new();
292 let cx = &rt.cx;
293
294 const VALUE: i32 = 6;
295 let array = Array::new(cx);
296 assert!(array.set(cx, 0, &Value::i32(cx, VALUE)));
297 assert_eq!(array.len(cx), 1);
298
299 assert!(array.has(cx, 0));
300 let result = array.get(cx, 0).unwrap().unwrap();
301 assert!(result.handle().is_int32());
302 assert_eq!(result.handle().to_int32(), VALUE);
303
304 assert!(!array.has(cx, 1));
305 let result = array.get(cx, 1).unwrap();
306 assert!(result.is_none());
307 }
308
309 #[test]
310 fn get_set_as() {
311 let rt = TestRuntime::new();
312 let cx = &rt.cx;
313
314 let array = Array::new(cx);
315
316 const STRING_VALUE: &str = "spiderfire";
317 assert!(array.set_as(cx, 0, &STRING_VALUE));
318 assert_eq!(array.len(cx), 1);
319
320 assert!(array.has(cx, 0));
321 let result: String = array.get_as(cx, 0, true, ()).unwrap().unwrap();
322 assert_eq!(result, STRING_VALUE);
323
324 const BOOL_VALUE: bool = true;
325 assert!(array.set_as(cx, 1, &BOOL_VALUE));
326 assert_eq!(array.len(cx), 2);
327
328 assert!(array.has(cx, 1));
329 let result: bool = array.get_as(cx, 1, true, ()).unwrap().unwrap();
330 assert_eq!(result, BOOL_VALUE);
331 }
332
333 #[test]
334 fn define_descriptor() {
335 let rt = TestRuntime::new();
336 let cx = &rt.cx;
337
338 let array = Array::new(cx);
339
340 const VALUE: i32 = 6;
341 let flags = PropertyFlags::READ_ONLY | PropertyFlags::PERMANENT;
342 assert!(array.define(cx, 0, &Value::i32(cx, VALUE), flags));
343
344 let result = array.get(cx, 0).unwrap().unwrap();
345 assert!(result.handle().is_int32());
346 assert_eq!(result.handle().to_int32(), VALUE);
347
348 let desc = array.get_descriptor(cx, 0).unwrap().unwrap();
349 assert!(!desc.is_writable());
350 assert!(!desc.is_configurable());
351
352 assert!(array.set(cx, 0, &Value::i32(cx, VALUE + 1)));
353 let result = array.get(cx, 0).unwrap().unwrap();
354 assert!(result.handle().is_int32());
355 assert_eq!(result.handle().to_int32(), VALUE);
356 }
357
358 #[test]
359 fn delete_sparse() {
360 let rt = TestRuntime::new();
361 let cx = &rt.cx;
362
363 let array = Array::new(cx);
364 array.set_as(cx, 0, &0);
365 array.set_as(cx, 1, &1);
366 assert_eq!(array.len(cx), 2);
367
368 assert!(array.delete(cx, 0));
369
370 assert!(!array.has(cx, 0));
371 assert!(array.has(cx, 1));
372 assert_eq!(array.len(cx), 2);
373 }
374
375 #[test]
376 fn iter() {
377 let rt = TestRuntime::new();
378 let cx = &rt.cx;
379
380 let array = Array::new(cx);
381 assert!(array.set_as(cx, 0, &0));
382 assert!(array.set_as(cx, 1, &1));
383 assert!(array.set_as(cx, 3, &3));
384
385 let indices: Vec<_> = array.indices(cx, None).collect();
386 assert_eq!(indices, [0, 1, 3]);
387
388 let pairs: Vec<_> = array
389 .iter(cx, None)
390 .map(|(k, v)| Ok((k, v?.handle().to_int32())))
391 .collect::<Result<_>>()
392 .unwrap();
393 assert_eq!(pairs, [(0, 0), (1, 1), (3, 3)]);
394 }
395}