1use std::{iter, ptr};
8
9use mozjs::gc::Traceable;
10use mozjs::jsapi::{
11 GetRealmIteratorPrototype, Heap, JSCLASS_BACKGROUND_FINALIZE, JSClass, JSClassOps, JSContext, JSFunctionSpec,
12 JSNativeWrapper, JSObject, JSTracer,
13};
14use mozjs::jsval::JSVal;
15
16use crate::class::{
17 NativeClass, NativeObject, PrototypeChain, Reflector, TypeIdWrapper, finalise_native_object_operation,
18 trace_native_object_operation,
19};
20use crate::conversions::{IntoValue, ToValue};
21use crate::flags::PropertyFlags;
22use crate::function::NativeFunction;
23use crate::object::class_reserved_slots;
24use crate::spec::{create_function_spec, create_function_spec_symbol};
25use crate::symbol::WellKnownSymbolCode;
26use crate::{Arguments, ClassDefinition, Context, Local, Object, ThrowException as _, Value};
27
28pub trait JSIterator {
29 fn next_value<'cx>(&mut self, cx: &'cx Context, private: &Value<'cx>) -> Option<Value<'cx>>;
30}
31
32impl<T, I: iter::Iterator<Item = T>> JSIterator for I
33where
34 T: for<'cx> IntoValue<'cx>,
35{
36 fn next_value<'cx>(&mut self, cx: &'cx Context, _: &Value) -> Option<Value<'cx>> {
37 self.next().map(|val| {
38 let mut rval = Value::undefined(cx);
39 Box::new(val).into_value(cx, &mut rval);
40 rval
41 })
42 }
43}
44
45pub struct IteratorResult<'cx> {
46 value: Value<'cx>,
47 done: bool,
48}
49
50impl<'cx> ToValue<'cx> for IteratorResult<'cx> {
51 fn to_value(&self, cx: &'cx Context, value: &mut Value) {
52 let object = Object::new(cx);
53 object.set_as(cx, "value", &self.value);
54 object.set_as(cx, "done", &self.done);
55 object.to_value(cx, value);
56 }
57}
58
59pub struct Iterator {
60 reflector: Reflector,
61 iter: Box<dyn JSIterator>,
62 private: Box<Heap<JSVal>>,
63}
64
65impl Iterator {
66 pub fn new<I: JSIterator + 'static>(iter: I, private: &Value) -> Iterator {
67 Iterator {
68 reflector: Reflector::default(),
69 iter: Box::new(iter),
70 private: Heap::boxed(private.get()),
71 }
72 }
73
74 pub fn next_value<'cx>(&'cx mut self, cx: &'cx Context) -> IteratorResult<'cx> {
75 let private = Value::from(unsafe { Local::from_heap(&self.private) });
76 let next = self.iter.next_value(cx, &private);
77 IteratorResult {
78 done: next.is_none(),
79 value: next.unwrap_or_else(Value::undefined_handle),
80 }
81 }
82}
83
84impl Iterator {
85 unsafe extern "C" fn next_raw(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
86 let cx = &unsafe { Context::new_unchecked(cx) };
87 let args = &mut unsafe { Arguments::new(cx, argc, vp) };
88
89 let this = args.this().to_object(cx);
90 let iterator = match Iterator::get_mut_private(cx, &this) {
91 Ok(iterator) => iterator,
92 Err(e) => {
93 e.throw(cx);
94 return false;
95 }
96 };
97 let result = iterator.next_value(cx);
98
99 result.to_value(cx, &mut args.rval());
100 true
101 }
102
103 unsafe extern "C" fn iterable(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
104 let cx = &unsafe { Context::new_unchecked(cx) };
105 let args = &mut unsafe { Arguments::new(cx, argc, vp) };
106
107 let this = args.this().handle().get();
108 args.rval().handle_mut().set(this);
109
110 true
111 }
112}
113
114impl IntoValue<'_> for Iterator {
115 fn into_value(self: Box<Self>, cx: &Context, value: &mut Value) {
116 let object = cx.root(Iterator::new_object(cx, self));
117 object.handle().get().to_value(cx, value);
118 }
119}
120
121unsafe impl Traceable for Iterator {
122 unsafe fn trace(&self, trc: *mut JSTracer) {
123 unsafe {
124 self.private.trace(trc);
125 }
126 }
127}
128
129static ITERATOR_CLASS_OPS: JSClassOps = JSClassOps {
130 addProperty: None,
131 delProperty: None,
132 enumerate: None,
133 newEnumerate: None,
134 resolve: None,
135 mayResolve: None,
136 finalize: Some(finalise_native_object_operation::<Iterator>),
137 call: None,
138 construct: None,
139 trace: Some(trace_native_object_operation::<Iterator>),
140};
141
142static ITERATOR_CLASS: NativeClass = NativeClass {
143 base: JSClass {
144 name: c"NativeIterator".as_ptr(),
145 flags: JSCLASS_BACKGROUND_FINALIZE | class_reserved_slots(1),
146 cOps: &raw const ITERATOR_CLASS_OPS,
147 spec: ptr::null_mut(),
148 ext: ptr::null_mut(),
149 oOps: ptr::null_mut(),
150 },
151 prototype_chain: PrototypeChain::new().push(&TypeIdWrapper::<Iterator>::new()),
152};
153
154static ITERATOR_METHODS: &[JSFunctionSpec] = &[
155 create_function_spec(
156 c"next",
157 JSNativeWrapper {
158 op: Some(Iterator::next_raw),
159 info: ptr::null_mut(),
160 },
161 0,
162 PropertyFlags::CONSTANT_ENUMERATED,
163 ),
164 create_function_spec_symbol(
165 WellKnownSymbolCode::Iterator,
166 JSNativeWrapper {
167 op: Some(Iterator::iterable),
168 info: ptr::null_mut(),
169 },
170 0,
171 PropertyFlags::CONSTANT,
172 ),
173 JSFunctionSpec::ZERO,
174];
175
176impl NativeObject for Iterator {
177 fn reflector(&self) -> &Reflector {
178 &self.reflector
179 }
180}
181
182impl ClassDefinition for Iterator {
183 fn class() -> &'static NativeClass {
184 &ITERATOR_CLASS
185 }
186
187 fn proto_class() -> Option<&'static NativeClass> {
188 None
189 }
190
191 fn parent_prototype(cx: &Context) -> Option<Local<'_, *mut JSObject>> {
192 Some(cx.root(unsafe { GetRealmIteratorPrototype(cx.as_ptr()) }))
193 }
194
195 fn constructor() -> (Option<NativeFunction>, u32) {
196 (None, 0)
197 }
198
199 fn functions() -> Option<&'static [JSFunctionSpec]> {
200 Some(ITERATOR_METHODS)
201 }
202}