ferrite-lua/src/vm/mod.rs
2026-03-20 21:04:24 +02:00

1045 lines
40 KiB
Rust

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<Value>) -> Result<Vec<Value>, 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<Constant>),
#[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<Instruction>,
pub parameters: usize,
}
#[derive(Debug, Clone)]
pub struct VirtualMachine {
pub(super) environment: Table,
pub(super) constants: Vec<Constant>,
pub(super) prototypes: HashMap<u32, Prototype>,
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<IndexableValue, Value> {
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<u16, Rc<RefCell<Value>>>,
}
impl Closure {
pub fn run(&self, params: Vec<Value>) -> 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<u16, Rc<RefCell<Value>>>,
inner: Option<Box<ClosureRunner>>,
function_register: u16,
return_registers: Vec<u16>,
top: u16,
parameters: Vec<Value>,
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<u16, Rc<RefCell<Value>>> {
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<Option<Vec<Value>>, 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<Value, RuntimeError>,
metamethod: &str,
lhs: Value,
rhs: Value,
) -> Result<Value, RuntimeError> {
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<Value>,
) -> Result<Value, RuntimeError> {
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),
)
}
}