[linker] Provide better error message when an error occurs while processing xml descr...
[mono-project.git] / mcs / tools / linker / Mono.Linker.Steps / ResolveFromXmlStep.cs
blob5e9f681fd1784d25dfd201c93fc913c409c6fa9f
1 //
2 // ResolveFromXmlStep.cs
3 //
4 // Author:
5 // Jb Evain (jbevain@gmail.com)
6 //
7 // (C) 2006 Jb Evain
8 // (C) 2007 Novell, Inc.
9 // Copyright 2013 Xamarin Inc.
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System;
32 using SR = System.Reflection;
33 using System.Text;
34 using System.Text.RegularExpressions;
35 using System.Xml.XPath;
37 using Mono.Cecil;
39 namespace Mono.Linker.Steps {
41 public class XmlResolutionException : Exception {
42 public XmlResolutionException (string message, Exception innerException)
43 : base (message, innerException)
48 public class ResolveFromXmlStep : ResolveStep {
50 static readonly string _signature = "signature";
51 static readonly string _fullname = "fullname";
52 static readonly string _required = "required";
53 static readonly string _preserve = "preserve";
54 static readonly string _ns = string.Empty;
56 XPathDocument _document;
57 string _xmlDocumentLocation;
59 public ResolveFromXmlStep (XPathDocument document, string xmlDocumentLocation = "<unspecified>")
61 _document = document;
62 _xmlDocumentLocation = xmlDocumentLocation;
65 protected override void Process ()
67 XPathNavigator nav = _document.CreateNavigator ();
68 nav.MoveToFirstChild ();
70 // This step can be created with XML files that aren't necessarily
71 // linker descriptor files. So bail if we don't have a <linker> element.
72 if (nav.LocalName != "linker")
73 return;
75 try {
76 ProcessAssemblies (Context, nav.SelectChildren ("assembly", _ns));
77 } catch (Exception ex) {
78 throw new XmlResolutionException (string.Format ("Failed to process XML description: {0}", _xmlDocumentLocation), ex);
82 void ProcessAssemblies (LinkContext context, XPathNodeIterator iterator)
84 while (iterator.MoveNext ()) {
85 AssemblyDefinition assembly = GetAssembly (context, GetFullName (iterator.Current));
86 ProcessTypes (assembly, iterator.Current.SelectChildren ("type", _ns));
87 ProcessNamespaces (assembly, iterator.Current.SelectChildren ("namespace", _ns));
91 void ProcessNamespaces (AssemblyDefinition assembly, XPathNodeIterator iterator)
93 while (iterator.MoveNext ()) {
94 string fullname = GetFullName (iterator.Current);
95 foreach (TypeDefinition type in assembly.MainModule.Types) {
96 if (type.Namespace != fullname)
97 continue;
99 MarkAndPreserveAll (type);
104 void MarkAndPreserveAll (TypeDefinition type)
106 Annotations.Mark (type);
107 Annotations.SetPreserve (type, TypePreserve.All);
109 if (!type.HasNestedTypes)
110 return;
112 foreach (TypeDefinition nested in type.NestedTypes)
113 MarkAndPreserveAll (nested);
116 void ProcessTypes (AssemblyDefinition assembly, XPathNodeIterator iterator)
118 while (iterator.MoveNext ()) {
119 XPathNavigator nav = iterator.Current;
120 string fullname = GetFullName (nav);
122 if (IsTypePattern (fullname)) {
123 ProcessTypePattern (fullname, assembly, nav);
124 continue;
127 TypeDefinition type = assembly.MainModule.GetType (fullname);
128 if (type == null)
129 continue;
131 ProcessType (type, nav);
135 static bool IsTypePattern (string fullname)
137 return fullname.IndexOf ("*") != -1;
140 static Regex CreateRegexFromPattern (string pattern)
142 return new Regex (pattern.Replace(".", @"\.").Replace("*", "(.*)"));
145 void MatchType (TypeDefinition type, Regex regex, XPathNavigator nav)
147 if (regex.Match (type.FullName).Success)
148 ProcessType (type, nav);
150 if (!type.HasNestedTypes)
151 return;
153 foreach (var nt in type.NestedTypes)
154 MatchType (nt, regex, nav);
157 void ProcessTypePattern (string fullname, AssemblyDefinition assembly, XPathNavigator nav)
159 Regex regex = CreateRegexFromPattern (fullname);
161 foreach (TypeDefinition type in assembly.MainModule.Types) {
162 MatchType (type, regex, nav);
166 void ProcessType (TypeDefinition type, XPathNavigator nav)
168 TypePreserve preserve = GetTypePreserve (nav);
170 if (!IsRequired (nav)) {
171 Annotations.SetPreserve (type, preserve);
172 return;
175 Annotations.Mark (type);
177 switch (preserve) {
178 case TypePreserve.Nothing:
179 if (!nav.HasChildren)
180 Annotations.SetPreserve (type, TypePreserve.All);
181 break;
182 default:
183 Annotations.SetPreserve (type, preserve);
184 break;
187 if (nav.HasChildren) {
188 MarkSelectedFields (nav, type);
189 MarkSelectedMethods (nav, type);
193 void MarkSelectedFields (XPathNavigator nav, TypeDefinition type)
195 XPathNodeIterator fields = nav.SelectChildren ("field", _ns);
196 if (fields.Count == 0)
197 return;
199 ProcessFields (type, fields);
202 void MarkSelectedMethods (XPathNavigator nav, TypeDefinition type)
204 XPathNodeIterator methods = nav.SelectChildren ("method", _ns);
205 if (methods.Count == 0)
206 return;
208 ProcessMethods (type, methods);
211 static TypePreserve GetTypePreserve (XPathNavigator nav)
213 string attribute = GetAttribute (nav, _preserve);
214 if (attribute == null || attribute.Length == 0)
215 return TypePreserve.Nothing;
217 try {
218 return (TypePreserve) Enum.Parse (typeof (TypePreserve), attribute, true);
219 } catch {
220 return TypePreserve.Nothing;
224 void ProcessFields (TypeDefinition type, XPathNodeIterator iterator)
226 while (iterator.MoveNext ()) {
227 string value = GetSignature (iterator.Current);
228 if (!String.IsNullOrEmpty (value))
229 ProcessFieldSignature (type, value);
231 value = GetAttribute (iterator.Current, "name");
232 if (!String.IsNullOrEmpty (value))
233 ProcessFieldName (type, value);
237 void ProcessFieldSignature (TypeDefinition type, string signature)
239 FieldDefinition field = GetField (type, signature);
240 MarkField (type, field, signature);
243 void MarkField (TypeDefinition type, FieldDefinition field, string signature)
245 if (field != null)
246 Annotations.Mark (field);
247 else
248 AddUnresolveMarker (string.Format ("T: {0}; F: {1}", type, signature));
251 void ProcessFieldName (TypeDefinition type, string name)
253 if (!type.HasFields)
254 return;
256 foreach (FieldDefinition field in type.Fields)
257 if (field.Name == name)
258 MarkField (type, field, name);
261 static FieldDefinition GetField (TypeDefinition type, string signature)
263 if (!type.HasFields)
264 return null;
266 foreach (FieldDefinition field in type.Fields)
267 if (signature == GetFieldSignature (field))
268 return field;
270 return null;
273 static string GetFieldSignature (FieldDefinition field)
275 return field.FieldType.FullName + " " + field.Name;
278 void ProcessMethods (TypeDefinition type, XPathNodeIterator iterator)
280 while (iterator.MoveNext()) {
281 string value = GetSignature (iterator.Current);
282 if (!String.IsNullOrEmpty (value))
283 ProcessMethodSignature (type, value);
285 value = GetAttribute (iterator.Current, "name");
286 if (!String.IsNullOrEmpty (value))
287 ProcessMethodName (type, value);
291 void ProcessMethodSignature (TypeDefinition type, string signature)
293 MethodDefinition meth = GetMethod (type, signature);
294 MarkMethod (type, meth, signature);
297 void MarkMethod (TypeDefinition type, MethodDefinition method, string signature)
299 if (method != null) {
300 Annotations.Mark (method);
301 Annotations.SetAction (method, MethodAction.Parse);
302 } else
303 AddUnresolveMarker (string.Format ("T: {0}; M: {1}", type, signature));
306 void ProcessMethodName (TypeDefinition type, string name)
308 if (!type.HasMethods)
309 return;
311 foreach (MethodDefinition method in type.Methods)
312 if (name == method.Name)
313 MarkMethod (type, method, name);
316 static MethodDefinition GetMethod (TypeDefinition type, string signature)
318 if (type.HasMethods)
319 foreach (MethodDefinition meth in type.Methods)
320 if (signature == GetMethodSignature (meth))
321 return meth;
323 return null;
326 static string GetMethodSignature (MethodDefinition meth)
328 StringBuilder sb = new StringBuilder ();
329 sb.Append (meth.ReturnType.FullName);
330 sb.Append (" ");
331 sb.Append (meth.Name);
332 sb.Append ("(");
333 if (meth.HasParameters) {
334 for (int i = 0; i < meth.Parameters.Count; i++) {
335 if (i > 0)
336 sb.Append (",");
338 sb.Append (meth.Parameters [i].ParameterType.FullName);
341 sb.Append (")");
342 return sb.ToString ();
345 static AssemblyDefinition GetAssembly (LinkContext context, string assemblyName)
347 AssemblyNameReference reference = AssemblyNameReference.Parse (assemblyName);
348 AssemblyDefinition assembly;
350 assembly = context.Resolve (reference);
352 ProcessReferences (assembly, context);
353 return assembly;
356 static void ProcessReferences (AssemblyDefinition assembly, LinkContext context)
358 foreach (AssemblyNameReference name in assembly.MainModule.AssemblyReferences)
359 context.Resolve (name);
362 static bool IsRequired (XPathNavigator nav)
364 string attribute = GetAttribute (nav, _required);
365 if (attribute == null || attribute.Length == 0)
366 return true;
368 return TryParseBool (attribute);
371 static bool TryParseBool (string s)
373 try {
374 return bool.Parse (s);
375 } catch {
376 return false;
380 static string GetSignature (XPathNavigator nav)
382 return GetAttribute (nav, _signature);
385 static string GetFullName (XPathNavigator nav)
387 return GetAttribute (nav, _fullname);
390 static string GetAttribute (XPathNavigator nav, string attribute)
392 return nav.GetAttribute (attribute, _ns);