diff --git a/src/main.rs b/src/main.rs index acd91dd..69c4ea4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -57,14 +57,16 @@ fn main () { dbg! (&chunk); } - let mut vm = State::default (); + let upvalues = State::upvalues_from_args ([exe_name].into_iter ().chain (args)); + + let mut vm = State::new (&chunk, &upvalues); if std::env::var("LUA_DEBUG").is_ok() { vm.debug_print = true; } - let upvalues = State::upvalues_from_args ([exe_name].into_iter ().chain (args)); + vm.breakpoints = breakpoints; - vm.execute_chunk (&chunk, &upvalues); + vm.execute_chunk (); } diff --git a/src/state.rs b/src/state.rs index 8b42999..edeeeff 100644 --- a/src/state.rs +++ b/src/state.rs @@ -47,7 +47,7 @@ pub struct Breakpoint { } #[derive (Debug)] -pub struct State { +pub struct State <'a> { registers: Vec , // Currently only used for native function calls top: usize, @@ -56,25 +56,8 @@ pub struct State { pub debug_print: bool, pub breakpoints: Vec , step_count: u32, -} - -impl Default for State { - fn default () -> 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: &'a Chunk, + upvalues: &'a [Value], } fn lw_print (l: &mut State) -> i32 { @@ -93,7 +76,30 @@ fn lw_print (l: &mut State) -> i32 { 1 } -impl State { +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 (); @@ -131,517 +137,527 @@ impl State { self.top - self.stack.last ().unwrap ().register_offset } - pub fn execute_chunk (&mut self, chunk: &Chunk, upvalues: &[Value]) + 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 { - 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 - continue; - }, - 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 - 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 { - 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 { - 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 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 - continue; - }, - 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; + match self.step () { + None => (), + Some (StepOutput::ChunkReturned (x)) => return x, } } diff --git a/src/tests.rs b/src/tests.rs index 5956353..92e24ff 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -23,9 +23,9 @@ fn calculate_hash(t: &T) -> u64 { /// and returns the output fn run_chunk (args: &[&str], chunk: &Chunk) -> Vec { - let mut vm = State::default (); let upvalues = State::upvalues_from_args (args.into_iter ().map (|s| s.to_string ())); - vm.execute_chunk (chunk, &upvalues) + let mut vm = State::new (chunk, &upvalues); + vm.execute_chunk () } /// Takes arguments and Lua bytecode, loads it, runs it, @@ -118,10 +118,9 @@ fn bools () { ] { let expected: Vec = expected; - let mut vm = State::default (); let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ())); - - let actual = vm.execute_chunk (&chunk, &upvalues); + let mut vm = State::new (&chunk, &upvalues); + let actual = vm.execute_chunk (); assert_eq! (actual, expected); } } @@ -175,9 +174,9 @@ fn floats () { (vec! ["_exe_name", " "], vec! [3.5.into ()]), ] { let expected: Vec = expected; - let mut vm = State::default (); let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ())); - let actual = vm.execute_chunk (&chunk, &upvalues); + let mut vm = State::new (&chunk, &upvalues); + let actual = vm.execute_chunk (); assert_eq! (actual, expected); } @@ -197,9 +196,9 @@ fn fma () { (vec! ["_exe_name"], vec! [122.into ()]), ] { let expected: Vec = expected; - let mut vm = State::default (); let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ())); - let actual = vm.execute_chunk (&chunk, &upvalues); + let mut vm = State::new (&chunk, &upvalues); + let actual = vm.execute_chunk (); assert_eq! (actual, expected); }