diff --git a/boa/src/builtins/symbol/mod.rs b/boa/src/builtins/symbol/mod.rs index c6336ef8ec4..8d2bf562666 100644 --- a/boa/src/builtins/symbol/mod.rs +++ b/boa/src/builtins/symbol/mod.rs @@ -22,7 +22,7 @@ use crate::{ builtins::BuiltIn, object::{ConstructorBuilder, FunctionBuilder}, property::Attribute, - symbol::{RcSymbol, WellKnownSymbols}, + symbol::{JsSymbol, WellKnownSymbols}, value::Value, BoaProfiler, Context, Result, }; @@ -130,10 +130,10 @@ impl Symbol { _ => None, }; - Ok(context.construct_symbol(description).into()) + Ok(JsSymbol::new(description).into()) } - fn this_symbol_value(value: &Value, context: &mut Context) -> Result { + fn this_symbol_value(value: &Value, context: &mut Context) -> Result { match value { Value::Symbol(ref symbol) => return Ok(symbol.clone()), Value::Object(ref object) => { @@ -161,8 +161,7 @@ impl Symbol { #[allow(clippy::wrong_self_convention)] pub(crate) fn to_string(this: &Value, _: &[Value], context: &mut Context) -> Result { let symbol = Self::this_symbol_value(this, context)?; - let description = symbol.description().unwrap_or(""); - Ok(Value::from(format!("Symbol({})", description))) + Ok(symbol.to_string().into()) } /// `get Symbol.prototype.description` @@ -180,7 +179,8 @@ impl Symbol { _: &[Value], context: &mut Context, ) -> Result { - if let Some(ref description) = Self::this_symbol_value(this, context)?.description { + let symbol = Self::this_symbol_value(this, context)?; + if let Some(ref description) = symbol.description() { Ok(description.clone().into()) } else { Ok(Value::undefined()) diff --git a/boa/src/context.rs b/boa/src/context.rs index 0257fcdd7f9..568367a5519 100644 --- a/boa/src/context.rs +++ b/boa/src/context.rs @@ -11,7 +11,6 @@ use crate::{ object::{FunctionBuilder, GcObject, Object, PROTOTYPE}, property::{Attribute, DataDescriptor, PropertyKey}, realm::Realm, - symbol::{RcSymbol, Symbol}, syntax::{ ast::{ node::{ @@ -22,8 +21,7 @@ use crate::{ }, Parser, }, - value::{RcString, Value}, - BoaProfiler, Executable, Result, + BoaProfiler, Executable, Result, Value, }; #[cfg(feature = "console")] @@ -297,12 +295,6 @@ impl Context { builtins::init(self); } - /// Construct a new `Symbol` with an optional description. - #[inline] - pub fn construct_symbol(&mut self, description: Option) -> RcSymbol { - RcSymbol::from(Symbol::new(description)) - } - /// Construct an empty object. #[inline] pub fn construct_object(&self) -> GcObject { diff --git a/boa/src/lib.rs b/boa/src/lib.rs index 5957b9e7107..f7b0d8c3499 100644 --- a/boa/src/lib.rs +++ b/boa/src/lib.rs @@ -71,7 +71,7 @@ pub(crate) use crate::{exec::Executable, profiler::BoaProfiler}; // Export things to root level #[doc(inline)] -pub use crate::{context::Context, value::Value}; +pub use crate::{context::Context, symbol::JsSymbol, value::Value}; use crate::syntax::{ ast::node::StatementList, diff --git a/boa/src/object/iter.rs b/boa/src/object/iter.rs index f765b227c48..c0b6d4813f1 100644 --- a/boa/src/object/iter.rs +++ b/boa/src/object/iter.rs @@ -1,5 +1,5 @@ use super::{Object, PropertyDescriptor, PropertyKey}; -use crate::{symbol::RcSymbol, value::RcString}; +use crate::{value::RcString, JsSymbol}; use std::{collections::hash_map, iter::FusedIterator}; impl Object { @@ -110,7 +110,7 @@ impl Object { pub struct Iter<'a> { indexed_properties: hash_map::Iter<'a, u32, PropertyDescriptor>, string_properties: hash_map::Iter<'a, RcString, PropertyDescriptor>, - symbol_properties: hash_map::Iter<'a, RcSymbol, PropertyDescriptor>, + symbol_properties: hash_map::Iter<'a, JsSymbol, PropertyDescriptor>, } impl<'a> Iterator for Iter<'a> { @@ -180,10 +180,10 @@ impl FusedIterator for Values<'_> {} /// An iterator over the `Symbol` property entries of an `Object` #[derive(Debug, Clone)] -pub struct SymbolProperties<'a>(hash_map::Iter<'a, RcSymbol, PropertyDescriptor>); +pub struct SymbolProperties<'a>(hash_map::Iter<'a, JsSymbol, PropertyDescriptor>); impl<'a> Iterator for SymbolProperties<'a> { - type Item = (&'a RcSymbol, &'a PropertyDescriptor); + type Item = (&'a JsSymbol, &'a PropertyDescriptor); #[inline] fn next(&mut self) -> Option { @@ -207,10 +207,10 @@ impl FusedIterator for SymbolProperties<'_> {} /// An iterator over the keys (`RcSymbol`) of an `Object`. #[derive(Debug, Clone)] -pub struct SymbolPropertyKeys<'a>(hash_map::Keys<'a, RcSymbol, PropertyDescriptor>); +pub struct SymbolPropertyKeys<'a>(hash_map::Keys<'a, JsSymbol, PropertyDescriptor>); impl<'a> Iterator for SymbolPropertyKeys<'a> { - type Item = &'a RcSymbol; + type Item = &'a JsSymbol; #[inline] fn next(&mut self) -> Option { @@ -234,7 +234,7 @@ impl FusedIterator for SymbolPropertyKeys<'_> {} /// An iterator over the `Symbol` values (`Property`) of an `Object`. #[derive(Debug, Clone)] -pub struct SymbolPropertyValues<'a>(hash_map::Values<'a, RcSymbol, PropertyDescriptor>); +pub struct SymbolPropertyValues<'a>(hash_map::Values<'a, JsSymbol, PropertyDescriptor>); impl<'a> Iterator for SymbolPropertyValues<'a> { type Item = &'a PropertyDescriptor; diff --git a/boa/src/object/mod.rs b/boa/src/object/mod.rs index 6da61421201..3da5b7ece20 100644 --- a/boa/src/object/mod.rs +++ b/boa/src/object/mod.rs @@ -15,9 +15,8 @@ use crate::{ context::StandardConstructor, gc::{Finalize, Trace}, property::{AccessorDescriptor, Attribute, DataDescriptor, PropertyDescriptor, PropertyKey}, - symbol::RcSymbol, value::{RcBigInt, RcString, Value}, - BoaProfiler, Context, + BoaProfiler, Context, JsSymbol, }; use rustc_hash::FxHashMap; use std::{ @@ -72,7 +71,7 @@ pub struct Object { /// Properties string_properties: FxHashMap, /// Symbol Properties - symbol_properties: FxHashMap, + symbol_properties: FxHashMap, /// Instance prototype `__proto__`. prototype: Value, /// Whether it can have new properties added to it. @@ -97,7 +96,7 @@ pub enum ObjectData { String(RcString), StringIterator(StringIterator), Number(f64), - Symbol(RcSymbol), + Symbol(JsSymbol), Error, Ordinary, Date(Date), @@ -442,7 +441,7 @@ impl Object { } #[inline] - pub fn as_symbol(&self) -> Option { + pub fn as_symbol(&self) -> Option { match self.data { ObjectData::Symbol(ref symbol) => Some(symbol.clone()), _ => None, diff --git a/boa/src/property/mod.rs b/boa/src/property/mod.rs index 5520f07f32c..bd3a8891915 100644 --- a/boa/src/property/mod.rs +++ b/boa/src/property/mod.rs @@ -17,8 +17,8 @@ use crate::{ gc::{Finalize, Trace}, object::GcObject, - symbol::RcSymbol, value::{RcString, Value}, + JsSymbol, }; use std::{convert::TryFrom, fmt}; @@ -307,7 +307,7 @@ impl PropertyDescriptor { #[derive(Trace, Finalize, Debug, Clone)] pub enum PropertyKey { String(RcString), - Symbol(RcSymbol), + Symbol(JsSymbol), Index(u32), } @@ -355,9 +355,9 @@ impl From> for PropertyKey { } } -impl From for PropertyKey { +impl From for PropertyKey { #[inline] - fn from(symbol: RcSymbol) -> PropertyKey { + fn from(symbol: JsSymbol) -> PropertyKey { PropertyKey::Symbol(symbol) } } diff --git a/boa/src/symbol/mod.rs b/boa/src/symbol.rs similarity index 66% rename from boa/src/symbol/mod.rs rename to boa/src/symbol.rs index a97e586d837..353dee4dd98 100644 --- a/boa/src/symbol/mod.rs +++ b/boa/src/symbol.rs @@ -15,19 +15,17 @@ //! [spec]: https://tc39.es/ecma262/#sec-symbol-value //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol -mod rcsymbol; - use crate::{ - gc::{Finalize, Trace}, + gc::{empty_trace, Finalize, Trace}, value::RcString, }; use std::{ cell::Cell, + fmt::{self, Display}, hash::{Hash, Hasher}, + rc::Rc, }; -pub use rcsymbol::RcSymbol; - /// A structure that contains the JavaScript well known symbols. /// /// # Examples @@ -35,24 +33,24 @@ pub use rcsymbol::RcSymbol; ///# use boa::symbol::WellKnownSymbols; /// /// let iterator = WellKnownSymbols::iterator(); -/// assert_eq!(iterator.description(), Some("Symbol.iterator")); +/// assert_eq!(iterator.description().as_deref(), Some("Symbol.iterator")); /// ``` /// This is equivalent to `let iterator = Symbol.iterator` in JavaScript. #[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, + async_iterator: JsSymbol, + has_instance: JsSymbol, + is_concat_spreadable: JsSymbol, + iterator: JsSymbol, + match_: JsSymbol, + match_all: JsSymbol, + replace: JsSymbol, + search: JsSymbol, + species: JsSymbol, + split: JsSymbol, + to_primitive: JsSymbol, + to_string_tag: JsSymbol, + unscopables: JsSymbol, } /// Reserved number of symbols. @@ -76,32 +74,32 @@ impl WellKnownSymbols { fn new() -> Self { let mut count = 0; - let async_iterator = Symbol::with_hash(count, Some("Symbol.asyncIterator".into())).into(); + let async_iterator = JsSymbol::with_hash(count, Some("Symbol.asyncIterator".into())); count += 1; - let has_instance = Symbol::with_hash(count, Some("Symbol.hasInstance".into())).into(); + let has_instance = JsSymbol::with_hash(count, Some("Symbol.hasInstance".into())); count += 1; let is_concat_spreadable = - Symbol::with_hash(count, Some("Symbol.isConcatSpreadable".into())).into(); + JsSymbol::with_hash(count, Some("Symbol.isConcatSpreadable".into())); count += 1; - let iterator = Symbol::with_hash(count, Some("Symbol.iterator".into())).into(); + let iterator = JsSymbol::with_hash(count, Some("Symbol.iterator".into())); count += 1; - let match_ = Symbol::with_hash(count, Some("Symbol.match".into())).into(); + let match_ = JsSymbol::with_hash(count, Some("Symbol.match".into())); count += 1; - let match_all = Symbol::with_hash(count, Some("Symbol.matchAll".into())).into(); + let match_all = JsSymbol::with_hash(count, Some("Symbol.matchAll".into())); count += 1; - let replace = Symbol::with_hash(count, Some("Symbol.replace".into())).into(); + let replace = JsSymbol::with_hash(count, Some("Symbol.replace".into())); count += 1; - let search = Symbol::with_hash(count, Some("Symbol.search".into())).into(); + let search = JsSymbol::with_hash(count, Some("Symbol.search".into())); count += 1; - let species = Symbol::with_hash(count, Some("Symbol.species".into())).into(); + let species = JsSymbol::with_hash(count, Some("Symbol.species".into())); count += 1; - let split = Symbol::with_hash(count, Some("Symbol.split".into())).into(); + let split = JsSymbol::with_hash(count, Some("Symbol.split".into())); count += 1; - let to_primitive = Symbol::with_hash(count, Some("Symbol.toPrimitive".into())).into(); + let to_primitive = JsSymbol::with_hash(count, Some("Symbol.toPrimitive".into())); count += 1; - let to_string_tag = Symbol::with_hash(count, Some("Symbol.toStringTag".into())).into(); + let to_string_tag = JsSymbol::with_hash(count, Some("Symbol.toStringTag".into())); count += 1; - let unscopables = Symbol::with_hash(count, Some("Symbol.unscopables".into())).into(); + let unscopables = JsSymbol::with_hash(count, Some("Symbol.unscopables".into())); Self { async_iterator, @@ -125,7 +123,7 @@ impl WellKnownSymbols { /// A method that returns the default AsyncIterator for an object. /// Called by the semantics of the `for-await-of` statement. #[inline] - pub fn async_iterator() -> RcSymbol { + pub fn async_iterator() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.async_iterator.clone()) } @@ -135,7 +133,7 @@ impl WellKnownSymbols { /// recognizes an object as one of the `constructor`'s instances. /// Called by the semantics of the instanceof operator. #[inline] - pub fn has_instance() -> RcSymbol { + pub fn has_instance() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.has_instance.clone()) } @@ -145,7 +143,7 @@ impl WellKnownSymbols { /// an object should be flattened to its array elements /// by `Array.prototype.concat`. #[inline] - pub fn is_concat_spreadable() -> RcSymbol { + pub fn is_concat_spreadable() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.is_concat_spreadable.clone()) } @@ -154,7 +152,7 @@ impl WellKnownSymbols { /// A method that returns the default Iterator for an object. /// Called by the semantics of the `for-of` statement. #[inline] - pub fn iterator() -> RcSymbol { + pub fn iterator() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.iterator.clone()) } @@ -163,7 +161,7 @@ impl WellKnownSymbols { /// A regular expression method that matches the regular expression /// against a string. Called by the `String.prototype.match` method. #[inline] - pub fn match_() -> RcSymbol { + pub fn match_() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.match_.clone()) } @@ -173,7 +171,7 @@ impl WellKnownSymbols { /// matches of the regular expression against a string. /// Called by the `String.prototype.matchAll` method. #[inline] - pub fn match_all() -> RcSymbol { + pub fn match_all() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.match_all.clone()) } @@ -182,7 +180,7 @@ impl WellKnownSymbols { /// A regular expression method that replaces matched substrings /// of a string. Called by the `String.prototype.replace` method. #[inline] - pub fn replace() -> RcSymbol { + pub fn replace() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.replace.clone()) } @@ -192,7 +190,7 @@ impl WellKnownSymbols { /// string that matches the regular expression. /// Called by the `String.prototype.search` method. #[inline] - pub fn search() -> RcSymbol { + pub fn search() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.search.clone()) } @@ -201,7 +199,7 @@ impl WellKnownSymbols { /// A function valued property that is the `constructor` function /// that is used to create derived objects. #[inline] - pub fn species() -> RcSymbol { + pub fn species() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.species.clone()) } @@ -211,7 +209,7 @@ impl WellKnownSymbols { /// that match the regular expression. /// Called by the `String.prototype.split` method. #[inline] - pub fn split() -> RcSymbol { + pub fn split() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.split.clone()) } @@ -220,7 +218,7 @@ impl WellKnownSymbols { /// A method that converts an object to a corresponding primitive value. /// Called by the `ToPrimitive` (`Value::to_primitve`) abstract operation. #[inline] - pub fn to_primitive() -> RcSymbol { + pub fn to_primitive() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.to_primitive.clone()) } @@ -230,7 +228,7 @@ impl WellKnownSymbols { /// string description of an object. /// Accessed by the built-in method `Object.prototype.toString`. #[inline] - pub fn to_string_tag() -> RcSymbol { + pub fn to_string_tag() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.to_string_tag.clone()) } @@ -239,23 +237,25 @@ impl WellKnownSymbols { /// An object valued property whose own and inherited property names are property /// names that are excluded from the `with` environment bindings of the associated object. #[inline] - pub fn unscopables() -> RcSymbol { + pub fn unscopables() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.unscopables.clone()) } } -#[derive(Debug, Finalize, Trace, Clone, Eq, PartialOrd, Ord)] -pub struct Symbol { - pub(crate) hash: u64, - pub(crate) description: Option, +/// The inner representation of a JavaScript symbol. +#[derive(Debug, Clone)] +struct Inner { + hash: u64, + description: Option, } -impl Symbol { - /// Create a new symbol with a specified hash and description. - fn with_hash(hash: u64, description: Option) -> Self { - Self { hash, description } - } +/// This represents a JavaScript symbol primitive. +#[derive(Debug, Clone)] +pub struct JsSymbol { + inner: Rc, +} +impl JsSymbol { /// Create a new symbol. #[inline] pub fn new(description: Option) -> Self { @@ -264,13 +264,24 @@ impl Symbol { count.set(hash + 1); hash }); - Self { hash, description } + + Self { + inner: Rc::new(Inner { hash, description }), + } + } + + /// Create a new symbol with a specified hash and description. + #[inline] + fn with_hash(hash: u64, description: Option) -> Self { + Self { + inner: Rc::new(Inner { hash, description }), + } } /// Returns the `Symbol`s description. #[inline] - pub fn description(&self) -> Option<&str> { - self.description.as_deref() + pub fn description(&self) -> Option { + self.inner.description.clone() } /// Returns the `Symbol`s hash. @@ -278,20 +289,54 @@ impl Symbol { /// The hash is guaranteed to be unique. #[inline] pub fn hash(&self) -> u64 { - self.hash + self.inner.hash } } -impl PartialEq for Symbol { +impl Finalize for JsSymbol {} + +// Safety: `JsSymbol` does not contain any object that require trace, +// so this is safe. +unsafe impl Trace for JsSymbol { + empty_trace!(); +} + +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.hash == other.hash + self.inner.hash == other.inner.hash + } +} + +impl PartialOrd for JsSymbol { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + 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 Symbol { +impl Hash for JsSymbol { #[inline] fn hash(&self, state: &mut H) { - self.hash.hash(state); + self.inner.hash.hash(state); } } diff --git a/boa/src/symbol/rcsymbol.rs b/boa/src/symbol/rcsymbol.rs deleted file mode 100644 index c7b34696e67..00000000000 --- a/boa/src/symbol/rcsymbol.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::{ - gc::{empty_trace, Finalize, Trace}, - symbol::Symbol, -}; - -use std::{ - fmt::{self, Display}, - ops::Deref, - rc::Rc, -}; - -#[derive(Debug, Finalize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct RcSymbol(Rc); - -unsafe impl Trace for RcSymbol { - empty_trace!(); -} - -impl Display for RcSymbol { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0.description() { - Some(desc) => write!(f, "Symbol({})", desc), - None => write!(f, "Symbol()"), - } - } -} - -impl Deref for RcSymbol { - type Target = Symbol; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl From for RcSymbol { - #[inline] - fn from(symbol: Symbol) -> Self { - Self(Rc::from(symbol)) - } -} diff --git a/boa/src/value/conversions.rs b/boa/src/value/conversions.rs index 1a1ae39d442..b920242ff28 100644 --- a/boa/src/value/conversions.rs +++ b/boa/src/value/conversions.rs @@ -51,9 +51,9 @@ impl From for Value { } } -impl From for Value { +impl From for Value { #[inline] - fn from(value: RcSymbol) -> Self { + fn from(value: JsSymbol) -> Self { Value::Symbol(value) } } diff --git a/boa/src/value/mod.rs b/boa/src/value/mod.rs index d1e519e69d5..9708e0f1a18 100644 --- a/boa/src/value/mod.rs +++ b/boa/src/value/mod.rs @@ -13,7 +13,7 @@ use crate::{ }, object::{GcObject, Object, ObjectData}, property::{Attribute, DataDescriptor, PropertyDescriptor, PropertyKey}, - symbol::{RcSymbol, WellKnownSymbols}, + symbol::{JsSymbol, WellKnownSymbols}, BoaProfiler, Context, Result, }; use gc::{Finalize, Trace}; @@ -63,7 +63,7 @@ pub enum Value { /// `Object` - An object, such as `Math`, represented by a binary tree of string keys to Javascript values. Object(GcObject), /// `Symbol` - A Symbol Primitive type. - Symbol(RcSymbol), + Symbol(JsSymbol), } /// Represents the result of ToIntegerOrInfinity operation @@ -164,7 +164,7 @@ impl Value { /// Creates a new symbol value. #[inline] - pub fn symbol(symbol: RcSymbol) -> Self { + pub fn symbol(symbol: JsSymbol) -> Self { Self::Symbol(symbol) } @@ -293,7 +293,7 @@ impl Value { matches!(self, Self::Symbol(_)) } - pub fn as_symbol(&self) -> Option { + pub fn as_symbol(&self) -> Option { match self { Self::Symbol(symbol) => Some(symbol.clone()), _ => None,