1
1
using System ;
2
+ using System . Collections . Generic ;
2
3
using System . Diagnostics ;
4
+ using System . Linq ;
5
+ using System . Reflection ;
3
6
using System . Runtime . InteropServices ;
4
7
using System . Runtime . Serialization ;
5
8
@@ -79,41 +82,80 @@ public static NewReference tp_new(BorrowedReference tp, BorrowedReference args,
79
82
BorrowedReference bases = Runtime . PyTuple_GetItem ( args , 1 ) ;
80
83
BorrowedReference dict = Runtime . PyTuple_GetItem ( args , 2 ) ;
81
84
82
- // We do not support multiple inheritance, so the bases argument
83
- // should be a 1-item tuple containing the type we are subtyping.
84
- // That type must itself have a managed implementation. We check
85
- // that by making sure its metatype is the CLR metatype.
85
+ // Extract interface types and base class types.
86
+ var interfaces = new List < Type > ( ) ;
86
87
87
- if ( Runtime . PyTuple_Size ( bases ) != 1 )
88
- {
89
- return Exceptions . RaiseTypeError ( "cannot use multiple inheritance with managed classes" ) ;
90
- }
88
+ // More than one base type case be declared, but an exception will be thrown
89
+ // if more than one is a class/not an interface.
90
+ var baseTypes = new List < ClassBase > ( ) ;
91
91
92
- BorrowedReference base_type = Runtime . PyTuple_GetItem ( bases , 0 ) ;
93
- BorrowedReference mt = Runtime . PyObject_TYPE ( base_type ) ;
94
-
95
- if ( ! ( mt == PyCLRMetaType || mt == Runtime . PyTypeType ) )
92
+ var baseClassCount = Runtime . PyTuple_Size ( bases ) ;
93
+ if ( baseClassCount == 0 )
96
94
{
97
- return Exceptions . RaiseTypeError ( "invalid metatype " ) ;
95
+ return Exceptions . RaiseTypeError ( "zero base classes " ) ;
98
96
}
99
97
100
- // Ensure that the reflected type is appropriate for subclassing,
101
- // disallowing subclassing of delegates, enums and array types.
102
-
103
- if ( GetManagedObject ( base_type ) is ClassBase cb )
98
+ for ( nint i = 0 ; i < baseClassCount ; i ++ )
104
99
{
105
- try
100
+ var baseTypeIt = Runtime . PyTuple_GetItem ( bases , ( int ) i ) ;
101
+
102
+ if ( GetManagedObject ( baseTypeIt ) is ClassBase classBaseIt )
106
103
{
107
- if ( ! cb . CanSubclass ( ) )
104
+ if ( ! classBaseIt . type . Valid )
105
+ {
106
+ return Exceptions . RaiseTypeError ( "Invalid type used as a super type." ) ;
107
+ }
108
+ if ( classBaseIt . type . Value . IsInterface )
108
109
{
109
- return Exceptions . RaiseTypeError ( "delegates, enums and array types cannot be subclassed" ) ;
110
+ interfaces . Add ( classBaseIt . type . Value ) ;
110
111
}
112
+ else
113
+ {
114
+ baseTypes . Add ( classBaseIt ) ;
115
+ }
116
+ }
117
+ else
118
+ {
119
+ return Exceptions . RaiseTypeError ( "Non .NET type used as super class for meta type. This is not supported." ) ;
111
120
}
112
- catch ( SerializationException )
121
+ }
122
+ // if the base type count is 0, there might still be interfaces to implement.
123
+ if ( baseTypes . Count == 0 )
124
+ {
125
+ baseTypes . Add ( new ClassBase ( typeof ( object ) ) ) ;
126
+ }
127
+
128
+ // Multiple inheritance is not supported, unless the other types are interfaces
129
+ if ( baseTypes . Count > 1 )
130
+ {
131
+ var types = string . Join ( ", " , baseTypes . Select ( baseType => baseType . type . Value ) ) ;
132
+ return Exceptions . RaiseTypeError ( $ "Multiple inheritance with managed classes cannot be used. Types: { types } ") ;
133
+ }
134
+
135
+ // check if the list of interfaces contains no duplicates.
136
+ if ( interfaces . Distinct ( ) . Count ( ) != interfaces . Count )
137
+ {
138
+ // generate a string containing the problematic types.
139
+ var duplicateTypes = interfaces . GroupBy ( type => type )
140
+ . Where ( typeGroup => typeGroup . Count ( ) > 1 )
141
+ . Select ( typeGroup => typeGroup . Key ) ;
142
+ var duplicateTypesString = string . Join ( ", " , duplicateTypes ) ;
143
+
144
+ return Exceptions . RaiseTypeError ( $ "An interface can only be implemented once. Duplicate types: { duplicateTypesString } ") ;
145
+ }
146
+
147
+ var cb = baseTypes [ 0 ] ;
148
+ try
149
+ {
150
+ if ( ! cb . CanSubclass ( ) )
113
151
{
114
- return Exceptions . RaiseTypeError ( $ "Underlying C# Base class { cb . type } has been deleted ") ;
152
+ return Exceptions . RaiseTypeError ( "delegates, enums and array types cannot be subclassed ") ;
115
153
}
116
154
}
155
+ catch ( SerializationException )
156
+ {
157
+ return Exceptions . RaiseTypeError ( $ "Underlying C# Base class { cb . type } has been deleted") ;
158
+ }
117
159
118
160
BorrowedReference slots = Runtime . PyDict_GetItem ( dict , PyIdentifier . __slots__ ) ;
119
161
if ( slots != null )
@@ -130,10 +172,12 @@ public static NewReference tp_new(BorrowedReference tp, BorrowedReference args,
130
172
using var clsDict = new PyDict ( dict ) ;
131
173
if ( clsDict . HasKey ( "__assembly__" ) || clsDict . HasKey ( "__namespace__" ) )
132
174
{
133
- return TypeManager . CreateSubType ( name , base_type , clsDict ) ;
175
+ return TypeManager . CreateSubType ( name , baseTypes [ 0 ] , interfaces , clsDict ) ;
134
176
}
135
177
}
136
178
179
+ var base_type = Runtime . PyTuple_GetItem ( bases , 0 ) ;
180
+
137
181
// otherwise just create a basic type without reflecting back into the managed side.
138
182
IntPtr func = Util . ReadIntPtr ( Runtime . PyTypeType , TypeOffset . tp_new ) ;
139
183
NewReference type = NativeCall . Call_3 ( func , tp , args , kw ) ;
0 commit comments