File: Linker.Steps\DiscoverSerializationHandler.cs
Web Access
Project: src\src\tools\illink\src\linker\Mono.Linker.csproj (illink)
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
 
using System;
using System.Diagnostics;
using Mono.Cecil;
 
namespace Mono.Linker.Steps
{
	// This discovers types attributed with certain serialization attributes, to match the old behavior
	// of xamarin-android. It is not meant to be complete. Unlike xamarin-andorid:
	// - this will only discover attributed types that are marked
	// - this will discover types in non-"link" assemblies as well
	public class DiscoverSerializationHandler : IMarkHandler
	{
		LinkContext? _context;
		LinkContext Context {
			get {
				Debug.Assert (_context != null);
				return _context;
			}
		}
 
		public void Initialize (LinkContext context, MarkContext markContext)
		{
			_context = context;
			markContext.RegisterMarkTypeAction (ProcessType);
			markContext.RegisterMarkMethodAction (CheckForSerializerActivation);
		}
 
		void CheckForSerializerActivation (MethodDefinition method)
		{
			var type = method.DeclaringType;
 
			if (!Context.SerializationMarker.IsActive (SerializerKind.DataContractSerializer) &&
				method.IsConstructor && !method.IsStatic &&
				((type.Namespace == "System.Runtime.Serialization" && type.Name == "DataContractSerializer") ||
				(type.Namespace == "System.Runtime.Serialization.Json" && type.Name == "DataContractJsonSerializer"))) {
 
				Context.SerializationMarker.Activate (SerializerKind.DataContractSerializer);
			}
 
			if (!Context.SerializationMarker.IsActive (SerializerKind.XmlSerializer) &&
				method.IsConstructor && !method.IsStatic &&
				type.Namespace == "System.Xml.Serialization" &&
				type.Name == "XmlSerializer") {
 
				Context.SerializationMarker.Activate (SerializerKind.XmlSerializer);
			}
		}
 
		void ProcessType (TypeDefinition type)
		{
			ProcessAttributeProvider (type);
 
			if (type.HasFields) {
				foreach (var field in type.Fields)
					ProcessAttributeProvider (field);
			}
 
			if (type.HasProperties) {
				foreach (var property in type.Properties)
					ProcessAttributeProvider (property);
			}
 
			if (type.HasMethods) {
				foreach (var method in type.Methods)
					ProcessAttributeProvider (method);
			}
 
			if (type.HasEvents) {
				foreach (var @event in type.Events) {
					ProcessAttributeProvider (@event);
				}
			}
		}
 
		void ProcessAttributeProvider (ICustomAttributeProvider provider)
		{
			if (!provider.HasCustomAttributes)
				return;
 
			var serializedFor = SerializerKind.None;
 
			foreach (var attribute in provider.CustomAttributes) {
				if (IsPreservedSerializationAttribute (provider, attribute, out SerializerKind serializerKind))
					serializedFor |= serializerKind;
			}
 
			if (serializedFor == SerializerKind.None)
				return;
 
			if (serializedFor.HasFlag (SerializerKind.DataContractSerializer))
				Context.SerializationMarker.TrackForSerialization (provider, SerializerKind.DataContractSerializer);
			if (serializedFor.HasFlag (SerializerKind.XmlSerializer))
				Context.SerializationMarker.TrackForSerialization (provider, SerializerKind.XmlSerializer);
		}
 
		static bool IsPreservedSerializationAttribute (ICustomAttributeProvider provider, CustomAttribute attribute, out SerializerKind serializerKind)
		{
			TypeReference attributeType = attribute.Constructor.DeclaringType;
			serializerKind = SerializerKind.None;
 
			switch (attributeType.Namespace) {
 
			// http://bugzilla.xamarin.com/show_bug.cgi?id=1415
			// http://msdn.microsoft.com/en-us/library/system.runtime.serialization.datamemberattribute.aspx
			case "System.Runtime.Serialization":
				var serialized = false;
				if (provider is PropertyDefinition or FieldDefinition or EventDefinition)
					serialized = attributeType.Name == "DataMemberAttribute";
				else if (provider is TypeDefinition)
					serialized = attributeType.Name == "DataContractAttribute";
 
				if (serialized) {
					serializerKind = SerializerKind.DataContractSerializer;
					return true;
				}
				break;
 
			// http://msdn.microsoft.com/en-us/library/83y7df3e.aspx
			case "System.Xml.Serialization":
				var attributeName = attributeType.Name;
				if (attributeName.StartsWith ("Xml", StringComparison.Ordinal)
					&& attributeName.EndsWith ("Attribute", StringComparison.Ordinal)
					&& attributeName != "XmlIgnoreAttribute") {
					serializerKind = SerializerKind.XmlSerializer;
					return true;
				}
				break;
 
			};
 
			return false;
		}
	}
}