#[cfg(test)]
mod tests;
use crate::{
builtins::BuiltIn,
gc::{Finalize, Trace},
object::ConstructorBuilder,
property::Attribute,
value::{RcString, RcSymbol, Value},
BoaProfiler, Context, Result,
};
#[derive(Debug, Clone)]
pub struct WellKnownSymbols {
async_iterator: RcSymbol,
has_instance: RcSymbol,
is_concat_spreadable: RcSymbol,
iterator: RcSymbol,
match_: RcSymbol,
match_all: RcSymbol,
replace: RcSymbol,
search: RcSymbol,
species: RcSymbol,
split: RcSymbol,
to_primitive: RcSymbol,
to_string_tag: RcSymbol,
unscopables: RcSymbol,
}
impl WellKnownSymbols {
pub(crate) fn new() -> (Self, u64) {
let mut count = 0;
let async_iterator = Symbol::new(count, Some("Symbol.asyncIterator".into())).into();
count += 1;
let has_instance = Symbol::new(count, Some("Symbol.hasInstance".into())).into();
count += 1;
let is_concat_spreadable =
Symbol::new(count, Some("Symbol.isConcatSpreadable".into())).into();
count += 1;
let iterator = Symbol::new(count, Some("Symbol.iterator".into())).into();
count += 1;
let match_ = Symbol::new(count, Some("Symbol.match".into())).into();
count += 1;
let match_all = Symbol::new(count, Some("Symbol.matchAll".into())).into();
count += 1;
let replace = Symbol::new(count, Some("Symbol.replace".into())).into();
count += 1;
let search = Symbol::new(count, Some("Symbol.search".into())).into();
count += 1;
let species = Symbol::new(count, Some("Symbol.species".into())).into();
count += 1;
let split = Symbol::new(count, Some("Symbol.split".into())).into();
count += 1;
let to_primitive = Symbol::new(count, Some("Symbol.toPrimitive".into())).into();
count += 1;
let to_string_tag = Symbol::new(count, Some("Symbol.toStringTag".into())).into();
count += 1;
let unscopables = Symbol::new(count, Some("Symbol.unscopables".into())).into();
count += 1;
(
Self {
async_iterator,
has_instance,
is_concat_spreadable,
iterator,
match_,
match_all,
replace,
search,
species,
split,
to_primitive,
to_string_tag,
unscopables,
},
count,
)
}
#[inline]
pub fn async_iterator_symbol(&self) -> RcSymbol {
self.async_iterator.clone()
}
#[inline]
pub fn has_instance_symbol(&self) -> RcSymbol {
self.has_instance.clone()
}
#[inline]
pub fn is_concat_spreadable_symbol(&self) -> RcSymbol {
self.is_concat_spreadable.clone()
}
#[inline]
pub fn iterator_symbol(&self) -> RcSymbol {
self.iterator.clone()
}
#[inline]
pub fn match_symbol(&self) -> RcSymbol {
self.match_.clone()
}
#[inline]
pub fn match_all_symbol(&self) -> RcSymbol {
self.match_all.clone()
}
#[inline]
pub fn replace_symbol(&self) -> RcSymbol {
self.replace.clone()
}
#[inline]
pub fn search_symbol(&self) -> RcSymbol {
self.search.clone()
}
#[inline]
pub fn species_symbol(&self) -> RcSymbol {
self.species.clone()
}
#[inline]
pub fn split_symbol(&self) -> RcSymbol {
self.split.clone()
}
#[inline]
pub fn to_primitive_symbol(&self) -> RcSymbol {
self.to_primitive.clone()
}
#[inline]
pub fn to_string_tag_symbol(&self) -> RcSymbol {
self.to_string_tag.clone()
}
#[inline]
pub fn unscopables_symbol(&self) -> RcSymbol {
self.unscopables.clone()
}
}
#[derive(Debug, Finalize, Trace, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Symbol {
hash: u64,
description: Option<RcString>,
}
impl Symbol {
pub(crate) fn new(hash: u64, description: Option<RcString>) -> Self {
Self { hash, description }
}
}
impl BuiltIn for Symbol {
const NAME: &'static str = "Symbol";
fn attribute() -> Attribute {
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
}
fn init(context: &mut Context) -> (&'static str, Value, Attribute) {
let well_known_symbols = context.well_known_symbols();
let symbol_async_iterator = well_known_symbols.async_iterator_symbol();
let symbol_has_instance = well_known_symbols.has_instance_symbol();
let symbol_is_concat_spreadable = well_known_symbols.is_concat_spreadable_symbol();
let symbol_iterator = well_known_symbols.iterator_symbol();
let symbol_match = well_known_symbols.match_symbol();
let symbol_match_all = well_known_symbols.match_all_symbol();
let symbol_replace = well_known_symbols.replace_symbol();
let symbol_search = well_known_symbols.search_symbol();
let symbol_species = well_known_symbols.species_symbol();
let symbol_split = well_known_symbols.split_symbol();
let symbol_to_primitive = well_known_symbols.to_primitive_symbol();
let symbol_to_string_tag = well_known_symbols.to_string_tag_symbol();
let symbol_unscopables = well_known_symbols.unscopables_symbol();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
let symbol_object = ConstructorBuilder::with_standard_object(
context,
Self::constructor,
context.standard_objects().symbol_object().clone(),
)
.name(Self::NAME)
.length(Self::LENGTH)
.static_property("asyncIterator", symbol_async_iterator, attribute)
.static_property("hasInstance", symbol_has_instance, attribute)
.static_property("isConcatSpreadable", symbol_is_concat_spreadable, attribute)
.static_property("iterator", symbol_iterator, attribute)
.static_property("match", symbol_match, attribute)
.static_property("matchAll", symbol_match_all, attribute)
.static_property("replace", symbol_replace, attribute)
.static_property("search", symbol_search, attribute)
.static_property("species", symbol_species, attribute)
.static_property("split", symbol_split, attribute)
.static_property("toPrimitive", symbol_to_primitive, attribute)
.static_property("toStringTag", symbol_to_string_tag, attribute)
.static_property("unscopables", symbol_unscopables, attribute)
.method(Self::to_string, "toString", 0)
.callable(true)
.constructable(false)
.build();
(Self::NAME, symbol_object.into(), Self::attribute())
}
}
impl Symbol {
pub(crate) const LENGTH: usize = 0;
pub fn description(&self) -> Option<&str> {
self.description.as_deref()
}
pub fn hash(&self) -> u64 {
self.hash
}
pub(crate) fn constructor(
new_target: &Value,
args: &[Value],
context: &mut Context,
) -> Result<Value> {
if new_target.is_undefined() {
return context.throw_type_error("Symbol is not a constructor");
}
let description = match args.get(0) {
Some(ref value) if !value.is_undefined() => Some(value.to_string(context)?),
_ => None,
};
Ok(context.construct_symbol(description).into())
}
fn this_symbol_value(value: &Value, context: &mut Context) -> Result<RcSymbol> {
match value {
Value::Symbol(ref symbol) => return Ok(symbol.clone()),
Value::Object(ref object) => {
let object = object.borrow();
if let Some(symbol) = object.as_symbol() {
return Ok(symbol);
}
}
_ => {}
}
Err(context.construct_type_error("'this' is not a Symbol"))
}
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
let symbol = Self::this_symbol_value(this, context)?;
let description = symbol.description().unwrap_or("");
Ok(Value::from(format!("Symbol({})", description)))
}
}