From 10efbfdd16d02732686cd178244d8b7561923513 Mon Sep 17 00:00:00 2001 From: William Venner Date: Sat, 23 Oct 2021 20:05:52 +0100 Subject: [PATCH] Add support for full userdata with proper dropping support --- .gitignore | 2 +- gmod/src/lib.rs | 1 + gmod/src/lua/import.rs | 4 +- gmod/src/lua/lua_state.rs | 34 +++ gmod/src/userdata.rs | 6 + tests/.gitignore | 1 + tests/userdata/Cargo.lock | 386 +++++++++++++++++++++++++++++ tests/userdata/Cargo.toml | 13 + tests/userdata/rust-toolchain.toml | 2 + tests/userdata/src/lib.rs | 76 ++++++ 10 files changed, 523 insertions(+), 2 deletions(-) create mode 100644 tests/.gitignore create mode 100644 tests/userdata/Cargo.lock create mode 100644 tests/userdata/Cargo.toml create mode 100644 tests/userdata/rust-toolchain.toml create mode 100644 tests/userdata/src/lib.rs diff --git a/.gitignore b/.gitignore index ea8c4bf..2f7896d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -/target +target/ diff --git a/gmod/src/lib.rs b/gmod/src/lib.rs index b0b4792..571cdab 100644 --- a/gmod/src/lib.rs +++ b/gmod/src/lib.rs @@ -1,5 +1,6 @@ #![feature(c_unwind)] #![feature(thread_id_value)] +#![feature(const_btree_new)] pub use libloading; pub use detour; diff --git a/gmod/src/lua/import.rs b/gmod/src/lua/import.rs index 49f1ef7..0d92e0f 100644 --- a/gmod/src/lua/import.rs +++ b/gmod/src/lua/import.rs @@ -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_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_newuserdata: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, size: usize) -> *mut c_void>, } unsafe impl Sync for LuaShared {} impl LuaShared { @@ -232,7 +233,8 @@ impl LuaShared { lua_getinfo: find_symbol!("lua_getinfo"), lua_getstack: find_symbol!("lua_getstack"), lua_next: find_symbol!("lua_next"), - lua_topointer: find_symbol!("lua_topointer") + lua_topointer: find_symbol!("lua_topointer"), + lua_newuserdata: find_symbol!("lua_newuserdata"), } } } diff --git a/gmod/src/lua/lua_state.rs b/gmod/src/lua/lua_state.rs index e4aab65..64fe4e0 100644 --- a/gmod/src/lua/lua_state.rs +++ b/gmod/src/lua/lua_state.rs @@ -386,6 +386,40 @@ impl LuaState { (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(&self, data: T, metatable: Option) -> *mut T { + let has_metatable = if std::mem::needs_drop::() { + if let Some(metatable) = metatable { + self.push_value(metatable); + } else { + self.new_table(); + } + self.push_function(crate::userdata::__gc::); + 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::()) 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>(&self, msg: S) -> ! { self.push_string(msg.as_ref()); (LUA_SHARED.lua_error)(*self); diff --git a/gmod/src/userdata.rs b/gmod/src/userdata.rs index ddaf085..43e0198 100644 --- a/gmod/src/userdata.rs +++ b/gmod/src/userdata.rs @@ -109,4 +109,10 @@ macro_rules! userdata { userdata! { UserData::Vector => Vector, UserData::Angle => Angle +} + +pub(crate) unsafe extern "C-unwind" fn __gc(lua: crate::lua::State) -> i32 { + let userdata = lua.to_userdata(1) as *mut T; + std::ptr::read(userdata); + 0 } \ No newline at end of file diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..d413552 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1 @@ +*/target/ \ No newline at end of file diff --git a/tests/userdata/Cargo.lock b/tests/userdata/Cargo.lock new file mode 100644 index 0000000..a8eb825 --- /dev/null +++ b/tests/userdata/Cargo.lock @@ -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" diff --git a/tests/userdata/Cargo.toml b/tests/userdata/Cargo.toml new file mode 100644 index 0000000..5530e2e --- /dev/null +++ b/tests/userdata/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "userdata" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib"] + +[dependencies] +gmod = { path = "../../gmod" } + +[workspace] \ No newline at end of file diff --git a/tests/userdata/rust-toolchain.toml b/tests/userdata/rust-toolchain.toml new file mode 100644 index 0000000..271800c --- /dev/null +++ b/tests/userdata/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" \ No newline at end of file diff --git a/tests/userdata/src/lib.rs b/tests/userdata/src/lib.rs new file mode 100644 index 0000000..cc3bb95 --- /dev/null +++ b/tests/userdata/src/lib.rs @@ -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 +} \ No newline at end of file