1052 lines
28 KiB
Rust
1052 lines
28 KiB
Rust
use std::rc::Rc;
|
|
|
|
use crate::{
|
|
instruction::Instruction,
|
|
string_interner::Interner,
|
|
value::{
|
|
BogusClosure,
|
|
Value,
|
|
},
|
|
};
|
|
|
|
#[derive (Clone, Debug)]
|
|
pub struct Upvalue {
|
|
pub in_stack: bool,
|
|
pub idx: u8,
|
|
pub kind: u8,
|
|
}
|
|
|
|
#[derive (Clone, Debug)]
|
|
pub struct Block {
|
|
pub instructions: Vec <Instruction>,
|
|
pub constants: Vec <Value>,
|
|
pub upvalues: Vec <Upvalue>,
|
|
}
|
|
|
|
#[derive (Clone, Debug, Default)]
|
|
pub struct Chunk {
|
|
pub blocks: Vec <Rc <Block>>,
|
|
}
|
|
|
|
#[derive (Clone, Copy, Debug, Default)]
|
|
pub struct StackFrame {
|
|
// Starts at 0 right after OP_CALL
|
|
|
|
program_counter: usize,
|
|
|
|
// 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: usize,
|
|
}
|
|
|
|
#[derive (Debug)]
|
|
pub struct State {
|
|
pub registers: Vec <Value>,
|
|
// Currently only used for native function calls
|
|
top: usize,
|
|
pub stack: Vec <StackFrame>,
|
|
stack_top: StackFrame,
|
|
|
|
pub debug_print: bool,
|
|
chunk: Chunk,
|
|
current_block: Rc <Block>,
|
|
pub upvalues: Vec <Value>,
|
|
pub si: Interner,
|
|
}
|
|
|
|
fn lw_io_write (l: &mut State, num_args: usize) -> usize {
|
|
for i in 0..u8::try_from (num_args).unwrap () {
|
|
match l.reg (i) {
|
|
Value::Float (x) => print! ("{}", x),
|
|
Value::Integer (x) => print! ("{}", x),
|
|
Value::String (x) => print! ("{}", l.si.get (*x)),
|
|
_ => panic! ("Can't io.write this value"),
|
|
}
|
|
}
|
|
// TODO: PUC Lua actually returns the file handle here.
|
|
0
|
|
}
|
|
|
|
fn lw_print (l: &mut State, num_args: usize) -> usize {
|
|
for i in 0..u8::try_from (num_args).unwrap () {
|
|
let input = l.reg (i);
|
|
|
|
if i == 0 {
|
|
print! ("{input}");
|
|
}
|
|
else {
|
|
print! ("\t{input}");
|
|
}
|
|
}
|
|
println! ("");
|
|
1
|
|
}
|
|
|
|
fn lw_sqrt (l: &mut State, num_args: usize) -> usize {
|
|
assert! (num_args >= 1, "math.sqrt needs 1 argument");
|
|
let input = l.reg (0).as_float ().unwrap ();
|
|
let output = input.sqrt ();
|
|
*l.reg_mut (0) = Value::from (output);
|
|
1
|
|
}
|
|
|
|
fn lw_string_format (l: &mut State, num_args: usize) -> usize {
|
|
assert! (num_args >= 1, "string.format needs at least 1 argument");
|
|
assert_eq! (l.get_top (), 2, "string.format not fully implemented");
|
|
let f_string = l.reg (0).as_str ().unwrap ();
|
|
assert_eq! (Value::String (f_string), l.to_string ("%0.9f"));
|
|
let num = l.reg (1).as_float ().unwrap ();
|
|
|
|
let output = format! ("{:0.9}", num);
|
|
|
|
*l.reg_mut (0) = l.to_string (&output);
|
|
1
|
|
}
|
|
|
|
fn lw_table_concat (l: &mut State, num_args: usize) -> usize {
|
|
assert_eq! (num_args, 2);
|
|
|
|
let s = {
|
|
let t = l.reg (0).as_table ().unwrap ().borrow ();
|
|
let joiner = l.reg (1).as_str ().unwrap ();
|
|
|
|
let mut s = String::new ();
|
|
|
|
for i in 0..t.length () {
|
|
if i > 0 {
|
|
s.push_str (l.si.get (joiner));
|
|
}
|
|
let x = t.get_int (i + 1);
|
|
s.push_str (&format! ("{}", x));
|
|
}
|
|
s
|
|
};
|
|
*l.reg_mut (0) = l.to_string (&s);
|
|
1
|
|
}
|
|
|
|
fn lw_table_pack (l: &mut State, num_args: usize) -> usize {
|
|
let mut v = vec! [];
|
|
for i in 0..num_args {
|
|
v.push (l.reg (u8::try_from (i).unwrap ()).clone ());
|
|
}
|
|
*l.reg_mut (0) = Value::from_iter (v.into_iter ());
|
|
1
|
|
}
|
|
|
|
fn tonumber (l: &State, value: &Value) -> Value {
|
|
match value {
|
|
Value::Float (x) => Value::Float (*x),
|
|
Value::Integer (x) => Value::Integer (*x),
|
|
Value::String (x) => {
|
|
if let Ok (x) = str::parse::<i64> (l.si.get (*x)) {
|
|
Value::from (x)
|
|
}
|
|
else if let Ok (x) = str::parse::<f64> (l.si.get (*x)) {
|
|
Value::from (x)
|
|
}
|
|
else {
|
|
Value::Nil
|
|
}
|
|
},
|
|
_ => Value::Nil,
|
|
}
|
|
}
|
|
|
|
fn lw_tonumber (l: &mut State, num_args: usize) -> usize {
|
|
assert_eq! (num_args, 1, "tonumber only implemented for 1 argument");
|
|
let output = tonumber (&l, l.reg (0));
|
|
*l.reg_mut (0) = output;
|
|
1
|
|
}
|
|
|
|
pub enum StepOutput {
|
|
ChunkReturned (Vec <Value>),
|
|
}
|
|
|
|
#[derive (Debug, thiserror::Error)]
|
|
pub enum StepError {
|
|
#[error ("generic")]
|
|
Generic {
|
|
frame: StackFrame,
|
|
inst: Instruction,
|
|
msg: &'static str,
|
|
},
|
|
}
|
|
|
|
impl State {
|
|
pub fn new (chunk: Chunk, upvalues: Vec <Value>) -> Self {
|
|
let current_block = Rc::clone (&chunk.blocks [0]);
|
|
|
|
Self {
|
|
// TODO: Stack is actually supposed to grow to a limit of
|
|
// idk 10,000. I thought it was fixed at 256.
|
|
registers: vec! [Value::Nil; 256],
|
|
top: 0,
|
|
stack: vec! [],
|
|
stack_top: Default::default (),
|
|
debug_print: false,
|
|
chunk,
|
|
current_block,
|
|
upvalues,
|
|
si: Default::default (),
|
|
}
|
|
}
|
|
|
|
pub fn new_with_args <I: Iterator <Item = String>> (chunk: Chunk, mut si: Interner, args: I) -> Self {
|
|
let upvalues = Self::upvalues_from_args (&mut si, args);
|
|
let current_block = Rc::clone (&chunk.blocks [0]);
|
|
|
|
Self {
|
|
// TODO: Stack is actually supposed to grow to a limit of
|
|
// idk 10,000. I thought it was fixed at 256.
|
|
registers: vec! [Value::Nil; 256],
|
|
top: 0,
|
|
stack: vec! [],
|
|
stack_top: Default::default (),
|
|
debug_print: false,
|
|
chunk,
|
|
current_block,
|
|
upvalues,
|
|
si,
|
|
}
|
|
}
|
|
|
|
pub fn at_breakpoint (&self, bp: &Breakpoint) -> bool {
|
|
let frame = &self.stack_top;
|
|
frame.block_idx == bp.block_idx && frame.program_counter == bp.program_counter
|
|
}
|
|
|
|
pub fn upvalues_from_args <I: Iterator <Item = String>> (si: &mut Interner, args: I) -> Vec <Value>
|
|
{
|
|
let arg = args.map (|s| si.intern (&s)).enumerate ();
|
|
let arg = Value::from_iter (arg.map (|(i, v)| (Value::from (i), Value::String (v))));
|
|
|
|
let io: Vec <_> = [
|
|
("write", Value::RsFunc (lw_io_write)),
|
|
].into_iter ().map (|(k, v)| (si.intern (k), v)).collect ();
|
|
|
|
let math: Vec <_> = [
|
|
("sqrt", Value::RsFunc (lw_sqrt)),
|
|
].into_iter ().map (|(k, v)| (si.intern (k), v)).collect ();
|
|
|
|
let string: Vec <_> = [
|
|
("format", Value::RsFunc (lw_string_format)),
|
|
].into_iter ().map (|(k, v)| (si.intern (k), v)).collect ();
|
|
|
|
let table: Vec <_> = [
|
|
("concat", Value::RsFunc (lw_table_concat)),
|
|
("pack", Value::RsFunc (lw_table_pack)),
|
|
].into_iter ().map (|(k, v)| (si.intern (k), v)).collect ();
|
|
|
|
let env = [
|
|
("arg", arg),
|
|
("io", Value::from_iter (io.into_iter ())),
|
|
("math", Value::from_iter (math.into_iter ())),
|
|
("print", Value::RsFunc (lw_print)),
|
|
("string", Value::from_iter (string.into_iter ())),
|
|
("table", Value::from_iter (table.into_iter ())),
|
|
("tonumber", Value::RsFunc (lw_tonumber)),
|
|
].into_iter ().map (|(k, v)| (si.intern (k), v));
|
|
|
|
vec! [
|
|
Value::from_iter (env.into_iter ()),
|
|
]
|
|
}
|
|
|
|
fn register_window (&self) -> &[Value] {
|
|
let frame = &self.stack_top;
|
|
&self.registers [frame.register_offset..]
|
|
}
|
|
|
|
/// Short form to get access to a register within our window
|
|
|
|
pub fn reg (&self, i: u8) -> &Value {
|
|
let idx = self.stack_top.register_offset + i as usize;
|
|
&self.registers [idx]
|
|
}
|
|
|
|
pub fn reg_mut (&mut self, i: u8) -> &mut Value {
|
|
&mut self.registers [self.stack_top.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_top.register_offset
|
|
}
|
|
|
|
fn make_step_error (&self, msg: &'static str, inst: &Instruction) -> StepError
|
|
{
|
|
StepError::Generic {
|
|
frame: self.stack_top.clone (),
|
|
inst: inst.clone (),
|
|
msg,
|
|
}
|
|
}
|
|
|
|
fn op_add (&mut self, a: u8, b: u8, c: u8) -> bool {
|
|
let v_b = self.reg (b);
|
|
let v_c = self.reg (c);
|
|
|
|
*self.reg_mut (a) = match (v_b, v_c) {
|
|
(Value::Float (b), Value::Float (c)) => Value::from (b + c),
|
|
(Value::Integer (b), Value::Integer (c)) => Value::from (b + c),
|
|
(Value::Integer (b), Value::Float (c)) => Value::from (*b as f64 + c),
|
|
(Value::Float (b), Value::Integer (c)) => Value::from (b + *c as f64),
|
|
_ => return false,
|
|
};
|
|
|
|
true
|
|
}
|
|
|
|
fn op_call (&mut self, a: u8, b: u8, c: u8) -> bool {
|
|
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 target_block = idx;
|
|
|
|
self.stack.push (self.stack_top);
|
|
self.set_stack_top (StackFrame {
|
|
program_counter: 0,
|
|
block_idx: target_block,
|
|
register_offset: self.stack_top.register_offset + a as usize + 1,
|
|
});
|
|
|
|
// Skip the PC increment at the bottom of the loop
|
|
return true;
|
|
},
|
|
Value::RsFunc (x) => {
|
|
let old_offset = self.stack_top.register_offset;
|
|
self.stack_top.register_offset = old_offset + usize::from (a) + 1;
|
|
|
|
// Trash the stack frame so it doesn't point to a
|
|
// valid Lua function
|
|
self.stack.push (self.stack_top);
|
|
self.stack_top = StackFrame {
|
|
program_counter: 65535, // Bogus for native functions
|
|
block_idx: 65535, // Bogus
|
|
register_offset: self.stack_top.register_offset,
|
|
};
|
|
|
|
let num_args = if b == 0 {
|
|
self.top - self.stack_top.register_offset
|
|
}
|
|
else {
|
|
b - 1
|
|
};
|
|
|
|
// Call
|
|
let num_results = x (self, num_args);
|
|
|
|
let popped_frame = self.stack_top;
|
|
let x = self.stack.pop ().unwrap ();
|
|
self.set_stack_top (x);
|
|
self.stack_top.register_offset = old_offset;
|
|
let offset = old_offset + usize::from (a);
|
|
|
|
for i in (offset)..(offset + usize::try_from (num_results).unwrap ()) {
|
|
self.registers [i] = self.registers [i + 1].take ();
|
|
}
|
|
|
|
// Set up top for the next call
|
|
if c == 0 {
|
|
self.top = popped_frame.register_offset - 1 + num_results;
|
|
}
|
|
},
|
|
x => {
|
|
panic! ("Cannot call value {x:?}");
|
|
},
|
|
}
|
|
|
|
false
|
|
}
|
|
|
|
fn op_div (&mut self, a: u8, b: u8, c: u8) -> bool {
|
|
let v_b = self.reg (b);
|
|
let v_c = self.reg (c);
|
|
|
|
*self.reg_mut (a) = match (v_b, v_c) {
|
|
(Value::Float (b), Value::Float (c)) => Value::from (b / c),
|
|
(Value::Integer (b), Value::Integer (c)) => Value::from (*b as f64 / *c as f64),
|
|
(Value::Integer (b), Value::Float (c)) => Value::from (*b as f64 / c),
|
|
(Value::Float (b), Value::Integer (c)) => Value::from (b / *c as f64),
|
|
_ => return false,
|
|
};
|
|
|
|
true
|
|
}
|
|
|
|
fn op_get_field (&mut self, a: u8, b: u8, c: u8) {
|
|
let block = &self.current_block;
|
|
let constants = &block.constants;
|
|
|
|
let key = match &constants [usize::from (c)] {
|
|
Value::String (s) => s,
|
|
_ => panic! ("K[C] must be a string"),
|
|
};
|
|
|
|
let val = match &self.registers [self.stack_top.register_offset + usize::from (b)] {
|
|
Value::Nil => panic! ("R[B] must not be nil"),
|
|
Value::Table (t) => t.borrow ().get_str (*key).clone (),
|
|
_ => panic! ("R[B] must be a table"),
|
|
};
|
|
|
|
*self.reg_mut (a) = val;
|
|
}
|
|
|
|
fn op_get_table (&mut self, a: u8, b: u8, c: u8) {
|
|
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 ()).clone ();
|
|
|
|
*self.reg_mut (a) = val;
|
|
}
|
|
|
|
fn op_mmbin (&mut self, a: u8, b: u8, _c: u8) {
|
|
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 implement OP_MMBIN for these 2 values {a:?}, {b:?}");
|
|
}
|
|
}
|
|
|
|
fn op_mul (&mut self, a: u8, b: u8, c: u8) -> bool {
|
|
let v_b = self.reg (b);
|
|
let v_c = self.reg (c);
|
|
|
|
*self.reg_mut (a) = match (v_b, v_c) {
|
|
(Value::Float (b), Value::Float (c)) => Value::from (b * c),
|
|
(Value::Integer (b), Value::Integer (c)) => Value::from (b * c),
|
|
(Value::Integer (b), Value::Float (c)) => Value::from (*b as f64 * c),
|
|
(Value::Float (b), Value::Integer (c)) => Value::from (b * *c as f64),
|
|
_ => return false,
|
|
};
|
|
|
|
true
|
|
}
|
|
|
|
fn op_set_field (&mut self, a: u8, b: u8, c: u8, k: bool) {
|
|
let block = &self.current_block;
|
|
let constants = &block.constants;
|
|
|
|
let b = usize::try_from (b).unwrap ();
|
|
|
|
let key = match constants.get (b).unwrap () {
|
|
Value::String (s) => *s,
|
|
_ => panic! ("SetField only supports string keys"),
|
|
};
|
|
|
|
let value = if k {
|
|
&constants [usize::from (c)]
|
|
}
|
|
else {
|
|
self.reg_mut (c)
|
|
}.clone ();
|
|
|
|
let mut dst = self.reg (a).as_table ()
|
|
.expect ("SetField only works on tables").borrow_mut ();
|
|
|
|
dst.insert_str (key, value);
|
|
}
|
|
|
|
fn op_sub (&mut self, a: u8, b: u8, c: u8) -> bool {
|
|
let v_b = self.reg (b);
|
|
let v_c = self.reg (c);
|
|
|
|
*self.reg_mut (a) = match (v_b, v_c) {
|
|
(Value::Float (b), Value::Float (c)) => Value::from (b - c),
|
|
(Value::Integer (b), Value::Integer (c)) => Value::from (b - c),
|
|
(Value::Integer (b), Value::Float (c)) => Value::from (*b as f64 - c),
|
|
(Value::Float (b), Value::Integer (c)) => Value::from (b - *c as f64),
|
|
_ => return false,
|
|
};
|
|
|
|
true
|
|
}
|
|
|
|
fn constants (&self) -> &[Value] {
|
|
&self.current_block.constants
|
|
}
|
|
|
|
fn set_block_idx (&mut self, block_idx: usize) {
|
|
self.stack_top.block_idx = block_idx;
|
|
self.current_block = Rc::clone (&self.chunk.blocks [block_idx]);
|
|
}
|
|
|
|
fn set_stack_top (&mut self, frame: StackFrame) {
|
|
self.stack_top = frame;
|
|
self.current_block = Rc::clone (&self.chunk.blocks [frame.block_idx]);
|
|
}
|
|
|
|
fn fetch (&self) -> &Instruction {
|
|
match self.current_block.instructions.get (self.stack_top.program_counter) {
|
|
Some (x) => x,
|
|
None => {
|
|
dbg! (&self.stack, &self.stack_top);
|
|
panic! ("program_counter went out of bounds");
|
|
}
|
|
}
|
|
}
|
|
|
|
fn incr_pc (&mut self) {
|
|
self.stack_top.program_counter += 1;
|
|
}
|
|
|
|
pub fn step (&mut self) -> Result <Option <StepOutput>, StepError>
|
|
{
|
|
let instruction = self.fetch ();
|
|
|
|
let make_step_error = |msg| {
|
|
self.make_step_error (msg, &instruction)
|
|
};
|
|
|
|
match *instruction {
|
|
Instruction::Add (a, b, c) => {
|
|
if self.op_add (a, b, c) {
|
|
self.stack_top.program_counter += 1;
|
|
}
|
|
},
|
|
Instruction::AddI (a, b, s_c) => {
|
|
let v_b = self.reg (b);
|
|
|
|
*self.reg_mut (a) = match v_b {
|
|
Value::Integer (v_b) => Value::from (v_b + s_c as i64),
|
|
Value::Float (v_b) => Value::from (v_b + s_c as f64),
|
|
x => panic! ("{x}"),
|
|
};
|
|
},
|
|
Instruction::Call (a, b, c) => {
|
|
if self.op_call (a, b, c) {
|
|
// Skip the PC increment at the bottom
|
|
return Ok (None);
|
|
}
|
|
},
|
|
Instruction::Closure (a, b) => {
|
|
let b = usize::try_from (b).unwrap ();
|
|
|
|
let idx = self.stack_top.block_idx + b + 1;
|
|
let block = &self.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::Concat (_a, _b) => {
|
|
unimplemented! ("OP_CONCAT")
|
|
},
|
|
Instruction::Div (a, b, c) => {
|
|
if self.op_div (a, b, c) {
|
|
self.stack_top.program_counter += 1;
|
|
}
|
|
},
|
|
Instruction::EqI (a, sb, k_flag) => {
|
|
if (self.reg (a).as_int ().unwrap () == sb as i64) != k_flag
|
|
{
|
|
self.stack_top.program_counter += 1;
|
|
}
|
|
},
|
|
Instruction::EqK (a, b, k) => {
|
|
let b = usize::from (b);
|
|
|
|
if (*self.reg (a) == self.constants ()[b]) != k {
|
|
self.stack_top.program_counter += 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 {
|
|
self.stack_top.program_counter -= usize::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 {
|
|
self.stack_top.program_counter += usize::try_from (bx).unwrap () + 1;
|
|
}
|
|
|
|
*self.reg_mut (a + 3) = start.into ();
|
|
},
|
|
Instruction::GetField (a, b, c) => {
|
|
self.op_get_field (a, b, c);
|
|
},
|
|
Instruction::GetTable (a, b, c) => {
|
|
self.op_get_table (a, b, c);
|
|
},
|
|
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_top;
|
|
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 self.constants ().get (c).unwrap () {
|
|
Value::String (s) => *s,
|
|
_ => panic! ("GetTabUp only supports string keys"),
|
|
};
|
|
|
|
*self.reg_mut (a) = table.get_str (key).clone ();
|
|
},
|
|
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_int (key).clone ()
|
|
};
|
|
|
|
*self.reg_mut (a) = value;
|
|
},
|
|
Instruction::GetUpVal (a, b) => {
|
|
let this_func = self.stack_top.register_offset - 1;
|
|
let closure = match &self.registers [this_func] {
|
|
Value::BogusClosure (rc) => rc,
|
|
_ => 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! (&self);
|
|
panic! ("Missing upvalue");
|
|
}
|
|
};
|
|
*self.reg_mut (a) = upvalue;
|
|
},
|
|
Instruction::Jmp (s_j) => self.stack_top.program_counter = usize::try_from (i32::try_from (self.stack_top.program_counter).unwrap () + s_j).unwrap (),
|
|
Instruction::Len (a, b) => {
|
|
let len = match self.reg (b) {
|
|
Value::BogusClosure (_) => Err (make_step_error ("attempt to get length of a function value"))?,
|
|
Value::Boolean (_) => Err (make_step_error ("attempt to get length of a boolean value"))?,
|
|
Value::Float (_) => Err (make_step_error ("attempt to get length of a number value"))?,
|
|
Value::Integer (_) => Err (make_step_error ("attempt to get length of a number value"))?,
|
|
Value::Nil => Err (make_step_error ("attempt to get length of a nil value"))?,
|
|
Value::RsFunc (_) => Err (make_step_error ("attempt to get length of a function value"))?,
|
|
Value::String (s) => self.si.get (*s).len ().into (),
|
|
Value::Table (t) => t.borrow ().length ().into (),
|
|
};
|
|
|
|
*self.reg_mut (a) = len;
|
|
}
|
|
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) = self.constants ()[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) => {
|
|
self.op_mmbin (a, b, c);
|
|
},
|
|
Instruction::MmBinI (_a, _s_b, _c, _k) => {
|
|
// Ignore
|
|
},
|
|
Instruction::MmBinK (_a, _b, _c, _k) => {
|
|
// Ignore
|
|
},
|
|
Instruction::ModK (a, b, c) => {
|
|
let b = self.reg (b).as_int().unwrap ();
|
|
let c = self.constants ()[usize::from (c)].as_int ().unwrap ();
|
|
|
|
*self.reg_mut (a) = (b % c).into ();
|
|
},
|
|
Instruction::Move (a, b) => {
|
|
// If the value in b is deleted instead of duplicated,
|
|
// a bunch of tests fail
|
|
|
|
*self.reg_mut (a) = self.reg (b).clone ();
|
|
},
|
|
Instruction::Mul (a, b, c) => {
|
|
// If we handled the mul as a regular int or float,
|
|
// skip the OP_MMBIN that probably comes after this
|
|
|
|
if self.op_mul (a, b, c) {
|
|
self.stack_top.program_counter += 1;
|
|
}
|
|
},
|
|
Instruction::MulK (a, b, c) => {
|
|
let v_b = self.reg (b);
|
|
let v_c = &self.constants ()[usize::from (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_flag) => {
|
|
let a = usize::try_from (a).unwrap ();
|
|
let b = usize::try_from (b).unwrap ();
|
|
|
|
let popped_frame = self.stack_top;
|
|
|
|
// Build closure if needed. No point building if we're
|
|
// popping the last frame and exiting the program.
|
|
|
|
if k_flag && ! self.stack.is_empty () {
|
|
let closure_idx = match &self.registers [popped_frame.register_offset + a] {
|
|
Value::BogusClosure (rc) => rc.borrow ().idx,
|
|
_ => panic! ("Impossible"),
|
|
};
|
|
|
|
let upvalue_count = self.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.pop () {
|
|
self.set_stack_top (new_frame);
|
|
|
|
// 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 ();
|
|
}
|
|
|
|
self.top = popped_frame.register_offset - 1 + b - 1;
|
|
}
|
|
else {
|
|
// Return from the entire chunk
|
|
return Ok (Some (StepOutput::ChunkReturned (self.registers [a..(a + b - 1)].to_vec())));
|
|
}
|
|
},
|
|
Instruction::Return0 => {
|
|
let popped_frame = self.stack_top;
|
|
let x = self.stack.pop ().unwrap ();
|
|
self.set_stack_top (x);
|
|
self.top = popped_frame.register_offset - 1 + 0;
|
|
},
|
|
Instruction::Return1 (a) => {
|
|
let a = usize::try_from (a).unwrap ();
|
|
let popped_frame = self.stack_top;
|
|
|
|
self.registers [popped_frame.register_offset - 1] = self.register_window ()[a].clone ();
|
|
|
|
let x = self.stack.pop ().unwrap ();
|
|
self.set_stack_top (x);
|
|
|
|
// Shift output register down
|
|
let offset = popped_frame.register_offset;
|
|
self.registers [offset - 1] = self.registers [offset + a].take ();
|
|
|
|
self.top = popped_frame.register_offset - 1 + 1;
|
|
},
|
|
Instruction::SetField (a, b, c, k_flag) => {
|
|
self.op_set_field (a, b, c, k_flag);
|
|
},
|
|
Instruction::SetI (a, b, c, k_flag) => {
|
|
let value = if k_flag {
|
|
&self.constants ()[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_int (i64::from (b), value);
|
|
},
|
|
Instruction::SetList (a, b, c, k_flag) => {
|
|
if b == 0 {
|
|
panic! ("SetList with b == 0 not implemented");
|
|
}
|
|
if k_flag {
|
|
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_int (i64::from (c + i), src.clone ());
|
|
}
|
|
},
|
|
Instruction::SetTabUp (a, b, c, k_flag) => {
|
|
let a = usize::try_from (a).unwrap ();
|
|
let b = usize::try_from (b).unwrap ();
|
|
|
|
let value = if k_flag {
|
|
&self.constants ()[usize::from (c)]
|
|
}
|
|
else {
|
|
self.reg (c)
|
|
}
|
|
.clone ();
|
|
|
|
let key = self.constants ().get (b).unwrap ().as_str ().expect ("SetTabUp K[B] must be a string");
|
|
let table = self.upvalues.get_mut (a).unwrap ().as_table ().unwrap ();
|
|
table.borrow_mut ().insert_str (key, value);
|
|
},
|
|
Instruction::Sub (a, b, c) => {
|
|
if self.op_sub (a, b, c) {
|
|
self.stack_top.program_counter += 1;
|
|
}
|
|
},
|
|
Instruction::TailCall (a, b, c, k) => {
|
|
let a = usize::from (a);
|
|
assert! (!k, "closing over values in tail calls not implemented");
|
|
|
|
let offset = self.stack_top.register_offset;
|
|
let value = self.registers [offset + a].take ();
|
|
match value {
|
|
Value::BogusClosure (closure) => {
|
|
let closure = closure.borrow ();
|
|
|
|
// Shift inputs into place
|
|
|
|
let b = usize::from (b);
|
|
|
|
let num_args = if b == 0 {
|
|
self.top - a
|
|
}
|
|
else {
|
|
b - 1
|
|
};
|
|
|
|
for i in (offset)..(offset + num_args) {
|
|
self.registers [i] = self.registers [i + a + 1].take ();
|
|
}
|
|
|
|
// Jump into the other function
|
|
|
|
self.set_stack_top (StackFrame {
|
|
block_idx: closure.idx,
|
|
program_counter: 0,
|
|
register_offset: self.stack_top.register_offset,
|
|
});
|
|
|
|
// Skip the PC increment
|
|
return Ok (None);
|
|
},
|
|
Value::RsFunc (x) => {
|
|
// Shift inputs into place
|
|
let b = usize::from (b);
|
|
for i in (offset)..(offset + b) {
|
|
self.registers [i] = self.registers [i + a + 1].take ();
|
|
}
|
|
|
|
// Trash the stack frame so it doesn't point
|
|
// to any valid Lua function
|
|
self.stack_top = StackFrame {
|
|
block_idx: 65535,
|
|
program_counter: 65535,
|
|
register_offset: offset,
|
|
};
|
|
|
|
let num_args = if b == 0 {
|
|
self.top - a
|
|
}
|
|
else {
|
|
b - 1
|
|
};
|
|
|
|
// Call
|
|
let num_results = x (self, num_args);
|
|
|
|
// Pop and handle outputs
|
|
|
|
let popped_frame = self.stack_top;
|
|
|
|
if let Some (new_frame) = self.stack.pop () {
|
|
self.set_stack_top (new_frame);
|
|
|
|
// Set up top for the next call
|
|
if c == 0 {
|
|
self.top = popped_frame.register_offset - 1 + num_results;
|
|
}
|
|
}
|
|
else {
|
|
// The whole chunk is exiting
|
|
return Ok (Some (StepOutput::ChunkReturned (self.registers [a..(a + num_results)].to_vec())));
|
|
}
|
|
},
|
|
_ => {
|
|
dbg! (&self.stack, &self.stack_top);
|
|
panic! ("OP_TAILCALL argument must be a function");
|
|
},
|
|
}
|
|
},
|
|
Instruction::Test (a, k_flag) => {
|
|
if self.reg (a).is_truthy() != k_flag {
|
|
self.stack_top.program_counter += 1;
|
|
}
|
|
},
|
|
Instruction::UnM (a, b) => {
|
|
let v_b = self.reg (b);
|
|
|
|
let x = if let Some (v_b) = v_b.as_int ()
|
|
{
|
|
Value::from (-v_b)
|
|
}
|
|
else {
|
|
let v_b = v_b.as_float ().unwrap_or_else (|| panic! ("{v_b}"));
|
|
Value::from (-v_b)
|
|
};
|
|
|
|
*self.reg_mut (a) = x;
|
|
},
|
|
Instruction::VarArgPrep (_) => (),
|
|
}
|
|
|
|
self.incr_pc ();
|
|
|
|
Ok (None)
|
|
}
|
|
|
|
pub fn eval (&mut self, src: &str) -> Result <Vec <Value>, crate::Error>
|
|
{
|
|
let bytecode = crate::compile_bytecode (src.as_bytes ().to_vec ())?;
|
|
let chunk = crate::parse_chunk (&bytecode, &mut self.si).unwrap ();
|
|
|
|
self.set_chunk (chunk);
|
|
Ok (self.execute ()?)
|
|
}
|
|
|
|
pub fn execute (&mut self)
|
|
-> Result <Vec <Value>, StepError> {
|
|
let max_iters = 2000;
|
|
|
|
for _ in 0..max_iters {
|
|
match self.step ()? {
|
|
None => (),
|
|
Some (StepOutput::ChunkReturned (x)) => return Ok (x),
|
|
}
|
|
}
|
|
|
|
dbg! (self);
|
|
panic! ("Hit max iterations before block returned");
|
|
}
|
|
|
|
pub fn set_chunk (&mut self, chunk: Chunk) {
|
|
self.stack = vec! [];
|
|
self.set_stack_top (Default::default ());
|
|
self.chunk = chunk;
|
|
}
|
|
|
|
pub fn to_string (&mut self, s: &str) -> Value {
|
|
Value::String (self.si.intern (s))
|
|
}
|
|
}
|