From 38eaaf44fb36b07d51c62a336c8e31dd3b4d519e Mon Sep 17 00:00:00 2001 From: Halid Odat Date: Tue, 22 Sep 2020 21:11:31 +0200 Subject: [PATCH] Cache well known symbols (#706) --- boa/src/builtins/symbol/mod.rs | 232 ++++++++++++++++++++++++++++++--- boa/src/context.rs | 26 +++- 2 files changed, 237 insertions(+), 21 deletions(-) diff --git a/boa/src/builtins/symbol/mod.rs b/boa/src/builtins/symbol/mod.rs index 576a0a7c0f..3eae3a8d61 100644 --- a/boa/src/builtins/symbol/mod.rs +++ b/boa/src/builtins/symbol/mod.rs @@ -26,6 +26,200 @@ use crate::{ }; use gc::{Finalize, Trace}; +/// A structure that contains the JavaScript well known symbols. +#[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, + ) + } + + /// The `Symbol.asyncIterator` well known symbol. + /// + /// 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_symbol(&self) -> RcSymbol { + self.async_iterator.clone() + } + + /// The `Symbol.hasInstance` well known symbol. + /// + /// A method that determines if a `constructor` object + /// recognizes an object as one of the `constructor`'s instances. + /// Called by the semantics of the instanceof operator. + #[inline] + pub fn has_instance_symbol(&self) -> RcSymbol { + self.async_iterator.clone() + } + + /// The `Symbol.isConcatSpreadable` well known symbol. + /// + /// A Boolean valued property that if `true` indicates that + /// an object should be flattened to its array elements + /// by `Array.prototype.concat`. + #[inline] + pub fn is_concat_spreadable_symbol(&self) -> RcSymbol { + self.is_concat_spreadable.clone() + } + + /// The `Symbol.iterator` well known symbol. + /// + /// A method that returns the default Iterator for an object. + /// Called by the semantics of the `for-of` statement. + #[inline] + pub fn iterator_symbol(&self) -> RcSymbol { + self.iterator.clone() + } + + /// The `Symbol.match` well known symbol. + /// + /// A regular expression method that matches the regular expression + /// against a string. Called by the `String.prototype.match` method. + #[inline] + pub fn match_symbol(&self) -> RcSymbol { + self.match_.clone() + } + + /// The `Symbol.matchAll` well known symbol. + /// + /// A regular expression method that returns an iterator, that yields + /// matches of the regular expression against a string. + /// Called by the `String.prototype.matchAll` method. + #[inline] + pub fn match_all_symbol(&self) -> RcSymbol { + self.match_all.clone() + } + + /// The `Symbol.replace` well known symbol. + /// + /// A regular expression method that replaces matched substrings + /// of a string. Called by the `String.prototype.replace` method. + #[inline] + pub fn replace_symbol(&self) -> RcSymbol { + self.replace.clone() + } + + /// The `Symbol.search` well known symbol. + /// + /// A regular expression method that returns the index within a + /// string that matches the regular expression. + /// Called by the `String.prototype.search` method. + #[inline] + pub fn search_symbol(&self) -> RcSymbol { + self.search.clone() + } + + /// The `Symbol.species` well known symbol. + /// + /// A function valued property that is the `constructor` function + /// that is used to create derived objects. + #[inline] + pub fn species_symbol(&self) -> RcSymbol { + self.species.clone() + } + + /// The `Symbol.split` well known symbol. + /// + /// A regular expression method that splits a string at the indices + /// that match the regular expression. + /// Called by the `String.prototype.split` method. + #[inline] + pub fn split_symbol(&self) -> RcSymbol { + self.split.clone() + } + + /// The `Symbol.toPrimitive` well known symbol. + /// + /// 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_symbol(&self) -> RcSymbol { + self.to_primitive.clone() + } + + /// The `Symbol.toStringTag` well known symbol. + /// + /// A String valued property that is used in the creation of the default + /// string description of an object. + /// Accessed by the built-in method `Object.prototype.toString`. + #[inline] + pub fn to_string_tag_symbol(&self) -> RcSymbol { + self.to_string_tag.clone() + } + + /// The `Symbol.unscopables` well known symbol. + /// + /// 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_symbol(&self) -> RcSymbol { + self.unscopables.clone() + } +} + #[derive(Debug, Finalize, Trace, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Symbol { hash: u32, @@ -109,32 +303,32 @@ impl Symbol { /// Initialise the `Symbol` object on the global object. #[inline] - pub fn init(interpreter: &mut Context) -> (&'static str, Value) { + pub fn init(context: &mut Context) -> (&'static str, Value) { // Define the Well-Known Symbols // https://tc39.es/ecma262/#sec-well-known-symbols - let symbol_async_iterator = - interpreter.construct_symbol(Some("Symbol.asyncIterator".into())); - let symbol_has_instance = interpreter.construct_symbol(Some("Symbol.hasInstance".into())); - let symbol_is_concat_spreadable = - interpreter.construct_symbol(Some("Symbol.isConcatSpreadable".into())); - let symbol_iterator = interpreter.construct_symbol(Some("Symbol.iterator".into())); - let symbol_match = interpreter.construct_symbol(Some("Symbol.match".into())); - let symbol_match_all = interpreter.construct_symbol(Some("Symbol.matchAll".into())); - let symbol_replace = interpreter.construct_symbol(Some("Symbol.replace".into())); - let symbol_search = interpreter.construct_symbol(Some("Symbol.search".into())); - let symbol_species = interpreter.construct_symbol(Some("Symbol.species".into())); - let symbol_split = interpreter.construct_symbol(Some("Symbol.split".into())); - let symbol_to_primitive = interpreter.construct_symbol(Some("Symbol.toPrimitive".into())); - let symbol_to_string_tag = interpreter.construct_symbol(Some("Symbol.toStringTag".into())); - let symbol_unscopables = interpreter.construct_symbol(Some("Symbol.unscopables".into())); - - let global = interpreter.global_object(); + 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"); // Create prototype object let prototype = Value::new_object(Some(global)); - make_builtin_fn(Self::to_string, "toString", &prototype, 0, interpreter); + make_builtin_fn(Self::to_string, "toString", &prototype, 0, context); let symbol_object = make_constructor_fn( Self::NAME, diff --git a/boa/src/context.rs b/boa/src/context.rs index 1eacbfacee..cb2b6abf9f 100644 --- a/boa/src/context.rs +++ b/boa/src/context.rs @@ -4,7 +4,8 @@ use crate::{ builtins::{ self, function::{Function, FunctionFlags, NativeFunction}, - Console, Symbol, + symbol::{Symbol, WellKnownSymbols}, + Console, }, class::{Class, ClassBuilder}, exec::Interpreter, @@ -46,17 +47,22 @@ pub struct Context { /// console object state. console: Console, + + /// Cached well known symbols + well_known_symbols: WellKnownSymbols, } impl Default for Context { fn default() -> Self { let realm = Realm::create(); let executor = Interpreter::new(); + let (well_known_symbols, symbol_count) = WellKnownSymbols::new(); let mut context = Self { realm, executor, - symbol_count: 0, + symbol_count, console: Console::default(), + well_known_symbols, }; // Add new builtIns to Context Realm @@ -495,4 +501,20 @@ impl Context { result } + + /// Returns a structure that contains the JavaScript well known symbols. + /// + /// # Examples + /// ``` + ///# use boa::Context; + /// let mut context = Context::new(); + /// + /// let iterator = context.well_known_symbols().iterator_symbol(); + /// assert_eq!(iterator.description(), Some("Symbol.iterator")); + /// ``` + /// This is equivalent to `let iterator = Symbol.iterator` in JavaScript. + #[inline] + pub fn well_known_symbols(&self) -> &WellKnownSymbols { + &self.well_known_symbols + } }