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