Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cache well known symbols #706

Merged
merged 3 commits into from
Sep 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 213 additions & 19 deletions boa/src/builtins/symbol/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
26 changes: 24 additions & 2 deletions boa/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ use crate::{
builtins::{
self,
function::{Function, FunctionFlags, NativeFunction},
Console, Symbol,
symbol::{Symbol, WellKnownSymbols},
Console,
},
class::{Class, ClassBuilder},
exec::Interpreter,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
}