diff --git a/crates/oxide_emit/src/function_body.rs b/crates/oxide_emit/src/function_body.rs index bf506fa5..06deb6cd 100644 --- a/crates/oxide_emit/src/function_body.rs +++ b/crates/oxide_emit/src/function_body.rs @@ -502,6 +502,64 @@ impl Emitter { Ok(ir) } + /// 规范 `argumentsObjNeeded` 的引擎判据:函数作用域是否声明 `arguments` + /// 从而抑制自动 arguments 对象。 + /// + /// 规范 FunctionDeclarationInstantiation 仅在箭头函数、形参名含 + /// `arguments`、或(无参数表达式时)函数体**顶层**函数/词法声明含 + /// `arguments` 时不建对象;嵌套块、循环、`switch`/`try` 内的同名声明属块 + /// 作用域,不构成抑制。 + /// + /// # 边界与前提 + /// - `param_names` 须含解构形参叶名(规范 `paramNames`)。 + /// - `has_param_exprs` 为形参默认值存在性(规范 `ContainsExpression`): + /// 为 true 时顶层函数声明不抑制,对象须在参数求值期已可读。 + /// - 函数体顶层词法声明同名无条件抑制:引擎同一作用域无法同时承载自动 + /// 绑定与顶层词法绑定。 + /// - `var arguments` 不在规范 `funcNames` 内,不抑制。 + fn arguments_obj_needed( + &self, body_stmts: &[Statement], param_names: &HashSet, has_param_exprs: bool, + ) -> bool { + if param_names.contains("arguments") { + return false; + } + for stmt in body_stmts { + match stmt { + // 顶层 let/const/class 与自动绑定同作用域冲突,无条件抑制。 + Statement::VariableDeclaration(vd) if !matches!(vd.kind, VariableDeclarationKind::Var) => { + let mut names = HashSet::new(); + for d in &vd.declarations { + collect_binding_pattern_names(&d.id, &mut names); + } + if names.contains("arguments") { + return false; + } + } + Statement::ClassDeclaration(cd) => { + if cd.id.as_ref().is_some_and(|id| id.name == "arguments") { + return false; + } + } + // 顶层函数声明仅无参数默认值时抑制。 + Statement::FunctionDeclaration(fd) => { + if !has_param_exprs && fd.id.as_ref().is_some_and(|id| id.name == "arguments") { + return false; + } + } + // Annex B.3.2 标签链直接包裹的函数声明与直接子函数声明同面。 + _ => { + if !has_param_exprs + && Self::labeled_function_decl(stmt) + .is_some_and(|fd| fd.id.as_ref().is_some_and(|id| id.name == "arguments")) + { + return false; + } + } + } + } + true + } + /// 参数 prologue:函数作用域 + 参数声明/解构 + 闭包捕获与 upvalue 分析。返回 param_base。 #[allow(clippy::too_many_arguments)] fn emit_params_prologue<'a>( @@ -548,11 +606,13 @@ impl Emitter { } } - // 自动声明 arguments 绑定(非箭头函数,且用户未显式声明同名标识符)。 + // 自动声明 arguments 绑定(非箭头函数,且函数作用域未声明同名标识符)。 // 先登记符号并纳入 own_bindings,使嵌套箭头引用 arguments 被识别为本函数 // 绑定(否则被当自由变量 → 子模块 upvalue 解析错位)。 let mut arguments_reg = None; - if !matches!(body_context, FunctionBodyContext::Arrow) && !ctx.own_bindings.contains("arguments") { + if !matches!(body_context, FunctionBodyContext::Arrow) + && self.arguments_obj_needed(body_stmts, &ctx.param_names, !param_defaults.is_empty()) + { let reg = ctx.alloc_reg(); ctx.declare_initialized("arguments", reg, VariableDeclarationKind::Var, false)?; ctx.own_bindings.insert("arguments".to_string()); diff --git a/crates/oxide_vm/tests/arguments_tests.rs b/crates/oxide_vm/tests/arguments_tests.rs index 720115f6..a00e2a28 100644 --- a/crates/oxide_vm/tests/arguments_tests.rs +++ b/crates/oxide_vm/tests/arguments_tests.rs @@ -14,6 +14,11 @@ fn eval(vm: &mut Vm, source: &str) -> Result { vm.run(&Arc::new(module)) } +fn eval_str(vm: &mut Vm, source: &str) -> String { + let result = eval(vm, source).unwrap_or_else(|e| panic!("{source} -> {e}")); + vm.lookup_str(result).unwrap_or_else(|| panic!("{source} -> 非字符串结果")) +} + #[test] fn arguments_length_counts_actual_args() { let mut vm = Vm::new(); @@ -112,3 +117,66 @@ fn new_expression_target_gets_arguments() { let result = eval(&mut vm, "function Ctor(){this.n=arguments.length} var c=new Ctor(1,2,3); c.n").unwrap(); assert_eq!(result.as_int(), 3); } + +#[test] +fn nested_block_fn_named_arguments_keeps_object_before_decl() { + // 嵌套块内函数声明名 arguments 不抑制自动对象:块求值前读为 Arguments 对象, + // 块求值后 web-compat 写回同槽成函数(与 node 一致)。 + let mut vm = Vm::new(); + let result = eval_str( + &mut vm, + "function f(){var r=typeof arguments;{function arguments(){}}return r+',' +typeof arguments} f()", + ); + assert_eq!(result, "object,function"); +} + +#[test] +fn nested_block_lexical_named_arguments_keeps_object_before_decl() { + // 嵌套块 let arguments 是块作用域绑定,不进入函数顶层 lexicalNames。 + let mut vm = Vm::new(); + let result = eval_str(&mut vm, "function f(){var r=typeof arguments;{let arguments=1;}return r} f()"); + assert_eq!(result, "object"); +} + +#[test] +fn var_named_arguments_keeps_object() { + // var arguments 不在规范抑制面内,入口仍建对象。 + let mut vm = Vm::new(); + let result = eval_str(&mut vm, "function f(){var r=typeof arguments;var arguments;return r} f()"); + assert_eq!(result, "object"); +} + +#[test] +fn top_level_lexical_named_arguments_suppresses_object() { + // 函数体顶层 let arguments 与自动绑定同作用域冲突,词法绑定胜出。 + let mut vm = Vm::new(); + let result = eval_str(&mut vm, "function f(){ let arguments='L'; return typeof arguments } f()"); + assert_eq!(result, "string"); +} + +#[test] +fn destructured_param_leaf_named_arguments_suppresses_object() { + // 解构形参叶名 arguments 是规范 paramNames 成员,自动对象被抑制。 + let mut vm = Vm::new(); + let result = eval(&mut vm, "function f({arguments}){return typeof arguments} f({arguments:5})").unwrap(); + assert_eq!(vm.lookup_str(result).as_deref(), Some("number")); +} + +#[test] +fn default_param_reads_arguments_when_body_has_arguments_fn() { + // 含参数默认值时顶层函数声明不抑制对象:默认参数内可读到 Arguments 对象。 + let mut vm = Vm::new(); + let result = eval_str( + &mut vm, + "var args; function f(x = args = arguments) { function arguments() {} } f(); typeof args+',' +args.length", + ); + assert_eq!(result, "object,0"); +} + +#[test] +fn top_level_fn_named_arguments_suppresses_object() { + // 无参数默认值时函数体顶层函数声明 arguments 抑制自动对象,body 见函数对象。 + let mut vm = Vm::new(); + let result = eval_str(&mut vm, "function f(){return typeof arguments; function arguments(){}} f()"); + assert_eq!(result, "function"); +}