Skip to content

Commit 972c41d

Browse files
committed
WIP: use C# 9 function pointers for PInvoke
1 parent 21683b3 commit 972c41d

16 files changed

+1345
-800
lines changed

src/runtime/BorrowedReference.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ readonly ref struct BorrowedReference
1010
readonly IntPtr pointer;
1111
public bool IsNull => this.pointer == IntPtr.Zero;
1212

13-
1413
/// <summary>Gets a raw pointer to the Python object</summary>
1514
public IntPtr DangerousGetAddress()
1615
=> this.IsNull ? throw new NullReferenceException() : this.pointer;
16+
/// <summary>Gets a raw pointer to the Python object</summary>
17+
public IntPtr DangerousGetAddressOrNull() => this.pointer;
1718

1819
/// <summary>
1920
/// Creates new instance of <see cref="BorrowedReference"/> from raw pointer. Unsafe.

src/runtime/NewReference.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,16 @@ public PyObject MoveToPyObject()
2828
return result;
2929
}
3030

31+
/// <summary>Moves ownership of this instance to unmanged pointer</summary>
32+
public IntPtr DangerousMoveToPointer()
33+
{
34+
if (this.IsNull()) throw new NullReferenceException();
35+
36+
var result = this.pointer;
37+
this.pointer = IntPtr.Zero;
38+
return result;
39+
}
40+
3141
/// <summary>Moves ownership of this instance to unmanged pointer</summary>
3242
public IntPtr DangerousMoveToPointerOrNull()
3343
{

src/runtime/Python.Runtime.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<PropertyGroup>
33
<TargetFrameworks>netstandard2.0</TargetFrameworks>
44
<Platforms>AnyCPU</Platforms>
5+
<LangVersion>9.0</LangVersion>
56
<RootNamespace>Python.Runtime</RootNamespace>
67
<AssemblyName>Python.Runtime</AssemblyName>
78
<LangVersion>9.0</LangVersion>

src/runtime/classmanager.cs

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ internal static void SaveRuntimeData(RuntimeDataStorage storage)
117117
// Python object's dictionary tool; thus raising an AttributeError
118118
// instead of a TypeError.
119119
// Classes are re-initialized on in RestoreRuntimeData.
120-
IntPtr dict = Marshal.ReadIntPtr(cls.Value.tpHandle, TypeOffset.tp_dict);
120+
var dict = new BorrowedReference(Marshal.ReadIntPtr(cls.Value.tpHandle, TypeOffset.tp_dict));
121121
foreach (var member in cls.Value.dotNetMembers)
122122
{
123123
// No need to decref the member, the ClassBase instance does
@@ -269,7 +269,7 @@ private static void InitClassBase(Type type, ClassBase impl)
269269
IntPtr tp = TypeManager.GetTypeHandle(impl, type);
270270

271271
// Finally, initialize the class __dict__ and return the object.
272-
IntPtr dict = Marshal.ReadIntPtr(tp, TypeOffset.tp_dict);
272+
var dict = new BorrowedReference(Marshal.ReadIntPtr(tp, TypeOffset.tp_dict));
273273

274274

275275
if (impl.dotNetMembers == null)
@@ -282,7 +282,7 @@ private static void InitClassBase(Type type, ClassBase impl)
282282
var item = (ManagedType)iter.Value;
283283
var name = (string)iter.Key;
284284
impl.dotNetMembers.Add(name);
285-
Runtime.PyDict_SetItemString(dict, name, item.pyHandle);
285+
Runtime.PyDict_SetItemString(dict, name, item.ObjectReference);
286286
// Decref the item now that it's been used.
287287
item.DecrRefCount();
288288
if (ClassBase.CilToPyOpMap.TryGetValue(name, out var pyOp)) {
@@ -291,20 +291,15 @@ private static void InitClassBase(Type type, ClassBase impl)
291291
}
292292

293293
// If class has constructors, generate an __doc__ attribute.
294-
IntPtr doc = IntPtr.Zero;
294+
NewReference doc = default;
295295
Type marker = typeof(DocStringAttribute);
296296
var attrs = (Attribute[])type.GetCustomAttributes(marker, false);
297-
if (attrs.Length == 0)
298-
{
299-
doc = IntPtr.Zero;
300-
}
301-
else
297+
if (attrs.Length != 0)
302298
{
303299
var attr = (DocStringAttribute)attrs[0];
304300
string docStr = attr.DocString;
305-
doc = Runtime.PyString_FromString(docStr);
301+
doc = NewReference.DangerousFromPointer(Runtime.PyString_FromString(docStr));
306302
Runtime.PyDict_SetItem(dict, PyIdentifier.__doc__, doc);
307-
Runtime.XDecref(doc);
308303
}
309304

310305
var co = impl as ClassObject;
@@ -320,20 +315,21 @@ private static void InitClassBase(Type type, ClassBase impl)
320315
var ctors = new ConstructorBinding(type, tp, co.binder);
321316
// ExtensionType types are untracked, so don't Incref() them.
322317
// TODO: deprecate __overloads__ soon...
323-
Runtime.PyDict_SetItem(dict, PyIdentifier.__overloads__, ctors.pyHandle);
324-
Runtime.PyDict_SetItem(dict, PyIdentifier.Overloads, ctors.pyHandle);
318+
Runtime.PyDict_SetItem(dict, PyIdentifier.__overloads__, ctors.ObjectReference);
319+
Runtime.PyDict_SetItem(dict, PyIdentifier.Overloads, ctors.ObjectReference);
325320
ctors.DecrRefCount();
326321
}
327322

328323
// don't generate the docstring if one was already set from a DocStringAttribute.
329-
if (!CLRModule._SuppressDocs && doc == IntPtr.Zero)
324+
if (!CLRModule._SuppressDocs && doc.IsNull())
330325
{
331326
doc = co.GetDocString();
332327
Runtime.PyDict_SetItem(dict, PyIdentifier.__doc__, doc);
333-
Runtime.XDecref(doc);
334328
}
335329
}
336330
}
331+
doc.Dispose();
332+
337333
// The type has been modified after PyType_Ready has been called
338334
// Refresh the type
339335
Runtime.PyType_Modified(tp);

