summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorkyren <kerriganw@gmail.com>2018-02-05 14:39:01 -0500
committerkyren <kerriganw@gmail.com>2018-02-05 14:40:20 -0500
commitfe35742026af80b5ac120dfb9e5966689a698040 (patch)
tree437151e83ebfd697102284bf1f7e2d1868e7ec59 /src
parent6382baa99111aa3a233d35674b7fb8c82a416c39 (diff)
downloadmlua-fe35742026af80b5ac120dfb9e5966689a698040.zip
Set the metatable of __gc'ed userdata to something more informative
Diffstat (limited to 'src')
-rw-r--r--src/userdata.rs2
-rw-r--r--src/util.rs73
2 files changed, 67 insertions, 8 deletions
diff --git a/src/userdata.rs b/src/userdata.rs
index eea63d5..2c395e4 100644
--- a/src/userdata.rs
+++ b/src/userdata.rs
@@ -548,7 +548,7 @@ mod tests {
}
#[test]
- fn test_expired_userdata() {
+ fn test_gc_userdata() {
struct MyUserdata {
id: u8,
}
diff --git a/src/util.rs b/src/util.rs
index a7b1c76..fa36028 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -250,13 +250,12 @@ pub unsafe fn get_userdata<T>(state: *mut ffi::lua_State, index: c_int) -> *mut
pub unsafe extern "C" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int {
callback_error(state, || {
- // We clear the metatable of userdata on __gc so that it will not be double dropped, and
- // also so that it cannot be used or identified as any particular userdata type after the
- // first call to __gc.
- gc_guard(state, || {
- ffi::lua_newtable(state);
- ffi::lua_setmetatable(state, -2);
- });
+ // We set the metatable of userdata on __gc to a special table with no __gc method and with
+ // metamethods that trigger an error on access. We do this so that it will not be double
+ // dropped, and also so that it cannot be used or identified as any particular userdata type
+ // after the first call to __gc.
+ get_gc_userdata_metatable(state);
+ ffi::lua_setmetatable(state, -2);
let ud = &mut *(ffi::lua_touserdata(state, 1) as *mut T);
mem::replace(ud, mem::uninitialized());
Ok(0)
@@ -588,3 +587,63 @@ unsafe fn get_panic_metatable(state: *mut ffi::lua_State) -> c_int {
ffi::LUA_TTABLE
}
+
+unsafe fn get_gc_userdata_metatable(state: *mut ffi::lua_State) -> c_int {
+ static GC_USERDATA_METATABLE: u8 = 0;
+
+ unsafe extern "C" fn gc_error(state: *mut ffi::lua_State) -> c_int {
+ ffi::lua_pushstring(state, cstr!("userdata has been garbage collected"));
+ ffi::lua_error(state)
+ }
+
+ ffi::lua_pushlightuserdata(state, &GC_USERDATA_METATABLE as *const u8 as *mut c_void);
+ let t = ffi::lua_gettable(state, ffi::LUA_REGISTRYINDEX);
+
+ if t != ffi::LUA_TTABLE {
+ ffi::lua_pop(state, 1);
+
+ ffi::luaL_checkstack(state, 8, ptr::null());
+
+ gc_guard(state, || {
+ ffi::lua_newtable(state);
+ ffi::lua_pushlightuserdata(state, &GC_USERDATA_METATABLE as *const u8 as *mut c_void);
+ ffi::lua_pushvalue(state, -2);
+
+ for &method in &[
+ cstr!("__add"),
+ cstr!("__sub"),
+ cstr!("__mul"),
+ cstr!("__div"),
+ cstr!("__mod"),
+ cstr!("__pow"),
+ cstr!("__unm"),
+ cstr!("__idiv"),
+ cstr!("__band"),
+ cstr!("__bor"),
+ cstr!("__bxor"),
+ cstr!("__bnot"),
+ cstr!("__shl"),
+ cstr!("__shr"),
+ cstr!("__concat"),
+ cstr!("__len"),
+ cstr!("__eq"),
+ cstr!("__lt"),
+ cstr!("__le"),
+ cstr!("__index"),
+ cstr!("__newindex"),
+ cstr!("__call"),
+ cstr!("__tostring"),
+ cstr!("__pairs"),
+ cstr!("__ipairs"),
+ ] {
+ ffi::lua_pushstring(state, method);
+ ffi::lua_pushcfunction(state, gc_error);
+ ffi::lua_rawset(state, -3);
+ }
+
+ ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
+ });
+ }
+
+ ffi::LUA_TTABLE
+}