1use crate::core::env::sym::BUILTIN_SYMBOLS;
2use crate::core::gc::{Block, Context, GcHeap, GcMoveable, GcState, Trace, TracePtr};
3use crate::core::object::{CloneIn, Function, FunctionType, Gc, IntoObject, TagType, WithLifetime};
4use anyhow::{Result, bail};
5use std::cell::Cell;
6use std::fmt;
7use std::hash::{Hash, Hasher};
8use std::marker::PhantomData;
9use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
10
11pub(crate) struct SymbolCell(GcHeap<SymbolCellData>);
19
20struct SymbolCellData {
21 name: SymbolName,
22 func: Option<AtomicPtr<u8>>,
25 special: AtomicBool,
26 }
28
29#[derive(Debug)]
30enum SymbolName {
31 Interned(&'static str),
32 Uninterned(Cell<&'static str>),
33}
34
35impl SymbolName {
36 fn as_bytes(&self) -> &[u8] {
37 match self {
38 SymbolName::Interned(x) => x.as_bytes(),
39 SymbolName::Uninterned(x) => x.get().as_bytes(),
40 }
41 }
42}
43
44unsafe impl Sync for SymbolName {}
45
46#[derive(PartialEq, Eq, Copy, Clone)]
47pub(crate) struct Symbol<'a> {
48 data: *const u8,
50 marker: PhantomData<&'a SymbolCell>,
51}
52
53impl std::ops::Deref for Symbol<'_> {
54 type Target = SymbolCell;
55
56 fn deref(&self) -> &Self::Target {
57 self.get()
58 }
59}
60
61impl<'a> Symbol<'a> {
62 pub(crate) fn get(self) -> &'a SymbolCell {
63 unsafe {
64 let base = BUILTIN_SYMBOLS.as_ptr().addr();
65 let ptr = self.data.map_addr(|x| x.wrapping_add(base)).cast::<SymbolCell>();
66 if BUILTIN_SYMBOLS.as_ptr_range().contains(&ptr) {
68 &*BUILTIN_SYMBOLS.as_ptr().with_addr(ptr.addr())
69 } else {
70 &*ptr
71 }
72 }
73 }
74
75 pub(in crate::core) fn as_ptr(self) -> *const u8 {
76 self.data.cast()
77 }
78
79 pub(in crate::core) unsafe fn from_offset_ptr(ptr: *const u8) -> Self {
80 Self { data: ptr.cast(), marker: PhantomData }
81 }
82
83 pub(in crate::core) unsafe fn from_ptr(ptr: *const SymbolCell) -> Self {
84 let base = BUILTIN_SYMBOLS.as_ptr().addr();
85 let ptr = ptr.map_addr(|x| x.wrapping_sub(base));
86 Self { data: ptr.cast::<u8>(), marker: PhantomData }
87 }
88
89 pub(in crate::core) const fn new_builtin(idx: usize) -> Self {
90 let ptr = core::ptr::without_provenance(idx * size_of::<SymbolCell>());
91 Self { data: ptr, marker: PhantomData }
92 }
93
94 pub(crate) fn make_special(self) {
95 self.0.special.store(true, Ordering::Release);
96 }
97
98 pub(crate) fn is_special(self) -> bool {
99 self.0.special.load(Ordering::Acquire)
100 }
101}
102
103unsafe impl Send for Symbol<'_> {}
104
105impl<'new> WithLifetime<'new> for Symbol<'_> {
107 type Out = Symbol<'new>;
108
109 unsafe fn with_lifetime(self) -> Self::Out {
110 unsafe { std::mem::transmute(self) }
111 }
112}
113
114impl TracePtr for Symbol<'_> {
115 fn trace_ptr(&self, state: &mut GcState) {
116 self.get().trace(state);
117 }
118}
119
120impl<'a> GcMoveable for Symbol<'a> {
121 type Value = Symbol<'a>;
122
123 fn move_value(&self, to_space: &bumpalo::Bump) -> Option<(Self::Value, bool)> {
124 let val = self.get().0.move_value(to_space);
125 val.map(|(ptr, moved)| {
126 let symbol = unsafe {
127 let ptr = ptr.cast::<SymbolCell>();
129 Self::from_ptr(ptr.as_ptr())
130 };
131 (symbol, moved)
132 })
133 }
134}
135
136impl Trace for SymbolCell {
137 fn trace(&self, state: &mut GcState) {
138 if let SymbolName::Uninterned(name) = &self.0.name {
139 let new = state.to_space.alloc_str(name.get());
140 let new = unsafe { std::mem::transmute::<&str, &'static str>(new) };
141 name.set(new);
142 }
143 }
146}
147
148impl fmt::Display for Symbol<'_> {
149 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
150 write!(f, "{}", self.name())
151 }
152}
153
154impl fmt::Debug for Symbol<'_> {
155 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
156 write!(f, "{}", self.name())
157 }
158}
159
160impl Hash for Symbol<'_> {
161 fn hash<H: Hasher>(&self, state: &mut H) {
162 self.data.hash(state);
163 }
164}
165
166impl<'new> Symbol<'_> {
167 pub(in crate::core) fn clone_in<const C: bool>(
168 self,
169 bk: &'new crate::core::gc::Block<C>,
170 ) -> Gc<Symbol<'new>> {
171 if let SymbolName::Uninterned(name) = &self.0.name {
172 match bk.uninterned_symbol_map.get(self) {
173 Some(new) => new.tag(),
174 None => {
175 let sym = Symbol::new_uninterned(name.get(), bk);
176 if let Some(old_func) = self.get().get() {
177 let new_func = old_func.clone_in(bk);
178 unsafe {
179 sym.set_func(new_func).unwrap();
180 }
181 }
182 let new = sym.into_obj(bk);
183 bk.uninterned_symbol_map.insert(self, new.untag());
184 new
185 }
186 }
187 } else {
188 unsafe { self.with_lifetime().tag() }
189 }
190 }
191}
192
193impl PartialEq for SymbolCell {
196 fn eq(&self, other: &Self) -> bool {
197 std::ptr::eq(self, other)
198 }
199}
200
201impl<'ob> Symbol<'ob> {
202 pub(in crate::core) fn new(name: &'static str, block: &'ob Block<true>) -> Self {
203 SymbolCell::new_normal(name, block).into_obj(block).untag()
204 }
205
206 pub(crate) fn new_uninterned<const C: bool>(name: &str, block: &'ob Block<C>) -> Self {
207 SymbolCell::new_uninterned(name, block).into_obj(block).untag()
208 }
209}
210
211impl SymbolCell {
212 const NULL: *mut u8 = std::ptr::null_mut();
213 #[expect(clippy::declare_interior_mutable_const)]
214 const EMTPTY: AtomicPtr<u8> = AtomicPtr::new(Self::NULL);
215
216 pub(crate) fn as_bytes(&self) -> &[u8] {
217 self.0.name.as_bytes()
218 }
219
220 fn new_normal(name: &'static str, block: &Block<true>) -> Self {
221 if name.as_bytes()[0] == b':' {
223 Self::new_const(name, block)
224 } else {
225 Self(GcHeap::new(
226 SymbolCellData {
227 name: SymbolName::Interned(name),
228 func: Some(Self::EMTPTY),
229 special: AtomicBool::new(false),
230 },
231 true,
232 ))
233 }
234 }
235
236 pub(in crate::core) const fn new_static(name: &'static str) -> Self {
237 if name.as_bytes()[0] == b':' {
239 Self::new_static_const(name)
240 } else {
241 Self(GcHeap::new_pure(SymbolCellData {
242 name: SymbolName::Interned(name),
243 func: Some(Self::EMTPTY),
244 special: AtomicBool::new(false),
245 }))
246 }
247 }
248
249 pub(in crate::core) const fn new_static_special(name: &'static str) -> Self {
250 Self(GcHeap::new_pure(SymbolCellData {
251 name: SymbolName::Interned(name),
252 func: Some(Self::EMTPTY),
253 special: AtomicBool::new(true),
254 }))
255 }
256
257 fn new_const(name: &'static str, _block: &Block<true>) -> Self {
258 Self(GcHeap::new(
259 SymbolCellData {
260 name: SymbolName::Interned(name),
261 func: None,
262 special: AtomicBool::new(true),
263 },
264 true,
265 ))
266 }
267
268 pub(in crate::core) const fn new_static_const(name: &'static str) -> Self {
269 Self(GcHeap::new_pure(SymbolCellData {
270 name: SymbolName::Interned(name),
271 func: None,
272 special: AtomicBool::new(true),
273 }))
274 }
275
276 fn new_uninterned<const C: bool>(name: &str, bk: &Block<C>) -> Self {
277 let mut owned_name = bk.string_with_capacity(name.len());
278 owned_name.push_str(name);
279 let name = unsafe { std::mem::transmute::<&str, &'static str>(owned_name.into_bump_str()) };
280 Self(GcHeap::new(
281 SymbolCellData {
282 name: SymbolName::Uninterned(Cell::new(name)),
283 func: Some(Self::EMTPTY),
284 special: AtomicBool::new(false),
285 },
286 C,
287 ))
288 }
289
290 pub(crate) fn name(&self) -> &str {
291 match &self.0.name {
292 SymbolName::Interned(x) => x,
293 SymbolName::Uninterned(x) => x.get(),
294 }
295 }
296
297 pub(crate) fn interned(&self) -> bool {
298 matches!(self.0.name, SymbolName::Interned(_))
299 }
300
301 #[inline(always)]
302 pub(crate) fn is_const(&self) -> bool {
304 self.0.func.is_none()
305 }
306
307 pub(crate) fn has_func(&self) -> bool {
308 match &self.0.func {
309 Some(func) => !func.load(Ordering::Acquire).is_null(),
310 None => false,
311 }
312 }
313
314 fn get(&self) -> Option<Function<'_>> {
315 if let Some(func) = &self.0.func {
316 let ptr = func.load(Ordering::Acquire);
317 if !ptr.is_null() {
319 return Some(unsafe { Gc::from_raw_ptr(ptr) });
320 }
321 }
322 None
323 }
324
325 pub(crate) fn func<'a>(&self, _cx: &'a Context) -> Option<Function<'a>> {
326 self.get().map(|x| unsafe { x.with_lifetime() })
327 }
328
329 pub(crate) fn follow_indirect<'ob>(&self, cx: &'ob Context) -> Option<Function<'ob>> {
331 let func = self.func(cx)?;
332 match func.untag() {
333 FunctionType::Symbol(sym) => sym.follow_indirect(cx),
334 _ => Some(func),
335 }
336 }
337
338 pub(in crate::core) unsafe fn set_func(&self, func: Function) -> Result<()> {
344 let Some(fn_cell) = self.0.func.as_ref() else {
345 bail!("Attempt to set a constant symbol: {self}")
346 };
347 let val = func.into_ptr().cast_mut();
348 fn_cell.store(val, Ordering::Release);
349 Ok(())
350 }
351
352 pub(crate) fn unbind_func(&self) {
353 if let Some(func) = &self.0.func {
354 func.store(Self::NULL, Ordering::Release);
355 }
356 }
357}
358
359impl fmt::Display for SymbolCell {
360 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
361 write!(f, "{}", self.name())
362 }
363}
364
365impl fmt::Debug for SymbolCell {
366 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
367 write!(f, "{}", self.name())
368 }
369}
370
371#[derive(Default)]
375pub(in crate::core) struct UninternedSymbolMap {
376 map: std::cell::RefCell<Vec<(Symbol<'static>, Symbol<'static>)>>,
377}
378
379impl UninternedSymbolMap {
380 fn get<'a>(&'a self, symbol: Symbol) -> Option<Symbol<'a>> {
381 self.map.borrow().iter().find(|x| x.0 == symbol).map(|x| x.1)
382 }
383
384 fn insert(&self, old: Symbol, new: Symbol) {
385 self.map
386 .borrow_mut()
387 .push(unsafe { (old.with_lifetime(), new.with_lifetime()) });
388 }
389
390 pub(in crate::core) fn clear(&self) {
391 self.map.borrow_mut().clear();
392 }
393}