src/runtime/classobject.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ internal ClassObject(Type tp) : base(tp)
3131
/// <summary>
3232
/// Helper to get docstring from reflected constructor info.
3333
/// </summary>
34-
internal IntPtr GetDocString()
34+
internal NewReference GetDocString()
3535
{
3636
MethodBase[] methods = binder.GetMethods();
3737
var str = "";
@@ -43,7 +43,7 @@ internal IntPtr GetDocString()
4343
}
4444
str += t.ToString();
4545
}
46-
return Runtime.PyString_FromString(str);
46+
return NewReference.DangerousFromPointer(Runtime.PyString_FromString(str));
4747
}
4848

4949

src/runtime/importhook.cs

Lines changed: 61 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ internal static class ImportHook
1313
private static CLRModule root;
1414
private static MethodWrapper hook;
1515
private static IntPtr py_clr_module;
16+
static BorrowedReference ClrModuleReference => new BorrowedReference(py_clr_module);
1617

1718
private static IntPtr module_def = IntPtr.Zero;
1819

@@ -74,7 +75,7 @@ static void RestoreImport()
7475
/// <summary>
7576
/// Initialization performed on startup of the Python runtime.
7677
/// </summary>
77-
internal static void Initialize()
78+
internal static unsafe void Initialize()
7879
{
7980
InitImport();
8081

@@ -86,14 +87,13 @@ internal static void Initialize()
8687
py_clr_module = Runtime.PyModule_Create2(module_def, 3);
8788

8889
// both dicts are borrowed references
89-
IntPtr mod_dict = Runtime.PyModule_GetDict(py_clr_module);
90-
IntPtr clr_dict = Runtime._PyObject_GetDictPtr(root.pyHandle); // PyObject**
91-
clr_dict = (IntPtr)Marshal.PtrToStructure(clr_dict, typeof(IntPtr));
90+
BorrowedReference mod_dict = Runtime.PyModule_GetDict(ClrModuleReference);
91+
BorrowedReference clr_dict = *Runtime._PyObject_GetDictPtr(root.ObjectReference);
9292

9393
Runtime.PyDict_Update(mod_dict, clr_dict);
94-
IntPtr dict = Runtime.PyImport_GetModuleDict();
95-
Runtime.PyDict_SetItemString(dict, "CLR", py_clr_module);
96-
Runtime.PyDict_SetItemString(dict, "clr", py_clr_module);
94+
BorrowedReference dict = Runtime.PyImport_GetModuleDict();
95+
Runtime.PyDict_SetItemString(dict, "CLR", ClrModuleReference);
96+
Runtime.PyDict_SetItemString(dict, "clr", ClrModuleReference);
9797
}
9898

9999

