1use super::JsClass;
2use crate::{markers::Invariant, qjs, Atom, Class, Ctx, Module, Value};
3use core::marker::PhantomData;
4
5#[cfg(feature = "either")]
6use either::{Either, Left, Right};
7
8pub trait Trace<'js> {
15 fn trace<'a>(&self, tracer: Tracer<'a, 'js>);
16}
17
18#[derive(Clone, Copy)]
20pub struct Tracer<'a, 'js> {
21 rt: *mut qjs::JSRuntime,
22 mark_func: qjs::JS_MarkFunc,
23 _inv: Invariant<'js>,
25 _marker: PhantomData<&'a qjs::JSRuntime>,
27}
28
29impl<'a, 'js> Tracer<'a, 'js> {
30 pub unsafe fn from_ffi(rt: *mut qjs::JSRuntime, mark_func: qjs::JS_MarkFunc) -> Self {
36 Self {
37 rt,
38 mark_func,
39 _inv: Invariant::new(),
40 _marker: PhantomData,
41 }
42 }
43
44 pub(crate) unsafe fn cast_js_lifetime<'js2>(self) -> Tracer<'a, 'js2> {
45 Tracer {
46 rt: self.rt,
47 mark_func: self.mark_func,
48 _inv: Invariant::new(),
49 _marker: PhantomData,
50 }
51 }
52
53 pub fn mark(self, value: &Value<'js>) {
55 self.mark_ctx(value.ctx());
56 let value = value.as_js_value();
57 if unsafe { qjs::JS_VALUE_HAS_REF_COUNT(value) } {
58 unsafe { qjs::JS_MarkValue(self.rt, value, self.mark_func) };
59 }
60 }
61
62 pub fn mark_ctx(self, ctx: &Ctx<'js>) {
64 let ptr = ctx.as_ptr();
65 unsafe { (self.mark_func.unwrap())(self.rt, ptr.cast()) }
66 }
67}
68
69impl<'js> Trace<'js> for Value<'js> {
70 fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
71 tracer.mark(self);
72 }
73}
74
75impl<'js> Trace<'js> for Ctx<'js> {
76 fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
77 tracer.mark_ctx(self);
78 }
79}
80
81impl<'js, T> Trace<'js> for Class<'js, T>
82where
83 T: JsClass<'js>,
84{
85 fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
86 self.0.trace(tracer)
87 }
88}
89
90impl<'js, T> Trace<'js> for Module<'js, T> {
91 fn trace<'a>(&self, _tracer: Tracer<'a, 'js>) {}
92}
93
94impl<'js, T> Trace<'js> for Option<T>
95where
96 T: Trace<'js>,
97{
98 fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
99 if let Some(inner) = &self {
100 inner.trace(tracer);
101 }
102 }
103}
104
105impl<'js> Trace<'js> for Atom<'js> {
106 fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
107 tracer.mark_ctx(&self.ctx);
108 }
109}
110
111impl<'js> Trace<'js> for crate::Constructor<'js> {
112 fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
113 self.0.trace(tracer)
114 }
115}
116
117impl<'js> Trace<'js> for crate::Proxy<'js> {
118 fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
119 self.0.trace(tracer)
120 }
121}
122
123impl<'js, T> Trace<'js> for crate::TypedArray<'js, T> {
124 fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
125 self.as_value().trace(tracer)
126 }
127}
128
129#[cfg(feature = "either")]
130#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "either")))]
131impl<'js, L, R> Trace<'js> for Either<L, R>
132where
133 L: Trace<'js>,
134 R: Trace<'js>,
135{
136 fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
137 match self {
138 Left(l) => l.trace(tracer),
139 Right(r) => r.trace(tracer),
140 }
141 }
142}
143
144macro_rules! trace_impls {
145
146 (primitive: $( $(#[$meta:meta])* $($type:ident)::+$(<$lt:lifetime>)?,)*) => {
147 $(
148 $(#[$meta])*
149 impl<'js> Trace<'js> for $($type)::*$(<$lt>)*{
150 fn trace<'a>(&self, _tracer: Tracer<'a,'js>) { }
151 }
152 )*
153 };
154
155 (base: $( $(#[$meta:meta])* $($type:ident)::+,)*) => {
156 $(
157 $(#[$meta])*
158 impl<'js> Trace<'js> for $($type)::*<'js>{
159 fn trace<'a>(&self, tracer: Tracer<'a,'js>) {
160 self.as_value().trace(tracer)
161 }
162 }
163 )*
164 };
165
166 (ref: $($($type:ident)::+,)*) => {
167 $(
168 impl<'js, T> Trace<'js> for $($type)::*<T>
169 where
170 T: Trace<'js>,
171 {
172 fn trace<'a>(&self, tracer: Tracer<'a,'js>) {
173 let this: &T = &self;
174 this.trace(tracer);
175 }
176 }
177 )*
178 };
179
180 (tup: $($($type:ident)*,)*) => {
181 $(
182 impl<'js, $($type),*> Trace<'js> for ($($type,)*)
183 where
184 $($type: Trace<'js>,)*
185 {
186 #[allow(non_snake_case)]
187 fn trace<'a>(&self, _tracer: Tracer<'a,'js>) {
188 let ($($type,)*) = &self;
189 $($type.trace(_tracer);)*
190 }
191 }
192 )*
193 };
194
195 (list: $($(#[$meta:meta])* $($type:ident)::+ $({$param:ident})*,)*) => {
196 $(
197 $(#[$meta])*
198 impl<'js, T $(,$param)*> Trace<'js> for $($type)::*<T $(,$param)*>
199 where
200 T: Trace<'js>,
201 {
202 fn trace<'a>(&self, tracer: Tracer<'a,'js>) {
203 for item in self.iter() {
204 item.trace(tracer);
205 }
206 }
207 }
208 )*
209 };
210
211 (map: $($(#[$meta:meta])* $($type:ident)::+ $({$param:ident})*,)*) => {
212 $(
213 $(#[$meta])*
214 impl<'js, K, V $(,$param)*> Trace<'js> for $($type)::*<K, V $(,$param)*>
215 where
216 K: Trace<'js>,
217 V: Trace<'js>,
218 {
219 fn trace<'a>(&self, tracer: Tracer<'a,'js>) {
220 for (key,item) in self.iter() {
221 key.trace(tracer);
222 item.trace(tracer);
223 }
224 }
225 }
226 )*
227 };
228}
229
230trace_impls! {
231 primitive:
232 u8,u16,u32,u64,usize,
233 i8,i16,i32,i64,isize,
234 f32,f64,
235 bool,char,
236 alloc::string::String,
237}
238
239trace_impls! {
240 base:
241 crate::Object,
242 crate::Array,
243 crate::Function,
244 crate::BigInt,
245 crate::Symbol,
246 crate::Exception,
247 crate::String,
248 crate::Promise,
249 crate::ArrayBuffer,
250}
251
252trace_impls! {
253 ref:
254 alloc::boxed::Box,
255 alloc::rc::Rc,
256 alloc::sync::Arc,
257}
258
259trace_impls! {
260 tup:
261 ,
262 A,
263 A B,
264 A B C,
265 A B C D,
266 A B C D E,
267 A B C D E F,
268 A B C D E F G,
269 A B C D E F G H,
270 A B C D E F G H I,
271 A B C D E F G H I J,
272 A B C D E F G H I J K,
273 A B C D E F G H I J K L,
274 A B C D E F G H I J K L M,
275 A B C D E F G H I J K L M N,
276 A B C D E F G H I J K L M N O,
277 A B C D E F G H I J K L M N O P,
278}
279
280trace_impls! {
281 list:
282 alloc::vec::Vec,
283 alloc::collections::VecDeque,
284 alloc::collections::LinkedList,
285 #[cfg(feature = "std")]
286 std::collections::HashSet {S},
287 alloc::collections::BTreeSet,
288 #[cfg(feature = "indexmap")]
289 #[cfg_attr(feature = "doc-cfg", doc(cfg(all(feature = "indexmap"))))]
290 indexmap::IndexSet {S},
291}
292
293trace_impls! {
294 map:
295 #[cfg(feature = "std")]
296 std::collections::HashMap {S},
297 alloc::collections::BTreeMap,
298 #[cfg(feature = "indexmap")]
299 #[cfg_attr(feature = "doc-cfg", doc(cfg(all(feature = "indexmap"))))]
300 indexmap::IndexMap {S},
301}