use crate::JsString;
use boa_gc::{unsafe_empty_trace, Finalize, Trace};
use std::{
cell::Cell,
fmt::{self, Display},
hash::{Hash, Hasher},
rc::Rc,
};
#[derive(Debug, Clone)]
pub struct WellKnownSymbols {
async_iterator: JsSymbol,
has_instance: JsSymbol,
is_concat_spreadable: JsSymbol,
iterator: JsSymbol,
r#match: JsSymbol,
match_all: JsSymbol,
replace: JsSymbol,
search: JsSymbol,
species: JsSymbol,
split: JsSymbol,
to_primitive: JsSymbol,
to_string_tag: JsSymbol,
unscopables: JsSymbol,
}
const RESERVED_SYMBOL_HASHES: u64 = 128;
thread_local! {
static WELL_KNOW_SYMBOLS: WellKnownSymbols = WellKnownSymbols::new();
static SYMBOL_HASH_COUNT: Cell<u64> = Cell::new(RESERVED_SYMBOL_HASHES);
}
impl WellKnownSymbols {
fn new() -> Self {
let mut count = 0;
let async_iterator = JsSymbol::with_hash(count, Some("Symbol.asyncIterator".into()));
count += 1;
let has_instance = JsSymbol::with_hash(count, Some("Symbol.hasInstance".into()));
count += 1;
let is_concat_spreadable =
JsSymbol::with_hash(count, Some("Symbol.isConcatSpreadable".into()));
count += 1;
let iterator = JsSymbol::with_hash(count, Some("Symbol.iterator".into()));
count += 1;
let match_ = JsSymbol::with_hash(count, Some("Symbol.match".into()));
count += 1;
let match_all = JsSymbol::with_hash(count, Some("Symbol.matchAll".into()));
count += 1;
let replace = JsSymbol::with_hash(count, Some("Symbol.replace".into()));
count += 1;
let search = JsSymbol::with_hash(count, Some("Symbol.search".into()));
count += 1;
let species = JsSymbol::with_hash(count, Some("Symbol.species".into()));
count += 1;
let split = JsSymbol::with_hash(count, Some("Symbol.split".into()));
count += 1;
let to_primitive = JsSymbol::with_hash(count, Some("Symbol.toPrimitive".into()));
count += 1;
let to_string_tag = JsSymbol::with_hash(count, Some("Symbol.toStringTag".into()));
count += 1;
let unscopables = JsSymbol::with_hash(count, Some("Symbol.unscopables".into()));
Self {
async_iterator,
has_instance,
is_concat_spreadable,
iterator,
r#match: match_,
match_all,
replace,
search,
species,
split,
to_primitive,
to_string_tag,
unscopables,
}
}
#[inline]
pub fn async_iterator() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.async_iterator.clone())
}
#[inline]
pub fn has_instance() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.has_instance.clone())
}
#[inline]
pub fn is_concat_spreadable() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.is_concat_spreadable.clone())
}
#[inline]
pub fn iterator() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.iterator.clone())
}
#[inline]
pub fn r#match() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.r#match.clone())
}
#[inline]
pub fn match_all() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.match_all.clone())
}
#[inline]
pub fn replace() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.replace.clone())
}
#[inline]
pub fn search() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.search.clone())
}
#[inline]
pub fn species() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.species.clone())
}
#[inline]
pub fn split() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.split.clone())
}
#[inline]
pub fn to_primitive() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.to_primitive.clone())
}
#[inline]
pub fn to_string_tag() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.to_string_tag.clone())
}
#[inline]
pub fn unscopables() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.unscopables.clone())
}
}
#[derive(Debug, Clone)]
struct Inner {
hash: u64,
description: Option<JsString>,
}
#[derive(Debug, Clone, Finalize)]
pub struct JsSymbol {
inner: Rc<Inner>,
}
unsafe impl Trace for JsSymbol {
unsafe_empty_trace!();
}
impl JsSymbol {
#[inline]
pub fn new(description: Option<JsString>) -> Self {
let hash = SYMBOL_HASH_COUNT.with(|count| {
let hash = count.get();
count.set(hash + 1);
hash
});
Self {
inner: Rc::new(Inner { hash, description }),
}
}
#[inline]
fn with_hash(hash: u64, description: Option<JsString>) -> Self {
Self {
inner: Rc::new(Inner { hash, description }),
}
}
#[inline]
pub fn description(&self) -> Option<JsString> {
self.inner.description.clone()
}
#[inline]
pub fn hash(&self) -> u64 {
self.inner.hash
}
pub fn descriptive_string(&self) -> JsString {
self.to_string().into()
}
}
impl Display for JsSymbol {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.inner.description {
Some(desc) => write!(f, "Symbol({desc})"),
None => write!(f, "Symbol()"),
}
}
}
impl Eq for JsSymbol {}
impl PartialEq for JsSymbol {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.inner.hash == other.inner.hash
}
}
impl PartialOrd for JsSymbol {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.inner.hash.partial_cmp(&other.inner.hash)
}
}
impl Ord for JsSymbol {
#[inline]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.inner.hash.cmp(&other.inner.hash)
}
}
impl Hash for JsSymbol {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.inner.hash.hash(state);
}
}