@@ -955,50 +955,211 @@ pub fn exec_call(
955955 }
956956
957957 // Slow path: Check for __call metamethod (for Table and Userdata)
958- let metatable_opt = match func. kind ( ) {
959- LuaValueKind :: Table => func. as_table_id ( ) . and_then ( |table_id| {
960- vm. object_pool
961- . get_table ( table_id)
962- . and_then ( |t| t. get_metatable ( ) )
963- } ) ,
964- LuaValueKind :: Userdata => func. as_userdata_id ( ) . and_then ( |ud_id| {
965- vm. object_pool
966- . get_userdata ( ud_id)
967- . map ( |ud| ud. get_metatable ( ) )
968- . filter ( |mt| !mt. is_nil ( ) )
969- } ) ,
970- _ => None ,
971- } ;
958+ // Need to handle chains of __call metamethods by inserting extra arguments
959+ let mut current_func = func;
960+ let mut extra_args: Vec < LuaValue > = Vec :: new ( ) ; // Tables in the chain to insert as args
961+ let mut depth = 0 ;
962+ const MAX_CALL_CHAIN : usize = 200 ; // Prevent infinite loops
963+
964+ loop {
965+ let metatable_opt = match current_func. kind ( ) {
966+ LuaValueKind :: Table => current_func. as_table_id ( ) . and_then ( |table_id| {
967+ vm. object_pool
968+ . get_table ( table_id)
969+ . and_then ( |t| t. get_metatable ( ) )
970+ } ) ,
971+ LuaValueKind :: Userdata => current_func. as_userdata_id ( ) . and_then ( |ud_id| {
972+ vm. object_pool
973+ . get_userdata ( ud_id)
974+ . map ( |ud| ud. get_metatable ( ) )
975+ . filter ( |mt| !mt. is_nil ( ) )
976+ } ) ,
977+ _ => break , // Not a table/userdata, cannot have __call
978+ } ;
972979
973- if let Some ( metatable) = metatable_opt {
974- // Use pre-cached __call StringId
975- let call_key = LuaValue :: string ( vm. object_pool . tm_call ) ;
976- if let Some ( call_func) = vm. table_get_with_meta ( & metatable, & call_key) {
977- if call_func. is_callable ( ) {
978- if call_func. is_cfunction ( ) {
979- exec_call_cfunction ( vm, call_func, a, b, c, base, true , func, frame_ptr_ptr) ?;
980+ if let Some ( metatable) = metatable_opt {
981+ // Use pre-cached __call StringId
982+ let call_key = LuaValue :: string ( vm. object_pool . tm_call ) ;
983+ if let Some ( call_func) = vm. table_get_with_meta ( & metatable, & call_key) {
984+ // Add current_func to the extra args (at the front)
985+ extra_args. insert ( 0 , current_func) ;
986+
987+ if call_func. is_callable ( ) {
988+ // Found a callable __call metamethod
989+ // Now we need to insert all extra_args before the original arguments
990+ exec_call_with_extra_args ( vm, call_func, a, b, c, base, & extra_args, frame_ptr_ptr) ?;
980991 return Ok ( ( ) ) ;
992+ } else if call_func. is_table ( ) || call_func. is_userdata ( ) {
993+ // __call is itself a table/userdata, follow the chain
994+ current_func = call_func;
995+ depth += 1 ;
996+ if depth >= MAX_CALL_CHAIN {
997+ return Err ( vm. error ( "'__call' chain too long; possible loop" . to_string ( ) ) ) ;
998+ }
999+ continue ;
9811000 } else {
982- exec_call_lua_function (
983- vm,
984- call_func,
985- a,
986- b,
987- c,
988- base,
989- true ,
990- func,
991- frame_ptr_ptr,
992- ) ?;
993- return Ok ( ( ) ) ;
1001+ break ; // __call is not callable and not a table
9941002 }
1003+ } else {
1004+ break ; // No __call metamethod
9951005 }
1006+ } else {
1007+ break ; // No metatable
9961008 }
9971009 }
9981010
9991011 Err ( vm. error ( format ! ( "attempt to call a {} value" , func. type_name( ) ) ) )
10001012}
10011013
1014+ /// Execute a call with extra arguments inserted at the front (for __call chains)
1015+ /// extra_args are inserted before the original arguments
1016+ #[ inline( never) ]
1017+ fn exec_call_with_extra_args (
1018+ vm : & mut LuaVM ,
1019+ call_func : LuaValue ,
1020+ a : usize ,
1021+ b : usize ,
1022+ c : usize ,
1023+ base : usize ,
1024+ extra_args : & [ LuaValue ] ,
1025+ frame_ptr_ptr : & mut * mut LuaCallFrame ,
1026+ ) -> LuaResult < ( ) > {
1027+ // Calculate original argument count
1028+ let orig_arg_count = if b == 0 {
1029+ let frame = vm. current_frame ( ) ;
1030+ if frame. top as usize > a + 1 {
1031+ ( frame. top as usize ) - ( a + 1 )
1032+ } else {
1033+ 0
1034+ }
1035+ } else {
1036+ vm. current_frame_mut ( ) . top = ( a + b) as u32 ;
1037+ b - 1
1038+ } ;
1039+
1040+ let extra_count = extra_args. len ( ) ;
1041+ let total_arg_count = extra_count + orig_arg_count;
1042+ let return_count = if c == 0 { usize:: MAX } else { c - 1 } ;
1043+
1044+ // New frame base = R[A+1] in caller's frame
1045+ let new_base = base + a + 1 ;
1046+
1047+ // Ensure we have enough stack space
1048+ // First, make room for the extra args by shifting original args
1049+ let required_capacity = new_base + total_arg_count + 10 ; // Extra buffer
1050+ vm. ensure_stack_capacity ( required_capacity) ;
1051+
1052+ // Shift original arguments to make room for extra_args at the front
1053+ if orig_arg_count > 0 && extra_count > 0 {
1054+ // Move original args from [new_base..new_base+orig_arg_count]
1055+ // to [new_base+extra_count..new_base+extra_count+orig_arg_count]
1056+ unsafe {
1057+ let reg_ptr = vm. register_stack . as_mut_ptr ( ) ;
1058+ // Copy in reverse to avoid overwriting
1059+ for i in ( 0 ..orig_arg_count) . rev ( ) {
1060+ * reg_ptr. add ( new_base + extra_count + i) = * reg_ptr. add ( new_base + i) ;
1061+ }
1062+ }
1063+ }
1064+
1065+ // Insert extra_args at the front
1066+ for ( i, arg) in extra_args. iter ( ) . enumerate ( ) {
1067+ vm. register_stack [ new_base + i] = * arg;
1068+ }
1069+
1070+ // Update top to reflect new argument count
1071+ vm. current_frame_mut ( ) . top = ( a + 1 + total_arg_count) as u32 ;
1072+
1073+ // Now call the function
1074+ if call_func. is_cfunction ( ) {
1075+ let c_func = call_func. as_cfunction ( ) . unwrap ( ) ;
1076+ // For C function, we need to set up the frame manually
1077+ let call_base = base + a;
1078+ vm. register_stack [ call_base] = call_func;
1079+
1080+ let temp_frame = LuaCallFrame :: new_c_function ( call_base, total_arg_count + 1 ) ;
1081+ * frame_ptr_ptr = vm. push_frame ( temp_frame) ;
1082+
1083+ let result = c_func ( vm) ?;
1084+ vm. pop_frame_discard ( ) ;
1085+
1086+ // Restore frame_ptr to caller's frame
1087+ * frame_ptr_ptr = vm. current_frame_ptr ( ) ;
1088+
1089+ // Write results
1090+ let vals = result. all_values ( ) ;
1091+ let count = if return_count == usize:: MAX {
1092+ vals. len ( )
1093+ } else {
1094+ vals. len ( ) . min ( return_count)
1095+ } ;
1096+
1097+ for i in 0 ..count {
1098+ vm. register_stack [ base + a + i] = vals[ i] ;
1099+ }
1100+
1101+ // Fill remaining expected results with nil
1102+ if return_count != usize:: MAX {
1103+ for i in count..return_count {
1104+ vm. register_stack [ base + a + i] = LuaValue :: nil ( ) ;
1105+ }
1106+ }
1107+
1108+ // Update caller's top if variable return
1109+ if return_count == usize:: MAX {
1110+ vm. current_frame_mut ( ) . top = ( a + count) as u32 ;
1111+ }
1112+
1113+ Ok ( ( ) )
1114+ } else {
1115+ // Lua function
1116+ let func_id = match call_func. as_function_id ( ) {
1117+ Some ( id) => id,
1118+ None => return Err ( vm. error ( "not a function" . to_string ( ) ) ) ,
1119+ } ;
1120+ let func_ref = match vm. object_pool . get_function ( func_id) {
1121+ Some ( f) => f,
1122+ None => return Err ( vm. error ( "invalid function" . to_string ( ) ) ) ,
1123+ } ;
1124+ let chunk = func_ref. lua_chunk ( ) ;
1125+
1126+ let max_stack_size = chunk. max_stack_size ;
1127+ let num_params = chunk. param_count ;
1128+ let is_vararg = chunk. is_vararg ;
1129+ let code_ptr = chunk. code . as_ptr ( ) ;
1130+ let constants_ptr = chunk. constants . as_ptr ( ) ;
1131+ let upvalues_ptr = func_ref. upvalues . as_ptr ( ) ;
1132+
1133+ // Ensure stack capacity
1134+ let required_size = new_base + max_stack_size;
1135+ vm. ensure_stack_capacity ( required_size) ;
1136+
1137+ // Fill missing arguments with nil
1138+ if total_arg_count < num_params && !is_vararg {
1139+ for i in total_arg_count..num_params {
1140+ vm. register_stack [ new_base + i] = LuaValue :: nil ( ) ;
1141+ }
1142+ }
1143+
1144+ // Create and push new frame
1145+ let nresults = if return_count == usize:: MAX { -1i16 } else { return_count as i16 } ;
1146+ let new_frame = LuaCallFrame :: new_lua_function (
1147+ func_id,
1148+ code_ptr,
1149+ constants_ptr,
1150+ upvalues_ptr,
1151+ new_base,
1152+ total_arg_count,
1153+ a,
1154+ nresults,
1155+ max_stack_size,
1156+ ) ;
1157+
1158+ * frame_ptr_ptr = vm. push_frame ( new_frame) ;
1159+ Ok ( ( ) )
1160+ }
1161+ }
1162+
10021163/// Slow path for vararg Lua function calls
10031164#[ inline( never) ]
10041165fn exec_call_lua_vararg (
@@ -1698,6 +1859,7 @@ pub fn exec_tailcall(
16981859 } ;
16991860 let chunk = func_ref. lua_chunk ( ) ;
17001861 let max_stack_size = chunk. max_stack_size ;
1862+ let num_params = chunk. param_count ;
17011863 let code_ptr = chunk. code . as_ptr ( ) ;
17021864 let constants_ptr = chunk. constants . as_ptr ( ) ;
17031865 let upvalues_ptr = func_ref. upvalues . as_ptr ( ) ;
@@ -1719,6 +1881,14 @@ pub fn exec_tailcall(
17191881 vm. register_stack [ old_base + i] = * arg;
17201882 }
17211883
1884+ // CRITICAL: Fill missing parameters with nil
1885+ // If fewer arguments were passed than parameters expected, fill with nil
1886+ if arg_count < num_params {
1887+ for i in arg_count..num_params {
1888+ vm. register_stack [ old_base + i] = LuaValue :: nil ( ) ;
1889+ }
1890+ }
1891+
17221892 // Create new frame at same location
17231893 let nresults = if return_count == usize:: MAX {
17241894 -1i16
0 commit comments