// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. /*============================================================ ** ** ** ** Purpose: Provides some static methods to aid with the implementation ** of a Formatter for Serialization. ** ** ============================================================*/ namespace System.Runtime.Serialization { using System; using System.Reflection; using System.Collections; using System.Collections.Generic; using System.Security; using System.Security.Permissions; #if FEATURE_SERIALIZATION using System.Runtime.Serialization.Formatters; #endif using System.Runtime.Remoting; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Threading; using System.IO; using System.Text; using System.Globalization; using System.Diagnostics; using System.Diagnostics.Contracts; [System.Runtime.InteropServices.ComVisible(true)] public static class FormatterServices { #if FEATURE_SERIALIZATION internal static Dictionary m_MemberInfoTable = new Dictionary(32); private static bool unsafeTypeForwardersIsEnabled = false; private static volatile bool unsafeTypeForwardersIsEnabledInitialized = false; private static Object s_FormatterServicesSyncObject = null; private static Object formatterServicesSyncObject { get { if (s_FormatterServicesSyncObject == null) { Object o = new Object(); Interlocked.CompareExchange(ref s_FormatterServicesSyncObject, o, null); } return s_FormatterServicesSyncObject; } } static FormatterServices() { // Static initialization touches security critical types, so we need an // explicit static constructor to allow us to mark it safe critical. } private static MemberInfo[] GetSerializableMembers(RuntimeType 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) { FieldInfo[] 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 CheckSerializable(RuntimeType type) { if (type.IsSerializable) { return true; } return false; } private static MemberInfo[] InternalGetSerializableMembers(RuntimeType type) { List allMembers = null; MemberInfo[] typeMembers; FieldInfo [] typeFields; RuntimeType parentType; Debug.Assert((object)type != null, "[GetAllSerializableMembers]type!=null"); if (type.IsInterface) { return new MemberInfo[0]; } if (!(CheckSerializable(type))) { throw new SerializationException(Environment.GetResourceString("Serialization_NonSerType", type.FullName, type.Module.Assembly.FullName)); } //Get all of the serializable members in the class to be serialized. typeMembers = GetSerializableMembers(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. parentType = (RuntimeType)(type.BaseType); if (parentType != null && parentType != (RuntimeType)typeof(Object)) { RuntimeType[] parentTypes = null; int parentTypeCount = 0; bool classNamesUnique = GetParentTypes(parentType, out parentTypes, out parentTypeCount); if (parentTypeCount > 0){ allMembers = new List(); for (int i = 0; i < parentTypeCount;i++){ parentType = parentTypes[i]; if (!CheckSerializable(parentType)) { throw new SerializationException(Environment.GetResourceString("Serialization_NonSerType", parentType.FullName, parentType.Module.Assembly.FullName)); } 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((RuntimeFieldInfo)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) { MemberInfo[] membersTemp = new MemberInfo[allMembers.Count + typeMembers.Length]; Array.Copy(typeMembers, membersTemp, typeMembers.Length); ((ICollection)allMembers).CopyTo(membersTemp, typeMembers.Length); typeMembers = membersTemp; } } } return typeMembers; } private static bool GetParentTypes(RuntimeType parentType, out RuntimeType[] parentTypes, out int parentTypeCount){ //Check if there are any dup class names. Then we need to include as part of //typeName to prefix the Field names in SerializationFieldInfo /*out*/ parentTypes = null; /*out*/ parentTypeCount = 0; bool unique = true; RuntimeType objectType = (RuntimeType)typeof(object); for (RuntimeType t1 = parentType; t1 != objectType; t1 = (RuntimeType)t1.BaseType) { if (t1.IsInterface) continue; string t1Name = t1.Name; for(int i=0;unique && i 0) { hasTypeForwardedFrom = true; TypeForwardedFromAttribute typeForwardedFromAttribute = (TypeForwardedFromAttribute)typeAttributes[0]; return typeForwardedFromAttribute.AssemblyFullName; } else { hasTypeForwardedFrom = false; return type.Assembly.FullName; } } internal static string GetClrTypeFullName(Type type) { if (type.IsArray) { return GetClrTypeFullNameForArray(type); } else { return GetClrTypeFullNameForNonArrayTypes(type); } } static string GetClrTypeFullNameForArray(Type type) { int rank = type.GetArrayRank(); if (rank == 1) { return String.Format(CultureInfo.InvariantCulture, "{0}{1}", GetClrTypeFullName(type.GetElementType()), "[]"); } else { StringBuilder builder = new StringBuilder(GetClrTypeFullName(type.GetElementType())).Append("["); for (int commaIndex = 1; commaIndex < rank; commaIndex++) { builder.Append(","); } builder.Append("]"); return builder.ToString(); } } static string GetClrTypeFullNameForNonArrayTypes(Type type) { if (!type.IsGenericType) { return type.FullName; } Type[] genericArguments = type.GetGenericArguments(); StringBuilder builder = new StringBuilder(type.GetGenericTypeDefinition().FullName).Append("["); bool hasTypeForwardedFrom; foreach (Type genericArgument in genericArguments) { 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 { ISerializationSurrogate innerSurrogate; internal SurrogateForCyclicalReference(ISerializationSurrogate innerSurrogate) { if (innerSurrogate == null) throw new ArgumentNullException(nameof(innerSurrogate)); this.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); } } }