feat: 九原型新增 toSource 遗留扩展方法

This commit is contained in:
2026-10-09 11:41:50 +00:00
parent 0fc105243d
commit 11903fc520
14 changed files with 375 additions and 22 deletions
+4 -1
View File
@@ -67,7 +67,10 @@ pub fn bind_array(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
session.builtin_world(),
array_proto,
core,
&[("toString", oxide_builtins::array::array_to_string::<crate::vm::Vm> as *const (), 0)],
&[
("toString", oxide_builtins::array::array_to_string::<crate::vm::Vm> as *const (), 0),
("toSource", oxide_builtins::to_source::to_source_array::<crate::vm::Vm> as *const (), 0),
],
);
// 内置原型方法不可枚举(否则会泄漏进 for-in)。
let si = core.perm_interner().intern("toString").0;
@@ -29,6 +29,7 @@ pub fn bind_boolean(core: &Arc<KernelCore>, session: &KernelSession, global: &mu
oxide_builtins::boolean::boolean_prototype_to_string::<crate::vm::Vm> as *const (),
0,
),
("toSource", oxide_builtins::to_source::to_source_boolean::<crate::vm::Vm> as *const (), 0),
],
);
@@ -139,6 +139,7 @@ pub fn bind_date(core: &Arc<KernelCore>, session: &KernelSession, global: &mut J
0,
),
("valueOf", oxide_builtins::date::date_value_of::<crate::vm::Vm> as *const (), 0),
("toSource", oxide_builtins::to_source::to_source_date::<crate::vm::Vm> as *const (), 0),
],
);
@@ -135,6 +135,20 @@ pub fn bind_error(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
);
}
// toSource 遗留扩展(Error 原型方法不走 kernel 侧 bind_error_methods,
// 此处独立经 apply_binding_table 安装,描述符非枚举由绑定器内置)。
{
let proto_ptr = session.builtin_world().error_proto.as_ptr() as *mut JsObject;
// SAFETY: error_proto 由 session 持有,存活整个 session;本块内只改其 shape/属性区,无 reset。
let proto = unsafe { &mut *proto_ptr };
super::apply_binding_table(
session.builtin_world(),
proto,
core,
&[("toSource", oxide_builtins::to_source::to_source_error::<crate::vm::Vm> as *const (), 0)],
);
}
// 全局 Error 槽位既有槽原位更新(旧家族构造器指针不得滞留在属性 vec),
// 无槽时开新槽;描述符非枚举(规范 { writable:true, enumerable:false, configurable:true })。
let si_err = core.perm_interner().intern("Error").0;
@@ -6,6 +6,7 @@ use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::{JsObject, NativeFnPtr, PropAttributes};
use oxide_types::value::JsValue;
use super::apply_binding_table;
use super::bind_global_value;
use super::configure_native_constructor;
@@ -66,6 +67,15 @@ pub fn bind_function(core: &Arc<KernelCore>, session: &KernelSession, global: &m
// 函数对象,任何访问抛 TypeError(AddRestrictedFunctionProperties 语义)。
bind_function_proto_restricted(core, session, proto);
// toSource 遗留扩展(Function 原型方法不走 kernel 侧 bind_function_methods,
// 此处独立经 apply_binding_table 安装,描述符非枚举由绑定器内置)。
apply_binding_table(
session.builtin_world(),
proto,
core,
&[("toSource", oxide_builtins::to_source::to_source_function::<crate::vm::Vm> as *const (), 0)],
);
bind_global_value(core, global, "Function", JsValue::from_js_object(function_ctor));
}
@@ -59,6 +59,7 @@ pub fn bind_number(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
0,
),
("valueOf", oxide_builtins::number::number_value_of::<crate::vm::Vm> as *const (), 0),
("toSource", oxide_builtins::to_source::to_source_number::<crate::vm::Vm> as *const (), 0),
],
);
@@ -86,6 +86,7 @@ pub fn bind_object(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
oxide_builtins::object::object_proto_lookup_setter::<crate::vm::Vm> as *const (),
1,
),
("toSource", oxide_builtins::to_source::to_source_object::<crate::vm::Vm> as *const (), 0),
],
);
// 内置原型方法不可枚举(否则会泄漏进 for-in)。
@@ -98,6 +99,7 @@ pub fn bind_object(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
"__defineSetter__",
"__lookupGetter__",
"__lookupSetter__",
"toSource",
] {
let si = core.perm_interner().intern(name).0;
if let Some(pos) = core.shape_forge().lookup_position(object_proto.shape_id(), si) {
@@ -38,6 +38,7 @@ pub fn bind_regexp(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
("test", oxide_builtins::regexp::regexp_test::<crate::vm::Vm> as *const (), 1),
("toString", oxide_builtins::regexp::regexp_to_string::<crate::vm::Vm> as *const (), 0),
("compile", oxide_builtins::regexp::regexp_compile::<crate::vm::Vm> as *const (), 2),
("toSource", oxide_builtins::to_source::to_source_regexp::<crate::vm::Vm> as *const (), 0),
],
);
+4 -1
View File
@@ -86,7 +86,10 @@ pub fn bind_string(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
session.builtin_world(),
proto,
core,
&[("toString", oxide_builtins::string::string_to_string::<crate::vm::Vm> as *const (), 0)],
&[
("toString", oxide_builtins::string::string_to_string::<crate::vm::Vm> as *const (), 0),
("toSource", oxide_builtins::to_source::to_source_string::<crate::vm::Vm> as *const (), 0),
],
);
// String.prototype[@@iterator]:逐 code point 迭代字符;name 属性按规范
// 落 "[Symbol.iterator]"。
@@ -0,0 +1,142 @@
//! toSource 遗留扩展(九原型 own 方法与 null/undefined this 门禁)单测。
//!
//! 验收面:九原型 own toSource 存在性、描述符非枚举、null/undefined/de-Reference
//! 三形态门禁抛 TypeError、返回值形态抽查。
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
let allocator = oxide_parser::Allocator::default();
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
vm.run(&Arc::new(module))
}
fn to_str(vm: &Vm, val: JsValue) -> String {
vm.lookup_str(val).unwrap_or_default()
}
const CLASSES: [&str; 9] = ["Object", "Function", "Array", "String", "Boolean", "Number", "Date", "RegExp", "Error"];
// 九原型 own toSource 存在且为函数。
#[test]
fn tosource_own_exists_on_nine_prototypes() {
let mut vm = Vm::new();
for cls in CLASSES {
let source = format!("typeof {}.prototype.toSource", cls);
let result = eval(&mut vm, &source).unwrap();
assert_eq!(to_str(&vm, result), "function", "toSource missing on {cls}");
}
}
// 九原型 toSource 描述符 { writable:true, enumerable:false, configurable:true }。
#[test]
fn tosource_descriptor_non_enumerable() {
let mut vm = Vm::new();
for cls in CLASSES {
let source = format!(
"(() => {{ var d = Object.getOwnPropertyDescriptor({}.prototype, 'toSource'); return d.writable + ',' + d.enumerable + ',' + d.configurable; }})()",
cls
);
let result = eval(&mut vm, &source).unwrap();
assert_eq!(to_str(&vm, result), "true,false,true", "descriptor wrong on {cls}");
}
}
// 九原型 toSource.call(null) 抛 TypeError(门禁核)。
#[test]
fn tosource_rejects_null_this() {
let mut vm = Vm::new();
for cls in CLASSES {
let source = format!(
"(() => {{ try {{ {}.prototype.toSource.call(null); return 'no-throw'; }} catch (e) {{ return e instanceof TypeError ? 'TypeError' : 'other'; }} }})()",
cls
);
let result = eval(&mut vm, &source).unwrap();
assert_eq!(to_str(&vm, result), "TypeError", "null this not rejected on {cls}");
}
}
// 九原型 toSource.call(undefined) 抛 TypeError(门禁核)。
#[test]
fn tosource_rejects_undefined_this() {
let mut vm = Vm::new();
for cls in CLASSES {
let source = format!(
"(() => {{ try {{ {}.prototype.toSource.call(undefined); return 'no-throw'; }} catch (e) {{ return e instanceof TypeError ? 'TypeError' : 'other'; }} }})()",
cls
);
let result = eval(&mut vm, &source).unwrap();
assert_eq!(to_str(&vm, result), "TypeError", "undefined this not rejected on {cls}");
}
}
// 九原型 (0, X.prototype.toSource)() de-Reference 形态抛 TypeError。
#[test]
fn tosource_rejects_dereference_call() {
let mut vm = Vm::new();
for cls in CLASSES {
let source = format!(
"(() => {{ try {{ (0, {}.prototype.toSource)(); return 'no-throw'; }} catch (e) {{ return e instanceof TypeError ? 'TypeError' : 'other'; }} }})()",
cls
);
let result = eval(&mut vm, &source).unwrap();
assert_eq!(to_str(&vm, result), "TypeError", "de-Reference not rejected on {cls}");
}
}
// 返回值形态抽查:Object/Array/Boolean/Number/String 五个最小合理形。
#[test]
fn tosource_return_value_forms() {
let mut vm = Vm::new();
let result = eval(&mut vm, "Object.prototype.toSource.call({})").unwrap();
assert_eq!(to_str(&vm, result), "{}");
let result = eval(&mut vm, "Array.prototype.toSource.call([])").unwrap();
assert_eq!(to_str(&vm, result), "[]");
let result = eval(&mut vm, "Boolean.prototype.toSource.call(true)").unwrap();
assert_eq!(to_str(&vm, result), "true");
let result = eval(&mut vm, "Number.prototype.toSource.call(42)").unwrap();
assert_eq!(to_str(&vm, result), "42");
let result = eval(&mut vm, "String.prototype.toSource.call('hi')").unwrap();
assert_eq!(to_str(&vm, result), "\"hi\"");
}
// Function 臂:引擎无函数源文本,恒返空串。
#[test]
fn tosource_function_returns_empty() {
let mut vm = Vm::new();
let result = eval(&mut vm, "Function.prototype.toSource.call(function(){})").unwrap();
assert_eq!(to_str(&vm, result), "");
}
// RegExp 臂:/source/flags 斜杠形。
#[test]
fn tosource_regexp_slash_form() {
let mut vm = Vm::new();
let result = eval(&mut vm, "RegExp.prototype.toSource.call(/ab+c/i)").unwrap();
assert_eq!(to_str(&vm, result), "/ab+c/i");
}
// Error 臂:new Error("message") 构造形。
#[test]
fn tosource_error_construction_form() {
let mut vm = Vm::new();
let result = eval(&mut vm, "Error.prototype.toSource.call(new Error('boom'))").unwrap();
assert_eq!(to_str(&vm, result), "new Error(\"boom\")");
}
// Date 臂:new Date(<ms>) 构造形。
#[test]
fn tosource_date_construction_form() {
let mut vm = Vm::new();
let result = eval(&mut vm, "Date.prototype.toSource.call(new Date(1234))").unwrap();
assert_eq!(to_str(&vm, result), "new Date(1234)");
}