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);
|
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(
|
apply_binding_table(
|
||||||
session.builtin_world(),
|
session.builtin_world(),
|
||||||
proto,
|
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;
|
let si = vm.kernel_core().string_forge().intern(&result).0;
|
||||||
NativeResult::Ok(JsValue::string(si, 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)]
|
#![allow(clippy::arc_with_non_send_sync)]
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use oxide_compiler::module::CompiledModule;
|
use oxide_compiler::module::CompiledModule;
|
||||||
@@ -151,6 +152,7 @@ pub struct Vm {
|
|||||||
pub(crate) pending_error_kind: Option<&'static str>,
|
pub(crate) pending_error_kind: Option<&'static str>,
|
||||||
pub(crate) symbol_counter: u32,
|
pub(crate) symbol_counter: u32,
|
||||||
pub(crate) symbol_descriptions: Vec<String>,
|
pub(crate) symbol_descriptions: Vec<String>,
|
||||||
|
pub(crate) symbol_registry: HashMap<String, u32>,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) for_of_iters: Vec<*mut u8>,
|
pub(crate) for_of_iters: Vec<*mut u8>,
|
||||||
pub(crate) root_reg_limit: u8,
|
pub(crate) root_reg_limit: u8,
|
||||||
@@ -1184,6 +1186,20 @@ mod tests {
|
|||||||
assert!(vm.constants.is_empty());
|
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]
|
#[test]
|
||||||
fn for_of_opcodes_fail_explicitly() {
|
fn for_of_opcodes_fail_explicitly() {
|
||||||
for (op, expected) in [
|
for (op, expected) in [
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ impl Vm {
|
|||||||
pending_error_kind: None,
|
pending_error_kind: None,
|
||||||
symbol_counter: 0,
|
symbol_counter: 0,
|
||||||
symbol_descriptions: Vec::new(),
|
symbol_descriptions: Vec::new(),
|
||||||
|
symbol_registry: std::collections::HashMap::new(),
|
||||||
for_of_iters: Vec::new(),
|
for_of_iters: Vec::new(),
|
||||||
root_reg_limit: 0,
|
root_reg_limit: 0,
|
||||||
active_reg_limit: 0,
|
active_reg_limit: 0,
|
||||||
@@ -77,6 +78,7 @@ impl Vm {
|
|||||||
pending_error_kind: None,
|
pending_error_kind: None,
|
||||||
symbol_counter: 0,
|
symbol_counter: 0,
|
||||||
symbol_descriptions: Vec::new(),
|
symbol_descriptions: Vec::new(),
|
||||||
|
symbol_registry: std::collections::HashMap::new(),
|
||||||
for_of_iters: Vec::new(),
|
for_of_iters: Vec::new(),
|
||||||
root_reg_limit: 0,
|
root_reg_limit: 0,
|
||||||
active_reg_limit: 0,
|
active_reg_limit: 0,
|
||||||
@@ -95,6 +97,9 @@ impl Vm {
|
|||||||
self.constants.clear();
|
self.constants.clear();
|
||||||
self.epoch.reset();
|
self.epoch.reset();
|
||||||
self.interned_strings.clear();
|
self.interned_strings.clear();
|
||||||
|
self.symbol_counter = 0;
|
||||||
|
self.symbol_descriptions.clear();
|
||||||
|
self.symbol_registry.clear();
|
||||||
self.root_reg_limit = 0;
|
self.root_reg_limit = 0;
|
||||||
self.active_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();
|
let result = eval(&mut vm, "typeof Symbol.iterator").unwrap();
|
||||||
assert_eq!(to_str(&vm, result), "object");
|
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