Skip to content

Commit f34aa5e

Browse files
committed
fix
1 parent 9f85a62 commit f34aa5e

File tree

2 files changed

+54
-26
lines changed

2 files changed

+54
-26
lines changed

crates/luars/src/lua_vm/dispatcher/control_instructions.rs

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -936,8 +936,17 @@ pub fn exec_tailcall(vm: &mut LuaVM, instr: u32) -> LuaResult<DispatchAction> {
936936
let a = Instruction::get_a(instr) as usize;
937937
let b = Instruction::get_b(instr) as usize;
938938

939-
let frame = vm.frames.last().unwrap();
940-
let base = frame.base_ptr;
939+
// Extract all frame information we'll need BEFORE taking mutable references
940+
let (base, return_count, result_reg, function_value, pc) = {
941+
let frame = vm.frames.last().unwrap();
942+
(
943+
frame.base_ptr,
944+
frame.get_num_results(),
945+
frame.get_result_reg(),
946+
frame.function_value,
947+
frame.pc,
948+
)
949+
};
941950

942951
// Get function from R[A]
943952
let func = vm.register_stack[base + a];
@@ -973,8 +982,8 @@ pub fn exec_tailcall(vm: &mut LuaVM, instr: u32) -> LuaResult<DispatchAction> {
973982
let max_stack_size = unsafe { (*func_ptr).borrow().chunk.max_stack_size };
974983

975984
// Pop current frame (tail call optimization)
976-
let old_base = frame.base_ptr;
977-
let return_count = frame.get_num_results();
985+
let old_base = base; // Already extracted
986+
// return_count already extracted
978987
vm.frames.pop();
979988

980989
// Create new frame at same location
@@ -1004,31 +1013,51 @@ pub fn exec_tailcall(vm: &mut LuaVM, instr: u32) -> LuaResult<DispatchAction> {
10041013
// C function: cannot use tail call optimization
10051014
// Convert to regular call
10061015

1007-
// Get return information before function call
1008-
let return_count = frame.get_num_results();
1009-
let result_reg = frame.get_result_reg();
1010-
let caller_base = frame.base_ptr;
1016+
// CRITICAL: return_count, result_reg, caller_base already extracted
1017+
// These tell us where to write results in the CALLER's frame
1018+
let args_len = args.len();
1019+
1020+
// IMPORTANT: For TAILCALL, we need the PARENT frame's base
1021+
// The current frame IS the tail-calling function
1022+
// When we pop it, we return to the parent
1023+
// result_reg is relative to parent's base
10111024

10121025
let c_func = func.as_cfunction()
10131026
.ok_or_else(|| LuaError::RuntimeError("Invalid C function".to_string()))?;
10141027

1015-
// Store function and arguments in registers for C function call
1016-
vm.register_stack[base + a] = func;
1028+
// Create temporary C function frame
1029+
let frame_id = vm.next_frame_id;
1030+
vm.next_frame_id += 1;
1031+
1032+
// Set up arguments in a temporary stack space
1033+
let call_base = vm.register_stack.len();
1034+
vm.ensure_stack_capacity(call_base + args_len + 1);
1035+
1036+
vm.register_stack[call_base] = func;
10171037
for (i, arg) in args.iter().enumerate() {
1018-
vm.register_stack[base + a + 1 + i] = *arg;
1038+
vm.register_stack[call_base + 1 + i] = *arg;
10191039
}
1020-
1021-
// Update frame top to include all arguments
1022-
vm.current_frame_mut().top = a + 1 + args.len();
1023-
1024-
// Call C function
1040+
1041+
let temp_frame = LuaCallFrame::new_c_function(
1042+
frame_id,
1043+
function_value,
1044+
pc,
1045+
call_base,
1046+
args_len + 1,
1047+
);
1048+
1049+
// Push temp frame and call C function
1050+
vm.frames.push(temp_frame);
10251051
let result = c_func(vm)?;
1026-
1027-
// Pop current frame
1052+
vm.frames.pop(); // Pop temp frame
1053+
1054+
// NOW pop the tail-calling function's frame
10281055
vm.frames.pop();
10291056

1030-
// Write return values if there's a caller frame
1057+
// Write return values to PARENT frame
1058+
// CRITICAL: result_reg is relative to PARENT's base_ptr!
10311059
if !vm.frames.is_empty() {
1060+
let parent_base = vm.current_frame().base_ptr;
10321061
let vals = result.all_values();
10331062
let count = if return_count == usize::MAX {
10341063
vals.len()
@@ -1038,17 +1067,19 @@ pub fn exec_tailcall(vm: &mut LuaVM, instr: u32) -> LuaResult<DispatchAction> {
10381067

10391068
// Write return values
10401069
for i in 0..count {
1041-
vm.register_stack[caller_base + result_reg + i] = vals[i];
1070+
vm.register_stack[parent_base + result_reg + i] = vals[i];
10421071
}
10431072

10441073
// Fill remaining expected values with nil
10451074
if return_count != usize::MAX {
10461075
for i in count..return_count {
1047-
vm.register_stack[caller_base + result_reg + i] = LuaValue::nil();
1076+
vm.register_stack[parent_base + result_reg + i] = LuaValue::nil();
10481077
}
10491078
}
1050-
1051-
// No need to restore top, it will be handled by the next instruction
1079+
1080+
// CRITICAL: Update parent frame's top to reflect the number of return values
1081+
// This is essential for variable returns (return_count == usize::MAX)
1082+
vm.current_frame_mut().top = result_reg + count;
10521083
}
10531084

10541085
Ok(DispatchAction::Continue)

crates/luars/src/test/test_functions.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,6 @@ fn test_function_vararg_count() {
114114
assert(count_args() == 0)
115115
assert(count_args(nil, nil, 1) == 3)
116116
"#);
117-
if let Err(e) = &result {
118-
eprintln!("Error: {:?}", e);
119-
}
120117
assert!(result.is_ok());
121118
}
122119

0 commit comments

Comments
 (0)