Browse Source

Implement `Symbol.for` and `Symbol.keyFor` (#1424)

pull/1431/head
Halid Odat 3 years ago committed by GitHub
parent
commit
beecb2db80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 102
      boa/src/builtins/symbol/mod.rs

102
boa/src/builtins/symbol/mod.rs

@ -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")
}
}
}

Loading…
Cancel
Save