Skip to content

Commit 03d89e8

Browse files
committed
update error handle
1 parent 6f0ae62 commit 03d89e8

File tree

2 files changed

+106
-36
lines changed

2 files changed

+106
-36
lines changed

crates/luars/src/lua_vm/mod.rs

Lines changed: 40 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2015,9 +2015,35 @@ impl LuaVM {
20152015
// ===== Lightweight Error Handling API =====
20162016

20172017
/// Set runtime error and return lightweight error enum
2018-
#[inline]
2018+
/// If an error handler is set (via xpcall), it will be called immediately
20192019
pub fn error(&mut self, message: impl Into<String>) -> LuaError {
2020-
self.error_message = message.into();
2020+
let msg = message.into();
2021+
2022+
// Check if there's an error handler (set by xpcall)
2023+
if let Some(handler) = self.error_handler.clone() {
2024+
// Call the error handler with the error message
2025+
// Note: The call stack is still intact at this point
2026+
let err_value = self.create_string(&msg);
2027+
2028+
match self.call_function_internal(handler, vec![err_value]) {
2029+
Ok(handler_results) => {
2030+
// Error handler succeeded, use its return value as the new error message
2031+
if let Some(result) = handler_results.first() {
2032+
self.error_message = self.value_to_string_raw(result);
2033+
} else {
2034+
self.error_message = msg;
2035+
}
2036+
}
2037+
Err(_) => {
2038+
// Error handler itself failed, use original message
2039+
self.error_message = format!("error in error handler: {}", msg);
2040+
}
2041+
}
2042+
} else {
2043+
// No error handler, just set the message
2044+
self.error_message = msg;
2045+
}
2046+
20212047
LuaError::RuntimeError
20222048
}
20232049

@@ -2413,58 +2439,43 @@ impl LuaVM {
24132439
}
24142440
}
24152441

2416-
/// Protected call with error handler
2442+
/// Protected call with error handler (xpcall semantics)
2443+
/// The error handler is registered and will be called by error() when an error occurs
24172444
/// Note: Yields are NOT caught by xpcall - they propagate through
24182445
pub fn protected_call_with_handler(
24192446
&mut self,
24202447
func: LuaValue,
24212448
args: Vec<LuaValue>,
24222449
err_handler: LuaValue,
24232450
) -> LuaResult<(bool, Vec<LuaValue>)> {
2451+
// Save old error handler and set the new one
24242452
let old_handler = self.error_handler.clone();
24252453
self.error_handler = Some(err_handler.clone());
24262454

24272455
let initial_frame_count = self.frame_count;
24282456

2457+
// Call the function - if it errors, error() will call the handler
24292458
let result = self.call_function_internal(func, args);
24302459

2460+
// Restore old error handler
24312461
self.error_handler = old_handler;
24322462

24332463
match result {
24342464
Ok(values) => Ok((true, values)),
24352465
Err(LuaError::Yield) => Err(LuaError::Yield),
24362466
Err(_) => {
2437-
// IMPORTANT: For xpcall, we call the error handler BEFORE cleaning up frames
2438-
// This allows the handler to access the full call stack for traceback generation
2439-
2440-
// Get the error message (which already includes traceback)
2441-
let msg = self.error_message.clone();
2442-
let err_value = self.create_string(&msg);
2443-
let err_display = format!("Runtime Error: {}", msg);
2444-
2445-
// Call error handler with the error message BEFORE cleaning up frames
2446-
let handler_result = self.call_function_internal(err_handler, vec![err_value]);
2447-
2448-
// NOW clean up frames created by the failed function call
2467+
// Error occurred (and handler was already called in error())
2468+
// Clean up frames created by the failed function call
24492469
while self.frame_count > initial_frame_count {
24502470
let frame = self.pop_frame().unwrap();
24512471
// Close upvalues belonging to this frame
24522472
self.close_upvalues_from(frame.base_ptr);
24532473
}
2454-
2455-
match handler_result {
2456-
Ok(handler_values) => Ok((false, handler_values)),
2457-
Err(LuaError::Yield) => {
2458-
// Yield from error handler - propagate it
2459-
let values = self.take_yield_values();
2460-
Err(self.do_yield(values))
2461-
}
2462-
Err(_) => {
2463-
let err_str =
2464-
self.create_string(&format!("Error in error handler: {}", err_display));
2465-
Ok((false, vec![err_str]))
2466-
}
2467-
}
2474+
2475+
// Return the error message (which may have been modified by the handler)
2476+
let msg = self.error_message.clone();
2477+
let err_str = self.create_string(&msg);
2478+
Ok((false, vec![err_str]))
24682479
}
24692480
}
24702481
}

crates/luars/src/stdlib/debug.rs

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,73 @@ pub fn create_debug_lib() -> LibraryModule {
1515
}
1616

1717
fn debug_traceback(vm: &mut LuaVM) -> LuaResult<MultiValue> {
18-
// Simple traceback
18+
// debug.traceback([thread,] [message [, level]])
19+
// For now, we don't support the thread parameter
20+
21+
// Get message argument (can be nil)
1922
let message = crate::lib_registry::get_arg(vm, 0).unwrap_or(LuaValue::nil());
20-
// .and_then(|v| v.as_lua_string())
21-
// .map(|s| s.as_str().to_string())
22-
// .unwrap_or_else(|| "stack traceback:".to_string());
23-
24-
// let result = vm.create_string(message);
25-
Ok(MultiValue::single(message))
23+
let message_str = if message.is_nil() {
24+
None
25+
} else {
26+
// Try to convert to string
27+
Some(vm.value_to_string_raw(&message))
28+
};
29+
30+
// Get level argument (default is 1)
31+
let level = crate::lib_registry::get_arg(vm, 1)
32+
.and_then(|v| v.as_integer())
33+
.unwrap_or(1) as usize;
34+
35+
// Generate traceback, skipping the first 'level' frames
36+
let mut trace = String::new();
37+
38+
if let Some(msg) = message_str {
39+
trace.push_str(&msg);
40+
trace.push('\n');
41+
}
42+
43+
trace.push_str("stack traceback:");
44+
45+
// Iterate through call frames, starting from 'level'
46+
let total_frames = vm.frame_count;
47+
if level < total_frames {
48+
for i in (level..total_frames).rev() {
49+
let frame = &vm.frames[i];
50+
51+
if frame.is_lua() {
52+
if let Some(func_id) = frame.get_function_id() {
53+
if let Some(func) = vm.object_pool.get_function(func_id) {
54+
let chunk = &func.chunk;
55+
let source = chunk.source_name.as_deref().unwrap_or("?");
56+
57+
// Get line number from pc
58+
let pc = frame.pc.saturating_sub(1);
59+
let line = if !chunk.line_info.is_empty() && pc < chunk.line_info.len() {
60+
chunk.line_info[pc]
61+
} else {
62+
0
63+
};
64+
65+
if line > 0 {
66+
trace.push_str(&format!("\n\t{}:{}: in function", source, line));
67+
} else {
68+
trace.push_str(&format!("\n\t{}: in function", source));
69+
}
70+
} else {
71+
trace.push_str("\n\t?: in function");
72+
}
73+
} else {
74+
trace.push_str("\n\t?: in function");
75+
}
76+
} else {
77+
// C function
78+
trace.push_str("\n\t[C]: in function");
79+
}
80+
}
81+
}
82+
83+
let result = vm.create_string(&trace);
84+
Ok(MultiValue::single(result))
2685
}
2786

2887
fn debug_getinfo(_vm: &mut LuaVM) -> LuaResult<MultiValue> {

0 commit comments

Comments
 (0)