Skip to content

Commit bc3ba6a

Browse files
committed
Replaced magic offsets with per-type calculation
removed some macro-like method copy-pastes from CPython and bits of dead code All Python types created to represent CLR concepts derive from `CLR MetaType` (as before), which now has two new fields: - `tp_clr_inst_offset`, which is similar to `tp_dictoffset` in that it tells where to find `GCHandle` in instances of this type (e.g. where to find `GCHandle` pointing to `System.Uri` in corresponding Python object) - `tp_clr_inst`, which holds an optional instance of `ManagedType`, that implements the behavior of the type itself (e.g. `GCHandle` pointing to `ClassObject(type = System.Uri)`) So the layout of all Python types created by Python.NET is PyType type; nint tp_clr_inst_offset; GCHandel tp_clr_inst; (points, for example, to an instance of `ClassObject`) When a Python type, that reflects CLR type is created, the layout of instances will be: BaseTypeFields base; optional (if not in base): IntPtr dict; optional (if not in base): IntPtr weaklist; GCHandle gcHandle; (points to `CLRObject` instance, which in turn, for example, points to the actual instance of `System.Uri`) The offset to GC handle is recorded in the Python type's `tp_clr_inst_offset`, and can be found as `PyObject_Type(inst).tp_clr_inst_offset`. Or, preferably, accessor functions in `ManagedType` should be used.
1 parent 23527d1 commit bc3ba6a

18 files changed

+266
-507
lines changed

src/embed_tests/TestPyType.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Runtime.InteropServices;
12
using System.Text;
23

34
using NUnit.Framework;
@@ -30,7 +31,7 @@ public void CanCreateHeapType()
3031
using var doc = new StrPtr(docStr, Encoding.UTF8);
3132
var spec = new TypeSpec(
3233
name: name,
33-
basicSize: ObjectOffset.Size(Runtime.Runtime.PyTypeType),
34+
basicSize: Marshal.ReadInt32(Runtime.Runtime.PyBaseObjectType, TypeOffset.tp_basicsize),
3435
slots: new TypeSpec.Slot[] {
3536
new (TypeSlotID.tp_doc, doc.RawPointer),
3637
},

src/runtime/classbase.cs

+8-9
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
using System;
22
using System.Collections;
33
using System.Collections.Generic;
4-
using System.Diagnostics;
5-
using System.Runtime.InteropServices;
6-
using System.Runtime.Serialization;
74

85
namespace Python.Runtime
96
{
@@ -355,19 +352,21 @@ public static void tp_dealloc(IntPtr ob)
355352
{
356353
ManagedType self = GetManagedObject(ob);
357354
tp_clear(ob);
358-
Runtime.PyObject_GC_UnTrack(self.pyHandle);
359-
Runtime.PyObject_GC_Del(self.pyHandle);
360-
self.FreeGCHandle();
355+
Runtime.PyObject_GC_UnTrack(ob);
356+
Runtime.PyObject_GC_Del(ob);
357+
self?.FreeGCHandle();
361358
}
362359

363360
public static int tp_clear(IntPtr ob)
364361
{
365362
ManagedType self = GetManagedObject(ob);
366-
if (!self.IsTypeObject())
363+
364+
bool isTypeObject = Runtime.PyObject_TYPE(ob) == Runtime.PyCLRMetaType;
365+
if (!isTypeObject)
367366
{
368367
ClearObjectDict(ob);
369368
}
370-
self.tpHandle = IntPtr.Zero;
369+
if (self is not null) self.tpHandle = IntPtr.Zero;
371370
return 0;
372371
}
373372

@@ -391,7 +390,7 @@ protected override void OnLoad(InterDomainContext context)
391390
SetObjectDict(pyHandle, dict);
392391
}
393392
gcHandle = AllocGCHandle();
394-
Marshal.WriteIntPtr(pyHandle, TypeOffset.magic(), (IntPtr)gcHandle);
393+
SetGCHandle(ObjectReference, gcHandle);
395394
}
396395

397396

src/runtime/classderived.cs

+4-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Diagnostics;
34
using System.Linq;
45
using System.Reflection;
56
using System.Reflection.Emit;
@@ -75,8 +76,8 @@ internal ClassDerivedObject(Type tp) : base(tp)
7576
// So we don't call PyObject_GC_Del here and instead we set the python
7677
// reference to a weak reference so that the C# object can be collected.
7778
GCHandle gc = GCHandle.Alloc(self, GCHandleType.Weak);
78-
int gcOffset = ObjectOffset.magic(Runtime.PyObject_TYPE(self.pyHandle));
79-
Marshal.WriteIntPtr(self.pyHandle, gcOffset, (IntPtr)gc);
79+
Debug.Assert(self.TypeReference == Runtime.PyObject_TYPE(self.ObjectReference));
80+
SetGCHandle(self.ObjectReference, self.TypeReference, gc);
8081
self.gcHandle.Free();
8182
self.gcHandle = gc;
8283
}
@@ -106,7 +107,7 @@ internal static IntPtr ToPython(IPythonDerivedType obj)
106107
Runtime._Py_NewReference(self.pyHandle);
107108
#endif
108109
GCHandle gc = GCHandle.Alloc(self, GCHandleType.Normal);
109-
Marshal.WriteIntPtr(self.pyHandle, ObjectOffset.magic(self.tpHandle), (IntPtr)gc);
110+
SetGCHandle(self.ObjectReference, self.TypeReference, gc);
110111
self.gcHandle.Free();
111112
self.gcHandle = gc;
112113

