we (web engine): Experimental web browser project to understand the limits of Claude

Fix closure-unaware code paths in compiler

Object literal shorthand ({ x }) and for-in loop variable bindings
used find_local + Move, which would copy cell GcRefs instead of values
for captured variables. Updated both paths to use find_local_info with
proper CellLoad/CellStore/upvalue handling. Removed now-unused
find_local method.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+30 -12
+30 -12
crates/js/src/compiler.rs
··· 91 91 self.locals.iter().rev().find(|l| l.name == name) 92 92 } 93 93 94 - /// Look up a local variable by name (register only). 95 - fn find_local(&self, name: &str) -> Option<Reg> { 96 - self.find_local_info(name).map(|l| l.reg) 97 - } 98 - 99 94 /// Look up an upvalue by name, returning its index. 100 95 fn find_upvalue(&self, name: &str) -> Option<u8> { 101 96 self.upvalues ··· 833 828 834 829 // Bind the loop variable. 835 830 match left { 836 - ForInOfLeft::VarDecl { kind: _, pattern } => { 831 + ForInOfLeft::VarDecl { kind, pattern } => { 837 832 if let PatternKind::Identifier(name) = &pattern.kind { 838 - let var_r = fc.define_local(name); 839 - fc.builder.emit_reg_reg(Op::Move, var_r, key_r); 833 + let is_captured = fc.captured_names.contains(name.as_str()); 834 + let is_const = *kind == VarKind::Const; 835 + let var_r = fc.define_local_ext(name, is_captured, is_const); 836 + if is_captured { 837 + fc.builder.emit_reg(Op::NewCell, var_r); 838 + fc.builder.emit_reg_reg(Op::CellStore, var_r, key_r); 839 + } else { 840 + fc.builder.emit_reg_reg(Op::Move, var_r, key_r); 841 + } 840 842 } 841 843 } 842 844 ForInOfLeft::Pattern(pattern) => { 843 845 if let PatternKind::Identifier(name) = &pattern.kind { 844 - if let Some(local) = fc.find_local(name) { 845 - fc.builder.emit_reg_reg(Op::Move, local, key_r); 846 + if let Some(local) = fc.find_local_info(name) { 847 + let reg = local.reg; 848 + let captured = local.is_captured; 849 + if captured { 850 + fc.builder.emit_reg_reg(Op::CellStore, reg, key_r); 851 + } else { 852 + fc.builder.emit_reg_reg(Op::Move, reg, key_r); 853 + } 854 + } else if let Some(uv_idx) = fc.find_upvalue(name) { 855 + fc.builder.emit_store_upvalue(uv_idx, key_r); 846 856 } else { 847 857 let ni = fc.builder.add_name(name); 848 858 fc.builder.emit_store_global(ni, key_r); ··· 2081 2091 } else { 2082 2092 // Shorthand: `{ x }` means `{ x: x }`. 2083 2093 if let PropertyKey::Identifier(name) = &prop.key { 2084 - if let Some(local) = fc.find_local(name) { 2085 - fc.builder.emit_reg_reg(Op::Move, val_reg, local); 2094 + if let Some(local) = fc.find_local_info(name) { 2095 + let reg = local.reg; 2096 + let captured = local.is_captured; 2097 + if captured { 2098 + fc.builder.emit_reg_reg(Op::CellLoad, val_reg, reg); 2099 + } else { 2100 + fc.builder.emit_reg_reg(Op::Move, val_reg, reg); 2101 + } 2102 + } else if let Some(uv_idx) = fc.find_upvalue(name) { 2103 + fc.builder.emit_load_upvalue(val_reg, uv_idx); 2086 2104 } else { 2087 2105 let ni = fc.builder.add_name(name); 2088 2106 fc.builder.emit_load_global(val_reg, ni);