mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: 实现 Symbol.for 和 Symbol.keyFor
This commit is contained in:
@@ -15,6 +15,16 @@ pub fn bind_symbol(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
|
||||
|
||||
configure_native_constructor(ctor, crate::builtins::symbol::symbol_constructor as *const (), 1);
|
||||
|
||||
apply_binding_table(
|
||||
session.builtin_world(),
|
||||
ctor,
|
||||
core,
|
||||
&[
|
||||
("for", crate::builtins::symbol::symbol_for as *const (), 1),
|
||||
("keyFor", crate::builtins::symbol::symbol_key_for as *const (), 1),
|
||||
],
|
||||
);
|
||||
|
||||
apply_binding_table(
|
||||
session.builtin_world(),
|
||||
proto,
|
||||
|
||||
@@ -60,3 +60,37 @@ pub fn symbol_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
let si = vm.kernel_core().string_forge().intern(&result).0;
|
||||
NativeResult::Ok(JsValue::string(si, 0))
|
||||
}
|
||||
|
||||
pub fn symbol_for(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
let key = if args.len() > 1 {
|
||||
coercion::to_string(vm.kernel_core().string_forge().as_ref(), vm.reg(args[1]))
|
||||
} else {
|
||||
"undefined".to_string()
|
||||
};
|
||||
|
||||
if let Some(&idx) = vm.symbol_registry.get(&key) {
|
||||
return NativeResult::Ok(JsValue::symbol(idx));
|
||||
}
|
||||
|
||||
let idx = vm.symbol_descriptions.len() as u32;
|
||||
vm.symbol_descriptions.push(key.clone());
|
||||
vm.symbol_registry.insert(key, idx);
|
||||
NativeResult::Ok(JsValue::symbol(idx))
|
||||
}
|
||||
|
||||
pub fn symbol_key_for(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
let sym = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||||
if !sym.is_symbol() {
|
||||
return NativeResult::Err(crate::builtins::error::create_type_error(vm, "is not a symbol"));
|
||||
}
|
||||
|
||||
let idx = sym.as_symbol_index();
|
||||
for (key, ®istered_idx) in &vm.symbol_registry {
|
||||
if registered_idx == idx {
|
||||
let si = vm.kernel_core().string_forge().intern(key).0;
|
||||
return NativeResult::Ok(JsValue::string(si, 0));
|
||||
}
|
||||
}
|
||||
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#![allow(clippy::arc_with_non_send_sync)]
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use oxide_compiler::module::CompiledModule;
|
||||
@@ -151,6 +152,7 @@ pub struct Vm {
|
||||
pub(crate) pending_error_kind: Option<&'static str>,
|
||||
pub(crate) symbol_counter: u32,
|
||||
pub(crate) symbol_descriptions: Vec<String>,
|
||||
pub(crate) symbol_registry: HashMap<String, u32>,
|
||||
#[allow(dead_code)]
|
||||
pub(crate) for_of_iters: Vec<*mut u8>,
|
||||
pub(crate) root_reg_limit: u8,
|
||||
@@ -1184,6 +1186,20 @@ mod tests {
|
||||
assert!(vm.constants.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_reset_clears_symbol_state() {
|
||||
let mut vm = Vm::new();
|
||||
vm.symbol_counter = 1;
|
||||
vm.symbol_descriptions.push("shared".to_string());
|
||||
vm.symbol_registry.insert("shared".to_string(), 0);
|
||||
|
||||
vm.full_reset();
|
||||
|
||||
assert_eq!(vm.symbol_counter, 0);
|
||||
assert!(vm.symbol_descriptions.is_empty());
|
||||
assert!(vm.symbol_registry.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_of_opcodes_fail_explicitly() {
|
||||
for (op, expected) in [
|
||||
|
||||
@@ -42,6 +42,7 @@ impl Vm {
|
||||
pending_error_kind: None,
|
||||
symbol_counter: 0,
|
||||
symbol_descriptions: Vec::new(),
|
||||
symbol_registry: std::collections::HashMap::new(),
|
||||
for_of_iters: Vec::new(),
|
||||
root_reg_limit: 0,
|
||||
active_reg_limit: 0,
|
||||
@@ -77,6 +78,7 @@ impl Vm {
|
||||
pending_error_kind: None,
|
||||
symbol_counter: 0,
|
||||
symbol_descriptions: Vec::new(),
|
||||
symbol_registry: std::collections::HashMap::new(),
|
||||
for_of_iters: Vec::new(),
|
||||
root_reg_limit: 0,
|
||||
active_reg_limit: 0,
|
||||
@@ -95,6 +97,9 @@ impl Vm {
|
||||
self.constants.clear();
|
||||
self.epoch.reset();
|
||||
self.interned_strings.clear();
|
||||
self.symbol_counter = 0;
|
||||
self.symbol_descriptions.clear();
|
||||
self.symbol_registry.clear();
|
||||
self.root_reg_limit = 0;
|
||||
self.active_reg_limit = 0;
|
||||
}
|
||||
|
||||
@@ -123,3 +123,38 @@ fn symbol_iterator_exists() {
|
||||
let result = eval(&mut vm, "typeof Symbol.iterator").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "object");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbol_for_reuses_registered_symbol() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "var s1 = Symbol.for('shared'); var s2 = Symbol.for('shared'); s1 === s2").unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbol_for_is_distinct_from_symbol_constructor() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "var s1 = Symbol.for('shared'); var s2 = Symbol('shared'); s1 === s2").unwrap();
|
||||
assert!(!result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbol_key_for_returns_registered_key() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "Symbol.keyFor(Symbol.for('shared'))").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "shared");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbol_key_for_unregistered_symbol_returns_undefined() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "Symbol.keyFor(Symbol('shared'))").unwrap();
|
||||
assert!(result.is_undefined());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbol_key_for_non_symbol_throws_type_error() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "try { Symbol.keyFor(42) } catch (e) { e instanceof TypeError }").unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user