Add support for full userdata with proper dropping support

This commit is contained in:
William Venner 2021-10-23 20:05:52 +01:00
parent 3777db69de
commit 10efbfdd16
10 changed files with 523 additions and 2 deletions

2
.gitignore vendored
View File

@ -1 +1 @@
/target target/

View File

@ -1,5 +1,6 @@
#![feature(c_unwind)] #![feature(c_unwind)]
#![feature(thread_id_value)] #![feature(thread_id_value)]
#![feature(const_btree_new)]
pub use libloading; pub use libloading;
pub use detour; pub use detour;

View File

@ -168,6 +168,7 @@ pub struct LuaShared {
pub lua_getstack: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, level: i32, ar: *mut LuaDebug) -> i32>, pub lua_getstack: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, level: i32, ar: *mut LuaDebug) -> i32>,
pub lua_next: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, index: i32) -> i32>, pub lua_next: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, index: i32) -> i32>,
pub lua_topointer: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, index: i32) -> *const c_void>, pub lua_topointer: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, index: i32) -> *const c_void>,
pub lua_newuserdata: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, size: usize) -> *mut c_void>,
} }
unsafe impl Sync for LuaShared {} unsafe impl Sync for LuaShared {}
impl LuaShared { impl LuaShared {
@ -232,7 +233,8 @@ impl LuaShared {
lua_getinfo: find_symbol!("lua_getinfo"), lua_getinfo: find_symbol!("lua_getinfo"),
lua_getstack: find_symbol!("lua_getstack"), lua_getstack: find_symbol!("lua_getstack"),
lua_next: find_symbol!("lua_next"), lua_next: find_symbol!("lua_next"),
lua_topointer: find_symbol!("lua_topointer") lua_topointer: find_symbol!("lua_topointer"),
lua_newuserdata: find_symbol!("lua_newuserdata"),
} }
} }
} }

View File

