11// Helper module for managing the frame stack in LuaJIT, which is highly complicated and fragile.
2- use crate :: { GCfunc , LJState , TValue } ;
2+ use crate :: { BCIns , GCRef , GCfunc , LJ_FR2 , LJState , TValue } ;
33
44pub const FRAME_TYPE : u8 = 3 ;
55pub const FRAME_P : u8 = 4 ;
66pub const FRAME_TYPEP : u8 = FRAME_TYPE | FRAME_P ;
7+
8+ #[ derive( Debug , Copy , Clone , PartialEq , Eq ) ]
79pub enum FrameType {
810 Lua = 0 ,
911 C = 1 ,
@@ -14,26 +16,65 @@ pub enum FrameType {
1416 FfPcallWithHook = 7 ,
1517}
1618
17- #[ derive( Debug ) ]
19+ impl std:: fmt:: Display for FrameType {
20+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
21+ let type_str = match self {
22+ FrameType :: Lua => "Lua" ,
23+ FrameType :: C => "C" ,
24+ FrameType :: Continuation => "Continuation" ,
25+ FrameType :: LuaVararg => "LuaVararg" ,
26+ FrameType :: Cpcall => "Cpcall" ,
27+ FrameType :: FfPcall => "FfPcall" ,
28+ FrameType :: FfPcallWithHook => "FfPcallWithHook" ,
29+ } ;
30+
31+ write ! ( f, "{}" , type_str)
32+ }
33+ }
34+
35+ #[ derive( Debug , Copy , Clone ) ]
1836/// Frames are stored in the LJ state as TValues with their payloads below them in the stack.
1937/// This struct helps manage and interpret these frames.
2038pub struct Frame {
2139 pub tvalue : * mut TValue ,
22- pub size : u32 ,
40+ payload_copy : TValue ,
2341}
2442
2543impl Frame {
26- pub fn new ( tvalue : * mut TValue , size : u32 ) -> Self {
27- Self { tvalue, size }
44+ pub fn new ( tvalue : * mut TValue ) -> Self {
45+ Self {
46+ tvalue,
47+ payload_copy : unsafe { * tvalue. offset ( -1 ) } ,
48+ }
2849 }
2950
3051 pub fn from_debug_ci ( state : & mut LJState , i_ci : i32 ) -> Self {
3152 let offset = ( i_ci as u32 ) & 0xffff ;
32- let size = ( i_ci as u32 ) >> 16 ;
3353 let tvstack = state. stack . as_ptr :: < TValue > ( ) ;
3454 let frametv = unsafe { tvstack. add ( offset as usize ) } ;
3555
36- Frame :: new ( frametv, size)
56+ frametv. into ( )
57+ }
58+
59+ pub fn walk_stack ( state : * mut LJState ) -> Vec < Frame > {
60+ let mut frames = Vec :: new ( ) ;
61+
62+ // traversal is in backwards order
63+ let start = unsafe { ( * state) . stack . as_ptr :: < TValue > ( ) . add ( LJ_FR2 as usize ) } ;
64+ let mut frame = unsafe { ( * state) . base . offset ( -1 ) } ;
65+
66+ while frame > start {
67+ let current_frame: Frame = frame. into ( ) ;
68+ frames. push ( current_frame) ;
69+
70+ if current_frame. is_lua_frame ( ) {
71+ frame = current_frame. get_previous_lua_frame ( ) . tvalue ;
72+ } else {
73+ frame = current_frame. get_previous_delta_frame ( ) . tvalue ;
74+ }
75+ }
76+
77+ frames
3778 }
3879
3980 pub fn get_type ( & self ) -> FrameType {
@@ -51,14 +92,14 @@ impl Frame {
5192 }
5293
5394 pub fn is_lua_frame ( & self ) -> bool {
54- matches ! ( self . get_type( ) , FrameType :: Lua | FrameType :: LuaVararg )
95+ self . get_type ( ) == FrameType :: Lua
5596 }
5697
5798 pub fn is_c_frame ( & self ) -> bool {
58- matches ! ( self . get_type( ) , FrameType :: C | FrameType :: Cpcall )
99+ self . get_type ( ) == FrameType :: C
59100 }
60101
61- pub fn get_gc_func ( & mut self ) -> anyhow:: Result < & mut GCfunc > {
102+ pub fn get_gc_func ( & self ) -> anyhow:: Result < & mut GCfunc > {
62103 unsafe {
63104 let func_tv = self . tvalue . offset ( -1 ) ;
64105 let gcfunc = ( * func_tv) . as_mut :: < GCfunc > ( ) ?;
@@ -69,4 +110,54 @@ impl Frame {
69110 pub fn get_func_tv ( & self ) -> * mut TValue {
70111 unsafe { self . tvalue . offset ( -1 ) }
71112 }
113+
114+ fn overwrite_payload_gcr ( & mut self , new_gcr : GCRef ) {
115+ unsafe {
116+ ( * self . tvalue . offset ( -1 ) ) . gcr = new_gcr;
117+ }
118+ }
119+
120+ fn restore_payload ( & mut self ) {
121+ unsafe {
122+ * self . tvalue . offset ( -1 ) = self . payload_copy ;
123+ }
124+ }
125+
126+ pub fn mark_as_dummy_frame ( & mut self , state : * mut LJState ) {
127+ // We can overwrite the payload to point to the Lua state,
128+ // which is a special GCRef that indicates a dummy frame.
129+ self . overwrite_payload_gcr ( GCRef :: from_ptr ( state) ) ;
130+ }
131+
132+ pub fn restore_from_dummy_frame ( & mut self ) {
133+ self . restore_payload ( ) ;
134+ }
135+
136+ pub fn get_pc ( & self ) -> * const BCIns {
137+ unsafe { ( * self . tvalue ) . ftsz as * const BCIns }
138+ }
139+
140+ pub fn get_previous_lua_frame ( & self ) -> Self {
141+ let bc_ins_ptr = self . get_pc ( ) ;
142+ let bc_ins = unsafe { bc_ins_ptr. offset ( -1 ) . read_unaligned ( ) } ;
143+ let bc_a = bc_ins. a ( ) ;
144+ let offset = ( 1 + LJ_FR2 + ( bc_a as u32 ) ) as isize ;
145+
146+ unsafe { self . tvalue . offset ( -offset) } . into ( )
147+ }
148+
149+ pub fn get_delta_size ( & self ) -> u64 {
150+ unsafe { ( * self . tvalue ) . ftsz & !( FRAME_TYPEP as u64 ) }
151+ }
152+
153+ pub fn get_previous_delta_frame ( & self ) -> Self {
154+ let size = self . get_delta_size ( ) as usize ;
155+ unsafe { self . tvalue . byte_sub ( size) . into ( ) }
156+ }
157+ }
158+
159+ impl Into < Frame > for * mut TValue {
160+ fn into ( self ) -> Frame {
161+ Frame :: new ( self )
162+ }
72163}
0 commit comments