#[cfg(test)]
mod tests;
use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{
property::{Attribute, Property},
value::{RcString, RcSymbol, Value},
BoaProfiler, Context, Result,
};
use gc::{Finalize, Trace};
#[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, u32) {
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.async_iterator.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: u32,
description: Option<RcString>,
}
impl Symbol {
pub(crate) fn new(hash: u32, description: Option<RcString>) -> Self {
Self { hash, description }
}
}
impl Symbol {
pub(crate) const NAME: &'static str = "Symbol";
pub(crate) const LENGTH: usize = 0;
pub fn description(&self) -> Option<&str> {
self.description.as_deref()
}
pub fn hash(&self) -> u32 {
self.hash
}
fn this_symbol_value(value: &Value, ctx: &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(ctx.construct_type_error("'this' is not a Symbol"))
}
pub(crate) fn call(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let description = match args.get(0) {
Some(ref value) if !value.is_undefined() => Some(value.to_string(ctx)?),
_ => None,
};
Ok(ctx.construct_symbol(description).into())
}
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
let symbol = Self::this_symbol_value(this, ctx)?;
let description = symbol.description().unwrap_or("");
Ok(Value::from(format!("Symbol({})", description)))
}
#[inline]
pub fn init(context: &mut Context) -> (&'static str, Value) {
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 global = context.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let prototype = Value::new_object(Some(global));
make_builtin_fn(Self::to_string, "toString", &prototype, 0, context);
let symbol_object = make_constructor_fn(
Self::NAME,
Self::LENGTH,
Self::call,
global,
prototype,
false,
true,
);
{
let mut symbol_object = symbol_object.as_object_mut().unwrap();
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
symbol_object.insert_property(
"asyncIterator",
Property::data_descriptor(symbol_async_iterator.into(), attribute),
);
symbol_object.insert_property(
"hasInstance",
Property::data_descriptor(symbol_has_instance.into(), attribute),
);
symbol_object.insert_property(
"isConcatSpreadable",
Property::data_descriptor(symbol_is_concat_spreadable.into(), attribute),
);
symbol_object.insert_property(
"iterator",
Property::data_descriptor(symbol_iterator.into(), attribute),
);
symbol_object.insert_property(
"match",
Property::data_descriptor(symbol_match.into(), attribute),
);
symbol_object.insert_property(
"matchAll",
Property::data_descriptor(symbol_match_all.into(), attribute),
);
symbol_object.insert_property(
"replace",
Property::data_descriptor(symbol_replace.into(), attribute),
);
symbol_object.insert_property(
"search",
Property::data_descriptor(symbol_search.into(), attribute),
);
symbol_object.insert_property(
"species",
Property::data_descriptor(symbol_species.into(), attribute),
);
symbol_object.insert_property(
"split",
Property::data_descriptor(symbol_split.into(), attribute),
);
symbol_object.insert_property(
"toPrimitive",
Property::data_descriptor(symbol_to_primitive.into(), attribute),
);
symbol_object.insert_property(
"toStringTag",
Property::data_descriptor(symbol_to_string_tag.into(), attribute),
);
symbol_object.insert_property(
"unscopables",
Property::data_descriptor(symbol_unscopables.into(), attribute),
);
}
(Self::NAME, symbol_object)
}
}