diff --git a/crates/luars/src/compiler/expr.rs b/crates/luars/src/compiler/expr.rs index f9375ff5..a804f31e 100644 --- a/crates/luars/src/compiler/expr.rs +++ b/crates/luars/src/compiler/expr.rs @@ -1609,21 +1609,21 @@ fn compile_binary_expr_to( BinaryOperator::OpConcat => { // CONCAT has special instruction format: CONCAT A B // Concatenates R[A] through R[A+B] (B+1 values), result in R[A] - + // LUA 5.4 OPTIMIZATION: Merge consecutive CONCAT operations // When compiling "e1 .. e2", if e2's code just generated a CONCAT instruction, // we can merge them into a single CONCAT that concatenates all values at once. // This is critical for performance with chains like "a" .. "b" .. "c" - + let code = &c.chunk.code; if !code.is_empty() { let prev_instr = code[code.len() - 1]; let prev_opcode = Instruction::get_opcode(prev_instr); - + if prev_opcode == OpCode::Concat { let prev_a = Instruction::get_a(prev_instr); let prev_b = Instruction::get_b(prev_instr); - + // Check if right_reg is the result of previous CONCAT // Previous CONCAT: R[prev_a] = R[prev_a]..R[prev_a+1]..R[prev_a+prev_b] // If right_reg == prev_a and left_reg == prev_a - 1, we can merge @@ -1633,11 +1633,11 @@ fn compile_binary_expr_to( let last_idx = code.len() - 1; c.chunk.code[last_idx] = Instruction::encode_abc( OpCode::Concat, - left_reg, // Start from left_reg instead - prev_b + 1, // Increase count by 1 - 0 + left_reg, // Start from left_reg instead + prev_b + 1, // Increase count by 1 + 0, ); - + // BUGFIX: Respect dest parameter if let Some(d) = dest { if d != left_reg { diff --git a/crates/luars/src/gc.rs b/crates/luars/src/gc.rs index be89d102..c24d2815 100644 --- a/crates/luars/src/gc.rs +++ b/crates/luars/src/gc.rs @@ -139,7 +139,7 @@ impl GC { allocations_since_minor_gc: 0, minor_gc_count: 0, check_counter: 0, - check_interval: 1000, // Run GC every 1000 checks when debt > 0 (reduce overhead) + check_interval: 10000, // Run GC every 10000 checks when debt > 0 (massive overhead reduction) finalize_queue: Vec::new(), // __gc finalizer queue collection_count: 0, stats: GCStats::default(), diff --git a/crates/luars/src/lib_registry.rs b/crates/luars/src/lib_registry.rs index df4ba6ad..b69da582 100644 --- a/crates/luars/src/lib_registry.rs +++ b/crates/luars/src/lib_registry.rs @@ -2,7 +2,7 @@ // Provides a clean way to register Rust functions as Lua libraries use crate::lua_value::{CFunction, LuaValue}; -use crate::lua_vm::{LuaError, LuaResult, LuaVM}; +use crate::lua_vm::{LuaResult, LuaVM}; use crate::stdlib; /// Type for value initializers - functions that create values when the module loads @@ -189,7 +189,7 @@ pub fn create_standard_registry() -> LibraryRegistry { registry.register(stdlib::string::create_string_lib()); registry.register(stdlib::table::create_table_lib()); registry.register(stdlib::math::create_math_lib()); - registry.register(stdlib::io::create_io_lib()); + // registry.register(stdlib::io::create_io_lib()); registry.register(stdlib::os::create_os_lib()); registry.register(stdlib::utf8::create_utf8_lib()); registry.register(stdlib::coroutine::create_coroutine_lib()); @@ -213,17 +213,18 @@ pub fn get_args(vm: &LuaVM) -> Vec { } /// Helper to get a specific argument +/// 1 based index #[inline(always)] pub fn get_arg(vm: &LuaVM, index: usize) -> Option { let frame = vm.frames.last().unwrap(); let base_ptr = frame.base_ptr; let top = frame.top; - // Arguments are 0-indexed from caller's perspective - // But register 0 is the function, so arg 0 is at register 1 - // get_arg(0) should return register[base_ptr + 1] - // get_arg(1) should return register[base_ptr + 2] - let reg_offset = index + 1; // +1 to skip the function at register 0 + // Arguments use 1-based indexing (Lua convention) + // Register 0 is the function itself + // get_arg(1) returns register[base_ptr + 1] (first argument) + // get_arg(2) returns register[base_ptr + 2] (second argument) + let reg_offset = index; if reg_offset < top { let reg_index = base_ptr + reg_offset; if reg_index < vm.register_stack.len() { @@ -237,11 +238,13 @@ pub fn get_arg(vm: &LuaVM, index: usize) -> Option { } /// Helper to require an argument +/// 1 based index #[inline] -pub fn require_arg(vm: &LuaVM, index: usize, func_name: &str) -> LuaResult { - get_arg(vm, index).ok_or_else(|| { - LuaError::RuntimeError(format!("{}() requires argument {}", func_name, index + 1)) - }) +pub fn require_arg(vm: &mut LuaVM, index: usize, func_name: &str) -> LuaResult { + let Some(arg) = get_arg(vm, index) else { + return Err(vm.error(format!("{}() requires argument {}", func_name, index + 1))); + }; + Ok(arg) } /// Helper to get argument count @@ -251,19 +254,3 @@ pub fn arg_count(vm: &LuaVM) -> usize { // Subtract 1 for the function itself frame.top.saturating_sub(1) } - -/// Helper to get string argument -pub fn get_string(vm: &LuaVM, index: usize, func_name: &str) -> LuaResult { - let arg = require_arg(vm, index, func_name)?; - unsafe { - arg.as_string() - .map(|s| s.as_str().to_string()) - .ok_or_else(|| { - LuaError::RuntimeError(format!( - "{}() requires string argument {}", - func_name, - index + 1 - )) - }) - } -} diff --git a/crates/luars/src/lua_value/lua_table.rs b/crates/luars/src/lua_value/lua_table.rs index 0e41d950..1fd73f60 100644 --- a/crates/luars/src/lua_value/lua_table.rs +++ b/crates/luars/src/lua_value/lua_table.rs @@ -1,10 +1,8 @@ // High-performance Lua table implementation following Lua 5.4 design // - Array part for integer keys [1..n] // - Hash part using open addressing (same as Lua 5.4) -use crate::LuaVM; -use crate::lua_vm::{LuaError, LuaResult}; - use super::LuaValue; +use crate::LuaVM; /// Hash node - mimics Lua 5.4's Node structure /// Contains key+value pair and next index for collision chaining @@ -61,7 +59,7 @@ impl LuaTable { } else { 0 }; - + LuaTable { array: if array_size > 0 { Some(Vec::with_capacity(array_size)) @@ -162,7 +160,7 @@ impl LuaTable { if self.nodes.is_none() { panic!("insert_node_simple called with uninitialized nodes - this is a bug"); } - + let nodes = self.nodes.as_mut().unwrap(); let size = nodes.len(); let mut idx = Self::hash_key(&key, size); @@ -291,15 +289,15 @@ impl LuaTable { if key > 0 { let idx = (key - 1) as usize; - + // Initialize array if needed if self.array.is_none() { self.array = Some(Vec::new()); } - + let array = self.array.as_mut().unwrap(); let array_len = array.len(); - + // Limit array growth to reasonable size (64K elements) if idx < array_len { array[idx] = value; @@ -410,19 +408,17 @@ impl LuaTable { /// Insert value at position in array part, shifting elements to the right /// Position is 0-indexed internally but Lua uses 1-indexed - pub fn insert_array_at(&mut self, pos: usize, value: LuaValue) -> LuaResult<()> { + pub fn insert_array_at(&mut self, pos: usize, value: LuaValue) -> Result<(), String> { let len = self.len(); if pos > len { - return Err(LuaError::RuntimeError( - "insert position out of bounds".to_string(), - )); + return Err("insert position out of bounds".to_string()); } // Initialize array if needed if self.array.is_none() { self.array = Some(Vec::new()); } - + let array = self.array.as_mut().unwrap(); // CRITICAL OPTIMIZATION: Fast path for appending at end (no shift needed!) @@ -439,12 +435,10 @@ impl LuaTable { /// Remove value at position in array part, shifting elements to the left /// Position is 0-indexed internally but Lua uses 1-indexed - pub fn remove_array_at(&mut self, pos: usize) -> LuaResult { + pub fn remove_array_at(&mut self, pos: usize) -> Result { let len = self.len(); if pos >= len { - return Err(LuaError::RuntimeError( - "remove position out of bounds".to_string(), - )); + return Err("remove position out of bounds".to_string()); } let array = self.array.as_mut().expect("array should exist"); diff --git a/crates/luars/src/lua_value/mod.rs b/crates/luars/src/lua_value/mod.rs index c9091259..2e3fb0b3 100644 --- a/crates/luars/src/lua_value/mod.rs +++ b/crates/luars/src/lua_value/mod.rs @@ -19,8 +19,8 @@ pub use lua_table::LuaTable; pub use lua_thread::*; pub use lua_value::{ LuaValue, LuaValueKind, NAN_BASE, TAG_BOOLEAN, TAG_CFUNCTION, TAG_FLOAT, TAG_FUNCTION, - TAG_INTEGER, TAG_NIL, TAG_STRING, TAG_TABLE, TAG_USERDATA, TYPE_MASK, - VALUE_TRUE, VALUE_FALSE, VALUE_NIL, + TAG_INTEGER, TAG_NIL, TAG_STRING, TAG_TABLE, TAG_USERDATA, TYPE_MASK, VALUE_FALSE, VALUE_NIL, + VALUE_TRUE, }; /// Multi-return values from Lua functions diff --git a/crates/luars/src/lua_vm/dispatcher/arithmetic_instructions.rs b/crates/luars/src/lua_vm/dispatcher/arithmetic_instructions.rs index 4252f060..4152e96d 100644 --- a/crates/luars/src/lua_vm/dispatcher/arithmetic_instructions.rs +++ b/crates/luars/src/lua_vm/dispatcher/arithmetic_instructions.rs @@ -4,7 +4,7 @@ use crate::{ LuaValue, lua_value::{TAG_FLOAT, TAG_INTEGER, TYPE_MASK}, - lua_vm::{Instruction, LuaError, LuaResult, LuaVM}, + lua_vm::{Instruction, LuaResult, LuaVM}, }; /// ADD: R[A] = R[B] + R[C] @@ -238,9 +238,7 @@ pub fn exec_idiv(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let result = if combined_tags == TAG_INTEGER { let r = right.secondary as i64; if r == 0 { - return Err(LuaError::RuntimeError( - "attempt to divide by zero".to_string(), - )); + return Err(vm.error("attempt to divide by zero".to_string())); } let l = left.secondary as i64; LuaValue::integer(l.div_euclid(r)) @@ -292,9 +290,7 @@ pub fn exec_mod(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let result = if combined_tags == TAG_INTEGER { let r = right.secondary as i64; if r == 0 { - return Err(LuaError::RuntimeError( - "attempt to perform 'n%0'".to_string(), - )); + return Err(vm.error("attempt to perform 'n%0'".to_string())); } let l = left.secondary as i64; LuaValue::integer(l.rem_euclid(r)) @@ -389,7 +385,7 @@ pub fn exec_unm(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { } } } - return Err(LuaError::RuntimeError(format!( + return Err(vm.error(format!( "attempt to perform arithmetic on {}", value.type_name() ))); @@ -444,17 +440,12 @@ pub fn exec_addk(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let base_ptr = frame.base_ptr; // Get constant from chunk - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - let func = unsafe { &*func_ptr }; - let constant = func - .borrow() - .chunk - .constants - .get(c) - .copied() - .ok_or_else(|| LuaError::RuntimeError(format!("Invalid constant index: {}", c)))?; + let Some(func) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; + let Some(constant) = func.borrow().chunk.constants.get(c).copied() else { + return Err(vm.error(format!("Invalid constant index: {}", c))); + }; let left = vm.register_stack[base_ptr + b]; @@ -487,17 +478,12 @@ pub fn exec_subk(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let frame = vm.current_frame(); let base_ptr = frame.base_ptr; - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - let func = unsafe { &*func_ptr }; - let constant = func - .borrow() - .chunk - .constants - .get(c) - .copied() - .ok_or_else(|| LuaError::RuntimeError(format!("Invalid constant index: {}", c)))?; + let Some(func) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; + let Some(constant) = func.borrow().chunk.constants.get(c).copied() else { + return Err(vm.error(format!("Invalid constant index: {}", c))); + }; let left = vm.register_stack[base_ptr + b]; @@ -528,22 +514,13 @@ pub fn exec_mulk(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let base_ptr = frame.base_ptr; // Get constant once - let func_ptr = match frame.get_function_ptr() { - Some(ptr) => ptr, - None => return Err(LuaError::RuntimeError("Not a Lua function".to_string())), + let Some(func_ptr) = frame.get_function_ptr() else { + return Err(vm.error("Not a Lua function".to_string())); }; let func = unsafe { &*func_ptr }; - let constants = &func.borrow().chunk.constants; - let constant = match constants.get(c) { - Some(val) => *val, - None => { - return Err(LuaError::RuntimeError(format!( - "Invalid constant index: {}", - c - ))); - } + let Some(constant) = func.borrow().chunk.constants.get(c).copied() else { + return Err(vm.error(format!("Invalid constant index: {}", c))); }; - let left = vm.register_stack[base_ptr + b]; // Fast path: Direct type tag check (avoid method call overhead) @@ -592,27 +569,19 @@ pub fn exec_modk(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let frame = vm.current_frame(); let base_ptr = frame.base_ptr; - - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - let func = unsafe { &*func_ptr }; - let constant = func - .borrow() - .chunk - .constants - .get(c) - .copied() - .ok_or_else(|| LuaError::RuntimeError(format!("Invalid constant index: {}", c)))?; + let Some(func) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; + let Some(constant) = func.borrow().chunk.constants.get(c).copied() else { + return Err(vm.error(format!("Invalid constant index: {}", c))); + }; let left = vm.register_stack[base_ptr + b]; // Try integer operation if let (Some(l), Some(r)) = (left.as_integer(), constant.as_integer()) { if r == 0 { - return Err(LuaError::RuntimeError( - "attempt to perform 'n%0'".to_string(), - )); + return Err(vm.error("attempt to perform 'n%0'".to_string())); } vm.register_stack[base_ptr + a] = LuaValue::integer(l.rem_euclid(r)); vm.current_frame_mut().pc += 1; @@ -638,17 +607,12 @@ pub fn exec_powk(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let frame = vm.current_frame(); let base_ptr = frame.base_ptr; - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - let func = unsafe { &*func_ptr }; - let constant = func - .borrow() - .chunk - .constants - .get(c) - .copied() - .ok_or_else(|| LuaError::RuntimeError(format!("Invalid constant index: {}", c)))?; + let Some(func) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; + let Some(constant) = func.borrow().chunk.constants.get(c).copied() else { + return Err(vm.error(format!("Invalid constant index: {}", c))); + }; let left = vm.register_stack[base_ptr + b]; @@ -676,18 +640,12 @@ pub fn exec_divk(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let frame = vm.current_frame(); let base_ptr = frame.base_ptr; - - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - let func = unsafe { &*func_ptr }; - let constant = func - .borrow() - .chunk - .constants - .get(c) - .copied() - .ok_or_else(|| LuaError::RuntimeError(format!("Invalid constant index: {}", c)))?; + let Some(func) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; + let Some(constant) = func.borrow().chunk.constants.get(c).copied() else { + return Err(vm.error(format!("Invalid constant index: {}", c))); + }; let left = vm.register_stack[base_ptr + b]; @@ -715,27 +673,19 @@ pub fn exec_idivk(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let frame = vm.current_frame(); let base_ptr = frame.base_ptr; - - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - let func = unsafe { &*func_ptr }; - let constant = func - .borrow() - .chunk - .constants - .get(c) - .copied() - .ok_or_else(|| LuaError::RuntimeError(format!("Invalid constant index: {}", c)))?; + let Some(func) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; + let Some(constant) = func.borrow().chunk.constants.get(c).copied() else { + return Err(vm.error(format!("Invalid constant index: {}", c))); + }; let left = vm.register_stack[base_ptr + b]; // Try integer operation if let (Some(l), Some(r)) = (left.as_integer(), constant.as_integer()) { if r == 0 { - return Err(LuaError::RuntimeError( - "attempt to divide by zero".to_string(), - )); + return Err(vm.error("attempt to divide by zero".to_string())); } vm.register_stack[base_ptr + a] = LuaValue::integer(l.div_euclid(r)); vm.current_frame_mut().pc += 1; @@ -846,18 +796,12 @@ pub fn exec_shl(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let left = vm.register_stack[base_ptr + b]; let right = vm.register_stack[base_ptr + c]; - let l_int = left.as_integer().ok_or_else(|| { - LuaError::RuntimeError(format!( - "attempt to perform bitwise operation on {}", - left.type_name() - )) - })?; - let r_int = right.as_integer().ok_or_else(|| { - LuaError::RuntimeError(format!( - "attempt to perform bitwise operation on {}", - right.type_name() - )) - })?; + let Some(l_int) = left.as_integer() else { + return Ok(()); + }; + let Some(r_int) = right.as_integer() else { + return Ok(()); + }; let result = if r_int >= 0 { LuaValue::integer(l_int << (r_int & 63)) @@ -911,17 +855,12 @@ pub fn exec_bandk(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let frame = vm.current_frame(); let base_ptr = frame.base_ptr; - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - let func = unsafe { &*func_ptr }; - let constant = func - .borrow() - .chunk - .constants - .get(c) - .copied() - .ok_or_else(|| LuaError::RuntimeError(format!("Invalid constant index: {}", c)))?; + let Some(func) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; + let Some(constant) = func.borrow().chunk.constants.get(c).copied() else { + return Err(vm.error(format!("Invalid constant index: {}", c))); + }; let left = vm.register_stack[base_ptr + b]; @@ -964,18 +903,12 @@ pub fn exec_bork(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let frame = vm.current_frame(); let base_ptr = frame.base_ptr; - - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - let func = unsafe { &*func_ptr }; - let constant = func - .borrow() - .chunk - .constants - .get(c) - .copied() - .ok_or_else(|| LuaError::RuntimeError(format!("Invalid constant index: {}", c)))?; + let Some(func) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; + let Some(constant) = func.borrow().chunk.constants.get(c).copied() else { + return Err(vm.error(format!("Invalid constant index: {}", c))); + }; let left = vm.register_stack[base_ptr + b]; @@ -1018,18 +951,12 @@ pub fn exec_bxork(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let frame = vm.current_frame(); let base_ptr = frame.base_ptr; - - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - let func = unsafe { &*func_ptr }; - let constant = func - .borrow() - .chunk - .constants - .get(c) - .copied() - .ok_or_else(|| LuaError::RuntimeError(format!("Invalid constant index: {}", c)))?; + let Some(func) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; + let Some(constant) = func.borrow().chunk.constants.get(c).copied() else { + return Err(vm.error(format!("Invalid constant index: {}", c))); + }; let left = vm.register_stack[base_ptr + b]; @@ -1144,7 +1071,7 @@ pub fn exec_bnot(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { } } } - return Err(LuaError::RuntimeError(format!( + return Err(vm.error(format!( "attempt to perform bitwise operation on {}", value.type_name() ))); @@ -1209,10 +1136,7 @@ pub fn exec_len(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { 0 } } else { - return Err(LuaError::RuntimeError(format!( - "attempt to get length of {}", - value.type_name() - ))); + return Err(vm.error(format!("attempt to get length of {}", value.type_name()))); }; let result = LuaValue::integer(len); @@ -1260,21 +1184,14 @@ pub fn exec_mmbin(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { // Get the previous instruction to find the destination register let frame = vm.current_frame(); - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - let func = unsafe { &*func_ptr }; - let func_ref = func.borrow(); - let chunk = &func_ref.chunk; let prev_pc = frame.pc - 1; // Previous instruction was the failed arithmetic op - if prev_pc == 0 { - return Err(LuaError::RuntimeError( - "MMBIN: no previous instruction".to_string(), - )); + return Err(vm.error("MMBIN: no previous instruction".to_string())); } - - let prev_instr = chunk.code[prev_pc - 1]; + let Some(func) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; + let prev_instr = func.borrow().chunk.code[prev_pc - 1]; let dest_reg = Instruction::get_a(prev_instr) as usize; // Destination register from ADD/SUB/etc let base_ptr = frame.base_ptr; @@ -1298,7 +1215,7 @@ pub fn exec_mmbin(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { }; if metamethod.is_nil() { - return Err(LuaError::RuntimeError(format!( + return Err(vm.error(format!( "attempt to perform arithmetic on {} and {}", ra.type_name(), rb.type_name() @@ -1325,21 +1242,16 @@ pub fn exec_mmbini(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { // Get the previous instruction to find the destination register let frame = vm.current_frame(); - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - let func = unsafe { &*func_ptr }; - let func_ref = func.borrow(); - let chunk = &func_ref.chunk; let prev_pc = frame.pc - 1; // Previous instruction was the failed arithmetic op if prev_pc == 0 { - return Err(LuaError::RuntimeError( - "MMBINI: no previous instruction".to_string(), - )); + return Err(vm.error("MMBINI: no previous instruction".to_string())); } - let prev_instr = chunk.code[prev_pc - 1]; + let Some(func) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; + let prev_instr = func.borrow().chunk.code[prev_pc - 1]; let dest_reg = Instruction::get_a(prev_instr) as usize; // Destination register from ADDI let base_ptr = frame.base_ptr; @@ -1362,7 +1274,7 @@ pub fn exec_mmbini(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { }; if metamethod.is_nil() { - return Err(LuaError::RuntimeError(format!( + return Err(vm.error(format!( "attempt to perform arithmetic on {} and {}", rb.type_name(), rc.type_name() @@ -1388,30 +1300,24 @@ pub fn exec_mmbink(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { // Get the previous instruction to find the destination register let frame = vm.current_frame(); - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - let func = unsafe { &*func_ptr }; - let func_ref = func.borrow(); - let chunk = &func_ref.chunk; let prev_pc = frame.pc - 1; // Previous instruction was the failed arithmetic op if prev_pc == 0 { - return Err(LuaError::RuntimeError( - "MMBINK: no previous instruction".to_string(), - )); + return Err(vm.error("MMBINK: no previous instruction".to_string())); } - let prev_instr = chunk.code[prev_pc - 1]; + let Some(func) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; + let prev_instr = func.borrow().chunk.code[prev_pc - 1]; let dest_reg = Instruction::get_a(prev_instr) as usize; // Destination register from ADDK/SUBK/etc let base_ptr = frame.base_ptr; let ra = vm.register_stack[base_ptr + a]; - let kb = - chunk.constants.get(b).copied().ok_or_else(|| { - LuaError::RuntimeError(format!("Constant index out of bounds: {}", b)) - })?; + let Some(kb) = func.borrow().chunk.constants.get(b).copied() else { + return Err(vm.error(format!("Constant index out of bounds: {}", b))); + }; // C is the TagMethod, not a constant index let metamethod_name = get_binop_metamethod(c as u8); @@ -1432,7 +1338,7 @@ pub fn exec_mmbink(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { }; if metamethod.is_nil() { - return Err(LuaError::RuntimeError(format!( + return Err(vm.error(format!( "attempt to perform arithmetic on {} and {}", left.type_name(), right.type_name() diff --git a/crates/luars/src/lua_vm/dispatcher/control_instructions.rs b/crates/luars/src/lua_vm/dispatcher/control_instructions.rs index 80d2bdec..c3a677e0 100644 --- a/crates/luars/src/lua_vm/dispatcher/control_instructions.rs +++ b/crates/luars/src/lua_vm/dispatcher/control_instructions.rs @@ -18,10 +18,9 @@ pub fn exec_return(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let base_ptr = vm.current_frame().base_ptr; vm.close_upvalues_from(base_ptr); - let frame = vm - .frames - .pop() - .ok_or_else(|| LuaError::RuntimeError("RETURN with no frame on stack".to_string()))?; + let Some(frame) = vm.frames.pop() else { + return Err(vm.error("RETURN with no frame on stack".to_string())); + }; let base_ptr = frame.base_ptr; let result_reg = frame.get_result_reg(); @@ -124,6 +123,20 @@ pub fn exec_return(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { vm.close_to_be_closed(close_from)?; } + // CRITICAL: If frames are now empty, we're done - return control to caller + // This prevents run() loop from trying to access empty frame + if vm.frames.is_empty() { + // Save return values before exiting + vm.return_values.clear(); + for i in 0..return_count { + if base_ptr + a + i < vm.register_stack.len() { + vm.return_values.push(vm.register_stack[base_ptr + a + i]); + } + } + // Signal end of execution + return Err(LuaError::Exit); + } + Ok(()) } @@ -161,7 +174,7 @@ pub fn exec_test(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { // OPTIMIZATION: Fast truthiness check using type tags // nil = TAG_NIL, false = VALUE_FALSE // Only nil and false are falsy - use crate::lua_value::{VALUE_FALSE, TAG_NIL}; + use crate::lua_value::{TAG_NIL, VALUE_FALSE}; let is_truthy = value.primary != TAG_NIL && value.primary != VALUE_FALSE; // If (not value) == k, skip next instruction @@ -190,7 +203,7 @@ pub fn exec_testset(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let value = *reg_ptr.add(b); // OPTIMIZATION: Fast truthiness check - use crate::lua_value::{VALUE_FALSE, TAG_NIL}; + use crate::lua_value::{TAG_NIL, VALUE_FALSE}; let is_truthy = value.primary != TAG_NIL && value.primary != VALUE_FALSE; // If (is_truthy == k), assign R[A] = R[B], otherwise skip next instruction @@ -224,7 +237,7 @@ pub fn exec_eq(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { }; // OPTIMIZATION: Fast path - check if primary fields are identical (same type and value/id) - use crate::lua_value::{TAG_INTEGER, TAG_FLOAT, TAG_STRING, TYPE_MASK}; + use crate::lua_value::{TAG_FLOAT, TAG_INTEGER, TAG_STRING, TYPE_MASK}; let left_tag = left.primary & TYPE_MASK; let right_tag = right.primary & TYPE_MASK; @@ -300,7 +313,7 @@ pub fn exec_lt(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { }; // OPTIMIZATION: Direct type tag comparison (inline integer/float checks) - use crate::lua_value::{TAG_INTEGER, TAG_FLOAT, TAG_STRING, TYPE_MASK}; + use crate::lua_value::{TAG_FLOAT, TAG_INTEGER, TAG_STRING, TYPE_MASK}; let left_tag = left.primary & TYPE_MASK; let right_tag = right.primary & TYPE_MASK; @@ -366,7 +379,7 @@ pub fn exec_lt(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { } if !found_metamethod { - return Err(LuaError::RuntimeError(format!( + return Err(vm.error(format!( "attempt to compare {} with {}", left.type_name(), right.type_name() @@ -486,7 +499,7 @@ pub fn exec_le(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { } if !found_metamethod { - return Err(LuaError::RuntimeError(format!( + return Err(vm.error(format!( "attempt to compare {} with {}", left.type_name(), right.type_name() @@ -512,17 +525,12 @@ pub fn exec_eqk(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let frame = vm.current_frame(); let base_ptr = frame.base_ptr; - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - let func = unsafe { &*func_ptr }; - let constant = func - .borrow() - .chunk - .constants - .get(b) - .copied() - .ok_or_else(|| LuaError::RuntimeError(format!("Invalid constant index: {}", b)))?; + let Some(func) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; + let Some(constant) = func.borrow().chunk.constants.get(b).copied() else { + return Err(vm.error(format!("Invalid constant index: {}", b))); + }; let left = vm.register_stack[base_ptr + a]; @@ -584,7 +592,7 @@ pub fn exec_lti(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { } else if let Some(l) = left.as_number() { l < sb as f64 } else { - return Err(LuaError::RuntimeError(format!( + return Err(vm.error(format!( "attempt to compare {} with number", left.type_name() ))); @@ -615,7 +623,7 @@ pub fn exec_lei(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { } else if let Some(l) = left.as_number() { l <= sb as f64 } else { - return Err(LuaError::RuntimeError(format!( + return Err(vm.error(format!( "attempt to compare {} with number", left.type_name() ))); @@ -646,7 +654,7 @@ pub fn exec_gti(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { } else if let Some(l) = left.as_number() { l > sb as f64 } else { - return Err(LuaError::RuntimeError(format!( + return Err(vm.error(format!( "attempt to compare {} with number", left.type_name() ))); @@ -677,7 +685,7 @@ pub fn exec_gei(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { } else if let Some(l) = left.as_number() { l >= sb as f64 } else { - return Err(LuaError::RuntimeError(format!( + return Err(vm.error(format!( "attempt to compare {} with number", left.type_name() ))); @@ -696,9 +704,6 @@ pub fn exec_gei(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { /// R[A], ... ,R[A+C-2] := R[A](R[A+1], ... ,R[A+B-1]) #[inline(always)] pub fn exec_call(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { - use crate::lua_value::LuaValueKind; - use crate::lua_vm::LuaCallFrame; - // CALL A B C: R[A], ..., R[A+C-2] := R[A](R[A+1], ..., R[A+B-1]) // A: function register, B: arg count + 1 (0 = use top), C: return count + 1 (0 = use top) let a = Instruction::get_a(instr) as usize; @@ -739,10 +744,7 @@ pub fn exec_call(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { // If still not callable, error if !func.is_callable() { - return Err(LuaError::RuntimeError(format!( - "attempt to call a {} value", - func.type_name() - ))); + return Err(vm.error(format!("attempt to call a {} value", func.type_name()))); } } @@ -830,10 +832,10 @@ pub fn exec_call(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { // Lua: n = (*f)(L); // 直接调用 let result = match cfunc(vm) { - Ok(r) => Ok(r), - Err(LuaError::Yield(values)) => { + Ok(r) => r, + Err(LuaError::Yield) => { vm.frames.pop(); - return Err(LuaError::Yield(values)); + return Err(LuaError::Yield); } Err(e) => { vm.frames.pop(); @@ -841,7 +843,6 @@ pub fn exec_call(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { } }; vm.frames.pop(); - let result = result?; // === 关键改进:返回值已经在正确位置了!=== // C函数通过vm.push()写入返回值,已经在register_stack的末尾 @@ -877,11 +878,11 @@ pub fn exec_call(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { } LuaValueKind::Function => { // OPTIMIZATION: Direct pointer access - NO hash lookup! - let func_ptr = func - .as_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Invalid function pointer".to_string()))?; - let (max_stack_size, is_vararg) = unsafe { - let func_borrow = (*func_ptr).borrow(); + let Some(func_ref) = func.as_lua_function() else { + return Err(vm.error("not a function".to_string())); + }; + let (max_stack_size, is_vararg) = { + let func_borrow = func_ref.borrow(); let size = if func_borrow.chunk.max_stack_size == 0 { 1 } else { @@ -961,7 +962,7 @@ pub fn exec_call(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { } // Get code pointer from function - let code_ptr = unsafe { (*func_ptr).borrow().chunk.code.as_ptr() }; + let code_ptr = func_ref.borrow().chunk.code.as_ptr(); // Create and push new frame let new_frame = LuaCallFrame::new_lua_function( @@ -978,10 +979,7 @@ pub fn exec_call(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { Ok(()) } - _ => Err(LuaError::RuntimeError(format!( - "attempt to call a {} value", - func.type_name() - ))), + _ => Err(vm.error(format!("attempt to call a {} value", func.type_name()))), } } @@ -1033,11 +1031,10 @@ pub fn exec_tailcall(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { match func.kind() { LuaValueKind::Function => { // Lua function: tail call optimization - - let func_ptr = func - .as_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Invalid function pointer".to_string()))?; - let max_stack_size = unsafe { (*func_ptr).borrow().chunk.max_stack_size }; + let Some(func_ref) = func.as_lua_function() else { + return Err(vm.error("not a function".to_string())); + }; + let max_stack_size = func_ref.borrow().chunk.max_stack_size; // CRITICAL: Before popping the frame, close all upvalues that point to it // This ensures that any closures created in this frame can still access @@ -1061,11 +1058,7 @@ pub fn exec_tailcall(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { } // Get code pointer from function - let func_ptr = func - .as_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - let func_obj = unsafe { &*func_ptr }; - let code_ptr = func_obj.borrow().chunk.code.as_ptr(); + let code_ptr = func_ref.borrow().chunk.code.as_ptr(); let new_frame = LuaCallFrame::new_lua_function( frame_id, @@ -1093,9 +1086,9 @@ pub fn exec_tailcall(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { // When we pop it, we return to the parent // result_reg is relative to parent's base - let c_func = func - .as_cfunction() - .ok_or_else(|| LuaError::RuntimeError("Invalid C function".to_string()))?; + let Some(c_func) = func.as_cfunction() else { + return Err(vm.error("not a c function".to_string())); + }; // Create temporary C function frame let frame_id = vm.next_frame_id; @@ -1151,10 +1144,7 @@ pub fn exec_tailcall(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { Ok(()) } - _ => Err(LuaError::RuntimeError(format!( - "attempt to call a {} value", - func.type_name() - ))), + _ => Err(vm.error(format!("attempt to call a {} value", func.type_name()))), } } @@ -1165,10 +1155,9 @@ pub fn exec_return0(vm: &mut LuaVM, _instr: u32) -> LuaResult<()> { let base_ptr = vm.current_frame().base_ptr; vm.close_upvalues_from(base_ptr); - let frame = vm - .frames - .pop() - .ok_or_else(|| LuaError::RuntimeError("RETURN0 with no frame on stack".to_string()))?; + let Some(frame) = vm.frames.pop() else { + return Err(vm.error("RETURN0 with no frame on stack".to_string())); + }; vm.return_values.clear(); @@ -1188,6 +1177,9 @@ pub fn exec_return0(vm: &mut LuaVM, _instr: u32) -> LuaResult<()> { // Update caller's top to indicate 0 return values (for variable returns) vm.current_frame_mut().top = result_reg; // No return values, so top = result_reg + 0 + } else { + // If frames are empty, signal VM exit + return Err(LuaError::Exit); } Ok(()) @@ -1202,10 +1194,9 @@ pub fn exec_return1(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let base_ptr = vm.current_frame().base_ptr; vm.close_upvalues_from(base_ptr); - let frame = vm - .frames - .pop() - .ok_or_else(|| LuaError::RuntimeError("RETURN1 with no frame on stack".to_string()))?; + let Some(frame) = vm.frames.pop() else { + return Err(vm.error("RETURN1 with no frame on stack".to_string())); + }; let base_ptr = frame.base_ptr; let result_reg = frame.get_result_reg(); @@ -1245,6 +1236,9 @@ pub fn exec_return1(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { if should_update_top { vm.current_frame_mut().top = result_reg + 1; } + } else { + // If frames are empty, signal VM exit + return Err(LuaError::Exit); } Ok(()) diff --git a/crates/luars/src/lua_vm/dispatcher/load_instructions.rs b/crates/luars/src/lua_vm/dispatcher/load_instructions.rs index 3a8696ea..b01165aa 100644 --- a/crates/luars/src/lua_vm/dispatcher/load_instructions.rs +++ b/crates/luars/src/lua_vm/dispatcher/load_instructions.rs @@ -2,7 +2,7 @@ /// /// These instructions handle loading constants and moving values between registers. use crate::LuaValue; -use crate::lua_vm::{Instruction, LuaError, LuaResult, LuaVM}; +use crate::lua_vm::{Instruction, LuaResult, LuaVM}; /// VARARGPREP A /// Prepare stack for vararg function @@ -19,9 +19,7 @@ pub fn exec_varargprep(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let max_stack_size = if let Some(func_ref) = frame.function_value.as_lua_function() { func_ref.borrow().chunk.max_stack_size } else { - return Err(LuaError::RuntimeError( - "Invalid function in VARARGPREP".to_string(), - )); + return Err(vm.error("Invalid function in VARARGPREP".to_string())); }; // Arguments were placed starting at base_ptr by CALL instruction @@ -139,23 +137,16 @@ pub fn exec_loadk(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let bx = Instruction::get_bx(instr) as usize; let frame = vm.current_frame(); - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - - let func = unsafe { &*func_ptr }; - let func_ref = func.borrow(); - let chunk = &func_ref.chunk; + let Some(func_ref) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; - if bx >= chunk.constants.len() { - return Err(LuaError::RuntimeError(format!( - "Constant index out of bounds: {} >= {}", - bx, - chunk.constants.len() - ))); + let len = func_ref.borrow().chunk.constants.len(); + if bx >= len { + return Err(vm.error(format!("Constant index out of bounds: {} >= {}", bx, len))); } - let constant = chunk.constants[bx]; + let constant = func_ref.borrow().chunk.constants[bx]; let base_ptr = frame.base_ptr; vm.register_stack[base_ptr + a] = constant; @@ -173,32 +164,27 @@ pub fn exec_loadkx(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let pc = frame.pc; frame.pc += 1; // Skip the extra arg instruction - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; + let Some(func_ref) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; - let func = unsafe { &*func_ptr }; - let func_ref = func.borrow(); - let chunk = &func_ref.chunk; + let code_len = func_ref.borrow().chunk.code.len(); - if pc >= chunk.code.len() { - return Err(LuaError::RuntimeError( - "Missing EXTRAARG for LOADKX".to_string(), - )); + if pc >= code_len { + return Err(vm.error("Missing EXTRAARG for LOADKX".to_string())); } - let extra_instr = chunk.code[pc]; + let extra_instr = func_ref.borrow().chunk.code[pc]; let bx = Instruction::get_ax(extra_instr) as usize; - - if bx >= chunk.constants.len() { - return Err(LuaError::RuntimeError(format!( + let const_len = func_ref.borrow().chunk.constants.len(); + if bx >= const_len { + return Err(vm.error(format!( "Constant index out of bounds: {} >= {}", - bx, - chunk.constants.len() + bx, const_len ))); } - let constant = chunk.constants[bx]; + let constant = func_ref.borrow().chunk.constants[bx]; let base_ptr = vm.current_frame().base_ptr; vm.register_stack[base_ptr + a] = constant; @@ -207,12 +193,10 @@ pub fn exec_loadkx(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { } /// ExtraArg: Extra argument for LOADKX and other instructions -pub fn exec_extraarg(_vm: &mut LuaVM, _instr: u32) -> LuaResult<()> { +pub fn exec_extraarg(vm: &mut LuaVM, _instr: u32) -> LuaResult<()> { // EXTRAARG is consumed by the preceding instruction (like LOADKX) // It should never be executed directly - Err(LuaError::RuntimeError( - "EXTRAARG should not be executed directly".to_string(), - )) + Err(vm.error("EXTRAARG should not be executed directly".to_string())) } /// MOVE A B diff --git a/crates/luars/src/lua_vm/dispatcher/loop_instructions.rs b/crates/luars/src/lua_vm/dispatcher/loop_instructions.rs index 2d0e6a66..db98d183 100644 --- a/crates/luars/src/lua_vm/dispatcher/loop_instructions.rs +++ b/crates/luars/src/lua_vm/dispatcher/loop_instructions.rs @@ -4,7 +4,7 @@ use crate::{ LuaValue, lua_value::{LuaValueKind, TAG_FLOAT, TAG_INTEGER, TYPE_MASK}, - lua_vm::{Instruction, LuaCallFrame, LuaError, LuaResult, LuaVM}, + lua_vm::{Instruction, LuaCallFrame, LuaResult, LuaVM}, }; /// FORPREP A Bx @@ -26,7 +26,7 @@ pub fn exec_forprep(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { (init.as_integer(), limit.as_integer(), step.as_integer()) { if step_i == 0 { - return Err(LuaError::RuntimeError("'for' step is zero".to_string())); + return Err(vm.error("'for' step is zero".to_string())); } // Set control variable (R[A+3] = init) @@ -63,18 +63,18 @@ pub fn exec_forprep(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { } } else { // Float loop - let init_f = init.as_number().ok_or_else(|| { - LuaError::RuntimeError("'for' initial value must be a number".to_string()) - })?; - let limit_f = limit - .as_number() - .ok_or_else(|| LuaError::RuntimeError("'for' limit must be a number".to_string()))?; - let step_f = step - .as_number() - .ok_or_else(|| LuaError::RuntimeError("'for' step must be a number".to_string()))?; + let Some(init_f) = init.as_number() else { + return Err(vm.error("'for' initial value must be a number".to_string())); + }; + let Some(limit_f) = limit.as_number() else { + return Err(vm.error("'for' limit must be a number".to_string())); + }; + let Some(step_f) = step.as_number() else { + return Err(vm.error("'for' step must be a number".to_string())); + }; if step_f == 0.0 { - return Err(LuaError::RuntimeError("'for' step is zero".to_string())); + return Err(vm.error("'for' step is zero".to_string())); } // Set control variable @@ -187,9 +187,7 @@ pub fn exec_forloop(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { (*frame_ptr).pc -= bx; } } else { - return Err(LuaError::RuntimeError( - "'for' values must be numbers".to_string(), - )); + return Err(vm.error("'for' values must be numbers".to_string())); } } } @@ -236,9 +234,9 @@ pub fn exec_tforcall(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { // This is similar to CALL instruction but with fixed arguments match func.kind() { LuaValueKind::CFunction => { - let cfunc = func - .as_cfunction() - .ok_or_else(|| LuaError::RuntimeError("Invalid CFunction".to_string()))?; + let Some(cfunc) = func.as_cfunction() else { + return Err(vm.error("Invalid CFunction".to_string())); + }; // Create temporary frame for the call let frame_id = vm.next_frame_id; @@ -280,11 +278,11 @@ pub fn exec_tforcall(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { vm.register_stack[base_ptr + a + 5] = control; // OPTIMIZATION: Use direct pointer access instead of hash lookup - let func_ptr = func - .as_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Invalid function pointer".to_string()))?; + let Some(func_ref) = func.as_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; - let max_stack_size = unsafe { (*func_ptr).borrow().chunk.max_stack_size }; + let max_stack_size = func_ref.borrow().chunk.max_stack_size; let frame_id = vm.next_frame_id; vm.next_frame_id += 1; @@ -302,11 +300,7 @@ pub fn exec_tforcall(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { vm.register_stack[call_base + 1] = control; // Get code pointer from function - let func_ptr = func - .as_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - let func_obj = unsafe { &*func_ptr }; - let code_ptr = func_obj.borrow().chunk.code.as_ptr(); + let code_ptr = func_ref.borrow().chunk.code.as_ptr(); let new_frame = LuaCallFrame::new_lua_function( frame_id, @@ -322,9 +316,7 @@ pub fn exec_tforcall(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { // Execution will continue in the new frame } _ => { - return Err(LuaError::RuntimeError( - "attempt to call a non-function value in for loop".to_string(), - )); + return Err(vm.error("attempt to call a non-function value in for loop".to_string())); } } diff --git a/crates/luars/src/lua_vm/dispatcher/table_instructions.rs b/crates/luars/src/lua_vm/dispatcher/table_instructions.rs index 8615f7b9..0e6846ee 100644 --- a/crates/luars/src/lua_vm/dispatcher/table_instructions.rs +++ b/crates/luars/src/lua_vm/dispatcher/table_instructions.rs @@ -2,7 +2,7 @@ use crate::lua_value::LuaValue; /// Table operations /// /// These instructions handle table creation, access, and manipulation. -use crate::lua_vm::{Instruction, LuaError, LuaResult, LuaVM}; +use crate::lua_vm::{Instruction, LuaResult, LuaVM}; /// NEWTABLE A B C k /// R[A] := {} (size = B,C) @@ -26,7 +26,7 @@ pub fn exec_newtable(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let func = &*func_ptr; let func_ref = func.borrow(); let chunk = &func_ref.chunk; - + if pc < chunk.code.len() { Instruction::get_ax(chunk.code[pc]) } else { @@ -35,7 +35,11 @@ pub fn exec_newtable(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { }; // Calculate array size hint and hash size hint - let array_size = if b > 0 { (b - 1) as usize } else { extra_arg as usize }; + let array_size = if b > 0 { + (b - 1) as usize + } else { + extra_arg as usize + }; let hash_size = c as usize; // Create new table with size hints @@ -97,20 +101,15 @@ pub fn exec_settable(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let value = if k { // OPTIMIZATION: Get constant directly - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - unsafe { - (*func_ptr) - .borrow() - .chunk - .constants - .get(c) - .copied() - .ok_or_else(|| { - LuaError::RuntimeError(format!("Invalid constant index: {}", c)) - })? - } + let Some(func_ref) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; + + let Some(constant) = func_ref.borrow().chunk.constants.get(c).copied() else { + return Err(vm.error(format!("Invalid constant index: {}", c))); + }; + + constant } else { vm.register_stack[base_ptr + c] }; @@ -165,20 +164,16 @@ pub fn exec_seti(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let key = crate::LuaValue::integer(b as i64); let value = if k { - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - unsafe { - (*func_ptr) - .borrow() - .chunk - .constants - .get(c) - .copied() - .ok_or_else(|| { - LuaError::RuntimeError(format!("Invalid constant index: {}", c)) - })? - } + // OPTIMIZATION: Get constant directly + let Some(func_ref) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; + + let Some(constant) = func_ref.borrow().chunk.constants.get(c).copied() else { + return Err(vm.error(format!("Invalid constant index: {}", c))); + }; + + constant } else { vm.register_stack[base_ptr + c] }; @@ -205,17 +200,13 @@ pub fn exec_getfield(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let frame = vm.current_frame(); let base_ptr = frame.base_ptr; - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - let key = unsafe { - (*func_ptr) - .borrow() - .chunk - .constants - .get(c) - .copied() - .ok_or_else(|| LuaError::RuntimeError(format!("Invalid constant index: {}", c)))? + // OPTIMIZATION: Get constant directly + let Some(func_ref) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; + + let Some(key) = func_ref.borrow().chunk.constants.get(c).copied() else { + return Err(vm.error(format!("Invalid constant index: {}", c))); }; let table = vm.register_stack[base_ptr + b]; @@ -249,33 +240,23 @@ pub fn exec_setfield(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let frame = vm.current_frame(); let base_ptr = frame.base_ptr; - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - let key = unsafe { - (*func_ptr) - .borrow() - .chunk - .constants - .get(b) - .copied() - .ok_or_else(|| LuaError::RuntimeError(format!("Invalid constant index: {}", b)))? + let Some(func_ref) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; + + let Some(key) = func_ref.borrow().chunk.constants.get(b).copied() else { + return Err(vm.error(format!("Invalid constant index: {}", b))); }; let table = vm.register_stack[base_ptr + a]; let value = if k { - unsafe { - (*func_ptr) - .borrow() - .chunk - .constants - .get(c) - .copied() - .ok_or_else(|| { - LuaError::RuntimeError(format!("Invalid constant index: {}", c)) - })? - } + // OPTIMIZATION: Get constant directly + let Some(constant) = func_ref.borrow().chunk.constants.get(c).copied() else { + return Err(vm.error(format!("Invalid constant index: {}", c))); + }; + + constant } else { vm.register_stack[base_ptr + c] }; @@ -298,29 +279,23 @@ pub fn exec_gettabup(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let frame = vm.current_frame(); let base_ptr = frame.base_ptr; - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - let func = unsafe { &*func_ptr }; - let func_ref = func.borrow(); + let Some(func_ref) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; - let key = func_ref - .chunk - .constants - .get(c) - .copied() - .ok_or_else(|| LuaError::RuntimeError(format!("Invalid constant index: {}", c)))?; + let Some(key) = func_ref.borrow().chunk.constants.get(c).copied() else { + return Err(vm.error(format!("Invalid constant index: {}", c))); + }; - let upvalue = func_ref - .upvalues - .get(b) - .ok_or_else(|| LuaError::RuntimeError(format!("Invalid upvalue index: {}", b)))?; + let Some(upvalue) = func_ref.borrow().upvalues.get(b).cloned() else { + return Err(vm.error(format!("Invalid upvalue index: {}", b))); + }; let table = upvalue.get_value(&vm.frames, &vm.register_stack); let value = vm .table_get_with_meta(&table, &key) - .unwrap_or(crate::LuaValue::nil()); + .unwrap_or(LuaValue::nil()); vm.register_stack[base_ptr + a] = value; @@ -337,40 +312,38 @@ pub fn exec_settabup(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { // CRITICAL: Read all values BEFORE any metamethod calls // because metamethods can modify the register stack - let (table_value, key_value, set_value) = - { - let frame = vm.current_frame(); - let base_ptr = frame.base_ptr; - - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - let func = unsafe { &*func_ptr }; - let func_ref = func.borrow(); - - let key = - func_ref.chunk.constants.get(b).copied().ok_or_else(|| { - LuaError::RuntimeError(format!("Invalid constant index: {}", b)) - })?; - - let upvalue = func_ref - .upvalues - .get(a) - .ok_or_else(|| LuaError::RuntimeError(format!("Invalid upvalue index: {}", a)))?; - - let table = upvalue.get_value(&vm.frames, &vm.register_stack); - - let value = if k { - func_ref.chunk.constants.get(c).copied().ok_or_else(|| { - LuaError::RuntimeError(format!("Invalid constant index: {}", c)) - })? - } else { - vm.register_stack[base_ptr + c] + let (table_value, key_value, set_value) = { + let frame = vm.current_frame(); + let base_ptr = frame.base_ptr; + + let Some(func_ref) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; + + let Some(key) = func_ref.borrow().chunk.constants.get(b).copied() else { + return Err(vm.error(format!("Invalid constant index: {}", b))); + }; + + let Some(upvalue) = func_ref.borrow().upvalues.get(a).cloned() else { + return Err(vm.error(format!("Invalid upvalue index: {}", a))); + }; + + let table = upvalue.get_value(&vm.frames, &vm.register_stack); + + let value = if k { + // OPTIMIZATION: Get constant directly + let Some(constant) = func_ref.borrow().chunk.constants.get(c).copied() else { + return Err(vm.error(format!("Invalid constant index: {}", c))); }; - (table, key, value) + constant + } else { + vm.register_stack[base_ptr + c] }; + (table, key, value) + }; + vm.table_set_with_meta(table_value, key_value, set_value)?; Ok(()) @@ -389,17 +362,13 @@ pub fn exec_self(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let table = vm.register_stack[base_ptr + b]; // Get method key from constant - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - let func = unsafe { &*func_ptr }; - let key = func - .borrow() - .chunk - .constants - .get(c) - .copied() - .ok_or_else(|| LuaError::RuntimeError(format!("Invalid constant index: {}", c)))?; + let Some(func_ref) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; + + let Some(key) = func_ref.borrow().chunk.constants.get(c).copied() else { + return Err(vm.error(format!("Invalid constant index: {}", c))); + }; // R[A+1] := R[B] (self parameter) vm.register_stack[base_ptr + a + 1] = table; diff --git a/crates/luars/src/lua_vm/dispatcher/upvalue_instructions.rs b/crates/luars/src/lua_vm/dispatcher/upvalue_instructions.rs index 2e97af60..af7808a1 100644 --- a/crates/luars/src/lua_vm/dispatcher/upvalue_instructions.rs +++ b/crates/luars/src/lua_vm/dispatcher/upvalue_instructions.rs @@ -1,7 +1,10 @@ /// Upvalue and closure operations /// /// These instructions handle upvalues, closures, and variable captures. -use crate::lua_vm::{Instruction, LuaError, LuaResult, LuaVM}; +use crate::{ + LuaValue, + lua_vm::{Instruction, LuaResult, LuaVM}, +}; /// GETUPVAL A B /// R[A] := UpValue[B] @@ -12,16 +15,13 @@ pub fn exec_getupval(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let frame = vm.current_frame(); let base_ptr = frame.base_ptr; - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - let func = unsafe { &*func_ptr }; - let func_ref = func.borrow(); + let Some(func_ref) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; - let upvalue = func_ref - .upvalues - .get(b) - .ok_or_else(|| LuaError::RuntimeError(format!("Invalid upvalue index: {}", b)))?; + let Some(upvalue) = func_ref.borrow().upvalues.get(b).cloned() else { + return Err(vm.error(format!("Invalid upvalue index: {}", b))); + }; let value = upvalue.get_value(&vm.frames, &vm.register_stack); vm.register_stack[base_ptr + a] = value; @@ -38,16 +38,13 @@ pub fn exec_setupval(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let frame = vm.current_frame(); let base_ptr = frame.base_ptr; - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - let func = unsafe { &*func_ptr }; - let func_ref = func.borrow(); + let Some(func_ref) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; - let upvalue = func_ref - .upvalues - .get(b) - .ok_or_else(|| LuaError::RuntimeError(format!("Invalid upvalue index: {}", b)))?; + let Some(upvalue) = func_ref.borrow().upvalues.get(b).cloned() else { + return Err(vm.error(format!("Invalid upvalue index: {}", b))); + }; let value = vm.register_stack[base_ptr + a]; @@ -78,28 +75,22 @@ pub fn exec_closure(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let frame = vm.current_frame(); let base_ptr = frame.base_ptr; - let func_ptr = frame - .get_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - let func = unsafe { &*func_ptr }; - let func_ref = func.borrow(); + let Some(func_ref) = frame.get_lua_function() else { + return Err(vm.error("Not a Lua function".to_string())); + }; - let proto = func_ref - .chunk - .child_protos - .get(bx) - .ok_or_else(|| LuaError::RuntimeError(format!("Invalid prototype index: {}", bx)))? - .clone(); + let Some(proto) = func_ref.borrow().chunk.child_protos.get(bx).cloned() else { + return Err(vm.error(format!("Invalid prototype index: {}", bx))); + }; // Get upvalue descriptors from the prototype let upvalue_descs = proto.upvalue_descs.clone(); - drop(func_ref); // Create upvalues for the new closure based on descriptors let mut upvalues = Vec::new(); let mut new_open_upvalues = Vec::new(); - for (_i, desc) in upvalue_descs.iter().enumerate() { + for desc in upvalue_descs.iter() { if desc.is_local { // Upvalue refers to a register in current function @@ -120,16 +111,10 @@ pub fn exec_closure(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { } } else { // Upvalue refers to an upvalue in the enclosing function - let parent_func = unsafe { &*func_ptr }; - let parent_upvalues = &parent_func.borrow().upvalues; - - if let Some(parent_uv) = parent_upvalues.get(desc.index as usize) { + if let Some(parent_uv) = func_ref.borrow().upvalues.get(desc.index as usize) { upvalues.push(parent_uv.clone()); } else { - return Err(LuaError::RuntimeError(format!( - "Invalid upvalue index in parent: {}", - desc.index - ))); + return Err(vm.error(format!("Invalid upvalue index in parent: {}", desc.index))); } } } @@ -167,7 +152,7 @@ pub fn exec_vararg(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let value = if vararg_start + i < vm.register_stack.len() { vm.register_stack[vararg_start + i] } else { - crate::LuaValue::nil() + LuaValue::nil() }; vm.register_stack[base_ptr + a + i] = value; } @@ -178,7 +163,7 @@ pub fn exec_vararg(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { let value = if i < vararg_count && vararg_start + i < vm.register_stack.len() { vm.register_stack[vararg_start + i] } else { - crate::LuaValue::nil() + LuaValue::nil() }; vm.register_stack[base_ptr + a + i] = value; } @@ -201,11 +186,11 @@ pub fn exec_concat(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { // Estimate total capacity and format numbers inline with itoa let mut total_capacity = 0usize; let mut all_simple = true; - + // First pass: check types and estimate capacity for i in 0..=b { let value = vm.register_stack[base_ptr + a + i]; - + if let Some(s) = value.as_lua_string() { total_capacity += s.as_str().len(); } else if value.is_integer() { @@ -217,16 +202,16 @@ pub fn exec_concat(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { break; } } - + // Fast path: all strings/numbers, concatenate directly if all_simple { let mut result = String::with_capacity(total_capacity); let mut int_buffer = itoa::Buffer::new(); let mut float_buffer = ryu::Buffer::new(); - + for i in 0..=b { let value = vm.register_stack[base_ptr + a + i]; - + if let Some(s) = value.as_lua_string() { result.push_str(s.as_str()); } else if let Some(int_val) = value.as_integer() { @@ -237,10 +222,10 @@ pub fn exec_concat(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { result.push_str(float_buffer.format(float_val)); } } - + let result_value = vm.create_string(&result); vm.register_stack[base_ptr + a] = result_value; - + // No GC check for fast path - rely on debt mechanism // Only large allocations trigger automatic GC return Ok(()); @@ -262,7 +247,7 @@ pub fn exec_concat(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { } else { None }; - + let right_str = if let Some(s) = next_value.as_lua_string() { Some(s.as_str().to_string()) } else if let Some(int_val) = next_value.as_integer() { @@ -310,7 +295,7 @@ pub fn exec_concat(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { } if !found_metamethod { - return Err(LuaError::RuntimeError(format!( + return Err(vm.error(format!( "attempt to concatenate a {} value", if result_value.is_string() || result_value.is_number() { next_value.type_name() @@ -357,36 +342,34 @@ pub fn exec_setlist(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { // 1. SETLIST is only emitted by compiler for table constructors // 2. Newly created tables don't have metatables yet // 3. Even if metatable exists, Lua doesn't call __newindex for array part - + // Fast path: Use cached pointer (avoids ObjectPool lookup) - if let Some(ptr) = table.as_table_ptr() { - let lua_table = unsafe { &*ptr }; - + if let Some(lua_table) = table.as_lua_table() { // ULTRA-OPTIMIZED: Sequential insertion from start_idx let mut table_mut = lua_table.borrow_mut(); - + // Initialize array if needed if table_mut.array.is_none() { table_mut.array = Some(Vec::new()); } - + let array = table_mut.array.as_mut().unwrap(); - - // Reserve capacity upfront to avoid reallocations + + // Reserve capacity upfront to avoid reallocations let expected_end_idx = (start_idx - 1 + count) as usize; let current_len = array.len(); if array.capacity() < expected_end_idx && expected_end_idx > current_len { array.reserve(expected_end_idx - current_len); } - + // Batch process all values with proper index handling for i in 0..count { let key_int = (start_idx + i) as i64; let value = vm.register_stack[base_ptr + a + i + 1]; - + // Use optimized set_int which correctly handles nils and gaps let idx = (key_int - 1) as usize; - + if value.is_nil() { // Nil values: only store if filling a gap in existing array if idx < array.len() { @@ -410,12 +393,13 @@ pub fn exec_setlist(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { } } drop(table_mut); // Release borrow early - + // Write barrier for GC (outside borrow scope) // OPTIMIZATION: Only check for GC-relevant values if let Some(table_id) = table.as_table_id() { - vm.gc.barrier_forward(crate::gc::GcObjectType::Table, table_id.0); - + vm.gc + .barrier_forward(crate::gc::GcObjectType::Table, table_id.0); + // Optimized barrier: skip primitives for i in 0..count { let value = vm.register_stack[base_ptr + a + i + 1]; @@ -427,7 +411,7 @@ pub fn exec_setlist(vm: &mut LuaVM, instr: u32) -> LuaResult<()> { } else { // Slow path: fallback to table_set_with_meta for i in 0..count { - let key = crate::LuaValue::integer((start_idx + i) as i64); + let key = LuaValue::integer((start_idx + i) as i64); let value = vm.register_stack[base_ptr + a + i + 1]; vm.table_set_with_meta(table, key, value)?; } diff --git a/crates/luars/src/lua_vm/lua_call_frame.rs b/crates/luars/src/lua_vm/lua_call_frame.rs index 8c31c460..f1b3b4a0 100644 --- a/crates/luars/src/lua_vm/lua_call_frame.rs +++ b/crates/luars/src/lua_vm/lua_call_frame.rs @@ -101,6 +101,14 @@ impl LuaCallFrame { } } + pub fn get_lua_function(&self) -> Option<&RefCell> { + if self.is_lua() { + self.function_value.as_lua_function() + } else { + None + } + } + #[inline(always)] pub fn is_lua(&self) -> bool { self.flags & FLAG_IS_LUA != 0 diff --git a/crates/luars/src/lua_vm/lua_error.rs b/crates/luars/src/lua_vm/lua_error.rs index 6a99f494..bfda2c91 100644 --- a/crates/luars/src/lua_vm/lua_error.rs +++ b/crates/luars/src/lua_vm/lua_error.rs @@ -1,21 +1,45 @@ use crate::LuaValue; -#[derive(Debug, Clone)] +/// Lightweight error enum - only 1 byte! +/// Actual error data stored in VM to reduce Result size +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum LuaError { - RuntimeError(String), - CompileError(String), - /// Special error type for coroutine yield (not a real error) - /// Contains the yield values - Yield(Vec), - // Add more error types as needed + /// Runtime error - message stored in vm.error_message + RuntimeError, + /// Compile error - message stored in vm.error_message + CompileError, + /// Coroutine yield - values stored in vm.yield_values + Yield, + /// VM exit (internal use) - returned when top-level frame returns + Exit, } impl std::fmt::Display for LuaError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - LuaError::RuntimeError(msg) => write!(f, "Runtime Error: {}", msg), - LuaError::CompileError(msg) => write!(f, "Compile Error: {}", msg), - LuaError::Yield(_) => write!(f, "Coroutine Yield"), + LuaError::RuntimeError => write!(f, "Runtime Error"), + LuaError::CompileError => write!(f, "Compile Error"), + LuaError::Yield => write!(f, "Coroutine Yield"), + LuaError::Exit => write!(f, "VM Exit"), + } + } +} + +/// Legacy error type for compatibility (will be phased out) +/// Used during transition period +#[derive(Debug, Clone)] +pub enum LuaErrorLegacy { + RuntimeError(String), + CompileError(String), + Yield(Vec), +} + +impl From for LuaError { + fn from(legacy: LuaErrorLegacy) -> Self { + match legacy { + LuaErrorLegacy::RuntimeError(_) => LuaError::RuntimeError, + LuaErrorLegacy::CompileError(_) => LuaError::CompileError, + LuaErrorLegacy::Yield(_) => LuaError::Yield, } } } diff --git a/crates/luars/src/lua_vm/mod.rs b/crates/luars/src/lua_vm/mod.rs index 7f20fca8..c5bf194f 100644 --- a/crates/luars/src/lua_vm/mod.rs +++ b/crates/luars/src/lua_vm/mod.rs @@ -14,7 +14,7 @@ use crate::lua_value::{ }; pub use crate::lua_vm::lua_call_frame::LuaCallFrame; pub use crate::lua_vm::lua_error::LuaError; -use crate::{ObjectPool, lib_registry}; +use crate::{Compiler, ObjectPool, lib_registry}; use dispatcher::dispatch_instruction; pub use opcode::{Instruction, OpCode}; use std::cell::RefCell; @@ -73,6 +73,15 @@ pub struct LuaVM { // Async executor for Lua-Rust async bridge #[cfg(feature = "async")] pub(crate) async_executor: AsyncExecutor, + + // ===== Lightweight Error Storage ===== + // Store error/yield data here instead of in Result + // This reduces Result size from ~24 bytes to 1 byte! + /// Error message for RuntimeError/CompileError + pub(crate) error_message: String, + + /// Yield values for coroutine yield + pub(crate) yield_values: Vec, } impl LuaVM { @@ -96,6 +105,9 @@ impl LuaVM { object_pool: ObjectPool::new(), #[cfg(feature = "async")] async_executor: AsyncExecutor::new(), + // Initialize error storage + error_message: String::new(), + yield_values: Vec::new(), }; // Set _G to point to the global table itself @@ -189,9 +201,9 @@ impl LuaVM { self.open_upvalues.clear(); // Get function pointer - let func_ptr = func - .as_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a function".to_string()))?; + let Some(func_ptr) = func.as_function_ptr() else { + return Err(self.error("Not a function".to_string())); + }; let func_obj = unsafe { &*func_ptr }; let func_ref = func_obj.borrow(); @@ -239,11 +251,9 @@ impl LuaVM { /// Compile source code using VM's string pool pub fn compile(&mut self, source: &str) -> LuaResult { - use crate::compiler::Compiler; - let chunk = match Compiler::compile(self, source) { Ok(c) => c, - Err(e) => return Err(LuaError::CompileError(e)), + Err(e) => return Err(self.compile_error(e)), }; Ok(chunk) @@ -252,25 +262,23 @@ impl LuaVM { /// Main execution loop - interprets bytecode instructions /// Main VM execution loop - MAXIMUM PERFORMANCE /// 零返回值,零分支,直接调度 - fn run(&mut self) -> LuaResult { + fn run(&mut self) -> LuaResult { + // CRITICAL OPTIMIZATION: Check frames.is_empty() OUTSIDE the loop + // RETURN instruction will pop frames and check if empty + // This removes 100M+ unnecessary checks for tight loops loop { - // 检查是否有帧 - if self.frames.is_empty() { - return Ok(self - .return_values - .first() - .copied() - .unwrap_or(LuaValue::nil())); - } - + // ULTRA-FAST PATH: Direct unsafe access, no bounds checks let frame = unsafe { self.frames.last_mut().unwrap_unchecked() }; let instr = unsafe { *frame.code_ptr.add(frame.pc) }; frame.pc += 1; - if let Err(e) = dispatch_instruction(self, instr) { match e { - LuaError::Yield(_) => return Ok(LuaValue::nil()), + LuaError::Yield => return Ok(LuaValue::nil()), + LuaError::Exit => { + // Normal exit when all frames are popped + return Ok(LuaValue::nil()); + } _ => return Err(e), } } @@ -313,9 +321,10 @@ impl LuaVM { if let Some(global_id) = self.global_value.as_table_id() { let global = self.object_pool.get_table(global_id).unwrap(); global.borrow_mut().raw_set(key.clone(), value.clone()); - + // Write barrier: global table (old) may now reference new object - self.gc.barrier_forward(crate::gc::GcObjectType::Table, global_id.0); + self.gc + .barrier_forward(crate::gc::GcObjectType::Table, global_id.0); self.gc.barrier_back(&value); } } @@ -324,9 +333,10 @@ impl LuaVM { if let Some(global_id) = self.global_value.as_table_id() { let global = self.object_pool.get_table(global_id).unwrap(); global.borrow_mut().raw_set(key.clone(), value.clone()); - + // Write barrier - self.gc.barrier_forward(crate::gc::GcObjectType::Table, global_id.0); + self.gc + .barrier_forward(crate::gc::GcObjectType::Table, global_id.0); self.gc.barrier_back(&value); } } @@ -403,11 +413,11 @@ impl LuaVM { ) -> LuaResult<(bool, Vec)> { // Extract Rc from LuaValue let thread_rc = unsafe { - let ptr = thread_val - .as_thread_ptr() - .ok_or(LuaError::RuntimeError("invalid thread".to_string()))?; + let Some(ptr) = thread_val.as_thread_ptr() else { + return Err(self.error("invalid thread".to_string())); + }; if ptr.is_null() { - return Err(LuaError::RuntimeError("invalid thread".to_string())); + return Err(self.error("invalid thread".to_string())); } let rc = Rc::from_raw(ptr); let cloned = rc.clone(); @@ -470,8 +480,9 @@ impl LuaVM { .unwrap_or(LuaValue::nil()); match self.call_function_internal(func, args) { Ok(values) => Ok(values), - Err(LuaError::Yield(values)) => { + Err(LuaError::Yield) => { // Function yielded - this is expected + let values = self.take_yield_values(); Ok(values) } Err(e) => Err(e), @@ -516,7 +527,7 @@ impl LuaVM { self.return_values = args; // Continue execution from where it yielded - self.run().map(|v| vec![v]) + self.run().map(|_| self.return_values.clone()) }; // Check if thread yielded by examining the result @@ -557,9 +568,15 @@ impl LuaVM { thread.status = CoroutineStatus::Dead; Ok((true, values)) } - Err(e) => { + Err(LuaError::Exit) => { + // Normal exit - coroutine finished successfully + thread.status = CoroutineStatus::Dead; + Ok((true, thread.return_values.clone())) + } + Err(_) => { thread.status = CoroutineStatus::Dead; - Ok((false, vec![self.create_string(&format!("{}", e))])) + let error_msg = self.get_error_message().to_string(); + Ok((false, vec![self.create_string(&error_msg)])) } } }; @@ -584,11 +601,9 @@ impl LuaVM { thread_rc.borrow_mut().yield_values = values.clone(); thread_rc.borrow_mut().status = CoroutineStatus::Suspended; // Return Yield "error" to unwind the call stack - Err(LuaError::Yield(values)) + Err(self.do_yield(values)) } else { - Err(LuaError::RuntimeError( - "attempt to yield from outside a coroutine".to_string(), - )) + Err(self.error("attempt to yield from outside a coroutine".to_string())) } } @@ -896,10 +911,11 @@ impl LuaVM { if has_key { // Key exists, use raw set lua_table.borrow_mut().raw_set(key.clone(), value.clone()); - + // Write barrier: table may now reference new objects if let Some(table_id) = lua_table_val.as_table_id() { - self.gc.barrier_forward(crate::gc::GcObjectType::Table, table_id.0); + self.gc + .barrier_forward(crate::gc::GcObjectType::Table, table_id.0); self.gc.barrier_back(&value); } return Ok(()); @@ -923,10 +939,9 @@ impl LuaVM { mt_borrowed.raw_get(&newindex_key) } else { // Fallback to ObjectPool lookup - let metatable = self - .object_pool - .get_table(table_id) - .ok_or(LuaError::RuntimeError("missing metatable".to_string()))?; + let Some(metatable) = self.object_pool.get_table(table_id) else { + return Err(self.error("missing metatable".to_string())); + }; let mt_borrowed = metatable.borrow(); mt_borrowed.raw_get(&newindex_key) }; @@ -950,10 +965,11 @@ impl LuaVM { // No metamethod, use raw set lua_table.borrow_mut().raw_set(key.clone(), value.clone()); - + // Write barrier for new insertion if let Some(table_id) = lua_table_val.as_table_id() { - self.gc.barrier_forward(crate::gc::GcObjectType::Table, table_id.0); + self.gc + .barrier_forward(crate::gc::GcObjectType::Table, table_id.0); self.gc.barrier_back(&value); } return Ok(()); @@ -961,13 +977,12 @@ impl LuaVM { // Slow path: no cached pointer, use ObjectPool lookup let Some(table_id) = lua_table_val.as_table_id() else { - return Err(LuaError::RuntimeError("table_set: not a table".to_string())); + return Err(self.error("table_set: not a table".to_string())); }; - let lua_table = self - .object_pool - .get_table(table_id) - .ok_or(LuaError::RuntimeError("invalid table".to_string()))?; + let Some(lua_table) = self.object_pool.get_table(table_id) else { + return Err(self.error("invalid table".to_string())); + }; // Check if key already exists let has_key = { @@ -978,9 +993,10 @@ impl LuaVM { if has_key { // Key exists, use raw set lua_table.borrow_mut().raw_set(key.clone(), value.clone()); - + // Write barrier - self.gc.barrier_forward(crate::gc::GcObjectType::Table, table_id.0); + self.gc + .barrier_forward(crate::gc::GcObjectType::Table, table_id.0); self.gc.barrier_back(&value); return Ok(()); } @@ -995,10 +1011,9 @@ impl LuaVM { && let Some(table_id) = mt.as_table_id() { let newindex_key = self.create_string("__newindex"); - let metatable = self - .object_pool - .get_table(table_id) - .ok_or(LuaError::RuntimeError("missing metatable".to_string()))?; + let Some(metatable) = self.object_pool.get_table(table_id) else { + return Err(self.error("missing metatable".to_string())); + }; let newindex_value = { let mt_borrowed = metatable.borrow(); @@ -1024,15 +1039,15 @@ impl LuaVM { } } - let lua_table = self - .object_pool - .get_table(table_id) - .ok_or(LuaError::RuntimeError("invalid table".to_string()))?; + let Some(lua_table) = self.object_pool.get_table(table_id) else { + return Err(self.error("invalid table".to_string())); + }; // No metamethod or key doesn't exist, use raw set lua_table.borrow_mut().raw_set(key.clone(), value.clone()); - + // Write barrier for new insertion - self.gc.barrier_forward(crate::gc::GcObjectType::Table, table_id.0); + self.gc + .barrier_forward(crate::gc::GcObjectType::Table, table_id.0); self.gc.barrier_back(&value); Ok(()) } @@ -1131,14 +1146,14 @@ impl LuaVM { if reg_idx < stack_pos { break; } - + self.to_be_closed.pop(); - + // Skip nil values if value.is_nil() { continue; } - + // Try to get __close metamethod let close_key = self.create_string("__close"); let metamethod = if let Some(mt) = self.table_get_metatable(&value) { @@ -1146,7 +1161,7 @@ impl LuaVM { } else { None }; - + if let Some(mm) = metamethod { if !mm.is_nil() { // Call __close(value, error) @@ -1172,15 +1187,15 @@ impl LuaVM { /// - Long string: 1 Box allocation, GC registration, no pooling pub fn create_string(&mut self, s: &str) -> LuaValue { let id = self.object_pool.create_string(s); - + // Estimate memory cost: string data + LuaString struct overhead // LuaString: ~32 bytes base + string length let estimated_bytes = 32 + s.len(); self.gc.record_allocation(estimated_bytes); - + // GC check MUST NOT happen here - object not yet protected! // Caller must call check_gc() AFTER storing value in register - + // Get pointer from object pool for direct access let ptr = self .object_pool @@ -1189,7 +1204,7 @@ impl LuaVM { .unwrap_or(std::ptr::null()); LuaValue::string_id_ptr(id, ptr) } - + /// Get string by LuaValue (resolves ID from object pool) pub fn get_string(&self, value: &LuaValue) -> Option<&LuaString> { if let Some(id) = value.as_string_id() { @@ -1365,7 +1380,7 @@ impl LuaVM { /// Check GC and run a step if needed (like luaC_checkGC in Lua 5.4) /// This is called after allocating new objects (strings, tables, functions) /// Uses GC debt mechanism: runs when debt > 0 - /// + /// /// OPTIMIZATION: Use incremental collection with work budget fn check_gc(&mut self) { // Fast path: check debt without collecting roots @@ -1521,6 +1536,48 @@ impl LuaVM { ) } + // ===== Lightweight Error Handling API ===== + + /// Set runtime error and return lightweight error enum + #[inline] + pub fn error(&mut self, message: impl Into) -> LuaError { + self.error_message = message.into(); + LuaError::RuntimeError + } + + /// Set compile error and return lightweight error enum + #[inline] + pub fn compile_error(&mut self, message: impl Into) -> LuaError { + self.error_message = message.into(); + LuaError::CompileError + } + + /// Set yield values and return lightweight error enum + #[inline] + pub fn do_yield(&mut self, values: Vec) -> LuaError { + self.yield_values = values; + LuaError::Yield + } + + /// Get the current error message + #[inline] + pub fn get_error_message(&self) -> &str { + &self.error_message + } + + /// Take the yield values (clears internal storage) + #[inline] + pub fn take_yield_values(&mut self) -> Vec { + std::mem::take(&mut self.yield_values) + } + + /// Clear error state + #[inline] + pub fn clear_error(&mut self) { + self.error_message.clear(); + self.yield_values.clear(); + } + /// Try to get a metamethod from a value fn get_metamethod(&mut self, value: &LuaValue, event: &str) -> Option { match value.kind() { @@ -1604,16 +1661,10 @@ impl LuaVM { ) -> LuaResult { match metamethod.kind() { LuaValueKind::Function => { - let func_id = metamethod - .as_function_id() - .ok_or(LuaError::RuntimeError("Invalid function ID".to_string()))?; - let max_stack_size = - { - let func_ref = self.object_pool.get_function(func_id).ok_or( - LuaError::RuntimeError("Invalid function reference".to_string()), - )?; - func_ref.borrow().chunk.max_stack_size - }; + let Some(func_ref) = metamethod.as_lua_function() else { + return Err(self.error("Invalid function ID".to_string())); + }; + let max_stack_size = func_ref.borrow().chunk.max_stack_size; // Save current state let frame_id = self.next_frame_id; @@ -1631,11 +1682,7 @@ impl LuaVM { } // Get code pointer from metamethod function - let meta_func_ptr = metamethod - .as_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Invalid metamethod".to_string()))?; - let meta_func = unsafe { &*meta_func_ptr }; - let code_ptr = meta_func.borrow().chunk.code.as_ptr(); + let code_ptr = func_ref.borrow().chunk.code.as_ptr(); let temp_frame = LuaCallFrame::new_lua_function( frame_id, @@ -1729,9 +1776,7 @@ impl LuaVM { if let Some(str) = self.get_string(&s) { Ok(str.as_str().to_string()) } else { - Err(LuaError::RuntimeError( - "`__tostring` metamethod did not return a string".to_string(), - )) + Err(self.error("`__tostring` metamethod did not return a string".to_string())) } } else { Ok(value.to_string_repr()) @@ -1787,9 +1832,10 @@ impl LuaVM { // Success: return true and the return values Ok((true, return_values)) } - Err(LuaError::Yield(values)) => { + Err(LuaError::Yield) => { // Yield is not an error - propagate it - Err(LuaError::Yield(values)) + let values = self.take_yield_values(); + Err(self.do_yield(values)) } Err(error_msg) => { // Real error: clean up frames and return false with error message @@ -1817,32 +1863,19 @@ impl LuaVM { args: Vec, err_handler: LuaValue, ) -> LuaResult<(bool, Vec)> { - eprintln!("[xpcall] protected_call_with_handler called"); - eprintln!("[xpcall] err_handler type: {:?}", err_handler.kind()); - let old_handler = self.error_handler.clone(); self.error_handler = Some(err_handler.clone()); let initial_frame_count = self.frames.len(); - eprintln!("[xpcall] initial_frame_count: {}", initial_frame_count); let result = self.call_function_internal(func, args); self.error_handler = old_handler; match result { - Ok(values) => { - eprintln!("[xpcall] Success, returning {} values", values.len()); - Ok((true, values)) - } - Err(LuaError::Yield(values)) => { - eprintln!("[xpcall] Yield encountered"); - // Yield is not an error - propagate it - Err(LuaError::Yield(values)) - } + Ok(values) => Ok((true, values)), + Err(LuaError::Yield) => Err(LuaError::Yield), Err(err_msg) => { - eprintln!("[xpcall] Error encountered: {:?}", err_msg); - // Clean up frames created by the failed function call while self.frames.len() > initial_frame_count { let frame = self.frames.pop().unwrap(); @@ -1853,11 +1886,13 @@ impl LuaVM { eprintln!("[xpcall] Calling error handler"); // Extract the actual error message without the "Runtime Error: " prefix let (err_value, err_display) = match &err_msg { - LuaError::RuntimeError(msg) => { - (self.create_string(msg), format!("{}", err_msg)) + LuaError::RuntimeError => { + let msg = self.get_error_message().to_string(); + (self.create_string(&msg), format!("Runtime Error: {}", msg)) } - LuaError::CompileError(msg) => { - (self.create_string(msg), format!("{}", err_msg)) + LuaError::CompileError => { + let msg = self.get_error_message().to_string(); + (self.create_string(&msg), format!("Compile Error: {}", msg)) } _ => { let display = format!("{}", err_msg); @@ -1865,13 +1900,13 @@ impl LuaVM { } }; let handler_result = self.call_function_internal(err_handler, vec![err_value]); - eprintln!("[xpcall] Handler result: {:?}", handler_result.is_ok()); match handler_result { Ok(handler_values) => Ok((false, handler_values)), - Err(LuaError::Yield(values)) => { + Err(LuaError::Yield) => { // Yield from error handler - propagate it - Err(LuaError::Yield(values)) + let values = self.take_yield_values(); + Err(self.do_yield(values)) } Err(_) => { let err_str = @@ -1962,11 +1997,9 @@ impl LuaVM { // Call CFunction - ensure frame is always popped even on error let result = match cfunc(self) { - Ok(r) => Ok(r), - Err(LuaError::Yield(values)) => { - // CFunction yielded - this is valid for coroutine.yield - // Don't pop frame, just return the yield - return Err(LuaError::Yield(values)); + Ok(r) => r, + Err(LuaError::Yield) => { + return Err(LuaError::Yield); } Err(e) => { self.frames.pop(); @@ -1976,16 +2009,15 @@ impl LuaVM { self.frames.pop(); - Ok(result?.all_values()) + Ok(result.all_values()) } LuaValueKind::Function => { - let lua_func_id = func.as_function_id().unwrap(); + let Some(lua_func_ref) = func.as_lua_function() else { + return Err(self.error("Invalid function reference".to_string())); + }; // Get max_stack_size before entering the execution loop let max_stack_size = { - let lua_func_ref = self.object_pool.get_function(lua_func_id).ok_or( - LuaError::RuntimeError("Invalid function reference".to_string()), - )?; let size = lua_func_ref.borrow().chunk.max_stack_size; // Ensure at least 1 register for function body if size == 0 { 1 } else { size } @@ -2026,12 +2058,7 @@ impl LuaVM { let safe_result_reg = new_base; // Same as caller_base + caller_max_stack // Get code pointer from function - let func_ptr = func - .as_function_ptr() - .ok_or_else(|| LuaError::RuntimeError("Not a Lua function".to_string()))?; - let func_obj = unsafe { &*func_ptr }; - let code_ptr = func_obj.borrow().chunk.code.as_ptr(); - + let code_ptr = lua_func_ref.borrow().chunk.code.as_ptr(); let new_frame = LuaCallFrame::new_lua_function( frame_id, func, @@ -2060,9 +2087,7 @@ impl LuaVM { let chunk = if let Some(func_ref) = self.get_function(&function_value) { func_ref.borrow().chunk.clone() } else { - break Err(LuaError::RuntimeError( - "Invalid function in frame".to_string(), - )); + break Err(self.error("Invalid function in frame".to_string())); }; if pc >= chunk.code.len() { @@ -2075,7 +2100,7 @@ impl LuaVM { self.frames[frame_idx].pc += 1; // Dispatch instruction (zero-return-value design) - if let Err(e) = crate::lua_vm::dispatcher::dispatch_instruction(self, instr) { + if let Err(e) = dispatch_instruction(self, instr) { break Err(e); } }; @@ -2090,9 +2115,7 @@ impl LuaVM { Err(e) => Err(e), } } - _ => Err(LuaError::RuntimeError( - "attempt to call a non-function value".to_string(), - )), + _ => Err(self.error("attempt to call a non-function value".to_string())), } } diff --git a/crates/luars/src/object_pool.rs b/crates/luars/src/object_pool.rs index e012a488..f423defd 100644 --- a/crates/luars/src/object_pool.rs +++ b/crates/luars/src/object_pool.rs @@ -6,8 +6,8 @@ use crate::lua_value::{self, LuaUserdata}; use crate::{LuaFunction, LuaString, LuaTable}; use std::cell::RefCell; use std::collections::HashMap; -use std::rc::Rc; use std::hash::Hash; +use std::rc::Rc; /// Slot-based storage with free list for O(1) allocation and deallocation struct SlotVec { @@ -38,7 +38,7 @@ impl SlotVec { #[inline] fn insert(&mut self, value: T) -> u32 { self.count += 1; - + if let Some(free_id) = self.free_list.pop() { self.slots[free_id as usize] = Some(value); free_id @@ -83,12 +83,16 @@ impl SlotVec { while let Some(None) = self.slots.last() { let removed_id = self.slots.len() - 1; self.slots.pop(); - - if let Some(pos) = self.free_list.iter().rposition(|&id| id as usize == removed_id) { + + if let Some(pos) = self + .free_list + .iter() + .rposition(|&id| id as usize == removed_id) + { self.free_list.swap_remove(pos); } } - + self.slots.shrink_to_fit(); self.free_list.shrink_to_fit(); } @@ -211,7 +215,7 @@ impl ObjectPool { StringId(slot_id) } } - + /// Get string by ID #[inline] pub fn get_string(&self, id: StringId) -> Option<&Rc> { diff --git a/crates/luars/src/stdlib/async_lib.rs b/crates/luars/src/stdlib/async_lib.rs index 15039b00..8337abe5 100644 --- a/crates/luars/src/stdlib/async_lib.rs +++ b/crates/luars/src/stdlib/async_lib.rs @@ -28,17 +28,13 @@ pub fn register_async_functions(vm: &mut LuaVM) { /// Async sleep implementation (runs in tokio) async fn async_sleep_impl(args: Vec) -> LuaResult> { if args.is_empty() { - return Err(LuaError::RuntimeError( - "sleep requires 1 argument (milliseconds)".to_string(), - )); + return Err(vm.error("sleep requires 1 argument (milliseconds)".to_string())); } let ms = match args[0].as_number() { Some(n) => n as u64, None => { - return Err(LuaError::RuntimeError( - "sleep argument must be a number".to_string(), - )); + return Err(vm.error("sleep argument must be a number".to_string())); } }; @@ -52,7 +48,7 @@ async fn async_sleep_impl(args: Vec) -> LuaResult> { fn async_sleep_wrapper(vm: &mut LuaVM) -> LuaResult { // 检查是否在协程中 let coroutine = vm.current_thread_value.clone().ok_or_else(|| { - LuaError::RuntimeError("async.sleep can only be called from within a coroutine".to_string()) + vm.error("async.sleep can only be called from within a coroutine".to_string()) })?; // 收集参数 diff --git a/crates/luars/src/stdlib/basic.rs b/crates/luars/src/stdlib/basic.rs index 2a5e8ce3..cd3161df 100644 --- a/crates/luars/src/stdlib/basic.rs +++ b/crates/luars/src/stdlib/basic.rs @@ -3,9 +3,11 @@ // select, ipairs, pairs, next, pcall, xpcall, getmetatable, setmetatable, // rawget, rawset, rawlen, rawequal, collectgarbage, dofile, loadfile, load +use std::rc::Rc; + use crate::lib_registry::{LibraryModule, get_arg, get_args, require_arg}; use crate::lua_value::{LuaUpvalue, LuaValue, LuaValueKind, MultiValue}; -use crate::lua_vm::{LuaError, LuaResult, LuaVM}; +use crate::lua_vm::{LuaResult, LuaVM}; pub fn create_basic_lib() -> LibraryModule { crate::lib_module!("_G", { @@ -57,7 +59,7 @@ fn lua_print(vm: &mut LuaVM) -> LuaResult { /// type(v) - Return the type of a value as a string fn lua_type(vm: &mut LuaVM) -> LuaResult { - let value = get_arg(vm, 0).unwrap_or(LuaValue::nil()); + let value = get_arg(vm, 1).unwrap_or(LuaValue::nil()); let type_name = match value.kind() { LuaValueKind::Nil => "nil", @@ -76,13 +78,13 @@ fn lua_type(vm: &mut LuaVM) -> LuaResult { /// assert(v [, message]) - Raise error if v is false or nil fn lua_assert(vm: &mut LuaVM) -> LuaResult { - let condition = get_arg(vm, 0).unwrap_or(LuaValue::nil()); + let condition = get_arg(vm, 1).unwrap_or(LuaValue::nil()); if !condition.is_truthy() { - let message = get_arg(vm, 1) + let message = get_arg(vm, 2) .and_then(|v| v.as_lua_string().map(|s| s.as_str().to_string())) .unwrap_or_else(|| "assertion failed!".to_string()); - return Err(LuaError::RuntimeError(message)); + return Err(vm.error(message)); } // Return all arguments @@ -91,7 +93,7 @@ fn lua_assert(vm: &mut LuaVM) -> LuaResult { /// error(message) - Raise an error fn lua_error(vm: &mut LuaVM) -> LuaResult { - let message = get_arg(vm, 0) + let message = get_arg(vm, 1) .map(|v| { vm.value_to_string(&v) .unwrap_or_else(|_| v.to_string_repr()) @@ -99,18 +101,16 @@ fn lua_error(vm: &mut LuaVM) -> LuaResult { .unwrap_or_else(|| "error".to_string()); // Return error message directly for now - Err(LuaError::RuntimeError(message)) + Err(vm.error(message)) } /// tonumber(e [, base]) - Convert to number fn lua_tonumber(vm: &mut LuaVM) -> LuaResult { - let value = get_arg(vm, 0).unwrap_or(LuaValue::nil()); - let base = get_arg(vm, 1).and_then(|v| v.as_integer()).unwrap_or(10); + let value = require_arg(vm, 1, "tonumber")?; + let base = get_arg(vm, 2).and_then(|v| v.as_integer()).unwrap_or(10); if base < 2 || base > 36 { - return Err(LuaError::RuntimeError( - "bad argument #2 to 'tonumber' (base out of range)".to_string(), - )); + return Err(vm.error("bad argument #2 to 'tonumber' (base out of range)".to_string())); } let result = match value.kind() { @@ -151,24 +151,24 @@ fn lua_tonumber(vm: &mut LuaVM) -> LuaResult { /// tostring(v) - Convert to string /// OPTIMIZED: Fast path for common types fn lua_tostring(vm: &mut LuaVM) -> LuaResult { - let value = get_arg(vm, 0).unwrap_or(LuaValue::nil()); + let value = require_arg(vm, 1, "tostring")?; // Fast path: if already a string, return it directly if value.is_string() { return Ok(MultiValue::single(value)); } - + // Fast path: simple types without metamethods if value.is_nil() { let result = vm.create_string("nil"); return Ok(MultiValue::single(result)); } - + if let Some(b) = value.as_bool() { let result = vm.create_string(if b { "true" } else { "false" }); return Ok(MultiValue::single(result)); } - + if let Some(i) = value.as_integer() { // OPTIMIZED: Use itoa for fast integer formatting (10x faster than format!) let mut buffer = itoa::Buffer::new(); @@ -176,7 +176,7 @@ fn lua_tostring(vm: &mut LuaVM) -> LuaResult { let result = vm.create_string(s); return Ok(MultiValue::single(result)); } - + if let Some(f) = value.as_number() { let result = vm.create_string(&f.to_string()); return Ok(MultiValue::single(result)); @@ -190,7 +190,7 @@ fn lua_tostring(vm: &mut LuaVM) -> LuaResult { /// select(index, ...) - Return subset of arguments fn lua_select(vm: &mut LuaVM) -> LuaResult { - let index_arg = require_arg(vm, 0, "select")?; + let index_arg = require_arg(vm, 1, "select")?; let args = get_args(vm); // Handle "#" special case @@ -204,14 +204,12 @@ fn lua_select(vm: &mut LuaVM) -> LuaResult { } } - let index = index_arg.as_integer().ok_or_else(|| { - LuaError::RuntimeError("bad argument #1 to 'select' (number expected)".to_string()) - })?; + let index = index_arg + .as_integer() + .ok_or_else(|| vm.error("bad argument #1 to 'select' (number expected)".to_string()))?; if index == 0 { - return Err(LuaError::RuntimeError( - "bad argument #1 to 'select' (index out of range)".to_string(), - )); + return Err(vm.error("bad argument #1 to 'select' (index out of range)".to_string())); } let start = if index > 0 { @@ -231,13 +229,11 @@ fn lua_select(vm: &mut LuaVM) -> LuaResult { /// ipairs(t) - Return iterator for array part of table fn lua_ipairs(vm: &mut LuaVM) -> LuaResult { - let table_val = require_arg(vm, 0, "ipairs")?; + let table_val = require_arg(vm, 1, "ipairs")?; // Validate that it's a table if !table_val.is_table() { - return Err(LuaError::RuntimeError( - "bad argument #1 to 'ipairs' (table expected)".to_string(), - )); + return Err(vm.error("bad argument #1 to 'ipairs' (table expected)".to_string())); } // Return iterator function, table, and 0 @@ -254,20 +250,16 @@ fn lua_ipairs(vm: &mut LuaVM) -> LuaResult { #[inline] fn ipairs_next(vm: &mut LuaVM) -> LuaResult { // Ultra-fast path: direct argument access without validation - let table_val = if let Some(val) = get_arg(vm, 0) { + let table_val = if let Some(val) = get_arg(vm, 1) { val } else { - return Err(LuaError::RuntimeError( - "ipairs iterator: table expected".to_string(), - )); + return Err(vm.error("ipairs iterator: table expected".to_string())); }; - let index_val = if let Some(val) = get_arg(vm, 1) { + let index_val = if let Some(val) = get_arg(vm, 2) { val } else { - return Err(LuaError::RuntimeError( - "ipairs iterator: index expected".to_string(), - )); + return Err(vm.error("ipairs iterator: index expected".to_string())); }; // Fast type check using direct pointer (ZERO ObjectPool lookup!) @@ -292,20 +284,16 @@ fn ipairs_next(vm: &mut LuaVM) -> LuaResult { } // Slow path with proper validation - Err(LuaError::RuntimeError( - "ipairs iterator: invalid table or index".to_string(), - )) + Err(vm.error("ipairs iterator: invalid table or index".to_string())) } /// pairs(t) - Return iterator for all key-value pairs fn lua_pairs(vm: &mut LuaVM) -> LuaResult { - let table_val = require_arg(vm, 0, "pairs")?; + let table_val = require_arg(vm, 1, "pairs")?; // Validate that it's a table if table_val.as_table_id().is_none() { - return Err(LuaError::RuntimeError( - "bad argument #1 to 'pairs' (table expected)".to_string(), - )); + return Err(vm.error("bad argument #1 to 'pairs' (table expected)".to_string())); } // TODO: Check for __pairs metamethod @@ -319,8 +307,8 @@ fn lua_pairs(vm: &mut LuaVM) -> LuaResult { /// next(table [, index]) - Return next key-value pair fn lua_next(vm: &mut LuaVM) -> LuaResult { - let table_val = require_arg(vm, 0, "next")?; - let index_val = get_arg(vm, 1).unwrap_or(LuaValue::nil()); + let table_val = require_arg(vm, 1, "next")?; + let index_val = get_arg(vm, 2).unwrap_or(LuaValue::nil()); // CRITICAL OPTIMIZATION: Use direct pointer access instead of ObjectPool lookup! // This eliminates HashMap lookup on every iteration @@ -333,7 +321,7 @@ fn lua_next(vm: &mut LuaVM) -> LuaResult { None => Ok(MultiValue::single(LuaValue::nil())), } } else { - Err(LuaError::RuntimeError("Invalid table".to_string())) + Err(vm.error("Invalid table".to_string())) } } @@ -341,8 +329,8 @@ fn lua_next(vm: &mut LuaVM) -> LuaResult { fn lua_pcall(vm: &mut LuaVM) -> LuaResult { // pcall(f, arg1, arg2, ...) -> status, result or error - // Get the function to call (argument 0) - let func = require_arg(vm, 0, "pcall")?; + // Get the function to call (argument 1) + let func = require_arg(vm, 1, "pcall")?; // Get all arguments after the function let all_args = get_args(vm); @@ -365,26 +353,15 @@ fn lua_pcall(vm: &mut LuaVM) -> LuaResult { /// xpcall(f, msgh [, arg1, ...]) - Protected call with error handler fn lua_xpcall(vm: &mut LuaVM) -> LuaResult { // xpcall(f, msgh, arg1, arg2, ...) -> status, result or error - - eprintln!("[lua_xpcall] Called"); - let all_args = get_args(vm); - eprintln!("[lua_xpcall] Total args: {}", all_args.len()); - for (i, arg) in all_args.iter().enumerate() { - eprintln!("[lua_xpcall] Arg {}: {:?}", i, arg.kind()); - } - - // Get the function to call (argument 0) - let func = require_arg(vm, 0, "xpcall")?; - eprintln!("[lua_xpcall] func type: {:?}", func.kind()); - - // Get the error handler (argument 1) - let err_handler = require_arg(vm, 1, "xpcall")?; - eprintln!("[lua_xpcall] err_handler type: {:?}", err_handler.kind()); + // Get the function to call (argument 1) + let func = require_arg(vm, 1, "xpcall")?; + // Get the error handler (argument 2) + let err_handler = require_arg(vm, 2, "xpcall")?; // Get all arguments after the function and error handler let all_args = get_args(vm); let args: Vec = if all_args.len() > 2 { - all_args[2..].to_vec() + all_args[3..].to_vec() } else { Vec::new() }; @@ -401,13 +378,13 @@ fn lua_xpcall(vm: &mut LuaVM) -> LuaResult { /// getmetatable(object) - Get metatable fn lua_getmetatable(vm: &mut LuaVM) -> LuaResult { - let value = require_arg(vm, 0, "getmetatable")?; + let value = require_arg(vm, 1, "getmetatable")?; match value.kind() { LuaValueKind::Table => { - let table_ref = vm - .get_table(&value) - .ok_or(LuaError::RuntimeError("Invalid table".to_string()))?; + let Some(table_ref) = value.as_lua_table() else { + return Err(vm.error("Invalid table".to_string())); + }; // Get metatable, releasing the borrow immediately let mt = table_ref.borrow().get_metatable(); @@ -443,43 +420,24 @@ fn lua_getmetatable(vm: &mut LuaVM) -> LuaResult { /// setmetatable(table, metatable) - Set metatable fn lua_setmetatable(vm: &mut LuaVM) -> LuaResult { - let table = require_arg(vm, 0, "setmetatable")?; - let metatable = require_arg(vm, 1, "setmetatable")?; - - // First argument must be a table - if !table.is_table() { - return Err(LuaError::RuntimeError( - "setmetatable() first argument must be a table".to_string(), - )); - } + let table = require_arg(vm, 1, "setmetatable")?; + let metatable = require_arg(vm, 2, "setmetatable")?; - // Check if the current metatable has __metatable protection - let has_protection = { - let table_ref = vm - .get_table(&table) - .ok_or(LuaError::RuntimeError("Invalid table".to_string()))?; - table_ref.borrow().get_metatable() - }; // table_ref borrow ends here - - if let Some(current_mt) = has_protection { - let metatable_key = vm.create_string("__metatable"); - if let Some(mt_table) = vm.get_table(¤t_mt) { - if let Some(protected) = mt_table.borrow().raw_get(&metatable_key) { - if !protected.is_nil() { - // Metatable is protected, cannot change it - return Err(LuaError::RuntimeError( - "cannot change a protected metatable".to_string(), - )); - } + // Set the new metatable + let Some(table_ref) = table.as_lua_table() else { + return Err(vm.error("Invalid table".to_string())); + }; + + // Check if current metatable has __metatable field (protection) + if let Some(current_mt) = table_ref.borrow().get_metatable() { + if let Some(mt_table) = current_mt.as_lua_table() { + let metatable_field = vm.create_string("__metatable"); + if let Some(_) = mt_table.borrow().raw_get(&metatable_field) { + return Err(vm.error("cannot change a protected metatable".to_string())); } } } - // Set the new metatable - let table_ref = vm - .get_table(&table) - .ok_or(LuaError::RuntimeError("Invalid table".to_string()))?; - match metatable.kind() { LuaValueKind::Nil => { table_ref.borrow_mut().set_metatable(None); @@ -491,9 +449,9 @@ fn lua_setmetatable(vm: &mut LuaVM) -> LuaResult { .set_metatable(Some(metatable.clone())); } _ => { - return Err(LuaError::RuntimeError( - "setmetatable() second argument must be a table or nil".to_string(), - )); + return Err( + vm.error("setmetatable() second argument must be a table or nil".to_string()) + ); } } @@ -503,48 +461,34 @@ fn lua_setmetatable(vm: &mut LuaVM) -> LuaResult { /// rawget(table, index) - Get without metamethods fn lua_rawget(vm: &mut LuaVM) -> LuaResult { - let table = require_arg(vm, 0, "rawget")?; - let key = require_arg(vm, 1, "rawget")?; + let table = require_arg(vm, 1, "rawget")?; + let key = require_arg(vm, 2, "rawget")?; - if table.is_table() { - let table_ref = vm - .get_table(&table) - .ok_or(LuaError::RuntimeError("Invalid table".to_string()))?; + if let Some(table_ref) = table.as_lua_table() { let value = table_ref.borrow().raw_get(&key).unwrap_or(LuaValue::nil()); Ok(MultiValue::single(value)) } else { - Err(LuaError::RuntimeError( - "rawget() first argument must be a table".to_string(), - )) + Err(vm.error("rawget() first argument must be a table".to_string())) } } /// rawset(table, index, value) - Set without metamethods fn lua_rawset(vm: &mut LuaVM) -> LuaResult { - let table = require_arg(vm, 0, "rawset")?; - let key = require_arg(vm, 1, "rawset")?; - let value = require_arg(vm, 2, "rawset")?; - - if table.is_table() { - if key.is_nil() { - return Err(LuaError::RuntimeError("table index is nil".to_string())); - } + let table = require_arg(vm, 1, "rawset")?; + let key = require_arg(vm, 2, "rawset")?; + let value = require_arg(vm, 3, "rawset")?; - let table_ref = vm - .get_table(&table) - .ok_or(LuaError::RuntimeError("Invalid table".to_string()))?; - table_ref.borrow_mut().raw_set(key.clone(), value.clone()); - Ok(MultiValue::single(table.clone())) + if let Some(table_ref) = table.as_lua_table() { + table_ref.borrow_mut().raw_set(key, value); + Ok(MultiValue::single(table)) } else { - Err(LuaError::RuntimeError( - "rawset() first argument must be a table".to_string(), - )) + Err(vm.error("rawset() first argument must be a table".to_string())) } } /// rawlen(v) - Length without metamethods fn lua_rawlen(vm: &mut LuaVM) -> LuaResult { - let value = require_arg(vm, 0, "rawlen")?; + let value = require_arg(vm, 1, "rawlen")?; let len = unsafe { match value.kind() { @@ -553,15 +497,11 @@ fn lua_rawlen(vm: &mut LuaVM) -> LuaResult { if let Some(s) = value.as_string() { s.as_str().len() as i64 } else { - return Err(LuaError::RuntimeError( - "rawlen() argument must be a table or string".to_string(), - )); + return Err(vm.error("rawlen() argument must be a table or string".to_string())); } } _ => { - return Err(LuaError::RuntimeError( - "rawlen() argument must be a table or string".to_string(), - )); + return Err(vm.error("rawlen() argument must be a table or string".to_string())); } } }; @@ -571,8 +511,8 @@ fn lua_rawlen(vm: &mut LuaVM) -> LuaResult { /// rawequal(v1, v2) - Equality without metamethods fn lua_rawequal(vm: &mut LuaVM) -> LuaResult { - let v1 = get_arg(vm, 0).unwrap_or(LuaValue::nil()); - let v2 = get_arg(vm, 1).unwrap_or(LuaValue::nil()); + let v1 = get_arg(vm, 1).unwrap_or(LuaValue::nil()); + let v2 = get_arg(vm, 2).unwrap_or(LuaValue::nil()); let result = v1 == v2; Ok(MultiValue::single(LuaValue::boolean(result))) @@ -580,7 +520,7 @@ fn lua_rawequal(vm: &mut LuaVM) -> LuaResult { /// collectgarbage([opt [, arg]]) - Garbage collector control fn lua_collectgarbage(vm: &mut LuaVM) -> LuaResult { - let opt = get_arg(vm, 0) + let opt = get_arg(vm, 1) .and_then(|v| v.as_lua_string().map(|s| s.as_str().to_string())) .unwrap_or_else(|| "=(load)".to_string()); @@ -597,10 +537,7 @@ fn lua_collectgarbage(vm: &mut LuaVM) -> LuaResult { // Simplified: just return 0 Ok(MultiValue::single(LuaValue::integer(0))) } - _ => Err(LuaError::RuntimeError(format!( - "collectgarbage: invalid option '{}'", - opt - ))), + _ => Err(vm.error(format!("collectgarbage: invalid option '{}'", opt))), } } @@ -612,71 +549,52 @@ fn lua_version(vm: &mut LuaVM) -> LuaResult { /// require(modname) - Load a module fn lua_require(vm: &mut LuaVM) -> LuaResult { - let modname_str = require_arg(vm, 0, "require")?; + let modname_str = require_arg(vm, 1, "require")?; if !modname_str.is_string() { - return Err(LuaError::RuntimeError( - "module name must be a string".to_string(), - )); + return Err(vm.error("module name must be a string".to_string())); } // Check if module is already loaded in package.loaded - if let Some(package_table) = vm.get_global("package") { - if let Some(_package_id) = package_table.as_table_id() { + let package_table = if let Some(package_table) = vm.get_global("package") { + if let Some(package_ref) = package_table.as_lua_table() { let loaded_key = vm.create_string("loaded"); - let package_ref = vm - .get_table(&package_table) - .ok_or(LuaError::RuntimeError("Invalid package table".to_string()))?; if let Some(loaded_table) = package_ref.borrow().raw_get(&loaded_key) { - if loaded_table.is_table() { - if let Some(module_value) = vm - .get_table(&loaded_table) - .ok_or(LuaError::RuntimeError("Invalid loaded table".to_string()))? - .borrow() - .raw_get(&modname_str) - { - if !module_value.is_nil() { - return Ok(MultiValue::single(module_value)); + if let Some(loaded_ref) = loaded_table.as_lua_table() { + if let Some(module_val) = loaded_ref.borrow().raw_get(&modname_str) { + if !module_val.is_nil() { + // Module already loaded - return it + return Ok(MultiValue::single(module_val)); } } } // Module not in loaded table - continue to searchers } } - } + package_table + } else { + return Err(vm.error("package table not found".to_string())); + }; // Try each searcher in package.searchers let mut error_messages = Vec::new(); - - let package_table = vm - .get_global("package") - .ok_or_else(|| LuaError::RuntimeError("package table not found".to_string()))?; - let key = vm.create_string("searchers"); - let package_ref = vm - .get_table(&package_table) - .ok_or(LuaError::RuntimeError("Invalid package table".to_string()))?; + let Some(package_ref) = package_table.as_lua_table() else { + return Err(vm.error("Invalid package table".to_string())); + }; let searchers_val = package_ref .borrow() .raw_get(&key) .unwrap_or(LuaValue::nil()); - let searchers_table_val = searchers_val; - let _searchers_id = searchers_table_val - .as_table_id() - .ok_or_else(|| LuaError::RuntimeError("package.searchers is not a table".to_string()))?; - + let Some(searchers_table) = searchers_val.as_lua_table() else { + return Err(vm.error("package.searchers is not a table".to_string())); + }; // Try each searcher (1-based indexing) let mut i = 1; loop { - let searcher_key = LuaValue::integer(i); - let searchers_ref = vm - .get_table(&searchers_table_val) - .ok_or(LuaError::RuntimeError( - "Invalid searchers table".to_string(), - ))?; - let searcher = searchers_ref + let searcher = searchers_table .borrow() - .raw_get(&searcher_key) + .get_int(i) .unwrap_or(LuaValue::nil()); if searcher.is_nil() { @@ -692,10 +610,7 @@ fn lua_require(vm: &mut LuaVM) -> LuaResult { .and_then(|v| v.as_lua_string()) .map(|s| s.as_str().to_string()) .unwrap_or_else(|| "unknown error in searcher".to_string()); - return Err(LuaError::RuntimeError(format!( - "error calling searcher: {}", - error_msg - ))); + return Err(vm.error(format!("error calling searcher: {}", error_msg))); } // Check result @@ -720,7 +635,7 @@ fn lua_require(vm: &mut LuaVM) -> LuaResult { .and_then(|v| v.as_lua_string()) .map(|s| s.as_str().to_string()) .unwrap_or_else(|| "unknown error".to_string()); - return Err(LuaError::RuntimeError(format!( + return Err(vm.error(format!( "error loading module '{}': {}", modname_str, error_msg ))); @@ -733,19 +648,15 @@ fn lua_require(vm: &mut LuaVM) -> LuaResult { load_results[0].clone() }; - // Store in package.loaded - let loaded_key = vm.create_string("loaded"); - let package_ref = vm - .get_table(&package_table) - .ok_or(LuaError::RuntimeError("Invalid package table".to_string()))?; - if let Some(loaded_table) = package_ref.borrow().raw_get(&loaded_key) { - if let Some(_loaded_id) = loaded_table.as_table_id() { - let loaded_ref = vm - .get_table(&loaded_table) - .ok_or(LuaError::RuntimeError("Invalid loaded table".to_string()))?; - loaded_ref - .borrow_mut() - .raw_set(modname_str, module_value.clone()); + if let Some(package_ref) = package_table.as_lua_table() { + let loaded_key = vm.create_string("loaded"); + if let Some(loaded_table) = package_ref.borrow().raw_get(&loaded_key) { + if let Some(loaded_ref) = loaded_table.as_lua_table() { + loaded_ref + .borrow_mut() + .raw_set(modname_str, module_value.clone()); + } + // Module not in loaded table - continue to searchers } } @@ -761,12 +672,9 @@ fn lua_require(vm: &mut LuaVM) -> LuaResult { // All searchers failed if error_messages.is_empty() { - Err(LuaError::RuntimeError(format!( - "module '{}' not found", - modname_str - ))) + Err(vm.error(format!("module '{}' not found", modname_str))) } else { - Err(LuaError::RuntimeError(format!( + Err(vm.error(format!( "module '{}' not found:{}", modname_str, error_messages.join("") @@ -776,41 +684,39 @@ fn lua_require(vm: &mut LuaVM) -> LuaResult { /// load(chunk [, chunkname [, mode [, env]]]) - Load a chunk fn lua_load(vm: &mut LuaVM) -> LuaResult { - let chunk_val = require_arg(vm, 0, "load")?; + let chunk_val = require_arg(vm, 1, "load")?; // Get the chunk string - let code = unsafe { - chunk_val - .as_string() - .ok_or_else(|| { - LuaError::RuntimeError("bad argument #1 to 'load' (string expected)".to_string()) - })? - .as_str() - .to_string() + let Some(code) = chunk_val.as_lua_string() else { + return Err(vm.error("bad argument #1 to 'load' (string expected)".to_string())); }; // Optional chunk name for error messages - let _chunkname = get_arg(vm, 1) + let _chunkname = get_arg(vm, 2) .and_then(|v| unsafe { v.as_string().map(|s| s.as_str().to_string()) }) .unwrap_or_else(|| "=(load)".to_string()); // Optional mode ("b", "t", or "bt") - we only support "t" (text) - let _mode = get_arg(vm, 2) + let _mode = get_arg(vm, 3) .and_then(|v| unsafe { v.as_string().map(|s| s.as_str().to_string()) }) .unwrap_or_else(|| "bt".to_string()); // Optional environment table - let _env = get_arg(vm, 3); + let env = get_arg(vm, 4); // Compile the code using VM's string pool - match vm.compile(&code) { + match vm.compile(&code.as_str()) { Ok(chunk) => { // Create upvalue for _ENV (global table) // Loaded chunks need _ENV as upvalue[0] - let env_upvalue = LuaUpvalue::new_closed(vm.global_value); + let env_upvalue = if let Some(env) = env { + LuaUpvalue::new_closed(env) + } else { + LuaUpvalue::new_closed(vm.global_value) + }; let upvalues = vec![env_upvalue]; - let func = vm.create_function(std::rc::Rc::new(chunk), upvalues); + let func = vm.create_function(Rc::new(chunk), upvalues); Ok(MultiValue::single(func)) } Err(e) => { @@ -823,22 +729,19 @@ fn lua_load(vm: &mut LuaVM) -> LuaResult { /// loadfile([filename [, mode [, env]]]) - Load a file as a chunk fn lua_loadfile(vm: &mut LuaVM) -> LuaResult { - let filename = - get_arg(vm, 0).and_then(|v| unsafe { v.as_string().map(|s| s.as_str().to_string()) }); + let filename = require_arg(vm, 1, "loadfile")?; + let Some(filename_str) = filename.as_lua_string() else { + return Err(vm.error("bad argument #1 to 'loadfile' (string expected)".to_string())); + }; - let code = if let Some(fname) = filename { - // Load from specified file - match std::fs::read_to_string(&fname) { - Ok(c) => c, - Err(e) => { - let err_msg = vm.create_string(&format!("cannot open {}: {}", fname, e)); - return Ok(MultiValue::multiple(vec![LuaValue::nil(), err_msg])); - } + // Load from specified file + let code = match std::fs::read_to_string(&filename_str.as_str()) { + Ok(c) => c, + Err(e) => { + let err_msg = + vm.create_string(&format!("cannot open {}: {}", filename_str.as_str(), e)); + return Ok(MultiValue::multiple(vec![LuaValue::nil(), err_msg])); } - } else { - // Load from stdin (simplified: return nil for now) - let err_msg = vm.create_string("stdin loading not implemented"); - return Ok(MultiValue::multiple(vec![LuaValue::nil(), err_msg])); }; // Compile the code using VM's string pool @@ -857,24 +760,19 @@ fn lua_loadfile(vm: &mut LuaVM) -> LuaResult { /// dofile([filename]) - Execute a file fn lua_dofile(vm: &mut LuaVM) -> LuaResult { let filename = - get_arg(vm, 0).and_then(|v| unsafe { v.as_string().map(|s| s.as_str().to_string()) }); + get_arg(vm, 1).and_then(|v| unsafe { v.as_string().map(|s| s.as_str().to_string()) }); let code = if let Some(fname) = filename { // Load from specified file match std::fs::read_to_string(&fname) { Ok(c) => c, Err(e) => { - return Err(LuaError::RuntimeError(format!( - "cannot open {}: {}", - fname, e - ))); + return Err(vm.error(format!("cannot open {}: {}", fname, e))); } } } else { // Load from stdin (simplified: return error for now) - return Err(LuaError::RuntimeError( - "stdin loading not implemented".to_string(), - )); + return Err(vm.error("stdin loading not implemented".to_string())); }; // Compile and execute using VM's string pool @@ -895,10 +793,10 @@ fn lua_dofile(vm: &mut LuaVM) -> LuaResult { .map(|s| s.as_str().to_string()) .unwrap_or_else(|| "unknown error".to_string()) }; - Err(LuaError::RuntimeError(error_msg)) + Err(vm.error(error_msg)) } } - Err(e) => Err(LuaError::RuntimeError(format!("load error: {}", e))), + Err(e) => Err(vm.error(format!("load error: {}", e))), } } diff --git a/crates/luars/src/stdlib/coroutine.rs b/crates/luars/src/stdlib/coroutine.rs index c2f770c0..412f5cc9 100644 --- a/crates/luars/src/stdlib/coroutine.rs +++ b/crates/luars/src/stdlib/coroutine.rs @@ -3,7 +3,7 @@ use crate::lib_registry::{LibraryModule, arg_count, get_arg, get_args, require_arg}; use crate::lua_value::{CoroutineStatus, LuaValue, MultiValue}; -use crate::lua_vm::{LuaError, LuaResult, LuaVM}; +use crate::lua_vm::{LuaResult, LuaVM}; use std::rc::Rc; pub fn create_coroutine_lib() -> LibraryModule { @@ -21,12 +21,10 @@ pub fn create_coroutine_lib() -> LibraryModule { /// coroutine.create(f) - Create a new coroutine fn coroutine_create(vm: &mut LuaVM) -> LuaResult { - let func = require_arg(vm, 0, "coroutine.create")?; + let func = require_arg(vm, 1, "coroutine.create")?; if !func.is_function() && !func.is_cfunction() { - return Err(LuaError::RuntimeError( - "coroutine.create requires a function argument".to_string(), - )); + return Err(vm.error("coroutine.create requires a function argument".to_string())); } let thread_rc = vm.create_thread(func); @@ -37,12 +35,10 @@ fn coroutine_create(vm: &mut LuaVM) -> LuaResult { /// coroutine.resume(co, ...) - Resume a coroutine fn coroutine_resume(vm: &mut LuaVM) -> LuaResult { - let thread_val = require_arg(vm, 0, "coroutine.resume")?; + let thread_val = require_arg(vm, 1, "coroutine.resume")?; if !thread_val.is_thread() { - return Err(LuaError::RuntimeError( - "coroutine.resume requires a thread argument".to_string(), - )); + return Err(vm.error("coroutine.resume requires a thread argument".to_string())); } // Get arguments @@ -65,13 +61,11 @@ fn coroutine_resume(vm: &mut LuaVM) -> LuaResult { /// coroutine.yield(...) - Yield from current coroutine fn coroutine_yield(vm: &mut LuaVM) -> LuaResult { - let args = crate::lib_registry::get_args(vm); + let args = get_args(vm); // Check if we're in a coroutine if vm.current_thread.is_none() { - return Err(LuaError::RuntimeError( - "attempt to yield from outside a coroutine".to_string(), - )); + return Err(vm.error("attempt to yield from outside a coroutine".to_string())); } // Yield with values - this will store the values and mark as suspended @@ -85,19 +79,17 @@ fn coroutine_yield(vm: &mut LuaVM) -> LuaResult { /// coroutine.status(co) - Get coroutine status fn coroutine_status(vm: &mut LuaVM) -> LuaResult { - let thread_val = require_arg(vm, 0, "coroutine.status")?; + let thread_val = require_arg(vm, 1, "coroutine.status")?; if !thread_val.is_thread() { - return Err(LuaError::RuntimeError( - "coroutine.status requires a thread argument".to_string(), - )); + return Err(vm.error("coroutine.status requires a thread argument".to_string())); } // Get thread from value let status_str = unsafe { let ptr = thread_val .as_thread_ptr() - .ok_or(LuaError::RuntimeError("invalid thread".to_string()))?; + .ok_or(vm.error("invalid thread".to_string()))?; if ptr.is_null() { "dead" } else { @@ -145,12 +137,10 @@ fn coroutine_running(vm: &mut LuaVM) -> LuaResult { /// coroutine.wrap(f) - Create a wrapped coroutine /// This is placeholder - the actual implementation is injected as Lua code in lib_registry fn coroutine_wrap(vm: &mut LuaVM) -> LuaResult { - let func = require_arg(vm, 0, "coroutine.wrap")?; + let func = require_arg(vm, 1, "coroutine.wrap")?; if !func.is_function() && !func.is_cfunction() { - return Err(LuaError::RuntimeError( - "coroutine.wrap requires a function argument".to_string(), - )); + return Err(vm.error("coroutine.wrap requires a function argument".to_string())); } // Create the coroutine (same as coroutine.create) @@ -175,7 +165,7 @@ fn coroutine_wrap(vm: &mut LuaVM) -> LuaResult { // Set metatable on wrapper table let table_ref = wrapper_table .as_lua_table() - .ok_or(LuaError::RuntimeError("Invalid table".to_string()))?; + .ok_or(vm.error("Invalid table".to_string()))?; table_ref.borrow_mut().set_metatable(Some(metatable)); Ok(MultiValue::single(wrapper_table)) @@ -184,13 +174,13 @@ fn coroutine_wrap(vm: &mut LuaVM) -> LuaResult { /// Helper function for coroutine.wrap - called when the wrapper is invoked fn coroutine_wrap_call(vm: &mut LuaVM) -> LuaResult { // First argument is the wrapper table itself (self) - let wrapper_table = require_arg(vm, 0, "coroutine.wrap_call")?; + let wrapper_table = require_arg(vm, 1, "coroutine.wrap_call")?; // Get the stored coroutine let thread_key = vm.create_string("__thread"); let thread_val = vm .table_get_with_meta(&wrapper_table, &thread_key) - .ok_or_else(|| LuaError::RuntimeError("coroutine not found in wrapper".to_string()))?; + .ok_or(vm.error("coroutine not found in wrapper".to_string()))?; // Collect arguments (skip self at index 0) let mut args = Vec::new(); @@ -208,10 +198,10 @@ fn coroutine_wrap_call(vm: &mut LuaVM) -> LuaResult { // If resume failed, propagate the error if !results.is_empty() { if let Some(err_msg) = unsafe { results[0].as_string() } { - return Err(LuaError::RuntimeError(err_msg.as_str().to_string())); + return Err(vm.error(err_msg.as_str().to_string())); } } - return Err(LuaError::RuntimeError("coroutine error".to_string())); + return Err(vm.error("coroutine error".to_string())); } // Return results as MultiValue @@ -226,23 +216,19 @@ fn coroutine_isyieldable(vm: &mut LuaVM) -> LuaResult { /// coroutine.close(co) - Close a coroutine, marking it as dead fn coroutine_close(vm: &mut LuaVM) -> LuaResult { - let thread_val = require_arg(vm, 0, "coroutine.close")?; + let thread_val = require_arg(vm, 1, "coroutine.close")?; if !thread_val.is_thread() { - return Err(LuaError::RuntimeError( - "coroutine.close requires a thread argument".to_string(), - )); + return Err(vm.error("coroutine.close requires a thread argument".to_string())); } // Get thread from value unsafe { let ptr = thread_val .as_thread_ptr() - .ok_or(LuaError::RuntimeError("invalid thread".to_string()))?; + .ok_or(vm.error("invalid thread".to_string()))?; if ptr.is_null() { - return Err(LuaError::RuntimeError( - "cannot close dead coroutine".to_string(), - )); + return Err(vm.error("cannot close dead coroutine".to_string())); } let thread_rc = Rc::from_raw(ptr); @@ -251,17 +237,13 @@ fn coroutine_close(vm: &mut LuaVM) -> LuaResult { let status = thread_rc.borrow().status; if matches!(status, CoroutineStatus::Dead) { std::mem::forget(thread_rc); - return Err(LuaError::RuntimeError( - "cannot close dead coroutine".to_string(), - )); + return Err(vm.error("cannot close dead coroutine".to_string())); } // Check if running if matches!(status, CoroutineStatus::Running) { std::mem::forget(thread_rc); - return Err(LuaError::RuntimeError( - "cannot close running coroutine".to_string(), - )); + return Err(vm.error("cannot close running coroutine".to_string())); } // Mark as dead diff --git a/crates/luars/src/stdlib/io/file.rs b/crates/luars/src/stdlib/io/file.rs index 836e0322..0595836a 100644 --- a/crates/luars/src/stdlib/io/file.rs +++ b/crates/luars/src/stdlib/io/file.rs @@ -212,7 +212,7 @@ pub fn create_file_metatable(vm: &mut LuaVM) -> LuaResult { /// file:read([format]) fn file_read(vm: &mut LuaVM) -> LuaResult { // For method calls from Lua, register 1 is self (file object) - let file_val = require_arg(vm, 0, "file.read")?; + let file_val = require_arg(vm, 1, "file.read")?; // Extract LuaFile from userdata unsafe { @@ -277,7 +277,7 @@ fn file_read(vm: &mut LuaVM) -> LuaResult { /// file:write(...) fn file_write(vm: &mut LuaVM) -> LuaResult { // For method calls from Lua, register 1 is self (file object) - let file_val = require_arg(vm, 0, "file.write")?; + let file_val = require_arg(vm, 1, "file.write")?; // Extract LuaFile from userdata unsafe { @@ -344,7 +344,7 @@ fn file_write(vm: &mut LuaVM) -> LuaResult { /// file:flush() fn file_flush(vm: &mut LuaVM) -> LuaResult { // For method calls from Lua, register 1 is self (file object) - let file_val = require_arg(vm, 0, "file.flush")?; + let file_val = require_arg(vm, 1, "file.flush")?; // Extract LuaFile from userdata if let Some(ud) = vm.get_userdata(&file_val) { @@ -364,7 +364,7 @@ fn file_flush(vm: &mut LuaVM) -> LuaResult { /// file:close() fn file_close(vm: &mut LuaVM) -> LuaResult { // For method calls from Lua, register 1 is self (file object) - let file_val = require_arg(vm, 0, "file.close")?; + let file_val = require_arg(vm, 1, "file.close")?; // Extract LuaFile from userdata if let Some(ud) = vm.get_userdata(&file_val) { @@ -384,7 +384,7 @@ fn file_close(vm: &mut LuaVM) -> LuaResult { /// file:lines([formats]) - Returns an iterator for reading lines fn file_lines(vm: &mut LuaVM) -> LuaResult { // Get file handle from self - let file_val = require_arg(vm, 0, "file.lines")?; + let file_val = require_arg(vm, 1, "file.lines")?; // For now, return a simple iterator that reads lines // Create state table with file handle @@ -404,7 +404,7 @@ fn file_lines(vm: &mut LuaVM) -> LuaResult { /// Iterator function for file:lines() fn file_lines_iterator(vm: &mut LuaVM) -> LuaResult { - let state_val = require_arg(vm, 0, "iterator requires state")?; + let state_val = require_arg(vm, 1, "iterator requires state")?; let file_key = vm.create_string("file"); let state_ref_cell = vm .get_table(&state_val) @@ -436,7 +436,7 @@ fn file_lines_iterator(vm: &mut LuaVM) -> LuaResult { /// file:seek([whence [, offset]]) - Sets and gets the file position fn file_seek(vm: &mut LuaVM) -> LuaResult { - let file_val = require_arg(vm, 0, "file.seek")?; + let file_val = require_arg(vm, 1, "file.seek")?; let whence = get_arg(vm, 2) .and_then(|v| unsafe { v.as_string().map(|s| s.as_str().to_string()) }) @@ -487,7 +487,7 @@ fn file_seek(vm: &mut LuaVM) -> LuaResult { /// file:setvbuf(mode [, size]) - Sets the buffering mode fn file_setvbuf(vm: &mut LuaVM) -> LuaResult { - let file_val = require_arg(vm, 0, "file.setvbuf")?; + let file_val = require_arg(vm, 1, "file.setvbuf")?; let _mode = get_arg(vm, 2) .and_then(|v| unsafe { v.as_string().map(|s| s.as_str().to_string()) }) diff --git a/crates/luars/src/stdlib/io/mod.rs b/crates/luars/src/stdlib/io/mod.rs index a2ed965f..c240f704 100644 --- a/crates/luars/src/stdlib/io/mod.rs +++ b/crates/luars/src/stdlib/io/mod.rs @@ -133,7 +133,7 @@ fn io_flush(_vm: &mut LuaVM) -> LuaResult { fn io_open(vm: &mut LuaVM) -> LuaResult { use crate::lib_registry::{get_arg, require_arg}; - let filename_val = require_arg(vm, 0, "io.open")?; + let filename_val = require_arg(vm, 1, "io.open")?; let filename = filename_val.as_lua_string().ok_or_else(|| { LuaError::RuntimeError("bad argument #1 to 'io.open' (string expected)".to_string()) })?; diff --git a/crates/luars/src/stdlib/math.rs b/crates/luars/src/stdlib/math.rs index 114fbc72..ca99b19b 100644 --- a/crates/luars/src/stdlib/math.rs +++ b/crates/luars/src/stdlib/math.rs @@ -5,7 +5,7 @@ use crate::lib_registry::{LibraryModule, get_arg, get_args, require_arg}; use crate::lua_value::{LuaValue, LuaValueKind, MultiValue}; -use crate::lua_vm::{LuaError, LuaResult, LuaVM}; +use crate::lua_vm::{LuaResult, LuaVM}; pub fn create_math_lib() -> LibraryModule { let mut module = crate::lib_module!("math", { @@ -43,71 +43,74 @@ pub fn create_math_lib() -> LibraryModule { module } -fn get_number(vm: &LuaVM, idx: usize, func_name: &str) -> LuaResult { - require_arg(vm, idx, func_name)?.as_number().ok_or_else(|| { - LuaError::RuntimeError(format!( +fn get_number(vm: &mut LuaVM, idx: usize, func_name: &str) -> LuaResult { + let value = require_arg(vm, idx, func_name)?; + if let Some(value) = value.as_number() { + Ok(value) + } else { + Err(vm.error(format!( "bad argument #{} to '{}' (number expected)", idx + 1, func_name - )) - }) + ))) + } } fn math_abs(vm: &mut LuaVM) -> LuaResult { - let x = get_number(vm, 0, "math.abs")?; + let x = get_number(vm, 1, "math.abs")?; Ok(MultiValue::single(LuaValue::float(x.abs()))) } fn math_acos(vm: &mut LuaVM) -> LuaResult { - let x = get_number(vm, 0, "math.acos")?; + let x = get_number(vm, 1, "math.acos")?; Ok(MultiValue::single(LuaValue::float(x.acos()))) } fn math_asin(vm: &mut LuaVM) -> LuaResult { - let x = get_number(vm, 0, "math.asin")?; + let x = get_number(vm, 1, "math.asin")?; Ok(MultiValue::single(LuaValue::float(x.asin()))) } fn math_atan(vm: &mut LuaVM) -> LuaResult { - let y = get_number(vm, 0, "math.atan")?; - let x = get_arg(vm, 1).and_then(|v| v.as_number()).unwrap_or(1.0); + let y = get_number(vm, 1, "math.atan")?; + let x = get_arg(vm, 2).and_then(|v| v.as_number()).unwrap_or(1.0); Ok(MultiValue::single(LuaValue::float(y.atan2(x)))) } fn math_ceil(vm: &mut LuaVM) -> LuaResult { - let x = get_number(vm, 0, "math.ceil")?; + let x = get_number(vm, 1, "math.ceil")?; Ok(MultiValue::single(LuaValue::float(x.ceil()))) } fn math_cos(vm: &mut LuaVM) -> LuaResult { - let x = get_number(vm, 0, "math.cos")?; + let x = get_number(vm, 1, "math.cos")?; Ok(MultiValue::single(LuaValue::float(x.cos()))) } fn math_deg(vm: &mut LuaVM) -> LuaResult { - let x = get_number(vm, 0, "math.deg")?; + let x = get_number(vm, 1, "math.deg")?; Ok(MultiValue::single(LuaValue::float(x.to_degrees()))) } fn math_exp(vm: &mut LuaVM) -> LuaResult { - let x = get_number(vm, 0, "math.exp")?; + let x = get_number(vm, 1, "math.exp")?; Ok(MultiValue::single(LuaValue::float(x.exp()))) } fn math_floor(vm: &mut LuaVM) -> LuaResult { - let x = get_number(vm, 0, "math.floor")?; + let x = get_number(vm, 1, "math.floor")?; Ok(MultiValue::single(LuaValue::integer(x.floor() as i64))) } fn math_fmod(vm: &mut LuaVM) -> LuaResult { - let x = get_number(vm, 0, "math.fmod")?; - let y = get_number(vm, 1, "math.fmod")?; + let x = get_number(vm, 1, "math.fmod")?; + let y = get_number(vm, 2, "math.fmod")?; Ok(MultiValue::single(LuaValue::float(x % y))) } fn math_log(vm: &mut LuaVM) -> LuaResult { - let x = get_number(vm, 0, "math.log")?; - let base = get_arg(vm, 1).and_then(|v| v.as_number()); + let x = get_number(vm, 1, "math.log")?; + let base = get_arg(vm, 2).and_then(|v| v.as_number()); let result = if let Some(b) = base { x.log(b) } else { x.ln() }; @@ -118,21 +121,19 @@ fn math_max(vm: &mut LuaVM) -> LuaResult { let args = get_args(vm); if args.is_empty() { - return Err(LuaError::RuntimeError( - "bad argument to 'math.max' (value expected)".to_string(), - )); + return Err(vm.error("bad argument to 'math.max' (value expected)".to_string())); } // Keep the original value and its index to preserve type (integer vs float) let mut max_idx = 0; - let mut max_val = args[0].as_number().ok_or_else(|| { - LuaError::RuntimeError("bad argument to 'math.max' (number expected)".to_string()) - })?; + let mut max_val = args[0] + .as_number() + .ok_or_else(|| vm.error("bad argument to 'math.max' (number expected)".to_string()))?; for (i, arg) in args.iter().enumerate().skip(1) { - let val = arg.as_number().ok_or_else(|| { - LuaError::RuntimeError("bad argument to 'math.max' (number expected)".to_string()) - })?; + let val = arg + .as_number() + .ok_or_else(|| vm.error("bad argument to 'math.max' (number expected)".to_string()))?; if val > max_val { max_val = val; max_idx = i; @@ -147,21 +148,19 @@ fn math_min(vm: &mut LuaVM) -> LuaResult { let args = get_args(vm); if args.is_empty() { - return Err(LuaError::RuntimeError( - "bad argument to 'math.min' (value expected)".to_string(), - )); + return Err(vm.error("bad argument to 'math.min' (value expected)".to_string())); } // Keep the original value and its index to preserve type (integer vs float) let mut min_idx = 0; - let mut min_val = args[0].as_number().ok_or_else(|| { - LuaError::RuntimeError("bad argument to 'math.min' (number expected)".to_string()) - })?; + let mut min_val = args[0] + .as_number() + .ok_or_else(|| vm.error("bad argument to 'math.min' (number expected)".to_string()))?; for (i, arg) in args.iter().enumerate().skip(1) { - let val = arg.as_number().ok_or_else(|| { - LuaError::RuntimeError("bad argument to 'math.min' (number expected)".to_string()) - })?; + let val = arg + .as_number() + .ok_or_else(|| vm.error("bad argument to 'math.min' (number expected)".to_string()))?; if val < min_val { min_val = val; min_idx = i; @@ -173,7 +172,7 @@ fn math_min(vm: &mut LuaVM) -> LuaResult { } fn math_modf(vm: &mut LuaVM) -> LuaResult { - let x = get_number(vm, 0, "math.modf")?; + let x = get_number(vm, 1, "math.modf")?; let int_part = x.trunc(); let frac_part = x - int_part; @@ -184,7 +183,7 @@ fn math_modf(vm: &mut LuaVM) -> LuaResult { } fn math_rad(vm: &mut LuaVM) -> LuaResult { - let x = get_number(vm, 0, "math.rad")?; + let x = get_number(vm, 1, "math.rad")?; Ok(MultiValue::single(LuaValue::float(x.to_radians()))) } @@ -203,13 +202,13 @@ fn math_random(vm: &mut LuaVM) -> LuaResult { match argc { 0 => Ok(MultiValue::single(LuaValue::float(random))), 1 => { - let m = get_number(vm, 0, "math.random")? as i64; + let m = get_number(vm, 1, "math.random")? as i64; let result = (random * m as f64).floor() as i64 + 1; Ok(MultiValue::single(LuaValue::integer(result))) } _ => { - let m = get_number(vm, 0, "math.random")? as i64; - let n = get_number(vm, 1, "math.random")? as i64; + let m = get_number(vm, 1, "math.random")? as i64; + let n = get_number(vm, 2, "math.random")? as i64; let range = (n - m + 1) as f64; let result = m + (random * range).floor() as i64; Ok(MultiValue::single(LuaValue::integer(result))) @@ -219,27 +218,27 @@ fn math_random(vm: &mut LuaVM) -> LuaResult { fn math_randomseed(vm: &mut LuaVM) -> LuaResult { // Seed is ignored in our simple implementation - let _x = get_number(vm, 0, "math.randomseed")?; + let _x = get_number(vm, 1, "math.randomseed")?; Ok(MultiValue::empty()) } fn math_sin(vm: &mut LuaVM) -> LuaResult { - let x = get_number(vm, 0, "math.sin")?; + let x = get_number(vm, 1, "math.sin")?; Ok(MultiValue::single(LuaValue::float(x.sin()))) } fn math_sqrt(vm: &mut LuaVM) -> LuaResult { - let x = get_number(vm, 0, "math.sqrt")?; + let x = get_number(vm, 1, "math.sqrt")?; Ok(MultiValue::single(LuaValue::float(x.sqrt()))) } fn math_tan(vm: &mut LuaVM) -> LuaResult { - let x = get_number(vm, 0, "math.tan")?; + let x = get_number(vm, 1, "math.tan")?; Ok(MultiValue::single(LuaValue::float(x.tan()))) } fn math_tointeger(vm: &mut LuaVM) -> LuaResult { - let val = require_arg(vm, 0, "math.tointeger")?; + let val = require_arg(vm, 1, "math.tointeger")?; let result = if let Some(i) = val.as_integer() { LuaValue::integer(i) @@ -272,7 +271,7 @@ fn math_tointeger(vm: &mut LuaVM) -> LuaResult { } fn math_type(vm: &mut LuaVM) -> LuaResult { - let val = require_arg(vm, 0, "math.type")?; + let val = require_arg(vm, 1, "math.type")?; let type_str = match val.kind() { LuaValueKind::Integer => "integer", @@ -285,17 +284,14 @@ fn math_type(vm: &mut LuaVM) -> LuaResult { } fn math_ult(vm: &mut LuaVM) -> LuaResult { - let m = require_arg(vm, 0, "math.ult")? - .as_integer() - .ok_or_else(|| { - LuaError::RuntimeError("bad argument #1 to 'math.ult' (integer expected)".to_string()) - })?; - - let n = require_arg(vm, 1, "math.ult")? - .as_integer() - .ok_or_else(|| { - LuaError::RuntimeError("bad argument #2 to 'math.ult' (integer expected)".to_string()) - })?; + let m_value = require_arg(vm, 1, "math.ult")?; + let Some(m) = m_value.as_integer() else { + return Err(vm.error("bad argument #1 to 'math.ult' (integer expected)".to_string())); + }; + let n_value = require_arg(vm, 2, "math.ult")?; + let Some(n) = n_value.as_integer() else { + return Err(vm.error("bad argument #2 to 'math.ult' (integer expected)".to_string())); + }; // Unsigned less than let result = (m as u64) < (n as u64); Ok(MultiValue::single(LuaValue::boolean(result))) diff --git a/crates/luars/src/stdlib/mod.rs b/crates/luars/src/stdlib/mod.rs index e1e95a5c..d7adcd64 100644 --- a/crates/luars/src/stdlib/mod.rs +++ b/crates/luars/src/stdlib/mod.rs @@ -7,7 +7,7 @@ pub mod coroutine; pub mod debug; #[cfg(feature = "loadlib")] pub mod ffi; -pub mod io; +// pub mod io; pub mod math; pub mod os; pub mod package; diff --git a/crates/luars/src/stdlib/os.rs b/crates/luars/src/stdlib/os.rs index 3cc55177..5466602d 100644 --- a/crates/luars/src/stdlib/os.rs +++ b/crates/luars/src/stdlib/os.rs @@ -5,7 +5,6 @@ use crate::lib_registry::LibraryModule; use crate::lib_registry::get_arg; use crate::lua_value::{LuaValue, MultiValue}; -use crate::lua_vm::LuaError; use crate::lua_vm::LuaResult; use crate::lua_vm::LuaVM; @@ -76,14 +75,10 @@ fn os_exit(_vm: &mut LuaVM) -> LuaResult { fn os_difftime(vm: &mut LuaVM) -> LuaResult { let t2 = get_arg(vm, 0) .and_then(|v| v.as_integer()) - .ok_or(LuaError::RuntimeError( - "difftime: argument 1 must be a number".to_string(), - ))?; + .ok_or(vm.error("difftime: argument 1 must be a number".to_string()))?; let t1 = get_arg(vm, 1) .and_then(|v| v.as_integer()) - .ok_or(LuaError::RuntimeError( - "difftime: argument 2 must be a number".to_string(), - ))?; + .ok_or(vm.error("difftime: argument 2 must be a number".to_string()))?; let diff = t2 - t1; Ok(MultiValue::single(LuaValue::integer(diff))) @@ -94,9 +89,7 @@ fn os_execute(vm: &mut LuaVM) -> LuaResult { let cmd = get_arg(vm, 0) .and_then(|v| v.as_lua_string().map(|s| s.as_str().to_string())) - .ok_or(LuaError::RuntimeError( - "execute: argument 1 must be a string".to_string(), - ))?; + .ok_or(vm.error("execute: argument 1 must be a string".to_string()))?; let output = Command::new("sh").arg("-c").arg(cmd.as_str()).output(); @@ -116,9 +109,7 @@ fn os_execute(vm: &mut LuaVM) -> LuaResult { fn os_getenv(vm: &mut LuaVM) -> LuaResult { let varname = get_arg(vm, 0) .and_then(|v| v.as_lua_string().map(|s| s.as_str().to_string())) - .ok_or(LuaError::RuntimeError( - "getenv: argument 1 must be a string".to_string(), - ))?; + .ok_or(vm.error("getenv: argument 1 must be a string".to_string()))?; match std::env::var(varname.as_str()) { Ok(value) => { @@ -132,9 +123,7 @@ fn os_getenv(vm: &mut LuaVM) -> LuaResult { fn os_remove(vm: &mut LuaVM) -> LuaResult { let filename = get_arg(vm, 0) .and_then(|v| v.as_lua_string().map(|s| s.as_str().to_string())) - .ok_or(LuaError::RuntimeError( - "remove: argument 1 must be a string".to_string(), - ))?; + .ok_or(vm.error("remove: argument 1 must be a string".to_string()))?; match std::fs::remove_file(filename.as_str()) { Ok(_) => Ok(MultiValue::single(LuaValue::boolean(true))), @@ -148,14 +137,10 @@ fn os_remove(vm: &mut LuaVM) -> LuaResult { fn os_rename(vm: &mut LuaVM) -> LuaResult { let oldname = get_arg(vm, 0) .and_then(|v| v.as_lua_string().map(|s| s.as_str().to_string())) - .ok_or(LuaError::RuntimeError( - "rename: argument 1 must be a string".to_string(), - ))?; + .ok_or(vm.error("rename: argument 1 must be a string".to_string()))?; let newname = get_arg(vm, 1) .and_then(|v| v.as_lua_string().map(|s| s.as_str().to_string())) - .ok_or(LuaError::RuntimeError( - "rename: argument 2 must be a string".to_string(), - ))?; + .ok_or(vm.error("rename: argument 2 must be a string".to_string()))?; match std::fs::rename(oldname.as_str(), newname.as_str()) { Ok(_) => Ok(MultiValue::single(LuaValue::boolean(true))), diff --git a/crates/luars/src/stdlib/package.rs b/crates/luars/src/stdlib/package.rs index 87c75a5b..b7a38702 100644 --- a/crates/luars/src/stdlib/package.rs +++ b/crates/luars/src/stdlib/package.rs @@ -1,9 +1,11 @@ // Package library // Implements: config, cpath, loaded, loadlib, path, preload, searchers, searchpath -use crate::lib_registry::{LibraryModule, get_arg, require_arg}; +use std::rc::Rc; + +use crate::lib_registry::{LibraryModule, require_arg}; use crate::lua_value::{LuaValue, MultiValue}; -use crate::lua_vm::{LuaError, LuaResult, LuaVM}; +use crate::lua_vm::{LuaResult, LuaVM}; pub fn create_package_lib() -> LibraryModule { let mut module = LibraryModule::new("package"); @@ -78,44 +80,34 @@ fn create_searchers_table(vm: &mut LuaVM) -> LuaValue { // Searcher 1: Check package.preload fn searcher_preload(vm: &mut LuaVM) -> LuaResult { - let modname_val = require_arg(vm, 0, "preload searcher")?; - let modname = unsafe { - modname_val - .as_string() - .ok_or_else(|| LuaError::RuntimeError("module name expected".to_string()))? - .as_str() - .to_string() + let modname_val = require_arg(vm, 1, "preload searcher")?; + let Some(modname) = modname_val.as_lua_string() else { + return Err(vm.error("module name expected".to_string())); }; - let package_table = vm - .get_global("package") - .ok_or_else(|| LuaError::RuntimeError("package table not found".to_string()))?; + let Some(package_table) = vm.get_global("package") else { + return Err(vm.error("package table not found".to_string())); + }; + let Some(package_ref) = package_table.as_lua_table() else { + return Err(vm.error("Invalid package table".to_string())); + }; let preload_key = vm.create_string("preload"); - let package_ref_cell = vm - .get_table(&package_table) - .ok_or_else(|| LuaError::RuntimeError("Invalid package table".to_string()))?; - let preload_val = package_ref_cell + let preload_val = package_ref .borrow() .raw_get(&preload_key) .unwrap_or(LuaValue::nil()); - if preload_val.is_nil() || preload_val.as_table_id().is_none() { - let err = format!("\n\tno field package.preload['{}']", modname); - return Ok(MultiValue::single(vm.create_string(&err))); - } - - let modname_key = vm.create_string(&modname); - let preload_ref_cell = vm - .get_table(&preload_val) - .ok_or_else(|| LuaError::RuntimeError("Invalid preload table".to_string()))?; - let loader = preload_ref_cell + let Some(preload_ref) = preload_val.as_lua_table() else { + return Err(vm.error("package.preload is not a table".to_string())); + }; + let loader = preload_ref .borrow() - .raw_get(&modname_key) + .raw_get(&modname_val) .unwrap_or(LuaValue::nil()); if loader.is_nil() { - let err = format!("\n\tno field package.preload['{}']", modname); + let err = format!("\n\tno field package.preload['{}']", modname.as_str()); Ok(MultiValue::single(vm.create_string(&err))) } else { Ok(MultiValue::single(loader)) @@ -124,38 +116,30 @@ fn searcher_preload(vm: &mut LuaVM) -> LuaResult { // Searcher 2: Search package.path fn searcher_lua(vm: &mut LuaVM) -> LuaResult { - let modname_val = require_arg(vm, 0, "Lua searcher")?; - let modname = unsafe { - modname_val - .as_string() - .ok_or_else(|| LuaError::RuntimeError("module name expected".to_string()))? - .as_str() - .to_string() + let modname_val = require_arg(vm, 1, "Lua searcher")?; + let Some(modname) = modname_val.as_lua_string() else { + return Err(vm.error("module name expected".to_string())); + }; + + let Some(package_table) = vm.get_global("package") else { + return Err(vm.error("package table not found".to_string())); + }; + + let Some(package_ref) = package_table.as_lua_table() else { + return Err(vm.error("Invalid package table".to_string())); }; - let path = unsafe { - let package_table = vm - .get_global("package") - .ok_or_else(|| LuaError::RuntimeError("package table not found".to_string()))?; - - let path_key = vm.create_string("path"); - let package_ref_cell = vm - .get_table(&package_table) - .ok_or_else(|| LuaError::RuntimeError("Invalid package table".to_string()))?; - let path_val = package_ref_cell - .borrow() - .raw_get(&path_key) - .unwrap_or(LuaValue::nil()); - - path_val - .as_string() - .ok_or_else(|| LuaError::RuntimeError("package.path is not a string".to_string()))? - .as_str() - .to_string() + let path_key = vm.create_string("path"); + let Some(path_value) = package_ref.borrow().raw_get(&path_key) else { + return Err(vm.error("package.path not found".to_string())); + }; + + let Some(path) = path_value.as_lua_string() else { + return Err(vm.error("package.path is not a string".to_string())); }; // Search for the file - let result = search_path(&modname, &path, ".", "/")?; + let result = search_path(&modname.as_str(), &path.as_str(), ".", "/")?; match result { Some(filepath) => Ok(MultiValue::multiple(vec![ @@ -165,8 +149,9 @@ fn searcher_lua(vm: &mut LuaVM) -> LuaResult { None => { let err = format!( "\n\tno file '{}'", - path.split(';') - .map(|template| { template.replace('?', &modname.replace('.', "/")) }) + path.as_str() + .split(';') + .map(|template| { template.replace('?', &modname.as_str().replace('.', "/")) }) .collect::>() .join("'\n\tno file '") ); @@ -177,28 +162,23 @@ fn searcher_lua(vm: &mut LuaVM) -> LuaResult { // Loader function for Lua files (called by searcher_lua) fn lua_file_loader(vm: &mut LuaVM) -> LuaResult { - let _modname = get_arg(vm, 0); let filepath_val = require_arg(vm, 1, "Lua file loader")?; - let filepath = unsafe { - filepath_val - .as_string() - .ok_or_else(|| LuaError::RuntimeError("filepath expected".to_string()))? - .as_str() - .to_string() + let Some(filepath) = filepath_val.as_lua_string() else { + return Err(vm.error("file path must be a string".to_string())); }; - // Read the file - let source = std::fs::read_to_string(&filepath) - .map_err(|e| LuaError::RuntimeError(format!("cannot read file '{}': {}", filepath, e)))?; + let source = match std::fs::read_to_string(&filepath.as_str()) { + Ok(s) => s, + Err(_) => { + return Ok(MultiValue::single(LuaValue::nil())); + } + }; // Compile it using VM's string pool - let chunk = vm.compile(&source).map_err(|e| { - LuaError::RuntimeError(format!("error loading module '{}': {}", filepath, e)) - })?; - + let chunk = vm.compile(&source)?; // Create a function from the chunk - let func = vm.create_function(std::rc::Rc::new(chunk), vec![]); + let func = vm.create_function(Rc::new(chunk), vec![]); // Call the function let (success, results) = vm.protected_call(func, vec![])?; @@ -211,9 +191,10 @@ fn lua_file_loader(vm: &mut LuaVM) -> LuaResult { .map(|s| s.as_str().to_string()) .unwrap_or_else(|| "unknown error".to_string()) }; - return Err(LuaError::RuntimeError(format!( + return Err(vm.error(format!( "error loading module '{}': {}", - filepath, error_msg + filepath.as_str(), + error_msg ))); } @@ -228,68 +209,13 @@ fn lua_file_loader(vm: &mut LuaVM) -> LuaResult { } // Searcher 3: Search package.cpath (C libraries) -fn searcher_c(vm: &mut LuaVM) -> LuaResult { - let modname_val = require_arg(vm, 0, "C searcher")?; - let modname = unsafe { - modname_val - .as_string() - .ok_or_else(|| LuaError::RuntimeError("module name expected".to_string()))? - .as_str() - .to_string() - }; - - let cpath = unsafe { - let package_table = vm - .get_global("package") - .ok_or_else(|| LuaError::RuntimeError("package table not found".to_string()))?; - - let cpath_key = vm.create_string("cpath"); - let package_ref_cell = vm - .get_table(&package_table) - .ok_or_else(|| LuaError::RuntimeError("Invalid package table".to_string()))?; - let cpath_val = package_ref_cell - .borrow() - .raw_get(&cpath_key) - .unwrap_or(LuaValue::nil()); - - cpath_val - .as_string() - .ok_or_else(|| LuaError::RuntimeError("package.cpath is not a string".to_string()))? - .as_str() - .to_string() - }; - - // For now, just return error message (C loader not implemented) - let err = format!( - "\n\tC loader not implemented\n\tno file '{}'", - cpath - .split(';') - .map(|template| { template.replace('?', &modname.replace('.', "/")) }) - .collect::>() - .join("'\n\tno file '") - ); - Ok(MultiValue::single(vm.create_string(&err))) +fn searcher_c(_vm: &mut LuaVM) -> LuaResult { + return Ok(MultiValue::single(LuaValue::nil())); } // Searcher 4: all-in-one loader (stub) -fn searcher_allinone(vm: &mut LuaVM) -> LuaResult { - let modname_val = require_arg(vm, 0, "all-in-one searcher")?; - let modname = unsafe { - modname_val - .as_string() - .ok_or_else(|| LuaError::RuntimeError("module name expected".to_string()))? - .as_str() - .to_string() - }; - - // Only try if this is a submodule (contains '.') - if !modname.contains('.') { - let err = format!("\n\tno module '{}' in all-in-one loader", modname); - return Ok(MultiValue::single(vm.create_string(&err))); - } - - let err = format!("\n\tall-in-one loader not fully implemented"); - Ok(MultiValue::single(vm.create_string(&err))) +fn searcher_allinone(_vm: &mut LuaVM) -> LuaResult { + return Ok(MultiValue::single(LuaValue::nil())); } // Helper: Search for a file in path templates @@ -315,66 +241,27 @@ fn package_loadlib(vm: &mut LuaVM) -> LuaResult { } fn package_searchpath(vm: &mut LuaVM) -> LuaResult { - let name_val = require_arg(vm, 0, "searchpath")?; - let path_val = require_arg(vm, 1, "searchpath")?; - let sep_val = get_arg(vm, 2).unwrap_or(vm.create_string(".")); - let rep_val = get_arg(vm, 3).unwrap_or(vm.create_string("/")); - - let name = unsafe { - name_val - .as_string() - .ok_or_else(|| { - LuaError::RuntimeError( - "bad argument #1 to 'searchpath' (string expected)".to_string(), - ) - })? - .as_str() - .to_string() - }; - - let path = unsafe { - path_val - .as_string() - .ok_or_else(|| { - LuaError::RuntimeError( - "bad argument #2 to 'searchpath' (string expected)".to_string(), - ) - })? - .as_str() - .to_string() - }; + let name_val = require_arg(vm, 1, "searchpath")?; + let path_val = require_arg(vm, 2, "searchpath")?; + // let sep_val = get_arg(vm, 2).unwrap_or(vm.create_string(".")); + // let rep_val = get_arg(vm, 3).unwrap_or(vm.create_string("/")); - let sep = unsafe { - sep_val - .as_string() - .ok_or_else(|| { - LuaError::RuntimeError( - "bad argument #3 to 'searchpath' (string expected)".to_string(), - ) - })? - .as_str() - .to_string() + let Some(name) = name_val.as_lua_string() else { + return Err(vm.error("bad argument #1 to 'searchpath' (string expected)".to_string())); }; - let rep = unsafe { - rep_val - .as_string() - .ok_or_else(|| { - LuaError::RuntimeError( - "bad argument #4 to 'searchpath' (string expected)".to_string(), - ) - })? - .as_str() - .to_string() + let Some(path) = path_val.as_lua_string() else { + return Err(vm.error("bad argument #1 to 'searchpath' (string expected)".to_string())); }; - match search_path(&name, &path, &sep, &rep)? { + match search_path(name.as_str(), path.as_str(), ".", "/")? { Some(filepath) => Ok(MultiValue::single(vm.create_string(&filepath))), None => { - let searchname = name.replace(&sep, &rep); + let searchname = name.as_str().replace(".", "/"); let err = format!( "\n\tno file '{}'", - path.split(';') + path.as_str() + .split(';') .map(|template| { template.replace('?', &searchname) }) .collect::>() .join("'\n\tno file '") diff --git a/crates/luars/src/stdlib/string.rs b/crates/luars/src/stdlib/string.rs index 82d162e4..e11a90f4 100644 --- a/crates/luars/src/stdlib/string.rs +++ b/crates/luars/src/stdlib/string.rs @@ -5,7 +5,7 @@ use crate::lib_registry::{LibraryModule, get_arg, get_args, require_arg}; use crate::lua_pattern; use crate::lua_value::{LuaValue, MultiValue}; -use crate::lua_vm::{LuaError, LuaResult, LuaVM}; +use crate::lua_vm::{LuaResult, LuaVM}; pub fn create_string_lib() -> LibraryModule { crate::lib_module!("string", { @@ -30,17 +30,17 @@ pub fn create_string_lib() -> LibraryModule { /// string.byte(s [, i [, j]]) - Return byte values fn string_byte(vm: &mut LuaVM) -> LuaResult { - let s_value = require_arg(vm, 0, "string.byte")?; - let s = s_value.as_lua_string().ok_or_else(|| { - LuaError::RuntimeError("bad argument #1 to 'string.byte' (string expected)".to_string()) - })?; + let s_value = require_arg(vm, 1, "string.byte")?; + let Some(s) = s_value.as_lua_string() else { + return Err(vm.error("bad argument #1 to 'string.byte' (string expected)".to_string())); + }; let str_bytes = s.as_str().as_bytes(); let len = str_bytes.len() as i64; - let i = get_arg(vm, 1).and_then(|v| v.as_integer()).unwrap_or(1); + let i = get_arg(vm, 2).and_then(|v| v.as_integer()).unwrap_or(1); - let j = get_arg(vm, 2).and_then(|v| v.as_integer()).unwrap_or(i); + let j = get_arg(vm, 3).and_then(|v| v.as_integer()).unwrap_or(i); // Convert negative indices let start = if i < 0 { len + i + 1 } else { i }; @@ -67,15 +67,14 @@ fn string_char(vm: &mut LuaVM) -> LuaResult { let mut bytes = Vec::new(); for (i, arg) in args.iter().enumerate() { - let byte = arg.as_integer().ok_or_else(|| { - LuaError::RuntimeError(format!( + let Some(byte) = arg.as_integer() else { + return Err(vm.error(format!( "bad argument #{} to 'string.char' (number expected)", i + 1 - )) - })?; - + ))); + }; if byte < 0 || byte > 255 { - return Err(LuaError::RuntimeError(format!( + return Err(vm.error(format!( "bad argument #{} to 'string.char' (value out of range)", i + 1 ))); @@ -84,8 +83,12 @@ fn string_char(vm: &mut LuaVM) -> LuaResult { bytes.push(byte as u8); } - let result_str = String::from_utf8(bytes) - .map_err(|_| LuaError::RuntimeError("string.char: invalid UTF-8".to_string()))?; + let result_str = match String::from_utf8(bytes) { + Ok(s) => s, + Err(_) => { + return Err(vm.error("invalid byte sequence in 'string.char'".to_string())); + } + }; let result = vm.create_string(&result_str); Ok(MultiValue::single(result)) } @@ -93,11 +96,11 @@ fn string_char(vm: &mut LuaVM) -> LuaResult { /// string.len(s) - Return string length /// OPTIMIZED: Use byte length directly, Lua string.len returns byte length not char count fn string_len(vm: &mut LuaVM) -> LuaResult { - let s_value = require_arg(vm, 0, "string.len")?; + let s_value = require_arg(vm, 1, "string.len")?; - let s = s_value.as_lua_string().ok_or_else(|| { - LuaError::RuntimeError("bad argument #1 to 'string.len' (string expected)".to_string()) - })?; + let Some(s) = s_value.as_lua_string() else { + return Err(vm.error("bad argument #1 to 'string.len' (string expected)".to_string())); + }; // Lua string.len returns byte length, not UTF-8 character count // This is correct and much faster than chars().count() @@ -107,10 +110,10 @@ fn string_len(vm: &mut LuaVM) -> LuaResult { /// string.lower(s) - Convert to lowercase fn string_lower(vm: &mut LuaVM) -> LuaResult { - let s_value = require_arg(vm, 0, "string.lower")?; - let s = s_value.as_lua_string().ok_or_else(|| { - LuaError::RuntimeError("bad argument #1 to 'string.lower' (string expected)".to_string()) - })?; + let s_value = require_arg(vm, 1, "string.lower")?; + let Some(s) = s_value.as_lua_string() else { + return Err(vm.error("bad argument #1 to 'string.lower' (string expected)".to_string())); + }; let result = vm.create_string(&s.as_str().to_lowercase()); Ok(MultiValue::single(result)) @@ -118,10 +121,10 @@ fn string_lower(vm: &mut LuaVM) -> LuaResult { /// string.upper(s) - Convert to uppercase fn string_upper(vm: &mut LuaVM) -> LuaResult { - let s_value = require_arg(vm, 0, "string.upper")?; - let s = s_value.as_lua_string().ok_or_else(|| { - LuaError::RuntimeError("bad argument #1 to 'string.upper' (string expected)".to_string()) - })?; + let s_value = require_arg(vm, 1, "string.upper")?; + let Some(s) = s_value.as_lua_string() else { + return Err(vm.error("bad argument #1 to 'string.upper' (string expected)".to_string())); + }; let result = vm.create_string(&s.as_str().to_uppercase()); Ok(MultiValue::single(result)) @@ -129,28 +132,14 @@ fn string_upper(vm: &mut LuaVM) -> LuaResult { /// string.rep(s, n [, sep]) - Repeat string fn string_rep(vm: &mut LuaVM) -> LuaResult { - let s_value = require_arg(vm, 0, "string.rep")?; - let s = s_value.as_lua_string().ok_or_else(|| { - LuaError::RuntimeError("bad argument #1 to 'string.rep' (string expected)".to_string()) - })?; + let s_value = require_arg(vm, 1, "string.rep")?; + let Some(s) = s_value.as_lua_string() else { + return Err(vm.error("bad argument #1 to 'string.rep' (string expected)".to_string())); + }; - let n = require_arg(vm, 1, "string.rep")? - .as_integer() - .ok_or_else(|| { - LuaError::RuntimeError("bad argument #2 to 'string.rep' (number expected)".to_string()) - })?; - let sep_value = get_arg(vm, 2); - let sep = match sep_value { - Some(v) => vm - .get_string(&v) - .ok_or_else(|| { - LuaError::RuntimeError( - "bad argument #3 to 'string.rep' (string expected)".to_string(), - ) - })? - .as_str() - .to_string(), - None => "".to_string(), + let n_value = require_arg(vm, 2, "string.rep")?; + let Some(n) = n_value.as_integer() else { + return Err(vm.error("bad argument #2 to 'string.rep' (number expected)".to_string())); }; if n <= 0 { @@ -158,13 +147,32 @@ fn string_rep(vm: &mut LuaVM) -> LuaResult { return Ok(MultiValue::single(empty)); } + let sep_value = get_arg(vm, 3); + let mut result = String::new(); - for i in 0..n { - if i > 0 && !sep.is_empty() { - result.push_str(&sep.as_str()); + match sep_value { + Some(v) => { + let sep_str = if let Some(sep_str) = v.as_lua_string() { + sep_str.as_str() + } else { + return Err( + vm.error("bad argument #3 to 'string.rep' (string expected)".to_string()) + ); + }; + + for i in 0..n { + if i > 0 && !sep_str.is_empty() { + result.push_str(sep_str); + } + result.push_str(s.as_str()); + } } - result.push_str(s.as_str()); - } + None => { + for _ in 0..n { + result.push_str(s.as_str()); + } + } + }; let result = vm.create_string(&result); Ok(MultiValue::single(result)) @@ -172,9 +180,9 @@ fn string_rep(vm: &mut LuaVM) -> LuaResult { /// string.reverse(s) - Reverse string fn string_reverse(vm: &mut LuaVM) -> LuaResult { - let s_value = require_arg(vm, 0, "string.reverse")?; + let s_value = require_arg(vm, 1, "string.reverse")?; let s = s_value.as_lua_string().ok_or_else(|| { - LuaError::RuntimeError("bad argument #1 to 'string.reverse' (string expected)".to_string()) + vm.error("bad argument #1 to 'string.reverse' (string expected)".to_string()) })?; let reversed: String = s.as_str().chars().rev().collect(); @@ -185,22 +193,20 @@ fn string_reverse(vm: &mut LuaVM) -> LuaResult { /// string.sub(s, i [, j]) - Extract substring /// OPTIMIZED: Lua uses byte indices, not character indices! fn string_sub(vm: &mut LuaVM) -> LuaResult { - let s_value = require_arg(vm, 0, "string.sub")?; - let s = s_value.as_lua_string().ok_or_else(|| { - LuaError::RuntimeError("bad argument #1 to 'string.sub' (string expected)".to_string()) - })?; + let s_value = require_arg(vm, 1, "string.sub")?; + let Some(s) = s_value.as_lua_string() else { + return Err(vm.error("bad argument #1 to 'string.rep' (string expected)".to_string())); + }; let s_str = s.as_str(); let byte_len = s_str.len() as i64; - let i = require_arg(vm, 1, "string.sub")? - .as_integer() - .ok_or_else(|| { - LuaError::RuntimeError("bad argument #2 to 'string.sub' (number expected)".to_string()) - })?; - - let j = get_arg(vm, 2).and_then(|v| v.as_integer()).unwrap_or(-1); + let i_value = require_arg(vm, 2, "string.sub")?; + let Some(i) = i_value.as_integer() else { + return Err(vm.error("bad argument #2 to 'string.sub' (number expected)".to_string())); + }; + let j = get_arg(vm, 3).and_then(|v| v.as_integer()).unwrap_or(-1); // Lua string.sub uses byte positions, not character positions! // Convert negative indices let start = if i < 0 { byte_len + i + 1 } else { i }; @@ -225,15 +231,15 @@ fn string_sub(vm: &mut LuaVM) -> LuaResult { /// string.format(formatstring, ...) - Format string (simplified) fn string_format(vm: &mut LuaVM) -> LuaResult { - let format_str_value = require_arg(vm, 0, "string.format")?; - let format_str = format_str_value.as_lua_string().ok_or_else(|| { - LuaError::RuntimeError("bad argument #1 to 'string.format' (string expected)".to_string()) - })?; + let format_str_value = require_arg(vm, 1, "string.format")?; + let Some(format_str) = format_str_value.as_lua_string() else { + return Err(vm.error("bad argument #1 to 'string.format' (string expected)".to_string())); + }; // Copy the format string to avoid holding a borrow on vm throughout the loop - let format = format_str.as_str().to_string(); + let format = format_str.as_str(); let mut result = String::new(); - let mut arg_index = 1; + let mut arg_index = 2; // Start from 2 since arg 1 is the format string let mut chars = format.chars().peekable(); while let Some(ch) = chars.next() { @@ -262,9 +268,7 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { } if !has_format_char { - return Err(LuaError::RuntimeError( - "incomplete format string".to_string(), - )); + return Err(vm.error("incomplete format string".to_string())); } let format_char = chars.next().unwrap(); @@ -276,13 +280,13 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { 'c' => { // Character let val = get_arg(vm, arg_index).ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (no value)", arg_index + 1 )) })?; let num = val.as_integer().ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (number expected)", arg_index + 1 )) @@ -290,7 +294,7 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { if num >= 0 && num <= 255 { result.push(num as u8 as char); } else { - return Err(LuaError::RuntimeError(format!( + return Err(vm.error(format!( "bad argument #{} to 'format' (invalid value for '%%c')", arg_index + 1 ))); @@ -300,7 +304,7 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { 'd' | 'i' => { // Integer let val = get_arg(vm, arg_index).ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (no value)", arg_index + 1 )) @@ -309,7 +313,7 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { .as_integer() .or_else(|| val.as_number().map(|n| n as i64)) .ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (number expected)", arg_index + 1 )) @@ -320,7 +324,7 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { 'o' => { // Octal let val = get_arg(vm, arg_index).ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (no value)", arg_index + 1 )) @@ -329,7 +333,7 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { .as_integer() .or_else(|| val.as_number().map(|n| n as i64)) .ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (number expected)", arg_index + 1 )) @@ -340,7 +344,7 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { 'u' => { // Unsigned integer let val = get_arg(vm, arg_index).ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (no value)", arg_index + 1 )) @@ -349,7 +353,7 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { .as_integer() .or_else(|| val.as_number().map(|n| n as i64)) .ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (number expected)", arg_index + 1 )) @@ -360,7 +364,7 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { 'x' => { // Lowercase hexadecimal let val = get_arg(vm, arg_index).ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (no value)", arg_index + 1 )) @@ -369,7 +373,7 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { .as_integer() .or_else(|| val.as_number().map(|n| n as i64)) .ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (number expected)", arg_index + 1 )) @@ -380,7 +384,7 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { 'X' => { // Uppercase hexadecimal let val = get_arg(vm, arg_index).ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (no value)", arg_index + 1 )) @@ -389,7 +393,7 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { .as_integer() .or_else(|| val.as_number().map(|n| n as i64)) .ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (number expected)", arg_index + 1 )) @@ -400,7 +404,7 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { 'e' => { // Scientific notation (lowercase) let val = get_arg(vm, arg_index).ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (no value)", arg_index + 1 )) @@ -409,7 +413,7 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { .as_number() .or_else(|| val.as_integer().map(|i| i as f64)) .ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (number expected)", arg_index + 1 )) @@ -420,7 +424,7 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { 'E' => { // Scientific notation (uppercase) let val = get_arg(vm, arg_index).ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (no value)", arg_index + 1 )) @@ -429,7 +433,7 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { .as_number() .or_else(|| val.as_integer().map(|i| i as f64)) .ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (number expected)", arg_index + 1 )) @@ -440,7 +444,7 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { 'f' => { // Floating point let val = get_arg(vm, arg_index).ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (no value)", arg_index + 1 )) @@ -449,7 +453,7 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { .as_number() .or_else(|| val.as_integer().map(|i| i as f64)) .ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (number expected)", arg_index + 1 )) @@ -475,7 +479,7 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { 'g' => { // Automatic format (lowercase) - use shorter of %e or %f let val = get_arg(vm, arg_index).ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (no value)", arg_index + 1 )) @@ -484,7 +488,7 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { .as_number() .or_else(|| val.as_integer().map(|i| i as f64)) .ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (number expected)", arg_index + 1 )) @@ -501,7 +505,7 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { 'G' => { // Automatic format (uppercase) - use shorter of %E or %f let val = get_arg(vm, arg_index).ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (no value)", arg_index + 1 )) @@ -510,7 +514,7 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { .as_number() .or_else(|| val.as_integer().map(|i| i as f64)) .ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (number expected)", arg_index + 1 )) @@ -527,7 +531,7 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { 's' => { // String let val = get_arg(vm, arg_index).ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (no value)", arg_index + 1 )) @@ -567,13 +571,13 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { 'q' => { // Quoted string let val = get_arg(vm, arg_index).ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (no value)", arg_index + 1 )) })?; let s = val.as_lua_string().ok_or_else(|| { - LuaError::RuntimeError(format!( + vm.error(format!( "bad argument #{} to 'format' (string expected)", arg_index + 1 )) @@ -595,16 +599,13 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { arg_index += 1; } _ => { - return Err(LuaError::RuntimeError(format!( - "invalid option '%{}' to 'format'", - format_char - ))); + return Err( + vm.error(format!("invalid option '%{}' to 'format'", format_char)) + ); } } } else { - return Err(LuaError::RuntimeError( - "incomplete format string".to_string(), - )); + return Err(vm.error("incomplete format string".to_string())); } } else { result.push(ch); @@ -617,17 +618,17 @@ fn string_format(vm: &mut LuaVM) -> LuaResult { /// string.find(s, pattern [, init [, plain]]) - Find pattern fn string_find(vm: &mut LuaVM) -> LuaResult { - let s_value = require_arg(vm, 0, "string.find")?; - let s = s_value.as_lua_string().ok_or_else(|| { - LuaError::RuntimeError("bad argument #1 to 'string.find' (string expected)".to_string()) - })?; - let pattern_str_value = require_arg(vm, 1, "string.find")?; - let pattern_str = pattern_str_value.as_lua_string().ok_or_else(|| { - LuaError::RuntimeError("bad argument #2 to 'string.find' (string expected)".to_string()) - })?; + let s_value = require_arg(vm, 1, "string.find")?; + let Some(s) = s_value.as_lua_string() else { + return Err(vm.error("bad argument #1 to 'string.find' (string expected)".to_string())); + }; + let pattern_str_value = require_arg(vm, 2, "string.find")?; + let Some(pattern_str) = pattern_str_value.as_lua_string() else { + return Err(vm.error("bad argument #2 to 'string.find' (string expected)".to_string())); + }; - let init = get_arg(vm, 2).and_then(|v| v.as_integer()).unwrap_or(1); - let plain = get_arg(vm, 3).map(|v| v.is_truthy()).unwrap_or(false); + let init = get_arg(vm, 3).and_then(|v| v.as_integer()).unwrap_or(1); + let plain = get_arg(vm, 4).map(|v| v.is_truthy()).unwrap_or(false); let start_pos = if init > 0 { (init - 1) as usize } else { 0 }; // Cache string references to avoid repeated RC derefs @@ -661,7 +662,7 @@ fn string_find(vm: &mut LuaVM) -> LuaResult { } } else { // Pattern matching - parse and check if it's a simple literal - match crate::lua_pattern::parse_pattern(pattern) { + match lua_pattern::parse_pattern(pattern) { Ok(parsed_pattern) => { // Fast path: if pattern is just a literal string, use plain search if let Some(literal) = parsed_pattern.as_literal_string() { @@ -682,7 +683,7 @@ fn string_find(vm: &mut LuaVM) -> LuaResult { } else { // Complex pattern - use full pattern matcher if let Some((start, end, captures)) = - crate::lua_pattern::find(s_str, &parsed_pattern, start_pos) + lua_pattern::find(s_str, &parsed_pattern, start_pos) { let mut results = vec![ LuaValue::integer((start + 1) as i64), @@ -698,24 +699,24 @@ fn string_find(vm: &mut LuaVM) -> LuaResult { } } } - Err(e) => Err(LuaError::RuntimeError(format!("invalid pattern: {}", e))), + Err(e) => Err(vm.error(format!("invalid pattern: {}", e))), } } } /// string.match(s, pattern [, init]) - Match pattern fn string_match(vm: &mut LuaVM) -> LuaResult { - let s_value = require_arg(vm, 0, "string.match")?; - let s = s_value.as_lua_string().ok_or_else(|| { - LuaError::RuntimeError("bad argument #1 to 'string.match' (string expected)".to_string()) - })?; + let s_value = require_arg(vm, 1, "string.match")?; + let Some(s) = s_value.as_lua_string() else { + return Err(vm.error("bad argument #1 to 'string.match' (string expected)".to_string())); + }; - let pattern_str_value = require_arg(vm, 1, "string.match")?; - let pattern_str = pattern_str_value.as_lua_string().ok_or_else(|| { - LuaError::RuntimeError("bad argument #2 to 'string.match' (string expected)".to_string()) - })?; + let pattern_str_value = require_arg(vm, 2, "string.match")?; + let Some(pattern_str) = pattern_str_value.as_lua_string() else { + return Err(vm.error("bad argument #2 to 'string.match' (string expected)".to_string())); + }; - let init = get_arg(vm, 2).and_then(|v| v.as_integer()).unwrap_or(1); + let init = get_arg(vm, 3).and_then(|v| v.as_integer()).unwrap_or(1); let start_pos = if init > 0 { (init - 1) as usize } else { 0 }; let text = s.as_str()[start_pos..].to_string(); @@ -737,35 +738,31 @@ fn string_match(vm: &mut LuaVM) -> LuaResult { Ok(MultiValue::single(LuaValue::nil())) } } - Err(e) => Err(LuaError::RuntimeError(format!("invalid pattern: {}", e))), + Err(e) => Err(vm.error(format!("invalid pattern: {}", e))), } } /// string.gsub(s, pattern, repl [, n]) - Global substitution fn string_gsub(vm: &mut LuaVM) -> LuaResult { - let arg0 = require_arg(vm, 0, "string.gsub")?; - let s = unsafe { - arg0.as_string().ok_or_else(|| { - LuaError::RuntimeError("bad argument #1 to 'string.gsub' (string expected)".to_string()) - })? + let arg0 = require_arg(vm, 1, "string.gsub")?; + let Some(s) = arg0.as_lua_string() else { + return Err(vm.error("bad argument #1 to 'string.gsub' (string expected)".to_string())); }; - let arg1 = require_arg(vm, 1, "string.gsub")?; - let pattern_str = unsafe { - arg1.as_string().ok_or_else(|| { - LuaError::RuntimeError("bad argument #2 to 'string.gsub' (string expected)".to_string()) - })? + let arg1 = require_arg(vm, 2, "string.gsub")?; + let Some(pattern_str) = arg1.as_lua_string() else { + return Err(vm.error("bad argument #2 to 'string.gsub' (string expected)".to_string())); }; - let arg2 = require_arg(vm, 2, "string.gsub")?; + let arg2 = require_arg(vm, 3, "string.gsub")?; - let max = get_arg(vm, 3) + let max = get_arg(vm, 4) .and_then(|v| v.as_integer()) .map(|n| n as usize); let pattern = match lua_pattern::parse_pattern(pattern_str.as_str()) { Ok(p) => p, - Err(e) => return Err(LuaError::RuntimeError(format!("invalid pattern: {}", e))), + Err(e) => return Err(vm.error(format!("invalid pattern: {}", e))), }; // Check replacement type: string, function, or table @@ -780,7 +777,7 @@ fn string_gsub(vm: &mut LuaVM) -> LuaResult { LuaValue::integer(count as i64), ])) } - Err(e) => Err(LuaError::RuntimeError(e)), + Err(e) => Err(vm.error(e)), } } else if arg2.is_function() || arg2.is_cfunction() { // Function replacement @@ -789,9 +786,8 @@ fn string_gsub(vm: &mut LuaVM) -> LuaResult { // Table replacement (lookup) gsub_with_table(vm, s.as_str(), &pattern, arg2, max) } else { - Err(LuaError::RuntimeError( - "bad argument #3 to 'string.gsub' (string/function/table expected)".to_string(), - )) + Err(vm + .error("bad argument #3 to 'string.gsub' (string/function/table expected)".to_string())) } } @@ -839,7 +835,7 @@ fn gsub_with_function( .and_then(|v| v.as_lua_string()) .map(|s| s.as_str().to_string()) .unwrap_or_else(|| "unknown error".to_string()); - return Err(LuaError::RuntimeError(error_msg)); + return Err(vm.error(error_msg)); } // Get the replacement from function result @@ -897,8 +893,7 @@ fn gsub_with_table( } } - if let Some((end_pos, captures)) = crate::lua_pattern::try_match(pattern, &text_chars, pos) - { + if let Some((end_pos, captures)) = lua_pattern::try_match(pattern, &text_chars, pos) { count += 1; // Get the key for table lookup @@ -945,47 +940,24 @@ fn gsub_with_table( /// string.gmatch(s, pattern) - Returns an iterator function /// Usage: for capture in string.gmatch(s, pattern) do ... end fn string_gmatch(vm: &mut LuaVM) -> LuaResult { - let arg0 = require_arg(vm, 0, "string.gmatch")?; - let s = unsafe { - arg0.as_string().ok_or_else(|| { - LuaError::RuntimeError( - "bad argument #1 to 'string.gmatch' (string expected)".to_string(), - ) - })? - }; - - let arg1 = require_arg(vm, 1, "string.gmatch")?; - let pattern_str = unsafe { - arg1.as_string().ok_or_else(|| { - LuaError::RuntimeError( - "bad argument #2 to 'string.gmatch' (string expected)".to_string(), - ) - })? + let s_value = require_arg(vm, 1, "string.gmatch")?; + if !s_value.is_string() { + return Err(vm.error("bad argument #1 to 'string.gmatch' (string expected)".to_string())); }; - // Validate pattern early - let _pattern = match lua_pattern::parse_pattern(pattern_str.as_str()) { - Ok(p) => p, - Err(e) => return Err(LuaError::RuntimeError(format!("invalid pattern: {}", e))), + let pattern_value = require_arg(vm, 2, "string.gmatch")?; + if !pattern_value.is_string() { + return Err(vm.error("bad argument #2 to 'string.gmatch' (string expected)".to_string())); }; // Create state table: {string = s, pattern = p, position = 0} - let state_table = vm.create_table(0, 3); - let string_key = vm.create_string("string"); - let pattern_key = vm.create_string("pattern"); - let position_key = vm.create_string("position"); - let s_value = vm.create_string(s.as_str()); - let pattern_value = vm.create_string(pattern_str.as_str()); - - let state_ref = vm - .get_table(&state_table) - .ok_or_else(|| LuaError::RuntimeError("Invalid state table".to_string()))?; - state_ref.borrow_mut().raw_set(string_key, s_value); - state_ref.borrow_mut().raw_set(pattern_key, pattern_value); - state_ref - .borrow_mut() - .raw_set(position_key, LuaValue::integer(0)); - + let state_table = vm.create_table(3, 0); + let Some(state_ref) = state_table.as_lua_table() else { + return Err(vm.error("failed to create state table for gmatch".to_string())); + }; + state_ref.borrow_mut().set_int(1, s_value); + state_ref.borrow_mut().set_int(2, pattern_value); + state_ref.borrow_mut().set_int(3, LuaValue::integer(0)); // Return: iterator function, state table, nil (initial control variable) Ok(MultiValue::multiple(vec![ LuaValue::cfunction(gmatch_iterator), @@ -999,61 +971,46 @@ fn string_gmatch(vm: &mut LuaVM) -> LuaResult { fn gmatch_iterator(vm: &mut LuaVM) -> LuaResult { // Arg 0: state table // Arg 1: control variable (unused, we use state.position) - let state_table_value = require_arg(vm, 0, "gmatch iterator")?; + let state_table_value = require_arg(vm, 1, "gmatch iterator")?; // Extract string, pattern, and position from state - let string_key = vm.create_string("string"); - let pattern_key = vm.create_string("pattern"); - let position_key = vm.create_string("position"); - - let state_ref_cell = vm - .get_table(&state_table_value) - .ok_or_else(|| LuaError::RuntimeError("Invalid state table".to_string()))?; - let s_val = state_ref_cell - .borrow() - .raw_get(&string_key) - .ok_or_else(|| { - LuaError::RuntimeError("gmatch iterator: string not found in state".to_string()) - })?; - let s = unsafe { - s_val - .as_string() - .ok_or_else(|| LuaError::RuntimeError("gmatch iterator: string invalid".to_string()))? + let Some(state_ref) = state_table_value.as_lua_table() else { + return Err(vm.error("gmatch iterator: state is not a table".to_string())); + }; + let Some(s_val) = state_ref.borrow().get_int(1) else { + return Err(vm.error("gmatch iterator: string not found in state".to_string())); + }; + let Some(s) = s_val.as_lua_string() else { + return Err(vm.error("gmatch iterator: string invalid".to_string())); }; - let pattern_val = state_ref_cell - .borrow() - .raw_get(&pattern_key) - .ok_or_else(|| { - LuaError::RuntimeError("gmatch iterator: pattern not found in state".to_string()) - })?; - let pattern_str = unsafe { - pattern_val - .as_string() - .ok_or_else(|| LuaError::RuntimeError("gmatch iterator: pattern invalid".to_string()))? + let Some(pattern_val) = state_ref.borrow().get_int(2) else { + return Err(vm.error("gmatch iterator: pattern not found in state".to_string())); }; - let position = state_ref_cell + let Some(pattern_str) = pattern_val.as_lua_string() else { + return Err(vm.error("gmatch iterator: pattern invalid".to_string())); + }; + + let position_value = state_ref .borrow() - .raw_get(&position_key) - .and_then(|v| v.as_integer()) - .ok_or_else(|| { - LuaError::RuntimeError("gmatch iterator: position not found in state".to_string()) - })? as usize; + .get_int(3) + .unwrap_or(LuaValue::integer(0)); + let position = position_value.as_integer().unwrap_or(0) as usize; // Parse pattern let pattern = match lua_pattern::parse_pattern(pattern_str.as_str()) { Ok(p) => p, - Err(e) => return Err(LuaError::RuntimeError(format!("invalid pattern: {}", e))), + Err(e) => return Err(vm.error(format!("invalid pattern: {}", e))), }; // Find next match if let Some((start, end, captures)) = lua_pattern::find(s.as_str(), &pattern, position) { // Update position for next iteration let next_pos = if end > start { end } else { end + 1 }; - state_ref_cell + state_ref .borrow_mut() - .raw_set(position_key, LuaValue::integer(next_pos as i64)); + .set_int(3, LuaValue::integer(next_pos as i64)); // Return captures if any, otherwise return the matched string if captures.is_empty() { @@ -1075,16 +1032,9 @@ fn gmatch_iterator(vm: &mut LuaVM) -> LuaResult { /// string.pack(fmt, v1, v2, ...) - Pack values into binary string /// Simplified implementation supporting basic format codes fn string_pack(vm: &mut LuaVM) -> LuaResult { - let fmt_arg = require_arg(vm, 0, "string.pack")?; - let fmt = unsafe { - fmt_arg - .as_string() - .ok_or_else(|| { - LuaError::RuntimeError( - "bad argument #1 to 'string.pack' (string expected)".to_string(), - ) - })? - .as_str() + let fmt_value = require_arg(vm, 1, "string.pack")?; + let Some(fmt) = fmt_value.as_lua_string() else { + return Err(vm.error("bad argument #1 to 'string.pack' (string expected)".to_string())); }; let args = get_args(vm); @@ -1092,7 +1042,7 @@ fn string_pack(vm: &mut LuaVM) -> LuaResult { let mut result = Vec::new(); let mut value_idx = 0; - let mut chars = fmt.chars(); + let mut chars = fmt.as_str().chars(); while let Some(ch) = chars.next() { match ch { @@ -1100,14 +1050,12 @@ fn string_pack(vm: &mut LuaVM) -> LuaResult { 'b' => { // signed byte if value_idx >= values.len() { - return Err(LuaError::RuntimeError( - "bad argument to 'string.pack' (not enough values)".to_string(), - )); + return Err( + vm.error("bad argument to 'string.pack' (not enough values)".to_string()) + ); } let n = values[value_idx].as_integer().ok_or_else(|| { - LuaError::RuntimeError( - "bad argument to 'string.pack' (number expected)".to_string(), - ) + vm.error("bad argument to 'string.pack' (number expected)".to_string()) })?; result.push((n & 0xFF) as u8); value_idx += 1; @@ -1115,14 +1063,12 @@ fn string_pack(vm: &mut LuaVM) -> LuaResult { 'B' => { // unsigned byte if value_idx >= values.len() { - return Err(LuaError::RuntimeError( - "bad argument to 'string.pack' (not enough values)".to_string(), - )); + return Err( + vm.error("bad argument to 'string.pack' (not enough values)".to_string()) + ); } let n = values[value_idx].as_integer().ok_or_else(|| { - LuaError::RuntimeError( - "bad argument to 'string.pack' (number expected)".to_string(), - ) + vm.error("bad argument to 'string.pack' (number expected)".to_string()) })?; result.push((n & 0xFF) as u8); value_idx += 1; @@ -1130,14 +1076,12 @@ fn string_pack(vm: &mut LuaVM) -> LuaResult { 'h' => { // signed short (2 bytes, little-endian) if value_idx >= values.len() { - return Err(LuaError::RuntimeError( - "bad argument to 'string.pack' (not enough values)".to_string(), - )); + return Err( + vm.error("bad argument to 'string.pack' (not enough values)".to_string()) + ); } let n = values[value_idx].as_integer().ok_or_else(|| { - LuaError::RuntimeError( - "bad argument to 'string.pack' (number expected)".to_string(), - ) + vm.error("bad argument to 'string.pack' (number expected)".to_string()) })? as i16; result.extend_from_slice(&n.to_le_bytes()); value_idx += 1; @@ -1145,14 +1089,12 @@ fn string_pack(vm: &mut LuaVM) -> LuaResult { 'H' => { // unsigned short (2 bytes, little-endian) if value_idx >= values.len() { - return Err(LuaError::RuntimeError( - "bad argument to 'string.pack' (not enough values)".to_string(), - )); + return Err( + vm.error("bad argument to 'string.pack' (not enough values)".to_string()) + ); } let n = values[value_idx].as_integer().ok_or_else(|| { - LuaError::RuntimeError( - "bad argument to 'string.pack' (number expected)".to_string(), - ) + vm.error("bad argument to 'string.pack' (number expected)".to_string()) })? as u16; result.extend_from_slice(&n.to_le_bytes()); value_idx += 1; @@ -1160,14 +1102,12 @@ fn string_pack(vm: &mut LuaVM) -> LuaResult { 'i' | 'l' => { // signed int (4 bytes, little-endian) if value_idx >= values.len() { - return Err(LuaError::RuntimeError( - "bad argument to 'string.pack' (not enough values)".to_string(), - )); + return Err( + vm.error("bad argument to 'string.pack' (not enough values)".to_string()) + ); } let n = values[value_idx].as_integer().ok_or_else(|| { - LuaError::RuntimeError( - "bad argument to 'string.pack' (number expected)".to_string(), - ) + vm.error("bad argument to 'string.pack' (number expected)".to_string()) })? as i32; result.extend_from_slice(&n.to_le_bytes()); value_idx += 1; @@ -1175,14 +1115,12 @@ fn string_pack(vm: &mut LuaVM) -> LuaResult { 'I' | 'L' => { // unsigned int (4 bytes, little-endian) if value_idx >= values.len() { - return Err(LuaError::RuntimeError( - "bad argument to 'string.pack' (not enough values)".to_string(), - )); + return Err( + vm.error("bad argument to 'string.pack' (not enough values)".to_string()) + ); } let n = values[value_idx].as_integer().ok_or_else(|| { - LuaError::RuntimeError( - "bad argument to 'string.pack' (number expected)".to_string(), - ) + vm.error("bad argument to 'string.pack' (number expected)".to_string()) })? as u32; result.extend_from_slice(&n.to_le_bytes()); value_idx += 1; @@ -1190,14 +1128,12 @@ fn string_pack(vm: &mut LuaVM) -> LuaResult { 'f' => { // float (4 bytes, little-endian) if value_idx >= values.len() { - return Err(LuaError::RuntimeError( - "bad argument to 'string.pack' (not enough values)".to_string(), - )); + return Err( + vm.error("bad argument to 'string.pack' (not enough values)".to_string()) + ); } let n = values[value_idx].as_number().ok_or_else(|| { - LuaError::RuntimeError( - "bad argument to 'string.pack' (number expected)".to_string(), - ) + vm.error("bad argument to 'string.pack' (number expected)".to_string()) })? as f32; result.extend_from_slice(&n.to_le_bytes()); value_idx += 1; @@ -1205,14 +1141,12 @@ fn string_pack(vm: &mut LuaVM) -> LuaResult { 'd' => { // double (8 bytes, little-endian) if value_idx >= values.len() { - return Err(LuaError::RuntimeError( - "bad argument to 'string.pack' (not enough values)".to_string(), - )); + return Err( + vm.error("bad argument to 'string.pack' (not enough values)".to_string()) + ); } let n = values[value_idx].as_number().ok_or_else(|| { - LuaError::RuntimeError( - "bad argument to 'string.pack' (number expected)".to_string(), - ) + vm.error("bad argument to 'string.pack' (number expected)".to_string()) })?; result.extend_from_slice(&n.to_le_bytes()); value_idx += 1; @@ -1220,15 +1154,13 @@ fn string_pack(vm: &mut LuaVM) -> LuaResult { 'z' => { // zero-terminated string if value_idx >= values.len() { - return Err(LuaError::RuntimeError( - "bad argument to 'string.pack' (not enough values)".to_string(), - )); + return Err( + vm.error("bad argument to 'string.pack' (not enough values)".to_string()) + ); } let s = unsafe { values[value_idx].as_string().ok_or_else(|| { - LuaError::RuntimeError( - "bad argument to 'string.pack' (string expected)".to_string(), - ) + vm.error("bad argument to 'string.pack' (string expected)".to_string()) })? }; result.extend_from_slice(s.as_str().as_bytes()); @@ -1245,21 +1177,17 @@ fn string_pack(vm: &mut LuaVM) -> LuaResult { } } let size: usize = size_str.parse().map_err(|_| { - LuaError::RuntimeError( - "bad argument to 'string.pack' (invalid size)".to_string(), - ) + vm.error("bad argument to 'string.pack' (invalid size)".to_string()) })?; if value_idx >= values.len() { - return Err(LuaError::RuntimeError( - "bad argument to 'string.pack' (not enough values)".to_string(), - )); + return Err( + vm.error("bad argument to 'string.pack' (not enough values)".to_string()) + ); } let s = unsafe { values[value_idx].as_string().ok_or_else(|| { - LuaError::RuntimeError( - "bad argument to 'string.pack' (string expected)".to_string(), - ) + vm.error("bad argument to 'string.pack' (string expected)".to_string()) })? }; let bytes = s.as_str().as_bytes(); @@ -1271,7 +1199,7 @@ fn string_pack(vm: &mut LuaVM) -> LuaResult { value_idx += 1; } _ => { - return Err(LuaError::RuntimeError(format!( + return Err(vm.error(format!( "bad argument to 'string.pack' (invalid format option '{}')", ch ))); @@ -1287,20 +1215,12 @@ fn string_pack(vm: &mut LuaVM) -> LuaResult { /// string.packsize(fmt) - Return size of packed data fn string_packsize(vm: &mut LuaVM) -> LuaResult { - let fmt_arg = require_arg(vm, 0, "string.packsize")?; - let fmt = unsafe { - fmt_arg - .as_string() - .ok_or_else(|| { - LuaError::RuntimeError( - "bad argument #1 to 'string.packsize' (string expected)".to_string(), - ) - })? - .as_str() + let fmt_value = require_arg(vm, 1, "string.packsize")?; + let Some(fmt) = fmt_value.as_lua_string() else { + return Err(vm.error("bad argument #1 to 'string.packsize' (string expected)".to_string())); }; - let mut size = 0usize; - let mut chars = fmt.chars(); + let mut chars = fmt.as_str().chars(); while let Some(ch) = chars.next() { match ch { @@ -1310,9 +1230,7 @@ fn string_packsize(vm: &mut LuaVM) -> LuaResult { 'i' | 'I' | 'l' | 'L' | 'f' => size += 4, 'd' => size += 8, 'z' => { - return Err(LuaError::RuntimeError( - "variable-length format in 'string.packsize'".to_string(), - )); + return Err(vm.error("variable-length format in 'string.packsize'".to_string())); } 'c' => { let mut size_str = String::new(); @@ -1323,14 +1241,12 @@ fn string_packsize(vm: &mut LuaVM) -> LuaResult { } } let n: usize = size_str.parse().map_err(|_| { - LuaError::RuntimeError( - "bad argument to 'string.packsize' (invalid size)".to_string(), - ) + vm.error("bad argument to 'string.packsize' (invalid size)".to_string()) })?; size += n; } _ => { - return Err(LuaError::RuntimeError(format!( + return Err(vm.error(format!( "bad argument to 'string.packsize' (invalid format option '{}')", ch ))); @@ -1343,54 +1259,43 @@ fn string_packsize(vm: &mut LuaVM) -> LuaResult { /// string.unpack(fmt, s [, pos]) - Unpack binary string fn string_unpack(vm: &mut LuaVM) -> LuaResult { - let fmt_arg = require_arg(vm, 0, "string.unpack")?; - let fmt = unsafe { - fmt_arg - .as_string() - .ok_or_else(|| { - LuaError::RuntimeError( - "bad argument #1 to 'string.unpack' (string expected)".to_string(), - ) - })? - .as_str() + let fmt_value = require_arg(vm, 1, "string.unpack")?; + let Some(fmt) = fmt_value.as_lua_string() else { + return Err(vm.error("bad argument #1 to 'string.unpack' (string expected)".to_string())); }; - let s_arg = require_arg(vm, 1, "string.unpack")?; - let s = unsafe { - s_arg.as_string().ok_or_else(|| { - LuaError::RuntimeError( - "bad argument #2 to 'string.unpack' (string expected)".to_string(), - ) - })? + let s_value = require_arg(vm, 2, "string.unpack")?; + let Some(s) = s_value.as_lua_string() else { + return Err(vm.error("bad argument #2 to 'string.unpack' (string expected)".to_string())); }; let bytes = s.as_str().as_bytes(); - let pos = get_arg(vm, 2).and_then(|v| v.as_integer()).unwrap_or(1) as usize - 1; // Convert to 0-based + let pos = get_arg(vm, 3).and_then(|v| v.as_integer()).unwrap_or(1) as usize - 1; // Convert to 0-based let mut results = Vec::new(); let mut idx = pos; - let mut chars = fmt.chars(); + let mut chars = fmt.as_str().chars(); while let Some(ch) = chars.next() { match ch { ' ' | '\t' | '\n' | '\r' => continue, 'b' => { if idx >= bytes.len() { - return Err(LuaError::RuntimeError("data string too short".to_string())); + return Err(vm.error("data string too short".to_string())); } results.push(LuaValue::integer(bytes[idx] as i8 as i64)); idx += 1; } 'B' => { if idx >= bytes.len() { - return Err(LuaError::RuntimeError("data string too short".to_string())); + return Err(vm.error("data string too short".to_string())); } results.push(LuaValue::integer(bytes[idx] as i64)); idx += 1; } 'h' => { if idx + 2 > bytes.len() { - return Err(LuaError::RuntimeError("data string too short".to_string())); + return Err(vm.error("data string too short".to_string())); } let val = i16::from_le_bytes([bytes[idx], bytes[idx + 1]]); results.push(LuaValue::integer(val as i64)); @@ -1398,7 +1303,7 @@ fn string_unpack(vm: &mut LuaVM) -> LuaResult { } 'H' => { if idx + 2 > bytes.len() { - return Err(LuaError::RuntimeError("data string too short".to_string())); + return Err(vm.error("data string too short".to_string())); } let val = u16::from_le_bytes([bytes[idx], bytes[idx + 1]]); results.push(LuaValue::integer(val as i64)); @@ -1406,7 +1311,7 @@ fn string_unpack(vm: &mut LuaVM) -> LuaResult { } 'i' | 'l' => { if idx + 4 > bytes.len() { - return Err(LuaError::RuntimeError("data string too short".to_string())); + return Err(vm.error("data string too short".to_string())); } let val = i32::from_le_bytes([ bytes[idx], @@ -1419,7 +1324,7 @@ fn string_unpack(vm: &mut LuaVM) -> LuaResult { } 'I' | 'L' => { if idx + 4 > bytes.len() { - return Err(LuaError::RuntimeError("data string too short".to_string())); + return Err(vm.error("data string too short".to_string())); } let val = u32::from_le_bytes([ bytes[idx], @@ -1432,7 +1337,7 @@ fn string_unpack(vm: &mut LuaVM) -> LuaResult { } 'f' => { if idx + 4 > bytes.len() { - return Err(LuaError::RuntimeError("data string too short".to_string())); + return Err(vm.error("data string too short".to_string())); } let val = f32::from_le_bytes([ bytes[idx], @@ -1445,7 +1350,7 @@ fn string_unpack(vm: &mut LuaVM) -> LuaResult { } 'd' => { if idx + 8 > bytes.len() { - return Err(LuaError::RuntimeError("data string too short".to_string())); + return Err(vm.error("data string too short".to_string())); } let mut arr = [0u8; 8]; arr.copy_from_slice(&bytes[idx..idx + 8]); @@ -1472,20 +1377,18 @@ fn string_unpack(vm: &mut LuaVM) -> LuaResult { } } let size: usize = size_str.parse().map_err(|_| { - LuaError::RuntimeError( - "bad argument to 'string.unpack' (invalid size)".to_string(), - ) + vm.error("bad argument to 'string.unpack' (invalid size)".to_string()) })?; if idx + size > bytes.len() { - return Err(LuaError::RuntimeError("data string too short".to_string())); + return Err(vm.error("data string too short".to_string())); } let s = String::from_utf8_lossy(&bytes[idx..idx + size]); results.push(vm.create_string(&s)); idx += size; } _ => { - return Err(LuaError::RuntimeError(format!( + return Err(vm.error(format!( "bad argument to 'string.unpack' (invalid format option '{}')", ch ))); diff --git a/crates/luars/src/stdlib/table.rs b/crates/luars/src/stdlib/table.rs index 076f3109..a1334a81 100644 --- a/crates/luars/src/stdlib/table.rs +++ b/crates/luars/src/stdlib/table.rs @@ -1,9 +1,9 @@ // Table library // Implements: concat, insert, move, pack, remove, sort, unpack -use crate::lib_registry::{LibraryModule, get_arg, require_arg}; +use crate::lib_registry::{LibraryModule, arg_count, get_arg, require_arg}; use crate::lua_value::{LuaValue, MultiValue}; -use crate::lua_vm::{LuaError, LuaResult, LuaVM}; +use crate::lua_vm::{LuaResult, LuaVM}; pub fn create_table_lib() -> LibraryModule { crate::lib_module!("table", { @@ -19,48 +19,43 @@ pub fn create_table_lib() -> LibraryModule { /// table.concat(list [, sep [, i [, j]]]) - Concatenate table elements fn table_concat(vm: &mut LuaVM) -> LuaResult { - let table_val = require_arg(vm, 0, "table.concat")?; + let table_val = require_arg(vm, 1, "table.concat")?; - let sep_value = get_arg(vm, 1); + let sep_value = get_arg(vm, 2); let sep = match sep_value { - Some(v) => vm - .get_string(&v) - .ok_or_else(|| { - LuaError::RuntimeError( - "bad argument #2 to 'table.concat' (string expected)".to_string(), - ) - })? - .as_str() - .to_string(), + Some(v) => { + if let Some(s) = v.as_lua_string() { + s.as_str().to_string() + } else { + return Err( + vm.error("bad argument #2 to 'table.concat' (string expected)".to_string()) + ); + } + } None => "".to_string(), }; - let table_ptr = table_val - .as_table_ptr() - .ok_or(LuaError::RuntimeError("Invalid table".to_string()))?; - let len = unsafe { (*table_ptr).borrow().len() }; - - let i = get_arg(vm, 2).and_then(|v| v.as_integer()).unwrap_or(1); + let Some(table_ref) = table_val.as_lua_table() else { + return Err(vm.error("Invalid table".to_string())); + }; + let len = table_ref.borrow().len(); + let i = get_arg(vm, 3).and_then(|v| v.as_integer()).unwrap_or(1); - let j = get_arg(vm, 3) + let j = get_arg(vm, 4) .and_then(|v| v.as_integer()) .unwrap_or(len as i64); let mut parts = Vec::new(); + let Some(array_parts) = &table_ref.borrow().array else { + let s: LuaValue = vm.create_string(""); + return Ok(MultiValue::single(s)); + }; for idx in i..=j { - let key = LuaValue::integer(idx); - let value = unsafe { - (*table_ptr) - .borrow() - .raw_get(&key) - .unwrap_or(LuaValue::nil()) - }; - - unsafe { - if let Some(s) = value.as_string() { - parts.push(s.as_str().to_string()); + if let Some(value) = array_parts.get(idx as usize - 1) { + if let Some(s) = value.as_lua_string() { + parts.push(s.as_str()); } else { - return Err(LuaError::RuntimeError(format!( + return Err(vm.error(format!( "bad value at index {} in 'table.concat' (string expected)", idx ))); @@ -74,51 +69,51 @@ fn table_concat(vm: &mut LuaVM) -> LuaResult { /// table.insert(list, [pos,] value) - Insert element fn table_insert(vm: &mut LuaVM) -> LuaResult { - let table_val = require_arg(vm, 0, "table.insert")?; - let argc = crate::lib_registry::arg_count(vm); + let table_val = require_arg(vm, 1, "table.insert")?; + let argc = arg_count(vm); // CRITICAL: Use direct pointer to avoid HashMap lookup per insert! - let table_ptr = table_val - .as_table_ptr() - .ok_or(LuaError::RuntimeError("Invalid table".to_string()))?; + let Some(table_ref) = table_val.as_lua_table() else { + return Err(vm.error("Invalid table".to_string())); + }; - let len = unsafe { (*table_ptr).borrow().len() }; + let len = table_ref.borrow().len(); if argc == 2 { // table.insert(list, value) - append at end - let value = require_arg(vm, 1, "table.insert")?; - unsafe { - (*table_ptr) - .borrow_mut() - .raw_set(LuaValue::integer(len as i64 + 1), value) - }; + let value = require_arg(vm, 2, "table.insert")?; + match table_ref.borrow_mut().insert_array_at(len, value) { + Ok(_) => {} + Err(e) => { + return Err(vm.error(format!("error inserting into table: {}", e))); + } + } } else if argc == 3 { // table.insert(list, pos, value) - let pos = require_arg(vm, 1, "table.insert")? + let pos = require_arg(vm, 2, "table.insert")? .as_integer() .ok_or_else(|| { - LuaError::RuntimeError( - "bad argument #2 to 'table.insert' (number expected)".to_string(), - ) + vm.error("bad argument #2 to 'table.insert' (number expected)".to_string()) })?; - let value = require_arg(vm, 2, "table.insert")?; + let value = require_arg(vm, 3, "table.insert")?; if pos < 1 || pos > len as i64 + 1 { - return Err(LuaError::RuntimeError(format!( + return Err(vm.error(format!( "bad argument #2 to 'table.insert' (position out of bounds)" ))); } - - unsafe { - (*table_ptr) - .borrow_mut() - .insert_array_at(pos as usize - 1, value)? - }; + match table_ref + .borrow_mut() + .insert_array_at(pos as usize - 1, value) + { + Ok(_) => {} + Err(e) => { + return Err(vm.error(format!("error inserting into table: {}", e))); + } + } } else { - return Err(LuaError::RuntimeError( - "wrong number of arguments to 'table.insert'".to_string(), - )); + return Err(vm.error("wrong number of arguments to 'table.insert'".to_string())); } Ok(MultiValue::empty()) @@ -126,82 +121,73 @@ fn table_insert(vm: &mut LuaVM) -> LuaResult { /// table.remove(list [, pos]) - Remove element fn table_remove(vm: &mut LuaVM) -> LuaResult { - let table_val = require_arg(vm, 0, "table.remove")?; + let table_val = require_arg(vm, 1, "table.remove")?; // CRITICAL: Use direct pointer to avoid HashMap lookup - let table_ptr = table_val - .as_table_ptr() - .ok_or(LuaError::RuntimeError("Invalid table".to_string()))?; + let Some(table_ref) = table_val.as_lua_table() else { + return Err(vm.error("Invalid table".to_string())); + }; - let len = unsafe { (*table_ptr).borrow().len() }; + let len = table_ref.borrow().len(); if len == 0 { return Ok(MultiValue::single(LuaValue::nil())); } - let pos = get_arg(vm, 1) + let pos = get_arg(vm, 2) .and_then(|v| v.as_integer()) .unwrap_or(len as i64); if pos < 1 || pos > len as i64 { - return Err(LuaError::RuntimeError(format!( + return Err(vm.error(format!( "bad argument #2 to 'table.remove' (position out of bounds)" ))); } - let removed = unsafe { - (*table_ptr) - .borrow_mut() - .remove_array_at(pos as usize - 1)? + let removed = match table_ref.borrow_mut().remove_array_at(pos as usize - 1) { + Ok(val) => val, + Err(e) => return Err(vm.error(format!("error removing from table: {}", e))), }; Ok(MultiValue::single(removed)) } /// table.move(a1, f, e, t [, a2]) - Move elements fn table_move(vm: &mut LuaVM) -> LuaResult { - let src_val = require_arg(vm, 0, "table.move")?; + let src_val = require_arg(vm, 1, "table.move")?; - let f = require_arg(vm, 1, "table.move")? + let f = require_arg(vm, 2, "table.move")? .as_integer() - .ok_or_else(|| { - LuaError::RuntimeError("bad argument #2 to 'table.move' (number expected)".to_string()) - })?; + .ok_or_else(|| vm.error("bad argument #2 to 'table.move' (number expected)".to_string()))?; - let e = require_arg(vm, 2, "table.move")? + let e = require_arg(vm, 3, "table.move")? .as_integer() - .ok_or_else(|| { - LuaError::RuntimeError("bad argument #3 to 'table.move' (number expected)".to_string()) - })?; - let t = require_arg(vm, 3, "table.move")? + .ok_or_else(|| vm.error("bad argument #3 to 'table.move' (number expected)".to_string()))?; + let t = require_arg(vm, 4, "table.move")? .as_integer() - .ok_or_else(|| { - LuaError::RuntimeError("bad argument #4 to 'table.move' (number expected)".to_string()) - })?; - - let dst_value = get_arg(vm, 4).unwrap_or_else(|| src_val.clone()); + .ok_or_else(|| vm.error("bad argument #4 to 'table.move' (number expected)".to_string()))?; + let dst_value = get_arg(vm, 5).unwrap_or_else(|| src_val.clone()); // Copy elements let mut values = Vec::new(); { - let src_ptr = src_val - .as_table_ptr() - .ok_or(LuaError::RuntimeError("Invalid source table".to_string()))?; - let src_ref = unsafe { (*src_ptr).borrow() }; + let Some(src_ref) = src_val.as_lua_table() else { + return Err(vm.error("Invalid source table".to_string())); + }; + let src_ref_mut = src_ref.borrow_mut(); + for i in f..=e { - let val = src_ref - .raw_get(&LuaValue::integer(i)) - .unwrap_or(LuaValue::nil()); + let val = src_ref_mut.get_int(i).unwrap_or(LuaValue::nil()); values.push(val); } } { - let dst_ptr = dst_value.as_table_ptr().ok_or(LuaError::RuntimeError( - "Invalid destination table".to_string(), - ))?; - let mut dst_ref = unsafe { (*dst_ptr).borrow_mut() }; + let Some(dst_ref) = dst_value.as_lua_table() else { + return Err(vm.error("Invalid destination table".to_string())); + }; + let mut dst_ref_mut = dst_ref.borrow_mut(); for (offset, val) in values.into_iter().enumerate() { - dst_ref.raw_set(LuaValue::integer(t + offset as i64), val); + dst_ref_mut.set_int(t + offset as i64, val); } } @@ -214,41 +200,37 @@ fn table_pack(vm: &mut LuaVM) -> LuaResult { let table = vm.create_table(args.len(), 1); // Set 'n' field let n_key = vm.create_string("n"); - let table_ptr = table - .as_table_ptr() - .ok_or(LuaError::RuntimeError("Invalid table".to_string()))?; - let mut table_ref = unsafe { (*table_ptr).borrow_mut() }; + let Some(table_ref) = table.as_lua_table() else { + return Err(vm.error("Invalid table".to_string())); + }; + let mut table_ref = table_ref.borrow_mut(); for (i, arg) in args.iter().enumerate() { - table_ref.raw_set(LuaValue::integer(i as i64 + 1), arg.clone()); + table_ref.set_int(i as i64 + 1, arg.clone()); } table_ref.raw_set(n_key, LuaValue::integer(args.len() as i64)); - - drop(table_ref); Ok(MultiValue::single(table)) } /// table.unpack(list [, i [, j]]) - Unpack table into values fn table_unpack(vm: &mut LuaVM) -> LuaResult { - let table_val = require_arg(vm, 0, "table.unpack")?; - let table_ptr = table_val - .as_table_ptr() - .ok_or(LuaError::RuntimeError("Invalid table".to_string()))?; - let table_ref = unsafe { (*table_ptr).borrow() }; + let table_val = require_arg(vm, 1, "table.unpack")?; + let Some(table_ref) = table_val.as_lua_table() else { + return Err(vm.error("Invalid table".to_string())); + }; + let table_ref = table_ref.borrow(); let len = table_ref.len(); - let i = get_arg(vm, 1).and_then(|v| v.as_integer()).unwrap_or(1); + let i = get_arg(vm, 2).and_then(|v| v.as_integer()).unwrap_or(1); - let j = get_arg(vm, 2) + let j = get_arg(vm, 3) .and_then(|v| v.as_integer()) .unwrap_or(len as i64); let mut result = Vec::new(); for idx in i..=j { - let val = table_ref - .raw_get(&LuaValue::integer(idx)) - .unwrap_or(LuaValue::nil()); + let val = table_ref.get_int(idx).unwrap_or(LuaValue::nil()); result.push(val); } @@ -257,49 +239,27 @@ fn table_unpack(vm: &mut LuaVM) -> LuaResult { /// table.sort(list [, comp]) - Sort table in place fn table_sort(vm: &mut LuaVM) -> LuaResult { - let table_val = require_arg(vm, 0, "table.sort")?; - let comp = get_arg(vm, 1); + let table_val = require_arg(vm, 1, "table.sort")?; + let comp = get_arg(vm, 2); - // Get array length and extract values - let len = { - let table_ptr = table_val - .as_table_ptr() - .ok_or(LuaError::RuntimeError("Invalid table".to_string()))?; - let table_ref = unsafe { (*table_ptr).borrow() }; - table_ref.len() + let Some(table_ref) = table_val.as_lua_table() else { + return Err(vm.error("Invalid table".to_string())); }; + // Get array length and extract values + let len = table_ref.borrow().len(); if len <= 1 { return Ok(MultiValue::empty()); } // Extract values from array part [1..len] - let mut values = Vec::with_capacity(len); - { - let table_ptr = table_val - .as_table_ptr() - .ok_or(LuaError::RuntimeError("Invalid table".to_string()))?; - let table_ref = unsafe { (*table_ptr).borrow() }; - for i in 1..=len { - if let Some(val) = table_ref.get_int(i as i64) { - values.push(val); - } else { - values.push(LuaValue::nil()); - } - } - } + let Some(values) = &mut table_ref.borrow_mut().array else { + return Ok(MultiValue::empty()); + }; // If no comparison function, use default ordering if comp.is_none() || comp.as_ref().map(|v| v.is_nil()).unwrap_or(true) { values.sort(); - // Write back sorted values - let table_ptr = table_val - .as_table_ptr() - .ok_or(LuaError::RuntimeError("Invalid table".to_string()))?; - let mut table_ref = unsafe { (*table_ptr).borrow_mut() }; - for (i, val) in values.into_iter().enumerate() { - table_ref.set_int((i + 1) as i64, val); - } return Ok(MultiValue::empty()); } @@ -336,14 +296,5 @@ fn table_sort(vm: &mut LuaVM) -> LuaResult { } }); - // Write back the sorted values - let table_ref_cell = vm - .get_table(&table_val) - .ok_or(LuaError::RuntimeError("Invalid table".to_string()))?; - let mut table_ref = table_ref_cell.borrow_mut(); - for (i, val) in values.into_iter().enumerate() { - table_ref.set_int((i + 1) as i64, val); - } - Ok(MultiValue::empty()) } diff --git a/crates/luars/src/stdlib/utf8.rs b/crates/luars/src/stdlib/utf8.rs index 5b08e200..7dc72bbc 100644 --- a/crates/luars/src/stdlib/utf8.rs +++ b/crates/luars/src/stdlib/utf8.rs @@ -3,7 +3,7 @@ use crate::lib_registry::{LibraryModule, get_arg, get_args, require_arg}; use crate::lua_value::{LuaValue, MultiValue}; -use crate::lua_vm::{LuaError, LuaResult, LuaVM}; +use crate::lua_vm::{LuaResult, LuaVM}; pub fn create_utf8_lib() -> LibraryModule { let mut module = crate::lib_module!("utf8", { @@ -26,18 +26,18 @@ pub fn create_utf8_lib() -> LibraryModule { } fn utf8_len(vm: &mut LuaVM) -> LuaResult { - let s_value = require_arg(vm, 0, "utf8.len")?; - let s = s_value.as_lua_string().ok_or_else(|| { - LuaError::RuntimeError("bad argument #1 to 'utf8.len' (string expected)".to_string()) - })?; + let s_value = require_arg(vm, 1, "utf8.len")?; + let Some(s) = s_value.as_lua_string() else { + return Err(vm.error("bad argument #1 to 'utf8.len' (string expected)".to_string())); + }; let bytes = s.as_str().as_bytes(); let len = bytes.len() as i64; // Fast path: no range specified, count entire string - let i_arg = get_arg(vm, 1); - let j_arg = get_arg(vm, 2); - let lax = get_arg(vm, 3).and_then(|v| v.as_bool()).unwrap_or(false); + let i_arg = get_arg(vm, 2); + let j_arg = get_arg(vm, 3); + let lax = get_arg(vm, 4).and_then(|v| v.as_bool()).unwrap_or(false); if i_arg.is_none() && j_arg.is_none() { // Fast path: validate and count entire string @@ -106,21 +106,19 @@ fn utf8_char(vm: &mut LuaVM) -> LuaResult { for arg in args { if let Some(code) = arg.as_integer() { if code < 0 || code > 0x10FFFF { - return Err(LuaError::RuntimeError( - "bad argument to 'utf8.char' (value out of range)".to_string(), - )); + return Err( + vm.error("bad argument to 'utf8.char' (value out of range)".to_string()) + ); } if let Some(ch) = char::from_u32(code as u32) { result.push(ch); } else { - return Err(LuaError::RuntimeError( - "bad argument to 'utf8.char' (invalid code point)".to_string(), - )); + return Err( + vm.error("bad argument to 'utf8.char' (invalid code point)".to_string()) + ); } } else { - return Err(LuaError::RuntimeError( - "bad argument to 'utf8.char' (number expected)".to_string(), - )); + return Err(vm.error("bad argument to 'utf8.char' (number expected)".to_string())); } } @@ -130,20 +128,16 @@ fn utf8_char(vm: &mut LuaVM) -> LuaResult { /// utf8.codes(s) - Returns an iterator for UTF-8 characters fn utf8_codes(vm: &mut LuaVM) -> LuaResult { - let s_value = require_arg(vm, 0, "utf8.codes")?; + let s_value = require_arg(vm, 1, "utf8.codes")?; if !s_value.is_string() { - return Err(LuaError::RuntimeError( - "bad argument #1 to 'utf8.codes' (string expected)".to_string(), - )); + return Err(vm.error("bad argument #1 to 'utf8.codes' (string expected)".to_string())); } // Create state table: {string = s, position = 0} - let state_table = vm.create_table(0, 2); - let string_key = vm.create_string("string"); - let position_key = vm.create_string("position"); - let state_ref = vm - .get_table(&state_table) - .ok_or(LuaError::RuntimeError("Invalid state table".to_string()))?; + let state_table = vm.create_table(2, 0); + let string_key = LuaValue::integer(1); + let position_key = LuaValue::integer(2); + let state_ref = state_table.as_lua_table().unwrap(); state_ref.borrow_mut().raw_set(string_key, s_value); state_ref .borrow_mut() @@ -158,33 +152,26 @@ fn utf8_codes(vm: &mut LuaVM) -> LuaResult { /// Iterator function for utf8.codes fn utf8_codes_iterator(vm: &mut LuaVM) -> LuaResult { - let t_value = require_arg(vm, 0, "utf8.codes iterator")?; + let t_value = require_arg(vm, 1, "utf8.codes iterator")?; - let string_key = vm.create_string("string"); - let position_key = vm.create_string("position"); + let string_key = 1; + let position_key = 2; - let state_ref_cell = vm - .get_table(&t_value) - .ok_or(LuaError::RuntimeError("Invalid state table".to_string()))?; - let s_val = state_ref_cell - .borrow() - .raw_get(&string_key) - .ok_or_else(|| { - LuaError::RuntimeError("utf8.codes iterator: string not found".to_string()) - })?; - let s = unsafe { - s_val.as_string().ok_or_else(|| { - LuaError::RuntimeError("utf8.codes iterator: invalid string".to_string()) - })? + let Some(state_ref_cell) = t_value.as_lua_table() else { + return Err(vm.error("utf8.codes iterator: invalid state".to_string())); + }; + let Some(s_val) = state_ref_cell.borrow().get_int(string_key) else { + return Err(vm.error("utf8.codes iterator: string not found".to_string())); + }; + let Some(s) = s_val.as_lua_string() else { + return Err(vm.error("utf8.codes iterator: invalid string".to_string())); }; let pos = state_ref_cell .borrow() - .raw_get(&position_key) + .get_int(position_key) .and_then(|v| v.as_integer()) - .ok_or_else(|| { - LuaError::RuntimeError("utf8.codes iterator: position not found".to_string()) - })? as usize; + .unwrap_or(0) as usize; let bytes = s.as_str().as_bytes(); if pos >= bytes.len() { @@ -198,9 +185,10 @@ fn utf8_codes_iterator(vm: &mut LuaVM) -> LuaResult { let code_point = ch as u32; // Update position - state_ref_cell - .borrow_mut() - .raw_set(position_key, LuaValue::integer((pos + char_len) as i64)); + state_ref_cell.borrow_mut().raw_set( + LuaValue::integer(position_key), + LuaValue::integer((pos + char_len) as i64), + ); Ok(MultiValue::multiple(vec![ LuaValue::integer((pos + 1) as i64), // 1-based position @@ -213,14 +201,14 @@ fn utf8_codes_iterator(vm: &mut LuaVM) -> LuaResult { /// utf8.codepoint(s [, i [, j]]) - Returns code points of characters fn utf8_codepoint(vm: &mut LuaVM) -> LuaResult { - let s_value = require_arg(vm, 0, "utf8.codepoint")?; - let s = s_value.as_lua_string().ok_or_else(|| { - LuaError::RuntimeError("bad argument #1 to 'utf8.codepoint' (string expected)".to_string()) - })?; + let s_value = require_arg(vm, 1, "utf8.codepoint")?; + let Some(s) = s_value.as_lua_string() else { + return Err(vm.error("bad argument #1 to 'utf8.codepoint' (string expected)".to_string())); + }; - let i = get_arg(vm, 1).and_then(|v| v.as_integer()).unwrap_or(1) as usize; + let i = get_arg(vm, 2).and_then(|v| v.as_integer()).unwrap_or(1) as usize; - let j = get_arg(vm, 2) + let j = get_arg(vm, 3) .and_then(|v| v.as_integer()) .map(|v| v as usize) .unwrap_or(i); @@ -230,9 +218,7 @@ fn utf8_codepoint(vm: &mut LuaVM) -> LuaResult { let end_byte = if j > 0 { j } else { bytes.len() }; if start_byte >= bytes.len() { - return Err(LuaError::RuntimeError( - "bad argument #2 to 'utf8.codepoint' (out of range)".to_string(), - )); + return Err(vm.error("bad argument #2 to 'utf8.codepoint' (out of range)".to_string())); } let mut results = Vec::new(); @@ -253,17 +239,16 @@ fn utf8_codepoint(vm: &mut LuaVM) -> LuaResult { /// utf8.offset(s, n [, i]) - Returns byte position of n-th character fn utf8_offset(vm: &mut LuaVM) -> LuaResult { - let s_value = require_arg(vm, 0, "utf8.offset")?; - let s = s_value.as_lua_string().ok_or_else(|| { - LuaError::RuntimeError("bad argument #1 to 'utf8.offset' (string expected)".to_string()) - })?; - - let n = require_arg(vm, 1, "utf8.offset")? - .as_integer() - .ok_or_else(|| { - LuaError::RuntimeError("bad argument #2 to 'utf8.offset' (number expected)".to_string()) - })?; - let i = get_arg(vm, 2) + let s_value = require_arg(vm, 1, "utf8.offset")?; + let Some(s) = s_value.as_lua_string() else { + return Err(vm.error("bad argument #1 to 'utf8.offset' (string expected)".to_string())); + }; + + let n_value = require_arg(vm, 2, "utf8.offset")?; + let Some(n) = n_value.as_integer() else { + return Err(vm.error("bad argument #2 to 'utf8.offset' (number expected)".to_string())); + }; + let i = get_arg(vm, 3) .and_then(|v| v.as_integer()) .unwrap_or(if n >= 0 { 1 diff --git a/crates/luars/src/test/test_gc_metamethods.rs b/crates/luars/src/test/test_gc_metamethods.rs index bb88bf8d..187d409c 100644 --- a/crates/luars/src/test/test_gc_metamethods.rs +++ b/crates/luars/src/test/test_gc_metamethods.rs @@ -8,7 +8,7 @@ mod tests { fn test_gc_metamethod() { let mut vm = LuaVM::new(); vm.open_libs(); - + let code = r#" local finalized = {} @@ -33,7 +33,7 @@ mod tests { -- Check that finalizers were called return #finalized "#; - + match vm.execute_string(code) { Ok(result) => { println!("Finalized count: {:?}", result); @@ -50,7 +50,7 @@ mod tests { fn test_weak_keys_mode() { let mut vm = LuaVM::new(); vm.open_libs(); - + let code = r#" local weak_table = {} setmetatable(weak_table, {__mode = "k"}) @@ -79,7 +79,7 @@ mod tests { return count_before, count_after "#; - + match vm.execute_string(code) { Ok(result) => { println!("Weak keys test result: {:?}", result); @@ -93,7 +93,7 @@ mod tests { fn test_weak_values_mode() { let mut vm = LuaVM::new(); vm.open_libs(); - + let code = r#" local weak_table = {} setmetatable(weak_table, {__mode = "v"}) @@ -122,7 +122,7 @@ mod tests { return count_before, count_after "#; - + match vm.execute_string(code) { Ok(result) => { println!("Weak values test result: {:?}", result); @@ -136,7 +136,7 @@ mod tests { fn test_weak_keys_and_values_mode() { let mut vm = LuaVM::new(); vm.open_libs(); - + let code = r#" local weak_table = {} setmetatable(weak_table, {__mode = "kv"}) @@ -158,7 +158,7 @@ mod tests { return count "#; - + match vm.execute_string(code) { Ok(result) => { println!("Weak keys+values test result: {:?}", result); @@ -176,7 +176,7 @@ mod tests { fn test_gc_resurrection_prevention() { let mut vm = LuaVM::new(); vm.open_libs(); - + let code = r#" local resurrected = nil @@ -199,7 +199,7 @@ mod tests { -- In Lua 5.4, resurrection is allowed but object won't be finalized again return resurrected ~= nil "#; - + match vm.execute_string(code) { Ok(result) => { println!("Resurrection test result: {:?}", result); @@ -212,7 +212,7 @@ mod tests { fn test_finalizer_ordering() { let mut vm = LuaVM::new(); vm.open_libs(); - + let code = r#" local order = {} @@ -242,7 +242,7 @@ mod tests { return #order "#; - + match vm.execute_string(code) { Ok(result) => { println!("Finalizer ordering test result: {:?}", result); diff --git a/crates/luars_interpreter/src/bin/main.rs b/crates/luars_interpreter/src/bin/main.rs index 604119d5..04354e10 100644 --- a/crates/luars_interpreter/src/bin/main.rs +++ b/crates/luars_interpreter/src/bin/main.rs @@ -161,10 +161,11 @@ fn execute_file(vm: &mut LuaVM, filename: &str) -> Result<(), String> { match vm.compile(&code) { Ok(chunk) => { - vm.execute(Rc::new(chunk)).map_err(|e| format!("{}", e))?; + vm.execute(Rc::new(chunk)) + .map_err(|e| format!("{}: {}", e, vm.get_error_message()))?; Ok(()) } - Err(e) => Err(format!("{}: {}", filename, e)), + Err(e) => Err(format!("{}: {}: {}", filename, e, vm.get_error_message())), } }