test262: $262 宿主对象最小绑定 + is not defined 判定白名单化
引擎侧:绑定 $262 宿主对象(evalScript/global/gc 可用,detachArrayBuffer/ createRealm/agent 抛 not supported 而非 ReferenceError),并加入编译期 已知全局表;引用 $262.* 的测试从全量 skip 转为真实执行。 runner 侧:is not defined 不再一票吞 skip——解析错误消息中的标识符名, 仅在缺失全局白名单($262/structuredClone/queueMicrotask)内才按能力 缺失跳过;其余 is not defined 按真实失败计入,修复后应转 PASS 的 测试恢复可见。
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
//! test262 宿主对象 `$262` 的最小实现。
|
||||
//!
|
||||
//! `$262` 是 test262 套件约定的宿主注入对象(非 ECMAScript 标准全局):
|
||||
//! 测试经它访问当前 realm 全局、执行脚本、触发 GC 等。最小绑定使引用
|
||||
//! `$262.*` 的测试真实执行而非一律抛 `ReferenceError` 被 runner 吞为 skip;
|
||||
//! 未实现的方法抛 `not supported` 错误,runner 按能力缺失归类。
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use oxide_kernel::kernel::{KernelCore, KernelSession};
|
||||
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||||
use oxide_runtime_api::{to_string_full, NativeResult};
|
||||
use oxide_types::object::JsObject;
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
use crate::bindings::{apply_binding_table, bind_global_value};
|
||||
use crate::vm::Vm;
|
||||
|
||||
/// `$262` 未实现方法统一抛错:消息带 `not supported`,runner 据此按能力缺失跳过。
|
||||
fn not_supported(vm: &mut Vm, feature: &str) -> NativeResult {
|
||||
NativeResult::Err(oxide_builtins::error::create_type_error(vm, &format!("{feature} is not supported")))
|
||||
}
|
||||
|
||||
/// `$262.evalScript(code)`:在当前 realm 编译执行一段脚本并返回其完成值。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 实参 ToString 取脚本源码。
|
||||
/// 2. 经动态函数构造编译执行(`create_dynamic_function` + 同步调用)。
|
||||
/// 3. 编译失败转 SyntaxError;运行异常原样透传异常值。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 最小实现:函数作用域语义——var/函数声明不泄漏到全局,完成值仅限显式
|
||||
/// `return`(全局作用域 eval 落地前为已知上限)。
|
||||
/// - 无实参或非字符串实参按 ToString 规范处理。
|
||||
pub fn eval_script(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
if args.len() < 2 {
|
||||
return NativeResult::Ok(JsValue::undefined());
|
||||
}
|
||||
let code = match to_string_full(vm.reg(args[1]), vm) {
|
||||
Ok(s) => s,
|
||||
Err(e) => return NativeResult::Err(oxide_builtins::error::create_from_text(vm, &e)),
|
||||
};
|
||||
match vm.create_dynamic_function(&[], &code) {
|
||||
Ok(func) => {
|
||||
let global = JsValue::from_js_object(vm.session().global_object().as_ptr() as *mut JsObject);
|
||||
match vm.call_function_sync(func, global, &[]) {
|
||||
Ok(val) => NativeResult::Ok(val),
|
||||
Err(e) => NativeResult::Err(oxide_builtins::error::create_from_text(vm, &e)),
|
||||
}
|
||||
}
|
||||
Err(e) => NativeResult::Err(oxide_builtins::error::create_from_text(
|
||||
vm,
|
||||
&format!("SyntaxError: {e}"),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// `$262.detachArrayBuffer(buf)`:ArrayBuffer detach 未实现,抛能力缺失错误。
|
||||
pub fn detach_array_buffer(vm: &mut Vm, _args: &[u8]) -> NativeResult {
|
||||
not_supported(vm, "detachArrayBuffer")
|
||||
}
|
||||
|
||||
/// `$262.createRealm()`:跨 realm 未支持,抛能力缺失错误。
|
||||
pub fn create_realm(vm: &mut Vm, _args: &[u8]) -> NativeResult {
|
||||
not_supported(vm, "createRealm")
|
||||
}
|
||||
|
||||
/// `$262.gc()`:请求一次 session 全量回收(不计返回值)。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 执行 mark-sweep 搬移存活对象并释放不可达对象,会重写 session 对象指针。
|
||||
pub fn gc(vm: &mut Vm, _args: &[u8]) -> NativeResult {
|
||||
let mut session_gc = std::mem::take(&mut vm.gc_state.session_gc);
|
||||
session_gc.sweep(vm);
|
||||
vm.gc_state.session_gc = session_gc;
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
/// `$262.agent`:agent 线程接口依赖 SharedArrayBuffer 族,未支持,抛能力缺失错误。
|
||||
pub fn agent(vm: &mut Vm, _args: &[u8]) -> NativeResult {
|
||||
not_supported(vm, "agent")
|
||||
}
|
||||
|
||||
/// 把 `$262` 宿主对象绑定到 global:对象本体 + 标准方法 + `global` 数据属性。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 建 `$262` 普通对象(Object.prototype)。
|
||||
/// 2. 绑定 evalScript/detachArrayBuffer/createRealm/gc/agent 方法。
|
||||
/// 3. `global` 数据属性指向当前 session global;`$262` 本体挂到 global。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// - 宿主对象经 `Box::into_raw` 持有,随 session 生命周期存活(与 Reflect/Iterator
|
||||
/// 等全局对象同一约定);每次调用新建对象,重复绑定会泄漏旧对象(全量初始化
|
||||
/// 与 full_reset 频率低,可接受)。
|
||||
pub fn bind_test262_host(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
|
||||
let object_proto = session.builtin_world().object_proto.as_ptr() as *mut JsObject;
|
||||
let mut host = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto));
|
||||
apply_binding_table(
|
||||
session.builtin_world(),
|
||||
&mut host,
|
||||
core,
|
||||
&[
|
||||
("evalScript", eval_script as *const (), 1),
|
||||
("detachArrayBuffer", detach_array_buffer as *const (), 1),
|
||||
("createRealm", create_realm as *const (), 0),
|
||||
("gc", gc as *const (), 0),
|
||||
("agent", agent as *const (), 0),
|
||||
],
|
||||
);
|
||||
let global_this = JsValue::from_js_object(global as *mut JsObject);
|
||||
bind_global_value(core, &mut host, "global", global_this);
|
||||
bind_global_value(core, global, "$262", JsValue::from_js_object(Box::into_raw(Box::new(host))));
|
||||
}
|
||||
Reference in New Issue
Block a user