Skip to content

Commit 3787ff9

Browse files
committed
Optimize metatable pointer lookup for userdata (Luau)
1 parent 0a2a70c commit 3787ff9

File tree

5 files changed

+36
-27
lines changed

5 files changed

+36
-27
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ serde-value = { version = "0.7", optional = true }
5555
parking_lot = { version = "0.12", features = ["arc_lock"] }
5656
anyhow = { version = "1.0", optional = true }
5757

58-
ffi = { package = "mlua-sys", version = "0.6.3", path = "mlua-sys" }
58+
ffi = { package = "mlua-sys", version = "0.6.4", path = "mlua-sys" }
5959

6060
[target.'cfg(unix)'.dependencies]
6161
libloading = { version = "0.8", optional = true }

src/scope.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ use crate::state::{Lua, LuaGuard, RawLua};
99
use crate::traits::{FromLuaMulti, IntoLuaMulti};
1010
use crate::types::{Callback, CallbackUpvalue, ScopedCallback, ValueRef};
1111
use crate::userdata::{AnyUserData, UserData, UserDataRegistry, UserDataStorage};
12-
use crate::util::{self, assert_stack, check_stack, get_userdata, take_userdata, StackGuard};
12+
use crate::util::{
13+
self, assert_stack, check_stack, get_metatable_ptr, get_userdata, take_userdata, StackGuard,
14+
};
1315

1416
/// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and
1517
/// callbacks that are not required to be `Send` or `'static`.
@@ -199,9 +201,7 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
199201
}
200202

201203
// Deregister metatable
202-
ffi::lua_getmetatable(state, -1);
203-
let mt_ptr = ffi::lua_topointer(state, -1);
204-
ffi::lua_pop(state, 1);
204+
let mt_ptr = get_metatable_ptr(state, -1);
205205
rawlua.deregister_userdata_metatable(mt_ptr);
206206

207207
let ud = take_userdata::<UserDataStorage<T>>(state);

src/state/raw.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ use crate::types::{
2424
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataRegistry, UserDataStorage};
2525
use crate::util::{
2626
assert_stack, check_stack, get_destructed_userdata_metatable, get_internal_userdata, get_main_state,
27-
get_userdata, init_error_registry, init_internal_metatable, init_userdata_metatable, pop_error,
28-
push_internal_userdata, push_string, push_table, rawset_field, safe_pcall, safe_xpcall, short_type_name,
29-
StackGuard, WrappedFailure,
27+
get_metatable_ptr, get_userdata, init_error_registry, init_internal_metatable, init_userdata_metatable,
28+
pop_error, push_internal_userdata, push_string, push_table, rawset_field, safe_pcall, safe_xpcall,
29+
short_type_name, StackGuard, WrappedFailure,
3030
};
3131
use crate::value::{Nil, Value};
3232

@@ -1026,11 +1026,10 @@ impl RawLua {
10261026
state: *mut ffi::lua_State,
10271027
idx: c_int,
10281028
) -> Result<Option<TypeId>> {
1029-
if ffi::lua_getmetatable(state, idx) == 0 {
1029+
let mt_ptr = get_metatable_ptr(state, idx);
1030+
if mt_ptr.is_null() {
10301031
return Err(Error::UserDataTypeMismatch);
10311032
}
1032-
let mt_ptr = ffi::lua_topointer(state, -1);
1033-
ffi::lua_pop(state, 1);
10341033

10351034
// Fast path to skip looking up the metatable in the map
10361035
let (last_mt, last_type_id) = (*self.extra.get()).last_checked_userdata_mt;

src/util/mod.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::borrow::Cow;
22
use std::ffi::CStr;
3-
use std::os::raw::{c_char, c_int};
3+
use std::os::raw::{c_char, c_int, c_void};
44
use std::{ptr, slice, str};
55

66
use crate::error::{Error, Result};
@@ -282,6 +282,21 @@ pub(crate) unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> Stri
282282
}
283283
}
284284

285+
#[inline(always)]
286+
pub(crate) unsafe fn get_metatable_ptr(state: *mut ffi::lua_State, index: c_int) -> *const c_void {
287+
#[cfg(feature = "luau")]
288+
return ffi::lua_getmetatablepointer(state, index);
289+
290+
#[cfg(not(feature = "luau"))]
291+
if ffi::lua_getmetatable(state, index) == 0 {
292+
ptr::null()
293+
} else {
294+
let p = ffi::lua_topointer(state, -1);
295+
ffi::lua_pop(state, 1);
296+
p
297+
}
298+
}
299+
285300
pub(crate) unsafe fn ptr_to_str<'a>(input: *const c_char) -> Option<&'a str> {
286301
if input.is_null() {
287302
return None;

src/util/userdata.rs

+10-15
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::os::raw::{c_int, c_void};
33
use std::{ptr, str};
44

55
use crate::error::Result;
6-
use crate::util::{check_stack, push_string, push_table, rawset_field, TypeKey};
6+
use crate::util::{check_stack, get_metatable_ptr, push_string, push_table, rawset_field, TypeKey};
77

88
// Pushes the userdata and attaches a metatable with __gc method.
99
// Internally uses 3 stack spaces, does not call checkstack.
@@ -58,25 +58,20 @@ pub(crate) unsafe fn init_internal_metatable<T: TypeKey>(
5858
pub(crate) unsafe fn get_internal_userdata<T: TypeKey>(
5959
state: *mut ffi::lua_State,
6060
index: c_int,
61-
type_mt_ptr: *const c_void,
61+
mut type_mt_ptr: *const c_void,
6262
) -> *mut T {
6363
let ud = ffi::lua_touserdata(state, index) as *mut T;
64-
if ud.is_null() || ffi::lua_getmetatable(state, index) == 0 {
64+
if ud.is_null() {
6565
return ptr::null_mut();
6666
}
67-
if !type_mt_ptr.is_null() {
68-
let ud_mt_ptr = ffi::lua_topointer(state, -1);
69-
ffi::lua_pop(state, 1);
70-
if ud_mt_ptr != type_mt_ptr {
71-
return ptr::null_mut();
72-
}
73-
} else {
67+
let mt_ptr = get_metatable_ptr(state, index);
68+
if type_mt_ptr.is_null() {
7469
get_internal_metatable::<T>(state);
75-
let res = ffi::lua_rawequal(state, -1, -2);
76-
ffi::lua_pop(state, 2);
77-
if res == 0 {
78-
return ptr::null_mut();
79-
}
70+
type_mt_ptr = ffi::lua_topointer(state, -1);
71+
ffi::lua_pop(state, 1);
72+
}
73+
if mt_ptr != type_mt_ptr {
74+
return ptr::null_mut();
8075
}
8176
ud
8277
}

0 commit comments

Comments
 (0)