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