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