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
+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> {