From a9118200ce78c57a2c13beb436057b2fc4d315ac Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Mon, 14 Sep 2026 15:05:41 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=B5=8B=E8=AF=95=E5=AE=BF=E4=B8=BB=20e?= =?UTF-8?q?valScript=20=E6=94=B9=E8=B5=B0=E6=99=AE=E9=80=9A=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=E4=BD=9C=E7=94=A8=E5=9F=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_vm/src/test262_host.rs | 13 ++++--- crates/oxide_vm/src/vm.rs | 43 +++++++++++++++++++++ crates/oxide_vm/tests/test262_host_tests.rs | 11 ++++-- 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/crates/oxide_vm/src/test262_host.rs b/crates/oxide_vm/src/test262_host.rs index d5573a6f..d84b9ac7 100644 --- a/crates/oxide_vm/src/test262_host.rs +++ b/crates/oxide_vm/src/test262_host.rs @@ -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, &[]) { diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index ca4360e0..25adf68a 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -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 { + let allocator = oxide_parser::Allocator::default(); + let program = oxide_parser::parse(&allocator, code) + .map_err(|errs| errs.into_iter().map(|e| e.message).collect::>().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`, diff --git a/crates/oxide_vm/tests/test262_host_tests.rs b/crates/oxide_vm/tests/test262_host_tests.rs index 642a77c5..604b7f99 100644 --- a/crates/oxide_vm/tests/test262_host_tests.rs +++ b/crates/oxide_vm/tests/test262_host_tests.rs @@ -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 捕获)。