1#[allow(unused)]
19use std::{
20 collections::{HashMap, HashSet},
21 fmt,
22};
23
24use rustc_hash::FxHashMap;
25use serde::{Deserialize, Serialize};
26
27use super::GLOBALS;
28use crate::EqIgnoreSpan;
29
30#[derive(Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord, Hash, Serialize, Deserialize)]
33#[serde(transparent)]
34#[cfg_attr(
35 any(feature = "rkyv-impl"),
36 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
37)]
38#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
39#[cfg_attr(feature = "rkyv-impl", repr(C))]
40#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
41pub struct SyntaxContext(#[cfg_attr(feature = "__rkyv", rkyv(omit_bounds))] u32);
42
43#[cfg(feature = "encoding-impl")]
44impl cbor4ii::core::enc::Encode for SyntaxContext {
45 #[inline]
46 fn encode<W: cbor4ii::core::enc::Write>(
47 &self,
48 writer: &mut W,
49 ) -> Result<(), cbor4ii::core::enc::Error<W::Error>> {
50 self.0.encode(writer)
51 }
52}
53
54#[cfg(feature = "encoding-impl")]
55impl<'de> cbor4ii::core::dec::Decode<'de> for SyntaxContext {
56 #[inline]
57 fn decode<R: cbor4ii::core::dec::Read<'de>>(
58 reader: &mut R,
59 ) -> Result<Self, cbor4ii::core::dec::Error<R::Error>> {
60 u32::decode(reader).map(SyntaxContext)
61 }
62}
63
64#[cfg(feature = "arbitrary")]
65#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
66impl<'a> arbitrary::Arbitrary<'a> for SyntaxContext {
67 fn arbitrary(_: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
68 Ok(SyntaxContext::empty())
69 }
70}
71
72better_scoped_tls::scoped_tls!(static EQ_IGNORE_SPAN_IGNORE_CTXT: ());
73
74impl EqIgnoreSpan for SyntaxContext {
75 fn eq_ignore_span(&self, other: &Self) -> bool {
76 self == other || EQ_IGNORE_SPAN_IGNORE_CTXT.is_set()
77 }
78}
79
80impl SyntaxContext {
81 pub fn within_ignored_ctxt<F, Ret>(op: F) -> Ret
83 where
84 F: FnOnce() -> Ret,
85 {
86 EQ_IGNORE_SPAN_IGNORE_CTXT.set(&(), op)
87 }
88}
89
90#[allow(unused)]
91#[derive(Copy, Clone, Debug)]
92struct SyntaxContextData {
93 outer_mark: Mark,
94 prev_ctxt: SyntaxContext,
95}
96
97#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
99pub struct Mark(u32);
100
101#[allow(unused)]
102#[derive(Clone, Copy, Debug)]
103pub(crate) struct MarkData {
104 pub(crate) parent: Mark,
105}
106
107#[cfg_attr(
108 any(feature = "rkyv-impl"),
109 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
110)]
111#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
112#[cfg_attr(feature = "rkyv-impl", repr(C))]
113pub struct MutableMarkContext(pub u32, pub u32, pub u32);
114
115extern "C" {
119 fn __mark_fresh_proxy(mark: u32) -> u32;
123 fn __mark_parent_proxy(self_mark: u32) -> u32;
124 fn __syntax_context_apply_mark_proxy(self_syntax_context: u32, mark: u32) -> u32;
125 fn __syntax_context_outer_proxy(self_mark: u32) -> u32;
126
127 fn __mark_is_descendant_of_proxy(self_mark: u32, ancestor: u32, allocated_ptr: i32);
130 fn __mark_least_ancestor(a: u32, b: u32, allocated_ptr: i32);
131 fn __syntax_context_remove_mark_proxy(self_mark: u32, allocated_ptr: i32);
132}
133
134impl Mark {
135 #[track_caller]
137 #[allow(clippy::new_without_default)]
138 pub fn new() -> Self {
139 Mark::fresh(Mark::root())
140 }
141
142 #[track_caller]
143 pub fn fresh(parent: Mark) -> Self {
144 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
147 return Mark(unsafe { __mark_fresh_proxy(parent.as_u32()) });
148
149 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
153 return with_marks(|marks| {
154 marks.push(MarkData { parent });
155 Mark(marks.len() as u32 - 1)
156 });
157 }
158
159 #[inline]
162 pub const fn root() -> Self {
163 Mark(0)
164 }
165
166 #[inline]
167 pub fn as_u32(self) -> u32 {
168 self.0
169 }
170
171 #[inline]
172 pub fn from_u32(raw: u32) -> Mark {
173 Mark(raw)
174 }
175
176 #[inline]
177 pub fn parent(self) -> Mark {
178 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
179 return Mark(unsafe { __mark_parent_proxy(self.0) });
180
181 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
182 return with_marks(|marks| marks[self.0 as usize].parent);
183 }
184
185 #[allow(unused_assignments)]
186 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
187 pub fn is_descendant_of(mut self, ancestor: Mark) -> bool {
188 use crate::plugin::serialized::VersionedSerializable;
192 let serialized = crate::plugin::serialized::PluginSerializedBytes::try_serialize(
193 &VersionedSerializable::new(MutableMarkContext(0, 0, 0)),
194 )
195 .expect("Should be serializable");
196 let (ptr, len) = serialized.as_ptr();
197
198 unsafe {
201 __mark_is_descendant_of_proxy(self.0, ancestor.0, ptr as _);
202 }
203
204 let context: MutableMarkContext =
206 crate::plugin::serialized::PluginSerializedBytes::from_raw_ptr(
207 ptr,
208 len.try_into().expect("Should able to convert ptr length"),
209 )
210 .deserialize()
211 .expect("Should able to deserialize")
212 .into_inner();
213
214 self = Mark::from_u32(context.0);
215
216 return context.2 != 0;
217 }
218
219 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
220 pub fn is_descendant_of(mut self, ancestor: Mark) -> bool {
221 with_marks(|marks| {
222 while self != ancestor {
223 if self == Mark::root() {
224 return false;
225 }
226 self = marks[self.0 as usize].parent;
227 }
228 true
229 })
230 }
231
232 #[allow(unused_mut, unused_assignments)]
233 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
234 pub fn least_ancestor(mut a: Mark, mut b: Mark) -> Mark {
235 use crate::plugin::serialized::VersionedSerializable;
236
237 let serialized = crate::plugin::serialized::PluginSerializedBytes::try_serialize(
238 &VersionedSerializable::new(MutableMarkContext(0, 0, 0)),
239 )
240 .expect("Should be serializable");
241 let (ptr, len) = serialized.as_ptr();
242
243 unsafe {
244 __mark_least_ancestor(a.0, b.0, ptr as _);
245 }
246
247 let context: MutableMarkContext =
248 crate::plugin::serialized::PluginSerializedBytes::from_raw_ptr(
249 ptr,
250 len.try_into().expect("Should able to convert ptr length"),
251 )
252 .deserialize()
253 .expect("Should able to deserialize")
254 .into_inner();
255 a = Mark::from_u32(context.0);
256 b = Mark::from_u32(context.1);
257
258 return Mark(context.2);
259 }
260
261 #[allow(unused_mut)]
270 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
271 pub fn least_ancestor(mut a: Mark, mut b: Mark) -> Mark {
272 with_marks(|marks| {
273 let mut a_path = HashSet::<Mark>::default();
275 while a != Mark::root() {
276 a_path.insert(a);
277 a = marks[a.0 as usize].parent;
278 }
279
280 while !a_path.contains(&b) {
282 b = marks[b.0 as usize].parent;
283 }
284
285 b
286 })
287 }
288}
289
290#[derive(Clone, Debug)]
291pub(crate) struct HygieneData {
292 syntax_contexts: Vec<SyntaxContextData>,
293 markings: FxHashMap<(SyntaxContext, Mark), SyntaxContext>,
294}
295
296impl Default for HygieneData {
297 fn default() -> Self {
298 Self::new()
299 }
300}
301
302impl HygieneData {
303 pub(crate) fn new() -> Self {
304 HygieneData {
305 syntax_contexts: vec![SyntaxContextData {
306 outer_mark: Mark::root(),
307 prev_ctxt: SyntaxContext(0),
308 }],
309 markings: HashMap::default(),
310 }
311 }
312
313 fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T {
314 GLOBALS.with(|globals| {
315 return f(&mut globals.hygiene_data.lock().unwrap());
316 })
317 }
318}
319
320#[track_caller]
321#[allow(unused)]
322pub(crate) fn with_marks<T, F: FnOnce(&mut Vec<MarkData>) -> T>(f: F) -> T {
323 GLOBALS.with(|globals| {
324 return f(&mut globals.marks.lock().unwrap());
325 })
326}
327
328impl SyntaxContext {
333 pub const fn empty() -> Self {
334 SyntaxContext(0)
335 }
336
337 pub fn has_mark(self, mark: Mark) -> bool {
341 debug_assert_ne!(
342 mark,
343 Mark::root(),
344 "Cannot check if a span contains a `ROOT` mark"
345 );
346
347 let mut ctxt = self;
348
349 loop {
350 if ctxt == SyntaxContext::empty() {
351 return false;
352 }
353
354 let m = ctxt.remove_mark();
355 if m == mark {
356 return true;
357 }
358 if m == Mark::root() {
359 return false;
360 }
361 }
362 }
363
364 #[inline]
365 pub fn as_u32(self) -> u32 {
366 self.0
367 }
368
369 #[inline]
370 pub fn from_u32(raw: u32) -> SyntaxContext {
371 SyntaxContext(raw)
372 }
373
374 pub fn apply_mark(self, mark: Mark) -> SyntaxContext {
377 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
378 return unsafe { SyntaxContext(__syntax_context_apply_mark_proxy(self.0, mark.0)) };
379
380 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
381 {
382 assert_ne!(mark, Mark::root());
383 self.apply_mark_internal(mark)
384 }
385 }
386
387 #[allow(unused)]
388 fn apply_mark_internal(self, mark: Mark) -> SyntaxContext {
389 HygieneData::with(|data| {
390 *data.markings.entry((self, mark)).or_insert_with(|| {
391 let syntax_contexts = &mut data.syntax_contexts;
392 let new_opaque = SyntaxContext(syntax_contexts.len() as u32);
393 syntax_contexts.push(SyntaxContextData {
394 outer_mark: mark,
395 prev_ctxt: self,
396 });
397 new_opaque
398 })
399 })
400 }
401
402 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
403 pub fn remove_mark(&mut self) -> Mark {
404 use crate::plugin::serialized::VersionedSerializable;
405
406 let context = VersionedSerializable::new(MutableMarkContext(0, 0, 0));
407 let serialized = crate::plugin::serialized::PluginSerializedBytes::try_serialize(&context)
408 .expect("Should be serializable");
409 let (ptr, len) = serialized.as_ptr();
410
411 unsafe {
412 __syntax_context_remove_mark_proxy(self.0, ptr as _);
413 }
414
415 let context: MutableMarkContext =
416 crate::plugin::serialized::PluginSerializedBytes::from_raw_ptr(
417 ptr,
418 len.try_into().expect("Should able to convert ptr length"),
419 )
420 .deserialize()
421 .expect("Should able to deserialize")
422 .into_inner();
423
424 *self = SyntaxContext(context.0);
425
426 return Mark::from_u32(context.2);
427 }
428
429 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
446 pub fn remove_mark(&mut self) -> Mark {
447 HygieneData::with(|data| {
448 let outer_mark = data.syntax_contexts[self.0 as usize].outer_mark;
449 *self = data.syntax_contexts[self.0 as usize].prev_ctxt;
450 outer_mark
451 })
452 }
453
454 #[inline]
455 pub fn outer(self) -> Mark {
456 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
457 return unsafe { Mark(__syntax_context_outer_proxy(self.0)) };
458
459 #[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
460 HygieneData::with(|data| data.syntax_contexts[self.0 as usize].outer_mark)
461 }
462}
463
464impl fmt::Debug for SyntaxContext {
465 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
466 write!(f, "#{}", self.0)
467 }
468}
469
470impl Default for Mark {
471 #[track_caller]
472 fn default() -> Self {
473 Mark::new()
474 }
475}