1use crate::{
19 gc::{empty_trace, Finalize, Trace},
20 JsString,
21};
22use std::{
23 cell::Cell,
24 fmt::{self, Display},
25 hash::{Hash, Hasher},
26 rc::Rc,
27};
28
29#[derive(Debug, Clone)]
40pub struct WellKnownSymbols {
41 async_iterator: JsSymbol,
42 has_instance: JsSymbol,
43 is_concat_spreadable: JsSymbol,
44 iterator: JsSymbol,
45 match_: JsSymbol,
46 match_all: JsSymbol,
47 replace: JsSymbol,
48 search: JsSymbol,
49 species: JsSymbol,
50 split: JsSymbol,
51 to_primitive: JsSymbol,
52 to_string_tag: JsSymbol,
53 unscopables: JsSymbol,
54}
55
56const RESERVED_SYMBOL_HASHES: u64 = 128;
61
62thread_local! {
63 static WELL_KNOW_SYMBOLS: WellKnownSymbols = WellKnownSymbols::new();
65
66 static SYMBOL_HASH_COUNT: Cell<u64> = Cell::new(RESERVED_SYMBOL_HASHES);
70}
71
72impl WellKnownSymbols {
73 fn new() -> Self {
75 let mut count = 0;
76
77 let async_iterator = JsSymbol::with_hash(count, Some("Symbol.asyncIterator".into()));
78 count += 1;
79 let has_instance = JsSymbol::with_hash(count, Some("Symbol.hasInstance".into()));
80 count += 1;
81 let is_concat_spreadable =
82 JsSymbol::with_hash(count, Some("Symbol.isConcatSpreadable".into()));
83 count += 1;
84 let iterator = JsSymbol::with_hash(count, Some("Symbol.iterator".into()));
85 count += 1;
86 let match_ = JsSymbol::with_hash(count, Some("Symbol.match".into()));
87 count += 1;
88 let match_all = JsSymbol::with_hash(count, Some("Symbol.matchAll".into()));
89 count += 1;
90 let replace = JsSymbol::with_hash(count, Some("Symbol.replace".into()));
91 count += 1;
92 let search = JsSymbol::with_hash(count, Some("Symbol.search".into()));
93 count += 1;
94 let species = JsSymbol::with_hash(count, Some("Symbol.species".into()));
95 count += 1;
96 let split = JsSymbol::with_hash(count, Some("Symbol.split".into()));
97 count += 1;
98 let to_primitive = JsSymbol::with_hash(count, Some("Symbol.toPrimitive".into()));
99 count += 1;
100 let to_string_tag = JsSymbol::with_hash(count, Some("Symbol.toStringTag".into()));
101 count += 1;
102 let unscopables = JsSymbol::with_hash(count, Some("Symbol.unscopables".into()));
103
104 Self {
105 async_iterator,
106 has_instance,
107 is_concat_spreadable,
108 iterator,
109 match_,
110 match_all,
111 replace,
112 search,
113 species,
114 split,
115 to_primitive,
116 to_string_tag,
117 unscopables,
118 }
119 }
120
121 #[inline]
126 pub fn async_iterator() -> JsSymbol {
127 WELL_KNOW_SYMBOLS.with(|symbols| symbols.async_iterator.clone())
128 }
129
130 #[inline]
136 pub fn has_instance() -> JsSymbol {
137 WELL_KNOW_SYMBOLS.with(|symbols| symbols.has_instance.clone())
138 }
139
140 #[inline]
146 pub fn is_concat_spreadable() -> JsSymbol {
147 WELL_KNOW_SYMBOLS.with(|symbols| symbols.is_concat_spreadable.clone())
148 }
149
150 #[inline]
155 pub fn iterator() -> JsSymbol {
156 WELL_KNOW_SYMBOLS.with(|symbols| symbols.iterator.clone())
157 }
158
159 #[inline]
164 pub fn match_() -> JsSymbol {
165 WELL_KNOW_SYMBOLS.with(|symbols| symbols.match_.clone())
166 }
167
168 #[inline]
174 pub fn match_all() -> JsSymbol {
175 WELL_KNOW_SYMBOLS.with(|symbols| symbols.match_all.clone())
176 }
177
178 #[inline]
183 pub fn replace() -> JsSymbol {
184 WELL_KNOW_SYMBOLS.with(|symbols| symbols.replace.clone())
185 }
186
187 #[inline]
193 pub fn search() -> JsSymbol {
194 WELL_KNOW_SYMBOLS.with(|symbols| symbols.search.clone())
195 }
196
197 #[inline]
202 pub fn species() -> JsSymbol {
203 WELL_KNOW_SYMBOLS.with(|symbols| symbols.species.clone())
204 }
205
206 #[inline]
212 pub fn split() -> JsSymbol {
213 WELL_KNOW_SYMBOLS.with(|symbols| symbols.split.clone())
214 }
215
216 #[inline]
221 pub fn to_primitive() -> JsSymbol {
222 WELL_KNOW_SYMBOLS.with(|symbols| symbols.to_primitive.clone())
223 }
224
225 #[inline]
231 pub fn to_string_tag() -> JsSymbol {
232 WELL_KNOW_SYMBOLS.with(|symbols| symbols.to_string_tag.clone())
233 }
234
235 #[inline]
240 pub fn unscopables() -> JsSymbol {
241 WELL_KNOW_SYMBOLS.with(|symbols| symbols.unscopables.clone())
242 }
243}
244
245#[derive(Debug, Clone)]
247struct Inner {
248 hash: u64,
249 description: Option<JsString>,
250}
251
252#[derive(Debug, Clone)]
254pub struct JsSymbol {
255 inner: Rc<Inner>,
256}
257
258impl JsSymbol {
259 #[inline]
261 pub fn new(description: Option<JsString>) -> Self {
262 let hash = SYMBOL_HASH_COUNT.with(|count| {
263 let hash = count.get();
264 count.set(hash + 1);
265 hash
266 });
267
268 Self {
269 inner: Rc::new(Inner { hash, description }),
270 }
271 }
272
273 #[inline]
275 fn with_hash(hash: u64, description: Option<JsString>) -> Self {
276 Self {
277 inner: Rc::new(Inner { hash, description }),
278 }
279 }
280
281 #[inline]
283 pub fn description(&self) -> Option<JsString> {
284 self.inner.description.clone()
285 }
286
287 #[inline]
291 pub fn hash(&self) -> u64 {
292 self.inner.hash
293 }
294}
295
296impl Finalize for JsSymbol {}
297
298unsafe impl Trace for JsSymbol {
301 empty_trace!();
302}
303
304impl Display for JsSymbol {
305 #[inline]
306 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
307 match &self.inner.description {
308 Some(desc) => write!(f, "Symbol({})", desc),
309 None => write!(f, "Symbol()"),
310 }
311 }
312}
313
314impl Eq for JsSymbol {}
315
316impl PartialEq for JsSymbol {
317 #[inline]
318 fn eq(&self, other: &Self) -> bool {
319 self.inner.hash == other.inner.hash
320 }
321}
322
323impl PartialOrd for JsSymbol {
324 #[inline]
325 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
326 self.inner.hash.partial_cmp(&other.inner.hash)
327 }
328}
329
330impl Ord for JsSymbol {
331 #[inline]
332 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
333 self.inner.hash.cmp(&other.inner.hash)
334 }
335}
336
337impl Hash for JsSymbol {
338 #[inline]
339 fn hash<H: Hasher>(&self, state: &mut H) {
340 self.inner.hash.hash(state);
341 }
342}