8000 use a special class to stub .NET types that no longer exist after a d… · pythonnet/pythonnet@cb4bb9a · GitHub
[go: up one dir, main page]

Skip to content

Commit cb4bb9a

Browse files
committed
use a special class to stub .NET types that no longer exist after a domain reload
1 parent b1c9f5b commit cb4bb9a

File tree

12 files changed

+134
-89
lines changed

12 files changed

+134
-89
lines changed

src/runtime/ReflectedClrType.cs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace Python.Runtime;
99
internal sealed class ReflectedClrType : PyType
1010
{
1111
private ReflectedClrType(StolenReference reference) : base(reference, prevalidated: true) { }
12+
internal ReflectedClrType(ReflectedClrType original) : base(original, prevalidated: true) { }
1213

1314
internal ClassBase Impl => (ClassBase)ManagedType.GetManagedObject(this)!;
1415

@@ -19,12 +20,10 @@ private ReflectedClrType(StolenReference reference) : base(reference, prevalidat
1920
/// Returned <see cref="ReflectedClrType"/> might be partially initialized.
2021
/// If you need fully initialized type, use <see cref="GetOrInitialize(ClassBase, Type)"/>
2122
/// </remarks>
22-
public static ReflectedClrType GetOrCreate(Type type, out ClassBase impl)
23+
public static ReflectedClrType GetOrCreate(Type type)
2324
{
2425
if (ClassManager.cache.TryGetValue(type, out var pyType))
2526
{
26-
impl = (ClassBase)ManagedType.GetManagedObject(pyType)!;
27-
Debug.Assert(impl is not null);
2827
return pyType;
2928
}
3029

@@ -34,7 +33,7 @@ public static ReflectedClrType GetOrCreate(Type type, out ClassBase impl)
3433
pyType = AllocateClass(type);
3534
ClassManager.cache.Add(type, pyType);
3635

37-
impl = ClassManager.CreateClass(type);
36+
var impl = ClassManager.CreateClass(type);
3837

3938
TypeManager.InitializeClassCore(type, pyType, impl);
4039

@@ -52,11 +51,16 @@ internal void Restore(InterDomainContext context)
5251
{
5352
var cb = context.Storage.GetValue<ClassBase>("impl");
5453

54+
cb.Load(this, context);
55+
56+
Restore(cb);
57+
}
58+
59+
internal void Restore(ClassBase cb)
60+
{
5561
ClassManager.InitClassBase(cb.type.Value, cb, this);
5662

5763
TypeManager.InitializeClass(this, cb, cb.type.Value);
58-
59-
cb.Load(this, context);
6064
}
6165

6266
internal static NewReference CreateSubclass(ClassBase baseClass,
@@ -71,7 +75,7 @@ internal static NewReference CreateSubclass(ClassBase baseClass,
7175
ns,
7276
assembly);
7377

74-
var py_type = GetOrCreate(subType, out _);
78+
var py_type = GetOrCreate(subType);
7579

7680
// by default the class dict will have all the C# methods in it, but as this is a
7781
// derived class we want the python overrides in there instead if they exist.

src/runtime/UnloadedClass.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System;
2+
3+
namespace Python.Runtime;
4+
5+
[Serializable]
6+
internal class UnloadedClass : ClassBase
7+
{
8+
readonly string name;
9+
10+
internal UnloadedClass(string name) : base(typeof(object))
11+
{
12+
this.name = name;
13+
}
14+
15+
public static NewReference tp_new(BorrowedReference tp, BorrowedReference args, BorrowedReference kw)
16+
{
17+
var self = (UnloadedClass)GetManagedObject(tp)!;
18+
return self.RaiseTypeError();
19+
}
20+
21+
public override NewReference type_subscript( 10000 BorrowedReference idx) => RaiseTypeError();
22+
23+
private NewReference RaiseTypeError()
24+
=> Exceptions.RaiseTypeError("The .NET type no longer exists: " + name);
25+
26+
internal override bool CanSubclass() => false;
27+
}

src/runtime/classmanager.cs

Lines changed: 32 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ public static void Reset()
5656

5757
internal static void RemoveClasses()
5858
{
59+
foreach (var @class in cache.Values)
60+
{
61+
@class.Dispose();
62+
}
5963
cache.Clear();
6064
}
6165

@@ -81,11 +85,6 @@ internal static ClassManagerState SaveRuntimeData()
8185
var contexts = new Dictionary<ReflectedClrType, InterDomainContext>();
8286
foreach (var cls in cache)
8387
{
84-
if (!cls.Key.Valid)
85-
{
86-
// Don't serialize an invalid class
87-
continue;
88-
}
8988
var context = contexts[cls.Value] = new InterDomainContext();
9089
var cb = (ClassBase)ManagedType.GetManagedObject(cls.Value)!;
9190
cb.Save(cls.Value, context);
@@ -129,31 +128,32 @@ internal static void RestoreRuntimeData(ClassManagerState storage)
129128
var contexts = storage.Contexts;
130129
foreach (var pair in cache)
131130
{
132-
if (!pair.Key.Valid)
131+
var context = contexts[pair.Value];
132+
if (pair.Key.Valid)
133+
{
134+
pair.Value.Restore(context);
135+
}
136+
else
133137
{
134138
invalidClasses.Add(pair);
135-
continue;
139+
var cb = new UnloadedClass(pair.Key.Name);
140+
cb.Load(pair.Value, context);
141+
pair.Value.Restore(cb);
136142
}
137-
138-
pair.Value.Restore(contexts[pair.Value]);
139-
}
140-
141-
foreach (var pair in invalidClasses)
142-
{
143-
cache.Remove(pair.Key);
144-
pair.Value.Dispose();
145143
}
146144
}
147145

148146
/// <summary>
149147
/// Return the ClassBase-derived instance that implements a particular
150148
/// reflected managed type, creating it if it doesn't yet exist.
151149
/// </summary>
152-
internal static ReflectedClrType GetClass(Type type) => ReflectedClrType.GetOrCreate(type, out _);
150+
internal static ReflectedClrType GetClass(Type type) => ReflectedClrType.GetOrCreate(type);
153151
internal static ClassBase GetClassImpl(Type type)
154152
{
155-
ReflectedClrType.GetOrCreate(type, out var cb);
156-
return cb;
153+
var pyType = GetClass(type);
154+
var impl = (ClassBase)ManagedType.GetManagedObject(pyType)!;
155+
Debug.Assert(impl is not null);
156+
return impl!;
157157
}
158158

159159

@@ -236,22 +236,11 @@ internal static void InitClassBase(Type type, ClassBase impl, PyType pyType)
236236
var item = iter.Value;
237237
var name = iter.Key;
238238
impl.dotNetMembers.Add(name);
239-
switch (item)
240-
{
241-
case ClassBase nestedClass:
242-
Runtime.PyDict_SetItemString(dict, name, GetClass(nestedClass.type.Value));
243-
break;
244-
case ExtensionType extension:
245-
using (var pyRef = extension.Alloc())
246-
{
247-
Runtime.PyDict_SetItemString(dict, name, pyRef.Borrow());
248-
}
249-
break;
250-
default:
251-
throw new NotSupportedException();
252-
}
239+
Runtime.PyDict_SetItemString(dict, name, item);
253240
if (ClassBase.CilToPyOpMap.TryGetValue(name, out var pyOp)
254-
&& item is MethodObject method)
241+
// workaround for unintialized types crashing in GetManagedObject
242+
&& item is not ReflectedClrType
243+
&& ManagedType.GetManagedObject(item) is MethodObject method)
255244
{
256245
impl.richcompare.Add(pyOp, method);
257246
}
@@ -347,7 +336,7 @@ private static ClassInfo GetClassInfo(Type type)
347336
var ci = new ClassInfo();
348337
var methods = new Dictionary<string, List<MethodInfo>>();
349338
MethodInfo meth;
350-
ManagedType ob;
339+
ExtensionType ob;
351340
string name;
352341
Type tp;
353342
int i, n;
@@ -472,7 +461,7 @@ private static ClassInfo GetClassInfo(Type type)
472461
}
473462

474463
ob = new PropertyObject(pi);
475-
ci.members[pi.Name] = ob;
464+
ci.members[pi.Name] = ob.AllocObject();
476465
continue;
477466

478467
case MemberTypes.Field:
@@ -482,7 +471,7 @@ private static ClassInfo GetClassInfo(Type type)
482471
continue;
483472
}
484473
ob = new FieldObject(fi);
485-
ci.members[mi.Name] = ob;
474+
ci.members[mi.Name] = ob.AllocObject();
486475
continue;
487476

488477
case MemberTypes.Event:
@@ -494,7 +483,7 @@ private static ClassInfo GetClassInfo(Type type)
494483
ob = ei.AddMethod.IsStatic
495484
? new EventBinding(ei)
496485
: new EventObject(ei);
497-
ci.members[ei.Name] = ob;
486+
ci.members[ei.Name] = ob.AllocObject();
498487
continue;
499488

500489
case MemberTypes.NestedType:
@@ -506,9 +495,8 @@ private static ClassInfo GetClassInfo(Type type)
506495
}
507496
// Note the given instance might be uninitialized
508497
var pyType = GetClass(tp);
509-
ob = ManagedType.GetManagedObject(pyType)!;
510-
Debug.Assert(ob is not null);
511-
ci.members[mi.Name] = ob;
498+
// make a copy, that could be disposed later
499+
ci.members[mi.Name] = new ReflectedClrType(pyType);
512500
continue;
513501
}
514502
}
@@ -519,18 +507,18 @@ private static ClassInfo GetClassInfo(Type type)
519507
var mlist = iter.Value.ToArray();
520508

521509
ob = new MethodObject(type, name, mlist);
522-
ci.members[name] = ob;
510+
ci.members[name] = ob.AllocObject();
523511
if (mlist.Any(OperatorMethod.IsOperatorMethod))
524512
{
525513
string pyName = OperatorMethod.GetPyMethodName(name);
526514
string pyNameReverse = OperatorMethod.ReversePyMethodName(pyName);
527515
OperatorMethod.FilterMethods(mlist, out var forwardMethods, out var reverseMethods);
528516
// Only methods where the left operand is the declaring type.
529517
if (forwardMethods.Length > 0)
530-
ci.members[pyName] = new MethodObject(type, name, forwardMethods);
518+
ci.members[pyName] = new MethodObject(type, name, forwardMethods).AllocObject();
531519
// Only methods where only the righ 10000 t operand is the declaring type.
532520
if (reverseMethods.Length > 0)
533-
ci.members[pyNameReverse] = new MethodObject(type, name, reverseMethods);
521+
ci.members[pyNameReverse] = new MethodObject(type, name, reverseMethods).AllocObject();
534522
}
535523
}
536524

@@ -563,7 +551,7 @@ private static ClassInfo GetClassInfo(Type type)
563551
private class ClassInfo
564552
{
565553
public Indexer? indexer;
566-
public readonly Dictionary<string, ManagedType> members = new();
554+
public readonly Dictionary<string, PyObject> members = new();
567555

568556
internal ClassInfo()
569557
{

src/runtime/extensiontype.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ public virtual NewReference Alloc()
4040
return py.AnalyzerWorkaround();
4141
}
4242

43+
public PyObject AllocObject() => new PyObject(Alloc().Steal());
44+
4345
// "borrowed" references
4446
internal static readonly HashSet<IntPtr> loadedExtensions = new();
4547
void SetupGc (BorrowedReference ob, BorrowedReference tp)

src/runtime/importhook.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Concurrent;
33
using System.Collections.Generic;
4+
using System.Diagnostics;
45

56
using Python.Runtime.StateSerialization;
67

@@ -98,6 +99,7 @@ private static Dictionary<PyString, PyObject> GetDotNetModules()
9899
BorrowedReference pyModules = Runtime.PyImport_GetModuleDict();
99100
using var items = Runtime.PyDict_Items(pyModules);
100101
nint length = Runtime.PyList_Size(items.BorrowOrThrow());
102+
Debug.Assert(length >= 0);
101103
var modules = new Dictionary<PyString, PyObject>();
102104
for (nint i = 0; i < length; i++)
103105
{

src/runtime/metatype.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ public static void tp_dealloc(NewReference lastRef)
289289
{
290290
// Fix this when we dont cheat on the handle for subclasses!
291291

292-
var flags = (TypeFlags)Util.ReadCLong(lastRef.Borrow(), TypeOffset.tp_flags);
292+
var flags = PyType.GetFlags(lastRef.Borrow());
293293
if ((flags & TypeFlags.Subclass) == 0)
294294
{
295295
GetGCHandle(lastRef.Borrow()).Free();

src/runtime/native/TypeOffset.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ public static int GetSlotOffset(string slotName)
115115
return SlotOffsets[slotName];
116116
}
117117

118+
public static string? GetSlotName(int offset)
119+
=> SlotOffsets.FirstOrDefault(kv => kv.Value == offset).Key;
120+
118121
static readonly HashSet<string> slotNames = new HashSet<string>();
119122
internal static bool IsSupportedSlotName(string name) => slotNames.Contains(name);
120123

src/runtime/pyobject.cs

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public partial class PyObject : DynamicObject, IDisposable
2323
/// <summary>
2424
/// Trace stack for PyObject's construction
2525
/// </summary>
26-
public StackTrace Traceback { get; private set; }
26+
public StackTrace Traceback { get; } = new StackTrace(1);
2727
#endif
2828

2929
protected internal IntPtr rawPtr = IntPtr.Zero;
@@ -49,9 +49,6 @@ internal PyObject(IntPtr ptr)
4949

5050
rawPtr = ptr;
5151
Finalizer.Instance.ThrottledCollect();
52-
#if TRACE_ALLOC
53-
Traceback = new StackTrace(1);
54-
#endif
5552
}
5653

5754
[Obsolete("for testing purposes only")]
@@ -62,9 +59,6 @@ internal PyObject(IntPtr ptr, bool skipCollect)
6259
rawPtr = ptr;
6360
if (!skipCollect)
6461
Finalizer.Instance.ThrottledCollect();
65-
#if TRACE_ALLOC
66-
Traceback = new StackTrace(1);
67-
#endif
6862
}
6963

7064
/// <summary>
@@ -78,9 +72,15 @@ internal PyObject(BorrowedReference reference)
7872

7973
rawPtr = new NewReference(reference).DangerousMoveToPointer();
8074
Finalizer.Instance.ThrottledCollect();
81-
#if TRACE_ALLOC
82-
Traceback = new StackTrace(1);
83-
#endif
75+
}
76+
77+
internal PyObject(BorrowedReference reference, bool skipCollect)
78+
{
79+
if (reference.IsNull) throw new ArgumentNullException(nameof(reference));
80+
81+
rawPtr = new NewReference(reference).DangerousMoveToPointer();
82+
if (!skipCollect)
83+
Finalizer.Instance.ThrottledCollect();
8484
}
8585

8686
internal PyObject(in StolenReference reference)
@@ -89,9 +89,6 @@ internal PyObject(in StolenReference reference)
8989

9090
rawPtr = reference.DangerousGetAddressOrNull();
9191
Finalizer.Instance.ThrottledCollect();
92-
#if TRACE_ALLOC
93-
Traceback = new StackTrace(1);
94-
#endif
9592
}
9693

9794
// Ensure that encapsulated Python object is decref'ed appropriately

src/runtime/runtime.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,10 @@ private static void MoveClrInstancesOnwershipToPython()
479479
}
480480
}
481481

482-
foreach (IntPtr objWithGcHandle in ExtensionType.loadedExtensions.Concat(CLRObject.reflectedObjects).ToArray())
482+
foreach (IntPtr objWithGcHandle in ExtensionType.loadedExtensions
483+
.Concat(CLRObject.reflectedObjects)
484+
.ToArray()
485+
)
483486
{
484487
var @ref = new BorrowedReference(objWithGcHandle);
485488
GCHandle? handle = ManagedType.TryGetGCHandle(@ref);

0 commit comments

Comments
 (0)
0