hot dispatcher; slices passed in to avoid re-unwrapping the cache
(&mut self, chunk: &SSAChunk, slots: &mut [Val], cache: &mut OpcodeCache, insns: &[Instruction], consts: &[Val], ip: &mut usize, exc_base: usize)
| 363 | #[inline] |
| 364 | #[allow(clippy::too_many_arguments)] // hot dispatcher; slices passed in to avoid re-unwrapping the cache |
| 365 | fn dispatch(&mut self, chunk: &SSAChunk, slots: &mut [Val], cache: &mut OpcodeCache, insns: &[Instruction], consts: &[Val], ip: &mut usize, exc_base: usize) -> Result<Option<Val>, VmErr> { |
| 366 | let n = insns.len(); |
| 367 | let ins = insns[*ip]; |
| 368 | let rip = *ip; |
| 369 | let op = ins.operand; |
| 370 | *ip += 1; |
| 371 | |
| 372 | match ins.opcode { |
| 373 | // Short-circuit jumps; instance `__bool__` / `__len__` may run via `truthy_op`. |
| 374 | OpCode::JumpIfFalseOrPop => { |
| 375 | let v = *self.stack.last().ok_or(cold_runtime("stack underflow"))?; |
| 376 | if !self.truthy_op(v, chunk, slots)? { *ip = op as usize; } |
| 377 | else { self.pop()?; } |
| 378 | } |
| 379 | OpCode::JumpIfTrueOrPop => { |
| 380 | let v = *self.stack.last().ok_or(cold_runtime("stack underflow"))?; |
| 381 | if self.truthy_op(v, chunk, slots)? { *ip = op as usize; } |
| 382 | else { self.pop()?; } |
| 383 | } |
| 384 | |
| 385 | // Hot opcodes. |
| 386 | OpCode::LoadName => { |
| 387 | // At module scope, module_state holds the live value so function `global` writes are visible. |
| 388 | if core::ptr::eq(chunk, self.chunk) |
| 389 | && let Some(n) = chunk.names.get(op as usize) |
| 390 | && let Some(&gv) = self.module_state.get(ssa_strip(n)) |
| 391 | && !gv.is_undef() |
| 392 | { |
| 393 | self.push(gv); |
| 394 | } else { |
| 395 | // Malformed bytecode can carry an out-of-range slot; treat it as unbound. |
| 396 | let v = slots.get(op as usize).copied().unwrap_or(Val::undef()); |
| 397 | if v.is_undef() { |
| 398 | let name = chunk.names.get(op as usize).map(|n| ssa_strip(n)).unwrap_or_default(); |
| 399 | return Err(VmErr::Name(name.into())); |
| 400 | } |
| 401 | self.push(v); |
| 402 | } |
| 403 | } |
| 404 | OpCode::StoreName => { |
| 405 | self.handle_store(op, slots)?; |
| 406 | // Mirror entry-chunk stores into `module_state` so functions with `global X` see updates, and mirror Module values into `globals` so `import_module()` finds module aliases. |
| 407 | if core::ptr::eq(chunk, self.chunk) |
| 408 | && let Some(name) = chunk.names.get(op as usize) |
| 409 | { |
| 410 | let v = slots[op as usize]; |
| 411 | let bare = ssa_strip(name).to_string(); |
| 412 | self.module_state.insert(bare.clone(), v); |
| 413 | if v.is_heap() && matches!(self.heap.get(v), HeapObj::Module(..)) { |
| 414 | self.globals.insert(bare, v); |
| 415 | } |
| 416 | } |
| 417 | } |
| 418 | OpCode::LoadGlobal => { |
| 419 | let name = chunk.names.get(op as usize).ok_or(cold_runtime("LoadGlobal: name index out of bounds"))?; |
| 420 | let v = self.module_state.get(name.as_str()).copied() |
| 421 | .or_else(|| self.globals.get(name.as_str()).copied()) |
| 422 | .unwrap_or(Val::undef()); |
no test coverage detected