Merge pull request #1936 from esdrubal/DotNetRelativeOrAbsolute
[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 ResolveFromXmlStep : ResolveStep {
42
43                 static readonly string _signature = "signature";
44                 static readonly string _fullname = "fullname";
45                 static readonly string _required = "required";
46                 static readonly string _preserve = "preserve";
47                 static readonly string _ns = string.Empty;
48
49                 XPathDocument _document;
50
51                 public ResolveFromXmlStep (XPathDocument document)
52                 {
53                         _document = document;
54                 }
55
56                 protected override void Process ()
57                 {
58                         XPathNavigator nav = _document.CreateNavigator ();
59                         nav.MoveToFirstChild ();
60
61                         // This step can be created with XML files that aren't necessarily
62                         // linker descriptor files. So bail if we don't have a <linker> element.
63                         if (nav.LocalName != "linker")
64                                 return;
65
66                         ProcessAssemblies (Context, nav.SelectChildren ("assembly", _ns));
67                 }
68
69                 void ProcessAssemblies (LinkContext context, XPathNodeIterator iterator)
70                 {
71                         while (iterator.MoveNext ()) {
72                                 AssemblyDefinition assembly = GetAssembly (context, GetFullName (iterator.Current));
73                                 ProcessTypes (assembly, iterator.Current.SelectChildren ("type", _ns));
74                                 ProcessNamespaces (assembly, iterator.Current.SelectChildren ("namespace", _ns));
75                         }
76                 }
77
78                 void ProcessNamespaces (AssemblyDefinition assembly, XPathNodeIterator iterator)
79                 {
80                         while (iterator.MoveNext ()) {
81                                 string fullname = GetFullName (iterator.Current);
82                                 foreach (TypeDefinition type in assembly.MainModule.Types) {
83                                         if (type.Namespace != fullname)
84                                                 continue;
85
86                                         MarkAndPreserveAll (type);
87                                 }
88                         }
89                 }
90
91                 void MarkAndPreserveAll (TypeDefinition type)
92                 {
93                         Annotations.Mark (type);
94                         Annotations.SetPreserve (type, TypePreserve.All);
95
96                         if (!type.HasNestedTypes)
97                                 return;
98
99                         foreach (TypeDefinition nested in type.NestedTypes)
100                                 MarkAndPreserveAll (nested);
101                 }
102
103                 void ProcessTypes (AssemblyDefinition assembly, XPathNodeIterator iterator)
104                 {
105                         while (iterator.MoveNext ()) {
106                                 XPathNavigator nav = iterator.Current;
107                                 string fullname = GetFullName (nav);
108
109                                 if (IsTypePattern (fullname)) {
110                                         ProcessTypePattern (fullname, assembly, nav);
111                                         continue;
112                                 }
113
114                                 TypeDefinition type = assembly.MainModule.GetType (fullname);
115                                 if (type == null)
116                                         continue;
117
118                                 ProcessType (type, nav);
119                         }
120                 }
121
122                 static bool IsTypePattern (string fullname)
123                 {
124                         return fullname.IndexOf ("*") != -1;
125                 }
126
127                 static Regex CreateRegexFromPattern (string pattern)
128                 {
129                         return new Regex (pattern.Replace(".", @"\.").Replace("*", "(.*)"));
130                 }
131
132                 void ProcessTypePattern (string fullname, AssemblyDefinition assembly, XPathNavigator nav)
133                 {
134                         Regex regex = CreateRegexFromPattern (fullname);
135
136                         foreach (TypeDefinition type in assembly.MainModule.Types) {
137                                 if (!regex.Match (type.FullName).Success)
138                                         continue;
139
140                                 ProcessType (type, nav);
141                         }
142                 }
143
144                 void ProcessType (TypeDefinition type, XPathNavigator nav)
145                 {
146                         TypePreserve preserve = GetTypePreserve (nav);
147
148                         if (!IsRequired (nav)) {
149                                 Annotations.SetPreserve (type, preserve);
150                                 return;
151                         }
152
153                         Annotations.Mark (type);
154
155                         switch (preserve) {
156                         case TypePreserve.Nothing:
157                                 if (!nav.HasChildren)
158                                         Annotations.SetPreserve (type, TypePreserve.All);
159                                 break;
160                         default:
161                                 Annotations.SetPreserve (type, preserve);
162                                 break;
163                         }
164
165                         if (nav.HasChildren) {
166                                 MarkSelectedFields (nav, type);
167                                 MarkSelectedMethods (nav, type);
168                         }
169                 }
170
171                 void MarkSelectedFields (XPathNavigator nav, TypeDefinition type)
172                 {
173                         XPathNodeIterator fields = nav.SelectChildren ("field", _ns);
174                         if (fields.Count == 0)
175                                 return;
176
177                         ProcessFields (type, fields);
178                 }
179
180                 void MarkSelectedMethods (XPathNavigator nav, TypeDefinition type)
181                 {
182                         XPathNodeIterator methods = nav.SelectChildren ("method", _ns);
183                         if (methods.Count == 0)
184                                 return;
185
186                         ProcessMethods (type, methods);
187                 }
188
189                 static TypePreserve GetTypePreserve (XPathNavigator nav)
190                 {
191                         string attribute = GetAttribute (nav, _preserve);
192                         if (attribute == null || attribute.Length == 0)
193                                 return TypePreserve.Nothing;
194
195                         try {
196                                 return (TypePreserve) Enum.Parse (typeof (TypePreserve), attribute, true);
197                         } catch {
198                                 return TypePreserve.Nothing;
199                         }
200                 }
201
202                 void ProcessFields (TypeDefinition type, XPathNodeIterator iterator)
203                 {
204                         while (iterator.MoveNext ()) {
205                                 string value = GetSignature (iterator.Current);
206                                 if (!String.IsNullOrEmpty (value))
207                                         ProcessFieldSignature (type, value);
208
209                                 value = GetAttribute (iterator.Current, "name");
210                                 if (!String.IsNullOrEmpty (value))
211                                         ProcessFieldName (type, value);
212                         }
213                 }
214
215                 void ProcessFieldSignature (TypeDefinition type, string signature)
216                 {
217                         FieldDefinition field = GetField (type, signature);
218                         MarkField (type, field, signature);
219                 }
220
221                 void MarkField (TypeDefinition type, FieldDefinition field, string signature)
222                 {
223                         if (field != null)
224                                 Annotations.Mark (field);
225                         else
226                                 AddUnresolveMarker (string.Format ("T: {0}; F: {1}", type, signature));
227                 }
228
229                 void ProcessFieldName (TypeDefinition type, string name)
230                 {
231                         if (!type.HasFields)
232                                 return;
233
234                         foreach (FieldDefinition field in type.Fields)
235                                 if (field.Name == name)
236                                         MarkField (type, field, name);
237                 }
238
239                 static FieldDefinition GetField (TypeDefinition type, string signature)
240                 {
241                         if (!type.HasFields)
242                                 return null;
243
244                         foreach (FieldDefinition field in type.Fields)
245                                 if (signature == GetFieldSignature (field))
246                                         return field;
247
248                         return null;
249                 }
250
251                 static string GetFieldSignature (FieldDefinition field)
252                 {
253                         return field.FieldType.FullName + " " + field.Name;
254                 }
255
256                 void ProcessMethods (TypeDefinition type, XPathNodeIterator iterator)
257                 {
258                         while (iterator.MoveNext()) {
259                                 string value = GetSignature (iterator.Current);
260                                 if (!String.IsNullOrEmpty (value))
261                                         ProcessMethodSignature (type, value);
262
263                                 value = GetAttribute (iterator.Current, "name");
264                                 if (!String.IsNullOrEmpty (value))
265                                         ProcessMethodName (type, value);
266                         }
267                 }
268
269                 void ProcessMethodSignature (TypeDefinition type, string signature)
270                 {
271                         MethodDefinition meth = GetMethod (type, signature);
272                         MarkMethod (type, meth, signature);
273                 }
274
275                 void MarkMethod (TypeDefinition type, MethodDefinition method, string signature)
276                 {
277                         if (method != null) {
278                                 Annotations.Mark (method);
279                                 Annotations.SetAction (method, MethodAction.Parse);
280                         } else
281                                 AddUnresolveMarker (string.Format ("T: {0}; M: {1}", type, signature));
282                 }
283
284                 void ProcessMethodName (TypeDefinition type, string name)
285                 {
286                         if (!type.HasMethods)
287                                 return;
288
289                         foreach (MethodDefinition method in type.Methods)
290                                 if (name == method.Name)
291                                         MarkMethod (type, method, name);
292                 }
293
294                 static MethodDefinition GetMethod (TypeDefinition type, string signature)
295                 {
296                         if (type.HasMethods)
297                                 foreach (MethodDefinition meth in type.Methods)
298                                         if (signature == GetMethodSignature (meth))
299                                                 return meth;
300
301                         return null;
302                 }
303
304                 static string GetMethodSignature (MethodDefinition meth)
305                 {
306                         StringBuilder sb = new StringBuilder ();
307                         sb.Append (meth.ReturnType.FullName);
308                         sb.Append (" ");
309                         sb.Append (meth.Name);
310                         sb.Append ("(");
311                         if (meth.HasParameters) {
312                                 for (int i = 0; i < meth.Parameters.Count; i++) {
313                                         if (i > 0)
314                                                 sb.Append (",");
315
316                                         sb.Append (meth.Parameters [i].ParameterType.FullName);
317                                 }
318                         }
319                         sb.Append (")");
320                         return sb.ToString ();
321                 }
322
323                 static AssemblyDefinition GetAssembly (LinkContext context, string assemblyName)
324                 {
325                         AssemblyNameReference reference = AssemblyNameReference.Parse (assemblyName);
326                         AssemblyDefinition assembly;
327
328                         assembly = context.Resolve (reference);
329
330                         ProcessReferences (assembly, context);
331                         return assembly;
332                 }
333
334                 static void ProcessReferences (AssemblyDefinition assembly, LinkContext context)
335                 {
336                         foreach (AssemblyNameReference name in assembly.MainModule.AssemblyReferences)
337                                 context.Resolve (name);
338                 }
339
340                 static bool IsRequired (XPathNavigator nav)
341                 {
342                         string attribute = GetAttribute (nav, _required);
343                         if (attribute == null || attribute.Length == 0)
344                                 return true;
345
346                         return TryParseBool (attribute);
347                 }
348
349                 static bool TryParseBool (string s)
350                 {
351                         try {
352                                 return bool.Parse (s);
353                         } catch {
354                                 return false;
355                         }
356                 }
357
358                 static string GetSignature (XPathNavigator nav)
359                 {
360                         return GetAttribute (nav, _signature);
361                 }
362
363                 static string GetFullName (XPathNavigator nav)
364                 {
365                         return GetAttribute (nav, _fullname);
366                 }
367
368                 static string GetAttribute (XPathNavigator nav, string attribute)
369                 {
370                         return nav.GetAttribute (attribute, _ns);
371                 }
372         }
373 }