@@ -143,67 +143,62 @@ internal static void RestoreRuntimeData(RuntimeDataStorage storage)
143143
/// <summary>
144144
/// Return the clr python module (new reference)
145145
/// </summary>
146-
public static IntPtr GetCLRModule(IntPtr? fromList = null)
146+
public static unsafe NewReference GetCLRModule(BorrowedReference fromList = default)
147147
{
148148
root.InitializePreload();
149149

150150
// update the module dictionary with the contents of the root dictionary
151151
root.LoadNames();
152-
IntPtr py_mod_dict = Runtime.PyModule_GetDict(py_clr_module);
153-
IntPtr clr_dict = Runtime._PyObject_GetDictPtr(root.pyHandle); // PyObject**
154-
clr_dict = (IntPtr)Marshal.PtrToStructure(clr_dict, typeof(IntPtr));
152+
BorrowedReference py_mod_dict = Runtime.PyModule_GetDict(ClrModuleReference);
153+
BorrowedReference clr_dict = *Runtime._PyObject_GetDictPtr(root.ObjectReference);
155154
Runtime.PyDict_Update(py_mod_dict, clr_dict);
156155

157156
// find any items from the from list and get them from the root if they're not
158157
// already in the module dictionary
159-
if (fromList != null && fromList != IntPtr.Zero)
158+
if (fromList != null && fromList != default)
160159
{
161-
if (Runtime.PyTuple_Check(fromList.GetValueOrDefault()))
160+
if (Runtime.PyTuple_Check(fromList))
162161
{
163-
Runtime.XIncref(py_mod_dict);
164-
using (var mod_dict = new PyDict(py_mod_dict))
162+
using var mod_dict = new PyDict(py_mod_dict);
163+
using var from = new PyTuple(fromList);
164+
foreach (PyObject item in from)
165165
{
166-
Runtime.XIncref(fromList.GetValueOrDefault());
167-
using (var from = new PyTuple(fromList.GetValueOrDefault()))
166+
if (mod_dict.HasKey(item))
168167
{
169-
foreach (PyObject item in from)
170-
{
171-
if (mod_dict.HasKey(item))
172-
{
173-
continue;
174-
}
175-
176-
var s = item.AsManagedObject(typeof(string)) as string;
177-
if (s == null)
178-
{
179-
continue;
180-
}
181-
182-
ManagedType attr = root.GetAttribute(s, true);
183-
if (attr == null)
184-
{
185-
continue;
186-
}
187-
188-
Runtime.XIncref(attr.pyHandle);
189-
using (var obj = new PyObject(attr.pyHandle))
190-
{
191-
mod_dict.SetItem(s, obj);
192-
}
193-
}
168+
continue;
169+
}
170+
171+
var s = item.AsManagedObject(typeof(string)) as string;
172+
if (s == null)
173+
{
174+
continue;
175+
}
176+
177+
ManagedType attr = root.GetAttribute(s, true);
178+
if (attr == null)
179+
{
180+
continue;
181+
}
182+
183+
Runtime.XIncref(attr.pyHandle);
184+
using (var obj = new PyObject(attr.pyHandle))
185+
{
186+
mod_dict.SetItem(s, obj);
194187
}
195188
}
196189
}
197190
}
198191
Runtime.XIncref(py_clr_module);
199-
return py_clr_module;
192+
return NewReference.DangerousFromPointer(py_clr_module);
200193
}
201194

202195
/// <summary>
203196
/// The actual import hook that ties Python to the managed world.
204197
/// </summary>
205-
public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw)
198+
public static IntPtr __import__(IntPtr self, IntPtr argsRaw, IntPtr kw)
206199
{
200+
var args = new BorrowedReference(argsRaw);
201+
207202
// Replacement for the builtin __import__. The original import
208203
// hook is saved as this.py_import. This version handles CLR
209204
// import and defers to the normal builtin for everything else.
@@ -214,9 +209,8 @@ public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw)
214209
return Exceptions.RaiseTypeError("__import__() takes at least 1 argument (0 given)");
215210
}
216211

