fix: IC 快速路径 symbol 原始值走 Symbol.prototype 链
This commit is contained in:
@@ -24,11 +24,17 @@ pub fn symbol_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
}
|
||||
|
||||
// 描述符按规范 ToString(description):对象经 ToPrimitive(string hint) 强制转换,
|
||||
// Symbol 抛 TypeError,对象方法抛出的原始异常原样传播。
|
||||
// 描述符按规范 ToString(description):缺省参数与 undefined 对应无描述
|
||||
// ([[Description]] 为 undefined,`symbol.description` 返回 undefined);
|
||||
// 对象经 ToPrimitive(string hint) 强制转换,Symbol 抛 TypeError,
|
||||
// 对象方法抛出的原始异常原样传播。
|
||||
let description = if args.len() > 1 {
|
||||
match oxide_runtime_api::to_string_full(vm.reg(args[1]), vm) {
|
||||
Ok(s) => s,
|
||||
let desc_val = vm.reg(args[1]);
|
||||
if desc_val.is_undefined() {
|
||||
None
|
||||
} else {
|
||||
match oxide_runtime_api::to_string_full(desc_val, vm) {
|
||||
Ok(s) => Some(s),
|
||||
Err(_) => {
|
||||
if let Some(exc) = vm.take_uncaught_value() {
|
||||
return NativeResult::Err(exc);
|
||||
@@ -36,11 +42,12 @@ pub fn symbol_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
return NativeResult::Err(crate::error::create_type_error(vm, "Cannot convert value to a string"));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String::new()
|
||||
None
|
||||
};
|
||||
|
||||
let idx = vm.symbol_intern(Some(description));
|
||||
let idx = vm.symbol_intern(description);
|
||||
NativeResult::Ok(JsValue::symbol(idx))
|
||||
}
|
||||
|
||||
@@ -96,6 +103,22 @@ pub fn symbol_value_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// `Symbol.prototype.description` getter:返回该 Symbol 的 description 字符串;
|
||||
/// 无 description 的 Symbol 返回 undefined。this 必须是 Symbol 或 Symbol 包装对象,
|
||||
/// 否则抛 TypeError(规范 `SymbolDescriptiveString` 的 thisSymbolValue 校验)。
|
||||
pub fn symbol_description_getter<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let sym = match this_symbol_value(vm, this_val) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return NativeResult::Err(e),
|
||||
};
|
||||
let idx = sym.as_symbol_index();
|
||||
match vm.symbol_description(idx) {
|
||||
Some(desc) => NativeResult::Ok(vm.new_string_owned(desc.to_string())),
|
||||
None => NativeResult::Ok(JsValue::undefined()),
|
||||
}
|
||||
}
|
||||
|
||||
/// `Symbol.for(key)`:在全局 symbol 注册表中查找并返回同 key 的 Symbol;
|
||||
/// 不存在则新建并登记。同 key 的 Symbol 在全局唯一。
|
||||
pub fn symbol_for<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
|
||||
@@ -729,7 +729,6 @@ fn classify_vm_error(e: &str, neg: Option<&Negative>, no_skip: bool) -> TestOutc
|
||||
|| e.contains("unsupported")
|
||||
|| e.contains("step limit")
|
||||
|| e.contains("NEW_EXPRESSION")
|
||||
|| e.contains("IC_GET_PROP on non-object")
|
||||
|| e.contains("GET_PROP_DYNAMIC on non-object")
|
||||
|| e.contains("SET_PROP_DYNAMIC on non-object")
|
||||
|| e.contains("private field brand check")
|
||||
@@ -1634,6 +1633,7 @@ mod tests {
|
||||
"TypeError: Cannot convert object to primitive value",
|
||||
"TypeError: Cannot create property on non-object",
|
||||
"TypeError: Property description must be an object",
|
||||
"IC_GET_PROP on non-object",
|
||||
] {
|
||||
assert_outcome(e, None, false, &TestOutcome::Fail("".into()));
|
||||
}
|
||||
@@ -1647,7 +1647,6 @@ mod tests {
|
||||
"feature is not supported",
|
||||
"unsupported syntax",
|
||||
"vm error: NEW_EXPRESSION not supported",
|
||||
"IC_GET_PROP on non-object",
|
||||
"GET_PROP_DYNAMIC on non-object",
|
||||
"SET_PROP_DYNAMIC on non-object",
|
||||
"private field brand check",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::bindings::{apply_binding_table, configure_native_constructor};
|
||||
use crate::bindings::{apply_binding_table, bind_accessor_getter, configure_native_constructor};
|
||||
use oxide_kernel::kernel::{KernelCore, KernelSession};
|
||||
use oxide_types::object::JsObject;
|
||||
use oxide_types::value::JsValue;
|
||||
@@ -36,6 +36,15 @@ pub fn bind_symbol(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
|
||||
],
|
||||
);
|
||||
|
||||
// description 是访问器 getter(Symbol 原始值成员访问经 Symbol.prototype 链命中)。
|
||||
bind_accessor_getter(
|
||||
core,
|
||||
session,
|
||||
proto,
|
||||
"description",
|
||||
oxide_builtins::symbol::symbol_description_getter::<crate::vm::Vm> as *const (),
|
||||
);
|
||||
|
||||
for (name, val) in [
|
||||
(
|
||||
"match",
|
||||
|
||||
@@ -70,6 +70,11 @@ impl Vm {
|
||||
let proto = unsafe { &*proto_ptr };
|
||||
return self.ordinary_get(proto, prop_name_si, val).map(Some);
|
||||
}
|
||||
if val.is_symbol() {
|
||||
let proto_ptr = self.session.builtin_world().symbol_proto.as_ptr() as *mut JsObject;
|
||||
let proto = unsafe { &*proto_ptr };
|
||||
return self.ordinary_get(proto, prop_name_si, val).map(Some);
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
|
||||
@@ -283,3 +283,69 @@ fn symbol_key_read_via_get_own_property_symbols_index() {
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
// --- Symbol 原始值成员访问走 Symbol.prototype 链 ---
|
||||
|
||||
#[test]
|
||||
fn symbol_primitive_to_string_via_symbol_proto() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "Symbol('66').toString()").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "Symbol(66)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbol_primitive_value_of_via_symbol_proto() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "var s = Symbol('x'); s.valueOf() === s").unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbol_primitive_chain_reaches_object_proto_method() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "Symbol('x').hasOwnProperty('description')").unwrap();
|
||||
assert!(!result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbol_primitive_description_getter() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "Symbol('desc').description").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "desc");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbol_without_description_returns_undefined() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "Symbol().description").unwrap();
|
||||
assert!(result.is_undefined());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbol_wrapper_object_description_unboxes() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "Object(Symbol('w')).description").unwrap();
|
||||
assert_eq!(to_str(&vm, result), "w");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbol_description_non_symbol_receiver_throws_type_error() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"try { Object.getOwnPropertyDescriptor(Symbol.prototype, 'description').get.call(42); false } catch (e) { e instanceof TypeError }",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbol_proto_direct_description_access_throws_type_error() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"try { Symbol.prototype.description; false } catch (e) { e instanceof TypeError }",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
@@ -181,3 +181,18 @@ fn primitive_receiver_skips_ic_ext_words() {
|
||||
);
|
||||
assert_eq!(r, "303", "字符串 length 经 primitive 分支返回 3,扩展字跳越无错位");
|
||||
}
|
||||
|
||||
/// 非对象 receiver(symbol)经 IC primitive 分支走 Symbol.prototype 链:
|
||||
/// toString/description 均经原型解析,扩展字跳越无错位。
|
||||
#[test]
|
||||
fn symbol_primitive_receiver_walks_symbol_proto() {
|
||||
let (r, _hits, _misses) = run_once(
|
||||
r#"var s = Symbol('x');
|
||||
var ok = 0;
|
||||
for (var i = 0; i < 100; i++) {
|
||||
ok += (s.toString() === 'Symbol(x)') ? 1 : 0;
|
||||
}
|
||||
ok + ((s.description === 'x') ? 1 : 0)"#,
|
||||
);
|
||||
assert_eq!(r, "101", "symbol 原始值 toString/description 经 Symbol.prototype 解析");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user