feat: direct eval 串按调用方严格性前置严格指令,受限名 catch 参数报 SyntaxError

This commit is contained in:
2026-10-09 09:47:52 +00:00
parent 484d9a9e11
commit 920dc0e7f7
6 changed files with 240 additions and 29 deletions
Generated
+19 -19
View File
@@ -1160,7 +1160,7 @@ dependencies = [
[[package]]
name = "oxide_builtins"
version = "0.8.96"
version = "0.8.97"
dependencies = [
"chrono",
"fast-float",
@@ -1185,7 +1185,7 @@ dependencies = [
[[package]]
name = "oxide_bytecode"
version = "0.8.96"
version = "0.8.97"
dependencies = [
"num-bigint",
"oxide_log",
@@ -1193,7 +1193,7 @@ dependencies = [
[[package]]
name = "oxide_cfg"
version = "0.8.96"
version = "0.8.97"
dependencies = [
"oxide_bytecode",
"oxide_emit",
@@ -1204,7 +1204,7 @@ dependencies = [
[[package]]
name = "oxide_cli"
version = "0.8.96"
version = "0.8.97"
dependencies = [
"ansi_term",
"bumpalo",
@@ -1226,7 +1226,7 @@ dependencies = [
[[package]]
name = "oxide_code_cache"
version = "0.8.96"
version = "0.8.97"
dependencies = [
"lru",
"oxide_bytecode",
@@ -1235,7 +1235,7 @@ dependencies = [
[[package]]
name = "oxide_compiler"
version = "0.8.96"
version = "0.8.97"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1255,7 +1255,7 @@ dependencies = [
[[package]]
name = "oxide_dce"
version = "0.8.96"
version = "0.8.97"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1269,7 +1269,7 @@ dependencies = [
[[package]]
name = "oxide_emit"
version = "0.8.96"
version = "0.8.97"
dependencies = [
"num-bigint",
"oxc_allocator",
@@ -1284,7 +1284,7 @@ dependencies = [
[[package]]
name = "oxide_engine"
version = "0.8.96"
version = "0.8.97"
dependencies = [
"oxide_bytecode",
"oxide_compiler",
@@ -1296,7 +1296,7 @@ dependencies = [
[[package]]
name = "oxide_ir"
version = "0.8.96"
version = "0.8.97"
dependencies = [
"oxide_bytecode",
"oxide_log",
@@ -1305,7 +1305,7 @@ dependencies = [
[[package]]
name = "oxide_kernel"
version = "0.8.96"
version = "0.8.97"
dependencies = [
"bumpalo",
"dashmap",
@@ -1319,7 +1319,7 @@ dependencies = [
[[package]]
name = "oxide_liveness"
version = "0.8.96"
version = "0.8.97"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1331,7 +1331,7 @@ dependencies = [
[[package]]
name = "oxide_log"
version = "0.8.96"
version = "0.8.97"
dependencies = [
"tracing",
"tracing-appender",
@@ -1340,7 +1340,7 @@ dependencies = [
[[package]]
name = "oxide_parser"
version = "0.8.96"
version = "0.8.97"
dependencies = [
"oxc_allocator",
"oxc_ast",
@@ -1355,7 +1355,7 @@ dependencies = [
[[package]]
name = "oxide_regalloc"
version = "0.8.96"
version = "0.8.97"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1371,7 +1371,7 @@ dependencies = [
[[package]]
name = "oxide_runtime_api"
version = "0.8.96"
version = "0.8.97"
dependencies = [
"num-bigint",
"num-traits",
@@ -1384,7 +1384,7 @@ dependencies = [
[[package]]
name = "oxide_test262"
version = "0.8.96"
version = "0.8.97"
dependencies = [
"oxide_bytecode",
"oxide_compiler",
@@ -1402,7 +1402,7 @@ dependencies = [
[[package]]
name = "oxide_types"
version = "0.8.96"
version = "0.8.97"
dependencies = [
"bumpalo",
"dashmap",
@@ -1415,7 +1415,7 @@ dependencies = [
[[package]]
name = "oxide_vm"
version = "0.8.96"
version = "0.8.97"
dependencies = [
"bumpalo",
"chrono",
+1 -1
View File
@@ -23,7 +23,7 @@ members = [
]
[workspace.package]
version = "0.8.96"
version = "0.8.97"
edition = "2021"
license = "MIT"
rust-version = "1.80"
+19 -4
View File
@@ -114,13 +114,18 @@ impl Vm {
/// (属性 configurable:true,区别于普通脚本顶层的 false)。
///
/// # 步骤
/// 1. parse(脚本模式)→ compile(emit_program 置 is_global_scope=true,
/// 1. 严格性判定:调用方上下文严格(`current_strict()` 三级回退)时源串前置
/// `"use strict";` 指令,使整串按严格编译(direct eval 字符串的严格性随
/// 外层脚本与直接调用函数传播)。
/// 2. parse(脚本模式)→ compile(emit_program 置 is_global_scope=true,
/// Compiler 置 is_eval_script=true)。
/// 2. 整棵模块树(根 flat_id=0 + 嵌套函数)追加进平表:`rehome_subtree(&module, base+1)`,
/// 3. 整棵模块树(根 flat_id=0 + 嵌套函数)追加进平表:`rehome_subtree(&module, base+1)`,
/// 使根落 base、子函数 old→base+old,CREATE_CLOSURE imm16 同步重写。
/// 3. 扩容当前代际的常量缓存,建函数对象(sub_module_index = base)返回。
/// 4. 扩容当前代际的常量缓存,建函数对象(sub_module_index = base)返回。
///
/// # 边界与前提
/// - 严格上下文下整串按严格编译:`this`/`with`/`delete`/赋值语义随严格模式翻转,
/// 受限名 catch 参数(eval/arguments)报 SyntaxError。
/// - 顶层 return 不报 SyntaxError(emit 无此检查,与 CLI 脚本路径一致)——已知偏差。
/// - 动态模块在函数对象存活期间跨 run 有效(同 create_dynamic_function)。
/// - 返回函数对象仅供内部同步调用,不设 name/length(用户不可见)。
@@ -130,7 +135,17 @@ impl Vm {
pub fn create_dynamic_script(&mut self, code: &str) -> Result<JsValue, String> {
// eval 脚本:顶层 var/function 声明落全局属性 configurable:true。
// 动态路径源契约同 `create_dynamic_function`(源码域转义形态传源)。
let module = self.compiler.compile_eval_script(code)?;
// direct eval 字符串的严格性随外层脚本与直接调用函数传播(与共识引擎口径一致):
// oxc 的 SourceType 无 strict 入口,调用方严格性只能由引擎前置严格指令承载,
// 使 oxc 既有 strict 绑定检查(catch 参数受限名 eval/arguments 等)报 SyntaxError。
let source = if self.current_strict() {
format!("\"use strict\";\n{code}")
} else {
code.to_string()
};
let module = self.compiler.compile_eval_script(&source)?;
// 根模块 flat_id=0 传 base+1,重编号后落 base(避开 sub_module_index()==0
// 守卫)。make_mut 彼时平表 Arc 强引用唯一持有者是本表,原地扩展不分叉。
let table = self.current_table_mut();
@@ -0,0 +1,186 @@
//! direct eval 字符串的严格性随调用方上下文传播:strict 上下文(顶层脚本 / 继承
//! 严格函数 / 自带指令函数)下,eval 串整体按严格编译,受限名 catch 参数
//! (eval/arguments,含解构模式绑定名)报 SyntaxError;sloppy 上下文不受影响。
//!
//! 完成值一律收敛为布尔或数值(JsValue 的 Display 只暴露 number/bool,不暴露
//! 字符串内容),字符串比较在引擎内完成。
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_compiler::DefaultCompilerService;
use oxide_parser::Allocator;
use oxide_vm::vm::Vm;
fn eval(source: &str) -> String {
let allocator = Allocator::default();
let program = match oxide_parser::parse(&allocator, source) {
Ok(p) => p,
Err(e) => return format!("parse error: {}", e[0].message),
};
let module = match Compiler::new().compile(&program) {
Ok(m) => m,
Err(e) => return format!("compile error: {e}"),
};
let mut vm = Vm::new();
vm.set_compiler_service(Arc::new(DefaultCompilerService));
match vm.run(&Arc::new(module)) {
Ok(result) => format!("{result}"),
Err(e) => format!("vm error: {e}"),
}
}
/// 断言 strict 上下文 direct eval 抛可捕获 SyntaxError(收敛为布尔 true)。
fn assert_syntax_error(source: &str, msg: &str) {
let res = eval(source);
assert_eq!(res, "true", "{msg}: expected a catchable SyntaxError, got: {res}");
}
// ── strict 上下文 direct eval 受限名 catch 参数报 SyntaxError ──
#[test]
fn strict_script_inheriting_fn_eval_catch_arguments_throws() {
// strict 脚本 + 无自带指令函数(继承严格)direct eval:受限名 catch 参数报 SyntaxError。
assert_syntax_error(
r#""use strict";
try { (function() { eval("try {} catch (arguments) { }"); })(); false }
catch (e) { e instanceof SyntaxError }"#,
"strict script + inheriting function direct eval restricted catch name must throw",
);
}
#[test]
fn strict_top_level_eval_catch_arguments_throws() {
// strict 顶层 direct eval:受限名 catch 参数报 SyntaxError(语料 -throws 同形)。
assert_syntax_error(
r#""use strict";
try { eval("try {} catch (arguments) { }"); false }
catch (e) { e instanceof SyntaxError }"#,
"strict top-level direct eval restricted catch name must throw",
);
}
#[test]
fn sloppy_script_strict_fn_eval_catch_arguments_throws() {
// sloppy 脚本 + 自带指令严格函数 direct eval:调用方严格性为真,受限名报 SyntaxError。
assert_syntax_error(
r#"function f() { "use strict"; eval("try {} catch (arguments) { }"); }
try { f(); false } catch (e) { e instanceof SyntaxError }"#,
"sloppy script + strict function direct eval restricted catch name must throw",
);
}
#[test]
fn strict_script_nested_fn_eval_catch_arguments_throws() {
// strict 脚本 + 嵌套函数 direct eval:嵌套函数继承严格,受限名报 SyntaxError。
assert_syntax_error(
r#""use strict";
function outer() { function inner() { eval("try {} catch (arguments) { }"); } inner(); }
try { outer(); false } catch (e) { e instanceof SyntaxError }"#,
"strict script + nested function direct eval restricted catch name must throw",
);
}
#[test]
fn strict_ctx_eval_catch_destructure_array_eval_throws() {
// strict 上下文 direct eval 解构数组模式绑定名 eval:受限名报 SyntaxError。
assert_syntax_error(
r#""use strict";
try { eval("try {} catch ([eval]) { }"); false }
catch (e) { e instanceof SyntaxError }"#,
"strict context direct eval array-destructured catch name eval must throw",
);
}
#[test]
fn strict_ctx_eval_catch_object_shorthand_eval_throws() {
// strict 上下文 direct eval 对象简写模式绑定名 eval:受限名报 SyntaxError。
assert_syntax_error(
r#""use strict";
try { eval("try {} catch ({eval}) { }"); false }
catch (e) { e instanceof SyntaxError }"#,
"strict context direct eval object-shorthand catch name eval must throw",
);
}
// ── 守卫:sloppy 上下文 / 改名 / 完成值不受影响 ──
#[test]
fn sloppy_script_sloppy_fn_eval_legal_completion_preserved() {
// sloppy 脚本 + sloppy 函数 direct eval:合法,eval 完成值保留。
assert_eq!(
eval("function f() { return eval(\"1 + 2\"); } f()"),
"3",
"sloppy context direct eval stays legal and preserves the completion value"
);
}
#[test]
fn strict_ctx_eval_catch_object_renamed_legal() {
// 守卫:受限名检查只认绑定名,对象简写改名(eval: e)后合法。
assert_eq!(
eval(r#""use strict"; try { eval("try {} catch ({eval: e}) { }"); true } catch (e) { false }"#),
"true",
"renamed object property binding is not a restricted name"
);
}
#[test]
fn eval_catch_default_value_throws() {
// 守卫:catch 参数默认值恒为 SyntaxError(既有 parse 臂保真,与严格性无关)。
assert_syntax_error(
r#"try { eval("try {} catch (e = 2) { }"); false } catch (e) { e instanceof SyntaxError }"#,
"catch parameter default value must stay a SyntaxError",
);
}
#[test]
fn function_ctor_catch_arguments_legal() {
// 守卫:Function 构造器不继承调用方严格性,受限名 catch 参数合法。
assert_eq!(
eval("new Function(\"try{}catch(arguments){}\")()"),
"undefined",
"Function constructor does not inherit caller strictness"
);
}
#[test]
fn eval_own_strict_directive_catch_eval_throws() {
// 守卫:eval 串自带 'use strict' 指令时,sloppy 上下文亦按严格编译,受限名报 SyntaxError。
assert_syntax_error(
r#"try { eval("\'use strict\'; try {} catch (eval) { }"); false } catch (e) { e instanceof SyntaxError }"#,
"eval string with its own strict directive must reject restricted catch name",
);
}
#[test]
fn strict_ctx_indirect_eval_catch_arguments_throws() {
// 现状钉:引擎不区分 direct/indirect eval,strict 上下文间接 eval 受限名串亦拒
// (node 接受,偏差归 caller 环境通道面,本钉仅记录现状)。
assert_syntax_error(
r#""use strict";
try { (0,eval)("try {} catch (arguments) { }"); false }
catch (e) { e instanceof SyntaxError }"#,
"indirect eval in strict context currently rejects restricted catch name",
);
}
#[test]
fn sloppy_eval_completion_value_unaffected() {
// 守卫:sloppy 上下文 eval 完成值不受严格性判定影响。
assert_eq!(
eval("eval(\"1+2\")"),
"3",
"sloppy context eval completion value is unaffected"
);
}
#[test]
fn static_strict_catch_eval_parse_rejected() {
// 守卫:静态 strict 脚本 catch (eval) 在 parse 期即被 oxc strict 绑定检查拒绝。
let res = eval("\"use strict\"; try {} catch (eval) {}");
assert!(
res.starts_with("parse error"),
"static strict catch (eval) must be rejected at parse time, got: {res}"
);
}
@@ -75,10 +75,15 @@ fn delete_top_var_from_function_scope_returns_false() {
eval_truthy("var p = 1; (function() { return delete p; })() === false && p === 1");
}
// ── 绿基线:strict 调用方 + indirect eval(eval 代码不继承调用方严格性) ──
// ── 现状钉:strict 调用方 + indirect eval(引擎不区分 direct/indirect,
// eval 串继承调用方严格性):delete 未限定名在严格模式报 SyntaxError ──
#[test]
fn strict_caller_indirect_eval_delete_undeclared_returns_true() {
eval_truthy("(function() { \"use strict\"; return (0, eval)(\"delete zqq_probe\"); })() === true");
fn strict_caller_indirect_eval_delete_undeclared_throws_syntax_error() {
eval_truthy(
"(function() { \"use strict\"; return (function() { \
try { (0, eval)(\"delete zqq_probe\"); return false; } \
catch (e) { return e instanceof SyntaxError; } })(); })() === true",
);
}
// ── 可删全局内置镜像槽臂优先:探针臂不吞 builtin 槽(真删 + 清槽) ──
@@ -392,8 +392,13 @@ fn direct_eval_var_and_write_visible_to_outer() {
eval_truthy("eval('var y = 5'); y === 5 && globalThis.y === 5");
eval_truthy("var q = 1; eval('q = 55'); q === 55 && globalThis.q === 55");
eval_truthy("eval('var ev = 1'); Object.getOwnPropertyDescriptor(globalThis, 'ev').configurable === true");
// 嵌套函数内直接 eval:写点经 session 解析全局对象(不依赖 this)。
eval_truthy("var z = 1; (function(){ \"use strict\"; eval('z = 77'); })(); z === 77 && globalThis.z === 77");
// 嵌套函数内直接 eval:strict 上下文 eval 串继承调用方严格性,写未声明名抛
// ReferenceError(引擎独立编译 eval 程序,不共享调用方作用域,严格模式禁隐式
// 全局写入;sloppy 下该形为隐式全局写入,见上三行)。
eval_truthy(
"var z = 1; (function(){ \"use strict\"; return (function() { \
try { eval('z = 77'); return false; } catch (e) { return e instanceof ReferenceError; } })(); })() === true",
);
}
/// 既有属性与顶层 var 声明交互:序言 define-if-absent 零动作,声明值写