217-
// borrowed reference
218-
IntPtr py_mod_name = Runtime.PyTuple_GetItem(args, 0);
219-
if (py_mod_name == IntPtr.Zero ||
212+
BorrowedReference py_mod_name = Runtime.PyTuple_GetItem(args, 0);
213+
if (py_mod_name.IsNull ||
220214
!Runtime.IsStringType(py_mod_name))
221215
{
222216
return Exceptions.RaiseTypeError("string expected");
@@ -225,12 +219,12 @@ public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw)
225219
// Check whether the import is of the form 'from x import y'.
226220
// This determines whether we return the head or tail module.
227221

228-
IntPtr fromList = IntPtr.Zero;
222+
BorrowedReference fromList = default;
229223
var fromlist = false;
230224
if (num_args >= 4)
231225
{
232226
fromList = Runtime.PyTuple_GetItem(args, 3);
233-
if (fromList != IntPtr.Zero &&
227+
if (fromList != default &&
234228
Runtime.PyObject_IsTrue(fromList) == 1)
235229
{
236230
fromlist = true;
@@ -243,16 +237,16 @@ public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw)
243237
// the module.
244238
if (mod_name == "clr")
245239
{
246-
IntPtr clr_module = GetCLRModule(fromList);
247-
if (clr_module != IntPtr.Zero)
240+
NewReference clr_module = GetCLRModule(fromList);
241+
if (!clr_module.IsNull())
248242
{
249-
IntPtr sys_modules = Runtime.PyImport_GetModuleDict();
250-
if (sys_modules != IntPtr.Zero)
243+
BorrowedReference sys_modules = Runtime.PyImport_GetModuleDict();
244+
if (!sys_modules.IsNull)
251245
{
252246
Runtime.PyDict_SetItemString(sys_modules, "clr", clr_module);
253247
}
254248
}
255-
return clr_module;
249+
return clr_module.DangerousMoveToPointerOrNull();
256250
}
257251

258252
string realname = mod_name;
@@ -265,7 +259,7 @@ public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw)
265259
// Turns out that the AssemblyManager.ResolveHandler() checks to see if any
266260
// Assembly's FullName.ToLower().StartsWith(name.ToLower()), which makes very
267261
// little sense to me.
268-
IntPtr res = Runtime.PyObject_Call(py_import, args, kw);
262+
IntPtr res = Runtime.PyObject_Call(py_import, args.DangerousGetAddress(), kw);
269263
if (res != IntPtr.Zero)
270264
{
271265
// There was no error.
@@ -300,10 +294,10 @@ public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw)
300294

301295
// See if sys.modules for this interpreter already has the
302296
// requested module. If so, just return the existing module.
303-
IntPtr modules = Runtime.PyImport_GetModuleDict();
304-
IntPtr module = Runtime.PyDict_GetItem(modules, py_mod_name);
297+
BorrowedReference modules = Runtime.PyImport_GetModuleDict();
298+
BorrowedReference module = Runtime.PyDict_GetItem(modules, py_mod_name);
305299

306-
if (module != IntPtr.Zero)
300+
if (module != default)
307301
{
308302
if (fromlist)
309303
{
@@ -312,16 +306,15 @@ public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw)
312306
var mod = ManagedType.GetManagedObject(module) as ModuleObject;
313307
mod?.LoadNames();
314308
}
315-
Runtime.XIncref(module);
316-
return module;
309+
return Runtime.NewRef(module).DangerousMoveToPointer();
317310
}
318311
if (clr_prefix != null)
319312
{
320-
return GetCLRModule(fromList);
313+
return GetCLRModule(fromList).DangerousMoveToPointerOrNull();
321314
}
322315
module = Runtime.PyDict_GetItemString(modules, names[0]);
323-
Runtime.XIncref(module);
324-
return module;
316+
return Runtime.NewRefOrNull(module)
317+
.DangerousMoveToPointer();
325318
}
326319
Exceptions.Clear();
327320

@@ -358,12 +351,12 @@ public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw)
358351
}
359352

360353
// Add the module to sys.modules
361-
Runtime.PyDict_SetItemString(modules, tail.moduleName, tail.pyHandle);
354+
Runtime.PyDict_SetItemString(modules, tail.moduleName, tail.ObjectReference);
362355

363356
// If imported from CLR add clr.<modulename> to sys.modules as well
364357
if (clr_prefix != null)
365358
{
366-
Runtime.PyDict_SetItemString(modules, clr_prefix + tail.moduleName, tail.pyHandle);
359+
Runtime.PyDict_SetItemString(modules, clr_prefix + tail.moduleName, tail.ObjectReference);
367360
}
368361
}
369362

@@ -380,7 +373,7 @@ public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw)
380373
}
381374
}
382375

383-
private static bool IsLoadAll(IntPtr fromList)
376+
private static bool IsLoadAll(BorrowedReference fromList)
384377
{
385378
if (CLRModule.preload)
386379
{
@@ -390,10 +383,8 @@ private static bool IsLoadAll(IntPtr fromList)
390383
{
391384
return false;
392385
}
393-
IntPtr fp = Runtime.PySequence_GetItem(fromList, 0);
394-
bool res = Runtime.GetManagedString(fp) == "*";
395-
Runtime.XDecref(fp);
396-
return res;
386+
using var fp = Runtime.PySequence_GetItem(fromList, 0);
387+
return Runtime.GetManagedString(fp) == "*";
397388
}
398389
}
399390
}

0 commit comments

Comments
 (0)