mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: 测试宿主 evalScript 改走普通脚本作用域
This commit is contained in:
@@ -21,16 +21,19 @@ 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 编译执行一段脚本并返回其完成值。
|
||||
/// `$262.evalScript(code)`:在当前 realm 编译执行一段普通脚本并返回其完成值。
|
||||
///
|
||||
/// 按 test262 harness 规范 evalScript 即当前 realm 的 script(非 eval):
|
||||
/// 顶层 var/function 声明落全局对象,完成值为末条语句的正常完成值。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 实参 ToString 取脚本源码。
|
||||
/// 2. 经动态函数构造编译执行(`create_dynamic_function` + 同步调用)。
|
||||
/// 2. 经普通脚本动态编译(`create_plain_dynamic_script` + 同步调用)。
|
||||
/// 3. 编译失败转 SyntaxError;运行异常原样透传异常值。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 最小实现:函数作用域语义——var/函数声明不泄漏到全局,完成值仅限显式
|
||||
/// `return`(全局作用域 eval 落地前为已知上限)。
|
||||
/// - 普通脚本语义:var/函数声明落全局对象(configurable:false),let/const
|
||||
/// 落全局词法环境,完成值保留(`evalScript('var y = 1; y')` → 1)。
|
||||
/// - 无实参或非字符串实参按 ToString 规范处理。
|
||||
pub fn eval_script(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
if args.len() < 2 {
|
||||
@@ -42,7 +45,7 @@ pub fn eval_script(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
};
|
||||
// 动态路径源契约:源码域转义形态传源(见 `create_dynamic_function`)。
|
||||
let code = oxide_kernel::string_forge::source_escape(&units);
|
||||
match vm.create_dynamic_function(&[], &code) {
|
||||
match vm.create_plain_dynamic_script(&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, &[]) {
|
||||
|
||||
@@ -2821,6 +2821,49 @@ impl Vm {
|
||||
table.immutables.resize(table.modules.len(), OnceLock::new());
|
||||
Ok(self.create_function_object(base, self.current_gen, false, false, false, false))
|
||||
}
|
||||
|
||||
/// 动态编译脚本(普通脚本模式):顶层 var/function 声明落全局对象
|
||||
/// configurable:false,与静态脚本顶层一致(区别于 [`create_dynamic_script`]
|
||||
/// 的 eval 脚本 configurable:true 面)。
|
||||
///
|
||||
/// 供 test262 宿主 `$262.evalScript` 使用:按 test262 harness 规范它是
|
||||
/// 当前 realm 的一段普通 script,不是 eval。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. parse(脚本模式)→ compile(emit_program 置 is_global_scope=true,
|
||||
/// 不设 is_eval_script,与普通脚本同一编译臂)。
|
||||
/// 2. 整棵模块树追加进平表:同 [`create_dynamic_script`]
|
||||
/// (`rehome_subtree(&module, base+1)`,根落 base、子函数 old→base+old)。
|
||||
/// 3. 扩容当前代际的常量缓存,建函数对象(sub_module_index = base)返回。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 编译或解析失败返回 `Err`(由宿主层转 SyntaxError)。
|
||||
/// - 动态模块在函数对象存活期间跨 run 有效(同 create_dynamic_function)。
|
||||
/// - 返回函数对象仅供内部同步调用,不设 name/length(用户不可见)。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 扩展当前代际平表(`tables[current_gen]`)与常量缓存。
|
||||
pub fn create_plain_dynamic_script(&mut self, code: &str) -> Result<JsValue, String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program = oxide_parser::parse(&allocator, code)
|
||||
.map_err(|errs| errs.into_iter().map(|e| e.message).collect::<Vec<_>>().join("\n"))?;
|
||||
// 普通脚本:顶层 var/function 声明落全局属性 configurable:false,
|
||||
// let/const 落全局词法环境。动态路径源契约同 `create_dynamic_function`
|
||||
// (源码域转义形态传源)。
|
||||
let module = oxide_compiler::compiler::Compiler::new()
|
||||
.with_source_encoded(true)
|
||||
.compile(&program)?;
|
||||
// 根模块 flat_id=0 传 base+1,重编号后落 base(避开 sub_module_index()==0
|
||||
// 守卫)。make_mut 彼时平表 Arc 强引用唯一持有者是本表,原地扩展不分叉。
|
||||
let table = self.current_table_mut();
|
||||
let base = table.modules.len() as u32;
|
||||
let mut added = Vec::new();
|
||||
rehome_subtree(&module, base + 1, &mut added);
|
||||
Arc::make_mut(&mut table.modules).extend(added);
|
||||
// 平表变长后同步扩容常量缓存,否则激活新模块常量时越界 panic。
|
||||
table.immutables.resize(table.modules.len(), OnceLock::new());
|
||||
Ok(self.create_function_object(base, self.current_gen, false, false, false, false))
|
||||
}
|
||||
}
|
||||
|
||||
/// 把 flatten 后的子模块子树重编号到平表偏移 `base`:DFS 前序拷贝进 `out`,
|
||||
|
||||
@@ -41,11 +41,16 @@ fn host_methods_are_callable() {
|
||||
assert_eq!(eval("typeof $262.createRealm"), "function");
|
||||
}
|
||||
|
||||
/// evalScript 执行脚本并返回显式 `return` 的完成值;无 return 为 undefined。
|
||||
/// evalScript 以普通脚本执行并返回完成值:末条语句的正常完成值;顶层
|
||||
/// `return` 在 Script goal 下是编译期 SyntaxError。
|
||||
#[test]
|
||||
fn eval_script_runs_code_and_returns_value() {
|
||||
assert_eq!(eval("$262.evalScript('return 40 + 2')"), "42");
|
||||
assert_eq!(eval("$262.evalScript('var y = 1; y')"), "undefined");
|
||||
// 普通脚本语义:顶层语句的正常完成值即脚本完成值(var 声明语句本身完成值为
|
||||
// undefined,末条表达式语句 `y` 完成值为 1)。
|
||||
assert_eq!(eval("$262.evalScript('var y = 1; y')"), "1");
|
||||
// Script goal 顶层 return 非法:编译期错误转 JS 层可捕获的 SyntaxError。
|
||||
let r = eval("try { $262.evalScript('return 40 + 2'); 'no-throw' } catch (e) { e.name }");
|
||||
assert_eq!(r, "SyntaxError");
|
||||
}
|
||||
|
||||
/// evalScript 编译失败转 SyntaxError(可由 JS catch 捕获)。
|
||||
|
||||
Reference in New Issue
Block a user