Files
linux-packaging-mono/external/linker/linker/Mono.Linker.Steps/MarkStep.cs
Xamarin Public Jenkins (auto-signing) 7d05485754 Imported Upstream version 5.8.0.22
Former-commit-id: df344e34b07851d296efb3e6604c8db42b6f7aa3
2017-10-19 20:04:20 +00:00

1562 lines
41 KiB
C#

//
// MarkStep.cs
//
// Author:
// Jb Evain (jbevain@gmail.com)
//
// (C) 2006 Jb Evain
// (C) 2007 Novell, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Collections.Generic;
namespace Mono.Linker.Steps {
public class MarkStep : IStep {
protected LinkContext _context;
protected Queue<MethodDefinition> _methods;
protected List<MethodDefinition> _virtual_methods;
protected Dictionary<TypeDefinition, CustomAttribute> _assemblyDebuggerDisplayAttributes;
protected Dictionary<TypeDefinition, CustomAttribute> _assemblyDebuggerTypeProxyAttributes;
protected Queue<CustomAttribute> _topLevelAttributes;
public AnnotationStore Annotations {
get { return _context.Annotations; }
}
public MarkStep ()
{
_methods = new Queue<MethodDefinition> ();
_virtual_methods = new List<MethodDefinition> ();
_topLevelAttributes = new Queue<CustomAttribute> ();
_assemblyDebuggerDisplayAttributes = new Dictionary<TypeDefinition, CustomAttribute> ();
_assemblyDebuggerTypeProxyAttributes = new Dictionary<TypeDefinition, CustomAttribute> ();
}
public virtual void Process (LinkContext context)
{
_context = context;
Initialize ();
Process ();
}
void Initialize ()
{
foreach (AssemblyDefinition assembly in _context.GetAssemblies ())
InitializeAssembly (assembly);
}
protected virtual void InitializeAssembly (AssemblyDefinition assembly)
{
Annotations.Push (assembly);
try {
MarkAssembly (assembly);
foreach (TypeDefinition type in assembly.MainModule.Types)
InitializeType (type);
} finally {
Annotations.Pop ();
}
}
void InitializeType (TypeDefinition type)
{
if (type.HasNestedTypes) {
foreach (var nested in type.NestedTypes)
InitializeType (nested);
}
if (!Annotations.IsMarked (type))
return;
MarkType (type);
if (type.HasFields)
InitializeFields (type);
if (type.HasMethods)
InitializeMethods (type.Methods);
}
void InitializeFields (TypeDefinition type)
{
foreach (FieldDefinition field in type.Fields)
if (Annotations.IsMarked (field))
MarkField (field);
}
void InitializeMethods (Collection<MethodDefinition> methods)
{
foreach (MethodDefinition method in methods)
if (Annotations.IsMarked (method))
EnqueueMethod (method);
}
void Process ()
{
if (QueueIsEmpty ())
throw new InvalidOperationException ("No entry methods");
ProcessEntireQueue ();
// After all types have been processed then we can process the lazily marked attributes
ProcessLazyAttributes ();
// We need to process the queue again in case marking the attributes enqueued more work
ProcessEntireQueue ();
// deal with [TypeForwardedTo] pseudo-attributes
foreach (AssemblyDefinition assembly in _context.GetAssemblies ()) {
if (!assembly.MainModule.HasExportedTypes)
continue;
foreach (var exported in assembly.MainModule.ExportedTypes) {
bool isForwarder = exported.IsForwarder;
var declaringType = exported.DeclaringType;
while (!isForwarder && (declaringType != null)) {
isForwarder = declaringType.IsForwarder;
declaringType = declaringType.DeclaringType;
}
if (!isForwarder)
continue;
TypeDefinition type = null;
try {
type = exported.Resolve ();
}
catch (AssemblyResolutionException) {
continue;
}
if (!Annotations.IsMarked (type))
continue;
Annotations.Push (type);
try {
Annotations.Mark (exported);
if (_context.KeepTypeForwarderOnlyAssemblies) {
Annotations.Mark (assembly.MainModule);
}
} finally {
Annotations.Pop ();
}
}
}
}
void ProcessEntireQueue ()
{
while (!QueueIsEmpty ()) {
ProcessQueue ();
ProcessVirtualMethods ();
DoAdditionalProcessing ();
}
}
void ProcessQueue ()
{
while (!QueueIsEmpty ()) {
MethodDefinition method = _methods.Dequeue ();
Annotations.Push (method);
try {
ProcessMethod (method);
} catch (Exception e) {
throw new MarkException (string.Format ("Error processing method: '{0}' in assembly: '{1}'", method.FullName, method.Module.Name), e, method);
} finally {
Annotations.Pop ();
}
}
}
bool QueueIsEmpty ()
{
return _methods.Count == 0;
}
protected virtual void EnqueueMethod (MethodDefinition method)
{
_methods.Enqueue (method);
}
void ProcessVirtualMethods ()
{
foreach (MethodDefinition method in _virtual_methods) {
Annotations.Push (method);
ProcessVirtualMethod (method);
Annotations.Pop ();
}
}
void ProcessVirtualMethod (MethodDefinition method)
{
var overrides = Annotations.GetOverrides (method);
if (overrides == null)
return;
foreach (MethodDefinition @override in overrides)
ProcessOverride (@override);
}
void ProcessOverride (MethodDefinition method)
{
if (!Annotations.IsMarked (method.DeclaringType))
return;
if (Annotations.IsProcessed (method))
return;
if (Annotations.IsMarked (method))
return;
MarkMethod (method);
ProcessVirtualMethod (method);
}
void MarkMarshalSpec (IMarshalInfoProvider spec)
{
if (!spec.HasMarshalInfo)
return;
var marshaler = spec.MarshalInfo as CustomMarshalInfo;
if (marshaler == null)
return;
MarkType (marshaler.ManagedType);
}
void MarkCustomAttributes (ICustomAttributeProvider provider)
{
if (!provider.HasCustomAttributes)
return;
Annotations.Push (provider);
try {
foreach (CustomAttribute ca in provider.CustomAttributes)
MarkCustomAttribute (ca);
} finally {
Annotations.Pop ();
}
}
void LazyMarkCustomAttributes (ICustomAttributeProvider provider)
{
if (!provider.HasCustomAttributes)
return;
foreach (CustomAttribute ca in provider.CustomAttributes)
_topLevelAttributes.Enqueue (ca);
}
protected virtual void MarkCustomAttribute (CustomAttribute ca)
{
Annotations.Push ((object)ca.AttributeType ?? (object)ca);
try {
MarkMethod (ca.Constructor);
MarkCustomAttributeArguments (ca);
TypeReference constructor_type = ca.Constructor.DeclaringType;
TypeDefinition type = constructor_type.Resolve ();
if (type == null) {
HandleUnresolvedType (constructor_type);
return;
}
MarkCustomAttributeProperties (ca, type);
MarkCustomAttributeFields (ca, type);
} finally {
Annotations.Pop ();
}
}
protected void MarkSecurityDeclarations (ISecurityDeclarationProvider provider)
{
// most security declarations are removed (if linked) but user code might still have some
// and if the attribtues references types then they need to be marked too
if ((provider == null) || !provider.HasSecurityDeclarations)
return;
foreach (var sd in provider.SecurityDeclarations)
MarkSecurityDeclaration (sd);
}
protected virtual void MarkSecurityDeclaration (SecurityDeclaration sd)
{
if (!sd.HasSecurityAttributes)
return;
foreach (var sa in sd.SecurityAttributes)
MarkSecurityAttribute (sa);
}
protected virtual void MarkSecurityAttribute (SecurityAttribute sa)
{
TypeReference security_type = sa.AttributeType;
TypeDefinition type = security_type.Resolve ();
if (type == null)
throw new ResolutionException (security_type);
MarkType (security_type);
MarkSecurityAttributeProperties (sa, type);
MarkSecurityAttributeFields (sa, type);
}
protected void MarkSecurityAttributeProperties (SecurityAttribute sa, TypeDefinition attribute)
{
if (!sa.HasProperties)
return;
foreach (var named_argument in sa.Properties)
MarkCustomAttributeProperty (named_argument, attribute);
}
protected void MarkSecurityAttributeFields (SecurityAttribute sa, TypeDefinition attribute)
{
if (!sa.HasFields)
return;
foreach (var named_argument in sa.Fields)
MarkCustomAttributeField (named_argument, attribute);
}
protected void MarkCustomAttributeProperties (CustomAttribute ca, TypeDefinition attribute)
{
if (!ca.HasProperties)
return;
foreach (var named_argument in ca.Properties)
MarkCustomAttributeProperty (named_argument, attribute);
}
protected void MarkCustomAttributeProperty (CustomAttributeNamedArgument namedArgument, TypeDefinition attribute)
{
PropertyDefinition property = GetProperty (attribute, namedArgument.Name);
Annotations.Push (property);
if (property != null)
MarkMethod (property.SetMethod);
MarkIfType (namedArgument.Argument);
Annotations.Pop ();
}
PropertyDefinition GetProperty (TypeDefinition type, string propertyname)
{
while (type != null) {
PropertyDefinition property = type.Properties.FirstOrDefault (p => p.Name == propertyname);
if (property != null)
return property;
type = type.BaseType != null ? ResolveTypeDefinition (type.BaseType) : null;
}
return null;
}
protected void MarkCustomAttributeFields (CustomAttribute ca, TypeDefinition attribute)
{
if (!ca.HasFields)
return;
foreach (var named_argument in ca.Fields)
MarkCustomAttributeField (named_argument, attribute);
}
protected void MarkCustomAttributeField (CustomAttributeNamedArgument namedArgument, TypeDefinition attribute)
{
FieldDefinition field = GetField (attribute, namedArgument.Name);
if (field != null)
MarkField (field);
MarkIfType (namedArgument.Argument);
}
FieldDefinition GetField (TypeDefinition type, string fieldname)
{
while (type != null) {
FieldDefinition field = type.Fields.FirstOrDefault (f => f.Name == fieldname);
if (field != null)
return field;
type = type.BaseType != null ? ResolveTypeDefinition (type.BaseType) : null;
}
return null;
}
MethodDefinition GetMethodWithNoParameters (TypeDefinition type, string methodname)
{
while (type != null) {
MethodDefinition method = type.Methods.FirstOrDefault (m => m.Name == methodname && !m.HasParameters);
if (method != null)
return method;
type = type.BaseType != null ? ResolveTypeDefinition (type.BaseType) : null;
}
return null;
}
void MarkCustomAttributeArguments (CustomAttribute ca)
{
if (!ca.HasConstructorArguments)
return;
foreach (var argument in ca.ConstructorArguments)
MarkIfType (argument);
}
void MarkIfType (CustomAttributeArgument argument)
{
var at = argument.Type;
if (at.IsArray) {
var et = at.GetElementType ();
if (et.Namespace != "System" || et.Name != "Type")
return;
MarkType (et);
if (argument.Value == null)
return;
foreach (var cac in (CustomAttributeArgument[]) argument.Value)
MarkWithResolvedScope ((TypeReference) cac.Value);
} else if (at.Namespace == "System" && at.Name == "Type") {
MarkType (argument.Type);
MarkWithResolvedScope ((TypeReference) argument.Value);
}
}
// custom attributes encoding means it's possible to have a scope that will point into a PCL facade
// even if we (just before saving) will resolve all type references (bug #26752)
void MarkWithResolvedScope (TypeReference type)
{
if (type == null)
return;
// a GenericInstanceType can could contains generic arguments with scope that
// needs to be updated out of the PCL facade (bug #28823)
var git = (type as GenericInstanceType);
if ((git != null) && git.HasGenericArguments) {
foreach (var ga in git.GenericArguments)
MarkWithResolvedScope (ga);
}
// we cannot set the Scope of a TypeSpecification but it's element type can be set
// e.g. System.String[] -> System.String
var ts = (type as TypeSpecification);
if (ts != null) {
MarkWithResolvedScope (ts.ElementType);
return;
}
var td = type.Resolve ();
if (td != null)
type.Scope = td.Scope;
MarkType (type);
}
protected bool CheckProcessed (IMetadataTokenProvider provider)
{
if (Annotations.IsProcessed (provider))
return true;
Annotations.Processed (provider);
return false;
}
protected void MarkAssembly (AssemblyDefinition assembly)
{
if (CheckProcessed (assembly))
return;
ProcessModule (assembly);
MarkAssemblyCustomAttributes (assembly);
MarkSecurityDeclarations (assembly);
foreach (ModuleDefinition module in assembly.Modules)
LazyMarkCustomAttributes (module);
}
void ProcessModule (AssemblyDefinition assembly)
{
// Pre-mark <Module> if there is any methods as they need to be executed
// at assembly load time
foreach (TypeDefinition type in assembly.MainModule.Types)
{
if (type.Name == "<Module>" && type.HasMethods)
{
MarkType (type);
break;
}
}
}
void ProcessLazyAttributes ()
{
while (_topLevelAttributes.Count != 0) {
var customAttribute = _topLevelAttributes.Dequeue ();
var resolved = customAttribute.AttributeType.Resolve ();
if (resolved == null) {
HandleUnresolvedType (customAttribute.AttributeType);
continue;
}
// If an attribute's module has not been marked after processing all types in all assemblies and the attribute itself has not been marked,
// then surely nothing is using this attribute and there is no need to mark it
if (!Annotations.IsMarked (resolved.Module) && !Annotations.IsMarked (customAttribute.AttributeType))
continue;
MarkCustomAttribute (customAttribute);
}
}
protected void MarkField (FieldReference reference)
{
// if (IgnoreScope (reference.DeclaringType.Scope))
// return;
if (reference.DeclaringType is GenericInstanceType)
MarkType (reference.DeclaringType);
FieldDefinition field = ResolveFieldDefinition (reference);
if (field == null)
throw new ResolutionException (reference);
if (CheckProcessed (field))
return;
MarkType (field.DeclaringType);
MarkType (field.FieldType);
MarkCustomAttributes (field);
MarkMarshalSpec (field);
Annotations.Mark (field);
}
protected virtual bool IgnoreScope (IMetadataScope scope)
{
AssemblyDefinition assembly = ResolveAssembly (scope);
return Annotations.GetAction (assembly) != AssemblyAction.Link;
}
FieldDefinition ResolveFieldDefinition (FieldReference field)
{
FieldDefinition fd = field as FieldDefinition;
if (fd == null)
fd = field.Resolve ();
return fd;
}
void MarkScope (IMetadataScope scope)
{
var provider = scope as IMetadataTokenProvider;
if (provider == null)
return;
Annotations.Mark (provider);
}
protected virtual void MarkSerializable (TypeDefinition type)
{
MarkDefaultConstructor (type);
MarkMethodsIf (type.Methods, IsSpecialSerializationConstructor);
}
protected virtual TypeDefinition MarkType (TypeReference reference)
{
if (reference == null)
return null;
reference = GetOriginalType (reference);
if (reference is FunctionPointerType)
return null;
if (reference is GenericParameter)
return null;
// if (IgnoreScope (reference.Scope))
// return null;
TypeDefinition type = ResolveTypeDefinition (reference);
if (type == null) {
HandleUnresolvedType (reference);
return null;
}
if (CheckProcessed (type))
return null;
Annotations.Push (type);
MarkScope (type.Scope);
MarkType (type.BaseType);
MarkType (type.DeclaringType);
MarkCustomAttributes (type);
MarkSecurityDeclarations (type);
if (IsMulticastDelegate (type)) {
MarkMethodCollection (type.Methods);
}
if (IsSerializable (type))
MarkSerializable (type);
if (IsEventSource (type)) {
MarkEventSource (type);
}
MarkTypeSpecialCustomAttributes (type);
MarkGenericParameterProvider (type);
// keep fields for value-types and for classes with LayoutKind.Sequential or Explicit
if (type.IsValueType || !type.IsAutoLayout)
MarkFields (type, type.IsEnum);
if (type.HasInterfaces) {
foreach (var iface in type.Interfaces) {
MarkCustomAttributes (iface);
MarkType (iface.InterfaceType);
}
}
if (type.HasMethods) {
MarkMethodsIf (type.Methods, IsVirtualAndHasPreservedParent);
MarkMethodsIf (type.Methods, IsStaticConstructor);
MarkMethodsIf (type.Methods, HasSerializationAttribute);
}
DoAdditionalTypeProcessing (type);
Annotations.Pop ();
Annotations.Mark (type);
ApplyPreserveInfo (type);
return type;
}
// Allow subclassers to mark additional things in the main processing loop
protected virtual void DoAdditionalProcessing ()
{
}
// Allow subclassers to mark additional things when marking a method
protected virtual void DoAdditionalTypeProcessing (TypeDefinition method)
{
}
void MarkAssemblyCustomAttributes (AssemblyDefinition assembly)
{
if (!assembly.HasCustomAttributes)
return;
foreach (CustomAttribute attribute in assembly.CustomAttributes) {
string attributeFullName = attribute.Constructor.DeclaringType.FullName;
switch (attributeFullName) {
case "System.Diagnostics.DebuggerDisplayAttribute":
StoreDebuggerTypeTarget (assembly, attribute, _assemblyDebuggerDisplayAttributes);
break;
case "System.Diagnostics.DebuggerTypeProxyAttribute":
StoreDebuggerTypeTarget (assembly, attribute, _assemblyDebuggerTypeProxyAttributes);
break;
default:
_topLevelAttributes.Enqueue (attribute);
break;
}
}
}
void StoreDebuggerTypeTarget (AssemblyDefinition assembly, CustomAttribute attribute, Dictionary<TypeDefinition, CustomAttribute> dictionary)
{
if (_context.KeepMembersForDebuggerAttributes) {
TypeReference targetTypeReference = null;
TypeDefinition targetTypeDefinition = null;
foreach (var property in attribute.Properties) {
if (property.Name == "Target") {
targetTypeReference = (TypeReference) property.Argument.Value;
break;
}
if (property.Name == "TargetTypeName") {
targetTypeReference = assembly.MainModule.GetType ((string) property.Argument.Value);
break;
}
}
if (targetTypeReference != null) {
targetTypeDefinition = ResolveTypeDefinition (targetTypeReference);
if (targetTypeDefinition != null) {
dictionary[targetTypeDefinition] = attribute;
}
}
}
}
void MarkTypeSpecialCustomAttributes (TypeDefinition type)
{
CustomAttribute debuggerAttribute;
if (_assemblyDebuggerDisplayAttributes.TryGetValue (type, out debuggerAttribute)) {
MarkTypeWithDebuggerDisplayAttribute (type, debuggerAttribute);
}
if (_assemblyDebuggerTypeProxyAttributes.TryGetValue (type, out debuggerAttribute)) {
MarkTypeWithDebuggerTypeProxyAttribute (type, debuggerAttribute);
}
if (!type.HasCustomAttributes)
return;
foreach (CustomAttribute attribute in type.CustomAttributes) {
switch (attribute.Constructor.DeclaringType.FullName) {
case "System.Xml.Serialization.XmlSchemaProviderAttribute":
MarkXmlSchemaProvider (type, attribute);
break;
case "System.Diagnostics.DebuggerDisplayAttribute":
MarkTypeWithDebuggerDisplayAttribute (type, attribute);
break;
case "System.Diagnostics.DebuggerTypeProxyAttribute":
MarkTypeWithDebuggerTypeProxyAttribute (type, attribute);
break;
case "System.Diagnostics.Tracing.EventDataAttribute":
MarkTypeWithEventDataAttribute (type);
break;
}
}
}
void MarkMethodSpecialCustomAttributes (MethodDefinition method)
{
if (!method.HasCustomAttributes)
return;
foreach (CustomAttribute attribute in method.CustomAttributes) {
switch (attribute.Constructor.DeclaringType.FullName) {
case "System.Web.Services.Protocols.SoapHeaderAttribute":
MarkSoapHeader (method, attribute);
break;
}
}
}
void MarkTypeWithEventDataAttribute (TypeDefinition type)
{
MarkMethodsIf (type.Methods, IsPublicInstancePropertyMethod);
}
void MarkXmlSchemaProvider (TypeDefinition type, CustomAttribute attribute)
{
string method_name;
if (!TryGetStringArgument (attribute, out method_name))
return;
MarkNamedMethod (type, method_name);
}
void MarkTypeWithDebuggerDisplayAttribute (TypeDefinition type, CustomAttribute attribute)
{
if (_context.KeepMembersForDebuggerAttributes) {
string displayString = (string) attribute.ConstructorArguments[0].Value;
Regex regex = new Regex ("{[^{}]+}", RegexOptions.Compiled);
foreach (Match match in regex.Matches (displayString)) {
// Remove '{' and '}'
string realMatch = match.Value.Substring (1, match.Value.Length - 2);
// Remove ",nq" suffix if present
// (it asks the expression evaluator to remove the quotes when displaying the final value)
if (Regex.IsMatch(realMatch, @".+,\s*nq")) {
realMatch = realMatch.Substring (0, realMatch.LastIndexOf (','));
}
if (realMatch.EndsWith ("()")) {
string methodName = realMatch.Substring (0, realMatch.Length - 2);
MethodDefinition method = GetMethodWithNoParameters (type, methodName);
if (method != null) {
MarkMethod (method);
continue;
}
} else {
FieldDefinition field = GetField (type, realMatch);
if (field != null) {
MarkField (field);
continue;
}
PropertyDefinition property = GetProperty (type, realMatch);
if (property != null) {
if (property.GetMethod != null) {
MarkMethod (property.GetMethod);
}
if (property.SetMethod != null) {
MarkMethod (property.SetMethod);
}
continue;
}
}
while (type != null) {
MarkMethods (type);
MarkFields (type, includeStatic: true);
type = type.BaseType != null ? ResolveTypeDefinition (type.BaseType) : null;
}
return;
}
}
}
void MarkTypeWithDebuggerTypeProxyAttribute (TypeDefinition type, CustomAttribute attribute)
{
if (_context.KeepMembersForDebuggerAttributes) {
object constructorArgument = attribute.ConstructorArguments[0].Value;
TypeReference proxyTypeReference = constructorArgument as TypeReference;
if (proxyTypeReference == null) {
string proxyTypeReferenceString = constructorArgument as string;
if (proxyTypeReferenceString != null) {
proxyTypeReference = type.Module.GetType (proxyTypeReferenceString, runtimeName: true);
}
}
if (proxyTypeReference == null) {
return;
}
MarkType (proxyTypeReference);
TypeDefinition proxyType = ResolveTypeDefinition (proxyTypeReference);
if (proxyType != null) {
MarkMethods (proxyType);
MarkFields (proxyType, includeStatic: true);
}
}
}
static bool TryGetStringArgument (CustomAttribute attribute, out string argument)
{
argument = null;
if (attribute.ConstructorArguments.Count < 1)
return false;
argument = attribute.ConstructorArguments [0].Value as string;
return argument != null;
}
protected int MarkNamedMethod (TypeDefinition type, string method_name)
{
if (!type.HasMethods)
return 0;
int count = 0;
foreach (MethodDefinition method in type.Methods) {
if (method.Name != method_name)
continue;
MarkMethod (method);
count++;
}
return count;
}
void MarkSoapHeader (MethodDefinition method, CustomAttribute attribute)
{
string member_name;
if (!TryGetStringArgument (attribute, out member_name))
return;
MarkNamedField (method.DeclaringType, member_name);
MarkNamedProperty (method.DeclaringType, member_name);
}
void MarkNamedField (TypeDefinition type, string field_name)
{
if (!type.HasFields)
return;
foreach (FieldDefinition field in type.Fields) {
if (field.Name != field_name)
continue;
MarkField (field);
}
}
void MarkNamedProperty (TypeDefinition type, string property_name)
{
if (!type.HasProperties)
return;
foreach (PropertyDefinition property in type.Properties) {
if (property.Name != property_name)
continue;
Annotations.Push (property);
MarkMethod (property.GetMethod);
MarkMethod (property.SetMethod);
Annotations.Pop ();
}
}
void MarkGenericParameterProvider (IGenericParameterProvider provider)
{
if (!provider.HasGenericParameters)
return;
foreach (GenericParameter parameter in provider.GenericParameters)
MarkGenericParameter (parameter);
}
void MarkGenericParameter (GenericParameter parameter)
{
MarkCustomAttributes (parameter);
foreach (TypeReference constraint in parameter.Constraints)
MarkType (constraint);
}
bool IsVirtualAndHasPreservedParent (MethodDefinition method)
{
if (!method.IsVirtual)
return false;
var base_list = Annotations.GetBaseMethods (method);
if (base_list == null)
return false;
foreach (MethodDefinition @base in base_list) {
if (IgnoreScope (@base.DeclaringType.Scope))
return true;
if (IsVirtualAndHasPreservedParent (@base))
return true;
}
return false;
}
static bool IsSpecialSerializationConstructor (MethodDefinition method)
{
if (!IsInstanceConstructor (method))
return false;
var parameters = method.Parameters;
if (parameters.Count != 2)
return false;
return parameters [0].ParameterType.Name == "SerializationInfo" &&
parameters [1].ParameterType.Name == "StreamingContext";
}
protected void MarkMethodsIf (Collection<MethodDefinition> methods, Func<MethodDefinition, bool> predicate)
{
foreach (MethodDefinition method in methods)
if (predicate (method))
MarkMethod (method);
}
static bool IsDefaultConstructor (MethodDefinition method)
{
return IsInstanceConstructor (method) && !method.HasParameters;
}
protected static bool IsInstanceConstructor (MethodDefinition method)
{
return method.IsConstructor && !method.IsStatic;
}
protected void MarkDefaultConstructor (TypeDefinition type)
{
if ((type == null) || !type.HasMethods)
return;
MarkMethodsIf (type.Methods, IsDefaultConstructor);
}
static bool IsStaticConstructor (MethodDefinition method)
{
return method.IsConstructor && method.IsStatic;
}
static bool HasSerializationAttribute (MethodDefinition method)
{
if (!method.HasCustomAttributes)
return false;
foreach (var ca in method.CustomAttributes) {
var cat = ca.AttributeType;
if (cat.Namespace != "System.Runtime.Serialization")
continue;
switch (cat.Name) {
case "OnDeserializedAttribute":
case "OnDeserializingAttribute":
case "OnSerializedAttribute":
case "OnSerializingAttribute":
return true;
}
}
return false;
}
static bool IsSerializable (TypeDefinition td)
{
return (td.Attributes & TypeAttributes.Serializable) != 0;
}
static bool IsMulticastDelegate (TypeDefinition td)
{
return td.BaseType != null && td.BaseType.FullName == "System.MulticastDelegate";
}
bool IsEventSource (TypeDefinition td)
{
TypeReference type = td;
do {
if (type.FullName == "System.Diagnostics.Tracing.EventSource") {
return true;
}
TypeDefinition typeDef = type.Resolve ();
if (typeDef == null) {
HandleUnresolvedType (type);
return false;
}
type = typeDef.BaseType;
} while (type != null);
return false;
}
void MarkEventSource (TypeDefinition td)
{
foreach (var nestedType in td.NestedTypes) {
if (nestedType.Name == "Keywords" || nestedType.Name == "Tasks" || nestedType.Name == "Opcodes") {
MarkStaticFields (nestedType);
}
}
}
protected TypeDefinition ResolveTypeDefinition (TypeReference type)
{
TypeDefinition td = type as TypeDefinition;
if (td == null)
td = type.Resolve ();
return td;
}
protected TypeReference GetOriginalType (TypeReference type)
{
while (type is TypeSpecification) {
GenericInstanceType git = type as GenericInstanceType;
if (git != null)
MarkGenericArguments (git);
var mod = type as IModifierType;
if (mod != null)
MarkModifierType (mod);
var fnptr = type as FunctionPointerType;
if (fnptr != null) {
MarkParameters (fnptr);
MarkType (fnptr.ReturnType);
break; // FunctionPointerType is the original type
}
else {
type = ((TypeSpecification) type).ElementType;
}
}
return type;
}
void MarkParameters (FunctionPointerType fnptr)
{
if (!fnptr.HasParameters)
return;
for (int i = 0; i < fnptr.Parameters.Count; i++)
{
MarkType (fnptr.Parameters[i].ParameterType);
}
}
void MarkModifierType (IModifierType mod)
{
MarkType (mod.ModifierType);
}
void MarkGenericArguments (IGenericInstance instance)
{
foreach (TypeReference argument in instance.GenericArguments)
MarkType (argument);
MarkGenericArgumentConstructors (instance);
}
void MarkGenericArgumentConstructors (IGenericInstance instance)
{
var arguments = instance.GenericArguments;
var generic_element = GetGenericProviderFromInstance (instance);
if (generic_element == null)
return;
var parameters = generic_element.GenericParameters;
if (arguments.Count != parameters.Count)
return;
for (int i = 0; i < arguments.Count; i++) {
var argument = arguments [i];
var parameter = parameters [i];
if (!parameter.HasDefaultConstructorConstraint)
continue;
var argument_definition = ResolveTypeDefinition (argument);
if (argument_definition == null)
continue;
MarkMethodsIf (argument_definition.Methods, ctor => !ctor.IsStatic && !ctor.HasParameters);
}
}
IGenericParameterProvider GetGenericProviderFromInstance (IGenericInstance instance)
{
var method = instance as GenericInstanceMethod;
if (method != null)
return ResolveMethodDefinition (method.ElementMethod);
var type = instance as GenericInstanceType;
if (type != null)
return ResolveTypeDefinition (type.ElementType);
return null;
}
void ApplyPreserveInfo (TypeDefinition type)
{
ApplyPreserveMethods (type);
if (!Annotations.IsPreserved (type))
return;
switch (Annotations.GetPreserve (type)) {
case TypePreserve.All:
MarkFields (type, true);
MarkMethods (type);
break;
case TypePreserve.Fields:
MarkFields (type, true, true);
break;
case TypePreserve.Methods:
MarkMethods (type);
break;
}
}
void ApplyPreserveMethods (TypeDefinition type)
{
var list = Annotations.GetPreservedMethods (type);
if (list == null)
return;
MarkMethodCollection (list);
}
void ApplyPreserveMethods (MethodDefinition method)
{
var list = Annotations.GetPreservedMethods (method);
if (list == null)
return;
MarkMethodCollection (list);
}
protected void MarkFields (TypeDefinition type, bool includeStatic, bool markBackingFieldsOnlyIfPropertyMarked = false)
{
if (!type.HasFields)
return;
foreach (FieldDefinition field in type.Fields) {
if (!includeStatic && field.IsStatic)
continue;
if (markBackingFieldsOnlyIfPropertyMarked && field.Name.EndsWith (">k__BackingField")) {
// We can't reliably construct the expected property name from the backing field name for all compilers
// because csc shortens the name of the backing field in some cases
// For example:
// Field Name = <IFoo<int>.Bar>k__BackingField
// Property Name = IFoo<System.Int32>.Bar
//
// instead we will search the properties and find the one that makes use of the current backing field
var propertyDefinition = SearchPropertiesForMatchingFieldDefinition (field);
if (propertyDefinition != null && !Annotations.IsMarked (propertyDefinition))
continue;
}
MarkField (field);
}
}
static PropertyDefinition SearchPropertiesForMatchingFieldDefinition (FieldDefinition field)
{
foreach (var property in field.DeclaringType.Properties) {
foreach (var ins in property.GetMethod.Body.Instructions) {
if (ins.Operand != null && ins.Operand == field)
return property;
}
}
return null;
}
protected void MarkStaticFields(TypeDefinition type)
{
if (!type.HasFields)
return;
foreach (FieldDefinition field in type.Fields) {
if (field.IsStatic)
MarkField (field);
}
}
protected virtual void MarkMethods (TypeDefinition type)
{
if (type.HasMethods)
MarkMethodCollection (type.Methods);
}
void MarkMethodCollection (IList<MethodDefinition> methods)
{
foreach (MethodDefinition method in methods)
MarkMethod (method);
}
protected virtual MethodDefinition MarkMethod (MethodReference reference)
{
reference = GetOriginalMethod (reference);
if (reference.DeclaringType is ArrayType)
return null;
Annotations.Push (reference);
if (reference.DeclaringType is GenericInstanceType)
MarkType (reference.DeclaringType);
// if (IgnoreScope (reference.DeclaringType.Scope))
// return;
MethodDefinition method = ResolveMethodDefinition (reference);
try {
if (method == null) {
HandleUnresolvedMethod (reference);
return null;
}
if (Annotations.GetAction (method) == MethodAction.Nothing)
Annotations.SetAction (method, MethodAction.Parse);
EnqueueMethod (method);
} finally {
Annotations.Pop ();
}
Annotations.AddDependency (method);
return method;
}
AssemblyDefinition ResolveAssembly (IMetadataScope scope)
{
AssemblyDefinition assembly = _context.Resolve (scope);
MarkAssembly (assembly);
return assembly;
}
protected MethodReference GetOriginalMethod (MethodReference method)
{
while (method is MethodSpecification) {
GenericInstanceMethod gim = method as GenericInstanceMethod;
if (gim != null)
MarkGenericArguments (gim);
method = ((MethodSpecification) method).ElementMethod;
}
return method;
}
MethodDefinition ResolveMethodDefinition (MethodReference method)
{
MethodDefinition md = method as MethodDefinition;
if (md == null)
md = method.Resolve ();
return md;
}
protected virtual void ProcessMethod (MethodDefinition method)
{
if (CheckProcessed (method))
return;
Annotations.Push (method);
MarkType (method.DeclaringType);
MarkCustomAttributes (method);
MarkSecurityDeclarations (method);
MarkGenericParameterProvider (method);
if (IsPropertyMethod (method))
MarkProperty (GetProperty (method));
else if (IsEventMethod (method))
MarkEvent (GetEvent (method));
if (method.HasParameters) {
foreach (ParameterDefinition pd in method.Parameters) {
MarkType (pd.ParameterType);
MarkCustomAttributes (pd);
MarkMarshalSpec (pd);
}
}
if (method.HasOverrides) {
foreach (MethodReference ov in method.Overrides)
MarkMethod (ov);
}
MarkMethodSpecialCustomAttributes (method);
if (method.IsVirtual)
_virtual_methods.Add (method);
MarkBaseMethods (method);
MarkType (method.ReturnType);
MarkCustomAttributes (method.MethodReturnType);
MarkMarshalSpec (method.MethodReturnType);
if (method.IsPInvokeImpl || method.IsInternalCall) {
ProcessInteropMethod (method);
}
if (ShouldParseMethodBody (method))
MarkMethodBody (method.Body);
DoAdditionalMethodProcessing (method);
Annotations.Mark (method);
ApplyPreserveMethods (method);
Annotations.Pop ();
}
// Allow subclassers to mark additional things when marking a method
protected virtual void DoAdditionalMethodProcessing (MethodDefinition method)
{
}
void MarkBaseMethods (MethodDefinition method)
{
var base_methods = Annotations.GetBaseMethods (method);
if (base_methods == null)
return;
foreach (MethodDefinition base_method in base_methods) {
if (base_method.DeclaringType.IsInterface && !method.DeclaringType.IsInterface)
continue;
MarkMethod (base_method);
MarkBaseMethods (base_method);
}
}
void ProcessInteropMethod(MethodDefinition method)
{
TypeDefinition returnTypeDefinition = ResolveTypeDefinition (method.ReturnType);
const bool includeStaticFields = false;
if (returnTypeDefinition != null && !returnTypeDefinition.IsImport) {
MarkDefaultConstructor (returnTypeDefinition);
MarkFields (returnTypeDefinition, includeStaticFields);
}
if (method.HasThis && !method.DeclaringType.IsImport) {
MarkFields (method.DeclaringType, includeStaticFields);
}
foreach (ParameterDefinition pd in method.Parameters) {
TypeReference paramTypeReference = pd.ParameterType;
if (paramTypeReference is TypeSpecification) {
paramTypeReference = (paramTypeReference as TypeSpecification).ElementType;
}
TypeDefinition paramTypeDefinition = ResolveTypeDefinition (paramTypeReference);
if (paramTypeDefinition != null && !paramTypeDefinition.IsImport) {
MarkFields (paramTypeDefinition, includeStaticFields);
if (pd.ParameterType.IsByReference) {
MarkDefaultConstructor (paramTypeDefinition);
}
}
}
}
bool ShouldParseMethodBody (MethodDefinition method)
{
if (!method.HasBody)
return false;
switch (Annotations.GetAction (method)) {
case MethodAction.ForceParse:
return true;
case MethodAction.Parse:
AssemblyDefinition assembly = ResolveAssembly (method.DeclaringType.Scope);
return Annotations.GetAction (assembly) == AssemblyAction.Link;
default:
return false;
}
}
static internal bool IsPropertyMethod (MethodDefinition md)
{
return (md.SemanticsAttributes & MethodSemanticsAttributes.Getter) != 0 ||
(md.SemanticsAttributes & MethodSemanticsAttributes.Setter) != 0;
}
static internal bool IsPublicInstancePropertyMethod (MethodDefinition md)
{
return md.IsPublic && !md.IsStatic && IsPropertyMethod (md);
}
static bool IsEventMethod (MethodDefinition md)
{
return (md.SemanticsAttributes & MethodSemanticsAttributes.AddOn) != 0 ||
(md.SemanticsAttributes & MethodSemanticsAttributes.Fire) != 0 ||
(md.SemanticsAttributes & MethodSemanticsAttributes.RemoveOn) != 0;
}
static internal PropertyDefinition GetProperty (MethodDefinition md)
{
TypeDefinition declaringType = md.DeclaringType;
foreach (PropertyDefinition prop in declaringType.Properties)
if (prop.GetMethod == md || prop.SetMethod == md)
return prop;
return null;
}
static EventDefinition GetEvent (MethodDefinition md)
{
TypeDefinition declaringType = md.DeclaringType;
foreach (EventDefinition evt in declaringType.Events)
if (evt.AddMethod == md || evt.InvokeMethod == md || evt.RemoveMethod == md)
return evt;
return null;
}
protected void MarkProperty (PropertyDefinition prop)
{
MarkCustomAttributes (prop);
}
protected void MarkEvent (EventDefinition evt)
{
MarkCustomAttributes (evt);
MarkMethodIfNotNull (evt.AddMethod);
MarkMethodIfNotNull (evt.InvokeMethod);
MarkMethodIfNotNull (evt.RemoveMethod);
}
void MarkMethodIfNotNull (MethodReference method)
{
if (method == null)
return;
MarkMethod (method);
}
protected virtual void MarkMethodBody (MethodBody body)
{
foreach (VariableDefinition var in body.Variables)
MarkType (var.VariableType);
foreach (ExceptionHandler eh in body.ExceptionHandlers)
if (eh.HandlerType == ExceptionHandlerType.Catch)
MarkType (eh.CatchType);
foreach (Instruction instruction in body.Instructions)
MarkInstruction (instruction);
}
protected virtual void MarkInstruction (Instruction instruction)
{
switch (instruction.OpCode.OperandType) {
case OperandType.InlineField:
MarkField ((FieldReference) instruction.Operand);
break;
case OperandType.InlineMethod:
MarkMethod ((MethodReference) instruction.Operand);
break;
case OperandType.InlineTok:
object token = instruction.Operand;
if (token is TypeReference)
MarkType ((TypeReference) token);
else if (token is MethodReference)
MarkMethod ((MethodReference) token);
else
MarkField ((FieldReference) token);
break;
case OperandType.InlineType:
MarkType ((TypeReference) instruction.Operand);
break;
default:
break;
}
}
protected virtual void HandleUnresolvedType (TypeReference reference)
{
throw new ResolutionException (reference);
}
protected virtual void HandleUnresolvedMethod (MethodReference reference)
{
throw new ResolutionException (reference);
}
}
}