aa4f6afc760388c38a5f1267ab4f2aa69f9ce473
[mono.git] / mcs / tools / linker / Mono.Linker.Steps / ResolveFromXmlStep.cs
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.
10 //
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:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
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.
29 //
30
31 using System;
32 using SR = System.Reflection;
33 using System.Text;
34 using System.Text.RegularExpressions;
35 using System.Xml.XPath;
36
37 using Mono.Cecil;
38
39 namespace Mono.Linker.Steps {
40
41         public class XmlResolutionException : Exception {
42                 public XmlResolutionException (string message, Exception innerException)
43                         : base (message, innerException)
44                 {
45                 }
46         }
47
48         public class ResolveFromXmlStep : ResolveStep {
49
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;
55
56                 XPathDocument _document;
57                 string _xmlDocumentLocation;
58
59                 public ResolveFromXmlStep (XPathDocument document, string xmlDocumentLocation = "<unspecified>")
60                 {
61                         _document = document;
62                         _xmlDocumentLocation = xmlDocumentLocation;
63                 }
64
65                 protected override void Process ()
66                 {
67                         XPathNavigator nav = _document.CreateNavigator ();
68                         nav.MoveToFirstChild ();
69
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;
74
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);
79                         }
80                 }
81
82                 void ProcessAssemblies (LinkContext context, XPathNodeIterator iterator)
83                 {
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));
88                         }
89                 }
90
91                 void ProcessNamespaces (AssemblyDefinition assembly, XPathNodeIterator iterator)
92                 {
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;
98
99                                         MarkAndPreserveAll (type);
100                                 }
101                         }
102                 }
103
104                 void MarkAndPreserveAll (TypeDefinition type)
105                 {
106                         Annotations.Mark (type);
107                         Annotations.SetPreserve (type, TypePreserve.All);
108
109                         if (!type.HasNestedTypes)
110                                 return;
111
112                         foreach (TypeDefinition nested in type.NestedTypes)
113                                 MarkAndPreserveAll (nested);
114                 }
115
116                 void ProcessTypes (AssemblyDefinition assembly, XPathNodeIterator iterator)
117                 {
118                         while (iterator.MoveNext ()) {
119                                 XPathNavigator nav = iterator.Current;
120                                 string fullname = GetFullName (nav);
121
122                                 if (IsTypePattern (fullname)) {
123                                         ProcessTypePattern (fullname, assembly, nav);
124                                         continue;
125                                 }
126
127                                 TypeDefinition type = assembly.MainModule.GetType (fullname);
128                                 if (type == null)
129                                         continue;
130
131                                 ProcessType (type, nav);
132                         }
133                 }
134
135                 static bool IsTypePattern (string fullname)
136                 {
137                         return fullname.IndexOf ("*") != -1;
138                 }
139
140                 static Regex CreateRegexFromPattern (string pattern)
141                 {
142                         return new Regex (pattern.Replace(".", @"\.").Replace("*", "(.*)"));
143                 }
144
145                 void MatchType (TypeDefinition type, Regex regex, XPathNavigator nav)
146                 {
147                         if (regex.Match (type.FullName).Success)
148                                 ProcessType (type, nav);
149
150                         if (!type.HasNestedTypes)
151                                 return;
152
153                         foreach (var nt in type.NestedTypes)
154                                 MatchType (nt, regex, nav);
155                 }
156
157                 void ProcessTypePattern (string fullname, AssemblyDefinition assembly, XPathNavigator nav)
158                 {
159                         Regex regex = CreateRegexFromPattern (fullname);
160
161                         foreach (TypeDefinition type in assembly.MainModule.Types) {
162                                 MatchType (type, regex, nav);
163                         }
164                 }
165
166                 void ProcessType (TypeDefinition type, XPathNavigator nav)
167                 {
168                         TypePreserve preserve = GetTypePreserve (nav);
169
170                         if (!IsRequired (nav)) {
171                                 Annotations.SetPreserve (type, preserve);
172                                 return;
173                         }
174
175                         Annotations.Mark (type);
176
177                         if (type.IsNested) {
178                                 var parent = type;
179                                 while (parent.IsNested) {
180                                         parent = parent.DeclaringType;
181                                         Annotations.Mark (parent);
182                                 }
183                         }
184
185                         switch (preserve) {
186                         case TypePreserve.Nothing:
187                                 if (!nav.HasChildren)
188                                         Annotations.SetPreserve (type, TypePreserve.All);
189                                 break;
190                         default:
191                                 Annotations.SetPreserve (type, preserve);
192                                 break;
193                         }
194
195                         if (nav.HasChildren) {
196                                 MarkSelectedFields (nav, type);
197                                 MarkSelectedMethods (nav, type);
198                         }
199                 }
200
201                 void MarkSelectedFields (XPathNavigator nav, TypeDefinition type)
202                 {
203                         XPathNodeIterator fields = nav.SelectChildren ("field", _ns);
204                         if (fields.Count == 0)
205                                 return;
206
207                         ProcessFields (type, fields);
208                 }
209
210                 void MarkSelectedMethods (XPathNavigator nav, TypeDefinition type)
211                 {
212                         XPathNodeIterator methods = nav.SelectChildren ("method", _ns);
213                         if (methods.Count == 0)
214                                 return;
215
216                         ProcessMethods (type, methods);
217                 }
218
219                 static TypePreserve GetTypePreserve (XPathNavigator nav)
220                 {
221                         string attribute = GetAttribute (nav, _preserve);
222                         if (attribute == null || attribute.Length == 0)
223                                 return TypePreserve.Nothing;
224
225                         try {
226                                 return (TypePreserve) Enum.Parse (typeof (TypePreserve), attribute, true);
227                         } catch {
228                                 return TypePreserve.Nothing;
229                         }
230                 }
231
232                 void ProcessFields (TypeDefinition type, XPathNodeIterator iterator)
233                 {
234                         while (iterator.MoveNext ()) {
235                                 string value = GetSignature (iterator.Current);
236                                 if (!String.IsNullOrEmpty (value))
237                                         ProcessFieldSignature (type, value);
238
239                                 value = GetAttribute (iterator.Current, "name");
240                                 if (!String.IsNullOrEmpty (value))
241                                         ProcessFieldName (type, value);
242                         }
243                 }
244
245                 void ProcessFieldSignature (TypeDefinition type, string signature)
246                 {
247                         FieldDefinition field = GetField (type, signature);
248                         MarkField (type, field, signature);
249                 }
250
251                 void MarkField (TypeDefinition type, FieldDefinition field, string signature)
252                 {
253                         if (field != null)
254                                 Annotations.Mark (field);
255                         else
256                                 AddUnresolveMarker (string.Format ("T: {0}; F: {1}", type, signature));
257                 }
258
259                 void ProcessFieldName (TypeDefinition type, string name)
260                 {
261                         if (!type.HasFields)
262                                 return;
263
264                         foreach (FieldDefinition field in type.Fields)
265                                 if (field.Name == name)
266                                         MarkField (type, field, name);
267                 }
268
269                 static FieldDefinition GetField (TypeDefinition type, string signature)
270                 {
271                         if (!type.HasFields)
272                                 return null;
273
274                         foreach (FieldDefinition field in type.Fields)
275                                 if (signature == GetFieldSignature (field))
276                                         return field;
277
278                         return null;
279                 }
280
281                 static string GetFieldSignature (FieldDefinition field)
282                 {
283                         return field.FieldType.FullName + " " + field.Name;
284                 }
285
286                 void ProcessMethods (TypeDefinition type, XPathNodeIterator iterator)
287                 {
288                         while (iterator.MoveNext()) {
289                                 string value = GetSignature (iterator.Current);
290                                 if (!String.IsNullOrEmpty (value))
291                                         ProcessMethodSignature (type, value);
292
293                                 value = GetAttribute (iterator.Current, "name");
294                                 if (!String.IsNullOrEmpty (value))
295                                         ProcessMethodName (type, value);
296                         }
297                 }
298
299                 void ProcessMethodSignature (TypeDefinition type, string signature)
300                 {
301                         MethodDefinition meth = GetMethod (type, signature);
302                         MarkMethod (type, meth, signature);
303                 }
304
305                 void MarkMethod (TypeDefinition type, MethodDefinition method, string signature)
306                 {
307                         if (method != null) {
308                                 Annotations.Mark (method);
309                                 Annotations.SetAction (method, MethodAction.Parse);
310                         } else
311                                 AddUnresolveMarker (string.Format ("T: {0}; M: {1}", type, signature));
312                 }
313
314                 void ProcessMethodName (TypeDefinition type, string name)
315                 {
316                         if (!type.HasMethods)
317                                 return;
318
319                         foreach (MethodDefinition method in type.Methods)
320                                 if (name == method.Name)
321                                         MarkMethod (type, method, name);
322                 }
323
324                 static MethodDefinition GetMethod (TypeDefinition type, string signature)
325                 {
326                         if (type.HasMethods)
327                                 foreach (MethodDefinition meth in type.Methods)
328                                         if (signature == GetMethodSignature (meth))
329                                                 return meth;
330
331                         return null;
332                 }
333
334                 static string GetMethodSignature (MethodDefinition meth)
335                 {
336                         StringBuilder sb = new StringBuilder ();
337                         sb.Append (meth.ReturnType.FullName);
338                         sb.Append (" ");
339                         sb.Append (meth.Name);
340                         sb.Append ("(");
341                         if (meth.HasParameters) {
342                                 for (int i = 0; i < meth.Parameters.Count; i++) {
343                                         if (i > 0)
344                                                 sb.Append (",");
345
346                                         sb.Append (meth.Parameters [i].ParameterType.FullName);
347                                 }
348                         }
349                         sb.Append (")");
350                         return sb.ToString ();
351                 }
352
353                 static AssemblyDefinition GetAssembly (LinkContext context, string assemblyName)
354                 {
355                         AssemblyNameReference reference = AssemblyNameReference.Parse (assemblyName);
356                         AssemblyDefinition assembly;
357
358                         assembly = context.Resolve (reference);
359
360                         ProcessReferences (assembly, context);
361                         return assembly;
362                 }
363
364                 static void ProcessReferences (AssemblyDefinition assembly, LinkContext context)
365                 {
366                         foreach (AssemblyNameReference name in assembly.MainModule.AssemblyReferences)
367                                 context.Resolve (name);
368                 }
369
370                 static bool IsRequired (XPathNavigator nav)
371                 {
372                         string attribute = GetAttribute (nav, _required);
373                         if (attribute == null || attribute.Length == 0)
374                                 return true;
375
376                         return TryParseBool (attribute);
377                 }
378
379                 static bool TryParseBool (string s)
380                 {
381                         try {
382                                 return bool.Parse (s);
383                         } catch {
384                                 return false;
385                         }
386                 }
387
388                 static string GetSignature (XPathNavigator nav)
389                 {
390                         return GetAttribute (nav, _signature);
391                 }
392
393                 static string GetFullName (XPathNavigator nav)
394                 {
395                         return GetAttribute (nav, _fullname);
396                 }
397
398                 static string GetAttribute (XPathNavigator nav, string attribute)
399                 {
400                         return nav.GetAttribute (attribute, _ns);
401                 }
402         }
403 }