@@ -104,21 +104,25 @@ pub unsafe fn protect_lua_call<F, R>(
104
104
f : F ,
105
105
) -> Result < R >
106
106
where
107
- F : FnMut ( * mut ffi:: lua_State ) -> R ,
107
+ F : FnOnce ( * mut ffi:: lua_State ) -> R ,
108
108
{
109
109
struct Params < F , R > {
110
110
function : F ,
111
- ret : Option < R > ,
111
+ result : R ,
112
112
nresults : c_int ,
113
113
}
114
114
115
115
unsafe extern "C" fn do_call < F , R > ( state : * mut ffi:: lua_State ) -> c_int
116
116
where
117
- F : FnMut ( * mut ffi:: lua_State ) -> R ,
117
+ F : FnOnce ( * mut ffi:: lua_State ) -> R ,
118
118
{
119
119
let params = ffi:: lua_touserdata ( state, -1 ) as * mut Params < F , R > ;
120
120
ffi:: lua_pop ( state, 1 ) ;
121
- ( * params) . ret = Some ( ( ( * params) . function ) ( state) ) ;
121
+
122
+ let function = mem:: replace ( & mut ( * params) . function , mem:: uninitialized ( ) ) ;
123
+ mem:: replace ( & mut ( * params) . result , function ( state) ) ;
124
+ // params now has function uninitialied and result initialized
125
+
122
126
if ( * params) . nresults == ffi:: LUA_MULTRET {
123
127
ffi:: lua_gettop ( state)
124
128
} else {
@@ -132,20 +136,32 @@ where
132
136
ffi:: lua_pushcfunction ( state, do_call :: < F , R > ) ;
133
137
ffi:: lua_rotate ( state, stack_start + 1 , 2 ) ;
134
138
139
+ // We are about to do some really scary stuff with the Params structure, both because
140
+ // protect_lua_call is very hot, and becuase we would like to allow the function type to be
141
+ // FnOnce rather than FnMut. We are using Params here both to pass data to the callback and
142
+ // return data from the callback.
143
+ //
144
+ // params starts out with function initialized and result uninitialized, nresults is Copy so we
145
+ // don't care about it.
135
146
let mut params = Params {
136
147
function : f,
137
- ret : None ,
148
+ result : mem :: uninitialized ( ) ,
138
149
nresults,
139
150
} ;
140
151
141
152
ffi:: lua_pushlightuserdata ( state, & mut params as * mut Params < F , R > as * mut c_void ) ;
142
153
143
154
let ret = ffi:: lua_pcall ( state, nargs + 1 , nresults, stack_start + 1 ) ;
155
+ let result = mem:: replace ( & mut params. result , mem:: uninitialized ( ) ) ;
156
+
157
+ // params now has both function and result uninitialized, so we need to forget it so Drop isn't
158
+ // run.
159
+ mem:: forget ( params) ;
144
160
145
161
ffi:: lua_remove ( state, stack_start + 1 ) ;
146
162
147
163
if ret == ffi:: LUA_OK {
148
- Ok ( params . ret . unwrap ( ) )
164
+ Ok ( result )
149
165
} else {
150
166
Err ( pop_error ( state, ret) )
151
167
}
@@ -164,8 +180,7 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
164
180
if let Some ( err) = pop_wrapped_error ( state) {
165
181
err
166
182
} else if is_wrapped_panic ( state, -1 ) {
167
- let panic = get_userdata :: < WrappedPanic > ( state, -1 )
168
- . expect ( "WrappedPanic was somehow resurrected after garbage collection" ) ;
183
+ let panic = get_userdata :: < WrappedPanic > ( state, -1 ) ;
169
184
if let Some ( p) = ( * panic) . 0 . take ( ) {
170
185
ffi:: lua_settop ( state, 0 ) ;
171
186
resume_unwind ( p) ;
@@ -220,26 +235,30 @@ pub unsafe fn push_string(state: *mut ffi::lua_State, s: &str) -> Result<()> {
220
235
221
236
// Internally uses 4 stack spaces, does not call checkstack
222
237
pub unsafe fn push_userdata < T > ( state : * mut ffi:: lua_State , t : T ) -> Result < ( ) > {
223
- let mut t = Some ( t) ;
224
- protect_lua_call ( state, 0 , 1 , |state| {
225
- let ud = ffi:: lua_newuserdata ( state, mem:: size_of :: < Option < T > > ( ) ) as * mut Option < T > ;
226
- ptr:: write ( ud, t. take ( ) ) ;
238
+ protect_lua_call ( state, 0 , 1 , move |state| {
239
+ let ud = ffi:: lua_newuserdata ( state, mem:: size_of :: < T > ( ) ) as * mut T ;
240
+ ptr:: write ( ud, t) ;
227
241
} )
228
242
}
229
243
230
244
// Returns None in the case that the userdata has already been garbage collected.
231
- pub unsafe fn get_userdata < T > ( state : * mut ffi:: lua_State , index : c_int ) -> Result < * mut T > {
232
- let ud = ffi:: lua_touserdata ( state, index) as * mut Option < T > ;
245
+ pub unsafe fn get_userdata < T > ( state : * mut ffi:: lua_State , index : c_int ) -> * mut T {
246
+ let ud = ffi:: lua_touserdata ( state, index) as * mut T ;
233
247
lua_assert ! ( state, !ud. is_null( ) , "userdata pointer is null" ) ;
234
- ( * ud)
235
- . as_mut ( )
236
- . map ( |v| v as * mut T )
237
- . ok_or ( Error :: ExpiredUserData )
248
+ ud
238
249
}
239
250
240
251
pub unsafe extern "C" fn userdata_destructor < T > ( state : * mut ffi:: lua_State ) -> c_int {
241
252
callback_error ( state, || {
242
- * ( ffi:: lua_touserdata ( state, 1 ) as * mut Option < T > ) = None ;
253
+ // We clear the metatable of userdata on __gc so that it will not be double dropped, and
254
+ // also so that it cannot be used or identified as any particular userdata type after the
255
+ // first call to __gc.
256
+ gc_guard ( state, || {
257
+ ffi:: lua_newtable ( state) ;
258
+ ffi:: lua_setmetatable ( state, -2 ) ;
259
+ } ) ;
260
+ let ud = & mut * ( ffi:: lua_touserdata ( state, 1 ) as * mut T ) ;
261
+ mem:: replace ( ud, mem:: uninitialized ( ) ) ;
243
262
Ok ( 0 )
244
263
} )
245
264
}
@@ -367,9 +386,8 @@ pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) {
367
386
ffi:: luaL_checkstack ( state, 2 , ptr:: null ( ) ) ;
368
387
369
388
gc_guard ( state, || {
370
- let ud = ffi:: lua_newuserdata ( state, mem:: size_of :: < Option < WrappedError > > ( ) )
371
- as * mut Option < WrappedError > ;
372
- ptr:: write ( ud, Some ( WrappedError ( err) ) )
389
+ let ud = ffi:: lua_newuserdata ( state, mem:: size_of :: < WrappedError > ( ) ) as * mut WrappedError ;
390
+ ptr:: write ( ud, WrappedError ( err) )
373
391
} ) ;
374
392
375
393
get_error_metatable ( state) ;
@@ -382,8 +400,7 @@ pub unsafe fn pop_wrapped_error(state: *mut ffi::lua_State) -> Option<Error> {
382
400
if ffi:: lua_gettop ( state) == 0 || !is_wrapped_error ( state, -1 ) {
383
401
None
384
402
} else {
385
- let err = & * get_userdata :: < WrappedError > ( state, -1 )
386
- . expect ( "WrappedError was somehow resurrected after garbage collection" ) ;
403
+ let err = & * get_userdata :: < WrappedError > ( state, -1 ) ;
387
404
let err = err. 0 . clone ( ) ;
388
405
ffi:: lua_pop ( state, 1 ) ;
389
406
Some ( err)
@@ -415,9 +432,8 @@ unsafe fn push_wrapped_panic(state: *mut ffi::lua_State, panic: Box<Any + Send>)
415
432
ffi:: luaL_checkstack ( state, 2 , ptr:: null ( ) ) ;
416
433
417
434
gc_guard ( state, || {
418
- let ud = ffi:: lua_newuserdata ( state, mem:: size_of :: < Option < WrappedPanic > > ( ) )
419
- as * mut Option < WrappedPanic > ;
420
- ptr:: write ( ud, Some ( WrappedPanic ( Some ( panic) ) ) )
435
+ let ud = ffi:: lua_newuserdata ( state, mem:: size_of :: < WrappedPanic > ( ) ) as * mut WrappedPanic ;
436
+ ptr:: write ( ud, WrappedPanic ( Some ( panic) ) )
421
437
} ) ;
422
438
423
439
get_panic_metatable ( state) ;
@@ -480,8 +496,7 @@ unsafe fn get_error_metatable(state: *mut ffi::lua_State) -> c_int {
480
496
unsafe extern "C" fn error_tostring ( state : * mut ffi:: lua_State ) -> c_int {
481
497
callback_error ( state, || {
482
498
if is_wrapped_error ( state, -1 ) {
483
- let error = get_userdata :: < WrappedError > ( state, -1 )
484
- . expect ( "WrappedError was somehow resurrected after garbage collection" ) ;
499
+ let error = get_userdata :: < WrappedError > ( state, -1 ) ;
485
500
let error_str = ( * error) . 0 . to_string ( ) ;
486
501
gc_guard ( state, || {
487
502
ffi:: lua_pushlstring (
0 commit comments