Skip to content

Commit d2f1ab6

Browse files
committed
Workaround for mono attribute bug
1 parent 1002105 commit d2f1ab6

File tree

1 file changed

+22
-18
lines changed

1 file changed

+22
-18
lines changed

src/runtime/StateSerialization/RuntimeData.cs

+22-18
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,6 @@ private NotSerializedException(SerializationInfo info, StreamingContext context)
2323
override public void GetObjectData(SerializationInfo info, StreamingContext context) => base.GetObjectData(info, context);
2424
}
2525

26-
27-
// empty attribute to mark classes created by the "non-serializer" so we don't loop-inherit
28-
// on multiple cycles of de/serialization
29-
[System.AttributeUsage(System.AttributeTargets.All, Inherited = false, AllowMultiple = true)]
30-
[System.Serializable]
31-
sealed class PyNet_NotSerializedAttribute : System.Attribute {}
32-
3326
[Serializable]
3427
internal static class NonSerializedTypeBuilder
3528
{
@@ -40,7 +33,12 @@ internal static class NonSerializedTypeBuilder
4033
AppDomain.CurrentDomain.DefineDynamicAssembly(nonSerializedAssemblyName, AssemblyBuilderAccess.Run);
4134
internal static ModuleBuilder moduleBuilder = assemblyForNonSerializedClasses.DefineDynamicModule("NotSerializedModule");
4235
internal static HashSet<string> dontReimplementMethods = new(){"Finalize", "Dispose", "GetType", "ReferenceEquals", "GetHashCode", "Equals"};
43-
const string notSerializedSuffix = "_NotSerialized";
36+
internal const string notSerializedSuffix = "_NotSerialized";
37+
// dummy field name to mark classes created by the "non-serializer" so we don't loop-inherit
38+
// on multiple cycles of de/serialization. We use a static field instead of an attribute
39+
// becaues of a bug in mono. Put a space in the name so users will be extremely unlikely
40+
// to create a field with the same name.
41+
internal const string notSerializedFieldName = "__PyNet NonSerialized";
4442

4543
private static Func<Type, TypeAttributes, bool> hasVisibility = (tp, attr) => (tp.Attributes & TypeAttributes.VisibilityMask) == attr;
4644
private static Func<Type, bool> isNestedType = (tp) => hasVisibility(tp, TypeAttributes.NestedPrivate) || hasVisibility(tp, TypeAttributes.NestedPublic) || hasVisibility(tp, TypeAttributes.NestedFamily) || hasVisibility(tp, TypeAttributes.NestedAssembly);
@@ -96,14 +94,13 @@ static void FillTypeMethods(TypeBuilder tb)
9694

9795
static string MakeName(Type tp)
9896
{
99-
const string suffix = "_NotSerialized";
100-
string @out = tp.Name + suffix;
97+
string @out = tp.Name + notSerializedSuffix;
10198
var parentType = tp.DeclaringType;
10299
while (parentType is not null)
103100
{
104101
// If we have a nested class, we need the whole nester/nestee
105102
// chain with the suffix for each.
106-
@out = parentType.Name + suffix + "+" + @out;
103+
@out = parentType.Name + notSerializedSuffix + "+" + @out;
107104
parentType = parentType.DeclaringType;
108105
}
109106
return @out;
@@ -151,7 +148,7 @@ static string MakeName(Type tp)
151148
}
152149

153150
TypeBuilder tb = GetTypeBuilder(baseType);
154-
SetNonSerialiedAttr(tb);
151+
SetNonSerializedAttr(tb);
155152
FillTypeMethods(tb);
156153

157154
var nestedtypes = baseType.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
@@ -172,7 +169,7 @@ static string MakeName(Type tp)
172169
foreach(var builder in nestedBuilders)
173170
{
174171
FillTypeMethods(builder);
175-
SetNonSerialiedAttr(builder);
172+
SetNonSerializedAttr(builder);
176173
builder.CreateType();
177174
}
178175
return outTp;
@@ -207,11 +204,18 @@ private static void ImplementEqualityAndHash(TypeBuilder tb)
207204
equalsIlGen.Emit(OpCodes.Ret);
208205
}
209206

210-
private static void SetNonSerialiedAttr(TypeBuilder tb)
207+
private static void SetNonSerializedAttr(TypeBuilder tb)
211208
{
212-
ConstructorInfo attrCtorInfo = typeof(PyNet_NotSerializedAttribute).GetConstructor(new Type[]{});
213-
CustomAttributeBuilder attrBuilder = new CustomAttributeBuilder(attrCtorInfo,new object[]{});
214-
tb.SetCustomAttribute(attrBuilder);
209+
// Name of the function says we're adding an attribute, but for some
210+
// reason on Mono the attribute is not added, and no exceptions are
211+
// thrown.
212+
tb.DefineField(notSerializedFieldName, typeof(int), FieldAttributes.Public | FieldAttributes.Static);
213+
}
214+
215+
public static bool IsNonSerializedType(Type tp)
216+
{
217+
return tp.GetField(NonSerializedTypeBuilder.notSerializedFieldName, BindingFlags.Public | BindingFlags.Static) is not null;
218+
215219
}
216220

217221
private static TypeBuilder GetTypeBuilder(Type baseType)
@@ -325,7 +329,7 @@ public void GetObjectData(object obj, SerializationInfo info, StreamingContext c
325329

326330
MaybeType type = obj.GetType();
327331

328-
if (type.Value.CustomAttributes.Any((attr) => attr.AttributeType == typeof(PyNet_NotSerializedAttribute)))
332+
if (NonSerializedTypeBuilder.IsNonSerializedType(type.Value))
329333
{
330334
// Don't serialize a _NotSerialized. Serialize the base type, and deserialize as a _NotSerialized
331335
type = type.Value.BaseType;

0 commit comments

Comments
 (0)