From 12ac57c8776cb60b86d84c2cf92fc1604a7193f0 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Thu, 17 Sep 2026 23:03:10 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=9F=A5=E5=90=8D=E7=AC=A6=E5=8F=B7?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E5=8E=9F=E8=AF=AD=E5=80=BC=E8=A1=A8=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_builtins/src/object.rs | 28 ++------ crates/oxide_vm/src/bindings/bind_symbol.rs | 71 +++++-------------- crates/oxide_vm/src/vm/error.rs | 13 ++-- crates/oxide_vm/src/vm/props.rs | 20 ++++-- crates/oxide_vm/src/vm_dispatch_misc.rs | 11 +-- crates/oxide_vm/src/vm_state.rs | 16 ++++- .../oxide_vm/tests/builtins_iterator_tests.rs | 14 ++-- .../oxide_vm/tests/builtins_symbol_tests.rs | 12 ++-- 8 files changed, 79 insertions(+), 106 deletions(-) diff --git a/crates/oxide_builtins/src/object.rs b/crates/oxide_builtins/src/object.rs index d1d2152d..9ba76d01 100644 --- a/crates/oxide_builtins/src/object.rs +++ b/crates/oxide_builtins/src/object.rs @@ -140,27 +140,11 @@ fn walk_own_symbol_keys(vm: &H, obj: &JsObject) -> Vec<(u32, u32)> { keys } -/// 把 Symbol 键反解为对应的 Symbol 值:well-known 键还原为内置 symbol 对象, -/// 用户 symbol 键还原为 `JsValue::symbol` 值。 -fn decode_symbol_key(vm: &H, key: u32) -> JsValue { +/// 把 Symbol 键反解为对应的 Symbol 值:well-known 键还原为对应下标的符号原语, +/// 用户 symbol 键按偏移还原为 `JsValue::symbol` 值。 +fn decode_symbol_key(key: u32) -> JsValue { if let Some(id) = well_known_symbol_id_from_key(key) { - let world = vm.session().builtin_world(); - let ptr = match id { - 0 => world.sym_iterator.as_ptr(), - 1 => world.sym_match.as_ptr(), - 2 => world.sym_replace.as_ptr(), - 3 => world.sym_search.as_ptr(), - 4 => world.sym_split.as_ptr(), - 5 => world.sym_to_primitive.as_ptr(), - 6 => world.sym_has_instance.as_ptr(), - 7 => world.sym_match_all.as_ptr(), - 8 => world.sym_async_iterator.as_ptr(), - 10 => world.sym_species.as_ptr(), - 11 => world.sym_async_dispose.as_ptr(), - 12 => world.sym_dispose.as_ptr(), - _ => world.sym_to_string_tag.as_ptr(), - }; - return JsValue::from_js_object(ptr as *mut JsObject); + return JsValue::symbol(id); } JsValue::symbol(symbol_index_from_key(key)) } @@ -174,11 +158,11 @@ fn decode_symbol_key(vm: &H, key: u32) -> JsValue { /// - 无自身 Symbol 键时返回空向量;数组元素区不含 Symbol 键。 /// /// # 注意事项 -/// - 只读 shape 链,不分配对象;well-known symbol 值指向 session 内建 symbol 对象。 +/// - 只读 shape 链,不分配对象;well-known symbol 物化为符号原语。 pub fn own_symbol_key_values(vm: &H, obj: &JsObject) -> Vec { walk_own_symbol_keys(vm, obj) .iter() - .map(|(key, _)| decode_symbol_key(vm, *key)) + .map(|(key, _)| decode_symbol_key(*key)) .collect() } diff --git a/crates/oxide_vm/src/bindings/bind_symbol.rs b/crates/oxide_vm/src/bindings/bind_symbol.rs index f76340f5..6dd0de20 100644 --- a/crates/oxide_vm/src/bindings/bind_symbol.rs +++ b/crates/oxide_vm/src/bindings/bind_symbol.rs @@ -45,61 +45,24 @@ pub fn bind_symbol(core: &Arc, session: &KernelSession, global: &mut oxide_builtins::symbol::symbol_description_getter:: as *const (), ); - for (name, val) in [ - ( - "match", - JsValue::from_js_object(session.builtin_world().sym_match.as_ptr() as *mut JsObject), - ), - ( - "replace", - JsValue::from_js_object(session.builtin_world().sym_replace.as_ptr() as *mut JsObject), - ), - ( - "search", - JsValue::from_js_object(session.builtin_world().sym_search.as_ptr() as *mut JsObject), - ), - ( - "split", - JsValue::from_js_object(session.builtin_world().sym_split.as_ptr() as *mut JsObject), - ), - ( - "iterator", - JsValue::from_js_object(session.builtin_world().sym_iterator.as_ptr() as *mut JsObject), - ), - ( - "toPrimitive", - JsValue::from_js_object(session.builtin_world().sym_to_primitive.as_ptr() as *mut JsObject), - ), - ( - "hasInstance", - JsValue::from_js_object(session.builtin_world().sym_has_instance.as_ptr() as *mut JsObject), - ), - ( - "matchAll", - JsValue::from_js_object(session.builtin_world().sym_match_all.as_ptr() as *mut JsObject), - ), - ( - "asyncIterator", - JsValue::from_js_object(session.builtin_world().sym_async_iterator.as_ptr() as *mut JsObject), - ), - ( - "toStringTag", - JsValue::from_js_object(session.builtin_world().sym_to_string_tag.as_ptr() as *mut JsObject), - ), - ( - "species", - JsValue::from_js_object(session.builtin_world().sym_species.as_ptr() as *mut JsObject), - ), - ( - "asyncDispose", - JsValue::from_js_object(session.builtin_world().sym_async_dispose.as_ptr() as *mut JsObject), - ), - ( - "dispose", - JsValue::from_js_object(session.builtin_world().sym_dispose.as_ptr() as *mut JsObject), - ), + // well-known symbol 以符号原语绑定,下标与内建符号表(0..WELL_KNOWN_SYMBOL_COUNT) + // 一一对应;键编码由该下标直接推出。 + for (name, id) in [ + ("match", 1u32), + ("replace", 2), + ("search", 3), + ("split", 4), + ("iterator", 0), + ("toPrimitive", 5), + ("hasInstance", 6), + ("matchAll", 7), + ("asyncIterator", 8), + ("toStringTag", 9), + ("species", 10), + ("asyncDispose", 11), + ("dispose", 12), ] { - bind_well_known_symbol(core, ctor, name, val); + bind_well_known_symbol(core, ctor, name, JsValue::symbol(id)); } bind_constructor!(core, global, "Symbol", ctor_ptr, oxide_builtins::symbol::symbol_constructor::, 1, hash: true); diff --git a/crates/oxide_vm/src/vm/error.rs b/crates/oxide_vm/src/vm/error.rs index 5c32e9b9..c35d0bca 100644 --- a/crates/oxide_vm/src/vm/error.rs +++ b/crates/oxide_vm/src/vm/error.rs @@ -5,11 +5,15 @@ use oxide_runtime_api as coercion; use oxide_types::error::JsError; use oxide_types::object::JsObject; +use oxide_types::private_key::make_well_known_symbol_key; use oxide_types::value::{JsValue, PTR_MASK}; use super::{format_error_message, js_error_kind, js_error_kind_name, Vm}; use crate::vm_debug; +/// `Symbol.toPrimitive` 的 well-known symbol 下标。 +const TO_PRIMITIVE_SYMBOL_ID: u32 = 5; + impl Vm { /// ToPrimitive 有界版:按规范序把对象转为原始值,失败统一抛 `TypeError`。 /// @@ -46,13 +50,8 @@ impl Vm { } // ECMA-262 §7.1.1 step 1:exotic 对象上(沿原型链读取)的 Symbol.toPrimitive 优先于 - // OrdinaryToPrimitive。well-known symbol 键经 property_key_si 映射为固定 - // Symbol 键,读键路径与写键路径一致。 - let sym_key = { - let sym_ptr = self.session.builtin_world().sym_to_primitive.as_ptr() as *mut JsObject; - JsValue::from_js_object(sym_ptr) - }; - let sym_si = self.property_key_si(sym_key)?; + // OrdinaryToPrimitive;well-known symbol 键恒定,直接取保留槽。 + let sym_si = make_well_known_symbol_key(TO_PRIMITIVE_SYMBOL_ID); let exotic = { let obj = unsafe { &*obj_ptr }; self.ordinary_get(obj, sym_si, value)? diff --git a/crates/oxide_vm/src/vm/props.rs b/crates/oxide_vm/src/vm/props.rs index 8c7cb42a..1b7d493b 100644 --- a/crates/oxide_vm/src/vm/props.rs +++ b/crates/oxide_vm/src/vm/props.rs @@ -6,12 +6,23 @@ use oxide_runtime_api as coercion; use oxide_types::object::JsObject; use oxide_types::private_key::{ int_key_value, is_int_key, make_int_key, make_symbol_key, make_well_known_symbol_key, INT_KEY_COUNT, + WELL_KNOWN_SYMBOL_COUNT, }; use oxide_types::value::JsValue; use super::{canonical_index_of, canonical_index_units, Vm, MAX_PROTO_CHAIN_DEPTH}; use crate::vm_trace; +/// 把符号原语的下标编码为属性键:well-known 下标走保留键槽,用户下标加偏移, +/// 两段键区间不重叠。 +fn symbol_value_key(idx: u32) -> u32 { + if idx < WELL_KNOWN_SYMBOL_COUNT { + make_well_known_symbol_key(idx) + } else { + make_symbol_key(idx) + } +} + impl Vm { /// 把 `JsValue` 转为属性键 si(字符串 intern id,`u32`)。 /// @@ -43,11 +54,12 @@ impl Vm { // 单元载荷:键推导同规范(见 string_key_units)。 return Ok(self.string_key_units(&s.units())); } - // Symbol 值直接编码为 Symbol 键(不进字符串 interner,键相互独立)。 + // Symbol 值直接编码为 Symbol 键(不进字符串 interner,键相互独立); + // well-known 下标占保留槽,用户符号下标须加偏移,两段键区间不重叠。 if val.is_symbol() { - return Ok(make_symbol_key(val.as_symbol_index())); + return Ok(symbol_value_key(val.as_symbol_index())); } - // well-known symbol 是空对象:按指针比对映射到各自的 well-known Symbol 键, + // 遗留的 well-known symbol 空对象:按指针比对映射到各自的 well-known Symbol 键, // 避免全部塌缩成同一个键。 if val.is_object() { if let Some(id) = oxide_runtime_api::well_known_symbol_id(self, val.as_js_object_ptr()) { @@ -57,7 +69,7 @@ impl Vm { // 其余字符串按单元序列推导键(避免 lossy 文本桥接破坏孤立 surrogate 键)。 let prim = coercion::to_primitive(val, coercion::ToPrimitiveHint::String, self)?; if prim.is_symbol() { - return Ok(make_symbol_key(prim.as_symbol_index())); + return Ok(symbol_value_key(prim.as_symbol_index())); } let units = oxide_runtime_api::to_units_full(prim, self)?; return Ok(self.string_key_units(&units)); diff --git a/crates/oxide_vm/src/vm_dispatch_misc.rs b/crates/oxide_vm/src/vm_dispatch_misc.rs index 92060b77..017496ff 100644 --- a/crates/oxide_vm/src/vm_dispatch_misc.rs +++ b/crates/oxide_vm/src/vm_dispatch_misc.rs @@ -2,9 +2,14 @@ use crate::vm::{ForInIter, FrameArgs, FrameContinuation, Vm, MAX_PROTO_CHAIN_DEP use crate::vm_trace; use oxide_runtime_api::{push_units_to, to_boolean, to_units_full}; use oxide_types::object::{JsObject, PropAttributes}; -use oxide_types::private_key::{int_key_value, is_int_key, is_private_name_key, is_symbol_key, make_int_key}; +use oxide_types::private_key::{ + int_key_value, is_int_key, is_private_name_key, is_symbol_key, make_int_key, make_well_known_symbol_key, +}; use oxide_types::value::JsValue; +/// `Symbol.hasInstance` 的 well-known symbol 下标。 +const HAS_INSTANCE_SYMBOL_ID: u32 = 6; + impl Vm { pub(crate) fn dispatch_new_expression(&mut self, rd: usize, a: usize, b: usize) -> Result { let constructor_reg = a; @@ -314,9 +319,7 @@ impl Vm { return self.raise_type_error("INSTANCEOF right-hand side is not callable"); } - let has_instance_ptr = self.session.builtin_world().sym_has_instance.as_ptr() as *mut JsObject; - let has_instance_key = JsValue::from_js_object(has_instance_ptr); - let has_instance_si = self.property_key_si(has_instance_key)?; + let has_instance_si = make_well_known_symbol_key(HAS_INSTANCE_SYMBOL_ID); let ctor_obj = unsafe { &*rhs_val.as_js_object_ptr() }; let has_instance_val = self.ordinary_get(ctor_obj, has_instance_si, rhs_val)?; diff --git a/crates/oxide_vm/src/vm_state.rs b/crates/oxide_vm/src/vm_state.rs index 63dbeda2..03775694 100644 --- a/crates/oxide_vm/src/vm_state.rs +++ b/crates/oxide_vm/src/vm_state.rs @@ -16,6 +16,7 @@ use rustc_hash::FxBuildHasher; use crate::session_gc::SessionGc; use crate::vm::ForInIter; use oxide_types::object::{Cell as UpvalueCell, JsObject, JsString}; +use oxide_types::private_key::WELL_KNOWN_SYMBOL_COUNT; use oxide_types::value::JsValue; /// session arena 与 GC 簿记。 @@ -92,6 +93,10 @@ impl GcState { } /// Symbol 的 intern 状态。 +/// +/// 符号下标 `0..WELL_KNOWN_SYMBOL_COUNT` 保留给 well-known symbol(描述取内建 +/// 名表),用户 `Symbol()`/`Symbol.for()` 的下标自此区间之后递增, +/// `symbol_descriptions` 只存用户符号描述。 pub(crate) struct SymbolState { pub(crate) symbol_counter: u32, pub(crate) symbol_descriptions: Vec>, @@ -107,7 +112,8 @@ impl SymbolState { pub(crate) fn intern(&mut self, description: Option) -> u32 { self.symbol_counter = self.symbol_counter.wrapping_add(1); - let idx = self.symbol_descriptions.len() as u32; + // 用户符号下标顺延到 well-known 保留区间之后,与 well-known 下标空间不重叠。 + let idx = WELL_KNOWN_SYMBOL_COUNT + self.symbol_descriptions.len() as u32; self.symbol_descriptions.push(description); idx } @@ -121,7 +127,13 @@ impl SymbolState { } pub(crate) fn description(&self, id: u32) -> Option<&str> { - self.symbol_descriptions.get(id as usize).and_then(|s| s.as_deref()) + // well-known 下标区间取内建名表,用户区间按偏移查描述槽。 + if id < WELL_KNOWN_SYMBOL_COUNT { + return oxide_runtime_api::well_known_symbol_name(id); + } + self.symbol_descriptions + .get((id - WELL_KNOWN_SYMBOL_COUNT) as usize) + .and_then(|s| s.as_deref()) } pub(crate) fn key_for_id(&self, id: u32) -> Option { diff --git a/crates/oxide_vm/tests/builtins_iterator_tests.rs b/crates/oxide_vm/tests/builtins_iterator_tests.rs index 682d3fe3..c6c46af7 100644 --- a/crates/oxide_vm/tests/builtins_iterator_tests.rs +++ b/crates/oxide_vm/tests/builtins_iterator_tests.rs @@ -513,15 +513,15 @@ fn iterator_prototype_to_string_tag_accessor() { #[test] fn symbol_dispose_and_async_dispose_registered() { let mut vm = Vm::new(); - // well-known symbol 以空对象表示,typeof 为 object;两符号互不相同。 + // well-known symbol 以符号原语表示,typeof 为 symbol;两符号互不相同。 let bool_cases = [ - ("typeof Symbol.dispose === 'object'", true), - ("typeof Symbol.asyncDispose === 'object'", true), + ("typeof Symbol.dispose === 'symbol'", true), + ("typeof Symbol.asyncDispose === 'symbol'", true), ("Symbol.dispose !== Symbol.asyncDispose", true), ("Symbol.dispose !== Symbol.iterator", true), // 键可作属性名使用(well-known 键映射一致)。 ("({ [Symbol.dispose]: 1 })[Symbol.dispose] === 1", true), - // getOwnPropertySymbols 能反解出同一符号对象。 + // getOwnPropertySymbols 能反解出同一符号原语。 ("Object.getOwnPropertySymbols({ [Symbol.dispose]: 1 })[0] === Symbol.dispose", true), ]; for (src, expected) in bool_cases { @@ -529,7 +529,7 @@ fn symbol_dispose_and_async_dispose_registered() { assert_eq!(result.as_bool(), expected, "for {}", src); } // String(Symbol) 走 SymbolDescriptiveString(规范 String 构造器步骤), - // 不经 ToString——well-known 符号对象按描述名产出。 + // 不经 ToString——well-known 符号按描述名产出。 let s = eval(&mut vm, "String(Symbol.dispose)").unwrap(); assert_eq!(to_str(&vm, s), "Symbol(Symbol.dispose)"); } @@ -684,8 +684,8 @@ fn iterator_prototype_rebound_after_full_reset() { ("typeof Iterator.prototype[Symbol.dispose] === 'function'", true), ("Iterator.name === 'Iterator'", true), ("Iterator.length === 0", true), - ("typeof Symbol.dispose === 'object'", true), - ("typeof Symbol.asyncDispose === 'object'", true), + ("typeof Symbol.dispose === 'symbol'", true), + ("typeof Symbol.asyncDispose === 'symbol'", true), ]; for (src, expected) in cases { let result = eval(&mut vm, src).unwrap(); diff --git a/crates/oxide_vm/tests/builtins_symbol_tests.rs b/crates/oxide_vm/tests/builtins_symbol_tests.rs index 1cddee90..7184a949 100644 --- a/crates/oxide_vm/tests/builtins_symbol_tests.rs +++ b/crates/oxide_vm/tests/builtins_symbol_tests.rs @@ -67,28 +67,28 @@ fn typeof_symbol_variable() { fn symbol_match_exists() { let mut vm = Vm::new(); let result = eval(&mut vm, "typeof Symbol.match").unwrap(); - assert_eq!(to_str(&vm, result), "object"); + assert_eq!(to_str(&vm, result), "symbol"); } #[test] fn symbol_replace_exists() { let mut vm = Vm::new(); let result = eval(&mut vm, "typeof Symbol.replace").unwrap(); - assert_eq!(to_str(&vm, result), "object"); + assert_eq!(to_str(&vm, result), "symbol"); } #[test] fn symbol_search_exists() { let mut vm = Vm::new(); let result = eval(&mut vm, "typeof Symbol.search").unwrap(); - assert_eq!(to_str(&vm, result), "object"); + assert_eq!(to_str(&vm, result), "symbol"); } #[test] fn symbol_split_exists() { let mut vm = Vm::new(); let result = eval(&mut vm, "typeof Symbol.split").unwrap(); - assert_eq!(to_str(&vm, result), "object"); + assert_eq!(to_str(&vm, result), "symbol"); } // --- Symbol coercion --- @@ -120,7 +120,7 @@ fn symbol_not_equals_string() { fn symbol_iterator_exists() { let mut vm = Vm::new(); let result = eval(&mut vm, "typeof Symbol.iterator").unwrap(); - assert_eq!(to_str(&vm, result), "object"); + assert_eq!(to_str(&vm, result), "symbol"); } #[test] @@ -164,7 +164,7 @@ fn symbol_key_for_non_symbol_throws_type_error() { fn symbol_has_instance_exists() { let mut vm = Vm::new(); let result = eval(&mut vm, "typeof Symbol.hasInstance").unwrap(); - assert_eq!(to_str(&vm, result), "object"); + assert_eq!(to_str(&vm, result), "symbol"); } #[test]