|
|
|
@ -24,9 +24,50 @@ use crate::{
|
|
|
|
|
property::Attribute, |
|
|
|
|
symbol::{JsSymbol, WellKnownSymbols}, |
|
|
|
|
value::Value, |
|
|
|
|
BoaProfiler, Context, Result, |
|
|
|
|
BoaProfiler, Context, JsString, Result, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
use std::cell::RefCell; |
|
|
|
|
|
|
|
|
|
use rustc_hash::FxHashMap; |
|
|
|
|
|
|
|
|
|
thread_local! { |
|
|
|
|
static GLOBAL_SYMBOL_REGISTRY: RefCell<GlobalSymbolRegistry> = RefCell::new(GlobalSymbolRegistry::new()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
struct GlobalSymbolRegistry { |
|
|
|
|
keys: FxHashMap<JsString, JsSymbol>, |
|
|
|
|
symbols: FxHashMap<JsSymbol, JsString>, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl GlobalSymbolRegistry { |
|
|
|
|
fn new() -> Self { |
|
|
|
|
Self { |
|
|
|
|
keys: FxHashMap::default(), |
|
|
|
|
symbols: FxHashMap::default(), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn get_or_insert_key(&mut self, key: JsString) -> JsSymbol { |
|
|
|
|
if let Some(symbol) = self.keys.get(&key) { |
|
|
|
|
return symbol.clone(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let symbol = JsSymbol::new(Some(key.clone())); |
|
|
|
|
self.keys.insert(key.clone(), symbol.clone()); |
|
|
|
|
self.symbols.insert(symbol.clone(), key); |
|
|
|
|
symbol |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn get_symbol(&self, sym: JsSymbol) -> Option<JsString> { |
|
|
|
|
if let Some(key) = self.symbols.get(&sym) { |
|
|
|
|
return Some(key.clone()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
None |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)] |
|
|
|
|
pub struct Symbol; |
|
|
|
|
|
|
|
|
@ -69,6 +110,8 @@ impl BuiltIn for Symbol {
|
|
|
|
|
) |
|
|
|
|
.name(Self::NAME) |
|
|
|
|
.length(Self::LENGTH) |
|
|
|
|
.static_method(Self::for_, "for", 1) |
|
|
|
|
.static_method(Self::key_for, "keyFor", 1) |
|
|
|
|
.static_property("asyncIterator", symbol_async_iterator, attribute) |
|
|
|
|
.static_property("hasInstance", symbol_has_instance, attribute) |
|
|
|
|
.static_property("isConcatSpreadable", symbol_is_concat_spreadable, attribute) |
|
|
|
@ -186,4 +229,61 @@ impl Symbol {
|
|
|
|
|
Ok(Value::undefined()) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `Symbol.for( key )`
|
|
|
|
|
///
|
|
|
|
|
/// More information:
|
|
|
|
|
/// - [MDN documentation][mdn]
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
///
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-symbol.prototype.for
|
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/for
|
|
|
|
|
pub(crate) fn for_(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> { |
|
|
|
|
// 1. Let stringKey be ? ToString(key).
|
|
|
|
|
let string_key = args |
|
|
|
|
.get(0) |
|
|
|
|
.cloned() |
|
|
|
|
.unwrap_or_default() |
|
|
|
|
.to_string(context)?; |
|
|
|
|
// 2. For each element e of the GlobalSymbolRegistry List, do
|
|
|
|
|
// a. If SameValue(e.[[Key]], stringKey) is true, return e.[[Symbol]].
|
|
|
|
|
// 3. Assert: GlobalSymbolRegistry does not currently contain an entry for stringKey.
|
|
|
|
|
// 4. Let newSymbol be a new unique Symbol value whose [[Description]] value is stringKey.
|
|
|
|
|
// 5. Append the Record { [[Key]]: stringKey, [[Symbol]]: newSymbol } to the GlobalSymbolRegistry List.
|
|
|
|
|
// 6. Return newSymbol.
|
|
|
|
|
Ok(GLOBAL_SYMBOL_REGISTRY |
|
|
|
|
.with(move |registry| { |
|
|
|
|
let mut registry = registry.borrow_mut(); |
|
|
|
|
registry.get_or_insert_key(string_key) |
|
|
|
|
}) |
|
|
|
|
.into()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `Symbol.keyFor( sym )`
|
|
|
|
|
///
|
|
|
|
|
///
|
|
|
|
|
/// More information:
|
|
|
|
|
/// - [MDN documentation][mdn]
|
|
|
|
|
/// - [ECMAScript reference][spec]
|
|
|
|
|
///
|
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#sec-symbol.prototype.keyfor
|
|
|
|
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/keyFor
|
|
|
|
|
pub(crate) fn key_for(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> { |
|
|
|
|
let sym = args.get(0).cloned().unwrap_or_default(); |
|
|
|
|
// 1. If Type(sym) is not Symbol, throw a TypeError exception.
|
|
|
|
|
if let Some(sym) = sym.as_symbol() { |
|
|
|
|
// 2. For each element e of the GlobalSymbolRegistry List (see 20.4.2.2), do
|
|
|
|
|
// a. If SameValue(e.[[Symbol]], sym) is true, return e.[[Key]].
|
|
|
|
|
// 3. Assert: GlobalSymbolRegistry does not currently contain an entry for sym.
|
|
|
|
|
// 4. Return undefined.
|
|
|
|
|
let symbol = GLOBAL_SYMBOL_REGISTRY.with(move |registry| { |
|
|
|
|
let registry = registry.borrow(); |
|
|
|
|
registry.get_symbol(sym) |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
Ok(symbol.map(Value::from).unwrap_or_default()) |
|
|
|
|
} else { |
|
|
|
|
context.throw_type_error("Symbol.keyFor: sym is not a symbol") |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|