|
// 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;
}
}
}
|