diff --git a/crates/oxide_compiler/tests/compile_stmt_tests.rs b/crates/oxide_compiler/tests/compile_stmt_tests.rs
index c8748247..8c0ed02d 100644
--- a/crates/oxide_compiler/tests/compile_stmt_tests.rs
+++ b/crates/oxide_compiler/tests/compile_stmt_tests.rs
@@ -405,6 +405,14 @@ fn compile_closure_capture_arrow_emits_make_cell() {
);
}
+#[test]
+fn compile_adjacent_for_of_lexical_bindings_in_distinct_scopes() {
+ let module = compile_source(
+ "for (const [arg, descr] of [[1, 'a']]) { descr; } for (const [arg, descr] of [[2, 'b']]) { descr; }",
+ );
+ assert!(!module.bytecode.is_empty());
+}
+
#[test]
fn compile_compound_assignment_complement_ops() {
let module = compile_source("let x = 10; x -= 1; x *= 2; x /= 3; x %= 4;");
diff --git a/crates/oxide_emit/src/closure.rs b/crates/oxide_emit/src/closure.rs
index ab0a6e5c..b632b016 100644
--- a/crates/oxide_emit/src/closure.rs
+++ b/crates/oxide_emit/src/closure.rs
@@ -589,8 +589,16 @@ impl Emitter {
}
Expression::ObjectExpression(o) => {
for prop in &o.properties {
- if let oxide_parser::ObjectPropertyKind::ObjectProperty(p) = prop {
- self.collect_capture_names_expr(&p.value, ref_set, shadow, out);
+ match prop {
+ oxide_parser::ObjectPropertyKind::ObjectProperty(p) => {
+ if p.computed {
+ self.collect_capture_names_expr(p.key.to_expression(), ref_set, shadow, out);
+ }
+ self.collect_capture_names_expr(&p.value, ref_set, shadow, out);
+ }
+ oxide_parser::ObjectPropertyKind::SpreadProperty(spread) => {
+ self.collect_capture_names_expr(&spread.argument, ref_set, shadow, out);
+ }
}
}
}
@@ -884,8 +892,16 @@ impl Emitter {
}
Expression::ObjectExpression(o) => {
for prop in &o.properties {
- if let oxide_parser::ObjectPropertyKind::ObjectProperty(p) = prop {
- self.collect_captured_expr(&p.value, own, out);
+ match prop {
+ oxide_parser::ObjectPropertyKind::ObjectProperty(p) => {
+ if p.computed {
+ self.collect_captured_expr(p.key.to_expression(), own, out);
+ }
+ self.collect_captured_expr(&p.value, own, out);
+ }
+ oxide_parser::ObjectPropertyKind::SpreadProperty(spread) => {
+ self.collect_captured_expr(&spread.argument, own, out);
+ }
}
}
}
diff --git a/crates/oxide_emit/src/prepass.rs b/crates/oxide_emit/src/prepass.rs
index c79a3840..6d1c696a 100644
--- a/crates/oxide_emit/src/prepass.rs
+++ b/crates/oxide_emit/src/prepass.rs
@@ -187,11 +187,16 @@ impl Emitter {
}
Expression::ObjectExpression(obj) => {
for prop in &obj.properties {
- if let oxide_parser::ObjectPropertyKind::ObjectProperty(p) = prop {
- if p.computed {
- self.pre_scan_builtin_expr(p.key.to_expression(), ctx);
+ 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.value, ctx);
+ }
+ oxide_parser::ObjectPropertyKind::SpreadProperty(spread) => {
+ self.pre_scan_builtin_expr(&spread.argument, ctx);
}
- self.pre_scan_builtin_expr(&p.value, ctx);
}
}
}
diff --git a/crates/oxide_emit/src/stmt/iteration/for_.rs b/crates/oxide_emit/src/stmt/iteration/for_.rs
index 1508718b..f10a6533 100644
--- a/crates/oxide_emit/src/stmt/iteration/for_.rs
+++ b/crates/oxide_emit/src/stmt/iteration/for_.rs
@@ -36,6 +36,7 @@ impl Emitter {
let Statement::ForStatement(fr) = stmt else {
return Ok(None);
};
+ ctx.push_scope();
let start_label = ctx.next_label_id();
let update_label = ctx.next_label_id();
let end_label = ctx.next_label_id();
@@ -135,6 +136,7 @@ impl Emitter {
ctx.labels.set_label_pos(end_label, ctx.insts.len());
ctx.pop_label_scopes(n_labeled);
ctx.pop_loop();
+ ctx.pop_scope();
Ok(None)
}
}
diff --git a/crates/oxide_emit/src/stmt/iteration/for_in.rs b/crates/oxide_emit/src/stmt/iteration/for_in.rs
index 51a7749b..d692ed3f 100644
--- a/crates/oxide_emit/src/stmt/iteration/for_in.rs
+++ b/crates/oxide_emit/src/stmt/iteration/for_in.rs
@@ -11,6 +11,7 @@ impl Emitter {
let Statement::ForInStatement(fi) = stmt else {
return Ok(None);
};
+ ctx.push_scope();
let start_label = ctx.next_label_id();
let end_label = ctx.next_label_id();
let obj_reg = self.emit_expression(&fi.right, ctx)?;
@@ -40,7 +41,12 @@ impl Emitter {
ctx.declare(name, var_reg, decl.kind, is_const)?;
if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
let op = if fresh_cell { OpCode::MAKE_CELL_FRESH } else { OpCode::MAKE_CELL };
- ctx.inst(Inst::new(op, Operand::Reg(key_reg), Operand::Imm(cell_idx as u16), Operand::None));
+ ctx.inst(Inst::new(
+ op,
+ Operand::Reg(key_reg),
+ Operand::Imm(cell_idx as u16),
+ Operand::None,
+ ));
} else {
ctx.inst(Inst::new(
OpCode::STORE_VAR,
@@ -68,6 +74,7 @@ impl Emitter {
ctx.inst(Inst::new(OpCode::FOR_IN_CLEANUP, Operand::None, Operand::None, Operand::None));
ctx.pop_label_scopes(n_labeled);
ctx.pop_loop();
+ ctx.pop_scope();
Ok(None)
}
}
diff --git a/crates/oxide_emit/src/stmt/iteration/for_of.rs b/crates/oxide_emit/src/stmt/iteration/for_of.rs
index 1801dbdb..5ec366cd 100644
--- a/crates/oxide_emit/src/stmt/iteration/for_of.rs
+++ b/crates/oxide_emit/src/stmt/iteration/for_of.rs
@@ -23,6 +23,7 @@ impl Emitter {
}
fn emit_sync_for_of_statement(&self, fo: &ForOfStatement, ctx: &mut CompileCtx) -> Result