@@ -4,8 +4,11 @@ use std::marker::PhantomData;
4
4
use std:: os:: raw:: c_int;
5
5
use std:: ptr;
6
6
7
+ use rustc_hash:: FxHashMap ;
8
+
7
9
use super :: UserDataStorage ;
8
10
use crate :: error:: { Error , Result } ;
11
+ use crate :: types:: CallbackPtr ;
9
12
use crate :: util:: { get_userdata, rawget_field, rawset_field, take_userdata} ;
10
13
11
14
// This is a trick to check if a type is `Sync` or not.
@@ -244,6 +247,7 @@ pub(crate) unsafe fn init_userdata_metatable(
244
247
field_getters : Option < c_int > ,
245
248
field_setters : Option < c_int > ,
246
249
methods : Option < c_int > ,
250
+ _methods_map : Option < FxHashMap < Vec < u8 > , CallbackPtr > > , // Used only in Luau for `__namecall`
247
251
) -> Result < ( ) > {
248
252
if field_getters. is_some ( ) || methods. is_some ( ) {
249
253
// Push `__index` generator function
@@ -267,6 +271,13 @@ pub(crate) unsafe fn init_userdata_metatable(
267
271
}
268
272
269
273
rawset_field ( state, metatable, "__index" ) ?;
274
+
275
+ #[ cfg( feature = "luau" ) ]
276
+ if let Some ( methods_map) = _methods_map {
277
+ // In Luau we can speedup method calls by providing a dedicated `__namecall` metamethod
278
+ push_userdata_metatable_namecall ( state, methods_map) ?;
279
+ rawset_field ( state, metatable, "__namecall" ) ?;
280
+ }
270
281
}
271
282
272
283
if let Some ( field_setters) = field_setters {
@@ -425,6 +436,36 @@ unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Result
425
436
} )
426
437
}
427
438
439
+ #[ cfg( feature = "luau" ) ]
440
+ unsafe fn push_userdata_metatable_namecall (
441
+ state : * mut ffi:: lua_State ,
442
+ methods_map : FxHashMap < Vec < u8 > , CallbackPtr > ,
443
+ ) -> Result < ( ) > {
444
+ unsafe extern "C-unwind" fn namecall ( state : * mut ffi:: lua_State ) -> c_int {
445
+ let name = ffi:: lua_namecallatom ( state, ptr:: null_mut ( ) ) ;
446
+ if name. is_null ( ) {
447
+ ffi:: luaL_error ( state, cstr ! ( "attempt to call an unknown method" ) ) ;
448
+ }
449
+ let name_cs = std:: ffi:: CStr :: from_ptr ( name) ;
450
+ let methods_map = get_userdata :: < FxHashMap < Vec < u8 > , CallbackPtr > > ( state, ffi:: lua_upvalueindex ( 1 ) ) ;
451
+ let callback_ptr = match ( * methods_map) . get ( name_cs. to_bytes ( ) ) {
452
+ Some ( ptr) => * ptr,
453
+ #[ rustfmt:: skip]
454
+ None => ffi:: luaL_error ( state, cstr ! ( "attempt to call an unknown method '%s'" ) , name) ,
455
+ } ;
456
+ crate :: state:: callback_error_ext ( state, ptr:: null_mut ( ) , true , |extra, nargs| {
457
+ let rawlua = ( * extra) . raw_lua ( ) ;
458
+ ( * callback_ptr) ( rawlua, nargs)
459
+ } )
460
+ }
461
+
462
+ // Automatic destructor is provided for any Luau userdata
463
+ crate :: util:: push_userdata ( state, methods_map, true ) ?;
464
+ protect_lua ! ( state, 1 , 1 , |state| {
465
+ ffi:: lua_pushcclosured( state, namecall, cstr!( "__namecall" ) , 1 ) ;
466
+ } )
467
+ }
468
+
428
469
// This method is called by Lua GC when it's time to collect the userdata.
429
470
//
430
471
// This method is usually used to collect internal userdata.
0 commit comments