mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: 知名符号改为原语值表示
This commit is contained in:
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)?
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user