@@ -74,28 +74,27 @@ pub struct GC {
7474 // Lua 5.4 GC debt mechanism
7575 // GC runs when: GCdebt > 0
7676 // totalbytes = actual_bytes - GCdebt
77- pub ( crate ) gc_debt : isize , // Bytes allocated not yet compensated by collector
78- pub ( crate ) total_bytes : usize , // Number of bytes currently allocated - GCdebt
79- gc_estimate : usize , // Estimate of non-garbage memory
80-
77+ pub ( crate ) gc_debt : isize , // Bytes allocated not yet compensated by collector
78+ pub ( crate ) total_bytes : usize , // Number of bytes currently allocated - GCdebt
79+ gc_estimate : usize , // Estimate of non-garbage memory
80+
8181 // GC parameters (Lua 5.4 style)
82- gc_pause : usize , // Pause parameter (default 200 = 200%)
83- gc_step_mul : usize , // Step multiplier (default 100)
84- gen_minor_mul : u8 , // Minor generational control (default 20)
85- gen_major_mul : u8 , // Major generational control (default 100)
86-
82+ gc_pause : usize , // Pause parameter (default 200 = 200%)
83+
8784 // GC mode
88- gc_kind : GCKind , // KGC_INC or KGC_GEN
89- last_atomic : usize , // For generational mode
90-
85+ gc_kind : GCKind , // KGC_INC or KGC_GEN
86+
9187 // Shrink optimization - avoid frequent shrinking
92- shrink_cooldown : u32 , // Shrink 冷却计数器
93- shrink_threshold : u32 , // Shrink 阈值
94-
88+ shrink_cooldown : u32 , // Shrink 冷却计数器
89+ shrink_threshold : u32 , // Shrink 阈值
90+
9591 // Generational GC state
9692 allocations_since_minor_gc : usize ,
9793 minor_gc_count : usize ,
98-
94+
95+ // Finalization support (__gc metamethod)
96+ finalize_queue : Vec < ( GcObjectType , u32 ) > , // Objects awaiting finalization
97+
9998 // Statistics
10099 collection_count : usize ,
101100 stats : GCStats ,
@@ -125,19 +124,16 @@ impl GC {
125124 GC {
126125 objects : HashMap :: new ( ) ,
127126 next_gc_id : 1 ,
128- gc_debt : -( 200 * 1024 ) , // Start with negative debt (can allocate 200KB)
127+ gc_debt : -( 200 * 1024 ) , // Start with negative debt (can allocate 200KB)
129128 total_bytes : 0 ,
130129 gc_estimate : 0 ,
131130 gc_pause : 200 , // 200% pause (LUAI_GCPAUSE)
132- gc_step_mul : 100 , // 100 step mul (LUAI_GCMUL)
133- gen_minor_mul : 20 , // LUAI_GENMINORMUL
134- gen_major_mul : 100 , // LUAI_GENMAJORMUL
135131 gc_kind : GCKind :: Generational , // Default to generational (like Lua 5.4)
136- last_atomic : 0 ,
137132 shrink_cooldown : 0 ,
138- shrink_threshold : 10 , // 每 10 次 GC 才 shrink 一次
133+ shrink_threshold : 10 , // 每 10 次 GC 才 shrink 一次
139134 allocations_since_minor_gc : 0 ,
140135 minor_gc_count : 0 ,
136+ finalize_queue : Vec :: new ( ) , // __gc finalizer queue
141137 collection_count : 0 ,
142138 stats : GCStats :: default ( ) ,
143139 }
@@ -180,18 +176,19 @@ impl GC {
180176 pub fn should_collect ( & self ) -> bool {
181177 self . gc_debt > 0
182178 }
183-
179+
184180 /// Perform one step of GC work (like luaC_step in Lua 5.4)
185181 pub fn step ( & mut self , roots : & [ LuaValue ] , object_pool : & mut crate :: object_pool:: ObjectPool ) {
186182 if !self . should_collect ( ) {
187183 return ;
188184 }
189-
185+
190186 // Determine which collection to run
191187 match self . gc_kind {
192188 GCKind :: Generational => {
193189 // Simple generational: check if we should do minor or major
194- if self . minor_gc_count >= 10 { // Every 10 minor GCs, do a major
190+ if self . minor_gc_count >= 10 {
191+ // Every 10 minor GCs, do a major
195192 self . major_collect_internal ( roots, object_pool) ;
196193 } else {
197194 self . minor_collect_internal ( roots, object_pool) ;
@@ -203,11 +200,11 @@ impl GC {
203200 self . major_collect_internal ( roots, object_pool) ;
204201 }
205202 }
206-
203+
207204 // Reset debt based on estimate
208205 self . set_debt ( ) ;
209206 }
210-
207+
211208 /// Set GC debt based on current memory and pause parameter
212209 fn set_debt ( & mut self ) {
213210 let estimate = self . gc_estimate . max ( 1024 ) ;
@@ -267,6 +264,14 @@ impl GC {
267264
268265 survivors. push ( ( key, obj) ) ;
269266 } else {
267+ // Object is unreachable - check for __gc finalizer
268+ if self . check_finalizer ( obj_type, obj_id, object_pool) {
269+ // Has finalizer, keep alive for one more cycle
270+ // (Finalization happens externally by VM)
271+ survivors. push ( ( key, obj) ) ;
272+ continue ;
273+ }
274+
270275 // Collect garbage - remove from object pool!
271276 collected += 1 ;
272277 match obj_type {
@@ -299,6 +304,20 @@ impl GC {
299304 self . objects . insert ( key, obj) ;
300305 }
301306
307+ // Clean up weak tables after GC
308+ let all_tables: Vec < _ > = self
309+ . objects
310+ . iter ( )
311+ . filter ( |( ( obj_type, _) , _) | * obj_type == GcObjectType :: Table )
312+ . map ( |( ( _, obj_id) , _) | crate :: object_pool:: TableId ( * obj_id) )
313+ . collect ( ) ;
314+
315+ for table_id in all_tables {
316+ if let Some ( weak_mode) = self . get_weak_mode ( table_id, object_pool) {
317+ self . clear_weak_entries ( table_id, & weak_mode, object_pool) ;
318+ }
319+ }
320+
302321 self . stats . objects_collected += collected;
303322 self . stats . promoted_objects += promoted;
304323 self . update_generation_sizes ( ) ;
@@ -335,6 +354,12 @@ impl GC {
335354
336355 for key @ ( obj_type, obj_id) in keys {
337356 if !reachable. contains ( & key) {
357+ // Check for __gc finalizer before collecting
358+ if self . check_finalizer ( obj_type, obj_id, object_pool) {
359+ // Has finalizer, keep alive for one more cycle
360+ continue ;
361+ }
362+
338363 self . objects . remove ( & key) ;
339364 collected += 1 ;
340365
@@ -368,6 +393,20 @@ impl GC {
368393 obj. unmark ( ) ;
369394 }
370395
396+ // Clean up weak tables after GC
397+ let all_tables: Vec < _ > = self
398+ . objects
399+ . iter ( )
400+ . filter ( |( ( obj_type, _) , _) | * obj_type == GcObjectType :: Table )
401+ . map ( |( ( _, obj_id) , _) | crate :: object_pool:: TableId ( * obj_id) )
402+ . collect ( ) ;
403+
404+ for table_id in all_tables {
405+ if let Some ( weak_mode) = self . get_weak_mode ( table_id, object_pool) {
406+ self . clear_weak_entries ( table_id, & weak_mode, object_pool) ;
407+ }
408+ }
409+
371410 self . stats . objects_collected += collected;
372411 self . update_generation_sizes ( ) ;
373412
@@ -484,9 +523,9 @@ impl GC {
484523 crate :: lua_value:: LuaValueKind :: Table => {
485524 value. as_table_id ( ) . map ( |id| ( GcObjectType :: Table , id. 0 ) )
486525 }
487- crate :: lua_value:: LuaValueKind :: Function => {
488- value . as_function_id ( ) . map ( |id| ( GcObjectType :: Function , id . 0 ) )
489- }
526+ crate :: lua_value:: LuaValueKind :: Function => value
527+ . as_function_id ( )
528+ . map ( |id| ( GcObjectType :: Function , id . 0 ) ) ,
490529 _ => None ,
491530 } ;
492531
@@ -541,6 +580,146 @@ impl GC {
541580 // Adjust debt based on current memory
542581 self . adjust_threshold ( ) ;
543582 }
583+
584+ /// Check if object has __gc metamethod that needs to be called
585+ /// This should be called during sweep phase before removing object
586+ pub fn check_finalizer (
587+ & mut self ,
588+ obj_type : GcObjectType ,
589+ obj_id : u32 ,
590+ object_pool : & crate :: object_pool:: ObjectPool ,
591+ ) -> bool {
592+ if obj_type != GcObjectType :: Table {
593+ return false ; // Only tables can have metatables with __gc
594+ }
595+
596+ let table_id = crate :: object_pool:: TableId ( obj_id) ;
597+ if let Some ( table_rc) = object_pool. get_table ( table_id) {
598+ let table = table_rc. borrow ( ) ;
599+ if let Some ( meta_value) = table. get_metatable ( ) {
600+ if let Some ( meta_id) = meta_value. as_table_id ( ) {
601+ if let Some ( meta_table_rc) = object_pool. get_table ( meta_id) {
602+ let meta_table = meta_table_rc. borrow ( ) ;
603+ // Check for __gc key in metatable
604+ // We need to look for the string "__gc" in the metatable
605+ for ( key, value) in meta_table. iter_all ( ) {
606+ if let Some ( string_id) = key. as_string_id ( ) {
607+ if let Some ( string_rc) = object_pool. get_string ( string_id) {
608+ if string_rc. as_str ( ) == "__gc" && !value. is_nil ( ) {
609+ // Has __gc metamethod, add to finalize queue
610+ self . finalize_queue . push ( ( obj_type, obj_id) ) ;
611+ return true ;
612+ }
613+ }
614+ }
615+ }
616+ }
617+ }
618+ }
619+ }
620+ false
621+ }
622+
623+ /// Get finalization queue (for external processing by VM)
624+ /// VM should call __gc metamethods on these objects
625+ pub fn take_finalize_queue ( & mut self ) -> Vec < ( GcObjectType , u32 ) > {
626+ std:: mem:: take ( & mut self . finalize_queue )
627+ }
628+
629+ /// Check if table has weak mode (__mode metamethod)
630+ /// Returns: None if no weak mode, Some("k") for weak keys, Some("v") for weak values, Some("kv") for both
631+ pub fn get_weak_mode (
632+ & self ,
633+ table_id : crate :: object_pool:: TableId ,
634+ object_pool : & crate :: object_pool:: ObjectPool ,
635+ ) -> Option < String > {
636+ if let Some ( table_rc) = object_pool. get_table ( table_id) {
637+ let table = table_rc. borrow ( ) ;
638+ if let Some ( meta_value) = table. get_metatable ( ) {
639+ if let Some ( meta_id) = meta_value. as_table_id ( ) {
640+ if let Some ( meta_table_rc) = object_pool. get_table ( meta_id) {
641+ let meta_table = meta_table_rc. borrow ( ) ;
642+ // Look for __mode key
643+ for ( key, value) in meta_table. iter_all ( ) {
644+ if let Some ( string_id) = key. as_string_id ( ) {
645+ if let Some ( string_rc) = object_pool. get_string ( string_id) {
646+ if string_rc. as_str ( ) == "__mode" {
647+ if let Some ( mode_string_id) = value. as_string_id ( ) {
648+ if let Some ( mode_string_rc) =
649+ object_pool. get_string ( mode_string_id)
650+ {
651+ return Some ( mode_string_rc. as_str ( ) . to_string ( ) ) ;
652+ }
653+ }
654+ }
655+ }
656+ }
657+ }
658+ }
659+ }
660+ }
661+ }
662+ None
663+ }
664+
665+ /// Clear weak references from a weak table after GC
666+ /// This removes entries where the key or value was collected
667+ pub fn clear_weak_entries (
668+ & self ,
669+ table_id : crate :: object_pool:: TableId ,
670+ weak_mode : & str ,
671+ object_pool : & crate :: object_pool:: ObjectPool ,
672+ ) {
673+ if let Some ( table_rc) = object_pool. get_table ( table_id) {
674+ let mut table = table_rc. borrow_mut ( ) ;
675+ let has_weak_keys = weak_mode. contains ( 'k' ) ;
676+ let has_weak_values = weak_mode. contains ( 'v' ) ;
677+
678+ if !has_weak_keys && !has_weak_values {
679+ return ;
680+ }
681+
682+ // Collect keys to remove
683+ let mut keys_to_remove = Vec :: new ( ) ;
684+
685+ for ( key, value) in table. iter_all ( ) {
686+ let mut should_remove = false ;
687+
688+ // Check if weak key was collected
689+ if has_weak_keys {
690+ if let Some ( key_table_id) = key. as_table_id ( ) {
691+ if !self
692+ . objects
693+ . contains_key ( & ( GcObjectType :: Table , key_table_id. 0 ) )
694+ {
695+ should_remove = true ;
696+ }
697+ }
698+ }
699+
700+ // Check if weak value was collected
701+ if has_weak_values && !should_remove {
702+ if let Some ( val_table_id) = value. as_table_id ( ) {
703+ if !self
704+ . objects
705+ . contains_key ( & ( GcObjectType :: Table , val_table_id. 0 ) )
706+ {
707+ should_remove = true ;
708+ }
709+ }
710+ }
711+
712+ if should_remove {
713+ keys_to_remove. push ( key) ;
714+ }
715+ }
716+
717+ // Remove collected weak references (set to nil removes from table)
718+ for key in keys_to_remove {
719+ table. raw_set ( key, LuaValue :: nil ( ) ) ;
720+ }
721+ }
722+ }
544723}
545724
546725/// Memory pool for small object allocation
0 commit comments