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
111#[cfg(feature = "either")]
112#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "either")))]
113impl<'js, L, R> Trace<'js> for Either<L, R>
114where
115 L: Trace<'js>,
116 R: Trace<'js>,
117{
118 fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
119 match self {
120 Left(l) => l.trace(tracer),
121 Right(r) => r.trace(tracer),
122 }
123 }
124}
125
126macro_rules! trace_impls {
127
128 (primitive: $( $(#[$meta:meta])* $($type:ident)::+$(<$lt:lifetime>)?,)*) => {
129 $(
130 $(#[$meta])*
131 impl<'js> Trace<'js> for $($type)::*$(<$lt>)*{
132 fn trace<'a>(&self, _tracer: Tracer<'a,'js>) { }
133 }
134 )*
135 };
136
137 (base: $( $(#[$meta:meta])* $($type:ident)::+,)*) => {
138 $(
139 $(#[$meta])*
140 impl<'js> Trace<'js> for $($type)::*<'js>{
141 fn trace<'a>(&self, tracer: Tracer<'a,'js>) {
142 self.as_value().trace(tracer)
143 }
144 }
145 )*
146 };
147
148 (ref: $($($type:ident)::+,)*) => {
149 $(
150 impl<'js, T> Trace<'js> for $($type)::*<T>
151 where
152 T: Trace<'js>,
153 {
154 fn trace<'a>(&self, tracer: Tracer<'a,'js>) {
155 let this: &T = &self;
156 this.trace(tracer);
157 }
158 }
159 )*
160 };
161
162 (tup: $($($type:ident)*,)*) => {
163 $(
164 impl<'js, $($type),*> Trace<'js> for ($($type,)*)
165 where
166 $($type: Trace<'js>,)*
167 {
168 #[allow(non_snake_case)]
169 fn trace<'a>(&self, _tracer: Tracer<'a,'js>) {
170 let ($($type,)*) = &self;
171 $($type.trace(_tracer);)*
172 }
173 }
174 )*
175 };
176
177 (list: $($(#[$meta:meta])* $($type:ident)::+ $({$param:ident})*,)*) => {
178 $(
179 $(#[$meta])*
180 impl<'js, T $(,$param)*> Trace<'js> for $($type)::*<T $(,$param)*>
181 where
182 T: Trace<'js>,
183 {
184 fn trace<'a>(&self, tracer: Tracer<'a,'js>) {
185 for item in self.iter() {
186 item.trace(tracer);
187 }
188 }
189 }
190 )*
191 };
192
193 (map: $($(#[$meta:meta])* $($type:ident)::+ $({$param:ident})*,)*) => {
194 $(
195 $(#[$meta])*
196 impl<'js, K, V $(,$param)*> Trace<'js> for $($type)::*<K, V $(,$param)*>
197 where
198 K: Trace<'js>,
199 V: Trace<'js>,
200 {
201 fn trace<'a>(&self, tracer: Tracer<'a,'js>) {
202 for (key,item) in self.iter() {
203 key.trace(tracer);
204 item.trace(tracer);
205 }
206 }
207 }
208 )*
209 };
210}
211
212trace_impls! {
213 primitive:
214 u8,u16,u32,u64,usize,
215 i8,i16,i32,i64,isize,
216 f32,f64,
217 bool,char,
218 alloc::string::String,
219}
220
221trace_impls! {
222 base:
223 crate::Object,
224 crate::Array,
225 crate::Function,
226 crate::BigInt,
227 crate::Symbol,
228 crate::Exception,
229 crate::String,
230}
231
232trace_impls! {
233 ref:
234 alloc::boxed::Box,
235 alloc::rc::Rc,
236 alloc::sync::Arc,
237}
238
239trace_impls! {
240 tup:
241 ,
242 A,
243 A B,
244 A B C,
245 A B C D,
246 A B C D E,
247 A B C D E F,
248 A B C D E F G,
249 A B C D E F G H,
250 A B C D E F G H I,
251 A B C D E F G H I J,
252 A B C D E F G H I J K,
253 A B C D E F G H I J K L,
254 A B C D E F G H I J K L M,
255 A B C D E F G H I J K L M N,
256 A B C D E F G H I J K L M N O,
257 A B C D E F G H I J K L M N O P,
258}
259
260trace_impls! {
261 list:
262 alloc::vec::Vec,
263 alloc::collections::VecDeque,
264 alloc::collections::LinkedList,
265 #[cfg(feature = "std")]
266 std::collections::HashSet {S},
267 alloc::collections::BTreeSet,
268 #[cfg(feature = "indexmap")]
269 #[cfg_attr(feature = "doc-cfg", doc(cfg(all(feature = "indexmap"))))]
270 indexmap::IndexSet {S},
271}
272
273trace_impls! {
274 map:
275 #[cfg(feature = "std")]
276 std::collections::HashMap {S},
277 alloc::collections::BTreeMap,
278 #[cfg(feature = "indexmap")]
279 #[cfg_attr(feature = "doc-cfg", doc(cfg(all(feature = "indexmap"))))]
280 indexmap::IndexMap {S},
281}