@@ -883,11 +884,6 @@ public static void Finalize(IPythonDerivedType obj)
883884
// the C# object is being destroyed which must mean there are no more
884885
// references to the Python object as well so now we can dealloc the
885886
// python object.
886-
IntPtr dict = Marshal.ReadIntPtr(self.pyHandle, ObjectOffset.TypeDictOffset(self.tpHandle));
887-
if (dict != IntPtr.Zero)
888-
{
889-
Runtime.XDecref(dict);
890-
}
891887
Runtime.PyObject_GC_Del(self.pyHandle);
892888
self.gcHandle.Free();
893889
}

src/runtime/clrobject.cs

+8-15
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,16 @@ internal CLRObject(object ob, IntPtr tp)
1414
System.Diagnostics.Debug.Assert(tp != IntPtr.Zero);
1515
IntPtr py = Runtime.PyType_GenericAlloc(tp, 0);
1616

17-
var flags = (TypeFlags)Util.ReadCLong(tp, TypeOffset.tp_flags);
18-
if ((flags & TypeFlags.Subclass) != 0)
19-
{
20-
IntPtr dict = Marshal.ReadIntPtr(py, ObjectOffset.TypeDictOffset(tp));
21-
if (dict == IntPtr.Zero)
22-
{
23-
dict = Runtime.PyDict_New();
24-
Marshal.WriteIntPtr(py, ObjectOffset.TypeDictOffset(tp), dict);
25-
}
26-
}
27-
28-
GCHandle gc = AllocGCHandle(TrackTypes.Wrapper);
29-
Marshal.WriteIntPtr(py, ObjectOffset.magic(tp), (IntPtr)gc);
3017
tpHandle = tp;
3118
pyHandle = py;
3219
inst = ob;
3320

21+
GCHandle gc = AllocGCHandle(TrackTypes.Wrapper);
22+
InitGCHandle(ObjectReference, type: TypeReference, gc);
23+
3424
// Fix the BaseException args (and __cause__ in case of Python 3)
3525
// slot if wrapping a CLR exception
36-
Exceptions.SetArgsAndCause(py);
26+
if (ob is Exception e) Exceptions.SetArgsAndCause(e, py);
3727
}
3828

3929
protected CLRObject()
@@ -78,6 +68,9 @@ internal static IntPtr GetInstHandle(object ob)
7868
return co.pyHandle;
7969
}
8070

71+
internal static NewReference GetReference(object ob)
72+
=> NewReference.DangerousFromPointer(GetInstHandle(ob));
73+
8174
internal static CLRObject Restore(object ob, IntPtr pyHandle, InterDomainContext context)
8275
{
8376
CLRObject co = new CLRObject()
@@ -101,7 +94,7 @@ protected override void OnLoad(InterDomainContext context)
10194
{
10295
base.OnLoad(context);
10396
GCHandle gc = AllocGCHandle(TrackTypes.Wrapper);
104-
Marshal.WriteIntPtr(pyHandle, ObjectOffset.magic(tpHandle), (IntPtr)gc);
97+
SetGCHandle(ObjectReference, TypeReference, gc);
10598
}
10699
}
107100
}

