script_bindings/
iterable.rs1use std::cell::Cell;
8use std::marker::PhantomData;
9use std::ptr;
10use std::ptr::NonNull;
11
12use dom_struct::dom_struct;
13use js::conversions::ToJSValConvertible;
14use js::jsapi::{Heap, JSObject};
15use js::jsval::UndefinedValue;
16use js::rust::{HandleObject, HandleValue, MutableHandleObject};
17
18use crate::codegen::GenericBindings::IterableIteratorBinding::{
19 IterableKeyAndValueResult, IterableKeyOrValueResult,
20};
21use crate::conversions::IDLInterface;
22use crate::error::Fallible;
23use crate::interfaces::DomHelpers;
24use crate::realms::InRealm;
25use crate::reflector::{DomGlobalGeneric, DomObjectIteratorWrap, DomObjectWrap, Reflector};
26use crate::root::{Dom, DomRoot, Root};
27use crate::script_runtime::{CanGc, JSContext};
28use crate::trace::{NoTrace, RootedTraceableBox};
29use crate::utils::DOMClass;
30use crate::{DomTypes, JSTraceable};
31
32#[derive(JSTraceable, MallocSizeOf)]
34pub(crate) enum IteratorType {
35 Keys,
37 Values,
39 Entries,
41}
42
43pub trait Iterable {
45 type Key: ToJSValConvertible;
47 type Value: ToJSValConvertible;
49 fn get_iterable_length(&self) -> u32;
51 fn get_value_at_index(&self, index: u32) -> Self::Value;
53 fn get_key_at_index(&self, index: u32) -> Self::Key;
55}
56
57pub trait IteratorDerives {
62 fn derives(class: &'static DOMClass) -> bool;
63}
64
65#[dom_struct]
67pub struct IterableIterator<
68 D: DomTypes,
69 T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>,
70> {
71 reflector: Reflector,
72 iterable: Dom<T>,
73 type_: IteratorType,
74 index: Cell<u32>,
75 _marker: NoTrace<PhantomData<D>>,
76}
77
78impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable> IterableIterator<D, T> {
79 pub fn global_(&self, realm: InRealm) -> DomRoot<D::GlobalScope> {
80 <Self as DomGlobalGeneric<D>>::global_(self, realm)
81 }
82}
83
84impl<
85 D: DomTypes,
86 T: DomObjectIteratorWrap<D>
87 + JSTraceable
88 + Iterable
89 + DomGlobalGeneric<D>
90 + IDLInterface
91 + IteratorDerives,
92> IDLInterface for IterableIterator<D, T>
93{
94 fn derives(class: &'static DOMClass) -> bool {
95 <T as IteratorDerives>::derives(class)
96 }
97}
98
99impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>>
100 IterableIterator<D, T>
101{
102 pub(crate) fn new(iterable: &T, type_: IteratorType, realm: InRealm) -> DomRoot<Self> {
104 let iterator = Box::new(IterableIterator {
105 reflector: Reflector::new(),
106 type_,
107 iterable: Dom::from_ref(iterable),
108 index: Cell::new(0),
109 _marker: NoTrace(PhantomData),
110 });
111 <D as DomHelpers<D>>::reflect_dom_object(iterator, &*iterable.global_(realm), CanGc::note())
112 }
113
114 #[allow(non_snake_case)]
116 pub fn Next(&self, cx: JSContext) -> Fallible<NonNull<JSObject>> {
117 let index = self.index.get();
118 rooted!(in(*cx) let mut value = UndefinedValue());
119 rooted!(in(*cx) let mut rval = ptr::null_mut::<JSObject>());
120 let result = if index >= self.iterable.get_iterable_length() {
121 dict_return(cx, rval.handle_mut(), true, value.handle())
122 } else {
123 match self.type_ {
124 IteratorType::Keys => {
125 unsafe {
126 self.iterable
127 .get_key_at_index(index)
128 .to_jsval(*cx, value.handle_mut());
129 }
130 dict_return(cx, rval.handle_mut(), false, value.handle())
131 },
132 IteratorType::Values => {
133 unsafe {
134 self.iterable
135 .get_value_at_index(index)
136 .to_jsval(*cx, value.handle_mut());
137 }
138 dict_return(cx, rval.handle_mut(), false, value.handle())
139 },
140 IteratorType::Entries => {
141 rooted!(in(*cx) let mut key = UndefinedValue());
142 unsafe {
143 self.iterable
144 .get_key_at_index(index)
145 .to_jsval(*cx, key.handle_mut());
146 self.iterable
147 .get_value_at_index(index)
148 .to_jsval(*cx, value.handle_mut());
149 }
150 key_and_value_return(cx, rval.handle_mut(), key.handle(), value.handle())
151 },
152 }
153 };
154 self.index.set(index + 1);
155 result.map(|_| NonNull::new(rval.get()).expect("got a null pointer"))
156 }
157}
158
159impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>>
160 DomObjectWrap<D> for IterableIterator<D, T>
161{
162 const WRAP: unsafe fn(
163 JSContext,
164 &D::GlobalScope,
165 Option<HandleObject>,
166 Box<Self>,
167 CanGc,
168 ) -> Root<Dom<Self>> = T::ITER_WRAP;
169}
170
171fn dict_return(
172 cx: JSContext,
173 mut result: MutableHandleObject,
174 done: bool,
175 value: HandleValue,
176) -> Fallible<()> {
177 let mut dict = IterableKeyOrValueResult::empty();
178 dict.done = done;
179 dict.value.set(value.get());
180 rooted!(in(*cx) let mut dict_value = UndefinedValue());
181 unsafe {
182 dict.to_jsval(*cx, dict_value.handle_mut());
183 }
184 result.set(dict_value.to_object());
185 Ok(())
186}
187
188fn key_and_value_return(
189 cx: JSContext,
190 mut result: MutableHandleObject,
191 key: HandleValue,
192 value: HandleValue,
193) -> Fallible<()> {
194 let mut dict = IterableKeyAndValueResult::empty();
195 dict.done = false;
196 dict.value = Some(
197 vec![key, value]
198 .into_iter()
199 .map(|handle| RootedTraceableBox::from_box(Heap::boxed(handle.get())))
200 .collect(),
201 );
202 rooted!(in(*cx) let mut dict_value = UndefinedValue());
203 unsafe {
204 dict.to_jsval(*cx, dict_value.handle_mut());
205 }
206 result.set(dict_value.to_object());
207 Ok(())
208}