diff --git a/src/chunk.rs b/src/chunk.rs new file mode 100755 index 0000000..f686602 --- /dev/null +++ b/src/chunk.rs @@ -0,0 +1,77 @@ +use std::borrow::{Borrow, Cow}; + +use crate::{AsLua, FromLua, Function, State}; + +pub struct Chunk<'a> { + state: &'a State, + name: String, + data: Cow<'a, [u8]>, + func: Function<'a>, +} + +impl<'a> Chunk<'a> { + pub(crate) fn new(state: &'a State, name: String, data: Cow<'a, [u8]>, func: Function<'a>) -> Self { + Self { + state, + name, + data, + func, + } + } + + /// Construct a Lua Chunk from bytes + /* pub fn from_bytes(name: &'a str, bytes: &'a [u8]) -> Self { + Self { + name, + data: bytes + } + } + + /// Construct a Lua Chunk from a string + pub fn from_str(name: &'a str, text: &'a str) -> Self { + Self { + name, + data: text.as_bytes(), + } + } */ + + /// Execute the chunk in the Lua context + pub fn execute(&'a self, args: A) -> crate::Result + where + A: AsLua<'a>, + R: FromLua<'a> + { + self.state.execute_chunk::(self, args) + } + + /// Returns the name of the Chunk + pub fn name(&self) -> &str { + &self.name + } + + /// Returns the data of the chunk + pub fn data(&self) -> &[u8] { + self.data.borrow() + } + + /// Returns a handle to the chunk's executable function + pub fn function(&self) -> Function { + self.func.clone() + } +} + +pub trait IntoChunkData<'a> { + fn into_chunk(self) -> Cow<'a, [u8]>; +} + +impl<'a> IntoChunkData<'a> for &'a str { + fn into_chunk(self) -> Cow<'a, [u8]> { + Cow::Borrowed(self.as_bytes()) + } +} + +impl<'a> IntoChunkData<'a> for &'a [u8] { + fn into_chunk(self) -> Cow<'a, [u8]> { + Cow::Borrowed(self) + } +} \ No newline at end of file diff --git a/src/function.rs b/src/function.rs index 2175155..eff8ee9 100755 --- a/src/function.rs +++ b/src/function.rs @@ -1,13 +1,14 @@ -use std::ffi::CStr; +use std::sync::Arc; -use crate::{AsLua, FromLuaStack, LuaRef, PushToLuaStack, StackGuard, State}; +use crate::{AsLua, FromLua, FromLuaStack, LuaRef, PushToLuaStack, StackGuard, State, Value, ValueVec}; use mlua_sys as lua; #[derive(Clone)] pub struct Function<'a> { state: &'a State, - lref: LuaRef + lref: LuaRef, + pub(crate) error_handler: Option>>, } impl<'a> FromLuaStack<'a> for Function<'a> { @@ -15,6 +16,7 @@ impl<'a> FromLuaStack<'a> for Function<'a> { Ok(Self { state, lref: LuaRef::from_stack(state)?, + error_handler: None, }) } } @@ -30,32 +32,66 @@ impl<'a> Function<'a> { Self { state, lref, + error_handler: None, } } pub fn exec(&self, args: A) -> crate::Result where - A: PushToLuaStackMulti<'a>, - R: FromLuaStackMulti<'a>, + A: AsLua<'a>, + R: FromLua<'a>, { unsafe { let _g = StackGuard::new(self.state); - - self.push_to_lua_stack(self.state)?; - args.push_args_to_lua_stack(self.state)?; - - let arg_len = args.len() as i32; - let res_len = R::len() as i32; let s = self.state.state_ptr(); - if lua::lua_pcall(s, arg_len, res_len, 0) != 0 { - let s = lua::lua_tostring(s, -1); - let s = CStr::from_ptr(s); - let s = s.to_str().unwrap(); + + let args_val = args.as_lua(self.state)?; + let args_len = match args_val { + Value::Variable(v) => v.len(), + Value::None => 0, + _ => 1, + } as _; - return Err(crate::Error::runtime(s)); + self.state.ensure_stack(2 + args_len)?; + + let handler_idx = match &self.error_handler { + Some(handler) => { + handler.push_to_lua_stack(self.state)?; + lua::lua_gettop(s) + }, + None => 0, + }; + + self.push_to_lua_stack(self.state)?; + + let args_val = args.as_lua(self.state)?; + args_val.push_to_lua_stack(self.state)?; + + match lua::lua_pcall(s, args_len, lua::LUA_MULTRET, handler_idx) { + lua::LUA_ERRRUN => { + let er = self.state.get_error_str(); + return Err(crate::Error::runtime(er)); + }, + lua::LUA_ERRMEM => { + return Err(crate::Error::MemoryAlloc); + }, + lua::LUA_ERRERR => { + let er = self.state.get_error_str(); + // if this panics, its a bug + panic!("Failure when trying to execute error handler function! ({})", er); + }, + _ => {} } + + let ret_count = lua::lua_gettop(s); + let val = if ret_count > 1 { + let vals = ValueVec::from_lua_stack(self.state)?; + Value::Variable(vals) + } else { + Value::from_lua_stack(self.state)? + }; - R::results_from_lua_stack(self.state) + R::from_lua(self.state, val) } } } @@ -64,129 +100,4 @@ impl<'a> AsLua<'a> for Function<'a> { fn as_lua(&self, _lua: &'a State) -> crate::Result> { Ok(crate::Value::Function(self.clone())) } -} - -pub trait PushToLuaStackMulti<'a> { - fn len(&self) -> usize; - fn push_args_to_lua_stack(&self, state: &'a State) -> crate::Result<()>; -} - -impl<'a, T> PushToLuaStackMulti<'a> for T -where - T: PushToLuaStack<'a>, -{ - fn len(&self) -> usize { - 1 - } - - fn push_args_to_lua_stack(&self, state: &'a State) -> crate::Result<()> { - unsafe { - self.push_to_lua_stack(state)?; - } - - Ok(()) - } -} - -pub trait FromLuaStackMulti<'a>: Sized { - fn len() -> usize; - fn results_from_lua_stack(state: &'a State) -> crate::Result; -} - -impl<'a> FromLuaStackMulti<'a> for () { - fn len() -> usize { - 1 - } - - fn results_from_lua_stack(_state: &'a State) -> crate::Result { - Ok(()) - } -} - -impl<'a, T: FromLuaStack<'a>> FromLuaStackMulti<'a> for T { - fn len() -> usize { - 1 - } - - fn results_from_lua_stack(state: &'a State) -> crate::Result { - unsafe { T::from_lua_stack(state) } - } -} - -macro_rules! impl_function_arg_tuple { - ( $count: expr, $first: tt, $( $name: tt ),+ ) => ( - #[allow(non_snake_case)] - impl<'a, $first: PushToLuaStack<'a>, $($name: PushToLuaStack<'a>,)+> PushToLuaStackMulti<'a> for ($first, $($name,)+) { - fn len(&self) -> usize { - // this will end up generating $count - 1 - 1 - 1... hopefully the compiler will - // optimize that out - $count - } - - fn push_args_to_lua_stack(&self, state: &'a State) -> crate::Result<()> { - let ($first, $($name,)+) = self; - - unsafe { - $first.push_to_lua_stack(state)?; - $( $name.push_to_lua_stack(state)?; )+ - } - - Ok(()) - } - } - - #[allow(non_snake_case)] - impl<'a, $first: FromLuaStack<'a>, $($name: FromLuaStack<'a>,)+> FromLuaStackMulti<'a> for ($first, $($name,)+) { - fn len() -> usize { - $count - } - - fn results_from_lua_stack(state: &'a State) -> crate::Result { - unsafe { - let ($( $name, )+ $first) = ( $( $name::from_lua_stack(state)?, )+ $first::from_lua_stack(state)? ); - - Ok( ($first, $( $name, )+) ) - } - //Ok(unsafe { ( $( $name::from_lua_stack(state)?, )+ $first::from_lua_stack(state)? ) }) - //Ok(unsafe { ( $first::from_lua_stack(state)?, $( $name::from_lua_stack(state)?, )+ ) }) - } - } - - impl_function_arg_tuple!( $count - 1, $( $name ),+ ); - ); - - // implements PushToLuaStackMulti and FromLuaStackMulti for a tuple with a single element - ( $count: expr, $only: tt ) => { - #[allow(non_snake_case)] - impl<'a, $only: PushToLuaStack<'a>> PushToLuaStackMulti<'a> for ($only,) { - fn len(&self) -> usize { - 1 - } - - fn push_args_to_lua_stack(&self, state: &'a State) -> crate::Result<()> { - let (a,) = self; - - unsafe { - a.push_to_lua_stack(state)?; - } - - Ok(()) - } - } - - impl<'a, $only: FromLuaStack<'a>> FromLuaStackMulti<'a> for ($only,) { - fn len() -> usize { - 1 - } - - fn results_from_lua_stack(state: &'a State) -> crate::Result { - Ok(unsafe { ( $only::from_lua_stack(state)?, ) }) - } - } - }; - -} - -// hopefully 16 is more than enough -// if you have 16 function results, and need more than 16, you NEED help -impl_function_arg_tuple! { 16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16 } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index f5cade0..9a44202 100755 --- a/src/main.rs +++ b/src/main.rs @@ -23,6 +23,9 @@ use userdata::*; pub mod util; use util::*; +pub mod chunk; +use chunk::*; + fn main() -> Result<()> { let lua = State::new(); lua.expose_libraries(&[StdLibrary::Debug, StdLibrary::Package]); @@ -37,8 +40,7 @@ fn main() -> Result<()> { let f = lua.create_function(a)?; globals.set("native_test", f)?; - //let ud = lua.create_userdata("Vec2", Vec2 { x: 0.0, y: 0.0})?; - let ud = lua.create_userdata("Vec2", UserdataProxy::::new())?; + let ud = lua.create_userdata(UserdataProxy::::new())?; globals.set("Vec2", ud)?; let tbl = lua.create_table()?; @@ -47,7 +49,7 @@ fn main() -> Result<()> { //let globals = lua.globals()?; globals.set("X", tbl)?; - let res = lua.execute(r#" + let chunk = lua.load("text.lua", r#" require "util" --[[function dump_table(tbl) @@ -85,6 +87,7 @@ fn main() -> Result<()> { cool_num = 50 + print("Lua is about to exec native_test") local res = native_test(50) print("Lua got " .. res .. " back from rust!") @@ -119,7 +122,13 @@ fn main() -> Result<()> { end f3(v1, v2) - "#); + "#)?; + + // I don't care about the result of this execution, so I set the result as a + // Value which can be anything + // + // Chunks can also be executed with: `chunk.execute(())?;` + let res = lua.execute_chunk::<_, Value>(&chunk, ()); // if unwrapped, the new lines in the message would be escaped making // the traceback in the error difficult to read. @@ -347,6 +356,7 @@ impl<'a> PushToLuaStack<'a> for &str { } } +#[allow(dead_code)] pub struct Vec3 { x: f32, y: f32, @@ -354,7 +364,7 @@ pub struct Vec3 { } impl Userdata for Vec3 { - fn build<'a>(builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()> { + fn build<'a>(_builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()> { todo!() } @@ -379,7 +389,7 @@ impl Userdata for Vec2 { .field_setter("y", |_, this, y: f32| this.y = y) .function("new", |lua, (x, y)| { - lua.create_userdata("Vec2", Vec2 { x, y, }) + lua.create_userdata(Vec2 { x, y, }) }) // method test @@ -390,7 +400,7 @@ impl Userdata for Vec2 { let rx = rhs.x; let ry = rhs.y; - lua.create_userdata("Vec2", Vec2 { x: lx + rx, y: ly + ry, }) + lua.create_userdata(Vec2 { x: lx + rx, y: ly + ry, }) }) .meta_method(MetaMethod::Add, |lua, lhs: &Vec2, (rhs,): (&Vec2,)| { @@ -400,7 +410,7 @@ impl Userdata for Vec2 { let rx = rhs.x; let ry = rhs.y; - lua.create_userdata("Vec2", Vec2 { x: lx + rx, y: ly + ry, }) + lua.create_userdata(Vec2 { x: lx + rx, y: ly + ry, }) }); Ok(()) diff --git a/src/state.rs b/src/state.rs index 475d787..609c8e8 100755 --- a/src/state.rs +++ b/src/state.rs @@ -1,9 +1,9 @@ use core::ffi; -use std::{alloc::{self, Layout}, any::TypeId, collections::{HashMap, VecDeque}, ffi::{CStr, CString}, mem, ptr::{self, NonNull}, str::Utf8Error}; +use std::{alloc::{self, Layout}, any::TypeId, collections::HashMap, ffi::{CStr, CString}, mem, ptr::{self, NonNull}, str::Utf8Error, sync::Arc}; use mlua_sys as lua; -use crate::{lua_error_guard, AnyUserdata, AsLua, Error, FromLuaStack, FromLuaVec, Function, LuaRef, MetaMethod, PushToLuaStack, PushToLuaStackMulti, Result, StackGuard, Table, Userdata, UserdataBuilder, Value, ValueVec}; +use crate::{lua_error_guard, AnyUserdata, IntoChunkData, AsLua, Chunk, Error, FromLua, FromLuaStack, FromLuaVec, Function, LuaRef, MetaMethod, PushToLuaStack, Result, StackGuard, Table, Userdata, UserdataBuilder, Value, ValueVec}; pub fn ptr_to_string(ptr: *const i8) -> std::result::Result { let c = unsafe { CStr::from_ptr(ptr) }; @@ -150,7 +150,8 @@ impl State { } } - unsafe fn get_error_str(&self) -> &str { + /// Returns the string of an error on the top of the stack + pub(crate) unsafe fn get_error_str(&self) -> &str { let error_c = CStr::from_ptr(lua::lua_tostring(self.state_ptr(), -1)); let error_str = error_c.to_str() .unwrap_or_else(|_| { @@ -167,18 +168,24 @@ impl State { self.traceback(Some(&msg)) } - pub fn execute(&self, text: &str) -> Result<()> { - let text = format!("{}\0", text); - + pub fn load<'a, C>(&'a self, name: &str, chunk: C) -> Result + where + C: IntoChunkData<'a> + 'a + { unsafe { - self.ensure_stack(3)?; + self.ensure_stack(1)?; let _g = StackGuard::new(self); + let s = self.lua.as_ptr(); - let lua = self.lua.as_ptr(); - let text_c = text.as_ptr().cast(); + let chunk_data = chunk.into_chunk(); + let chunk_ptr = chunk_data.as_ptr().cast(); + + // zero terminated name + let name_zero = format!("{}\0", name); + let name_zero = name_zero.as_ptr().cast(); // Subtract one from the length to exclude the null terminator - match lua::luaL_loadbuffer(self.state_ptr(), text_c, text.len() - 1, "test.lua\0".as_ptr().cast()) { + match lua::luaL_loadbuffer(s, chunk_ptr, chunk_data.len() - 1, name_zero) { lua::LUA_ERRSYNTAX => { return Err(Error::Syntax(self.get_error_str().to_string())); }, @@ -188,30 +195,25 @@ impl State { _ => {}, } - let handler = self.create_function(|lua, msg: String| { - lua.state_error_handler(msg) - })?; - handler.push_to_lua_stack(self)?; - lua::lua_insert(lua, -2); + let func = Function::from_lua_stack(self)?; - match lua::lua_pcall(lua, 0, lua::LUA_MULTRET, -2) { - lua::LUA_ERRRUN => { - let er = self.get_error_str(); - return Err(Error::runtime(er)); - }, - lua::LUA_ERRMEM => { - return Err(Error::MemoryAlloc); - }, - lua::LUA_ERRERR => { - let er = self.get_error_str(); - // if this panics, its a bug - panic!("Failure when trying to execute error handler function! ({})", er); - }, - _ => {} - } + Ok(Chunk::new(self, name.to_string(), chunk_data, func)) } + } - Ok(()) + pub fn execute_chunk<'a, A, R>(&'a self, chunk: &'a Chunk, args: A) -> Result + where + A: AsLua<'a>, + R: FromLua<'a> + { + let handler = self.create_function(|lua, msg: String| { + lua.state_error_handler(msg) + })?; + + let mut chunk_func = chunk.function(); + chunk_func.error_handler = Some(Arc::new(handler)); + + chunk_func.exec::(args) } pub fn expose_libraries>(&self, libs: L) { @@ -283,7 +285,7 @@ impl State { pub fn create_function<'a, A, R, F>(&self, f: F) -> Result where A: FromLuaVec<'a>, - R: PushToLuaStackMulti<'a>, + R: AsLua<'a>, //PushToLuaStackMulti<'a>, F: Fn(&'a State, A) -> Result + 'static, { unsafe extern "C-unwind" fn rust_closure(s: *mut lua::lua_State) -> i32 { @@ -332,11 +334,21 @@ impl State { lua_error_guard(lua, || { let vec = ValueVec::from_lua_stack(lua)?; + let args = A::from_lua_value_vec(lua, vec)?; let r = f(lua, args)?; - r.push_args_to_lua_stack(lua)?; - Ok(r.len() as i32) + let r_val = r.as_lua(lua)?; + + let args_len = match &r_val { + Value::Variable(v) => v.len(), + Value::None => 0, + _ => 1, + } as _; + + r_val.push_to_lua_stack(lua)?; + + Ok(args_len) }) } }; @@ -361,7 +373,7 @@ impl State { } /// Create userdata - pub fn create_userdata(&self, name: &str, data: T) -> Result { + pub fn create_userdata(&self, data: T) -> Result { unsafe { self.ensure_stack(2)?; let _g = StackGuard::new(self); diff --git a/src/value.rs b/src/value.rs index 98914f9..4258529 100755 --- a/src/value.rs +++ b/src/value.rs @@ -15,6 +15,7 @@ pub enum Value<'a> { Function(Function<'a>), Table(Table<'a>), Userdata(AnyUserdata<'a>), + Variable(ValueVec<'a>), } impl<'a> Value<'a> { @@ -31,6 +32,7 @@ impl<'a> Value<'a> { Value::Function(_) => "Function".to_string(), Value::Table(_) => "Table".to_string(), Value::Userdata(ud) => ud.name().unwrap(), + Value::Variable(_) => todo!(), } } @@ -79,9 +81,7 @@ impl<'a> PushToLuaStack<'a> for Value<'a> { let s = state.state_ptr(); match self { - Value::None => { - - }, + Value::None => { }, Value::Nil => { state.ensure_stack(1)?; lua::lua_pushnil(s); @@ -100,6 +100,11 @@ impl<'a> PushToLuaStack<'a> for Value<'a> { Value::Function(f) => f.push_to_lua_stack(state)?, Value::Table(t) => t.push_to_lua_stack(state)?, Value::Userdata(ud) => ud.push_to_lua_stack(state)?, + Value::Variable(v) => { + for v in v.iter() { + v.push_to_lua_stack(state)?; + } + } } Ok(()) @@ -108,10 +113,13 @@ impl<'a> PushToLuaStack<'a> for Value<'a> { impl<'a> FromLuaStack<'a> for Value<'a> { unsafe fn from_lua_stack(state: &'a State) -> crate::Result { - let s = state.state_ptr(); - let ty = lua::lua_type(s, -1); + if lua::lua_gettop(s) == 0 { + return Ok(Self::None); + } + + let ty = lua::lua_type(s, -1); let val = match ty { lua::LUA_TNIL => { lua::lua_pop(s, 1); @@ -129,8 +137,9 @@ impl<'a> FromLuaStack<'a> for Value<'a> { lua::lua_pop(s, 1); let cstr = CStr::from_ptr(cstr); - let lua_str = cstr.to_str().unwrap().to_string(); - Ok(Value::String(lua_str)) + let lua_str = cstr.to_string_lossy(); + //let lua_str = cstr.to_str().unwrap().to_string(); + Ok(Value::String(lua_str.to_string())) }, lua::LUA_TFUNCTION => { Function::from_lua_stack(state) @@ -166,7 +175,25 @@ pub trait FromLua<'a>: Sized { impl<'a> AsLua<'a> for () { fn as_lua(&self, _lua: &'a State) -> crate::Result> { - Ok(Value::Nil) + Ok(Value::None) + } +} + +impl<'a> AsLua<'a> for Value<'a> { + fn as_lua(&self, _lua: &'a State) -> crate::Result> { + Ok(self.clone()) + } +} + +impl<'a> AsLua<'a> for String { + fn as_lua(&self, _lua: &'a State) -> crate::Result> { + Ok(Value::String(self.clone())) + } +} + +impl<'a> AsLua<'a> for &'a str { + fn as_lua(&self, _lua: &'a State) -> crate::Result> { + Ok(Value::String(self.to_string())) } } @@ -176,7 +203,13 @@ impl<'a> FromLua<'a> for Value<'a> { } } -#[derive(Default)] +impl<'a> FromLua<'a> for () { + fn from_lua(_lua: &'a State, _val: Value<'a>) -> crate::Result { + Ok(()) + } +} + +#[derive(Default, Clone)] pub struct ValueVec<'a>(VecDeque>); impl<'a> Deref for ValueVec<'a> { @@ -238,11 +271,32 @@ pub trait FromLuaVec<'a>: Sized { fn from_lua_value_vec(state: &'a State, values: ValueVec<'a>) -> crate::Result; } -impl<'a> FromLuaVec<'a> for () { - fn from_lua_value_vec(_state: &'a State, _values: ValueVec<'a>) -> crate::Result { - Ok(()) +pub trait IntoLuaVec<'a>: Sized { + fn into_lua_value_vec(self, state: &'a State) -> crate::Result>; +} + +/* impl<'a> FromLuaVec<'a> for ValueVec<'a> { + fn from_lua_value_vec(_state: &'a State, values: ValueVec<'a>) -> crate::Result { + Ok(values) } } + */ +impl<'a> IntoLuaVec<'a> for ValueVec<'a> { + fn into_lua_value_vec(self, _state: &'a State) -> crate::Result> { + Ok(self) + } +} + +/* impl<'a> FromLua<'a> for ValueVec<'a> { + fn from_lua(_lua: &'a State, val: Value<'a>) -> crate::Result { + match val { + Value::Variable(v) => Ok(v), + _ => { + Ok(ValueVec::from(val)) + } + } + } +} */ impl<'a> FromLuaVec<'a> for ValueVec<'a> { fn from_lua_value_vec(_state: &'a State, values: ValueVec<'a>) -> crate::Result { @@ -267,7 +321,6 @@ macro_rules! impl_from_lua_vec_tuple { ( $count: expr, $first: tt, $( $name: tt ),+ ) => ( #[allow(non_snake_case)] impl<'a, $first: FromLua<'a>, $($name: FromLua<'a>,)+> FromLuaVec<'a> for ($first, $($name,)+) { - fn from_lua_value_vec(state: &'a State, mut values: ValueVec<'a>) -> crate::Result { if values.len() != $count { return Err(crate::Error::IncorrectArgCount { @@ -289,6 +342,27 @@ macro_rules! impl_from_lua_vec_tuple { } } + #[allow(non_snake_case)] + impl<'a, $first: AsLua<'a>, $($name: AsLua<'a>,)+> IntoLuaVec<'a> for ($first, $($name,)+) { + fn into_lua_value_vec(self, state: &'a State) -> crate::Result> { + let ($first, $($name,)+) = self; + let mut vals = ValueVec::new(); + + let v = $first.as_lua(state) + .map_err(|e| crate::Error::ValueVecError { value_idx: 0, error: std::sync::Arc::new(e) })?; + vals.push_back(v); + + let mut idx = 0; + $( + let v = $name.as_lua(state) + .map_err(|e| crate::Error::ValueVecError { value_idx: {idx += 1; idx}, error: std::sync::Arc::new(e) })?; + vals.push_back(v); + )+ + + Ok(vals) + } + } + impl_from_lua_vec_tuple!( $count - 1, $( $name ),+ ); ); @@ -308,6 +382,20 @@ macro_rules! impl_from_lua_vec_tuple { Ok( (o,) ) } } + + #[allow(non_snake_case)] + impl<'a, $only: AsLua<'a>> IntoLuaVec<'a> for ($only,) { + fn into_lua_value_vec(self, state: &'a State) -> crate::Result> { + let ($only,) = self; + let mut vals = ValueVec::new(); + + let v = $only.as_lua(state) + .map_err(|e| crate::Error::ValueVecError { value_idx: 0, error: std::sync::Arc::new(e) })?; + vals.push_back(v); + + Ok(vals) + } + } }; }