@@ -21,8 +21,8 @@ use std::rc::Rc;
2121pub type LuaResult < T > = Result < T , LuaError > ;
2222
2323/// Maximum call stack depth (similar to LUAI_MAXCCALLS in Lua)
24- /// Lua uses 200 for the limit, we use 256 for power-of-2 alignment
25- pub const MAX_CALL_DEPTH : usize = 256 ;
24+ /// Using a lower limit because Rust stack space is limited
25+ pub const MAX_CALL_DEPTH : usize = 64 ;
2626
2727pub struct LuaVM {
2828 // Global environment table (_G and _ENV point to this)
@@ -403,12 +403,33 @@ impl LuaVM {
403403 // - Main VM: pre-filled to MAX_CALL_DEPTH for direct index access
404404 // - Coroutines: start small and grow on demand (like Lua's linked list CallInfo)
405405
406+ /// Push a new frame onto the call stack and return stable pointer
407+ /// OPTIMIZED: Direct index write when capacity allows, grow on demand otherwise
408+ /// Returns None if call stack overflow (for pcall error handling)
409+ #[ inline( always) ]
410+ pub ( crate ) fn try_push_frame ( & mut self , frame : LuaCallFrame ) -> Option < * mut LuaCallFrame > {
411+ let idx = self . frame_count ;
412+ if idx >= MAX_CALL_DEPTH {
413+ return None ; // Stack overflow
414+ }
415+
416+ // Fast path: direct write if Vec is pre-filled or has space
417+ if idx < self . frames . len ( ) {
418+ self . frames [ idx] = frame;
419+ } else {
420+ // Slow path: grow the Vec (for coroutines with on-demand allocation)
421+ self . frames . push ( frame) ;
422+ }
423+ self . frame_count = idx + 1 ;
424+ Some ( & mut self . frames [ idx] as * mut LuaCallFrame )
425+ }
426+
406427 /// Push a new frame onto the call stack and return stable pointer
407428 /// OPTIMIZED: Direct index write when capacity allows, grow on demand otherwise
408429 #[ inline( always) ]
409430 pub ( crate ) fn push_frame ( & mut self , frame : LuaCallFrame ) -> * mut LuaCallFrame {
410431 let idx = self . frame_count ;
411- debug_assert ! ( idx < MAX_CALL_DEPTH , "call stack overflow" ) ;
432+ assert ! ( idx < MAX_CALL_DEPTH , "call stack overflow" ) ;
412433
413434 // Fast path: direct write if Vec is pre-filled or has space
414435 if idx < self . frames . len ( ) {
@@ -2767,7 +2788,9 @@ impl LuaVM {
27672788 }
27682789
27692790 let temp_frame = LuaCallFrame :: new_c_function ( new_base, stack_size) ;
2770- self . push_frame ( temp_frame) ;
2791+ if self . try_push_frame ( temp_frame) . is_none ( ) {
2792+ return Err ( self . error ( "C stack overflow" . to_string ( ) ) ) ;
2793+ }
27712794
27722795 match cfunc ( self ) {
27732796 Ok ( r) => {
@@ -2823,9 +2846,11 @@ impl LuaVM {
28232846 }
28242847 }
28252848
2826- // Push boundary frame and Lua function frame
2849+ // Push boundary frame and Lua function frame - check for stack overflow
28272850 let boundary_frame = LuaCallFrame :: new_c_function ( new_base, 0 ) ;
2828- self . push_frame ( boundary_frame) ;
2851+ if self . try_push_frame ( boundary_frame) . is_none ( ) {
2852+ return Err ( self . error ( "C stack overflow" . to_string ( ) ) ) ;
2853+ }
28292854
28302855 let new_frame = LuaCallFrame :: new_lua_function (
28312856 func_id,
@@ -2838,7 +2863,10 @@ impl LuaVM {
28382863 -1 ,
28392864 max_stack_size,
28402865 ) ;
2841- self . push_frame ( new_frame) ;
2866+ if self . try_push_frame ( new_frame) . is_none ( ) {
2867+ self . pop_frame_discard ( ) ; // Pop the boundary frame
2868+ return Err ( self . error ( "C stack overflow" . to_string ( ) ) ) ;
2869+ }
28422870
28432871 let exec_result = execute:: luavm_execute ( self ) ;
28442872
@@ -2940,7 +2968,9 @@ impl LuaVM {
29402968
29412969 // Create C function frame
29422970 let temp_frame = LuaCallFrame :: new_c_function ( new_base, stack_size) ;
2943- self . push_frame ( temp_frame) ;
2971+ if self . try_push_frame ( temp_frame) . is_none ( ) {
2972+ return Err ( self . error ( "C stack overflow" . to_string ( ) ) ) ;
2973+ }
29442974
29452975 // Call CFunction
29462976 let result = match cfunc ( self ) {
@@ -3006,7 +3036,9 @@ impl LuaVM {
30063036
30073037 // Push C function boundary frame - RETURN will detect this and write to return_values
30083038 let boundary_frame = LuaCallFrame :: new_c_function ( new_base, 0 ) ;
3009- self . push_frame ( boundary_frame) ;
3039+ if self . try_push_frame ( boundary_frame) . is_none ( ) {
3040+ return Err ( self . error ( "C stack overflow" . to_string ( ) ) ) ;
3041+ }
30103042
30113043 // Push Lua function frame
30123044 let new_frame = LuaCallFrame :: new_lua_function (
@@ -3020,7 +3052,10 @@ impl LuaVM {
30203052 -1 , // LUA_MULTRET
30213053 max_stack_size,
30223054 ) ;
3023- self . push_frame ( new_frame) ;
3055+ if self . try_push_frame ( new_frame) . is_none ( ) {
3056+ self . pop_frame_discard ( ) ; // Pop the boundary frame
3057+ return Err ( self . error ( "C stack overflow" . to_string ( ) ) ) ;
3058+ }
30243059
30253060 // Execute using the main dispatcher - no duplicate code!
30263061 let exec_result = execute:: luavm_execute ( self ) ;
0 commit comments