make it possible to provide references of rust types to lua

This commit is contained in:
SeanOMik 2024-02-06 09:23:32 -05:00
parent 6c9798eb5b
commit 03e81f5553
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
8 changed files with 503 additions and 194 deletions

View File

@ -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, R>(&'a self, args: A) -> crate::Result<R>
where
A: AsLua<'a>,
A: IntoLuaVec<'a>,
R: FromLua<'a>
{
self.state.execute_chunk::<A, R>(self, args)

View File

@ -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<A, R>(&self, args: A) -> crate::Result<R>
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<crate::Value<'a>> {
Ok(crate::Value::Function(self.clone()))
}
}
impl<'a> FromLua<'a> for Function<'a> {
fn from_lua(_lua: &'a State, val: Value<'a>) -> crate::Result<Self> {
val.into_function()
}
}

View File

@ -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::<Vec2>::new())?;
globals.set("Vec2", ud)?;
/* let ud = lua.create_userdata(UserdataProxy::<Vec2>::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<Vec2>, Ref<Vec2>>(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<Vec2>, Ref<Vec2>>(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<T> {
}
impl<T: Userdata> Userdata for UserdataProxy<T> {
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::<T>::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<T: Userdata> Userdata for UserdataProxy<T> {
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::<T>::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::<UserdataRef<T>>()?;
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
}
}

View File

@ -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<String, Utf8Error> {
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<R>
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<Table<'a>> {
let mut builder = UserdataBuilder::<T>::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::<T>().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::<T>());
//todo!()
}
Ok(())

View File

@ -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<K, V>(&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<K, V>(&self, key: K) -> Result<V>
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<K>(&self, key: K) -> Result<bool>
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<K, V>(&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<K, V>(&'a self, key: K) -> Result<V>
pub fn raw_get<K, V>(&self, key: K) -> Result<V>
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<K>(&self, key: K) -> Result<bool>
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<K, V>(&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<Self> {
val.into_table()
}
}

View File

@ -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<dyn Fn(&'a State, ValueVec<'a>) -> crate::Result<Value<'a>>>;
pub type UserdataFn<'a> = Box<dyn Fn(&'a State, ValueVec<'a>) -> crate::Result<Value<'a>>>;
pub struct UserdataBuilder<'a, T> {
pub(crate) name: String,
@ -97,6 +100,9 @@ pub struct UserdataBuilder<'a, T> {
pub(crate) field_setters: HashMap<String, UserdataFn<'a>>,
pub(crate) functions: HashMap<String, UserdataFn<'a>>,
pub(crate) meta_methods: HashMap<String, UserdataFn<'a>>,
pub(crate) wrapped_getter: Arc<OnceCell<Box<dyn Fn(AnyUserdata<'a>) -> crate::Result<*const ()>>>>,
_marker: PhantomData<T>,
}
@ -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<OnceCell<Box<dyn Fn(AnyUserdata<'_>) -> 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::<T>()?;
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::<T>();
let this = unsafe { &*this_ptr };
f(lua, this).and_then(|r| r.as_lua(lua))
} else {
let this = this.as_ref::<T>()?;
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::<T>()?;
let this_name = T::name();
@ -233,78 +264,60 @@ impl<'a, T: Userdata> UserdataBuilder<'a, T> {
self
}
//fn append_fields_from<S>(&mut self, other: UserDataRegistry<'lua, S>) {
pub fn expand_with<U>(&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<T: Userdata + 'static>(&self) -> crate::Result<Ref<'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::<T>()
.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::<RefCell<T>>();
Ok(cell.borrow())
} else {
return Err(crate::Error::UserdataMismatch);
}
let cell = &*self.unsafe_ud.as_ptr_unchecked::<RefCell<T>>()?;
Ok(cell.borrow())
}
}
/// Returns a mutable reference to the userdata.
pub fn as_mut<T: Userdata + 'static>(&self) -> crate::Result<RefMut<'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::MissingMetatable);
}
self.state.get_userdata_metatable::<T>()
.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::<RefCell<T>>();
Ok(cell.borrow_mut())
} else {
Err(crate::Error::UserdataMismatch)
}
let cell = &*self.unsafe_ud.as_ptr_unchecked::<RefCell<T>>()?;
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<T: Userdata + 'static>(&self) -> crate::Result<*mut RefCell<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, -3);
Ok(cptr.cast())
let cell = self.unsafe_ud.as_ptr_unchecked::<RefCell<T>>()?;
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::<RefCell<T>>()?;
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<Table<'a>> {
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<K, V>(&'a self, key: K) -> crate::Result<V>
where
K: AsLua<'a>,
V: FromLua<'a>,
{
let mt = self.get_metatable()?;
mt.get::<K, V>(key)
}
/// Gets something from the userdata. Will **not** trigger a call to the `__index` metamethod.
pub fn raw_get<K, V>(&'a self, key: K) -> crate::Result<V>
where
K: AsLua<'a>,
V: FromLua<'a>,
{
let mt = self.get_metatable()?;
mt.raw_get::<K, V>(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<A, R>(&self, name: &str, args: A) -> crate::Result<R>
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)
}
}

180
src/userdata/unsafe_ud.rs Executable file
View File

@ -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<T: Userdata + 'static>(&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::<T>()
.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::<T>();
Ok(t)
} else {
return Err(crate::Error::UserdataMismatch);
}
}
}
/// Returns a mutable reference to the userdata.
pub fn as_mut<T: Userdata + 'static>(&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::<T>()
.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::<T>();
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<T>(&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<T: Userdata + 'static>(&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::<T>()
.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<String> {
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<Self> {
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<Value<'a>> {
Ok(Value::Userdata(self.clone()))
}
}
impl<'a> FromLua<'a> for UnsafeUserdata<'a> {
fn from_lua(_lua: &State, val: Value<'a>) -> crate::Result<Self> {
val.into_userdata()
}
} */

View File

@ -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<Table<'a>> {
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<Function<'a>> {
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<Self> {
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<ValueVec<'a>> {
Ok(ValueVec::new())
}
}
macro_rules! impl_from_lua_vec_tuple {
( $count: expr, $first: tt, $( $name: tt ),+ ) => (
#[allow(non_snake_case)]