fix: 知名符号改为原语值表示

This commit is contained in:
2026-09-17 23:03:10 +00:00
parent 2bd6da4b00
commit 12ac57c877
8 changed files with 79 additions and 106 deletions
+6 -22
View File
@@ -140,27 +140,11 @@ fn walk_own_symbol_keys<H: VmHost>(vm: &H, obj: &JsObject) -> Vec<(u32, u32)> {
keys
}
/// 把 Symbol 键反解为对应的 Symbol 值:well-known 键还原为内置 symbol 对象,
/// 用户 symbol 键还原为 `JsValue::symbol` 值。
fn decode_symbol_key<H: VmHost>(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<H: VmHost>(vm: &H, key: u32) -> JsValue {
/// - 无自身 Symbol 键时返回空向量;数组元素区不含 Symbol 键。
///
/// # 注意事项
/// - 只读 shape 链,不分配对象;well-known symbol 值指向 session 内建 symbol 对象。
/// - 只读 shape 链,不分配对象;well-known symbol 物化为符号原语。
pub fn own_symbol_key_values<H: VmHost>(vm: &H, obj: &JsObject) -> Vec<JsValue> {
walk_own_symbol_keys(vm, obj)
.iter()
.map(|(key, _)| decode_symbol_key(vm, *key))
.map(|(key, _)| decode_symbol_key(*key))
.collect()
}
+17 -54
View File
@@ -45,61 +45,24 @@ pub fn bind_symbol(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
oxide_builtins::symbol::symbol_description_getter::<crate::vm::Vm> 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::<crate::vm::Vm>, 1, hash: true);
+6 -7
View File
@@ -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)?
+16 -4
View File
@@ -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));
+7 -4
View File
@@ -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<bool, String> {
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)?;
+14 -2
View File
@@ -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<Option<String>>,
@@ -107,7 +112,8 @@ impl SymbolState {
pub(crate) fn intern(&mut self, description: Option<String>) -> 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<String> {
@@ -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();
@@ -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]