Changed link from GUID to URL
[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 MatchType (TypeDefinition type, Regex regex, XPathNavigator nav)
133                 {
134                         if (regex.Match (type.FullName).Success)
135                                 ProcessType (type, nav);
136
137                         if (!type.HasNestedTypes)
138                                 return;
139
140                         foreach (var nt in type.NestedTypes)
141                                 MatchType (nt, regex, nav);
142                 }
143
144                 void ProcessTypePattern (string fullname, AssemblyDefinition assembly, XPathNavigator nav)
145                 {
146                         Regex regex = CreateRegexFromPattern (fullname);
147
148                         foreach (TypeDefinition type in assembly.MainModule.Types) {
149                                 MatchType (type, regex, nav);
150                         }
151                 }
152
153                 void ProcessType (TypeDefinition type, XPathNavigator nav)
154                 {
155                         TypePreserve preserve = GetTypePreserve (nav);
156
157                         if (!IsRequired (nav)) {
158                                 Annotations.SetPreserve (type, preserve);
159                                 return;
160                         }
161
162                         Annotations.Mark (type);
163
164                         switch (preserve) {
165                         case TypePreserve.Nothing:
166                                 if (!nav.HasChildren)
167                                         Annotations.SetPreserve (type, TypePreserve.All);
168                                 break;
169                         default:
170                                 Annotations.SetPreserve (type, preserve);
171                                 break;
172                         }
173
174                         if (nav.HasChildren) {
175                                 MarkSelectedFields (nav, type);
176                                 MarkSelectedMethods (nav, type);
177                         }
178                 }
179
180                 void MarkSelectedFields (XPathNavigator nav, TypeDefinition type)
181                 {
182                         XPathNodeIterator fields = nav.SelectChildren ("field", _ns);
183                         if (fields.Count == 0)
184                                 return;
185
186                         ProcessFields (type, fields);
187                 }
188
189                 void MarkSelectedMethods (XPathNavigator nav, TypeDefinition type)
190                 {
191                         XPathNodeIterator methods = nav.SelectChildren ("method", _ns);
192                         if (methods.Count == 0)
193                                 return;
194
195                         ProcessMethods (type, methods);
196                 }
197
198                 static TypePreserve GetTypePreserve (XPathNavigator nav)
199                 {
200                         string attribute = GetAttribute (nav, _preserve);
201                         if (attribute == null || attribute.Length == 0)
202                                 return TypePreserve.Nothing;
203
204                         try {
205                                 return (TypePreserve) Enum.Parse (typeof (TypePreserve), attribute, true);
206                         } catch {
207                                 return TypePreserve.Nothing;
208                         }
209                 }
210
211                 void ProcessFields (TypeDefinition type, XPathNodeIterator iterator)
212                 {
213                         while (iterator.MoveNext ()) {
214                                 string value = GetSignature (iterator.Current);
215                                 if (!String.IsNullOrEmpty (value))
216                                         ProcessFieldSignature (type, value);
217
218                                 value = GetAttribute (iterator.Current, "name");
219                                 if (!String.IsNullOrEmpty (value))
220                                         ProcessFieldName (type, value);
221                         }
222                 }
223
224                 void ProcessFieldSignature (TypeDefinition type, string signature)
225                 {
226                         FieldDefinition field = GetField (type, signature);
227                         MarkField (type, field, signature);
228                 }
229
230                 void MarkField (TypeDefinition type, FieldDefinition field, string signature)
231                 {
232                         if (field != null)
233                                 Annotations.Mark (field);
234                         else
235                                 AddUnresolveMarker (string.Format ("T: {0}; F: {1}", type, signature));
236                 }
237
238                 void ProcessFieldName (TypeDefinition type, string name)
239                 {
240                         if (!type.HasFields)
241                                 return;
242
243                         foreach (FieldDefinition field in type.Fields)
244                                 if (field.Name == name)
245                                         MarkField (type, field, name);
246                 }
247
248                 static FieldDefinition GetField (TypeDefinition type, string signature)
249                 {
250                         if (!type.HasFields)
251                                 return null;
252
253                         foreach (FieldDefinition field in type.Fields)
254                                 if (signature == GetFieldSignature (field))
255                                         return field;
256
257                         return null;
258                 }
259
260                 static string GetFieldSignature (FieldDefinition field)
261                 {
262                         return field.FieldType.FullName + " " + field.Name;
263                 }
264
265                 void ProcessMethods (TypeDefinition type, XPathNodeIterator iterator)
266                 {
267                         while (iterator.MoveNext()) {
268                                 string value = GetSignature (iterator.Current);
269                                 if (!String.IsNullOrEmpty (value))
270                                         ProcessMethodSignature (type, value);
271
272                                 value = GetAttribute (iterator.Current, "name");
273                                 if (!String.IsNullOrEmpty (value))
274                                         ProcessMethodName (type, value);
275                         }
276                 }
277
278                 void ProcessMethodSignature (TypeDefinition type, string signature)
279                 {
280                         MethodDefinition meth = GetMethod (type, signature);
281                         MarkMethod (type, meth, signature);
282                 }
283
284                 void MarkMethod (TypeDefinition type, MethodDefinition method, string signature)
285                 {
286                         if (method != null) {
287                                 Annotations.Mark (method);
288                                 Annotations.SetAction (method, MethodAction.Parse);
289                         } else
290                                 AddUnresolveMarker (string.Format ("T: {0}; M: {1}", type, signature));
291                 }
292
293                 void ProcessMethodName (TypeDefinition type, string name)
294                 {
295                         if (!type.HasMethods)
296                                 return;
297
298                         foreach (MethodDefinition method in type.Methods)
299                                 if (name == method.Name)
300                                         MarkMethod (type, method, name);
301                 }
302
303                 static MethodDefinition GetMethod (TypeDefinition type, string signature)
304                 {
305                         if (type.HasMethods)
306                                 foreach (MethodDefinition meth in type.Methods)
307                                         if (signature == GetMethodSignature (meth))
308                                                 return meth;
309
310                         return null;
311                 }
312
313                 static string GetMethodSignature (MethodDefinition meth)
314                 {
315                         StringBuilder sb = new StringBuilder ();
316                         sb.Append (meth.ReturnType.FullName);
317                         sb.Append (" ");
318                         sb.Append (meth.Name);
319                         sb.Append ("(");
320                         if (meth.HasParameters) {
321                                 for (int i = 0; i < meth.Parameters.Count; i++) {
322                                         if (i > 0)
323                                                 sb.Append (",");
324
325                                         sb.Append (meth.Parameters [i].ParameterType.FullName);
326                                 }
327                         }
328                         sb.Append (")");
329                         return sb.ToString ();
330                 }
331
332                 static AssemblyDefinition GetAssembly (LinkContext context, string assemblyName)
333                 {
334                         AssemblyNameReference reference = AssemblyNameReference.Parse (assemblyName);
335                         AssemblyDefinition assembly;
336
337                         assembly = context.Resolve (reference);
338
339                         ProcessReferences (assembly, context);
340                         return assembly;
341                 }
342
343                 static void ProcessReferences (AssemblyDefinition assembly, LinkContext context)
344                 {
345                         foreach (AssemblyNameReference name in assembly.MainModule.AssemblyReferences)
346                                 context.Resolve (name);
347                 }
348
349                 static bool IsRequired (XPathNavigator nav)
350                 {
351                         string attribute = GetAttribute (nav, _required);
352                         if (attribute == null || attribute.Length == 0)
353                                 return true;
354
355                         return TryParseBool (attribute);
356                 }
357
358                 static bool TryParseBool (string s)
359                 {
360                         try {
361                                 return bool.Parse (s);
362                         } catch {
363                                 return false;
364                         }
365                 }
366
367                 static string GetSignature (XPathNavigator nav)
368                 {
369                         return GetAttribute (nav, _signature);
370                 }
371
372                 static string GetFullName (XPathNavigator nav)
373                 {
374                         return GetAttribute (nav, _fullname);
375                 }
376
377                 static string GetAttribute (XPathNavigator nav, string attribute)
378                 {
379                         return nav.GetAttribute (attribute, _ns);
380                 }
381         }
382 }