mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 18:51:13 +08:00
fix: for-const 循环 update 段豁免 const 写检查(per-iteration 绑定合法)
This commit is contained in:
@@ -19,7 +19,10 @@ impl Emitter {
|
||||
&self, name: &str, op: OpCode, rhs: u32, ctx: &mut CompileCtx,
|
||||
) -> Result<u32, String> {
|
||||
// const 复合赋值恒抛 TypeError(编译期拦截,值无关);检查在读旧值之前。
|
||||
self.emit_const_write_guard(name, ctx)?;
|
||||
// 循环 update 段的 let/const 循环变量是 per-iteration 可变绑定,复合写同样合法,豁免。
|
||||
if !ctx.register_update_names.iter().any(|n| n == name) {
|
||||
self.emit_const_write_guard(name, ctx)?;
|
||||
}
|
||||
// 循环 update 段:被捕获绑定走寄存器 RMW(C 风格 for 每迭代 fresh,
|
||||
// update 写寄存器供下一迭代 fresh 拷贝,不污染本迭代闭包捕获的 cell)。
|
||||
if ctx.register_update_names.iter().any(|n| n == name) {
|
||||
@@ -176,12 +179,7 @@ impl Emitter {
|
||||
// 复合赋值保规范求值序:先读属性(ic_get)再求值 RHS——RHS 副作用
|
||||
// 可能改写同一属性,后求值才能读到旧值。
|
||||
let val_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::LOAD_VAR,
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Reg(obj_reg),
|
||||
Operand::None,
|
||||
));
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(val_reg), Operand::Reg(obj_reg), Operand::None));
|
||||
ctx.inst(Inst::ic_get(Operand::Reg(val_reg), Operand::Reg(key_reg)));
|
||||
let rhs = self.emit_expression(&assign.right, ctx)?;
|
||||
let op = match assign.operator {
|
||||
|
||||
@@ -120,21 +120,19 @@ impl Emitter {
|
||||
/// 不发射写指令(THROW 后不可达)。
|
||||
/// - cell 写穿共享单元,无运行时 guard;const 拦截由本入口编译期完成。
|
||||
pub(crate) fn emit_identifier_store(&self, name: &str, val_reg: u32, const_flag: u16, ctx: &mut CompileCtx) {
|
||||
// 循环 update 段的 let/const 循环变量是 per-iteration 可变绑定(CreateMutableBinding),
|
||||
// 写寄存器而非 cell,且豁免 const 检查(register_update_names 覆盖全部循环头声明名)。
|
||||
let in_loop_update = ctx.register_update_names.iter().any(|n| n == name);
|
||||
// const 再赋值编译期抛 TypeError:赋值路径(含闭包捕获 const 写 cell)统一拦截。
|
||||
if const_flag != 0 {
|
||||
if const_flag != 0 && !in_loop_update {
|
||||
let _ = self.emit_throw_error("TypeError", "Assignment to constant variable", ctx);
|
||||
return;
|
||||
}
|
||||
// 循环 update 段:被捕获绑定走寄存器而非 cell(C 风格 for 每迭代 fresh,
|
||||
// update 写寄存器供下一迭代 fresh 拷贝,不污染本迭代闭包捕获的 cell)。
|
||||
if ctx.register_update_names.iter().any(|n| n == name) {
|
||||
if in_loop_update {
|
||||
if let Some(reg) = ctx.scopes.symbols.lookup_any(name) {
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_VAR,
|
||||
Operand::Reg(reg),
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Imm(const_flag),
|
||||
));
|
||||
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(reg), Operand::Reg(val_reg), Operand::Imm(0)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -467,8 +467,12 @@ impl Emitter {
|
||||
&self, name: &str, update: &oxide_parser::UpdateExpression, ctx: &mut CompileCtx,
|
||||
) -> Result<u32, String> {
|
||||
// 自增/自减先解析赋值引用:TDZ 绑定抛 ReferenceError;const 抛 TypeError(编译期拦截)。
|
||||
// 循环 update 段的 let/const 循环变量是 per-iteration 可变绑定(CreateMutableBinding),
|
||||
// 规范允许写,故豁免 const 检查(register_update_names 覆盖全部循环头声明名)。
|
||||
self.emit_identifier_tdz_guard(name, ctx)?;
|
||||
self.emit_const_write_guard(name, ctx)?;
|
||||
if !ctx.register_update_names.iter().any(|n| n == name) {
|
||||
self.emit_const_write_guard(name, ctx)?;
|
||||
}
|
||||
let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name);
|
||||
let captured_cell = ctx.captured_bindings.get(name).copied();
|
||||
// 循环 update 段:被捕获绑定走寄存器 INC/DEC(C 风格 for 每迭代 fresh,
|
||||
|
||||
@@ -44,6 +44,10 @@ impl Emitter {
|
||||
let n_labeled = ctx.take_pending_loop_labels(end_label, update_label);
|
||||
// 循环头声明中被嵌套函数捕获的 let/const 绑定:每迭代 fresh cell。
|
||||
let mut fresh_bindings: Vec<(String, u8)> = Vec::new();
|
||||
// 循环头 let/const 声明名:update 段是 per-iteration 可变绑定
|
||||
// (规范 §14.7.4.4 CreatePerIterationEnvironment 用 CreateMutableBinding),
|
||||
// 编译期 const 写检查对 update 段豁免,故记录全部声明名(含未被捕获者)。
|
||||
let mut update_names: Vec<String> = Vec::new();
|
||||
if let Some(init) = &fr.init {
|
||||
if let Some(expr) = init.as_expression() {
|
||||
self.emit_expression(expr, ctx)?;
|
||||
@@ -91,11 +95,13 @@ impl Emitter {
|
||||
));
|
||||
ctx.init_var(bi.name.as_str());
|
||||
}
|
||||
// 记录被捕获的 let/const 声明(解构 pattern 递归收集绑定名)。
|
||||
// 记录 let/const 循环头声明名(update 段写豁免所需)与被捕获的绑定
|
||||
// (解构 pattern 递归收集绑定名,被捕获者每迭代 fresh cell)。
|
||||
if !matches!(decl.kind, VariableDeclarationKind::Var) {
|
||||
let mut names = Vec::new();
|
||||
self.collect_pattern_binding_names(&d.id, &mut names);
|
||||
for name in &names {
|
||||
update_names.push(name.clone());
|
||||
if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
|
||||
fresh_bindings.push((name.clone(), cell_idx));
|
||||
}
|
||||
@@ -124,11 +130,11 @@ impl Emitter {
|
||||
self.emit_statement(&fr.body, ctx)?;
|
||||
ctx.labels.set_label_pos(update_label, ctx.insts.len());
|
||||
if let Some(update) = &fr.update {
|
||||
// 被捕获的 let/const 循环变量在 update 段写寄存器(而非 cell),
|
||||
// 供下一迭代 fresh 拷贝——否则 CELL_SET 会污染本迭代闭包捕获的 cell。
|
||||
// update 段写寄存器(而非 cell):被捕获的 let/const 循环变量每迭代 fresh,
|
||||
// update 写寄存器供下一迭代 fresh 拷贝——否则 CELL_SET 会污染本迭代闭包
|
||||
// 捕获的 cell。未捕获者本就走寄存器,一并登记以豁免 update 段的 const 检查。
|
||||
let prev = std::mem::take(&mut ctx.register_update_names);
|
||||
let names: Vec<String> = fresh_bindings.iter().map(|(n, _)| n.clone()).collect();
|
||||
ctx.register_update_names = names;
|
||||
ctx.register_update_names = update_names;
|
||||
self.emit_expression(update, ctx)?;
|
||||
ctx.register_update_names = prev;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user