From 77a52bf7e82848fc5f15cda01f93205248d79bdd Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Tue, 30 Jun 2026 21:52:18 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=B5=8C=E5=A5=97?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E8=B0=83=E7=94=A8=E5=B9=B6=E6=8E=A5=E9=80=9A?= =?UTF-8?q?=E9=97=AD=E5=8C=85=E6=8D=95=E8=8E=B7=E9=93=BE=E8=B7=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_compiler/src/compiler.rs | 22 ++++++---- crates/oxide_compiler/src/emitter/mod.rs | 11 +++-- .../src/emitter/stmt/declaration.rs | 2 +- crates/oxide_vm/src/dispatch/call.rs | 41 +++++++++++++------ crates/oxide_vm/src/vm.rs | 1 - 5 files changed, 50 insertions(+), 27 deletions(-) diff --git a/crates/oxide_compiler/src/compiler.rs b/crates/oxide_compiler/src/compiler.rs index 84e6d516..3bf27e9d 100644 --- a/crates/oxide_compiler/src/compiler.rs +++ b/crates/oxide_compiler/src/compiler.rs @@ -1089,10 +1089,13 @@ impl Compiler { is_class_constructor: false, is_derived_constructor: false, needs_home_object: false, - upvalue_captures: Vec::new(), - cells_needed: ctx.scopes.symbols.scopes[0] - .bindings - .values() + upvalue_captures: ctx.current_upvalue_captures.clone(), + cells_needed: ctx + .scopes + .symbols + .scopes + .iter() + .flat_map(|s| s.bindings.values()) .filter(|b| b.is_captured.get()) .count() as u8, }) @@ -1193,10 +1196,13 @@ impl Compiler { is_class_constructor: false, is_derived_constructor: false, needs_home_object: false, - upvalue_captures: Vec::new(), - cells_needed: ctx.scopes.symbols.scopes[0] - .bindings - .values() + upvalue_captures: ctx.current_upvalue_captures.clone(), + cells_needed: ctx + .scopes + .symbols + .scopes + .iter() + .flat_map(|s| s.bindings.values()) .filter(|b| b.is_captured.get()) .count() as u8, }) diff --git a/crates/oxide_compiler/src/emitter/mod.rs b/crates/oxide_compiler/src/emitter/mod.rs index ef1e807c..a31c6e34 100644 --- a/crates/oxide_compiler/src/emitter/mod.rs +++ b/crates/oxide_compiler/src/emitter/mod.rs @@ -356,9 +356,6 @@ impl Compiler { ) -> Result<(), String> { let var_reg = ctx.alloc_reg(); let target_reg = if matches!(kind, VariableDeclarationKind::Var) { - // `var` is hoisted, so the count pass already registered the name. A redeclare - // here reuses that existing register instead of erroring; the freshly allocated - // reg matches it (count/emit allocate in lock-step) so either resolves the same. match ctx.declare(name, var_reg, kind, is_const) { Ok(()) => var_reg, Err(_) => ctx.lookup(name).unwrap_or(var_reg), @@ -367,7 +364,13 @@ impl Compiler { ctx.declare(name, var_reg, kind, is_const)?; var_reg }; - ctx.emit(opcode::encode(OpCode::STORE_VAR, target_reg, src_reg, if is_const { 1 } else { 0 })); + if ctx.scopes.symbols.lookup_is_captured(name) { + let cell_idx = ctx.scopes.cell_registry.len() as u8; + ctx.scopes.cell_registry.push((name.to_string(), cell_idx)); + ctx.emit(opcode::encode(OpCode::MAKE_CELL, src_reg, cell_idx, 0)); + } else { + ctx.emit(opcode::encode(OpCode::STORE_VAR, target_reg, src_reg, if is_const { 1 } else { 0 })); + } ctx.init_var(name); Ok(()) } diff --git a/crates/oxide_compiler/src/emitter/stmt/declaration.rs b/crates/oxide_compiler/src/emitter/stmt/declaration.rs index 12b72cbc..8fa14aa2 100644 --- a/crates/oxide_compiler/src/emitter/stmt/declaration.rs +++ b/crates/oxide_compiler/src/emitter/stmt/declaration.rs @@ -37,7 +37,7 @@ impl Compiler { if is_captured { let cell_idx = ctx.scopes.cell_registry.len() as u8; ctx.scopes.cell_registry.push((bi.name.to_string(), cell_idx)); - ctx.emit(opcode::encode(OpCode::MAKE_CELL, var_reg, cell_idx, 0)); + ctx.emit(opcode::encode(OpCode::MAKE_CELL, tmp, cell_idx, 0)); } else { ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, tmp, 0)); } diff --git a/crates/oxide_vm/src/dispatch/call.rs b/crates/oxide_vm/src/dispatch/call.rs index c29017b2..5c94607f 100644 --- a/crates/oxide_vm/src/dispatch/call.rs +++ b/crates/oxide_vm/src/dispatch/call.rs @@ -96,7 +96,6 @@ impl Vm { let is_derived_constructor = sub.is_derived_constructor; let needs_home_object = sub.needs_home_object; let upvalue_captures = sub.upvalue_captures.clone(); - let cells_needed = sub.cells_needed; let result = self.create_function_object( sub_idx, is_arrow, @@ -104,7 +103,7 @@ impl Vm { is_derived_constructor, needs_home_object, ); - if !upvalue_captures.is_empty() && cells_needed > 0 { + if !upvalue_captures.is_empty() { if let Some(current_cells) = self.cell_stack.last() { let mut upvals = Box::new(Vec::with_capacity(upvalue_captures.len())); for capture in &upvalue_captures { @@ -126,7 +125,7 @@ impl Vm { pub(crate) fn dispatch_make_cell(&mut self, rd: usize, instr: u32) -> Result<(), String> { let cell_idx = opcode::imm16(instr) as usize; let value = self.regs[rd]; - let cell = self.gc_state.session_epoch.alloc(Cell::new(value, false)); + let cell = self.gc_state.session_epoch.alloc(Cell::new(value, true)); let cell_ptr = cell as *mut Cell; let current = self.cell_stack.last_mut().unwrap(); while current.len() <= cell_idx { @@ -137,19 +136,18 @@ impl Vm { } #[allow(dead_code)] - pub(crate) fn dispatch_cell_get(&mut self, rd: usize, _a: usize, b: usize) -> Result<(), String> { + pub(crate) fn dispatch_cell_get(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> { let cell_idx = b; - let current = self.cell_stack.last().unwrap(); - if cell_idx >= current.len() { - self.regs[rd] = JsValue::undefined(); - return Ok(()); + let current = self.cell_stack.last_mut().unwrap(); + while current.len() <= cell_idx { + current.push(std::ptr::null_mut()); } - let cell_ptr = current[cell_idx]; - if cell_ptr.is_null() { - self.regs[rd] = JsValue::undefined(); - return Ok(()); + if current[cell_idx].is_null() { + let val = self.regs[a]; + let cell = self.gc_state.session_epoch.alloc(Cell::new(val, true)); + current[cell_idx] = cell as *mut Cell; } - self.regs[rd] = unsafe { (*cell_ptr).value }; + self.regs[rd] = unsafe { (*current[cell_idx]).value }; Ok(()) } @@ -186,6 +184,23 @@ impl Vm { } } } + // Cell not yet created (hoisting order: CREATE_CLOSURE before MAKE_CELL). + // Create cell lazily with caller's register value. + self.lazy_create_upvalue_cell(rd, uv_idx) + } + + fn lazy_create_upvalue_cell(&mut self, rd: usize, uv_idx: usize) -> Result<(), String> { + let callee = self.frames.last().unwrap().callee; + if callee.is_object() { + let obj = unsafe { &mut *callee.as_js_object_ptr() }; + let upvals = obj.upvalues_slice_mut(); + if uv_idx < upvals.len() { + let cell = self.gc_state.session_epoch.alloc(Cell::new(self.regs[rd], true)); + upvals[uv_idx] = cell as *mut Cell; + self.regs[rd] = cell.value; + return Ok(()); + } + } self.regs[rd] = JsValue::undefined(); Ok(()) } diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index a82453a3..87e3b420 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -769,7 +769,6 @@ impl Vm { self.sub_modules = Arc::new(callee_subs.clone()); self.immutables_cache = (0..=callee_subs.len()).map(|_| OnceLock::new()).collect(); } - self.cell_stack.push(Vec::with_capacity(subs[sub_idx].cells_needed as usize)); self.active_reg_limit = sub_n_registers.max(1); self.pc = 0;