#[allow(unused)]
use std::{
collections::{HashMap, HashSet},
fmt,
};
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};
use super::GLOBALS;
use crate::EqIgnoreSpan;
#[derive(Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(transparent)]
#[cfg_attr(
any(feature = "rkyv-impl"),
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
#[cfg_attr(feature = "rkyv-impl", repr(C))]
#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
pub struct SyntaxContext(#[cfg_attr(feature = "__rkyv", rkyv(omit_bounds))] u32);
#[cfg(feature = "encoding-impl")]
impl cbor4ii::core::enc::Encode for SyntaxContext {
#[inline]
fn encode<W: cbor4ii::core::enc::Write>(
&self,
writer: &mut W,
) -> Result<(), cbor4ii::core::enc::Error<W::Error>> {
self.0.encode(writer)
}
}
#[cfg(feature = "encoding-impl")]
impl<'de> cbor4ii::core::dec::Decode<'de> for SyntaxContext {
#[inline]
fn decode<R: cbor4ii::core::dec::Read<'de>>(
reader: &mut R,
) -> Result<Self, cbor4ii::core::dec::Error<R::Error>> {
u32::decode(reader).map(SyntaxContext)
}
}
#[cfg(feature = "arbitrary")]
#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
impl<'a> arbitrary::Arbitrary<'a> for SyntaxContext {
fn arbitrary(_: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
Ok(SyntaxContext::empty())
}
}
better_scoped_tls::scoped_tls!(static EQ_IGNORE_SPAN_IGNORE_CTXT: ());
impl EqIgnoreSpan for SyntaxContext {
fn eq_ignore_span(&self, other: &Self) -> bool {
self == other || EQ_IGNORE_SPAN_IGNORE_CTXT.is_set()
}
}
impl SyntaxContext {
pub fn within_ignored_ctxt<F, Ret>(op: F) -> Ret
where
F: FnOnce() -> Ret,
{
EQ_IGNORE_SPAN_IGNORE_CTXT.set(&(), op)
}
}
#[allow(unused)]
#[derive(Copy, Clone, Debug)]
struct SyntaxContextData {
outer_mark: Mark,
prev_ctxt: SyntaxContext,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct Mark(u32);
#[allow(unused)]
#[derive(Clone, Copy, Debug)]
pub(crate) struct MarkData {
pub(crate) parent: Mark,
}
#[cfg_attr(
any(feature = "rkyv-impl"),
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
#[cfg_attr(feature = "rkyv-impl", repr(C))]
#[cfg_attr(
feature = "encoding-impl",
derive(::ast_node::Encode, ::ast_node::Decode)
)]
pub struct MutableMarkContext(pub u32, pub u32, pub u32);
extern "C" {
fn __mark_fresh_proxy(mark: u32) -> u32;
fn __mark_parent_proxy(self_mark: u32) -> u32;
fn __syntax_context_apply_mark_proxy(self_syntax_context: u32, mark: u32) -> u32;
fn __syntax_context_outer_proxy(self_mark: u32) -> u32;
fn __mark_is_descendant_of_proxy(self_mark: u32, ancestor: u32, allocated_ptr: i32);
fn __mark_least_ancestor(a: u32, b: u32, allocated_ptr: i32);
fn __syntax_context_remove_mark_proxy(self_mark: u32, allocated_ptr: i32);
}
impl Mark {
#[track_caller]
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Mark::fresh(Mark::root())
}
#[track_caller]
pub fn fresh(parent: Mark) -> Self {
#[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
return Mark(unsafe { __mark_fresh_proxy(parent.as_u32()) });
#[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
return with_marks(|marks| {
marks.push(MarkData { parent });
Mark(marks.len() as u32 - 1)
});
}
#[inline]
pub const fn root() -> Self {
Mark(0)
}
#[inline]
pub fn as_u32(self) -> u32 {
self.0
}
#[inline]
pub fn from_u32(raw: u32) -> Mark {
Mark(raw)
}
#[inline]
pub fn parent(self) -> Mark {
#[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
return Mark(unsafe { __mark_parent_proxy(self.0) });
#[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
return with_marks(|marks| marks[self.0 as usize].parent);
}
#[allow(unused_assignments)]
#[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
pub fn is_descendant_of(mut self, ancestor: Mark) -> bool {
use crate::plugin::serialized::VersionedSerializable;
let serialized = crate::plugin::serialized::PluginSerializedBytes::try_serialize(
&VersionedSerializable::new(MutableMarkContext(0, 0, 0)),
)
.expect("Should be serializable");
let (ptr, len) = serialized.as_ptr();
unsafe {
__mark_is_descendant_of_proxy(self.0, ancestor.0, ptr as _);
}
let context: MutableMarkContext =
crate::plugin::serialized::PluginSerializedBytes::from_raw_ptr(
ptr,
len.try_into().expect("Should able to convert ptr length"),
)
.deserialize()
.expect("Should able to deserialize")
.into_inner();
self = Mark::from_u32(context.0);
return context.2 != 0;
}
#[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
pub fn is_descendant_of(mut self, ancestor: Mark) -> bool {
with_marks(|marks| {
while self != ancestor {
if self == Mark::root() {
return false;
}
self = marks[self.0 as usize].parent;
}
true
})
}
#[allow(unused_mut, unused_assignments)]
#[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
pub fn least_ancestor(mut a: Mark, mut b: Mark) -> Mark {
use crate::plugin::serialized::VersionedSerializable;
let serialized = crate::plugin::serialized::PluginSerializedBytes::try_serialize(
&VersionedSerializable::new(MutableMarkContext(0, 0, 0)),
)
.expect("Should be serializable");
let (ptr, len) = serialized.as_ptr();
unsafe {
__mark_least_ancestor(a.0, b.0, ptr as _);
}
let context: MutableMarkContext =
crate::plugin::serialized::PluginSerializedBytes::from_raw_ptr(
ptr,
len.try_into().expect("Should able to convert ptr length"),
)
.deserialize()
.expect("Should able to deserialize")
.into_inner();
a = Mark::from_u32(context.0);
b = Mark::from_u32(context.1);
return Mark(context.2);
}
#[allow(unused_mut)]
#[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
pub fn least_ancestor(mut a: Mark, mut b: Mark) -> Mark {
with_marks(|marks| {
let mut a_path = HashSet::<Mark>::default();
while a != Mark::root() {
a_path.insert(a);
a = marks[a.0 as usize].parent;
}
while !a_path.contains(&b) {
b = marks[b.0 as usize].parent;
}
b
})
}
}
#[derive(Clone, Debug)]
pub(crate) struct HygieneData {
syntax_contexts: Vec<SyntaxContextData>,
markings: FxHashMap<(SyntaxContext, Mark), SyntaxContext>,
}
impl Default for HygieneData {
fn default() -> Self {
Self::new()
}
}
impl HygieneData {
pub(crate) fn new() -> Self {
HygieneData {
syntax_contexts: vec![SyntaxContextData {
outer_mark: Mark::root(),
prev_ctxt: SyntaxContext(0),
}],
markings: HashMap::default(),
}
}
fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T {
GLOBALS.with(|globals| {
return f(&mut globals.hygiene_data.lock().unwrap());
})
}
}
#[track_caller]
#[allow(unused)]
pub(crate) fn with_marks<T, F: FnOnce(&mut Vec<MarkData>) -> T>(f: F) -> T {
GLOBALS.with(|globals| {
return f(&mut globals.marks.lock().unwrap());
})
}
impl SyntaxContext {
pub const fn empty() -> Self {
SyntaxContext(0)
}
pub fn has_mark(self, mark: Mark) -> bool {
debug_assert_ne!(
mark,
Mark::root(),
"Cannot check if a span contains a `ROOT` mark"
);
let mut ctxt = self;
loop {
if ctxt == SyntaxContext::empty() {
return false;
}
let m = ctxt.remove_mark();
if m == mark {
return true;
}
if m == Mark::root() {
return false;
}
}
}
#[inline]
pub fn as_u32(self) -> u32 {
self.0
}
#[inline]
pub fn from_u32(raw: u32) -> SyntaxContext {
SyntaxContext(raw)
}
pub fn apply_mark(self, mark: Mark) -> SyntaxContext {
#[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
return unsafe { SyntaxContext(__syntax_context_apply_mark_proxy(self.0, mark.0)) };
#[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
{
assert_ne!(mark, Mark::root());
self.apply_mark_internal(mark)
}
}
#[allow(unused)]
fn apply_mark_internal(self, mark: Mark) -> SyntaxContext {
HygieneData::with(|data| {
*data.markings.entry((self, mark)).or_insert_with(|| {
let syntax_contexts = &mut data.syntax_contexts;
let new_opaque = SyntaxContext(syntax_contexts.len() as u32);
syntax_contexts.push(SyntaxContextData {
outer_mark: mark,
prev_ctxt: self,
});
new_opaque
})
})
}
#[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
pub fn remove_mark(&mut self) -> Mark {
use crate::plugin::serialized::VersionedSerializable;
let context = VersionedSerializable::new(MutableMarkContext(0, 0, 0));
let serialized = crate::plugin::serialized::PluginSerializedBytes::try_serialize(&context)
.expect("Should be serializable");
let (ptr, len) = serialized.as_ptr();
unsafe {
__syntax_context_remove_mark_proxy(self.0, ptr as _);
}
let context: MutableMarkContext =
crate::plugin::serialized::PluginSerializedBytes::from_raw_ptr(
ptr,
len.try_into().expect("Should able to convert ptr length"),
)
.deserialize()
.expect("Should able to deserialize")
.into_inner();
*self = SyntaxContext(context.0);
return Mark::from_u32(context.2);
}
#[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
pub fn remove_mark(&mut self) -> Mark {
HygieneData::with(|data| {
let outer_mark = data.syntax_contexts[self.0 as usize].outer_mark;
*self = data.syntax_contexts[self.0 as usize].prev_ctxt;
outer_mark
})
}
#[inline]
pub fn outer(self) -> Mark {
#[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
return unsafe { Mark(__syntax_context_outer_proxy(self.0)) };
#[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
HygieneData::with(|data| data.syntax_contexts[self.0 as usize].outer_mark)
}
}
impl fmt::Debug for SyntaxContext {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "#{}", self.0)
}
}
impl Default for Mark {
#[track_caller]
fn default() -> Self {
Mark::new()
}
}