668 lines
17 KiB
Rust
668 lines
17 KiB
Rust
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 <Instruction>,
|
|
pub constants: Vec <Value>,
|
|
pub upvalues: Vec <Upvalue>,
|
|
}
|
|
|
|
#[derive (Debug)]
|
|
pub struct Chunk {
|
|
pub blocks: Vec <Block>,
|
|
}
|
|
|
|
#[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 <Value>,
|
|
// Currently only used for native function calls
|
|
top: usize,
|
|
stack: Vec <StackFrame>,
|
|
|
|
pub debug_print: bool,
|
|
pub breakpoints: Vec <Breakpoint>,
|
|
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 <Value>),
|
|
}
|
|
|
|
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 <I: Iterator <Item = String>> (args: I) -> Vec <Value>
|
|
{
|
|
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 <StepOutput> {
|
|
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 <Value> {
|
|
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");
|
|
}
|
|
}
|