use thiserror::Error; use std::{cell::RefCell, collections::HashMap, fmt::Debug, i64, rc::Rc}; use crate::{ CompilationUnit, ast::{BinaryOperator, UnaryOperator}, vm::value::{Constant, IndexableValue, LuaInteger, RustFunction, Table, Value}, }; pub mod value; #[derive(Debug)] pub struct SetMetatable; impl RustFunction for SetMetatable { fn execute(&self, parameters: Vec) -> Result, RuntimeError> { let table = parameters .get(0) .ok_or(RuntimeError::NotTable(Value::Nil))?; let metatable = parameters .get(1) .ok_or(RuntimeError::NotTable(Value::Nil))?; match table { Value::Table { metatable: inner, .. } => match metatable { Value::Table { contents: metatable, .. } => { *inner.borrow_mut() = metatable.borrow().clone(); Ok(Vec::new()) } _ => Err(RuntimeError::NotTable(metatable.clone())), }, _ => Err(RuntimeError::NotTable(table.clone())), } } fn as_indexable(&self) -> String { "SETMETATABLE".to_owned() } } #[derive(Clone, Copy)] pub enum Instruction { /// R(A) := R(B) Move(u16, u16), /// R(A) ... R(A + func_register - top) := previous function return values MoveRetValues(u16), /// R(A) := K(Bx) LoadK(u16, u32), /// R(A), ..., R(B) := nil LoadNil(u16, u16), /// G[K(Bx)] := R(A) SetGlobal(u16, u32), /// R(A) := G[K(Bx)] GetGlobal(u16, u32), /// R(A) := U[B] GetUpVal(u16, u16), /// U[B] := R(A) SetUpVal(u16, u16), /// R(A)[R(B)] := R(C) SetTable(u16, u16, u16), /// R(A)[B...] := all values returned from previous function SetList(u16, u32), /// R(A) := R(B)[R(C)] GetTable(u16, u16, u16), /// R(A) := {} NewTable(u16), /// R(A) := R(B) .. R(C) Concat(u16, u16, u16), /// R(A) := R(B) + R(C) Add(u16, u16, u16), /// R(A) := R(B) * R(C) Mult(u16, u16, u16), /// R(A) := R(B) / R(C) Div(u16, u16, u16), /// R(A) := R(B) // R(C) IDiv(u16, u16, u16), /// R(A) := R(B) % R(C) Mod(u16, u16, u16), /// R(A) := R(B) ^ R(C) Exp(u16, u16, u16), /// R(A) := R(B) & R(C) BitAnd(u16, u16, u16), /// R(A) := R(B) | R(C) BitOr(u16, u16, u16), /// R(A) := R(B) ~ R(C) BitXOr(u16, u16, u16), /// R(A) := R(B) >> R(C) BitSRight(u16, u16, u16), /// R(A) := R(B) << R(C) BitSLeft(u16, u16, u16), /// R(A) := -R(B) Unm(u16, u16), /// R(A) := #R(B) (length operator) Len(u16, u16), /// R(A) := not R(B) Not(u16, u16), /// R(A) := ~R(B) BitNot(u16, u16), /// R(A) := R(B) == R(C) Equal(u16, u16, u16), /// R(A) := R(B) < R(C) LessThan(u16, u16, u16), /// R(A) := R(B) <= R(C) LessThanOrEqual(u16, u16, u16), /// R(A) := R(B) or R(C) Or(u16, u16, u16), /// R(A) := R(B) and R(C) And(u16, u16, u16), /// PC += sAx Jmp(i32), /// PC = Ax GoTo(u32), /// if (R(B) <=> C) then R(A) := R(B) else PC++ Test(u16, u16, u16), /// if R(C) >= 0 /// then if R(A) < R(B) /// PC ++ /// otherwise if R(A) > R(B) /// PC ++ ForTest(u16, u16, u16), /// [func] [params.len()] [ret_regs.len()] /// R(A), ... R(A+C-2) := R(A)(R(A+1), ... R(A+B-1)) Call(u16, u16, u16), /// return R(A), ... , R(B) Return(u16, u16), /// close stack variables up to R(A) Close(u16), /// R(A) := closure(KPROTO[Bx], R(A), ..., R(A+n)) Closure(u16, u32), /// R(A), ... , R(A+B-2) := varargs Vararg(u16, u16), } impl Debug for Instruction { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Instruction::Move(arg0, arg1) => write!(f, "MOVE {} {}", arg0, arg1), Instruction::MoveRetValues(arg0) => write!(f, "MOVERETVALS {}", arg0), Instruction::LoadK(arg0, arg1) => write!(f, "LOADK {} {}", arg0, arg1), Instruction::SetGlobal(arg0, arg1) => write!(f, "SETGLOBAL {} {}", arg0, arg1), Instruction::GetGlobal(arg0, arg1) => write!(f, "GETGLOBAL {} {}", arg0, arg1), Instruction::GetUpVal(arg0, arg1) => write!(f, "GETUPVAL {} {}", arg0, arg1), Instruction::SetUpVal(arg0, arg1) => write!(f, "SETUPVAL {} {}", arg0, arg1), Instruction::SetTable(arg0, arg1, arg2) => { write!(f, "SETTABLE {} {} {}", arg0, arg1, arg2) } Instruction::GetTable(arg0, arg1, arg2) => { write!(f, "GETTABLE {} {} {}", arg0, arg1, arg2) } Instruction::SetList(arg0, arg1) => write!(f, "SETLIST {} {}", arg0, arg1), Instruction::NewTable(arg0) => write!(f, "NEWTABLE {}", arg0), Instruction::Jmp(arg0) => write!(f, "JMP {}", arg0), Instruction::GoTo(arg0) => write!(f, "GOTO {}", arg0), Instruction::Test(arg0, arg1, arg2) => write!(f, "TEST {} {} {}", arg0, arg1, arg2), Instruction::ForTest(arg0, arg1, arg2) => { write!(f, "FORTEST {} {} {}", arg0, arg1, arg2) } Instruction::Call(arg0, arg1, arg2) => write!(f, "CALL {} {} {}", arg0, arg1, arg2), Instruction::Close(arg0) => write!(f, "CLOSE {}", arg0), Instruction::Closure(arg0, arg1) => write!(f, "CLOSURE {} {}", arg0, arg1), Instruction::Return(arg0, arg1) => write!(f, "RETURN {} {}", arg0, arg1), Instruction::Concat(arg0, arg1, arg2) => write!(f, "CONCAT {} {} {}", arg0, arg1, arg2), Instruction::Equal(arg0, arg1, arg2) => write!(f, "EQ {} {} {}", arg0, arg1, arg2), Instruction::LessThan(arg0, arg1, arg2) => write!(f, "LT {} {} {}", arg0, arg1, arg2), Instruction::LessThanOrEqual(arg0, arg1, arg2) => { write!(f, "LE {} {} {}", arg0, arg1, arg2) } Instruction::Add(arg0, arg1, arg2) => write!(f, "ADD {} {} {}", arg0, arg1, arg2), Instruction::Mult(arg0, arg1, arg2) => write!(f, "MUL {} {} {}", arg0, arg1, arg2), Instruction::Div(arg0, arg1, arg2) => write!(f, "DIV {} {} {}", arg0, arg1, arg2), Instruction::IDiv(arg0, arg1, arg2) => write!(f, "IDIV {} {} {}", arg0, arg1, arg2), Instruction::Mod(arg0, arg1, arg2) => write!(f, "MOD {} {} {}", arg0, arg1, arg2), Instruction::Exp(arg0, arg1, arg2) => write!(f, "EXP {} {} {}", arg0, arg1, arg2), Instruction::BitAnd(arg0, arg1, arg2) => write!(f, "BAND {} {} {}", arg0, arg1, arg2), Instruction::BitOr(arg0, arg1, arg2) => write!(f, "BOR {} {} {}", arg0, arg1, arg2), Instruction::BitXOr(arg0, arg1, arg2) => write!(f, "BXOR {} {} {}", arg0, arg1, arg2), Instruction::BitSRight(arg0, arg1, arg2) => write!(f, "BSR {} {} {}", arg0, arg1, arg2), Instruction::BitSLeft(arg0, arg1, arg2) => write!(f, "BSL {} {} {}", arg0, arg1, arg2), Instruction::LoadNil(arg0, arg1) => write!(f, "LOADNIL {} {}", arg0, arg1), Instruction::Unm(arg0, arg1) => write!(f, "UNM {} {}", arg0, arg1), Instruction::Len(arg0, arg1) => write!(f, "LEN {} {}", arg0, arg1), Instruction::Not(arg0, arg1) => write!(f, "NOT {} {}", arg0, arg1), Instruction::BitNot(arg0, arg1) => write!(f, "BNOT {} {}", arg0, arg1), Instruction::Or(arg0, arg1, arg2) => write!(f, "OR {} {} {}", arg0, arg1, arg2), Instruction::And(arg0, arg1, arg2) => write!(f, "AND {} {} {}", arg0, arg1, arg2), Instruction::Vararg(arg0, arg1) => write!(f, "VARARG {} {}", arg0, arg1), } } } #[derive(Error, Debug)] pub enum RuntimeError { #[error("Unable to perform {0:?} operator between {1:?} and {2:?}")] InvalidOperands(BinaryOperator, Value, Value), #[error("Unable to call metamethod {0} for values {1:?} and {2:?}")] InvalidOperation(String, Value, Value), #[error("Metatable is not a table: {0:?}")] MetatableNotTable(Value), #[error("Metafunction not found: {0}")] MetafunctionNotFound(String), #[error("Metafunction is not callable: {0:?}")] MetafunctionNotCallable(Value), #[error("Unable to perform {0:?} operator to {1:?}")] InvalidOperand(UnaryOperator, Value), #[error("Tried calling a non-function: {0:?}")] TriedCallingNonFunction(Value), #[error("Global not found: {0:?}")] GlobalNotFound(Option), #[error("Unable to index tables with {0:?}")] InvalidTableIndex(Value), #[error("Value is not a table: {0:?}")] NotTable(Value), #[error("Value is not coercable to a float: {0:?}")] NotFloatable(Value), #[error("Value is not coercable to bits: {0:?}")] NotBittable(Value), #[error("Value does not have a length: {0:?}")] NotLengthable(Value), #[error("{0}")] Custom(String), } #[derive(Debug, Clone, Default)] pub struct Prototype { pub instructions: Vec, pub parameters: usize, } #[derive(Debug, Clone)] pub struct VirtualMachine { pub(super) environment: Table, pub(super) constants: Vec, pub(super) prototypes: HashMap, pub(super) proto_counter: u32, } impl Default for VirtualMachine { fn default() -> Self { let environment: Table = Default::default(); environment.borrow_mut().insert( IndexableValue::String("SETMETATABLE".into()), SetMetatable.into(), ); Self { environment, constants: Default::default(), prototypes: Default::default(), proto_counter: Default::default(), } } } impl VirtualMachine { pub fn new_prototype(&mut self, instructions: Prototype) -> u32 { let proto_id = self.proto_counter; self.proto_counter += 1; self.prototypes.insert(proto_id, instructions); proto_id } pub(crate) fn create_closure(&self, prototype: u32) -> Closure { Closure { vm: self.clone(), prototype, environment: self.environment.clone(), upvalues: HashMap::new(), } } pub fn set_global(&mut self, key: Value, value: Value) -> Result<(), RuntimeError> { self.environment .borrow_mut() .insert(key.as_indexable()?, value); Ok(()) } pub fn get_globals(&self) -> HashMap { let mut values = HashMap::new(); for (key, value) in self.environment.borrow().iter() { values.insert(key.clone(), value.clone()); } values } } #[derive(Debug, Clone)] pub struct Closure { vm: VirtualMachine, pub(crate) prototype: u32, environment: Table, upvalues: HashMap>>, } impl Closure { pub fn run(&self, params: Vec) -> ClosureRunner { let mut stack = HashMap::new(); for (i, param) in params.iter().enumerate() { stack.insert(i as u16, Rc::new(RefCell::new(param.clone()))); } ClosureRunner { closure: self.clone(), program_counter: 0, stack, inner: None, function_register: 0, return_registers: Vec::new(), top: 0, parameters: params, to_close_upvalues: 0, } } fn get_upvalue(&mut self, idx: u16) -> StackValue { let value = self.upvalues.get(&idx); if let Some(value) = value { match &*value.borrow() { _ => StackValue::Value(value.borrow().clone()), } } else { StackValue::Value(Value::Nil) } } } pub struct ClosureRunner { closure: Closure, program_counter: usize, stack: HashMap>>, inner: Option>, function_register: u16, return_registers: Vec, top: u16, parameters: Vec, to_close_upvalues: u16, } #[derive(Clone, Debug)] enum StackValue { Value(Value), } impl ClosureRunner { fn set_stack(&mut self, idx: u16, value: StackValue) { if let Some(stack_slot) = self.stack.get_mut(&idx) { match value { StackValue::Value(value) => { *stack_slot.borrow_mut() = value; } } } else { match value { StackValue::Value(value) => { self.stack.insert(idx, Rc::new(RefCell::new(value))); } } } } fn get_stack(&mut self, idx: u16) -> StackValue { let value = self.stack.get(&idx); if let Some(value) = value { match &*value.borrow() { _ => StackValue::Value(value.borrow().clone()), } } else { StackValue::Value(Value::Nil) } } fn close_upvalues(&self) -> HashMap>> { let highest_upvalue = self .closure .upvalues .iter() .map(|(v, _)| *v as i32) .max() .unwrap_or(-1); let mut upvalues = self.closure.upvalues.clone(); for (reg, value) in self .stack .iter() .filter(|(r, _)| **r < self.to_close_upvalues) { upvalues.insert(reg + (highest_upvalue + 1) as u16, value.clone()); } upvalues } pub fn execute(&mut self, unit: &CompilationUnit) -> ClosureRunner { let mut vm = self.closure.vm.clone(); vm.constants = unit.constants.clone(); let proto_id = vm.new_prototype(Prototype { instructions: unit.instructions.clone(), parameters: 0, }); for prototype in &unit.state.prototypes { vm.new_prototype(prototype.clone()); } let closure = Closure { vm, prototype: proto_id, environment: self.closure.environment.clone(), upvalues: self.close_upvalues(), }; closure.run(Vec::new()) } pub fn next(&mut self) -> Result>, RuntimeError> { if let Some(inner) = &mut self.inner { match inner.next() { Ok(ret_values) => { if let Some(ret_values) = ret_values { self.inner = None; if self.return_registers.len() == 0 { for (i, value) in ret_values.iter().enumerate() { self.set_stack( self.function_register + i as u16 + 1, StackValue::Value(value.clone()), ); } self.top = self.function_register + ret_values.len() as u16; } for (i, reg) in self.return_registers.clone().iter().enumerate() { self.set_stack( *reg, StackValue::Value(ret_values.get(i).cloned().unwrap_or(Value::Nil)), ); } } else { return Ok(None); } } Err(e) => return Err(e), } } let prototype = self .closure .vm .prototypes .get(&self.closure.prototype) .unwrap() .clone(); let constants = self.closure.vm.constants.clone(); if let Some(instr) = prototype.instructions.get(self.program_counter) { match instr { Instruction::Move(a, b) => { let b = self.get_stack(*b); self.set_stack(*a, b); } Instruction::MoveRetValues(res) => { let length = self.top - self.function_register; let mut values = Vec::new(); for i in 0..length { let b = self.get_stack(self.function_register + i + 1); values.push(b); } for (i, val) in values.into_iter().enumerate() { self.set_stack(*res + i as u16, val); } self.top = *res + length; } Instruction::LoadK(reg, constant) => { self.set_stack( *reg, StackValue::Value(match constants.get(*constant as usize).unwrap() { Constant::String(value) => Value::String(value.clone()), Constant::Float(value) => Value::Float(*value), Constant::Integer(value) => Value::Integer(*value), Constant::Bool(lua_bool) => Value::Boolean(*lua_bool), Constant::Nil => Value::Nil, }), ); } Instruction::LoadNil(from_reg, to_reg) => { for i in *from_reg..=*to_reg { self.set_stack(i, StackValue::Value(Value::Nil)); } } Instruction::SetGlobal(reg, global) => { let value = self.get_stack(*reg); self.closure.environment.borrow_mut().insert( constants .get(*global as usize) .unwrap() .clone() .as_value() .as_indexable()?, match value { StackValue::Value(value) => value, }, ); } Instruction::GetGlobal(reg, global) => { let constant = constants.get(*global as usize).unwrap().clone(); if let Constant::String(name) = &constant && name.clone() == "_ENV".to_owned() { self.set_stack( *reg, StackValue::Value(Value::Table { contents: self.closure.environment.clone(), metatable: Rc::new(RefCell::new(HashMap::new())), }), ); } else { let environment = self.closure.environment.borrow_mut().clone(); let glob = environment .get(&constant.as_value().as_indexable()?) .cloned(); if let Some(global) = glob { self.set_stack(*reg, StackValue::Value(global)); } else { return Err(RuntimeError::GlobalNotFound( constants.get(*global as usize).cloned(), )); } } } Instruction::GetUpVal(reg, upvalreg) => { let upvalue = self.closure.get_upvalue(*upvalreg); self.set_stack(*reg, upvalue); } Instruction::SetUpVal(upvalreg, reg) => { *self.closure.upvalues.get(upvalreg).unwrap().borrow_mut() = self .stack .get(reg) .map(|v| v.borrow().clone()) .unwrap_or(Value::Nil); } Instruction::SetTable(tablereg, indexreg, valuereg) => { let table = self.stack.get(tablereg); match table { Some(value) => { let mut table = value.borrow_mut(); if let Value::Table { contents, .. } = &mut *table { let index_value = self .stack .get(indexreg) .map(|v| v.borrow().clone()) .unwrap_or(Value::Nil) .as_indexable()?; let value = self .stack .get(valuereg) .map(|v| v.borrow().clone()) .unwrap_or(Value::Nil); match value { Value::Nil => { contents.borrow_mut().remove(&index_value); } _ => { contents.borrow_mut().insert(index_value, value); } } } else { return Err(RuntimeError::NotTable(table.clone())); } } None => todo!(), } } Instruction::GetTable(res, tablereg, indexreg) => { let value = match self.stack.get(tablereg).cloned() { Some(value) => { let table = value.borrow(); if let Value::Table { contents, .. } = &*table { let table = contents.borrow(); let index_value = self .stack .get(indexreg) .map(|v| v.borrow().clone()) .unwrap_or(Value::Nil) .as_indexable()?; let value = table.get(&index_value); match value { Some(value) => StackValue::Value(value.clone()), None => StackValue::Value(Value::Nil), } } else { return Err(RuntimeError::NotTable(table.clone())); } } None => { return Err(RuntimeError::NotTable(Value::Nil)); } }; self.set_stack(*res, value); } Instruction::SetList(reg, start_idx) => { let table = self.stack.get(reg).cloned(); match table { Some(value) => { let mut table = value.borrow_mut(); if let Value::Table { contents, .. } = &mut *table { let mut counter = *start_idx as i64; for i in self.function_register..self.top { let value = self.get_stack(i + 1); match value { StackValue::Value(value) => contents.borrow_mut().insert( IndexableValue::Integer(LuaInteger(counter)), value.clone(), ), }; counter += 1; } } else { return Err(RuntimeError::NotTable(table.clone())); } } None => todo!(), } } Instruction::NewTable(reg) => { self.set_stack( *reg, StackValue::Value(Value::Table { contents: Rc::new(RefCell::new(HashMap::new())), metatable: Rc::new(RefCell::new(HashMap::new())), }), ); } Instruction::Jmp(b) => { self.program_counter = (self.program_counter as i32 + *b) as usize } Instruction::GoTo(a) => { self.program_counter = *a as usize; return Ok(None); } Instruction::Test(a, b, c) => { let is_true = match self .stack .get(b) .map(|v| v.borrow().clone()) .unwrap_or(Value::Nil) { Value::Boolean(val) => (val.0 as u16) == *c, _ => false, }; if is_true { let b = self.get_stack(*b); self.set_stack(*a, b); } else { self.program_counter += 1; } } Instruction::ForTest(counter_reg, end_reg, step_reg) => { let step_gte_0 = match self.get_stack(*step_reg) { StackValue::Value(value) => Value::Integer(LuaInteger(0)).lt(&value), }; let counter = match self.get_stack(*counter_reg) { StackValue::Value(value) => value, }; let end = match self.get_stack(*end_reg) { StackValue::Value(value) => value, }; if step_gte_0?.is_truthy() { if !end.lt(&counter)?.is_truthy() { self.program_counter += 1; } } else { if !counter.lt(&end)?.is_truthy() { self.program_counter += 1; } } } Instruction::Call(func_reg, param_len, ret_len) => { let param_start_func_reg = *func_reg; let param_len = if *param_len == 0 { self.top - self.top.min(param_start_func_reg + 1) } else { *param_len }; self.function_register = *func_reg; let mut params = Vec::new(); for i in 0..param_len { params.push( self.stack .get(&(param_start_func_reg + i + 1)) .map(|v| v.borrow().clone()) .unwrap_or(Value::Nil) .clone(), ); } let value = self .stack .get(func_reg) .map(|v| v.borrow().clone()) .unwrap_or(Value::Nil); match value { Value::RustFunction(func) => { let ret_values = func.borrow_mut().execute(params)?; if *ret_len != 0 { for i in 0..=(*ret_len - 2) { self.set_stack( *func_reg + i, StackValue::Value( ret_values .get(i as usize) .cloned() .unwrap_or(Value::Nil), ), ); } } else { for (i, value) in ret_values.iter().enumerate() { self.set_stack( *func_reg + i as u16 + 1, StackValue::Value(value.clone()), ); } self.top = *func_reg + ret_values.len() as u16; } } Value::Function(closure) => { self.return_registers = Vec::new(); if *ret_len != 0 { for i in 0..=(*ret_len - 2) { self.return_registers.push(*func_reg + i); } } self.inner = Some(Box::new(closure.run(params))); } _ => return Err(RuntimeError::TriedCallingNonFunction(value.clone())), } } Instruction::Close(up_until) => { self.to_close_upvalues = *up_until; } Instruction::Closure(reg, protok) => { let upvalues = self.close_upvalues(); self.set_stack( *reg, StackValue::Value(Value::Function(Closure { vm: self.closure.vm.clone(), prototype: *protok, environment: self.closure.environment.clone(), upvalues, })), ); } Instruction::Return(reg_start, reg_end) => { self.program_counter += 1; let mut ret_values = Vec::new(); let (reg_start, reg_end) = if *reg_end == 0 { if self.top > 0 { (*reg_start, self.top - 1) } else { (*reg_start, *reg_end) } } else { (*reg_start, *reg_end) }; for i in reg_start..=reg_end { ret_values.push( self.stack .get(&i) .map(|v| v.borrow().clone()) .unwrap_or(Value::Nil), ); } return Ok(Some(ret_values)); } Instruction::Concat(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); self.set_stack(*res, StackValue::Value(lhs.concat(&rhs)?)); } Instruction::Add(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); self.set_stack( *res, StackValue::Value(self.result_or_metamethod( lhs.add(&rhs), "__add", lhs, rhs, )?), ); } Instruction::Mult(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); self.set_stack(*res, StackValue::Value(lhs.mult(&rhs)?)); } Instruction::Div(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); self.set_stack(*res, StackValue::Value(lhs.div(&rhs)?)); } Instruction::IDiv(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); self.set_stack(*res, StackValue::Value(lhs.idiv(&rhs)?)); } Instruction::Mod(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); self.set_stack(*res, StackValue::Value(lhs.r#mod(&rhs)?)); } Instruction::Exp(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); self.set_stack(*res, StackValue::Value(lhs.exp(&rhs)?)); } Instruction::BitAnd(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); self.set_stack(*res, StackValue::Value(lhs.band(&rhs)?)); } Instruction::BitOr(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); self.set_stack(*res, StackValue::Value(lhs.bor(&rhs)?)); } Instruction::BitXOr(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); self.set_stack(*res, StackValue::Value(lhs.bxor(&rhs)?)); } Instruction::BitSRight(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); self.set_stack(*res, StackValue::Value(lhs.bsright(&rhs)?)); } Instruction::BitSLeft(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); self.set_stack(*res, StackValue::Value(lhs.bsleft(&rhs)?)); } Instruction::Equal(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); self.set_stack(*res, StackValue::Value(lhs.eq(&rhs)?)); } Instruction::LessThan(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); self.set_stack(*res, StackValue::Value(lhs.lt(&rhs)?)); } Instruction::LessThanOrEqual(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); self.set_stack(*res, StackValue::Value(lhs.lte(&rhs)?)); } Instruction::Or(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); self.set_stack(*res, StackValue::Value(lhs.or(&rhs)?)); } Instruction::And(res, lhs, rhs) => { let (lhs, rhs) = self.lhs_and_rhs(lhs, rhs); self.set_stack(*res, StackValue::Value(lhs.and(&rhs)?)); } Instruction::Unm(res, reg) => { self.set_stack( *res, StackValue::Value( self.stack .get(reg) .map(|v| v.borrow().clone()) .unwrap_or(Value::Nil) .unm()?, ), ); } Instruction::Len(res, reg) => { self.set_stack( *res, StackValue::Value( self.stack .get(reg) .map(|v| v.borrow().clone()) .unwrap_or(Value::Nil) .len()?, ), ); } Instruction::Not(res, reg) => { self.set_stack( *res, StackValue::Value( self.stack .get(reg) .map(|v| v.borrow().clone()) .unwrap_or(Value::Nil) .not()?, ), ); } Instruction::BitNot(res, reg) => { self.set_stack( *res, StackValue::Value( self.stack .get(reg) .map(|v| v.borrow().clone()) .unwrap_or(Value::Nil) .bxor(&Value::Integer(LuaInteger(u64::MAX as i64)))?, ), ); } Instruction::Vararg(reg, len) => { self.function_register = reg - 1; if *len == 0 { self.top = self.function_register + self.parameters.len() as u16 - prototype.parameters as u16; for (i, param) in self .parameters .clone() .iter() .skip(prototype.parameters) .enumerate() { self.set_stack(*reg + i as u16, StackValue::Value(param.clone())); } } else { let len = len - 2; self.top = self.function_register + len + 1; for i in 0..len { self.set_stack( *reg + i as u16, StackValue::Value( self.parameters .iter() .skip(prototype.parameters) .next() .unwrap_or(&Value::Nil) .clone(), ), ); } } } }; self.program_counter += 1; Ok(None) } else { Ok(Some(Vec::new())) } } fn result_or_metamethod( &self, value: Result, metamethod: &str, lhs: Value, rhs: Value, ) -> Result { match value { Ok(value) => Ok(value), Err(_) => match (&lhs, &rhs) { (Value::Table { metatable, .. }, _) => { self.call_metamethod(metatable, metamethod, vec![lhs.clone(), rhs.clone()]) } (_, Value::Table { metatable, .. }) => { self.call_metamethod(metatable, metamethod, vec![lhs.clone(), rhs.clone()]) } _ => Err(RuntimeError::InvalidOperation( metamethod.to_owned(), lhs, rhs, )), }, } } fn call_metamethod( &self, metatable: &Table, metamethod: &str, params: Vec, ) -> Result { if let Some(value) = metatable .borrow() .get(&IndexableValue::String(metamethod.to_owned())) { match value { Value::RustFunction(function) => { let result = function.borrow_mut().execute(params)?; Ok(result.into_iter().next().unwrap()) } Value::Function(closure) => { let mut runnable = closure.run(params); #[allow(unused_assignments)] let mut return_value = None; while { return_value = runnable.next()?; return_value.is_none() } {} Ok(return_value.unwrap().into_iter().next().unwrap()) } _ => Err(RuntimeError::MetafunctionNotCallable(value.clone())), } } else { Err(RuntimeError::MetafunctionNotFound(metamethod.to_owned())) } } fn lhs_and_rhs(&self, lhs: &u16, rhs: &u16) -> (Value, Value) { ( self.stack .get(lhs) .map(|v| v.borrow().clone()) .unwrap_or(Value::Nil), self.stack .get(rhs) .map(|v| v.borrow().clone()) .unwrap_or(Value::Nil), ) } }