@ -386,6 +386,40 @@ impl LuaState {
(LUA_SHARED.lua_topointer)(*self, index) (LUA_SHARED.lua_topointer)(*self, index)
} }
#[inline(always)]
pub unsafe fn to_userdata(&self, index: i32) -> *mut c_void {
(LUA_SHARED.lua_touserdata)(*self, index)
}
pub unsafe fn new_userdata<T: Sized>(&self, data: T, metatable: Option<i32>) -> *mut T {
let has_metatable = if std::mem::needs_drop::<T>() {
if let Some(metatable) = metatable {
self.push_value(metatable);
} else {
self.new_table();
}
self.push_function(crate::userdata::__gc::<T>);
self.set_field(-2, crate::lua_string!("__gc"));
true
} else if let Some(metatable) = metatable {
self.push_value(metatable);
true
} else {
false
};
let ptr = (LUA_SHARED.lua_newuserdata)(*self, std::mem::size_of::<T>()) as *mut T;
if has_metatable {
self.push_value(-2);
self.set_metatable(-2);
self.remove(-2);
}
ptr.write(data);
ptr
}
pub unsafe fn error<S: AsRef<str>>(&self, msg: S) -> ! { pub unsafe fn error<S: AsRef<str>>(&self, msg: S) -> ! {
self.push_string(msg.as_ref()); self.push_string(msg.as_ref());
(LUA_SHARED.lua_error)(*self); (LUA_SHARED.lua_error)(*self);

View File

@ -109,4 +109,10 @@ macro_rules! userdata {
userdata! { userdata! {
UserData::Vector => Vector, UserData::Vector => Vector,
UserData::Angle => Angle UserData::Angle => Angle
}
pub(crate) unsafe extern "C-unwind" fn __gc<T: Sized>(lua: crate::lua::State) -> i32 {
let userdata = lua.to_userdata(1) as *mut T;
std::ptr::read(userdata);
0
} }

1
tests/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*/target/

386
tests/userdata/Cargo.lock generated Normal file
View File

@ -0,0 +1,386 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cc"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cfg_table"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f3690291e34881a89ceb8e80802f1582f29b1361fd476eeefb4e89dd6a121e"
[[package]]
name = "cstr"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2846d3636dcaff720d311ea8983f5fa7a8288632b2f95145dd4b5819c397fd8"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "ctor"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "detour"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3c83fabcc3bc336e19320c13576ea708a15deec201d6b879b7ad1b92734d7b9"
dependencies = [
"cfg-if",
"generic-array",
"lazy_static",
"libc",
"libudis86-sys",
"mmap-fixed",
"region",
"slice-pool",
]
[[package]]
name = "fn_abi"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "451b828bd3c5f19949222834f58b38c3670604dcaa5f2b7aec50c0cd58f34900"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn_squash",
]
[[package]]
name = "fn_has_this"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb93938067213676967092012a53515e926de7c813afccd9b075ba0ec78dbba9"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn_squash",
]
[[package]]
name = "fn_type_alias"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "809e807f01217f1f89959fbeebd10c6471b5ef3971fce1b02a70fb7212cc6c1f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "generic-array"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "gmod"
version = "6.0.1"
dependencies = [
"cfg_table",
"cstr",
"ctor",
"detour",
"fn_abi",
"fn_has_this",
"fn_type_alias",
"gmod-macros",
"lazy_static",
"libloading",
"null_fn",
"skidscan",
]
[[package]]
name = "gmod-macros"
version = "1.0.0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
dependencies = [
"winapi 0.2.8",
"winapi-build",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "869d572136620d55835903746bcb5cdc54cb2851fd0aeec53220b4bb65ef3013"
[[package]]
name = "libloading"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0cf036d15402bea3c5d4de17b3fce76b3e4a56ebc1f577be0e7a72f7c607cf0"
dependencies = [
"cfg-if",
"winapi 0.3.9",
]
[[package]]
name = "libudis86-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "139bbf9ddb1bfc90c1ac64dd2923d9c957cd433cee7315c018125d72ab08a6b0"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "mach"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
dependencies = [
"libc",
]
[[package]]
name = "mmap-fixed"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27c1ae264d6343d3b4079549f6bc9e6d074dc4106cb1324c7753c6ce11d07b21"
dependencies = [
"kernel32-sys",
"libc",
"winapi 0.2.8",
]
[[package]]
name = "null_fn"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "345919cfb52d0d8da7efc21aea56046ca96d9e3a8adfdbdb27ef1172829976c1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "proc-macro-crate"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83"
dependencies = [
"thiserror",
"toml",
]
[[package]]
name = "proc-macro2"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
dependencies = [
"proc-macro2",
]
[[package]]
name = "region"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877e54ea2adcd70d80e9179344c97f93ef0dffd6b03e1f4529e6e83ab2fa9ae0"
dependencies = [
"bitflags",
"libc",
"mach",
"winapi 0.3.9",
]
[[package]]
name = "serde"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
[[package]]
name = "skidscan"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e599f7639f0115549da310b45dc29319858981e20dd5e15ee66f69fc8219092e"
dependencies = [
"libc",
"skidscan-macros",
"winapi 0.3.9",
]
[[package]]
name = "skidscan-macros"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "881ef2150f0e4e2a855bb661a3ecd621ab1b6aa07b34aef941d1bd37b4267e8d"
dependencies = [
"proc-macro-crate",
"syn",
]
[[package]]
name = "slice-pool"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "733fc6e5f1bd3a8136f842c9bdea4e5f17c910c2fcc98c90c3aa7604ef5e2e7a"
[[package]]
name = "syn"
version = "1.0.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "syn_squash"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134b985708d02b2569ab2b9e27c8758ca4baaaf725341a572d59bc2d174b9bb5"
[[package]]
name = "thiserror"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "toml"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
dependencies = [
"serde",
]
[[package]]
name = "typenum"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "userdata"
version = "0.1.0"
dependencies = [
"gmod",
]
[[package]]
name = "version_check"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

13
tests/userdata/Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[package]
name = "userdata"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
crate-type = ["cdylib"]
[dependencies]
gmod = { path = "../../gmod" }
[workspace]

View File

@ -0,0 +1,2 @@
[toolchain]
channel = "nightly"

76
tests/userdata/src/lib.rs Normal file
View File

@ -0,0 +1,76 @@
#![feature(c_unwind)]
#[macro_use]
extern crate gmod;
static mut DROP_OK: bool = false;
#[derive(PartialEq, Eq, Debug)]
pub struct DropMe {
pub x: i32,
pub y: i32,
pub z: i32,
pub hello: String
}
impl Drop for DropMe {
fn drop(&mut self) {
unsafe {
if DROP_OK {
DROP_OK = false;
println!("USERDATA DROP TEST PASSED");
println!("USERDATA DROP TEST PASSED");
println!("USERDATA DROP TEST PASSED");
println!("USERDATA DROP TEST PASSED");
println!("USERDATA DROP TEST PASSED");
println!("USERDATA DROP TEST PASSED");
println!("USERDATA DROP TEST PASSED");
println!("USERDATA DROP TEST PASSED");
println!("USERDATA DROP TEST PASSED");
println!("USERDATA DROP TEST PASSED");
println!("USERDATA DROP TEST PASSED");
println!("USERDATA DROP TEST PASSED");
println!("USERDATA DROP TEST PASSED");
} else {
panic!("Dropped too early or too late");
}
}
}
}
macro_rules! drop_me {
() => {
DropMe {
x: 69,
y: 420,
z: 123,
hello: "Hello".to_string()
}
};
}
#[gmod13_open]
unsafe fn gmod13_open(lua: gmod::lua::State) -> i32 {
let ud = lua.new_userdata(drop_me!(), None);
assert_eq!(&*ud, Box::leak(Box::new(drop_me!())));
lua.set_global(lua_string!("GMOD_RUST_DROP_TEST"));
lua.push_nil();
lua.set_global(lua_string!("GMOD_RUST_DROP_TEST"));
DROP_OK = true;
lua.get_global(lua_string!("collectgarbage"));
lua.push_value(-1);
lua.call(0, 0);
lua.call(0, 0);
let ud = lua.new_userdata(420_i32, None);
assert_eq!(*ud, 420_i32);
lua.get_global(lua_string!("collectgarbage"));
lua.push_value(-1);
lua.call(0, 0);
lua.call(0, 0);
0
}