use std::rc::Rc; use crate::{ instruction::Instruction, value::{ BogusClosure, Value, }, }; #[derive (Debug)] pub struct Upvalue { pub in_stack: bool, pub idx: u8, pub kind: u8, } #[derive (Debug)] pub struct Block { pub instructions: Vec , pub constants: Vec , pub upvalues: Vec , } #[derive (Debug)] pub struct Chunk { pub blocks: Vec , } #[derive (Clone, Debug)] struct StackFrame { // i32 makes it a little easier to implement jumps // Starts at 0 right after OP_CALL program_counter: i32, // Starts from 0 for main and 1 for the first closure block_idx: usize, register_offset: usize, } #[derive (Debug)] pub struct Breakpoint { pub block_idx: usize, pub program_counter: i32, } #[derive (Debug)] pub struct State <'a> { registers: Vec , // Currently only used for native function calls top: usize, stack: Vec , pub debug_print: bool, pub breakpoints: Vec , step_count: u32, chunk: &'a Chunk, upvalues: &'a [Value], } fn lw_print (l: &mut State) -> i32 { for i in 0..u8::try_from (l.get_top ()).unwrap () { let input = l.reg (i); if i == 0 { print! ("{input}"); } else { print! ("\t{input}"); } } println! (""); *l.reg_mut (0) = Value::from (1993); 1 } pub enum StepOutput { ChunkReturned (Vec ), } impl <'a> State <'a> { pub fn new (chunk: &'a Chunk, upvalues: &'a [Value]) -> Self { Self { registers: vec! [Value::Nil; 16], top: 0, stack: vec! [ StackFrame { program_counter: 0, block_idx: 0, register_offset: 0, }, ], debug_print: false, breakpoints: Default::default(), step_count: 0, chunk, upvalues, } } pub fn upvalues_from_args > (args: I) -> Vec { let arg = args.map (|s| Value::from (s)).enumerate (); let arg = Value::from_iter (arg.map (|(i, v)| (Value::from (i), v))); let env = [ ("arg", arg), ("print", Value::RsFunc (lw_print)), ].into_iter ().map (|(k, v)| (k.to_string (), v)); vec! [ Value::from_iter (env.into_iter ()), ] } fn register_window (&self) -> &[Value] { let frame = self.stack.last ().unwrap (); &self.registers [frame.register_offset..] } /// Short form to get access to a register within our window fn reg (&self, i: u8) -> &Value { let frame = self.stack.last ().unwrap (); &self.registers [frame.register_offset + i as usize] } fn reg_mut (&mut self, i: u8) -> &mut Value { let frame = self.stack.last ().unwrap (); &mut self.registers [frame.register_offset + i as usize] } // For native functions to check how many args they got pub fn get_top (&self) -> usize { self.top - self.stack.last ().unwrap ().register_offset } pub fn step (&mut self) -> Option { let chunk = self.chunk; self.step_count += 1; let frame = self.stack.last_mut ().unwrap ().clone (); let block = chunk.blocks.get (frame.block_idx).unwrap (); for bp in &self.breakpoints { if frame.block_idx == bp.block_idx && frame.program_counter == bp.program_counter { dbg! (&self); } } let mut next_pc = frame.program_counter; let pc = usize::try_from (frame.program_counter).expect ("program_counter is not a valid usize"); let instruction = match block.instructions.get (pc) { Some (x) => x, None => { dbg! (&self.stack); panic! ("program_counter went out of bounds"); } }; // let r = &mut self.registers [frame.register_offset..]; let k = &block.constants; match instruction { Instruction::Add (a, b, c) => { let v_b = self.reg (*b); let v_c = self.reg (*c); let x = if let (Some (v_b), Some (v_c)) = (v_b.as_int (), v_c.as_int ()) { Value::from (v_b + v_c) } else { let v_b = v_b.as_float ().unwrap_or_else (|| panic! ("{v_b}")); let v_c = v_c.as_float ().unwrap_or_else (|| panic! ("{v_c}")); Value::from (v_b + v_c) }; *self.reg_mut (*a) = x; }, Instruction::Call (a, b, _c) => { let b = usize::from (*b); // Take arguments from registers [a + 1, a + b) // Call the function in register [a] // Return values in registers [a, a + c - 1) // // That is, call a with b - 1 arguments and expect c returns // // e.g. CALL 0 2 1 mean "Call 0 with 1 argument, return 1 value", like for printing a constant // Do a clone here to avoid a borow problem. // Should be fixable with more clever code. let v_a = self.reg (*a).clone (); match v_a { Value::BogusClosure (rc) => { let idx = rc.borrow ().idx; let block_idx = frame.block_idx; let target_block = idx; let current_frame = self.stack.last ().unwrap (); self.stack.push (StackFrame { program_counter: 0, block_idx: target_block, register_offset: current_frame.register_offset + *a as usize + 1, }); if self.debug_print { println! ("Inst {block_idx}:{pc} calls {target_block}:0"); let stack_depth = self.stack.len (); println! ("stack_depth: {stack_depth}"); } // Skip the PC increment at the bottom of the loop return None; }, Value::RsFunc (x) => { let current_frame = self.stack.last ().unwrap (); let new_offset = current_frame.register_offset + usize::from (*a) + 1; self.stack.push (StackFrame { program_counter: 65535, // Bogus for native functions block_idx: 65535, // Bogus register_offset: new_offset, }); // No clue what the '1' is doing here self.top = new_offset + b - 1; // Call let num_results = x (self); let popped_frame = self.stack.pop ().unwrap (); let offset = popped_frame.register_offset - 1; for i in (offset)..(offset + usize::try_from (num_results).unwrap ()) { self.registers [i] = self.registers [i + 1 + usize::from (*a)].take (); } }, _ => { let stack = &self.stack; panic! ("Cannot call value {a:?}. backtrace: {stack:?}"); }, } }, Instruction::Closure (a, b) => { let b = usize::try_from (*b).unwrap (); let idx = b + 1; let block = &chunk.blocks [idx]; let mut new_upvalues = Vec::with_capacity (block.upvalues.len ()); for uv in &block.upvalues { let val = if uv.in_stack { self.reg (uv.idx).clone () } else { // TODO: This isn't really correct self.upvalues [usize::from (uv.idx)].clone () }; new_upvalues.push (val); } *self.reg_mut (*a) = Value::from (BogusClosure { idx, upvalues: new_upvalues, }); }, Instruction::EqI (a, sb, k_flag) => { if (self.reg (*a).as_int ().unwrap () == *sb as i64) != *k_flag { next_pc += 1; } }, Instruction::EqK (a, b, k_flag) => { let b = usize::from (*b); if (*self.reg (*a) == k [b]) != *k_flag { next_pc += 1; } }, Instruction::ExtraArg (ax) => { // This is used for NewTable. Maybe it's for reserving // capacity in the array or something? assert_eq! (*ax, 0, "implemented only for ax == 0"); }, Instruction::ForLoop (a, bx) => { let mut iter = self.reg (*a + 3).as_int ().unwrap (); iter += 1; *self.reg_mut (*a + 3) = iter.into (); let stop = self.reg (*a + 1).as_int ().unwrap (); if iter <= stop { next_pc -= i32::try_from (*bx).unwrap (); } }, Instruction::ForPrep (a, bx) => { let start = self.reg (*a).as_int ().unwrap (); let stop = self.reg (*a + 1).as_int ().unwrap (); if start > stop { next_pc += i32::try_from (*bx).unwrap () + 1; } *self.reg_mut (*a + 3) = start.into (); }, Instruction::GetField (a, b, c) => { let t = match self.reg (*b) { Value::Table (t) => t, _ => panic! ("R[B] must be a table"), }; let key = match &k [usize::from (*c)] { Value::String (s) => s, _ => panic! ("K[C] must be a string"), }; let val = t.borrow ().get (Value::String (Rc::clone (key))); *self.reg_mut (*a) = val; }, Instruction::GetTable (a, b, c) => { let t = match self.reg (*b) { Value::Table (t) => t, _ => panic! ("R[B] must be a table"), }; let key = self.reg (*c); let val = t.borrow ().get (key.clone ()); *self.reg_mut (*a) = val; }, Instruction::GetTabUp (a, b, c) => { let b = usize::try_from (*b).unwrap (); let c = usize::try_from (*c).unwrap (); // If we're inside a closure, use its upvalues // instead of the chunk's upvalues let frame = self.stack.last ().unwrap (); let value = if frame.register_offset == 0 { self.upvalues.get (b).unwrap ().clone () } else if let Some (cell) = self.registers [frame.register_offset - 1].as_closure () { let closure = cell.borrow (); let value = closure.upvalues.get (b).unwrap (); value.clone () } else { self.upvalues.get (b).unwrap ().clone () }; let table = value.as_table ().expect ("GetTabUp only works on tables").borrow (); let key = match k.get (c).unwrap () { Value::String (s) => String::from (s.as_ref()), _ => panic! ("GetTabUp only supports string keys"), }; *self.reg_mut (*a) = table.get (key); }, Instruction::GetI (a, b, c) => { let key = i64::try_from (*c).unwrap (); let value = { let table = self.reg (*b).as_table ().expect ("GetI only works on tables").borrow (); table.get (key) }; *self.reg_mut (*a) = value; }, Instruction::GetUpVal (a, b) => { let this_func = self.stack.last ().unwrap ().register_offset - 1; let closure = match &self.registers [this_func] { Value::BogusClosure (rc) => rc.clone (), _ => panic! ("Can't do GetUpVal outside a closure"), }; let b = usize::try_from (*b).unwrap (); let upvalue = match closure.borrow ().upvalues.get (b) { Some (x) => x.clone (), None => { dbg! (chunk, &self); panic! ("Missing upvalue"); } }; *self.reg_mut (*a) = upvalue; }, Instruction::Jmp (s_j) => next_pc += s_j, Instruction::Len (a, b) => { let len = match self.reg (*b) { Value::String (s) => s.len (), _ => unimplemented!(), }; *self.reg_mut (*a) = len.into (); } Instruction::LoadF (a, sbx) => { *self.reg_mut (*a) = Value::Float (*sbx as f64); } Instruction::LoadFalse (a) => { *self.reg_mut (*a) = false.into (); }, Instruction::LoadI (a, sbx) => { *self.reg_mut (*a) = Value::Integer (*sbx as i64); }, Instruction::LoadK (a, bx) => { let bx = usize::try_from (*bx).unwrap (); *self.reg_mut (*a) = k [bx].clone (); }, Instruction::LoadNil (a) => { *self.reg_mut (*a) = Value::Nil; }, Instruction::LoadTrue (a) => { *self.reg_mut (*a) = true.into (); }, Instruction::MmBin (a, b, _c) => { let a = self.reg (*a); let b = self.reg (*b); if a.as_float().is_some() && b.as_float().is_some () { // No need for metamethods } else { panic! ("Not sure how to implememtn OP_MMBIN for these 2 values {a:?}, {b:?}"); } }, Instruction::MmBinK (_a, _b, _c, _k) => { // Ignore }, Instruction::ModK (a, b, c) => { let b = self.reg (*b).as_int().unwrap (); let c = k [usize::from (*c)].as_int ().unwrap (); *self.reg_mut (*a) = (b % c).into (); }, Instruction::Move (a, b) => { *self.reg_mut (*a) = self.reg (*b).clone (); }, Instruction::Mul (a, b, c) => { let v_b = self.reg (*b); let v_c = self.reg (*c); let x = if let (Some (v_b), Some (v_c)) = (v_b.as_int (), v_c.as_int ()) { Value::from (v_b * v_c) } else { let v_b = v_b.as_float ().unwrap_or_else (|| panic! ("{v_b}")); let v_c = v_c.as_float ().unwrap_or_else (|| panic! ("{v_c}")); Value::from (v_b * v_c) }; *self.reg_mut (*a) = x; }, Instruction::NewTable (a) => { *self.reg_mut (*a) = Value::Table (Default::default ()); }, Instruction::Not (a, b) => { *self.reg_mut (*a) = Value::Boolean (! self.reg (*b).is_truthy()); } Instruction::Return (a, b, _c, k) => { let a = usize::try_from (*a).unwrap (); let b = usize::try_from (*b).unwrap (); let popped_frame = self.stack.pop ().unwrap (); // Build closure if needed if *k { let closure_idx = match &self.registers [popped_frame.register_offset + a] { Value::BogusClosure (rc) => rc.borrow ().idx, _ => panic! ("Impossible"), }; let upvalue_count = chunk.blocks [closure_idx].upvalues.len (); let start_reg = a + popped_frame.register_offset - upvalue_count; let upvalues = self.registers [start_reg..start_reg+upvalue_count].iter ().cloned ().collect (); self.registers [a + popped_frame.register_offset] = Value::from (BogusClosure { idx: closure_idx, upvalues, }); } if self.debug_print { let old_block = popped_frame.block_idx; let old_pc = popped_frame.program_counter; println! ("Inst {old_block}:{old_pc} returns"); let stack_depth = self.stack.len (); println! ("stack_depth: {stack_depth}"); } if let Some (new_frame) = self.stack.last() { next_pc = new_frame.program_counter; // Shift our output registers down so the caller // can grab them // idk exactly why Lua does this // Register that our function was in before we // called it. let offset = popped_frame.register_offset - 1; for i in (offset)..(offset - 1 + b) { self.registers [i] = self.registers [i + 1 + a].take (); } } else { // Return from the entire program return Some (StepOutput::ChunkReturned (self.registers [a..(a + b - 1)].to_vec())); } }, Instruction::Return0 => { self.stack.pop (); next_pc = self.stack.last ().unwrap ().program_counter; }, Instruction::Return1 (a) => { let a = usize::try_from (*a).unwrap (); let popped_frame = self.stack.pop ().unwrap (); self.registers [popped_frame.register_offset - 1] = self.register_window ()[a].clone (); let frame = self.stack.last ().unwrap (); let new_block = frame.block_idx; next_pc = frame.program_counter; if self.debug_print { let old_block = popped_frame.block_idx; let old_pc = popped_frame.program_counter; println! ("Inst {old_block}:{old_pc} returns to inst {new_block}:{next_pc}"); let stack_depth = self.stack.len (); println! ("stack_depth: {stack_depth}"); } // Shift output register down let offset = popped_frame.register_offset; self.registers [offset - 1] = self.registers [offset + a].take (); }, Instruction::SetField (a, b, c, k_flag) => { let value = if *k_flag { &k [usize::from (*c)] } else { self.reg (*c) } .clone (); let b = usize::try_from (*b).unwrap (); let key = match k.get (b).unwrap () { Value::String (s) => s.as_ref (), _ => panic! ("GetTabUp only supports string keys"), }; let mut dst = self.reg (*a).as_table () .expect ("SetField only works on tables").borrow_mut (); dst.insert (Value::from (key.as_str ()), value); }, Instruction::SetI (a, b, c, k_flag) => { let value = if *k_flag { &k [usize::from (*c)] } else { self.reg (*c) } .clone (); let mut dst = self.reg_mut (*a).as_table ().expect ("SetI only works on tables").borrow_mut (); dst.insert (i64::from (*b), value); }, Instruction::SetList (a, b, c, k) => { if *b == 0 { panic! ("SetList with b == 0 not implemented"); } if *k { panic! ("SetList with k = true not implemented"); } let mut dst = self.reg (*a).as_table ().expect ("SetList only works on tables").borrow_mut (); for i in 1..=*b { let src = self.reg (*a + i); dst.insert (Value::from (i64::from (*c + i)), src.clone ()); } }, Instruction::SetTabUp (_a, _b, _c) => unimplemented! (), Instruction::TailCall (a, b, _c, k) => { assert! (!k, "closing over values in tail calls not implemented"); // Shift closure and inputs into place let a = usize::from (*a); let b = usize::from (*b); let offset = frame.register_offset - 1; for i in (offset)..(offset + b) { self.registers [i] = self.registers [i + a + 1].take (); } let value = &self.registers [offset]; let closure = if let Some (x) = value.as_closure () { x } else { dbg! (self); panic! ("OP_TAILCALL only implemented for closures"); }; let closure = closure.borrow (); // Jump into the other function let frame = self.stack.last_mut ().unwrap (); frame.program_counter = 0; frame.block_idx = closure.idx; // Skip the PC increment return None; }, Instruction::Test (a, _k) => { if self.reg (*a).is_truthy() { next_pc += 1; } }, Instruction::VarArgPrep (_) => (), } next_pc += 1; { let frame = self.stack.last_mut ().unwrap (); frame.program_counter = next_pc; } None } pub fn execute_chunk (&mut self) -> Vec { let max_iters = 2000; for _ in 0..max_iters { match self.step () { None => (), Some (StepOutput::ChunkReturned (x)) => return x, } } dbg! (self); panic! ("Hit max iterations before block returned"); } }