From 03e81f5553ef45bcae2e1ae78d4926bef617bfdd Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Tue, 6 Feb 2024 09:23:32 -0500 Subject: [PATCH] make it possible to provide references of rust types to lua --- src/chunk.rs | 4 +- src/function.rs | 21 +-- src/main.rs | 174 ++++++++++++----------- src/state.rs | 9 +- src/table.rs | 74 ++++++---- src/{userdata.rs => userdata/mod.rs} | 197 ++++++++++++++++++--------- src/userdata/unsafe_ud.rs | 180 ++++++++++++++++++++++++ src/value.rs | 38 +++++- 8 files changed, 503 insertions(+), 194 deletions(-) rename src/{userdata.rs => userdata/mod.rs} (69%) create mode 100755 src/userdata/unsafe_ud.rs diff --git a/src/chunk.rs b/src/chunk.rs index f686602..67cd57f 100755 --- a/src/chunk.rs +++ b/src/chunk.rs @@ -1,6 +1,6 @@ use std::borrow::{Borrow, Cow}; -use crate::{AsLua, FromLua, Function, State}; +use crate::{FromLua, Function, IntoLuaVec, State}; pub struct Chunk<'a> { state: &'a State, @@ -38,7 +38,7 @@ impl<'a> Chunk<'a> { /// Execute the chunk in the Lua context pub fn execute(&'a self, args: A) -> crate::Result where - A: AsLua<'a>, + A: IntoLuaVec<'a>, R: FromLua<'a> { self.state.execute_chunk::(self, args) diff --git a/src/function.rs b/src/function.rs index dbb5659..af74075 100755 --- a/src/function.rs +++ b/src/function.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use crate::{AsLua, FromLua, FromLuaStack, LuaRef, PushToLuaStack, StackGuard, State, Value, ValueVec}; +use crate::{AsLua, FromLua, FromLuaStack, IntoLuaVec, LuaRef, PushToLuaStack, StackGuard, State, Value, ValueVec}; use mlua_sys as lua; @@ -38,19 +38,15 @@ impl<'a> Function<'a> { pub fn exec(&self, args: A) -> crate::Result where - A: AsLua<'a>, + A: IntoLuaVec<'a>, R: FromLua<'a>, { unsafe { let _g = StackGuard::new(self.state); let s = self.state.state_ptr(); - let args_val = args.as_lua(self.state)?; - let args_len = match args_val { - Value::Variable(v) => v.len(), - Value::None => 0, - _ => 1, - } as _; + let args_val = args.into_lua_value_vec(self.state)?; + let args_len = args_val.len() as _; self.state.ensure_stack(2 + args_len)?; @@ -64,7 +60,8 @@ impl<'a> Function<'a> { self.push_to_lua_stack(self.state)?; - let args_val = args.as_lua(self.state)?; + /* let args_val = args.into_lua_value_vec(self.state)?; + args_val.push_to_lua_stack(self.state)?; */ args_val.push_to_lua_stack(self.state)?; match lua::lua_pcall(s, args_len, lua::LUA_MULTRET, handler_idx) { @@ -100,4 +97,10 @@ impl<'a> AsLua<'a> for Function<'a> { fn as_lua(&self, _lua: &'a State) -> crate::Result> { Ok(crate::Value::Function(self.clone())) } +} + +impl<'a> FromLua<'a> for Function<'a> { + fn from_lua(_lua: &'a State, val: Value<'a>) -> crate::Result { + val.into_function() + } } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index b170659..71c113a 100755 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use std::{cell::Ref, marker::PhantomData, sync::Arc}; +use std::{cell::{Ref, RefCell}, marker::PhantomData, mem, sync::Arc}; use mlua_sys as lua; @@ -43,100 +43,63 @@ fn main() -> Result<()> { let f = lua.create_function(a)?; globals.set("native_test", f)?; - let ud = lua.create_userdata(UserdataProxy::::new())?; - globals.set("Vec2", ud)?; + /* let ud = lua.create_userdata(UserdataProxy::::new())?; + globals.set("Vec2", ud)?; */ - let tbl = lua.create_table()?; - tbl.set("x", 10)?; + let cell = RefCell::new(Vec2 { + x: 50.0, + y: 5.0 + }); - //let globals = lua.globals()?; - globals.set("X", tbl)?; + { + let ud = lua.create_userdata(UserdataRef { + ud_ptr: unsafe { + mem::transmute::, Ref>(cell.borrow()) + }, + })?; + globals.set("vec2", ud)?; + } let chunk = lua.load("text.lua", r#" require "util" - --[[function dump_table(tbl) - for k, v in pairs(tbl) do - if type(v) == "table" then - dump_table(v) - elseif type(v) == "function" then - - else - print(k .. "=" .. tostring(v)) - end - - end + if vec2 ~= nil then + print("vec2: (" .. vec2.x .. ", " .. vec2.y .. ")") end - for k, v in pairs(_G) do - --print("Found global named " .. k) - - if k == "X" then - --dump_table(v) - end - end]]-- - - function multiply_print(a, b) - print(a .. " * " .. b .. " = " .. a*b) - end - - function multiply_ret(a, b) - return a * b - end - - function say_number(a) - print("Lua says " .. a) - end - - cool_num = 50 - - print("Lua is about to exec native_test") - local res = native_test(50) - print("Lua got " .. res .. " back from rust!") - - print("Vec2: " .. dump_table(Vec2)) - print("Meta Vec2: " .. dump_table(getmetatable(Vec2))) - - --print("Vec2 is (" .. Vec2.x .. ", " .. Vec2.y .. ")") - - local v1 = Vec2.new(50, 50) - print("v1 is (" .. v1.x .. ", " .. v1.y .. ")") - - local v2 = Vec2.new(500, 500) - print("v2 is (" .. v2.x .. ", " .. v2.y .. ")") - - local v_add = v1 + v2 - v_add.x = 90 - print("v_add is (" .. v_add.x .. ", " .. v_add.y .. ")") - - function f1(v1, v2) - --print("f1 tb: " .. debug.traceback(1)) - local v_add = v1:add(v2) - end - - function f2(v1, v2) - --print("f2 tb: " .. debug.traceback(1)) - f1(v1, v2) - end - - function f3(v1, v2) - --print("f3 tb: " .. debug.traceback(1)) - f2(v1, v2) - end - - --f3(v1, v2) + --print("vec2.x = " .. vec2.x) + --print("vec2.y = " .. vec2.y) "#)?; - // 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, ()); + for _ in 0..2 { + // 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. - if let Err(e) = res { - panic!("{}", e); + // if unwrapped, the new lines in the message would be escaped making + // the traceback in the error difficult to read. + if let Err(e) = res { + panic!("{}", e); + } + + let vec2 = globals.get::<_, AnyUserdata>("vec2")?; + vec2.garbage_collect()?; + globals.set("vec2", Value::Nil)?; + + let mut t = cell.borrow_mut(); + t.x += 50.0; + t.y += 5.0; + drop(t); + + let ud = lua.create_userdata(UserdataRef { + // TODO: avoid unsafe here + ud_ptr: unsafe { + mem::transmute::, Ref>(cell.borrow()) + }, + })?; + globals.raw_set("vec2", ud)?; } unsafe { @@ -310,7 +273,7 @@ pub struct Vec3 { } impl Userdata for Vec3 { - fn build<'a>(_builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()> { + fn build<'a>(_state: &State, _builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()> { todo!() } @@ -325,7 +288,7 @@ pub struct Vec2 { } impl Userdata for Vec2 { - fn build<'a>(builder: &mut UserdataBuilder<'a, Vec2>) -> crate::Result<()> { + fn build<'a>(_state: &State, builder: &mut UserdataBuilder<'a, Vec2>) -> crate::Result<()> { builder .field_getter("x", |_, this| Ok(this.x)) .field_getter("y", |_, this| Ok(this.y)) @@ -381,9 +344,9 @@ impl<'a, T: Userdata> AsLua<'a> for UserdataProxy { } impl Userdata for UserdataProxy { - fn build<'a>(builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()> { + fn build<'a>(state: &State, builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()> { let mut other = UserdataBuilder::::new(); - T::build(&mut other)?; + T::build(state, &mut other)?; // only the functions need to be added since they're the only thing usable from a proxy builder.functions = other.functions; @@ -395,4 +358,39 @@ impl Userdata for UserdataProxy { let name = format!("{}Proxy", T::name()); name } +} + +pub struct UserdataRef<'a, T: Userdata> { + ud_ptr: Ref<'a, T> +} + +impl<'a, T: Userdata + 'static> Userdata for UserdataRef<'a, T> { + fn build<'b>(state: &State, builder: &mut UserdataBuilder<'b, Self>) -> crate::Result<()> { + let mut other = UserdataBuilder::::new(); + T::build(state, &mut other)?; + + builder.expand_with(other); + + let getter: fn(AnyUserdata<'_>) -> Result<*const ()> = move |ud: AnyUserdata| { + let ud_ptr = { + let ud = ud.as_ref::>()?; + + let ud_ptr: *const T = &*ud.ud_ptr; + ud_ptr + }; + + Ok(ud_ptr.cast::<()>()) + }; + + if builder.wrapped_getter.set(Box::new(getter)).is_err() { + panic!("Somehow the wrapped getter has already been set"); + } + + Ok(()) + } + + fn name() -> String { + let name = format!("{}Ref", T::name()); + name + } } \ No newline at end of file diff --git a/src/state.rs b/src/state.rs index a05aff4..e689c23 100755 --- a/src/state.rs +++ b/src/state.rs @@ -3,7 +3,7 @@ use std::{alloc::{self, Layout}, any::TypeId, cell::RefCell, collections::HashMa use mlua_sys as lua; -use crate::{lua_error_guard, AnyUserdata, IntoChunkData, AsLua, Chunk, Error, FromLua, FromLuaStack, FromLuaVec, Function, LuaRef, MetaMethod, PushToLuaStack, Result, StackGuard, Table, Userdata, UserdataBuilder, Value, ValueVec}; +use crate::{lua_error_guard, AnyUserdata, AsLua, Chunk, Error, FromLua, FromLuaStack, FromLuaVec, Function, IntoChunkData, IntoLuaVec, 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) }; @@ -212,7 +212,7 @@ impl State { pub fn execute_chunk<'a, A, R>(&'a self, chunk: &'a Chunk, args: A) -> Result where - A: AsLua<'a>, + A: IntoLuaVec<'a>, R: FromLua<'a> { let handler = self.create_function(|lua, msg: String| { @@ -431,7 +431,7 @@ impl State { pub(crate) fn create_userdata_metatable<'a, T: Userdata + 'static>(&'a self) -> Result> { let mut builder = UserdataBuilder::::new(); - T::build(&mut builder)?; + T::build(self, &mut builder)?; let getters = builder.field_getters; let setters = builder.field_setters; @@ -502,10 +502,11 @@ impl State { let ud_ptr = ud.as_ptr_unchecked::().unwrap(); ud_ptr.drop_in_place(); - lua::luaL_unref(lua.state_ptr(), lua::LUA_REGISTRYINDEX, *ud.lref.0); + lua::luaL_unref(lua.state_ptr(), lua::LUA_REGISTRYINDEX, *ud.unsafe_ud.lref.0); let extra = lua.get_extra_space(); extra.userdata_metatables.remove(&TypeId::of::()); + //todo!() } Ok(()) diff --git a/src/table.rs b/src/table.rs index ceb810b..ffd592d 100755 --- a/src/table.rs +++ b/src/table.rs @@ -1,7 +1,7 @@ use mlua_sys as lua; -use crate::{ensure_type, FromLuaStack, LuaRef, PushToLuaStack, Result, StackGuard, State}; +use crate::{ensure_type, AsLua, FromLua, FromLuaStack, LuaRef, PushToLuaStack, Result, StackGuard, State, Value}; #[derive(Clone)] pub struct Table<'a> { @@ -83,8 +83,8 @@ impl<'a> Table<'a> { /// This may trigger the `__newindex` metamethod, see [`Table::raw_set`] pub fn set(&self, key: K, val: V) -> Result<()> where - K: PushToLuaStack<'a>, - V: PushToLuaStack<'a> + K: AsLua<'a>, + V: AsLua<'a> { let s = self.state.state_ptr(); unsafe { @@ -97,8 +97,10 @@ impl<'a> Table<'a> { } self.lref.push_to_lua_stack(self.state)?; - key.push_to_lua_stack(self.state)?; - val.push_to_lua_stack(self.state)?; + key.as_lua(self.state)? + .push_to_lua_stack(self.state)?; + val.as_lua(self.state)? + .push_to_lua_stack(self.state)?; lua::lua_settable(s, -3); } @@ -111,8 +113,8 @@ impl<'a> Table<'a> { /// This may trigger the `__index` metamethod, see [`Table::raw_get`] pub fn get(&self, key: K) -> Result where - K: PushToLuaStack<'a>, - V: FromLuaStack<'a>, + K: AsLua<'a>, + V: FromLua<'a>, { let s = self.state.state_ptr(); unsafe { @@ -120,10 +122,13 @@ impl<'a> Table<'a> { let _g = StackGuard::new(self.state); self.lref.push_to_lua_stack(self.state)?; - key.push_to_lua_stack(self.state)?; + let val = key.as_lua(self.state)?; + val.push_to_lua_stack(self.state)?; + lua::lua_gettable(s, -2); // table[key] is at top of stack - let val = V::from_lua_stack(self.state)?; + let val = Value::from_lua_stack(self.state)?; + let val = V::from_lua(self.state, val)?; Ok(val) } @@ -150,7 +155,7 @@ impl<'a> Table<'a> { /// Returns a boolean indicating if this table has a key pub fn has_key(&self, key: K) -> Result where - K: PushToLuaStack<'a>, + K: AsLua<'a>, { let s = self.state.state_ptr(); unsafe { @@ -158,7 +163,8 @@ impl<'a> Table<'a> { let _g = StackGuard::new(self.state); self.lref.push_to_lua_stack(self.state)?; - key.push_to_lua_stack(self.state)?; + let key_val = key.as_lua(self.state)?; + key_val.push_to_lua_stack(self.state)?; // table[key] is at top of stack if lua::lua_gettable(s, -2) == lua::LUA_TNIL { @@ -172,8 +178,8 @@ impl<'a> Table<'a> { /// Set a key to a value in the table without calling any meta methods. pub fn raw_set(&self, key: K, val: V) -> Result<()> where - K: PushToLuaStack<'a>, - V: PushToLuaStack<'a> + K: AsLua<'a>, + V: AsLua<'a> { let s = self.state.state_ptr(); unsafe { @@ -181,8 +187,10 @@ impl<'a> Table<'a> { let _g = StackGuard::new(self.state); self.lref.push_to_lua_stack(self.state)?; - key.push_to_lua_stack(self.state)?; - val.push_to_lua_stack(self.state)?; + key.as_lua(self.state)? + .push_to_lua_stack(self.state)?; + val.as_lua(self.state)? + .push_to_lua_stack(self.state)?; lua::lua_rawset(s, -3); } @@ -191,10 +199,10 @@ impl<'a> Table<'a> { } /// Get a value from the table without calling any meta methods. - pub fn raw_get(&'a self, key: K) -> Result + pub fn raw_get(&self, key: K) -> Result where - K: PushToLuaStack<'a>, - V: FromLuaStack<'a>, + K: AsLua<'a>, + V: FromLua<'a>, { let s = self.state.state_ptr(); unsafe { @@ -202,9 +210,13 @@ impl<'a> Table<'a> { let _g = StackGuard::new(self.state); self.lref.push_to_lua_stack(self.state)?; - key.push_to_lua_stack(self.state)?; + key.as_lua(self.state)? + .push_to_lua_stack(self.state)?; + lua::lua_rawget(s, -2); // table[key] is at top of stack - V::from_lua_stack(self.state) + + let val = Value::from_lua_stack(self.state)?; + V::from_lua(self.state, val) } } @@ -226,7 +238,7 @@ impl<'a> Table<'a> { /// Returns a boolean indicating if this table has a key without calling any meta methods. pub fn raw_has_key(&self, key: K) -> Result where - K: PushToLuaStack<'a>, + K: AsLua<'a>, { let s = self.state.state_ptr(); unsafe { @@ -234,7 +246,9 @@ impl<'a> Table<'a> { let _g = StackGuard::new(self.state); self.lref.push_to_lua_stack(self.state)?; - key.push_to_lua_stack(self.state)?; + key.as_lua(self.state)? + .push_to_lua_stack(self.state)?; + // table[key] is at top of stack Ok(lua::lua_rawget(s, -2) != lua::LUA_TNIL) } @@ -245,8 +259,8 @@ impl<'a> Table<'a> { /// Does nothing if this table is not a metatable pub fn set_meta(&self, key: K, val: V) -> Result<()> where - K: PushToLuaStack<'a>, - V: PushToLuaStack<'a> + K: AsLua<'a>, + V: AsLua<'a> { if self.mt_marker { unsafe { @@ -255,8 +269,10 @@ impl<'a> Table<'a> { let _g = StackGuard::new(self.state); self.lref.push_to_lua_stack(self.state)?; - key.push_to_lua_stack(self.state)?; - val.push_to_lua_stack(self.state)?; + key.as_lua(self.state)? + .push_to_lua_stack(self.state)?; + val.as_lua(self.state)? + .push_to_lua_stack(self.state)?; lua::lua_settable(s, -3); } } @@ -279,4 +295,10 @@ impl<'a> FromLuaStack<'a> for Table<'a> { ensure_type(state, lua::LUA_TTABLE, -1)?; Table::with_ref(state, LuaRef::from_stack(state)?, false) } +} + +impl<'a> FromLua<'a> for Table<'a> { + fn from_lua(_lua: &'a State, val: Value<'a>) -> crate::Result { + val.into_table() + } } \ No newline at end of file diff --git a/src/userdata.rs b/src/userdata/mod.rs similarity index 69% rename from src/userdata.rs rename to src/userdata/mod.rs index c71fbe0..30bbbcb 100755 --- a/src/userdata.rs +++ b/src/userdata/mod.rs @@ -1,9 +1,12 @@ -use std::{cell::{Ref, RefCell, RefMut}, collections::HashMap, ffi::CStr, marker::PhantomData, ops::DerefMut}; +use std::{cell::{OnceCell, Ref, RefCell, RefMut}, collections::HashMap, ffi::CStr, marker::PhantomData, mem, ops::{Deref, DerefMut}, sync::Arc}; -use crate::{ensure_type, AsLua, FromLua, FromLuaStack, FromLuaVec, LuaRef, PushToLuaStack, StackGuard, State, Value, ValueVec}; +use crate::{ensure_type, AsLua, FromLua, FromLuaStack, FromLuaVec, Function, IntoLuaVec, LuaRef, PushToLuaStack, StackGuard, State, Table, Value, ValueVec}; use mlua_sys as lua; +pub mod unsafe_ud; +pub use unsafe_ud::*; + /// An enum representing all Lua MetaMethods /// https://gist.github.com/oatmealine/655c9e64599d0f0dd47687c1186de99f pub enum MetaMethod { @@ -89,7 +92,7 @@ impl<'a> PushToLuaStack<'a> for MetaMethod { } } -type UserdataFn<'a> = Box) -> crate::Result>>; +pub type UserdataFn<'a> = Box) -> crate::Result>>; pub struct UserdataBuilder<'a, T> { pub(crate) name: String, @@ -97,6 +100,9 @@ pub struct UserdataBuilder<'a, T> { pub(crate) field_setters: HashMap>, pub(crate) functions: HashMap>, pub(crate) meta_methods: HashMap>, + + pub(crate) wrapped_getter: Arc) -> crate::Result<*const ()>>>>, + _marker: PhantomData, } @@ -108,6 +114,7 @@ impl<'a, T: Userdata> UserdataBuilder<'a, T> { field_setters: HashMap::new(), functions: HashMap::new(), meta_methods: HashMap::new(), + wrapped_getter: Arc::new(OnceCell::new()), _marker: PhantomData, } } @@ -118,11 +125,34 @@ impl<'a, T: Userdata> UserdataBuilder<'a, T> { R: AsLua<'a>, T: Userdata + 'static { + let wrapped = self.wrapped_getter.clone(); + let wrapped: Arc) -> Result<*const (), crate::Error>>>> = unsafe { mem::transmute(wrapped) }; + let fn_name = Arc::new(name.to_string()); + let wrap = move |lua: &'a State, mut val: ValueVec<'a>| { let val = val.pop_front().unwrap(); let this = val.as_userdata().unwrap(); // if this panics, its a bug - let this = this.as_ref::()?; - f(lua, &*this).and_then(|r| r.as_lua(lua)) + + if let Some(getter) = wrapped.get() { + let this_ptr = match getter(this.clone()) { + Ok(v) => v, + Err(e) => { + return Err(crate::Error::BadArgument { + func: Some(fn_name.deref().clone()), + arg_index: 1, + arg_name: Some("self".to_string()), + error: Arc::new(e), + }); + } + }; + let this_ptr = this_ptr.cast::(); + let this = unsafe { &*this_ptr }; + + f(lua, this).and_then(|r| r.as_lua(lua)) + } else { + let this = this.as_ref::()?; + f(lua, &*this).and_then(|r| r.as_lua(lua)) + } }; self.field_getters.insert(name.to_string(), Box::new(wrap)); @@ -192,6 +222,7 @@ impl<'a, T: Userdata> UserdataBuilder<'a, T> { let wrap = move |lua: &'a State, mut val: ValueVec<'a>| { let this_val = val.pop_front().unwrap(); let this = this_val.as_userdata().unwrap(); // if this panics, its a bug + //this.unsafe_ud.as_ptr_unchecked() let this = this.as_ref::()?; let this_name = T::name(); @@ -233,78 +264,60 @@ impl<'a, T: Userdata> UserdataBuilder<'a, T> { self } + + //fn append_fields_from(&mut self, other: UserDataRegistry<'lua, S>) { + pub fn expand_with(&mut self, other: UserdataBuilder<'a, U>) { + self.field_getters = other.field_getters; + self.field_setters = other.field_setters; + self.functions = other.functions; + self.meta_methods = other.meta_methods; + self.wrapped_getter = other.wrapped_getter; + } } pub trait Userdata: Sized { fn name() -> String; - fn build<'a>(builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()>; + fn build<'a>(state: &State, builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()>; } /// A handle to some userdata on the stack -#[derive(Clone)] +//#[derive(Clone)] pub struct AnyUserdata<'a> { - pub(crate) lref: LuaRef<'a>, + pub(crate) unsafe_ud: UnsafeUserdata<'a>, state: &'a State, } +impl<'a> Clone for AnyUserdata<'a> { + fn clone(&self) -> Self { + Self { + state: self.state, + unsafe_ud: self.unsafe_ud.clone() + } + } +} + impl<'a> AnyUserdata<'a> { pub fn from_ref(state: &'a State, lref: LuaRef<'a>) -> Self { Self { - lref, state, + unsafe_ud: UnsafeUserdata::from_ref(state, lref) } } /// Returns a borrow to the userdata. pub fn as_ref(&self) -> crate::Result> { unsafe { - self.state.ensure_stack(3)?; - let _g = StackGuard::new(self.state); - let s = self.state.state_ptr(); - - self.lref.push_to_lua_stack(self.state)?; - - if lua::lua_getmetatable(s, -1) == 0 { - return Err(crate::Error::UserdataMismatch); - } - - self.state.get_userdata_metatable::() - .push_to_lua_stack(self.state)?; - - if lua::lua_rawequal(s, -2, -1) == 1 { - let cptr = lua::lua_touserdata(s, -3); - let cell = &*cptr.cast::>(); - Ok(cell.borrow()) - } else { - return Err(crate::Error::UserdataMismatch); - } + let cell = &*self.unsafe_ud.as_ptr_unchecked::>()?; + Ok(cell.borrow()) } } /// Returns a mutable reference to the userdata. pub fn as_mut(&self) -> crate::Result> { unsafe { - self.state.ensure_stack(3)?; - let _g = StackGuard::new(self.state); - let s = self.state.state_ptr(); - - self.lref.push_to_lua_stack(self.state)?; - - if lua::lua_getmetatable(s, -1) == 0 { - return Err(crate::Error::MissingMetatable); - } - - self.state.get_userdata_metatable::() - .push_to_lua_stack(self.state)?; - - if lua::lua_rawequal(s, -2, -1) == 1 { - let cptr = lua::lua_touserdata(s, -3); - let cell = &*cptr.cast::>(); - Ok(cell.borrow_mut()) - } else { - Err(crate::Error::UserdataMismatch) - } + let cell = &*self.unsafe_ud.as_ptr_unchecked::>()?; + Ok(cell.borrow_mut()) } } @@ -317,14 +330,8 @@ impl<'a> AnyUserdata<'a> { /// If there is a possibility that these types do not match, use [`AnyUserdata::as_ptr`] /// which does verify the types. pub unsafe fn as_ptr_unchecked(&self) -> crate::Result<*mut RefCell> { - self.state.ensure_stack(1)?; - let _g = StackGuard::new(self.state); - let s = self.state.state_ptr(); - - self.lref.push_to_lua_stack(self.state)?; - let cptr = lua::lua_touserdata(s, -3); - - Ok(cptr.cast()) + let cell = self.unsafe_ud.as_ptr_unchecked::>()?; + Ok(cell) } /// Returns a mutable pointer of the [`RefCell`] storing the userdata. @@ -335,7 +342,7 @@ impl<'a> AnyUserdata<'a> { let _g = StackGuard::new(self.state); let s = self.state.state_ptr(); - self.lref.push_to_lua_stack(self.state)?; + self.unsafe_ud.lref.push_to_lua_stack(self.state)?; if lua::lua_getmetatable(s, -1) == 0 { return Err(crate::Error::MissingMetatable); @@ -345,8 +352,9 @@ impl<'a> AnyUserdata<'a> { .push_to_lua_stack(self.state)?; if lua::lua_rawequal(s, -2, -1) == 1 { - let cptr = lua::lua_touserdata(s, -3); - Ok(cptr.cast()) + drop(_g); + let cell = self.unsafe_ud.as_ptr_unchecked::>()?; + Ok(cell) } else { Err(crate::Error::UserdataMismatch) } @@ -358,7 +366,7 @@ impl<'a> AnyUserdata<'a> { self.state.ensure_stack(3)?; let _g = StackGuard::new(self.state); - self.lref.push_to_lua_stack(self.state)?; + self.unsafe_ud.lref.push_to_lua_stack(self.state)?; let s = self.state.state_ptr(); if lua::lua_getmetatable(s, -1) == 0 { @@ -378,6 +386,69 @@ impl<'a> AnyUserdata<'a> { Ok(cstr.to_string()) } } + + /// Returns the metatable of this userdata + pub fn get_metatable(&self) -> crate::Result> { + unsafe { + self.state.ensure_stack(2)?; + let _g = StackGuard::new(self.state); + + self.unsafe_ud.lref.push_to_lua_stack(self.state)?; + let s = self.state.state_ptr(); + + if lua::lua_getmetatable(s, -1) == 0 { + Err(crate::Error::MissingMetatable) + } else { + Ok(Table::from_lua_stack(self.state)?) + } + } + } + + /// Gets something from the userdata. + /// + /// Will trigger a call to the `__index` metamethod. Use [`AnyUserdata::raw_get`] if you + /// don't want to call it. + pub fn get(&'a self, key: K) -> crate::Result + where + K: AsLua<'a>, + V: FromLua<'a>, + { + let mt = self.get_metatable()?; + mt.get::(key) + } + + /// Gets something from the userdata. Will **not** trigger a call to the `__index` metamethod. + pub fn raw_get(&'a self, key: K) -> crate::Result + where + K: AsLua<'a>, + V: FromLua<'a>, + { + let mt = self.get_metatable()?; + mt.raw_get::(key) + } + + /// Execute a method on this userdata. This finds a function with `name` and executes it + /// with the userdata as the first argument. + pub fn execute_method(&self, name: &str, args: A) -> crate::Result + where + A: IntoLuaVec<'a>, + R: FromLua<'a>, + { + //let s: &Self = unsafe { mem::transmute(self) }; + let name = name.to_string(); + let mt = self.get_metatable()?; + let f = mt.get::<_, Function>(name)?; + + let mut args = args.into_lua_value_vec(self.state)?; + args.push_front(self.clone().as_lua(self.state)?); + + f.exec(args) + } + + /// Trigger garbage collection on the userdata. + pub fn garbage_collect(self) -> crate::Result<()> { + self.execute_method("__gc", ()).into() + } } impl<'a> FromLuaStack<'a> for AnyUserdata<'a> { @@ -385,15 +456,15 @@ impl<'a> FromLuaStack<'a> for AnyUserdata<'a> { ensure_type(state, lua::LUA_TUSERDATA, -1)?; Ok(AnyUserdata { - lref: LuaRef::from_stack(state)?, state, + unsafe_ud: UnsafeUserdata::from_lua_stack(state)?, }) } } impl<'a> PushToLuaStack<'a> for AnyUserdata<'a> { unsafe fn push_to_lua_stack(&self, state: &'a State) -> crate::Result<()> { - self.lref.push_to_lua_stack(state) + self.unsafe_ud.lref.push_to_lua_stack(state) } } diff --git a/src/userdata/unsafe_ud.rs b/src/userdata/unsafe_ud.rs new file mode 100755 index 0000000..e067bd6 --- /dev/null +++ b/src/userdata/unsafe_ud.rs @@ -0,0 +1,180 @@ +use std::ffi::CStr; + +use crate::{ensure_type, FromLuaStack, LuaRef, PushToLuaStack, StackGuard, State, Userdata}; + +use mlua_sys as lua; + +pub struct UnsafeUserdata<'a> { + pub(crate) lref: LuaRef<'a>, + state: &'a State, +} + +impl<'a> Clone for UnsafeUserdata<'a> { + fn clone(&self) -> Self { + Self { + lref: self.lref.clone(), + state: self.state, + } + } +} + +impl<'a> UnsafeUserdata<'a> { + pub fn from_ref(state: &'a State, lref: LuaRef<'a>) -> Self { + Self { + lref, + state, + } + } + + /// Returns a borrow to the userdata. + pub fn as_ref(&self) -> crate::Result<&'a T> { + unsafe { + self.state.ensure_stack(3)?; + let _g = StackGuard::new(self.state); + let s = self.state.state_ptr(); + + self.lref.push_to_lua_stack(self.state)?; + + if lua::lua_getmetatable(s, -1) == 0 { + return Err(crate::Error::UserdataMismatch); + } + + self.state.get_userdata_metatable::() + .push_to_lua_stack(self.state)?; + + if lua::lua_rawequal(s, -2, -1) == 1 { + let cptr = lua::lua_touserdata(s, -3); + let t = &*cptr.cast::(); + Ok(t) + } else { + return Err(crate::Error::UserdataMismatch); + } + } + } + + /// Returns a mutable reference to the userdata. + pub fn as_mut(&self) -> crate::Result<&'a mut T> { + unsafe { + self.state.ensure_stack(3)?; + let _g = StackGuard::new(self.state); + let s = self.state.state_ptr(); + + self.lref.push_to_lua_stack(self.state)?; + + if lua::lua_getmetatable(s, -1) == 0 { + return Err(crate::Error::MissingMetatable); + } + + self.state.get_userdata_metatable::() + .push_to_lua_stack(self.state)?; + + if lua::lua_rawequal(s, -2, -1) == 1 { + let cptr = lua::lua_touserdata(s, -3); + let t = &mut *cptr.cast::(); + Ok(t) + } else { + Err(crate::Error::UserdataMismatch) + } + } + } + + /// Returns a mutable pointer of the [`RefCell`] of userdata **WITHOUT verifying the type of it**. + /// + /// # Safety + /// * You must be certain that the type `T` is the same type that this userdata has a handle to. + /// There is a blind cast from `void*` to `T*` + /// + /// If there is a possibility that these types do not match, use [`UnsafeUserdata::as_ptr`] + /// which does verify the types. + pub unsafe fn as_ptr_unchecked(&self) -> crate::Result<*mut T> { + self.state.ensure_stack(1)?; + let _g = StackGuard::new(self.state); + let s = self.state.state_ptr(); + + self.lref.push_to_lua_stack(self.state)?; + let cptr = lua::lua_touserdata(s, -1); + + Ok(cptr.cast()) + } + + /// Returns a mutable pointer of the data stored. + /// + /// This function ensures that the type of the userdata this struct has a handle to is the + /// same as `T`. If it isn't, a `UserdataMismatch` error will be returned. + pub unsafe fn as_ptr(&self) -> crate::Result<*mut T> { + let _g = StackGuard::new(self.state); + let s = self.state.state_ptr(); + + self.lref.push_to_lua_stack(self.state)?; + + if lua::lua_getmetatable(s, -1) == 0 { + return Err(crate::Error::MissingMetatable); + } + + self.state.get_userdata_metatable::() + .push_to_lua_stack(self.state)?; + + if lua::lua_rawequal(s, -2, -1) == 1 { + let cptr = lua::lua_touserdata(s, -3); + Ok(cptr.cast()) + } else { + Err(crate::Error::UserdataMismatch) + } + } + + /// Returns the name of the userdata by accessing the metatable + pub fn name(&self) -> crate::Result { + unsafe { + self.state.ensure_stack(3)?; + let _g = StackGuard::new(self.state); + + self.lref.push_to_lua_stack(self.state)?; + let s = self.state.state_ptr(); + + if lua::lua_getmetatable(s, -1) == 0 { + return Err(crate::Error::MissingMetatable); + } + + lua::lua_pushliteral(s, "__name"); + lua::lua_rawget(s, -2); + + ensure_type(self.state, lua::LUA_TSTRING, -1)?; + + let cstr = CStr::from_ptr(lua::lua_tostring(s, -1)); + let cstr = cstr.to_str() + // on panic, this should be considered a bug + .expect("Metatable name has invalid utf8 bytes!"); + + Ok(cstr.to_string()) + } + } +} + +impl<'a> FromLuaStack<'a> for UnsafeUserdata<'a> { + unsafe fn from_lua_stack(state: &'a State) -> crate::Result { + ensure_type(state, lua::LUA_TUSERDATA, -1)?; + + Ok(UnsafeUserdata { + lref: LuaRef::from_stack(state)?, + state, + }) + } +} + +impl<'a> PushToLuaStack<'a> for UnsafeUserdata<'a> { + unsafe fn push_to_lua_stack(&self, state: &'a State) -> crate::Result<()> { + self.lref.push_to_lua_stack(state) + } +} + +/* impl<'a> AsLua<'a> for UnsafeUserdata<'a> { + fn as_lua(&self, _lua: &'a State) -> crate::Result> { + Ok(Value::Userdata(self.clone())) + } +} + +impl<'a> FromLua<'a> for UnsafeUserdata<'a> { + fn from_lua(_lua: &State, val: Value<'a>) -> crate::Result { + val.into_userdata() + } +} */ \ No newline at end of file diff --git a/src/value.rs b/src/value.rs index 4258529..6d97ba8 100755 --- a/src/value.rs +++ b/src/value.rs @@ -36,7 +36,7 @@ impl<'a> Value<'a> { } } - pub fn as_userdata(&self) -> crate::Result<&AnyUserdata> { + pub fn as_userdata(&self) -> crate::Result<&AnyUserdata<'a>> { match self { Value::Userdata(ud) => Ok(ud), _ => { @@ -74,10 +74,28 @@ impl<'a> Value<'a> { } } } + + pub fn into_table(self) -> crate::Result> { + match self { + Value::Table(v) => Ok(v), + _ => { + Err(crate::Error::UnexpectedType("Table".to_string(), self.type_name().to_string())) + } + } + } + + pub fn into_function(self) -> crate::Result> { + match self { + Value::Function(v) => Ok(v), + _ => { + Err(crate::Error::UnexpectedType("Function".to_string(), self.type_name().to_string())) + } + } + } } impl<'a> PushToLuaStack<'a> for Value<'a> { - unsafe fn push_to_lua_stack(&self, state: &crate::State) -> crate::Result<()> { + unsafe fn push_to_lua_stack(&self, state: &'a crate::State) -> crate::Result<()> { let s = state.state_ptr(); match self { @@ -304,6 +322,16 @@ impl<'a> FromLuaVec<'a> for ValueVec<'a> { } } +impl<'a> PushToLuaStack<'a> for ValueVec<'a> { + unsafe fn push_to_lua_stack(&self, state: &'a State) -> crate::Result<()> { + for v in self.iter() { + v.push_to_lua_stack(state)?; + } + + Ok(()) + } +} + impl<'a, T: FromLua<'a>> FromLuaVec<'a> for T { fn from_lua_value_vec(state: &'a State, mut values: ValueVec<'a>) -> crate::Result { if let Some(val) = values.pop_front() { @@ -317,6 +345,12 @@ impl<'a, T: FromLua<'a>> FromLuaVec<'a> for T { } } +impl<'a> IntoLuaVec<'a> for () { + fn into_lua_value_vec(self, _state: &'a State) -> crate::Result> { + Ok(ValueVec::new()) + } +} + macro_rules! impl_from_lua_vec_tuple { ( $count: expr, $first: tt, $( $name: tt ),+ ) => ( #[allow(non_snake_case)]