From c4ce29c6f2db046c541e2c90e1536bde51092f9d Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sun, 20 Sep 2026 11:16:13 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=AF=8D=E6=B3=95=E5=A3=B0=E6=98=8E?= =?UTF-8?q?=E9=81=AE=E8=94=BD=E5=86=85=E7=BD=AE=E5=90=8D=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E5=8D=A0=E9=95=9C=E5=83=8F=E6=A7=BD=E5=B9=B6=E6=8B=92=E5=86=99?= =?UTF-8?q?=E8=87=82=E5=85=A8=E5=B1=80=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_emit/src/compile_ctx.rs | 4 +- crates/oxide_emit/src/function_body.rs | 7 +- crates/oxide_emit/src/module.rs | 6 +- crates/oxide_emit/src/prepass/builtin_scan.rs | 218 ++++++++-------- crates/oxide_emit/src/program.rs | 5 +- crates/oxide_emit/src/symbol_table.rs | 11 + .../global_lexical_shadow_builtin_tests.rs | 244 ++++++++++++++++++ 7 files changed, 389 insertions(+), 106 deletions(-) create mode 100644 crates/oxide_vm/tests/global_lexical_shadow_builtin_tests.rs diff --git a/crates/oxide_emit/src/compile_ctx.rs b/crates/oxide_emit/src/compile_ctx.rs index 8c47a0ef..5d77d807 100644 --- a/crates/oxide_emit/src/compile_ctx.rs +++ b/crates/oxide_emit/src/compile_ctx.rs @@ -593,11 +593,13 @@ impl CompileCtx { /// 名字是否解析到全局内置槽:builtin_reg_map 登记(预注册镜像槽),或绑定 /// 落全局作用域(scope 0,顶层 var 预声明/隐式全局,含继承的全局槽)。局部 /// var/let/参数遮蔽同名时解析到局部寄存器/局部作用域,两条件均不命中。 + /// scope 0 臂拒词法绑定:顶层 let/const/class/导入绑定遮蔽同名全局属性, + /// 值存储是其自身槽位,写不落全局对象。 fn resolves_to_global_builtin_slot(&self, name: &str, reg: u32) -> bool { if self.scopes.builtin_reg_map.iter().any(|(n, r)| n == name && *r == reg) { return true; } - matches!(self.scopes.symbols.lookup_any_binding(name), Some((_, 0))) + matches!(self.scopes.symbols.lookup_any_binding(name), Some((binding, 0)) if !binding.lexical) } /// 写目标是否为只读全局内置(undefined/NaN/Infinity 的全局绑定——全局对象上 diff --git a/crates/oxide_emit/src/function_body.rs b/crates/oxide_emit/src/function_body.rs index 75936072..32c91c05 100644 --- a/crates/oxide_emit/src/function_body.rs +++ b/crates/oxide_emit/src/function_body.rs @@ -355,6 +355,7 @@ impl Emitter { reg: binding.reg, initialized: binding.initialized, is_const: binding.is_const, + lexical: binding.lexical, predeclared: false, }, ); @@ -378,6 +379,8 @@ impl Emitter { reg: *reg, initialized: true, is_const: true, + // 类 this 自绑定非用户词法声明,不置位。 + lexical: false, predeclared: false, }, ); @@ -408,7 +411,9 @@ impl Emitter { self.predeclare_labeled_function_declarations(body_stmts, &mut ctx); // 预注册 builtin 引用(先于任何临时寄存器),builtin 槽不与被复用的临时值冲突。 - self.pre_register_builtin_references(body_stmts, &mut ctx); + // 函数体直接子级词法声明名排除登记(顶层词法遮蔽同口径,见 program.rs)。 + let lexical_excluded = collect_direct_lexical_names(body_stmts); + self.pre_register_builtin_references(body_stmts, &lexical_excluded, &mut ctx); // 预声明 `var` 名,使首个 sub-pass 中提升的函数声明能解析其闭包引用的外层 var。 self.predeclare_var_declarations(body_stmts, &mut ctx); diff --git a/crates/oxide_emit/src/module.rs b/crates/oxide_emit/src/module.rs index e1ff60bd..91c290f3 100644 --- a/crates/oxide_emit/src/module.rs +++ b/crates/oxide_emit/src/module.rs @@ -10,7 +10,7 @@ use std::collections::{HashMap, HashSet}; use std::path::Path; -use crate::capture::{collect_captured_bindings, collect_own_binding_names}; +use crate::capture::{collect_captured_bindings, collect_direct_lexical_names, collect_own_binding_names}; use crate::expr::call::pack_arg_regs; use crate::{CompileCtx, Emitter}; use oxc_allocator::Box; @@ -440,7 +440,9 @@ impl Emitter { // —— 预声明(镜像 emit_program;import/export 包装的声明也计入 hoisting)—— self.predeclare_function_declarations(body, ctx); - self.pre_register_builtin_references(body, ctx); + // 模块顶层词法声明名排除镜像登记(词法绑定遮蔽同名全局属性,同 program.rs)。 + let lexical_excluded = collect_direct_lexical_names(body); + self.pre_register_builtin_references(body, &lexical_excluded, ctx); self.predeclare_var_declarations(body, ctx); // 模块 lexical 声明入模块环境(非全局对象),不做受限全局名检查。 let _ = self.predeclare_lexical_declarations(body, ctx, false); diff --git a/crates/oxide_emit/src/prepass/builtin_scan.rs b/crates/oxide_emit/src/prepass/builtin_scan.rs index 2cd4216e..a455704e 100644 --- a/crates/oxide_emit/src/prepass/builtin_scan.rs +++ b/crates/oxide_emit/src/prepass/builtin_scan.rs @@ -1,151 +1,161 @@ //! emit 前置 pass,builtin 引用预扫描:临时寄存器池分配前遍历 AST,把内置 //! 全局标识符出现处(语句/表达式/赋值目标/链式成员/实参/绑定 pattern 计算 //! 键)预先登记到固定寄存器槽(builtin_reg_map),避免与临时寄存器池冲突。 +//! +//! 排除集:当前语句列表直接子级的词法声明名(let/const/class)。这些名在本作用域 +//! 是词法绑定并遮蔽同名全局属性,镜像槽不得为其登记——否则镜像占位先于词法预 +//! 声明占 scope 0,词法声明撞名被拒(规范合法遮蔽形被编译期误拒),写臂谓词亦 +//! 误把词法槽解析成全局内置槽。 + +use std::collections::HashSet; use crate::{CompileCtx, Emitter}; use oxide_parser::{BindingPattern, Declaration, Expression, Statement}; impl Emitter { /// 递归遍历程序 AST,把内置全局标识符引用预登记到固定寄存器槽(内置槽在临时 - /// 寄存器池之前分配,两者隔离,槽位分配序稳定)。 - pub(crate) fn pre_register_builtin_references(&self, stmts: &[Statement], ctx: &mut CompileCtx) { + /// 寄存器池之前分配,两者隔离,槽位分配序稳定)。`excluded` 内的名(本语句 + /// 列表直接子级词法声明名)跳过登记。 + pub(crate) fn pre_register_builtin_references( + &self, stmts: &[Statement], excluded: &HashSet, ctx: &mut CompileCtx, + ) { for stmt in stmts { - self.pre_scan_builtin_stmt(stmt, ctx); + self.pre_scan_builtin_stmt(stmt, excluded, ctx); } } /// 递归遍历语句树,登记其中的内置标识符引用:表达式语句、变量/函数/类声明、 /// 控制流各分支与模块导出声明。 - fn pre_scan_builtin_stmt(&self, stmt: &Statement, ctx: &mut CompileCtx) { + fn pre_scan_builtin_stmt(&self, stmt: &Statement, excluded: &HashSet, ctx: &mut CompileCtx) { match stmt { - Statement::ExpressionStatement(es) => self.pre_scan_builtin_expr(&es.expression, ctx), + Statement::ExpressionStatement(es) => self.pre_scan_builtin_expr(&es.expression, excluded, ctx), Statement::VariableDeclaration(vd) => { for d in &vd.declarations { - self.pre_scan_builtin_pattern(&d.id, ctx); + self.pre_scan_builtin_pattern(&d.id, excluded, ctx); if let Some(init) = &d.init { - self.pre_scan_builtin_expr(init, ctx); + self.pre_scan_builtin_expr(init, excluded, ctx); } } } Statement::ReturnStatement(rs) => { if let Some(a) = &rs.argument { - self.pre_scan_builtin_expr(a, ctx); + self.pre_scan_builtin_expr(a, excluded, ctx); } } Statement::IfStatement(is) => { - self.pre_scan_builtin_expr(&is.test, ctx); - self.pre_scan_builtin_stmt(&is.consequent, ctx); + self.pre_scan_builtin_expr(&is.test, excluded, ctx); + self.pre_scan_builtin_stmt(&is.consequent, excluded, ctx); if let Some(alt) = &is.alternate { - self.pre_scan_builtin_stmt(alt, ctx); + self.pre_scan_builtin_stmt(alt, excluded, ctx); } } Statement::WhileStatement(wh) => { - self.pre_scan_builtin_expr(&wh.test, ctx); - self.pre_scan_builtin_stmt(&wh.body, ctx); + self.pre_scan_builtin_expr(&wh.test, excluded, ctx); + self.pre_scan_builtin_stmt(&wh.body, excluded, ctx); } Statement::DoWhileStatement(dw) => { - self.pre_scan_builtin_stmt(&dw.body, ctx); - self.pre_scan_builtin_expr(&dw.test, ctx); + self.pre_scan_builtin_stmt(&dw.body, excluded, ctx); + self.pre_scan_builtin_expr(&dw.test, excluded, ctx); } Statement::ForStatement(fs) => { if let Some(init) = &fs.init { if let Some(e) = init.as_expression() { - self.pre_scan_builtin_expr(e, ctx); + self.pre_scan_builtin_expr(e, excluded, ctx); } else if let oxide_parser::ForStatementInit::VariableDeclaration(decl) = init { for d in &decl.declarations { - self.pre_scan_builtin_pattern(&d.id, ctx); + self.pre_scan_builtin_pattern(&d.id, excluded, ctx); if let Some(init_expr) = &d.init { - self.pre_scan_builtin_expr(init_expr, ctx); + self.pre_scan_builtin_expr(init_expr, excluded, ctx); } } } } if let Some(t) = &fs.test { - self.pre_scan_builtin_expr(t, ctx); + self.pre_scan_builtin_expr(t, excluded, ctx); } if let Some(u) = &fs.update { - self.pre_scan_builtin_expr(u, ctx); + self.pre_scan_builtin_expr(u, excluded, ctx); } - self.pre_scan_builtin_stmt(&fs.body, ctx); + self.pre_scan_builtin_stmt(&fs.body, excluded, ctx); } Statement::ForInStatement(fi) => { if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fi.left { for d in &vd.declarations { - self.pre_scan_builtin_pattern(&d.id, ctx); + self.pre_scan_builtin_pattern(&d.id, excluded, ctx); } } - self.pre_scan_builtin_expr(&fi.right, ctx); - self.pre_scan_builtin_stmt(&fi.body, ctx); + self.pre_scan_builtin_expr(&fi.right, excluded, ctx); + self.pre_scan_builtin_stmt(&fi.body, excluded, ctx); } Statement::ForOfStatement(fo) => { if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fo.left { for d in &vd.declarations { - self.pre_scan_builtin_pattern(&d.id, ctx); + self.pre_scan_builtin_pattern(&d.id, excluded, ctx); } } - self.pre_scan_builtin_expr(&fo.right, ctx); - self.pre_scan_builtin_stmt(&fo.body, ctx); + self.pre_scan_builtin_expr(&fo.right, excluded, ctx); + self.pre_scan_builtin_stmt(&fo.body, excluded, ctx); } Statement::SwitchStatement(sw) => { - self.pre_scan_builtin_expr(&sw.discriminant, ctx); + self.pre_scan_builtin_expr(&sw.discriminant, excluded, ctx); for case in &sw.cases { if let Some(test) = &case.test { - self.pre_scan_builtin_expr(test, ctx); + self.pre_scan_builtin_expr(test, excluded, ctx); } for s in &case.consequent { - self.pre_scan_builtin_stmt(s, ctx); + self.pre_scan_builtin_stmt(s, excluded, ctx); } } } Statement::ThrowStatement(ts) => { - self.pre_scan_builtin_expr(&ts.argument, ctx); + self.pre_scan_builtin_expr(&ts.argument, excluded, ctx); } Statement::TryStatement(ts) => { for s in &ts.block.body { - self.pre_scan_builtin_stmt(s, ctx); + self.pre_scan_builtin_stmt(s, excluded, ctx); } if let Some(handler) = &ts.handler { if let Some(param) = &handler.param { - self.pre_scan_builtin_pattern(¶m.pattern, ctx); + self.pre_scan_builtin_pattern(¶m.pattern, excluded, ctx); } for s in &handler.body.body { - self.pre_scan_builtin_stmt(s, ctx); + self.pre_scan_builtin_stmt(s, excluded, ctx); } } if let Some(finalizer) = &ts.finalizer { for s in &finalizer.body { - self.pre_scan_builtin_stmt(s, ctx); + self.pre_scan_builtin_stmt(s, excluded, ctx); } } } Statement::BlockStatement(bs) => { for s in &bs.body { - self.pre_scan_builtin_stmt(s, ctx); + self.pre_scan_builtin_stmt(s, excluded, ctx); } } - Statement::LabeledStatement(ls) => self.pre_scan_builtin_stmt(&ls.body, ctx), + Statement::LabeledStatement(ls) => self.pre_scan_builtin_stmt(&ls.body, excluded, ctx), Statement::WithStatement(ws) => { - self.pre_scan_builtin_expr(&ws.object, ctx); - self.pre_scan_builtin_stmt(&ws.body, ctx); + self.pre_scan_builtin_expr(&ws.object, excluded, ctx); + self.pre_scan_builtin_stmt(&ws.body, excluded, ctx); } Statement::ClassDeclaration(cd) => { if let Some(super_class) = &cd.super_class { - self.pre_scan_builtin_expr(super_class, ctx); + self.pre_scan_builtin_expr(super_class, excluded, ctx); } } Statement::ExportNamedDeclaration(exp) => { if let Some(Declaration::VariableDeclaration(vd)) = &exp.declaration { for d in &vd.declarations { - self.pre_scan_builtin_pattern(&d.id, ctx); + self.pre_scan_builtin_pattern(&d.id, excluded, ctx); if let Some(init) = &d.init { - self.pre_scan_builtin_expr(init, ctx); + self.pre_scan_builtin_expr(init, excluded, ctx); } } } } Statement::ExportDefaultDeclaration(exp) => { if let Some(e) = exp.declaration.as_expression() { - self.pre_scan_builtin_expr(e, ctx); + self.pre_scan_builtin_expr(e, excluded, ctx); } } Statement::FunctionDeclaration(_) | Statement::BreakStatement(_) | Statement::ContinueStatement(_) => {} @@ -155,67 +165,69 @@ impl Emitter { /// 递归遍历表达式树,登记其中的内置标识符引用:标识符、调用/构造、成员、可选链、 /// 对象/数组字面量与各类运算表达式。 - fn pre_scan_builtin_expr(&self, expr: &Expression, ctx: &mut CompileCtx) { + fn pre_scan_builtin_expr(&self, expr: &Expression, excluded: &HashSet, ctx: &mut CompileCtx) { match expr { Expression::Identifier(ident) => { - if CompileCtx::is_known_builtin(ident.name.as_str()) { + // 本语句列表词法声明同名的 builtin 不登记镜像槽(词法绑定遮蔽, + // 镜像占位会撞 TDZ 预声明并误导写臂全局解析)。 + if CompileCtx::is_known_builtin(ident.name.as_str()) && !excluded.contains(ident.name.as_str()) { let _ = ctx.lookup_or_builtin(ident.name.as_str()); } } Expression::BinaryExpression(bin) => { - self.pre_scan_builtin_expr(&bin.left, ctx); - self.pre_scan_builtin_expr(&bin.right, ctx); + self.pre_scan_builtin_expr(&bin.left, excluded, ctx); + self.pre_scan_builtin_expr(&bin.right, excluded, ctx); } Expression::UnaryExpression(un) => { - self.pre_scan_builtin_expr(&un.argument, ctx); + self.pre_scan_builtin_expr(&un.argument, excluded, ctx); } Expression::CallExpression(call) => { - self.pre_scan_builtin_expr(&call.callee, ctx); + self.pre_scan_builtin_expr(&call.callee, excluded, ctx); for arg in &call.arguments { - self.pre_scan_builtin_arg(arg, ctx); + self.pre_scan_builtin_arg(arg, excluded, ctx); } } Expression::NewExpression(ne) => { - self.pre_scan_builtin_expr(&ne.callee, ctx); + self.pre_scan_builtin_expr(&ne.callee, excluded, ctx); for arg in &ne.arguments { - self.pre_scan_builtin_arg(arg, ctx); + self.pre_scan_builtin_arg(arg, excluded, ctx); } } Expression::LogicalExpression(log) => { - self.pre_scan_builtin_expr(&log.left, ctx); - self.pre_scan_builtin_expr(&log.right, ctx); + self.pre_scan_builtin_expr(&log.left, excluded, ctx); + self.pre_scan_builtin_expr(&log.right, excluded, ctx); } Expression::ConditionalExpression(cond) => { - self.pre_scan_builtin_expr(&cond.test, ctx); - self.pre_scan_builtin_expr(&cond.consequent, ctx); - self.pre_scan_builtin_expr(&cond.alternate, ctx); + self.pre_scan_builtin_expr(&cond.test, excluded, ctx); + self.pre_scan_builtin_expr(&cond.consequent, excluded, ctx); + self.pre_scan_builtin_expr(&cond.alternate, excluded, ctx); } Expression::PrivateInExpression(pin) => { - self.pre_scan_builtin_expr(&pin.right, ctx); + self.pre_scan_builtin_expr(&pin.right, excluded, ctx); } Expression::SequenceExpression(seq) => { for e in &seq.expressions { - self.pre_scan_builtin_expr(e, ctx); + self.pre_scan_builtin_expr(e, excluded, ctx); } } Expression::AssignmentExpression(assign) => { if let Some(target) = assign.left.as_simple_assignment_target() { - self.pre_scan_builtin_target(target, ctx); + self.pre_scan_builtin_target(target, excluded, ctx); } - self.pre_scan_builtin_expr(&assign.right, ctx); + self.pre_scan_builtin_expr(&assign.right, excluded, ctx); } Expression::UpdateExpression(update) => { - self.pre_scan_builtin_target(&update.argument, ctx); + self.pre_scan_builtin_target(&update.argument, excluded, ctx); } Expression::TemplateLiteral(tl) => { for e in &tl.expressions { - self.pre_scan_builtin_expr(e, ctx); + self.pre_scan_builtin_expr(e, excluded, ctx); } } Expression::TaggedTemplateExpression(tt) => { - self.pre_scan_builtin_expr(&tt.tag, ctx); + self.pre_scan_builtin_expr(&tt.tag, excluded, ctx); for e in &tt.quasi.expressions { - self.pre_scan_builtin_expr(e, ctx); + self.pre_scan_builtin_expr(e, excluded, ctx); } } Expression::ObjectExpression(obj) => { @@ -223,12 +235,12 @@ impl Emitter { match prop { oxide_parser::ObjectPropertyKind::ObjectProperty(p) => { if p.computed { - self.pre_scan_builtin_expr(p.key.to_expression(), ctx); + self.pre_scan_builtin_expr(p.key.to_expression(), excluded, ctx); } - self.pre_scan_builtin_expr(&p.value, ctx); + self.pre_scan_builtin_expr(&p.value, excluded, ctx); } oxide_parser::ObjectPropertyKind::SpreadProperty(spread) => { - self.pre_scan_builtin_expr(&spread.argument, ctx); + self.pre_scan_builtin_expr(&spread.argument, excluded, ctx); } } } @@ -236,30 +248,30 @@ impl Emitter { Expression::ArrayExpression(arr) => { for e in &arr.elements { if let Some(e) = e.as_expression() { - self.pre_scan_builtin_expr(e, ctx); + self.pre_scan_builtin_expr(e, excluded, ctx); } } } Expression::PrivateFieldExpression(member) => { - self.pre_scan_builtin_expr(&member.object, ctx); + self.pre_scan_builtin_expr(&member.object, excluded, ctx); } Expression::StaticMemberExpression(member) => { - self.pre_scan_builtin_expr(&member.object, ctx); + self.pre_scan_builtin_expr(&member.object, excluded, ctx); } Expression::ComputedMemberExpression(member) => { - self.pre_scan_builtin_expr(&member.object, ctx); - self.pre_scan_builtin_expr(&member.expression, ctx); + self.pre_scan_builtin_expr(&member.object, excluded, ctx); + self.pre_scan_builtin_expr(&member.expression, excluded, ctx); } Expression::ChainExpression(chain) => { - self.pre_scan_builtin_chain(&chain.expression, ctx); + self.pre_scan_builtin_chain(&chain.expression, excluded, ctx); } - Expression::ParenthesizedExpression(p) => self.pre_scan_builtin_expr(&p.expression, ctx), + Expression::ParenthesizedExpression(p) => self.pre_scan_builtin_expr(&p.expression, excluded, ctx), Expression::YieldExpression(ye) => { if let Some(a) = &ye.argument { - self.pre_scan_builtin_expr(a, ctx); + self.pre_scan_builtin_expr(a, excluded, ctx); } } - Expression::AwaitExpression(ae) => self.pre_scan_builtin_expr(&ae.argument, ctx), + Expression::AwaitExpression(ae) => self.pre_scan_builtin_expr(&ae.argument, excluded, ctx), Expression::ArrowFunctionExpression(_) | Expression::FunctionExpression(_) | Expression::ClassExpression(_) => {} @@ -269,47 +281,51 @@ impl Emitter { /// 遍历赋值目标,登记其中的内置标识符引用:标识符目标直接登记,成员目标只扫描 /// 对象与计算键(属性名不是标识符引用)。 - fn pre_scan_builtin_target(&self, target: &oxide_parser::SimpleAssignmentTarget, ctx: &mut CompileCtx) { + fn pre_scan_builtin_target( + &self, target: &oxide_parser::SimpleAssignmentTarget, excluded: &HashSet, ctx: &mut CompileCtx, + ) { match target { oxide_parser::SimpleAssignmentTarget::AssignmentTargetIdentifier(id) => { // 标识符目标与表达式位置标识符引用同口径预登记:目标为某内置名的 // 唯一出现处(无读侧引用)时缺预登记会解析成隐式全局新槽,旧值读 - // 与短路判定拿到未预载值。 - if CompileCtx::is_known_builtin(id.name.as_str()) { + // 与短路判定拿到未预载值。本语句列表词法声明同名不登记(同表达式臂)。 + if CompileCtx::is_known_builtin(id.name.as_str()) && !excluded.contains(id.name.as_str()) { let _ = ctx.lookup_or_builtin(id.name.as_str()); } } oxide_parser::SimpleAssignmentTarget::StaticMemberExpression(member) => { - self.pre_scan_builtin_expr(&member.object, ctx); + self.pre_scan_builtin_expr(&member.object, excluded, ctx); } oxide_parser::SimpleAssignmentTarget::ComputedMemberExpression(member) => { - self.pre_scan_builtin_expr(&member.object, ctx); - self.pre_scan_builtin_expr(&member.expression, ctx); + self.pre_scan_builtin_expr(&member.object, excluded, ctx); + self.pre_scan_builtin_expr(&member.expression, excluded, ctx); } oxide_parser::SimpleAssignmentTarget::PrivateFieldExpression(member) => { - self.pre_scan_builtin_expr(&member.object, ctx); + self.pre_scan_builtin_expr(&member.object, excluded, ctx); } _ => {} } } /// 遍历可选链元素,登记链上成员访问、调用与实参中的内置标识符引用。 - fn pre_scan_builtin_chain(&self, element: &oxide_parser::ChainElement, ctx: &mut CompileCtx) { + fn pre_scan_builtin_chain( + &self, element: &oxide_parser::ChainElement, excluded: &HashSet, ctx: &mut CompileCtx, + ) { match element { oxide_parser::ChainElement::StaticMemberExpression(member) => { - self.pre_scan_builtin_expr(&member.object, ctx); + self.pre_scan_builtin_expr(&member.object, excluded, ctx); } oxide_parser::ChainElement::ComputedMemberExpression(member) => { - self.pre_scan_builtin_expr(&member.object, ctx); - self.pre_scan_builtin_expr(&member.expression, ctx); + self.pre_scan_builtin_expr(&member.object, excluded, ctx); + self.pre_scan_builtin_expr(&member.expression, excluded, ctx); } oxide_parser::ChainElement::PrivateFieldExpression(member) => { - self.pre_scan_builtin_expr(&member.object, ctx); + self.pre_scan_builtin_expr(&member.object, excluded, ctx); } oxide_parser::ChainElement::CallExpression(call) => { - self.pre_scan_builtin_expr(&call.callee, ctx); + self.pre_scan_builtin_expr(&call.callee, excluded, ctx); for arg in &call.arguments { - self.pre_scan_builtin_arg(arg, ctx); + self.pre_scan_builtin_arg(arg, excluded, ctx); } } _ => {} @@ -317,41 +333,41 @@ impl Emitter { } /// 遍历调用实参:spread 内部表达式也纳入 builtin 预扫描。 - fn pre_scan_builtin_arg(&self, arg: &oxide_parser::Argument, ctx: &mut CompileCtx) { + fn pre_scan_builtin_arg(&self, arg: &oxide_parser::Argument, excluded: &HashSet, ctx: &mut CompileCtx) { if let Some(e) = arg.as_expression() { - self.pre_scan_builtin_expr(e, ctx); + self.pre_scan_builtin_expr(e, excluded, ctx); } else if let oxide_parser::Argument::SpreadElement(sp) = arg { - self.pre_scan_builtin_expr(&sp.argument, ctx); + self.pre_scan_builtin_expr(&sp.argument, excluded, ctx); } } /// 遍历绑定 pattern 的计算键表达式(`{[k]: a}` 的键)里的 builtin 引用: /// 模式键是运行时求值表达式,键内 builtin 标识符须预先登记固定槽位 /// (与对象字面量计算键预扫描同口径)。 - fn pre_scan_builtin_pattern(&self, pattern: &BindingPattern, ctx: &mut CompileCtx) { + fn pre_scan_builtin_pattern(&self, pattern: &BindingPattern, excluded: &HashSet, ctx: &mut CompileCtx) { match pattern { BindingPattern::BindingIdentifier(_) => {} BindingPattern::ArrayPattern(ap) => { for e in ap.elements.iter().flatten() { - self.pre_scan_builtin_pattern(e, ctx); + self.pre_scan_builtin_pattern(e, excluded, ctx); } if let Some(rest) = &ap.rest { - self.pre_scan_builtin_pattern(&rest.argument, ctx); + self.pre_scan_builtin_pattern(&rest.argument, excluded, ctx); } } BindingPattern::ObjectPattern(op) => { for prop in &op.properties { if prop.computed { - self.pre_scan_builtin_expr(prop.key.to_expression(), ctx); + self.pre_scan_builtin_expr(prop.key.to_expression(), excluded, ctx); } - self.pre_scan_builtin_pattern(&prop.value, ctx); + self.pre_scan_builtin_pattern(&prop.value, excluded, ctx); } if let Some(rest) = &op.rest { - self.pre_scan_builtin_pattern(&rest.argument, ctx); + self.pre_scan_builtin_pattern(&rest.argument, excluded, ctx); } } BindingPattern::AssignmentPattern(ap) => { - self.pre_scan_builtin_pattern(&ap.left, ctx); + self.pre_scan_builtin_pattern(&ap.left, excluded, ctx); } } } diff --git a/crates/oxide_emit/src/program.rs b/crates/oxide_emit/src/program.rs index a610bb2f..7d963ee5 100644 --- a/crates/oxide_emit/src/program.rs +++ b/crates/oxide_emit/src/program.rs @@ -301,7 +301,10 @@ impl Emitter { self.predeclare_function_declarations(&program.body, &mut ctx); // 预注册 builtin 引用(先于任何临时寄存器),builtin 槽不进入临时寄存器池。 - self.pre_register_builtin_references(&program.body, &mut ctx); + // 顶层词法声明名(let/const/class)排除登记:词法绑定遮蔽同名全局属性, + // 镜像占位先建会与词法预声明撞 scope 0 致合法遮蔽形被编译期误拒。 + let lexical_excluded = collect_direct_lexical_names(&program.body); + self.pre_register_builtin_references(&program.body, &lexical_excluded, &mut ctx); // 预声明顶层 `var` 名,使首个 sub-pass 中提升的函数声明能解析外层 var。 self.predeclare_var_declarations(&program.body, &mut ctx); diff --git a/crates/oxide_emit/src/symbol_table.rs b/crates/oxide_emit/src/symbol_table.rs index 859e4a50..20a6fc63 100644 --- a/crates/oxide_emit/src/symbol_table.rs +++ b/crates/oxide_emit/src/symbol_table.rs @@ -23,6 +23,10 @@ pub(crate) struct Binding { pub(crate) reg: u32, pub(crate) initialized: bool, pub(crate) is_const: bool, + /// 词法(let/const/class/导入)绑定:写臂谓词据此拒绝全局内置槽解析—— + /// 顶层词法绑定遮蔽同名全局属性,写不落全局对象(var/隐式全局/镜像占位 + /// 不置位,其值存储本就是全局对象属性)。 + pub(crate) lexical: bool, /// 由块/函数级预声明(TDZ 占位)创建,声明点据此复用槽位。 /// 非预声明的同名绑定(如同 scope 的参数/var)不计,避免误复用。 pub(crate) predeclared: bool, @@ -109,6 +113,7 @@ impl SymbolTable { reg, initialized: false, is_const: matches!(kind, VariableDeclarationKind::Const) || is_const, + lexical: !matches!(kind, VariableDeclarationKind::Var), predeclared: false, }, ); @@ -172,6 +177,7 @@ impl SymbolTable { reg: reg_for_new, initialized: true, is_const: false, + lexical: false, predeclared: false, }, ); @@ -212,6 +218,7 @@ impl SymbolTable { reg, initialized: true, is_const: matches!(kind, VariableDeclarationKind::Const) || is_const, + lexical: !matches!(kind, VariableDeclarationKind::Var), predeclared: false, }, ); @@ -224,6 +231,7 @@ impl SymbolTable { reg, initialized: true, is_const: false, + lexical: false, predeclared: false, }); } @@ -248,6 +256,7 @@ impl SymbolTable { reg, initialized: false, is_const: matches!(kind, VariableDeclarationKind::Const) || is_const, + lexical: !matches!(kind, VariableDeclarationKind::Var), predeclared: true, }, ); @@ -306,6 +315,8 @@ impl SymbolTable { reg, initialized, is_const: true, + // 导入绑定是模块作用域词法(const)绑定,同 let/const 置位。 + lexical: true, predeclared: false, }, ); diff --git a/crates/oxide_vm/tests/global_lexical_shadow_builtin_tests.rs b/crates/oxide_vm/tests/global_lexical_shadow_builtin_tests.rs new file mode 100644 index 00000000..6a9e5843 --- /dev/null +++ b/crates/oxide_vm/tests/global_lexical_shadow_builtin_tests.rs @@ -0,0 +1,244 @@ +//! 顶层/模块词法声明遮蔽可配置全局内置名的合法化:let/const/class 声明名撞 +//! 非受限 builtin 名(Math/Array/isNaN/Promise 等可配置全局属性)时,镜像槽 +//! 不为其登记,声明合法建立遮蔽绑定——裸读读绑定槽、裸写/delete 不落全局 +//! 对象(全局属性原值保留),声明点前裸读按 TDZ 运行期抛 ReferenceError。 +//! +//! 回归约束:受限三常量(87 面)声明仍编译期拒绝;let+var / function+let +//! 撞 builtin 名的重复声明仍编译期拒绝(var 懒登记镜像与函数先登记撞点保留); +//! 未遮蔽 builtin 读、var 内置名写全局、函数内遮蔽读不回退。 + +use std::sync::Arc; + +use oxide_compiler::compiler::Compiler; +use oxide_emit::module::{ModuleSourceLoader, ResolvedModule}; +use oxide_types::value::JsValue; +use oxide_vm::vm::Vm; + +fn eval(source: &str) -> Result { + let allocator = oxide_parser::Allocator::default(); + let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?; + let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?; + let mut vm = Vm::new(); + vm.run(&Arc::new(module)) +} + +fn eval_str(source: &str) -> Result { + let value = eval(source)?; + if value.is_string() { + // SAFETY: 值已确认字符串类别,指针指向 VM 存活期内的 JsString。 + Ok(unsafe { &*value.as_string_ptr() }.as_str().to_string()) + } else { + Err(format!("expected string, got {value:?}")) + } +} + +/// 断言整程序编译失败且错消息为重复声明错形(声明实例化期拒绝)。 +fn assert_compile_err(source: &str) { + let err = eval(source).unwrap_err(); + assert!(err.contains("already been declared"), "got: {err}"); +} + +/// 断言编译 + 运行均成功,返回完成值。 +fn assert_ok(source: &str) -> JsValue { + eval(source).unwrap_or_else(|e| panic!("expected ok, got: {e}\nsource: {source}")) +} + +struct Loader; + +impl ModuleSourceLoader for Loader { + fn resolve( + &mut self, _base_dir: &str, _specifier: &str, _attributes: &[(&str, &str)], + ) -> Result { + Err("no deps".into()) + } +} + +fn eval_module(source: &str) -> Result { + let allocator = oxide_parser::Allocator::default(); + let program = oxide_parser::parse_module(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?; + let module = Compiler::new() + .compile_module(&program, "test.mjs", &mut Loader) + .map_err(|e| format!("Compile: {}", e))?; + let mut vm = Vm::new(); + vm.run(&Arc::new(module)) +} + +// ── 声明形 × builtin 名矩阵:编译过 + 读回声明值(修前全格编译期拒) ── + +#[test] +fn top_level_let_math_shadow() { + let r = assert_ok("let Math=1; Math"); + assert_eq!(r.as_int(), 1); +} + +#[test] +fn top_level_const_math_shadow() { + let r = assert_ok("const Math=1; Math"); + assert_eq!(r.as_int(), 1); +} + +#[test] +fn top_level_class_math_shadow() { + assert_eq!(eval_str("class Math{} typeof Math").unwrap(), "function"); +} + +#[test] +fn top_level_let_isnan_shadow() { + // isNaN 是可配置全局函数名(非受限三常量),合法遮蔽。 + let r = assert_ok("let isNaN=5; isNaN"); + assert_eq!(r.as_int(), 5); +} + +#[test] +fn top_level_const_array_shadow() { + // 语料钉 decl-lex-configurable-global.js 同形:可配置全局属性被遮蔽, + // 全局对象属性原值保留。 + let r = assert_ok("const Array=7; Array"); + assert_eq!(r.as_int(), 7); +} + +#[test] +fn top_level_class_promise_shadow() { + assert_eq!(eval_str("class Promise{} typeof Promise").unwrap(), "function"); +} + +#[test] +fn top_level_let_destructured_builtin_shadow() { + // 解构叶子同属声明名,排除集覆盖。 + let r = assert_ok("let {0: Math} = [3]; Math"); + assert_eq!(r.as_int(), 3); +} + +// ── 写臂 / delete 臂:值落绑定槽,全局对象属性不动 ── + +#[test] +fn top_level_let_shadow_write_not_global() { + // 裸写与复合写都只更新绑定槽;globalThis.Math 保留原对象。 + let r = assert_ok("let Math=1; Math=2; Math"); + assert_eq!(r.as_int(), 2); + let r = assert_ok("let Math=1; Math+=1; Math"); + assert_eq!(r.as_int(), 2); + assert_eq!(eval_str("let Math=1; Math=2; typeof globalThis.Math").unwrap(), "object"); +} + +#[test] +fn top_level_let_shadow_delete_not_global() { + // 词法绑定 delete 返 false(DeleteBinding 非属性引用),全局属性原值保留。 + let r = assert_ok("let Math=1; delete Math"); + assert!(r.is_bool() && !r.as_bool(), "实际 {r:?}"); + let r = assert_ok("let Math=1; delete Math; Math"); + assert_eq!(r.as_int(), 1); + assert_eq!(eval_str("let Math=1; delete Math; typeof globalThis.Math").unwrap(), "object"); +} + +#[test] +fn nested_fn_write_top_level_let_shadow() { + // 嵌套函数写顶层词法绑定经 upvalue cell,同样不落全局对象。 + let r = assert_ok("let Math=1; (function(){Math=2})(); Math"); + assert_eq!(r.as_int(), 2); + assert_eq!(eval_str("let Math=1; (function(){Math=2})(); typeof globalThis.Math").unwrap(), "object"); +} + +// ── TDZ 相位:声明点前裸读是运行期 ReferenceError(修前编译期拒) ── + +#[test] +fn top_level_let_shadow_tdz_read_throws() { + let err = eval("Math; let Math=1;").unwrap_err(); + assert!(err.contains("before initialization"), "got: {err}"); +} + +#[test] +fn top_level_let_shadow_tdz_second_read_throws() { + let err = eval("Math; let Math=1; Math").unwrap_err(); + assert!(err.contains("before initialization"), "got: {err}"); +} + +// ── 回归约束:受限名与重复声明拒面不翻 ── + +#[test] +fn top_level_let_restricted_names_still_rejected() { + // 受限三常量(87 面):非受限名修面不动此面,声明仍编译期拒绝。 + assert_compile_err("let undefined;"); + assert_compile_err("let NaN=1; NaN"); +} + +#[test] +fn let_then_var_builtin_name_still_rejected() { + // var 预声明懒登记镜像:排除集只挡预扫描臂,var 臂撞点保留。 + assert_compile_err("let Math=1; var Math;"); +} + +#[test] +fn function_then_let_builtin_name_still_rejected() { + // 函数声明先登记(提升 var 绑定):撞点保留。 + assert_compile_err("function Math(){} let Math=1;"); +} + +// ── 绿格回归:遮蔽不得回退未遮蔽面 ── + +#[test] +fn closure_reads_top_level_let_shadow() { + let r = assert_ok("let Math=1; (function(){return Math})()"); + assert_eq!(r.as_int(), 1); +} + +#[test] +fn let_shadow_without_reference_compiles() { + let r = assert_ok("let Math=1; 42"); + assert_eq!(r.as_int(), 42); +} + +#[test] +fn fn_decl_before_let_shadow_reads_via_capture() { + let r = assert_ok("function f(){return Math} let Math=1; f()"); + assert_eq!(r.as_int(), 1); +} + +#[test] +fn var_builtin_name_local_read() { + // var 内置名的本地读值不受本修影响(var 臂不置 lexical 标志)。 + let r = assert_ok("var Math=1; Math"); + assert_eq!(r.as_int(), 1); +} + +#[test] +fn unshadowed_builtin_read_unchanged() { + // 一元负号经 ToNumber 落 double 标签,断言按数值比。 + let r = assert_ok("let x=1; Math.abs(-1)"); + assert_eq!(r.as_double(), 1.0); + let r = assert_ok("let Math=1; parseInt('42')"); + assert_eq!(r.as_int(), 42); +} + +// ── 模块面:同形声明合法化,全局对象属性不动 ── + +#[test] +fn module_let_math_shadow_runs() { + let r = eval_module("let Math=1; Math;").expect("module compile+run"); + assert!(r.is_undefined(), "顶层模块完成值应为 undefined,实际 {r:?}"); +} + +#[test] +fn module_let_shadow_keeps_global() { + let r = eval_module("let Math=1; Math=2; Math;").expect("module compile+run"); + assert!(r.is_undefined(), "实际 {r:?}"); + assert_eq!(eval_str("typeof globalThis.Math").unwrap(), "object"); +} + +#[test] +fn module_const_array_shadow_runs() { + let r = eval_module("const Array=7; Array;").expect("module compile+run"); + assert!(r.is_undefined(), "实际 {r:?}"); +} + +#[test] +fn module_class_promise_shadow_runs() { + let r = eval_module("class Promise{} Promise;").expect("module compile+run"); + assert!(r.is_undefined(), "实际 {r:?}"); +} + +#[test] +fn module_let_shadow_tdz_read_throws() { + let err = eval_module("Math; let Math=1;").unwrap_err(); + assert!(err.contains("before initialization"), "got: {err}"); +}