src/runtime/converter.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo
580580
{
581581
if (Runtime.PyBytes_Size(value) == 1)
582582
{
583-
op = Runtime.PyBytes_AS_STRING(value);
583+
op = Runtime.PyBytes_AsString(value);
584584
result = (byte)Marshal.ReadByte(op);
585585
return true;
586586
}
@@ -606,7 +606,7 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo
606606
{
607607
if (Runtime.PyBytes_Size(value) == 1)
608608
{
609-
op = Runtime.PyBytes_AS_STRING(value);
609+
op = Runtime.PyBytes_AsString(value);
610610
result = (byte)Marshal.ReadByte(op);
611611
return true;
612612
}
@@ -632,7 +632,7 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo
632632
{
633633
if (Runtime.PyBytes_Size(value) == 1)
634634
{
635-
op = Runtime.PyBytes_AS_STRING(value);
635+
op = Runtime.PyBytes_AsString(value);
636636
result = (byte)Marshal.ReadByte(op);
637637
return true;
638638
}

src/runtime/exceptions.cs

+5-11
Original file line numberDiff line numberDiff line change
@@ -156,15 +156,8 @@ internal static void Shutdown()
156156
/// pointer.
157157
/// </summary>
158158
/// <param name="ob">The python object wrapping </param>
159-
internal static void SetArgsAndCause(IntPtr ob)
159+
internal static void SetArgsAndCause(Exception e, IntPtr ob)
160160
{
161-
// e: A CLR Exception
162-
Exception e = ExceptionClassObject.ToException(ob);
163-
if (e == null)
164-
{
165-
return;
166-
}
167-
168161
IntPtr args;
169162
if (!string.IsNullOrEmpty(e.Message))
170163
{
@@ -177,13 +170,14 @@ internal static void SetArgsAndCause(IntPtr ob)
177170
args = Runtime.PyTuple_New(0);
178171
}
179172

180-
Marshal.WriteIntPtr(ob, ExceptionOffset.args, args);
173+
if (Runtime.PyObject_SetAttrString(ob, "args", args) != 0)
174+
throw new PythonException();
181175

182176
if (e.InnerException != null)
183177
{
184178
// Note: For an AggregateException, InnerException is only the first of the InnerExceptions.
185-
IntPtr cause = CLRObject.GetInstHandle(e.InnerException);
186-
Marshal.WriteIntPtr(ob, ExceptionOffset.cause, cause);
179+
using var cause = CLRObject.GetReference(e.InnerException);
180+
Runtime.PyException_SetCause(ob, cause.DangerousMoveToPointer());
187181
}
188182
}
189183

src/runtime/extensiontype.cs

+7-4
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,16 @@ public ExtensionType()
3333
tpHandle = tp;
3434
pyHandle = py;
3535

36-
SetupGc();
36+
SetupGc(force: false);
3737
}
3838

39-
void SetupGc ()
39+
void SetupGc (bool force)
4040
{
4141
GCHandle gc = AllocGCHandle(TrackTypes.Extension);
42-
Marshal.WriteIntPtr(pyHandle, ObjectOffset.magic(tpHandle), (IntPtr)gc);
42+
if (force)
43+
SetGCHandle(ObjectReference, TypeReference, gc);
44+
else
45+
InitGCHandle(ObjectReference, TypeReference, gc);
4346

4447
// We have to support gc because the type machinery makes it very
4548
// hard not to - but we really don't have a need for it in most
@@ -106,7 +109,7 @@ public static void tp_dealloc(IntPtr ob)
106109
protected override void OnLoad(InterDomainContext context)
107110
{
108111
base.OnLoad(context);
109-
SetupGc();
112+
SetupGc(force: true);
110113
}
111114
}
112115
}

src/runtime/importhook.cs

+1-27
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,6 @@ internal static class ImportHook
1515
private static IntPtr py_clr_module;
1616
static BorrowedReference ClrModuleReference => new BorrowedReference(py_clr_module);
1717

18-
private static IntPtr module_def = IntPtr.Zero;
19-
20-
internal static void InitializeModuleDef()
21-
{
22-
if (module_def == IntPtr.Zero)
23-
{
24-
module_def = ModuleDefOffset.AllocModuleDef("clr");
25-
}
26-
}
27-
28-
internal static void ReleaseModuleDef()
29-
{
30-
if (module_def == IntPtr.Zero)
31-
{
32-
return;
33-
}
34-
ModuleDefOffset.FreeModuleDef(module_def);
35-
module_def = IntPtr.Zero;
36-
}
37-
3818
/// <summary>
3919
/// Initialize just the __import__ hook itself.
4020
/// </summary>
@@ -90,8 +70,7 @@ internal static unsafe void Initialize()
9070
root = new CLRModule();
9171

9272
// create a python module with the same methods as the clr module-like object
93-
InitializeModuleDef();
94-
py_clr_module = Runtime.PyModule_Create2(module_def, 3);
73+
py_clr_module = Runtime.PyModule_New("clr").DangerousMoveToPointer();
9574

9675
// both dicts are borrowed references
9776
BorrowedReference mod_dict = Runtime.PyModule_GetDict(ClrModuleReference);
@@ -116,13 +95,8 @@ internal static void Shutdown()
11695

11796
RestoreImport();
11897

119-
bool shouldFreeDef = Runtime.Refcount(py_clr_module) == 1;
12098
Runtime.XDecref(py_clr_module);
12199
py_clr_module = IntPtr.Zero;
122-
if (shouldFreeDef)
123-
{
124-
ReleaseModuleDef();
125-
}
126100

127101
Runtime.XDecref(root.pyHandle);
128102
root = null;

0 commit comments

Comments
 (0)