Skip to content

Commit 7555dec

Browse files
committed
fixed debug assert when inheriting from RuntimeError
This simplifies and generalizes logic for allocating extra fields when inheriting from Python classes. The new class instance size is base_size + clr_pointer_size + (optional, only if not in the base already) weaklist&dict
1 parent 9cc2b45 commit 7555dec

3 files changed

Lines changed: 49 additions & 29 deletions

File tree

src/embed_tests/Inheritance.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,26 @@ public void PythonCanSetAdHocAttributes() {
104104
Assert.AreEqual(expected: Inherited.X, actual);
105105
}
106106
}
107+
108+
[Test]
109+
public void FromException() {
110+
using var _ = Py.GIL();
111+
var runtimeError = PythonEngine.Eval("RuntimeError");
112+
PythonEngine.InteropConfiguration.PythonBaseTypeProviders.Add(
113+
new TestRuntimeError.BaseProvdier());
114+
var instance = new TestRuntimeError().ToPython();
115+
bool isRuntimeError = PyIsInstance(instance, runtimeError);
116+
Assert.IsTrue(isRuntimeError);
117+
}
118+
}
119+
120+
public class TestRuntimeError: Exception {
121+
internal class BaseProvdier: IPythonBaseTypeProvider {
122+
public IEnumerable<PyObject> GetBaseTypes(Type type, IList<PyObject> existingBases)
123+
=> type != typeof(TestRuntimeError)
124+
? existingBases
125+
: new[] { PythonEngine.Eval("RuntimeError") };
126+
}
107127
}
108128

109129
class CustomBaseTypeProvider : IPythonBaseTypeProvider {

src/runtime/interop.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,16 +111,18 @@ internal static void ClrGcHandleOffsetAssertSanity(int offset)
111111
/// <summary>
112112
/// Returns dict offset in instances of the specified <paramref name="type"/>
113113
/// </summary>
114-
public static int TypeDictOffset(BorrowedReference type) {
114+
public static unsafe int TypeDictOffset(BorrowedReference type) {
115+
#if DEBUG
115116
if (!Runtime.PyType_Check(type))
116117
throw new ArgumentException("Bad object type");
118+
#endif
117119

118-
return IsExceptionSubtype(type)
119-
? ExceptionOffset.ob_dict
120-
: ob_dict;
120+
IntPtr dictoffset = type.DangerousGetAddress() + TypeOffset.tp_dictoffset;
121+
int dict = *((int*)(dictoffset));
122+
Debug.Assert(dict > 0 && dict < 100_000);
123+
return dict;
121124
}
122-
public static int TypeDictOffset(IntPtr type)
123-
=> TypeDictOffset(new BorrowedReference(type));
125+
public static int TypeDictOffset() => ob_dict;
124126

125127
public static int Size(IntPtr ob) {
126128
if ((Runtime.PyObject_TypeCheck(ob, Exceptions.BaseException) ||

src/runtime/typemanager.cs

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ internal static IntPtr CreateType(Type impl)
8888
// Set tp_basicsize to the size of our managed instance objects.
8989
Marshal.WriteIntPtr(type, TypeOffset.tp_basicsize, (IntPtr)ob_size);
9090

91-
var offset = (IntPtr)ObjectOffset.TypeDictOffset(type);
91+
var offset = (IntPtr)ObjectOffset.TypeDictOffset();
9292
Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, offset);
9393

9494
InitializeSlots(type, impl);
@@ -109,21 +109,11 @@ internal static IntPtr CreateType(Type impl)
109109
return type;
110110
}
111111

112-
113112
internal static IntPtr CreateType(ManagedType impl, Type clrType)
114113
{
115114
string name = GetPythonTypeName(clrType);
116115

117116
int ob_size;
118-
int tp_dictoffset = ObjectOffset.TypeDictOffset(Runtime.PyTypeType);
119-
120-
// XXX Hack, use a different base class for System.Exception
121-
// Python 2.5+ allows new style class exceptions but they *must*
122-
// subclass BaseException (or better Exception).
123-
if (typeof(Exception).IsAssignableFrom(clrType))
124-
{
125-
tp_dictoffset = ObjectOffset.TypeDictOffset(Exceptions.Exception);
126-
}
127117

128118
IntPtr type = AllocateTypeObject(name, typeType: Runtime.PyCLRMetaType);
129119

@@ -159,22 +149,29 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType)
159149
Marshal.WriteIntPtr(type, TypeOffset.tp_bases, baseTuple.Reference.DangerousIncRefOrNull());
160150
}
161151

162-
int baseSize = primaryBase == Runtime.PyBaseObjectType ? ObjectOffset.PyObject_HEAD_Size()
163-
: primaryBase == Exceptions.Exception ? ExceptionOffset.Size()
164-
: checked((int)Marshal.ReadIntPtr(primaryBase, TypeOffset.tp_basicsize));
165-
if (!ManagedType.IsManagedType(primaryBase))
166-
{
152+
ob_size = checked((int)Marshal.ReadIntPtr(primaryBase, TypeOffset.tp_basicsize));
153+
void InheritOrAllocate(int typeField) {
154+
int value = Marshal.ReadInt32(primaryBase, typeField);
155+
if (value == 0) {
156+
Marshal.WriteIntPtr(type, typeField, new IntPtr(ob_size));
157+
ob_size += IntPtr.Size;
158+
} else {
159+
Marshal.WriteIntPtr(type, typeField, new IntPtr(value));
160+
}
161+
}
162+
163+
InheritOrAllocate(TypeOffset.tp_dictoffset);
164+
InheritOrAllocate(TypeOffset.tp_weaklistoffset);
165+
166+
if (!ManagedType.IsManagedType(primaryBase)) {
167167
// base type is a Python type, so we must allocate additional space for GC handle
168-
extraTypeDataOffset = baseSize;
168+
extraTypeDataOffset = ob_size;
169169
ObjectOffset.ClrGcHandleOffsetAssertSanity(extraTypeDataOffset);
170-
ob_size = baseSize + MetaType.ExtraTypeDataSize;
171-
} else
172-
{
170+
ob_size += MetaType.ExtraTypeDataSize;
171+
} else {
173172
extraTypeDataOffset = checked((int)Marshal.ReadIntPtr(primaryBase, TypeOffset.clr_gchandle_offset));
174173
ObjectOffset.ClrGcHandleOffsetAssertSanity(extraTypeDataOffset);
175-
ob_size = baseSize;
176174
}
177-
Debug.Assert(extraTypeDataOffset > tp_dictoffset);
178175
}
179176
catch (Exception error)
180177
{
@@ -186,7 +183,6 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType)
186183

187184
Marshal.WriteIntPtr(type, TypeOffset.tp_basicsize, (IntPtr)ob_size);
188185
Marshal.WriteIntPtr(type, TypeOffset.tp_itemsize, IntPtr.Zero);
189-
Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, (IntPtr)tp_dictoffset);
190186
Marshal.WriteIntPtr(type, TypeOffset.clr_gchandle_offset, (IntPtr)extraTypeDataOffset);
191187

192188
var flags = TypeFlags.Default;
@@ -206,6 +202,8 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType)
206202
return IntPtr.Zero;
207203
}
208204

205+
Debug.Assert(extraTypeDataOffset > Marshal.ReadInt32(type, TypeOffset.tp_dictoffset));
206+
209207
IntPtr dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict);
210208
string mn = clrType.Namespace ?? "";
211209
IntPtr mod = Runtime.PyString_FromString(mn);

0 commit comments

Comments
 (0)