//
// (C) 2006 Jb Evain
// (C) 2007 Novell, Inc.
+// Copyright 2013 Xamarin Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
using System;
using SR = System.Reflection;
using System.Text;
+using System.Text.RegularExpressions;
using System.Xml.XPath;
using Mono.Cecil;
namespace Mono.Linker.Steps {
+ public class XmlResolutionException : Exception {
+ public XmlResolutionException (string message, Exception innerException)
+ : base (message, innerException)
+ {
+ }
+ }
+
public class ResolveFromXmlStep : ResolveStep {
static readonly string _signature = "signature";
static readonly string _ns = string.Empty;
XPathDocument _document;
+ string _xmlDocumentLocation;
- public ResolveFromXmlStep (XPathDocument document)
+ public ResolveFromXmlStep (XPathDocument document, string xmlDocumentLocation = "<unspecified>")
{
_document = document;
+ _xmlDocumentLocation = xmlDocumentLocation;
}
- public override void Process (LinkContext context)
+ protected override void Process ()
{
XPathNavigator nav = _document.CreateNavigator ();
nav.MoveToFirstChild ();
- ProcessAssemblies (context, nav.SelectChildren ("assembly", _ns));
+
+ // This step can be created with XML files that aren't necessarily
+ // linker descriptor files. So bail if we don't have a <linker> element.
+ if (nav.LocalName != "linker")
+ return;
+
+ try {
+ ProcessAssemblies (Context, nav.SelectChildren ("assembly", _ns));
+ } catch (Exception ex) {
+ throw new XmlResolutionException (string.Format ("Failed to process XML description: {0}", _xmlDocumentLocation), ex);
+ }
}
void ProcessAssemblies (LinkContext context, XPathNodeIterator iterator)
}
}
- static void MarkAndPreserveAll (TypeDefinition type)
+ void MarkAndPreserveAll (TypeDefinition type)
{
Annotations.Mark (type);
Annotations.SetPreserve (type, TypePreserve.All);
+
+ if (!type.HasNestedTypes)
+ return;
+
foreach (TypeDefinition nested in type.NestedTypes)
MarkAndPreserveAll (nested);
}
while (iterator.MoveNext ()) {
XPathNavigator nav = iterator.Current;
string fullname = GetFullName (nav);
- TypeDefinition type = assembly.MainModule.Types [fullname];
+
+ if (IsTypePattern (fullname)) {
+ ProcessTypePattern (fullname, assembly, nav);
+ continue;
+ }
+
+ TypeDefinition type = assembly.MainModule.GetType (fullname);
if (type == null)
continue;
- TypePreserve preserve = GetTypePreserve (nav);
+ ProcessType (type, nav);
+ }
+ }
- if (!IsRequired (nav)) {
- Annotations.SetPreserve (type, preserve);
- continue;
- }
+ static bool IsTypePattern (string fullname)
+ {
+ return fullname.IndexOf ("*") != -1;
+ }
+
+ static Regex CreateRegexFromPattern (string pattern)
+ {
+ return new Regex (pattern.Replace(".", @"\.").Replace("*", "(.*)"));
+ }
+
+ void MatchType (TypeDefinition type, Regex regex, XPathNavigator nav)
+ {
+ if (regex.Match (type.FullName).Success)
+ ProcessType (type, nav);
+
+ if (!type.HasNestedTypes)
+ return;
+
+ foreach (var nt in type.NestedTypes)
+ MatchType (nt, regex, nav);
+ }
+
+ void ProcessTypePattern (string fullname, AssemblyDefinition assembly, XPathNavigator nav)
+ {
+ Regex regex = CreateRegexFromPattern (fullname);
+
+ foreach (TypeDefinition type in assembly.MainModule.Types) {
+ MatchType (type, regex, nav);
+ }
+ }
+
+ void ProcessType (TypeDefinition type, XPathNavigator nav)
+ {
+ TypePreserve preserve = GetTypePreserve (nav);
- Annotations.Mark (type);
-
- switch (preserve) {
- case TypePreserve.Nothing:
- if (nav.HasChildren) {
- MarkSelectedFields (nav, type);
- MarkSelectedMethods (nav, type);
- } else
- Annotations.SetPreserve (type, TypePreserve.All);
- break;
- default:
- Annotations.SetPreserve (type, preserve);
- break;
+ if (!IsRequired (nav)) {
+ Annotations.SetPreserve (type, preserve);
+ return;
+ }
+
+ Annotations.Mark (type);
+
+ if (type.IsNested) {
+ var parent = type;
+ while (parent.IsNested) {
+ parent = parent.DeclaringType;
+ Annotations.Mark (parent);
}
}
+
+ switch (preserve) {
+ case TypePreserve.Nothing:
+ if (!nav.HasChildren)
+ Annotations.SetPreserve (type, TypePreserve.All);
+ break;
+ default:
+ Annotations.SetPreserve (type, preserve);
+ break;
+ }
+
+ if (nav.HasChildren) {
+ MarkSelectedFields (nav, type);
+ MarkSelectedMethods (nav, type);
+ }
}
void MarkSelectedFields (XPathNavigator nav, TypeDefinition type)
void ProcessFields (TypeDefinition type, XPathNodeIterator iterator)
{
while (iterator.MoveNext ()) {
- string signature = GetSignature (iterator.Current);
- FieldDefinition field = GetField (type, signature);
- if (field != null)
- Annotations.Mark (field);
- else
- AddUnresolveMarker (string.Format ("T: {0}; F: {1}", type, signature));
+ string value = GetSignature (iterator.Current);
+ if (!String.IsNullOrEmpty (value))
+ ProcessFieldSignature (type, value);
+
+ value = GetAttribute (iterator.Current, "name");
+ if (!String.IsNullOrEmpty (value))
+ ProcessFieldName (type, value);
}
}
+ void ProcessFieldSignature (TypeDefinition type, string signature)
+ {
+ FieldDefinition field = GetField (type, signature);
+ MarkField (type, field, signature);
+ }
+
+ void MarkField (TypeDefinition type, FieldDefinition field, string signature)
+ {
+ if (field != null)
+ Annotations.Mark (field);
+ else
+ AddUnresolveMarker (string.Format ("T: {0}; F: {1}", type, signature));
+ }
+
+ void ProcessFieldName (TypeDefinition type, string name)
+ {
+ if (!type.HasFields)
+ return;
+
+ foreach (FieldDefinition field in type.Fields)
+ if (field.Name == name)
+ MarkField (type, field, name);
+ }
+
static FieldDefinition GetField (TypeDefinition type, string signature)
{
+ if (!type.HasFields)
+ return null;
+
foreach (FieldDefinition field in type.Fields)
if (signature == GetFieldSignature (field))
return field;
void ProcessMethods (TypeDefinition type, XPathNodeIterator iterator)
{
while (iterator.MoveNext()) {
- string signature = GetSignature (iterator.Current);
- MethodDefinition meth = GetMethod (type, signature);
- if (meth != null) {
- Annotations.Mark (meth);
- Annotations.SetAction (meth, MethodAction.Parse);
- } else
- AddUnresolveMarker (string.Format ("T: {0}; M: {1}", type, signature));
+ string value = GetSignature (iterator.Current);
+ if (!String.IsNullOrEmpty (value))
+ ProcessMethodSignature (type, value);
+
+ value = GetAttribute (iterator.Current, "name");
+ if (!String.IsNullOrEmpty (value))
+ ProcessMethodName (type, value);
}
}
- static MethodDefinition GetMethod (TypeDefinition type, string signature)
+ void ProcessMethodSignature (TypeDefinition type, string signature)
+ {
+ MethodDefinition meth = GetMethod (type, signature);
+ MarkMethod (type, meth, signature);
+ }
+
+ void MarkMethod (TypeDefinition type, MethodDefinition method, string signature)
{
- foreach (MethodDefinition meth in type.Methods)
- if (signature == GetMethodSignature (meth))
- return meth;
+ if (method != null) {
+ Annotations.Mark (method);
+ Annotations.SetAction (method, MethodAction.Parse);
+ } else
+ AddUnresolveMarker (string.Format ("T: {0}; M: {1}", type, signature));
+ }
- foreach (MethodDefinition ctor in type.Constructors)
- if (signature == GetMethodSignature (ctor))
- return ctor;
+ void ProcessMethodName (TypeDefinition type, string name)
+ {
+ if (!type.HasMethods)
+ return;
+
+ foreach (MethodDefinition method in type.Methods)
+ if (name == method.Name)
+ MarkMethod (type, method, name);
+ }
+
+ static MethodDefinition GetMethod (TypeDefinition type, string signature)
+ {
+ if (type.HasMethods)
+ foreach (MethodDefinition meth in type.Methods)
+ if (signature == GetMethodSignature (meth))
+ return meth;
return null;
}
static string GetMethodSignature (MethodDefinition meth)
{
StringBuilder sb = new StringBuilder ();
- sb.Append (meth.ReturnType.ReturnType.FullName);
+ sb.Append (meth.ReturnType.FullName);
sb.Append (" ");
sb.Append (meth.Name);
sb.Append ("(");
- for (int i = 0; i < meth.Parameters.Count; i++) {
- if (i > 0)
- sb.Append (",");
+ if (meth.HasParameters) {
+ for (int i = 0; i < meth.Parameters.Count; i++) {
+ if (i > 0)
+ sb.Append (",");
- sb.Append (meth.Parameters [i].ParameterType.FullName);
+ sb.Append (meth.Parameters [i].ParameterType.FullName);
+ }
}
sb.Append (")");
return sb.ToString ();