@@ -42,9 +42,17 @@ pub fn Type(comptime name: [:0]const u8, comptime definition: type) type {
42
42
};
43
43
44
44
const bases = Bases (definition );
45
- const attrs = Attributes (definition );
46
45
const slots = Slots (definition , name );
47
46
47
+ const flags = blk : {
48
+ var flags_ : usize = ffi .Py_TPFLAGS_DEFAULT | ffi .Py_TPFLAGS_BASETYPE ;
49
+ if (slots .gc .needsGc ) {
50
+ flags_ |= ffi .Py_TPFLAGS_HAVE_GC ;
51
+ }
52
+
53
+ break :blk flags_ ;
54
+ };
55
+
48
56
pub fn init (module : py.PyModule ) PyError ! py.PyObject {
49
57
var basesPtr : ? * ffi.PyObject = null ;
50
58
if (bases .bases .len > 0 ) {
@@ -61,7 +69,7 @@ pub fn Type(comptime name: [:0]const u8, comptime definition: type) type {
61
69
.name = qualifiedName .ptr ,
62
70
.basicsize = @sizeOf (PyTypeStruct (definition )),
63
71
.itemsize = 0 ,
64
- .flags = ffi . Py_TPFLAGS_DEFAULT | ffi . Py_TPFLAGS_BASETYPE ,
72
+ .flags = flags ,
65
73
.slots = @constCast (slots .slots .ptr ),
66
74
};
67
75
@@ -105,11 +113,22 @@ fn Slots(comptime definition: type, comptime name: [:0]const u8) type {
105
113
const properties = Properties (definition );
106
114
const doc = Doc (definition , name );
107
115
const richcmp = RichCompare (definition );
116
+ const gc = GC (definition );
108
117
109
118
/// Slots populated in the PyType
110
119
pub const slots : [:empty ]const ffi.PyType_Slot = blk : {
111
120
var slots_ : [:empty ]const ffi.PyType_Slot = &.{};
112
121
122
+ if (gc .needsGc ) {
123
+ slots_ = slots_ ++ .{ ffi.PyType_Slot {
124
+ .slot = ffi .Py_tp_clear ,
125
+ .pfunc = @constCast (& gc .tp_clear ),
126
+ }, ffi.PyType_Slot {
127
+ .slot = ffi .Py_tp_traverse ,
128
+ .pfunc = @constCast (& gc .tp_traverse ),
129
+ } };
130
+ }
131
+
113
132
if (doc .docLen != 0 ) {
114
133
slots_ = slots_ ++ .{ffi.PyType_Slot {
115
134
.slot = ffi .Py_tp_doc ,
@@ -305,7 +324,7 @@ fn Slots(comptime definition: type, comptime name: [:0]const u8) type {
305
324
/// Note: tp_del is deprecated in favour of tp_finalize.
306
325
///
307
326
/// See https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_finalize.
308
- fn tp_finalize (pyself : * ffi.PyObject ) void {
327
+ fn tp_finalize (pyself : * ffi.PyObject ) callconv ( .C ) void {
309
328
// The finalize slot shouldn't alter any exception that is currently set.
310
329
// So it's recommended we save the existing one (if any) and restore it afterwards.
311
330
// NOTE(ngates): we may want to move this logic to PyErr if it happens more?
@@ -455,6 +474,183 @@ fn Doc(comptime definition: type, comptime name: [:0]const u8) type {
455
474
};
456
475
}
457
476
477
+ fn GC (comptime definition : type ) type {
478
+ const VisitProc = * const fn (* ffi.PyObject , * anyopaque ) callconv (.C ) c_int ;
479
+
480
+ return struct {
481
+ const needsGc = classNeedsGc (definition );
482
+
483
+ fn classNeedsGc (comptime CT : type ) bool {
484
+ inline for (@typeInfo (CT ).Struct .fields ) | field | {
485
+ if (typeNeedsGc (field .type )) {
486
+ return true ;
487
+ }
488
+ }
489
+ return false ;
490
+ }
491
+
492
+ fn typeNeedsGc (comptime FT : type ) bool {
493
+ return switch (@typeInfo (FT )) {
494
+ .Pointer = > | p | @typeInfo (p .child ) == .Struct and (p .child == ffi .PyObject or typeNeedsGc (p .child )),
495
+ .Struct = > blk : {
496
+ if (State .findDefinition (FT )) | def | {
497
+ break :blk switch (def .type ) {
498
+ .attribute = > typeNeedsGc (@typeInfo (FT ).Struct .fields [0 ].type ),
499
+ .property = > classNeedsGc (FT ),
500
+ .class , .module = > false ,
501
+ };
502
+ } else {
503
+ break :blk @hasField (FT , "obj" ) and @hasField (std .meta .fieldInfo (FT , .obj ).type , "py" ) or FT == py .PyObject ;
504
+ }
505
+ },
506
+ .Optional = > | o | (@typeInfo (o .child ) == .Struct or @typeInfo (o .child ) == .Pointer ) and typeNeedsGc (o .child ),
507
+ else = > return false ,
508
+ };
509
+ }
510
+
511
+ fn tp_clear (pyself : * ffi.PyObject ) callconv (.C ) c_int {
512
+ var self : * PyTypeStruct (definition ) = @ptrCast (pyself );
513
+ clearFields (self .state );
514
+ return 0 ;
515
+ }
516
+
517
+ fn clearFields (class : anytype ) void {
518
+ inline for (@typeInfo (@TypeOf (class )).Struct .fields ) | field | {
519
+ clear (@field (class , field .name ));
520
+ }
521
+ }
522
+
523
+ fn clear (obj : anytype ) void {
524
+ const fieldType = @TypeOf (obj );
525
+ switch (@typeInfo (fieldType )) {
526
+ .Pointer = > | p | if (@typeInfo (p .child ) == .Struct ) {
527
+ if (p .child == ffi .PyObject ) {
528
+ pyClear (obj );
529
+ }
530
+ if (State .findDefinition (fieldType )) | def | {
531
+ if (def .type == .class ) {
532
+ pyClear (py .object (obj ).py );
533
+ }
534
+ }
535
+ },
536
+ .Struct = > {
537
+ if (State .findDefinition (fieldType )) | def | {
538
+ switch (def .type ) {
539
+ .attribute = > clear (@field (obj , @typeInfo (fieldType ).Struct .fields [0 ].name )),
540
+ .property = > clearFields (obj ),
541
+ .class , .module = > {},
542
+ }
543
+ } else {
544
+ if (@hasField (fieldType , "obj" ) and @hasField (std .meta .fieldInfo (fieldType , .obj ).type , "py" )) {
545
+ pyClear (obj .obj .py );
546
+ }
547
+
548
+ if (fieldType == py .PyObject ) {
549
+ pyClear (obj .py );
550
+ }
551
+ }
552
+ },
553
+ .Optional = > | o | if (@typeInfo (o .child ) == .Struct or @typeInfo (o .child ) == .Pointer ) {
554
+ if (obj == null ) {
555
+ return ;
556
+ }
557
+
558
+ clear (obj .? );
559
+ },
560
+ else = > {},
561
+ }
562
+ }
563
+
564
+ inline fn pyClear (obj : * ffi.PyObject ) void {
565
+ var objRef = @constCast (& obj );
566
+ const objOld = objRef .* ;
567
+ objRef .* = undefined ;
568
+ py .decref (objOld );
569
+ }
570
+
571
+ /// Visit all members of pyself. We visit all PyObjects that this object references
572
+ fn tp_traverse (pyself : * ffi.PyObject , visit : VisitProc , arg : * anyopaque ) callconv (.C ) c_int {
573
+ if (pyVisit (py .type_ (pyself ).obj .py , visit , arg )) | ret | {
574
+ return ret ;
575
+ }
576
+
577
+ const self : * const PyTypeStruct (definition ) = @ptrCast (pyself );
578
+ if (traverseFields (self .state , visit , arg )) | ret | {
579
+ return ret ;
580
+ }
581
+ return 0 ;
582
+ }
583
+
584
+ fn traverseFields (class : anytype , visit : VisitProc , arg : * anyopaque ) ? c_int {
585
+ inline for (@typeInfo (@TypeOf (class )).Struct .fields ) | field | {
586
+ if (traverse (@field (class , field .name ), visit , arg )) | ret | {
587
+ return ret ;
588
+ }
589
+ }
590
+ return null ;
591
+ }
592
+
593
+ fn traverse (obj : anytype , visit : VisitProc , arg : * anyopaque ) ? c_int {
594
+ const fieldType = @TypeOf (obj );
595
+ switch (@typeInfo (@TypeOf (obj ))) {
596
+ .Pointer = > | p | if (@typeInfo (p .child ) == .Struct ) {
597
+ if (p .child == ffi .PyObject ) {
598
+ if (pyVisit (obj , visit , arg )) | ret | {
599
+ return ret ;
600
+ }
601
+ }
602
+ if (State .findDefinition (fieldType )) | def | {
603
+ if (def .type == .class ) {
604
+ if (pyVisit (py .object (obj ).py , visit , arg )) | ret | {
605
+ return ret ;
606
+ }
607
+ }
608
+ }
609
+ },
610
+ .Struct = > if (State .findDefinition (fieldType )) | def | {
611
+ switch (def .type ) {
612
+ .attribute = > if (traverse (@field (obj , @typeInfo (@TypeOf (obj )).Struct .fields [0 ].name ), visit , arg )) | ret | {
613
+ return ret ;
614
+ },
615
+ .property = > if (traverseFields (obj , visit , arg )) | ret | {
616
+ return ret ;
617
+ },
618
+ .class , .module = > {},
619
+ }
620
+ } else {
621
+ if (@hasField (fieldType , "obj" ) and @hasField (std .meta .fieldInfo (fieldType , .obj ).type , "py" )) {
622
+ if (pyVisit (obj .obj .py , visit , arg )) | ret | {
623
+ return ret ;
624
+ }
625
+ }
626
+
627
+ if (fieldType == py .PyObject ) {
628
+ if (pyVisit (obj .py , visit , arg )) | ret | {
629
+ return ret ;
630
+ }
631
+ }
632
+ },
633
+ .Optional = > | o | if (@typeInfo (o .child ) == .Struct or @typeInfo (o .child ) == .Pointer ) {
634
+ if (obj == null ) {
635
+ return null ;
636
+ }
637
+
638
+ if (traverse (obj .? , visit , arg )) | ret | {
639
+ return ret ;
640
+ }
641
+ },
642
+ else = > return null ,
643
+ }
644
+ return null ;
645
+ }
646
+
647
+ inline fn pyVisit (obj : * ffi.PyObject , visit : VisitProc , arg : * anyopaque ) ? c_int {
648
+ const ret = visit (obj , arg );
649
+ return if (ret != 0 ) ret else null ;
650
+ }
651
+ };
652
+ }
653
+
458
654
fn Members (comptime definition : type ) type {
459
655
return struct {
460
656
const count = State .countFieldsWithType (definition , .attribute );
0 commit comments