diff --git a/crates/oxide_emit/src/capture/captured.rs b/crates/oxide_emit/src/capture/captured.rs index a6d2b3ea..008ce1fa 100644 --- a/crates/oxide_emit/src/capture/captured.rs +++ b/crates/oxide_emit/src/capture/captured.rs @@ -8,7 +8,10 @@ use oxide_parser::{Declaration, ExportDefaultDeclarationKind, Expression, Statem use super::collect_fn_param_names; use super::names::collect_own_binding_names; -use super::scanner::{collect_capture_names_expr, collect_capture_names_shadowed, collect_class_capture_names}; +use super::scanner::{ + collect_capture_names_expr, collect_capture_names_for_left, collect_capture_names_shadowed, + collect_class_capture_names, +}; /// 分析本函数:哪些绑定被任意深度嵌套函数捕获 → captured_bindings。 /// @@ -215,6 +218,10 @@ pub(crate) fn collect_captured_stmt(stmt: &Statement, own: &HashSet, out for d in &vd.declarations { collect_captured_binding_keys(&d.id, own, out); } + } else { + // 赋值头是既有绑定引用:头名与默认值表达式须纳入捕获判定。 + let empty_shadow = HashSet::new(); + collect_capture_names_for_left(&fi.left, own, &empty_shadow, out); } collect_captured_stmt(&fi.body, own, out); } @@ -224,6 +231,9 @@ pub(crate) fn collect_captured_stmt(stmt: &Statement, own: &HashSet, out for d in &vd.declarations { collect_captured_binding_keys(&d.id, own, out); } + } else { + let empty_shadow = HashSet::new(); + collect_capture_names_for_left(&fo.left, own, &empty_shadow, out); } collect_captured_stmt(&fo.body, own, out); } diff --git a/crates/oxide_emit/src/capture/scanner.rs b/crates/oxide_emit/src/capture/scanner.rs index 4da26c85..ccc11f91 100644 --- a/crates/oxide_emit/src/capture/scanner.rs +++ b/crates/oxide_emit/src/capture/scanner.rs @@ -104,6 +104,9 @@ pub(crate) fn collect_capture_names_stmt( for d in &vd.declarations { collect_capture_names_binding_keys(&d.id, ref_set, shadow, out); } + } else { + // 赋值头是既有绑定引用:头名与默认值表达式须纳入捕获判定。 + collect_capture_names_for_left(&fi.left, ref_set, shadow, out); } collect_capture_names_shadowed(std::slice::from_ref(&fi.body), ref_set, &for_shadow, out); } @@ -115,6 +118,8 @@ pub(crate) fn collect_capture_names_stmt( for d in &vd.declarations { collect_capture_names_binding_keys(&d.id, ref_set, shadow, out); } + } else { + collect_capture_names_for_left(&fo.left, ref_set, shadow, out); } collect_capture_names_shadowed(std::slice::from_ref(&fo.body), ref_set, &for_shadow, out); } @@ -335,32 +340,71 @@ pub(crate) fn collect_capture_names_assign_target( collect_capture_names_expr(&m.expression, ref_set, shadow, out); } oxide_parser::AssignmentTarget::ArrayAssignmentTarget(a) => { - for e in a.elements.iter().flatten() { - collect_capture_names_maybe_default_target(e, ref_set, shadow, out); - } - if let Some(rest) = &a.rest { - collect_capture_names_assign_target(&rest.target, ref_set, shadow, out); - } + collect_capture_names_array_target(a, ref_set, shadow, out); } oxide_parser::AssignmentTarget::ObjectAssignmentTarget(o) => { - for prop in &o.properties { - if let oxide_parser::AssignmentTargetProperty::AssignmentTargetPropertyIdentifier(id) = prop { - if ref_set.contains(id.binding.name.as_str()) && !shadow.contains(id.binding.name.as_str()) { - out.insert(id.binding.name.to_string()); - } - if let Some(init) = &id.init { - collect_capture_names_expr(init, ref_set, shadow, out); - } - } else if let oxide_parser::AssignmentTargetProperty::AssignmentTargetPropertyProperty(p) = prop { - if let Some(name_expr) = p.name.as_expression() { - collect_capture_names_expr(name_expr, ref_set, shadow, out); - } - collect_capture_names_maybe_default_target(&p.binding, ref_set, shadow, out); - } + collect_capture_names_object_target(o, ref_set, shadow, out); + } + _ => {} + } +} + +/// 数组赋值目标:元素递归(含默认值),rest 目标递归。 +fn collect_capture_names_array_target( + a: &oxide_parser::ArrayAssignmentTarget, ref_set: &HashSet, shadow: &HashSet, + out: &mut HashSet, +) { + for e in a.elements.iter().flatten() { + collect_capture_names_maybe_default_target(e, ref_set, shadow, out); + } + if let Some(rest) = &a.rest { + collect_capture_names_assign_target(&rest.target, ref_set, shadow, out); + } +} + +/// 对象赋值目标:属性标识符写名/默认值/计算键,rest 目标递归。 +fn collect_capture_names_object_target( + o: &oxide_parser::ObjectAssignmentTarget, ref_set: &HashSet, shadow: &HashSet, + out: &mut HashSet, +) { + for prop in &o.properties { + if let oxide_parser::AssignmentTargetProperty::AssignmentTargetPropertyIdentifier(id) = prop { + if ref_set.contains(id.binding.name.as_str()) && !shadow.contains(id.binding.name.as_str()) { + out.insert(id.binding.name.to_string()); } - if let Some(rest) = &o.rest { - collect_capture_names_assign_target(&rest.target, ref_set, shadow, out); + if let Some(init) = &id.init { + collect_capture_names_expr(init, ref_set, shadow, out); } + } else if let oxide_parser::AssignmentTargetProperty::AssignmentTargetPropertyProperty(p) = prop { + if let Some(name_expr) = p.name.as_expression() { + collect_capture_names_expr(name_expr, ref_set, shadow, out); + } + collect_capture_names_maybe_default_target(&p.binding, ref_set, shadow, out); + } + } + if let Some(rest) = &o.rest { + collect_capture_names_assign_target(&rest.target, ref_set, shadow, out); + } +} + +/// 扫描 for-in/for-of 赋值头:标识符写名、数组/对象解构元素(含默认值)与 rest。 +/// 声明头是新绑定无引用语义,不扫。 +pub(crate) fn collect_capture_names_for_left( + left: &oxide_parser::ForStatementLeft, ref_set: &HashSet, shadow: &HashSet, + out: &mut HashSet, +) { + match left { + oxide_parser::ForStatementLeft::AssignmentTargetIdentifier(id) => { + let name = id.name.as_str(); + if ref_set.contains(name) && !shadow.contains(name) { + out.insert(name.to_string()); + } + } + oxide_parser::ForStatementLeft::ArrayAssignmentTarget(a) => { + collect_capture_names_array_target(a, ref_set, shadow, out); + } + oxide_parser::ForStatementLeft::ObjectAssignmentTarget(o) => { + collect_capture_names_object_target(o, ref_set, shadow, out); } _ => {} } diff --git a/crates/oxide_emit/src/shared/binding.rs b/crates/oxide_emit/src/shared/binding.rs index 6555e93d..19ca9de7 100644 --- a/crates/oxide_emit/src/shared/binding.rs +++ b/crates/oxide_emit/src/shared/binding.rs @@ -146,6 +146,31 @@ impl Emitter { // 解构赋值目标:TDZ 写抛 ReferenceError,const 写抛 TypeError(编译期拦截)。 self.emit_identifier_tdz_guard(name, ctx)?; self.emit_const_write_guard(name, ctx)?; + // 目标若是 upvalue 引用,走 STORE_UPVALUE(与表达式赋值写点同臂序)。 + if let Some(uv_idx) = ctx.current_upvalue_captures.iter().position(|u| u.name == name) { + ctx.inst(Inst::new( + OpCode::STORE_UPVALUE, + Operand::Imm(0), + Operand::Reg(src_reg), + Operand::Imm(uv_idx as u16), + )); + self.emit_module_write_through(name, src_reg, ctx)?; + return Ok(()); + } + // 目标若是被捕获 cell,走 CELL_SET;仅当名字当前可解析为真实词法绑定时才写 + // cell,否则落下方全局解析(未声明名不建隐式全局登记)。 + if let Some(&cell_idx) = ctx.captured_bindings.get(name) { + if ctx.visible_binding_reg(name).is_some() { + ctx.inst(Inst::new( + OpCode::CELL_SET, + Operand::None, + Operand::Reg(src_reg), + Operand::Imm(cell_idx as u16), + )); + self.emit_module_write_through(name, src_reg, ctx)?; + return Ok(()); + } + } let var_reg = ctx.lookup_or_global(name); if ctx.targets_readonly_builtin(name, var_reg) { // 全局不可写内置:sloppy 静默跳过写(槽保留入口预载原值); @@ -359,6 +384,31 @@ impl Emitter { // 解构赋值目标:TDZ 写抛 ReferenceError,const 写抛 TypeError(编译期拦截)。 self.emit_identifier_tdz_guard(name, ctx)?; self.emit_const_write_guard(name, ctx)?; + // 目标若是 upvalue 引用,走 STORE_UPVALUE(与表达式赋值写点同臂序)。 + if let Some(uv_idx) = ctx.current_upvalue_captures.iter().position(|u| u.name == name) { + ctx.inst(Inst::new( + OpCode::STORE_UPVALUE, + Operand::Imm(0), + Operand::Reg(src_reg), + Operand::Imm(uv_idx as u16), + )); + self.emit_module_write_through(name, src_reg, ctx)?; + return Ok(()); + } + // 目标若是被捕获 cell,走 CELL_SET;仅当名字当前可解析为真实词法绑定时才写 + // cell,否则落下方全局解析(未声明名不建隐式全局登记)。 + if let Some(&cell_idx) = ctx.captured_bindings.get(name) { + if ctx.visible_binding_reg(name).is_some() { + ctx.inst(Inst::new( + OpCode::CELL_SET, + Operand::None, + Operand::Reg(src_reg), + Operand::Imm(cell_idx as u16), + )); + self.emit_module_write_through(name, src_reg, ctx)?; + return Ok(()); + } + } let var_reg = ctx.lookup_or_global(name); if ctx.targets_readonly_builtin(name, var_reg) { // 全局不可写内置:sloppy 静默跳过写(槽保留入口预载原值); @@ -454,6 +504,31 @@ impl Emitter { // 解构赋值目标:TDZ 写抛 ReferenceError,const 写抛 TypeError(编译期拦截)。 self.emit_identifier_tdz_guard(name, ctx)?; self.emit_const_write_guard(name, ctx)?; + // 目标若是 upvalue 引用,走 STORE_UPVALUE(与表达式赋值写点同臂序)。 + if let Some(uv_idx) = ctx.current_upvalue_captures.iter().position(|u| u.name == name) { + ctx.inst(Inst::new( + OpCode::STORE_UPVALUE, + Operand::Imm(0), + Operand::Reg(prop_reg), + Operand::Imm(uv_idx as u16), + )); + self.emit_module_write_through(name, prop_reg, ctx)?; + continue; + } + // 目标若是被捕获 cell,走 CELL_SET;仅当名字当前可解析为真实词法绑定时才写 + // cell,否则落下方全局解析(未声明名不建隐式全局登记)。 + if let Some(&cell_idx) = ctx.captured_bindings.get(name) { + if ctx.visible_binding_reg(name).is_some() { + ctx.inst(Inst::new( + OpCode::CELL_SET, + Operand::None, + Operand::Reg(prop_reg), + Operand::Imm(cell_idx as u16), + )); + self.emit_module_write_through(name, prop_reg, ctx)?; + continue; + } + } let var_reg = ctx.lookup_or_global(name); if ctx.targets_readonly_builtin(name, var_reg) { // 全局不可写内置:sloppy 静默跳过该属性写(其余属性继续赋值); diff --git a/crates/oxide_emit/src/stmt/iteration/for_in.rs b/crates/oxide_emit/src/stmt/iteration/for_in.rs index 3e312624..21010922 100644 --- a/crates/oxide_emit/src/stmt/iteration/for_in.rs +++ b/crates/oxide_emit/src/stmt/iteration/for_in.rs @@ -236,38 +236,30 @@ impl Emitter { } ForStatementLeft::AssignmentTargetIdentifier(id_ref) => { let name = id_ref.name.as_str(); - let var_reg = ctx.lookup_or_global(name); // 写仅在本迭代实际产生键后发生(for-in NEXT 之后),空集合不抛。 - if ctx.targets_readonly_builtin(name, var_reg) { - // 全局不可写内置:sloppy 静默跳过写(槽保留入口预载原值); - // strict 在本迭代抛 TypeError(put 失败)。 - if ctx.is_strict { - self.emit_throw_error("TypeError", "cannot assign to read-only property", ctx)?; + // 目标若是 upvalue 引用,走 STORE_UPVALUE(与表达式赋值写点同臂序); + // 目标若是被捕获 cell 且名字当前可解析为真实词法绑定,走 CELL_SET; + // 否则落全局槽写(未声明名不建隐式全局登记)。 + if let Some(uv_idx) = ctx.current_upvalue_captures.iter().position(|u| u.name == name) { + ctx.inst(Inst::new( + OpCode::STORE_UPVALUE, + Operand::Imm(0), + Operand::Reg(key_reg), + Operand::Imm(uv_idx as u16), + )); + } else if let Some(&cell_idx) = ctx.captured_bindings.get(name) { + if ctx.visible_binding_reg(name).is_some() { + ctx.inst(Inst::new( + OpCode::CELL_SET, + Operand::None, + Operand::Reg(key_reg), + Operand::Imm(cell_idx as u16), + )); + } else { + self.emit_for_in_assignment_target_write(name, key_reg, ctx)?; } } else { - let is_tier = self.is_global_tier_name(ctx, name); - let is_implicit = ctx.is_implicit_global_reg(var_reg); - if is_implicit && ctx.is_strict { - // 严格模式未声明写:抛 ReferenceError,跳过寄存器写(值无关)。 - self.emit_strict_undeclared_write(name, ctx)?; - } else if is_tier { - // 顶层 for-in 赋值头:迭代值写入全局对象属性——顶层 var 值的唯一存储是全局对象属性,引擎侧不另存副本。 - // 同上:只读三常量已在拦截臂跳过,其余内置名属性可写。 - self.emit_tier_global_write(name, key_reg, ctx); - } else { - ctx.inst(Inst::new( - OpCode::STORE_VAR, - Operand::Reg(var_reg), - Operand::Reg(key_reg), - Operand::None, - )); - if is_implicit { - self.emit_implicit_global_write(name, var_reg, ctx); - } else if ctx.targets_writable_builtin(name, var_reg) { - // 可写内置名:迭代键同步落全局对象属性。 - self.emit_global_put_write(name, var_reg, ctx); - } - } + self.emit_for_in_assignment_target_write(name, key_reg, ctx)?; } } _ => return Err("unsupported for-in left-hand side".into()), @@ -290,4 +282,47 @@ impl Emitter { }; Ok(Some(result)) } + + /// for-in 赋值头全局槽写:readonly 内置 / strict 未声明 / 顶层 tier / 普通槽 + /// 四分支(与表达式赋值写点同臂序)。 + /// + /// # 副作用 + /// - 发射写指令;strict 未声明写抛 ReferenceError,readonly 内置 strict 抛 TypeError。 + fn emit_for_in_assignment_target_write( + &self, name: &str, key_reg: u32, ctx: &mut CompileCtx, + ) -> Result<(), String> { + let var_reg = ctx.lookup_or_global(name); + if ctx.targets_readonly_builtin(name, var_reg) { + // 全局不可写内置:sloppy 静默跳过写(槽保留入口预载原值); + // strict 在本迭代抛 TypeError(put 失败)。 + if ctx.is_strict { + self.emit_throw_error("TypeError", "cannot assign to read-only property", ctx)?; + } + } else { + let is_tier = self.is_global_tier_name(ctx, name); + let is_implicit = ctx.is_implicit_global_reg(var_reg); + if is_implicit && ctx.is_strict { + // 严格模式未声明写:抛 ReferenceError,跳过寄存器写(值无关)。 + self.emit_strict_undeclared_write(name, ctx)?; + } else if is_tier { + // 顶层 for-in 赋值头:迭代值写入全局对象属性——顶层 var 值的唯一存储是全局对象属性,引擎侧不另存副本。 + // 同上:只读三常量已在拦截臂跳过,其余内置名属性可写。 + self.emit_tier_global_write(name, key_reg, ctx); + } else { + ctx.inst(Inst::new( + OpCode::STORE_VAR, + Operand::Reg(var_reg), + Operand::Reg(key_reg), + Operand::None, + )); + if is_implicit { + self.emit_implicit_global_write(name, var_reg, ctx); + } else if ctx.targets_writable_builtin(name, var_reg) { + // 可写内置名:迭代键同步落全局对象属性。 + self.emit_global_put_write(name, var_reg, ctx); + } + } + } + Ok(()) + } } diff --git a/crates/oxide_emit/src/stmt/iteration/for_of.rs b/crates/oxide_emit/src/stmt/iteration/for_of.rs index 5949354f..3ed6d345 100644 --- a/crates/oxide_emit/src/stmt/iteration/for_of.rs +++ b/crates/oxide_emit/src/stmt/iteration/for_of.rs @@ -142,8 +142,31 @@ impl Emitter { } ForStatementLeft::AssignmentTargetIdentifier(id_ref) => { let name = id_ref.name.as_str(); - let var_reg = ctx.lookup_or_global(name); // 写仅在本迭代实际产生值后发生(for-of NEXT 之后),空集合不抛。 + // 目标若是 upvalue 引用,走 STORE_UPVALUE(与表达式赋值写点同臂序)。 + if let Some(uv_idx) = ctx.current_upvalue_captures.iter().position(|u| u.name == name) { + ctx.inst(Inst::new( + OpCode::STORE_UPVALUE, + Operand::Imm(0), + Operand::Reg(val_reg), + Operand::Imm(uv_idx as u16), + )); + return Ok(()); + } + // 目标若是被捕获 cell,走 CELL_SET;仅当名字当前可解析为真实词法绑定时才写 + // cell,否则落下方全局解析(未声明名不建隐式全局登记)。 + if let Some(&cell_idx) = ctx.captured_bindings.get(name) { + if ctx.visible_binding_reg(name).is_some() { + ctx.inst(Inst::new( + OpCode::CELL_SET, + Operand::None, + Operand::Reg(val_reg), + Operand::Imm(cell_idx as u16), + )); + return Ok(()); + } + } + let var_reg = ctx.lookup_or_global(name); if ctx.targets_readonly_builtin(name, var_reg) { // 全局不可写内置:sloppy 静默跳过写(槽保留入口预载原值); // strict 在本迭代抛 TypeError(put 失败)。 @@ -151,10 +174,14 @@ impl Emitter { self.emit_throw_error("TypeError", "cannot assign to read-only property", ctx)?; } } else { + let is_tier = self.is_global_tier_name(ctx, name); let is_implicit = ctx.is_implicit_global_reg(var_reg); if is_implicit && ctx.is_strict { // 严格模式未声明写:抛 ReferenceError,跳过寄存器写(值无关)。 self.emit_strict_undeclared_write(name, ctx)?; + } else if is_tier { + // 顶层 for-of 赋值头:迭代值写入全局对象属性(顶层 var 的唯一存储)。 + self.emit_tier_global_write(name, val_reg, ctx); } else { ctx.inst(Inst::new( OpCode::STORE_VAR,