🚧 wip: got the closure test working
parent
543dab360b
commit
1518781753
|
@ -20,19 +20,25 @@ pub fn parse_inst (buf: [u8; 4]) -> Option <Inst>
|
||||||
let bx = bx.try_into().ok ()?;
|
let bx = bx.try_into().ok ()?;
|
||||||
let sbx = bx - 65535;
|
let sbx = bx - 65535;
|
||||||
let k = (buf [1] & 0x80) >> 7 == 1;
|
let k = (buf [1] & 0x80) >> 7 == 1;
|
||||||
|
let s_j = a as i32 + ((b as i32) << 8) + 1;
|
||||||
|
|
||||||
Some (match opcode {
|
Some (match opcode {
|
||||||
0x00 => Inst::Move (a, b),
|
0x00 => Inst::Move (a, b),
|
||||||
0x01 => Inst::LoadI (a, sbx),
|
0x01 => Inst::LoadI (a, sbx),
|
||||||
0x03 => Inst::LoadK (a, bx),
|
0x03 => Inst::LoadK (a, bx),
|
||||||
0x09 => Inst::GetUpVal (a, b),
|
0x09 => Inst::GetUpVal (a, b),
|
||||||
0x0b => Inst::GetTabUp (a, 0, 0),
|
0x0b => Inst::GetTabUp (a, b, c),
|
||||||
|
0x0d => Inst::GetI (a, b, c),
|
||||||
|
0x22 => Inst::Add (a, b, c),
|
||||||
|
0x2e => Inst::MmBin (a, b, c),
|
||||||
|
0x3c => Inst::EqK (a, b, c),
|
||||||
|
0x38 => Inst::Jmp (s_j),
|
||||||
0x44 => Inst::Call (a, b, c),
|
0x44 => Inst::Call (a, b, c),
|
||||||
0x46 => Inst::Return (a, b, c, k),
|
0x46 => Inst::Return (a, b, c, k),
|
||||||
0x47 => Inst::Return0,
|
0x47 => Inst::Return0,
|
||||||
0x48 => Inst::Return1 (a),
|
0x48 => Inst::Return1 (a),
|
||||||
0x4f => Inst::Closure (0, 0),
|
0x4f => Inst::Closure (a, bx),
|
||||||
0x51 => Inst::VarArgPrep (0),
|
0x51 => Inst::VarArgPrep (a.into ()),
|
||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -95,7 +101,7 @@ pub fn parse_block <R: Read> (rdr: &mut R) -> Option <Block>
|
||||||
for _ in 0..header.inst_count {
|
for _ in 0..header.inst_count {
|
||||||
let mut buf = [0u8; 4];
|
let mut buf = [0u8; 4];
|
||||||
rdr.read_exact (&mut buf).ok ()?;
|
rdr.read_exact (&mut buf).ok ()?;
|
||||||
instructions.push (parse_inst (buf)?);
|
instructions.push (parse_inst (buf).expect (&format! ("{buf:?}")));
|
||||||
}
|
}
|
||||||
|
|
||||||
let constant_count = {
|
let constant_count = {
|
||||||
|
@ -164,6 +170,7 @@ mod tests {
|
||||||
for (input, expected) in [
|
for (input, expected) in [
|
||||||
([0x51, 0x00, 0x00, 0x00], Inst::VarArgPrep (0)),
|
([0x51, 0x00, 0x00, 0x00], Inst::VarArgPrep (0)),
|
||||||
([0x4f, 0x00, 0x00, 0x00], Inst::Closure (0, 0)),
|
([0x4f, 0x00, 0x00, 0x00], Inst::Closure (0, 0)),
|
||||||
|
([0xcf, 0x00, 0x00, 0x00], Inst::Closure (1, 0)),
|
||||||
([0x8b, 0x00, 0x00, 0x00], Inst::GetTabUp (1, 0, 0)),
|
([0x8b, 0x00, 0x00, 0x00], Inst::GetTabUp (1, 0, 0)),
|
||||||
([0x03, 0x81, 0x00, 0x00], Inst::LoadK (2, 1)),
|
([0x03, 0x81, 0x00, 0x00], Inst::LoadK (2, 1)),
|
||||||
([0xc4, 0x00, 0x02, 0x01], Inst::Call (1, 2, 1)),
|
([0xc4, 0x00, 0x02, 0x01], Inst::Call (1, 2, 1)),
|
||||||
|
@ -187,6 +194,10 @@ mod tests {
|
||||||
([0x09, 0x00, 0x01, 0x00], Inst::GetUpVal (0, 1)),
|
([0x09, 0x00, 0x01, 0x00], Inst::GetUpVal (0, 1)),
|
||||||
([0x48, 0x00, 0x02, 0x00], Inst::Return1 (0)),
|
([0x48, 0x00, 0x02, 0x00], Inst::Return1 (0)),
|
||||||
([0x47, 0x00, 0x01, 0x00], Inst::Return0),
|
([0x47, 0x00, 0x01, 0x00], Inst::Return0),
|
||||||
|
([0x8d, 0x00, 0x01, 0x01], Inst::GetI (1, 1, 1)),
|
||||||
|
([0xbc, 0x00, 0x01, 0x00], Inst::EqK (1, 1, 0)),
|
||||||
|
([0xb8, 0x02, 0x00, 0x80], Inst::Jmp (6)),
|
||||||
|
([0x38, 0x02, 0x00, 0x80], Inst::Jmp (5)),
|
||||||
] {
|
] {
|
||||||
let actual = super::parse_inst (input).unwrap ();
|
let actual = super::parse_inst (input).unwrap ();
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
|
|
11
src/main.rs
11
src/main.rs
|
@ -12,6 +12,7 @@ fn main() {
|
||||||
let mut rdr = std::io::Cursor::new (data);
|
let mut rdr = std::io::Cursor::new (data);
|
||||||
loader::parse_chunk (&mut rdr).unwrap ()
|
loader::parse_chunk (&mut rdr).unwrap ()
|
||||||
};
|
};
|
||||||
|
assert_eq! (lua_file.blocks.len (), 3);
|
||||||
|
|
||||||
let mut vm = State::default ();
|
let mut vm = State::default ();
|
||||||
if std::env::var("LUA_DEBUG").is_ok() {
|
if std::env::var("LUA_DEBUG").is_ok() {
|
||||||
|
@ -19,5 +20,15 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let upvalues = State::upvalues_from_args (std::env::args ());
|
let upvalues = State::upvalues_from_args (std::env::args ());
|
||||||
|
|
||||||
|
vm.breakpoints.push (state::Breakpoint {
|
||||||
|
block_idx: 2,
|
||||||
|
program_counter: 3,
|
||||||
|
});
|
||||||
|
vm.breakpoints.push (state::Breakpoint {
|
||||||
|
block_idx: 0,
|
||||||
|
program_counter: 10,
|
||||||
|
});
|
||||||
|
|
||||||
println! ("Returned: {:?}", vm.execute_chunk (&lua_file, &upvalues));
|
println! ("Returned: {:?}", vm.execute_chunk (&lua_file, &upvalues));
|
||||||
}
|
}
|
||||||
|
|
74
src/state.rs
74
src/state.rs
|
@ -121,6 +121,12 @@ impl Value {
|
||||||
_ => true,
|
_ => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn take (&mut self) -> Self {
|
||||||
|
let mut x = Value::Nil;
|
||||||
|
std::mem::swap (self, &mut x);
|
||||||
|
x
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Block {
|
pub struct Block {
|
||||||
|
@ -147,16 +153,26 @@ struct StackFrame {
|
||||||
register_offset: usize,
|
register_offset: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive (Debug)]
|
||||||
|
pub struct Breakpoint {
|
||||||
|
pub block_idx: usize,
|
||||||
|
pub program_counter: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive (Debug)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
registers: Vec <Value>,
|
registers: Vec <Value>,
|
||||||
stack: Vec <StackFrame>,
|
stack: Vec <StackFrame>,
|
||||||
|
|
||||||
pub debug_print: bool,
|
pub debug_print: bool,
|
||||||
|
pub breakpoints: Vec <Breakpoint>,
|
||||||
|
step_count: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for State {
|
impl Default for State {
|
||||||
fn default () -> Self {
|
fn default () -> Self {
|
||||||
Self {
|
Self {
|
||||||
registers: vec! [Value::Nil; 256],
|
registers: vec! [Value::Nil; 16],
|
||||||
stack: vec! [
|
stack: vec! [
|
||||||
StackFrame {
|
StackFrame {
|
||||||
program_counter: 0,
|
program_counter: 0,
|
||||||
|
@ -165,6 +181,8 @@ impl Default for State {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
debug_print: false,
|
debug_print: false,
|
||||||
|
breakpoints: Default::default(),
|
||||||
|
step_count: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,9 +217,18 @@ impl State {
|
||||||
let max_iters = 2000;
|
let max_iters = 2000;
|
||||||
|
|
||||||
for _ in 0..max_iters {
|
for _ in 0..max_iters {
|
||||||
|
self.step_count += 1;
|
||||||
|
|
||||||
let frame = self.stack.last_mut ().unwrap ().clone ();
|
let frame = self.stack.last_mut ().unwrap ().clone ();
|
||||||
let block = chunk.blocks.get (frame.block_idx).unwrap ();
|
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 mut next_pc = frame.program_counter;
|
||||||
|
|
||||||
let pc = usize::try_from (frame.program_counter).expect ("program_counter is not a valid usize");
|
let pc = usize::try_from (frame.program_counter).expect ("program_counter is not a valid usize");
|
||||||
|
@ -241,7 +268,7 @@ impl State {
|
||||||
// TODO: Only implement printing values for now
|
// TODO: Only implement printing values for now
|
||||||
|
|
||||||
let a = usize::try_from (*a).unwrap ();
|
let a = usize::try_from (*a).unwrap ();
|
||||||
let r = self.register_window ();
|
let r = self.register_window_mut ();
|
||||||
let v_a = r.get (a).unwrap ();
|
let v_a = r.get (a).unwrap ();
|
||||||
|
|
||||||
match v_a {
|
match v_a {
|
||||||
|
@ -277,6 +304,7 @@ impl State {
|
||||||
assert_eq! (*c, 1);
|
assert_eq! (*c, 1);
|
||||||
|
|
||||||
println! ("{:?}", r.get (a + 1).unwrap ());
|
println! ("{:?}", r.get (a + 1).unwrap ());
|
||||||
|
r [a] = r [a + 1].take ();
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
let stack = &self.stack;
|
let stack = &self.stack;
|
||||||
|
@ -287,8 +315,12 @@ impl State {
|
||||||
Instruction::Closure (a, b) => {
|
Instruction::Closure (a, b) => {
|
||||||
let a = usize::try_from (*a).unwrap ();
|
let a = usize::try_from (*a).unwrap ();
|
||||||
let b = usize::try_from (*b).unwrap ();
|
let b = usize::try_from (*b).unwrap ();
|
||||||
let r = self.register_window_mut ();
|
|
||||||
|
|
||||||
|
if self.debug_print {
|
||||||
|
println! ("OP_CLOSURE {a} {b}");
|
||||||
|
}
|
||||||
|
|
||||||
|
let r = self.register_window_mut ();
|
||||||
r [a] = Value::BogusClosure {
|
r [a] = Value::BogusClosure {
|
||||||
idx: b + frame.block_idx + 1,
|
idx: b + frame.block_idx + 1,
|
||||||
upvalues: vec! [],
|
upvalues: vec! [],
|
||||||
|
@ -343,6 +375,18 @@ impl State {
|
||||||
|
|
||||||
r [a] = value;
|
r [a] = value;
|
||||||
},
|
},
|
||||||
|
Instruction::GetUpVal (a, b) => {
|
||||||
|
let this_func = self.stack.last ().unwrap ().register_offset - 1;
|
||||||
|
let upvalues = match &self.registers [this_func] {
|
||||||
|
Value::BogusClosure { idx, upvalues } => upvalues,
|
||||||
|
_ => panic! ("Can't do GetUpVal outside a closure"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let a = usize::try_from (*a).unwrap ();
|
||||||
|
let b = usize::try_from (*b).unwrap ();
|
||||||
|
|
||||||
|
self.register_window_mut ()[a] = upvalues [b].clone ();
|
||||||
|
},
|
||||||
Instruction::Jmp (s_j) => next_pc += s_j,
|
Instruction::Jmp (s_j) => next_pc += s_j,
|
||||||
Instruction::LoadFalse (a) => {
|
Instruction::LoadFalse (a) => {
|
||||||
let a = usize::try_from (*a).unwrap ();
|
let a = usize::try_from (*a).unwrap ();
|
||||||
|
@ -391,16 +435,17 @@ impl State {
|
||||||
let r = self.register_window_mut();
|
let r = self.register_window_mut();
|
||||||
r [a] = Value::Boolean (! r [b].is_truthy());
|
r [a] = Value::Boolean (! r [b].is_truthy());
|
||||||
}
|
}
|
||||||
Instruction::Return (a, b, _c, k) => {
|
Instruction::Return (a, b, c, k) => {
|
||||||
let a = usize::try_from (*a).unwrap ();
|
let a = usize::try_from (*a).unwrap ();
|
||||||
let b = usize::try_from (*b).unwrap ();
|
let b = usize::try_from (*b).unwrap ();
|
||||||
|
let c = usize::try_from (*c).unwrap ();
|
||||||
|
|
||||||
let popped_frame = self.stack.pop ().unwrap ();
|
let popped_frame = self.stack.pop ().unwrap ();
|
||||||
|
|
||||||
// Build closure if needed
|
// Build closure if needed
|
||||||
if *k {
|
if *k {
|
||||||
|
|
||||||
let closure_idx = match &self.register_window ()[a] {
|
let closure_idx = match &self.registers [popped_frame.register_offset + a] {
|
||||||
Value::BogusClosure { idx, upvalues } => idx,
|
Value::BogusClosure { idx, upvalues } => idx,
|
||||||
_ => panic! ("Impossible"),
|
_ => panic! ("Impossible"),
|
||||||
};
|
};
|
||||||
|
@ -427,9 +472,22 @@ impl State {
|
||||||
|
|
||||||
if let Some (new_frame) = self.stack.last() {
|
if let Some (new_frame) = self.stack.last() {
|
||||||
next_pc = new_frame.program_counter;
|
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 {
|
else {
|
||||||
return self.register_window ()[a..(a + b - 1)].to_vec();
|
// Return from the entire program
|
||||||
|
return self.registers [a..(a + b + c - 1)].to_vec();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Instruction::Return1 (a) => {
|
Instruction::Return1 (a) => {
|
||||||
|
@ -450,6 +508,10 @@ impl State {
|
||||||
let stack_depth = self.stack.len ();
|
let stack_depth = self.stack.len ();
|
||||||
println! ("stack_depth: {stack_depth}");
|
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::Test (a, _k) => {
|
Instruction::Test (a, _k) => {
|
||||||
let a = usize::try_from (*a).unwrap ();
|
let a = usize::try_from (*a).unwrap ();
|
||||||
|
|
148
src/tests.rs
148
src/tests.rs
|
@ -85,6 +85,25 @@ fn bools () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn closure () {
|
||||||
|
let bytecode = include_bytes! ("../test_vectors/closure.luac");
|
||||||
|
let mut rdr = std::io::Cursor::new (bytecode);
|
||||||
|
let file = crate::loader::parse_chunk (&mut rdr).unwrap ();
|
||||||
|
|
||||||
|
for (arg, expected) in [
|
||||||
|
// Run the same test twice so clippy won't complain about a vec of 1 element
|
||||||
|
(vec! ["_exe_name"], vec! [23.into ()]),
|
||||||
|
(vec! ["_exe_name"], vec! [23.into ()]),
|
||||||
|
] {
|
||||||
|
let mut vm = State::default ();
|
||||||
|
let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
|
||||||
|
let actual = vm.execute_chunk (&file, &upvalues);
|
||||||
|
|
||||||
|
assert_eq! (actual, expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn floats () {
|
fn floats () {
|
||||||
/*
|
/*
|
||||||
|
@ -135,72 +154,9 @@ fn floats () {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fma () {
|
fn fma () {
|
||||||
/*
|
let bytecode = include_bytes! ("../test_vectors/fma.luac");
|
||||||
|
let mut rdr = std::io::Cursor::new (bytecode);
|
||||||
*/
|
let file = crate::loader::parse_chunk (&mut rdr).unwrap ();
|
||||||
|
|
||||||
let chunk = Chunk {
|
|
||||||
file_name: "".to_string (),
|
|
||||||
blocks: vec! [
|
|
||||||
Block {
|
|
||||||
instructions: vec! [
|
|
||||||
Inst::VarArgPrep (0),
|
|
||||||
Inst::Closure (0, 0),
|
|
||||||
Inst::Closure (1, 1),
|
|
||||||
Inst::Closure (2, 2),
|
|
||||||
Inst::Move (3, 2),
|
|
||||||
Inst::LoadI (4, 10),
|
|
||||||
Inst::LoadI (5, 11),
|
|
||||||
Inst::LoadI (6, 12),
|
|
||||||
Inst::Call (3, 4, 2),
|
|
||||||
Inst::GetTabUp (4, 0, 0),
|
|
||||||
Inst::Move (5, 3),
|
|
||||||
Inst::Call (4, 2, 1),
|
|
||||||
Inst::Return (3, 2, 1, false), // k?
|
|
||||||
Inst::Return (3, 2, 1, false), // k?
|
|
||||||
],
|
|
||||||
constants: vec! [
|
|
||||||
"print".into (),
|
|
||||||
],
|
|
||||||
upvalue_count: 1,
|
|
||||||
},
|
|
||||||
Block {
|
|
||||||
instructions: vec! [
|
|
||||||
Inst::Add (2, 0, 1),
|
|
||||||
Inst::MmBin (0, 1, 6),
|
|
||||||
Inst::Return1 (2),
|
|
||||||
Inst::Return0,
|
|
||||||
],
|
|
||||||
constants: vec! [],
|
|
||||||
upvalue_count: 0,
|
|
||||||
},
|
|
||||||
Block {
|
|
||||||
instructions: vec! [
|
|
||||||
Inst::Mul (2, 0, 1),
|
|
||||||
Inst::MmBin (0, 1, 8),
|
|
||||||
Inst::Return1 (2),
|
|
||||||
Inst::Return0,
|
|
||||||
],
|
|
||||||
constants: vec! [],
|
|
||||||
upvalue_count: 0,
|
|
||||||
},
|
|
||||||
Block {
|
|
||||||
instructions: vec! [
|
|
||||||
Inst::GetUpVal (3, 0), // add
|
|
||||||
Inst::GetUpVal (4, 1), // mul
|
|
||||||
Inst::Move (5, 0),
|
|
||||||
Inst::Move (6, 1),
|
|
||||||
Inst::Call (4, 3, 2),
|
|
||||||
Inst::Move (5, 2),
|
|
||||||
Inst::TailCall (3, 3, 0),
|
|
||||||
Inst::Return (3, 0, 0, false),
|
|
||||||
Inst::Return0,
|
|
||||||
],
|
|
||||||
constants: vec! [],
|
|
||||||
upvalue_count: 2,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
for (arg, expected) in [
|
for (arg, expected) in [
|
||||||
(vec! ["_exe_name"], vec! [122.into ()]),
|
(vec! ["_exe_name"], vec! [122.into ()]),
|
||||||
|
@ -208,7 +164,7 @@ fn fma () {
|
||||||
] {
|
] {
|
||||||
let mut vm = State::default ();
|
let mut vm = State::default ();
|
||||||
let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
|
let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
|
||||||
let actual = vm.execute_chunk (&chunk, &upvalues);
|
let actual = vm.execute_chunk (&file, &upvalues);
|
||||||
|
|
||||||
assert_eq! (actual, expected);
|
assert_eq! (actual, expected);
|
||||||
}
|
}
|
||||||
|
@ -216,49 +172,9 @@ fn fma () {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn is_93 () {
|
fn is_93 () {
|
||||||
/*
|
let bytecode = include_bytes! ("../test_vectors/is_93.luac");
|
||||||
if arg [1] == "93" then
|
let mut rdr = std::io::Cursor::new (bytecode);
|
||||||
print "it's 93"
|
let file = crate::loader::parse_chunk (&mut rdr).unwrap ();
|
||||||
return 0
|
|
||||||
else
|
|
||||||
print "it's not 93"
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
*/
|
|
||||||
|
|
||||||
let block = Block {
|
|
||||||
instructions: vec! [
|
|
||||||
Inst::VarArgPrep (0),
|
|
||||||
Inst::GetTabUp (1, 0, 0),
|
|
||||||
Inst::GetI (1, 1, 1),
|
|
||||||
Inst::EqK (1, 1, 0),
|
|
||||||
Inst::Jmp (6),
|
|
||||||
Inst::GetTabUp (1, 0, 2),
|
|
||||||
Inst::LoadK (2, 3),
|
|
||||||
Inst::Call (1, 2, 1),
|
|
||||||
Inst::LoadI (1, 0),
|
|
||||||
Inst::Return (1, 2, 1, false),
|
|
||||||
Inst::Jmp (5),
|
|
||||||
Inst::GetTabUp (1, 0, 2),
|
|
||||||
Inst::LoadK (2, 4),
|
|
||||||
Inst::Call (1, 2, 1),
|
|
||||||
Inst::LoadI (1, 1),
|
|
||||||
Inst::Return (1, 2, 1, false),
|
|
||||||
Inst::Return (1, 1, 1, false),
|
|
||||||
],
|
|
||||||
constants: vec! [
|
|
||||||
"arg",
|
|
||||||
"93",
|
|
||||||
"print",
|
|
||||||
"it's 93",
|
|
||||||
"it's not 93",
|
|
||||||
].into_iter ().map (Value::from).collect (),
|
|
||||||
upvalue_count: 1,
|
|
||||||
};
|
|
||||||
let chunk = Chunk {
|
|
||||||
blocks: vec! [block],
|
|
||||||
file_name: "".to_string (),
|
|
||||||
};
|
|
||||||
|
|
||||||
for (arg, expected) in [
|
for (arg, expected) in [
|
||||||
(vec! ["_exe_name"], vec! [1.into ()]),
|
(vec! ["_exe_name"], vec! [1.into ()]),
|
||||||
|
@ -267,18 +183,8 @@ fn is_93 () {
|
||||||
] {
|
] {
|
||||||
let mut vm = State::default ();
|
let mut vm = State::default ();
|
||||||
let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
|
let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
|
||||||
let actual = vm.execute_chunk (&chunk, &upvalues);
|
let actual = vm.execute_chunk (&file, &upvalues);
|
||||||
|
|
||||||
assert_eq! (actual, expected);
|
assert_eq! (actual, expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn loader () {
|
|
||||||
let bytecode = include_bytes! ("../test_vectors/closure.luac");
|
|
||||||
let mut rdr = std::io::Cursor::new (bytecode);
|
|
||||||
let file = crate::loader::parse_chunk (&mut rdr).unwrap ();
|
|
||||||
|
|
||||||
assert_eq! (file.file_name, "@test_vectors/closure.lua");
|
|
||||||
assert_eq! (file.blocks.len (), 3);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,15 +1,9 @@
|
||||||
local function make_closure ()
|
local function make_closure (x)
|
||||||
local x = 5
|
print (x)
|
||||||
print "B"
|
return function (y)
|
||||||
|
return x + y
|
||||||
return function ()
|
|
||||||
print "D"
|
|
||||||
return x
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
print "A"
|
local f = make_closure (11)
|
||||||
local f = make_closure ()
|
print (f (12))
|
||||||
print "C"
|
|
||||||
print (f ())
|
|
||||||
print "E"
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue