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(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 right 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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy