|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization.Formatters;
using System.Text;
namespace System.Runtime.Serialization
{
[Obsolete(Obsoletions.LegacyFormatterMessage, DiagnosticId = Obsoletions.LegacyFormatterDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public static class FormatterServices
{
private static readonly ConcurrentDictionary<MemberHolder, MemberInfo[]> s_memberInfoTable = new ConcurrentDictionary<MemberHolder, MemberInfo[]>();
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2065:UnrecognizedReflectionPattern",
Justification = "The parentType is read from an array which currently can't be annotated," +
"but the input type is annotated with All, so all of its base types are also All.")]
private static FieldInfo[] InternalGetSerializableMembers(
// currently the only way to preserve base, non-public fields is to use All
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type)
{
Debug.Assert(type != null);
if (type.IsInterface)
{
return Array.Empty<FieldInfo>();
}
if (!type.IsSerializable)
{
throw new SerializationException(SR.Format(SR.Serialization_NonSerType, type.FullName, type.Assembly.FullName));
}
// Get all of the serializable members in the class to be serialized.
FieldInfo[] typeMembers = GetSerializableFields(type);
// If this class doesn't extend directly from object, walk its hierarchy and
// get all of the private and assembly-access fields (e.g. all fields that aren't
// virtual) and include them in the list of things to be serialized.
Type? parentType = type.BaseType;
if (parentType != null && parentType != typeof(object))
{
bool classNamesUnique = GetParentTypes(parentType, out Type[]? parentTypes, out int parentTypeCount);
if (parentTypeCount > 0)
{
var allMembers = new List<FieldInfo>();
for (int i = 0; i < parentTypeCount; i++)
{
parentType = parentTypes![i];
if (!parentType.IsSerializable)
{
throw new SerializationException(SR.Format(SR.Serialization_NonSerType, parentType.FullName, parentType.Module.Assembly.FullName));
}
FieldInfo[] typeFields = parentType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
string typeName = classNamesUnique ? parentType.Name : parentType.FullName!;
foreach (FieldInfo field in typeFields)
{
// Family and Assembly fields will be gathered by the type itself.
if (!field.IsNotSerialized)
{
allMembers.Add(new SerializationFieldInfo(field, typeName));
}
}
}
// If we actually found any new MemberInfo's, we need to create a new MemberInfo array and
// copy all of the members which we've found so far into that.
if (allMembers != null && allMembers.Count > 0)
{
var membersTemp = new FieldInfo[allMembers.Count + typeMembers.Length];
Array.Copy(typeMembers, membersTemp, typeMembers.Length);
allMembers.CopyTo(membersTemp, typeMembers.Length);
typeMembers = membersTemp;
}
}
}
return typeMembers;
}
private static FieldInfo[] GetSerializableFields(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] Type type)
{
// Get the list of all fields
FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
int countProper = 0;
for (int i = 0; i < fields.Length; i++)
{
if ((fields[i].Attributes & FieldAttributes.NotSerialized) == FieldAttributes.NotSerialized)
{
continue;
}
countProper++;
}
if (countProper != fields.Length)
{
var properFields = new FieldInfo[countProper];
countProper = 0;
for (int i = 0; i < fields.Length; i++)
{
if ((fields[i].Attributes & FieldAttributes.NotSerialized) == FieldAttributes.NotSerialized)
{
continue;
}
properFields[countProper] = fields[i];
countProper++;
}
return properFields;
}
else
{
return fields;
}
}
private static bool GetParentTypes(Type parentType, out Type[]? parentTypes, out int parentTypeCount)
{
parentTypes = null;
parentTypeCount = 0;
// Check if there are any dup class names. Then we need to include as part of
// typeName to prefix the Field names in SerializationFieldInfo
bool unique = true;
Type objectType = typeof(object);
for (Type t1 = parentType; t1 != objectType; t1 = t1.BaseType!)
{
if (t1.IsInterface)
{
continue;
}
string t1Name = t1.Name;
for (int i = 0; unique && i < parentTypeCount; i++)
{
string t2Name = parentTypes![i].Name;
if (t2Name.Length == t1Name.Length && t2Name[0] == t1Name[0] && t1Name == t2Name)
{
unique = false;
break;
}
}
// expand array if needed
if (parentTypes == null || parentTypeCount == parentTypes.Length)
{
Array.Resize(ref parentTypes, Math.Max(parentTypeCount * 2, 12));
}
parentTypes[parentTypeCount++] = t1;
}
return unique;
}
public static MemberInfo[] GetSerializableMembers(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type)
{
return GetSerializableMembers(type, new StreamingContext(StreamingContextStates.All));
}
public static MemberInfo[] GetSerializableMembers(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type,
StreamingContext context)
{
ArgumentNullException.ThrowIfNull(type);
// If we've already gathered the members for this type, just return them.
// Otherwise, get them and add them.
return s_memberInfoTable.GetOrAdd(
new MemberHolder(type, context),
mh => InternalGetSerializableMembers(mh._memberType));
}
public static void CheckTypeSecurity(Type t, TypeFilterLevel securityLevel)
{
// nop
}
public static object GetUninitializedObject(
// This API doesn't call any constructors, but the type needs to be seen as constructed.
// A type is seen as constructed if a constructor is kept.
// This obviously won't cover a type with no constructor. Reference types with no
// constructor are an academic problem. Valuetypes with no constructors are a problem,
// but IL Linker currently treats them as always implicitly boxed.
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
Type type) => RuntimeHelpers.GetUninitializedObject(type);
public static object GetSafeUninitializedObject(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
Type type) => RuntimeHelpers.GetUninitializedObject(type);
internal static void SerializationSetValue(MemberInfo fi, object? target, object? value)
{
Debug.Assert(fi != null);
if (fi is FieldInfo serField)
{
serField.SetValue(target, value);
return;
}
throw new ArgumentException(SR.Argument_InvalidFieldInfo);
}
public static object PopulateObjectMembers(object obj, MemberInfo[] members, object?[] data)
{
ArgumentNullException.ThrowIfNull(obj);
ArgumentNullException.ThrowIfNull(members);
ArgumentNullException.ThrowIfNull(data);
if (members.Length != data.Length)
{
throw new ArgumentException(SR.Argument_DataLengthDifferent);
}
for (int i = 0; i < members.Length; i++)
{
MemberInfo member = members[i];
if (member == null)
{
throw new ArgumentNullException(nameof(members), SR.Format(SR.ArgumentNull_NullMember, i));
}
// If we find an empty, it means that the value was never set during deserialization.
// This is either a forward reference or a null. In either case, this may break some of the
// invariants mantained by the setter, so we'll do nothing with it for right now.
object? value = data[i];
if (value == null)
{
continue;
}
// If it's a field, set its value.
if (member is FieldInfo field)
{
field.SetValue(obj, data[i]);
continue;
}
// Otherwise, it's not supported.
throw new SerializationException(SR.Serialization_UnknownMemberInfo);
}
return obj;
}
public static object?[] GetObjectData(object obj, MemberInfo[] members)
{
ArgumentNullException.ThrowIfNull(obj);
ArgumentNullException.ThrowIfNull(members);
object?[] data = new object[members.Length];
for (int i = 0; i < members.Length; i++)
{
MemberInfo member = members[i];
if (member == null)
{
throw new ArgumentNullException(nameof(members), SR.Format(SR.ArgumentNull_NullMember, i));
}
FieldInfo? field = member as FieldInfo;
if (field == null)
{
throw new SerializationException(SR.Serialization_UnknownMemberInfo);
}
data[i] = field.GetValue(obj);
}
return data;
}
public static ISerializationSurrogate GetSurrogateForCyclicalReference(ISerializationSurrogate innerSurrogate)
{
ArgumentNullException.ThrowIfNull(innerSurrogate);
return new SurrogateForCyclicalReference(innerSurrogate);
}
[RequiresUnreferencedCode("Types might be removed")]
public static Type? GetTypeFromAssembly(Assembly assem, string name)
{
ArgumentNullException.ThrowIfNull(assem);
return assem.GetType(name, throwOnError: false, ignoreCase: false);
}
internal static Assembly LoadAssemblyFromString(string assemblyName)
{
return Assembly.Load(new AssemblyName(assemblyName));
}
internal static Assembly? LoadAssemblyFromStringNoThrow(string assemblyName)
{
try
{
return LoadAssemblyFromString(assemblyName);
}
catch (Exception) { }
return null;
}
internal static string GetClrAssemblyName(Type type, out bool hasTypeForwardedFrom)
{
ArgumentNullException.ThrowIfNull(type);
// Special case types like arrays
Type attributedType = type;
while (attributedType.HasElementType)
{
attributedType = attributedType.GetElementType()!;
}
foreach (Attribute first in attributedType.GetCustomAttributes(typeof(TypeForwardedFromAttribute), false))
{
hasTypeForwardedFrom = true;
return ((TypeForwardedFromAttribute)first).AssemblyFullName;
}
hasTypeForwardedFrom = false;
return type.Assembly.FullName!;
}
internal static string GetClrTypeFullName(Type type)
{
return type.IsArray ?
GetClrTypeFullNameForArray(type) :
GetClrTypeFullNameForNonArrayTypes(type);
}
private static string GetClrTypeFullNameForArray(Type type)
{
int rank = type.GetArrayRank();
Debug.Assert(rank >= 1);
string typeName = GetClrTypeFullName(type.GetElementType()!);
return rank == 1 ?
typeName + "[]" :
typeName + "[" + new string(',', rank - 1) + "]";
}
private static string GetClrTypeFullNameForNonArrayTypes(Type type)
{
if (!type.IsGenericType)
{
return type.FullName!;
}
var builder = new StringBuilder(type.GetGenericTypeDefinition().FullName).Append('[');
bool hasTypeForwardedFrom;
foreach (Type genericArgument in type.GetGenericArguments())
{
builder.Append('[').Append(GetClrTypeFullName(genericArgument)).Append(", ");
builder.Append(GetClrAssemblyName(genericArgument, out hasTypeForwardedFrom)).Append("],");
}
//remove the last comma and close typename for generic with a close bracket
return builder.Remove(builder.Length - 1, 1).Append(']').ToString();
}
}
internal sealed class SurrogateForCyclicalReference : ISerializationSurrogate
{
private readonly ISerializationSurrogate _innerSurrogate;
internal SurrogateForCyclicalReference(ISerializationSurrogate innerSurrogate)
{
Debug.Assert(innerSurrogate != null);
_innerSurrogate = innerSurrogate;
}
public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
{
_innerSurrogate.GetObjectData(obj, info, context);
}
public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector? selector)
{
return _innerSurrogate.SetObjectData(obj, info, context, selector);
}
}
}
|