ion/object/
iterator.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 */
6
7use 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}