Skip to content

Commit ddebf56

Browse files
committed
Defer metatable return on userdata creation until the end
Relates to #477
1 parent 4f56575 commit ddebf56

File tree

3 files changed

+25
-22
lines changed

3 files changed

+25
-22
lines changed

src/state/raw.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -832,7 +832,7 @@ impl RawLua {
832832

833833
pub(crate) unsafe fn push_userdata_metatable<T>(&self, mut registry: UserDataRegistry<T>) -> Result<()> {
834834
let state = self.state();
835-
let _sg = StackGuard::with_top(state, ffi::lua_gettop(state) + 1);
835+
let mut stack_guard = StackGuard::new(state);
836836
check_stack(state, 13)?;
837837

838838
// Prepare metatable, add meta methods first and then meta fields
@@ -863,8 +863,6 @@ impl RawLua {
863863
}
864864
let metatable_index = ffi::lua_absindex(state, -1);
865865

866-
let mut extra_tables_count = 0;
867-
868866
let fields_nrec = registry.fields.len();
869867
if fields_nrec > 0 {
870868
// If `__index` is a table then update it in-place
@@ -909,7 +907,6 @@ impl RawLua {
909907
rawset_field(state, -2, &k)?;
910908
}
911909
field_getters_index = Some(ffi::lua_absindex(state, -1));
912-
extra_tables_count += 1;
913910
}
914911

915912
let mut field_setters_index = None;
@@ -921,7 +918,6 @@ impl RawLua {
921918
rawset_field(state, -2, &k)?;
922919
}
923920
field_setters_index = Some(ffi::lua_absindex(state, -1));
924-
extra_tables_count += 1;
925921
}
926922

927923
let mut methods_index = None;
@@ -958,7 +954,6 @@ impl RawLua {
958954
}
959955
_ => {
960956
methods_index = Some(ffi::lua_absindex(state, -1));
961-
extra_tables_count += 1;
962957
}
963958
}
964959
}
@@ -980,8 +975,8 @@ impl RawLua {
980975
extra_init,
981976
)?;
982977

983-
// Pop extra tables to get metatable on top of the stack
984-
ffi::lua_pop(state, extra_tables_count);
978+
// Update stack guard to keep metatable after return
979+
stack_guard.keep(1);
985980

986981
Ok(())
987982
}

src/util/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ impl StackGuard {
6666
pub(crate) fn with_top(state: *mut ffi::lua_State, top: c_int) -> StackGuard {
6767
StackGuard { state, top }
6868
}
69+
70+
#[inline]
71+
pub(crate) fn keep(&mut self, n: c_int) {
72+
self.top += n;
73+
}
6974
}
7075

7176
impl Drop for StackGuard {
@@ -129,6 +134,15 @@ pub(crate) unsafe fn push_table(
129134
}
130135
}
131136

137+
// Uses 4 stack spaces, does not call checkstack.
138+
pub(crate) unsafe fn rawget_field(state: *mut ffi::lua_State, table: c_int, field: &str) -> Result<c_int> {
139+
ffi::lua_pushvalue(state, table);
140+
protect_lua!(state, 1, 1, |state| {
141+
ffi::lua_pushlstring(state, field.as_ptr() as *const c_char, field.len());
142+
ffi::lua_rawget(state, -2)
143+
})
144+
}
145+
132146
// Uses 4 stack spaces, does not call checkstack.
133147
pub(crate) unsafe fn rawset_field(state: *mut ffi::lua_State, table: c_int, field: &str) -> Result<()> {
134148
ffi::lua_pushvalue(state, table);

src/util/userdata.rs

Lines changed: 8 additions & 14 deletions
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, get_metatable_ptr, push_string, push_table, rawset_field, TypeKey};
6+
use crate::util::{check_stack, get_metatable_ptr, push_table, rawget_field, 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.
@@ -154,14 +154,11 @@ pub(crate) unsafe fn init_userdata_metatable(
154154
methods: Option<c_int>,
155155
extra_init: Option<fn(*mut ffi::lua_State) -> Result<()>>,
156156
) -> Result<()> {
157-
ffi::lua_pushvalue(state, metatable);
158-
159157
if field_getters.is_some() || methods.is_some() {
160158
// Push `__index` generator function
161159
init_userdata_metatable_index(state)?;
162160

163-
push_string(state, b"__index", true)?;
164-
let index_type = ffi::lua_rawget(state, -3);
161+
let index_type = rawget_field(state, metatable, "__index")?;
165162
match index_type {
166163
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
167164
for &idx in &[field_getters, methods] {
@@ -175,28 +172,27 @@ pub(crate) unsafe fn init_userdata_metatable(
175172
// Generate `__index`
176173
protect_lua!(state, 4, 1, fn(state) ffi::lua_call(state, 3, 1))?;
177174
}
178-
_ => mlua_panic!("improper __index type {}", index_type),
175+
_ => mlua_panic!("improper `__index` type: {}", index_type),
179176
}
180177

181-
rawset_field(state, -2, "__index")?;
178+
rawset_field(state, metatable, "__index")?;
182179
}
183180

184181
if let Some(field_setters) = field_setters {
185182
// Push `__newindex` generator function
186183
init_userdata_metatable_newindex(state)?;
187184

188-
push_string(state, b"__newindex", true)?;
189-
let newindex_type = ffi::lua_rawget(state, -3);
185+
let newindex_type = rawget_field(state, metatable, "__newindex")?;
190186
match newindex_type {
191187
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
192188
ffi::lua_pushvalue(state, field_setters);
193189
// Generate `__newindex`
194190
protect_lua!(state, 3, 1, fn(state) ffi::lua_call(state, 2, 1))?;
195191
}
196-
_ => mlua_panic!("improper __newindex type {}", newindex_type),
192+
_ => mlua_panic!("improper `__newindex` type: {}", newindex_type),
197193
}
198194

199-
rawset_field(state, -2, "__newindex")?;
195+
rawset_field(state, metatable, "__newindex")?;
200196
}
201197

202198
// Additional initialization
@@ -205,9 +201,7 @@ pub(crate) unsafe fn init_userdata_metatable(
205201
}
206202

207203
ffi::lua_pushboolean(state, 0);
208-
rawset_field(state, -2, "__metatable")?;
209-
210-
ffi::lua_pop(state, 1);
204+
rawset_field(state, metatable, "__metatable")?;
211205

212206
Ok(())
213207
}

0 commit comments

Comments
 (0)