3fe12ae667c60f4ab5d9772833d6901b2891b452
[mono.git] / mcs / tools / linker / Mono.Linker.Steps / TypeMapStep.cs
1 //
2 // TypeMapStep.cs
3 //
4 // Author:
5 //   Jb Evain (jbevain@novell.com)
6 //
7 // (C) 2009 Novell, Inc.
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System;
30 using System.Collections;
31 using System.Collections.Generic;
32
33 using Mono.Cecil;
34
35 namespace Mono.Linker.Steps {
36
37         public class TypeMapStep : BaseStep {
38
39                 protected override void ProcessAssembly (AssemblyDefinition assembly)
40                 {
41                         foreach (TypeDefinition type in assembly.MainModule.Types)
42                                 MapType (type);
43                 }
44
45                 protected virtual void MapType (TypeDefinition type)
46                 {
47                         MapVirtualMethods (type);
48                         MapInterfaceMethodsInTypeHierarchy (type);
49
50                         if (!type.HasNestedTypes)
51                                 return;
52
53                         foreach (var nested in type.NestedTypes)
54                                 MapType (nested);
55                 }
56
57                 void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type)
58                 {
59                         if (!type.HasInterfaces)
60                                 return;
61
62                         foreach (var @interface in type.Interfaces) {
63                                 var iface = @interface.InterfaceType.Resolve ();
64                                 if (iface == null || !iface.HasMethods)
65                                         continue;
66
67                                 foreach (MethodDefinition method in iface.Methods) {
68                                         if (TryMatchMethod (type, method) != null)
69                                                 continue;
70
71                                         var @base = GetBaseMethodInTypeHierarchy (type, method);
72                                         if (@base == null)
73                                                 continue;
74
75                                         Annotations.AddPreservedMethod (type, @base);
76                                 }
77                         }
78                 }
79
80                 void MapVirtualMethods (TypeDefinition type)
81                 {
82                         if (!type.HasMethods)
83                                 return;
84
85                         foreach (MethodDefinition method in type.Methods) {
86                                 if (!method.IsVirtual)
87                                         continue;
88
89                                 MapVirtualMethod (method);
90
91                                 if (method.HasOverrides)
92                                         MapOverrides (method);
93                         }
94                 }
95
96                 void MapVirtualMethod (MethodDefinition method)
97                 {
98                         MapVirtualBaseMethod (method);
99                         MapVirtualInterfaceMethod (method);
100                 }
101
102                 void MapVirtualBaseMethod (MethodDefinition method)
103                 {
104                         MethodDefinition @base = GetBaseMethodInTypeHierarchy (method);
105                         if (@base == null)
106                                 return;
107
108                         AnnotateMethods (@base, method);
109                 }
110
111                 void MapVirtualInterfaceMethod (MethodDefinition method)
112                 {
113                         foreach (MethodDefinition @base in GetBaseMethodsInInterfaceHierarchy (method))
114                                 AnnotateMethods (@base, method);
115                 }
116
117                 void MapOverrides (MethodDefinition method)
118                 {
119                         foreach (MethodReference override_ref in method.Overrides) {
120                                 MethodDefinition @override = override_ref.Resolve ();
121                                 if (@override == null)
122                                         continue;
123
124                                 AnnotateMethods (@override, method);
125                         }
126                 }
127
128                 void AnnotateMethods (MethodDefinition @base, MethodDefinition @override)
129                 {
130                         Annotations.AddBaseMethod (@override, @base);
131                         Annotations.AddOverride (@base, @override);
132                 }
133
134                 static MethodDefinition GetBaseMethodInTypeHierarchy (MethodDefinition method)
135                 {
136                         return GetBaseMethodInTypeHierarchy (method.DeclaringType, method);
137                 }
138
139                 static MethodDefinition GetBaseMethodInTypeHierarchy (TypeDefinition type, MethodDefinition method)
140                 {
141                         TypeDefinition @base = GetBaseType (type);
142                         while (@base != null) {
143                                 MethodDefinition base_method = TryMatchMethod (@base, method);
144                                 if (base_method != null)
145                                         return base_method;
146
147                                 @base = GetBaseType (@base);
148                         }
149
150                         return null;
151                 }
152
153                 static IEnumerable<MethodDefinition> GetBaseMethodsInInterfaceHierarchy (MethodDefinition method)
154                 {
155                         return GetBaseMethodsInInterfaceHierarchy (method.DeclaringType, method);
156                 }
157
158                 static IEnumerable<MethodDefinition> GetBaseMethodsInInterfaceHierarchy (TypeDefinition type, MethodDefinition method)
159                 {
160                         if (!type.HasInterfaces)
161                                 yield break;
162
163                         foreach (var interface_ref in type.Interfaces) {
164                                 TypeDefinition @interface = interface_ref.InterfaceType.Resolve ();
165                                 if (@interface == null)
166                                         continue;
167
168                                 MethodDefinition base_method = TryMatchMethod (@interface, method);
169                                 if (base_method != null)
170                                         yield return base_method;
171
172                                 foreach (MethodDefinition @base in GetBaseMethodsInInterfaceHierarchy (@interface, method))
173                                         yield return @base;
174                         }
175                 }
176
177                 static MethodDefinition TryMatchMethod (TypeDefinition type, MethodDefinition method)
178                 {
179                         if (!type.HasMethods)
180                                 return null;
181
182                         Dictionary<string,string> gp = null;
183                         foreach (MethodDefinition candidate in type.Methods) {
184                                 if (MethodMatch (candidate, method, ref gp))
185                                         return candidate;
186                                 if (gp != null)
187                                         gp.Clear ();
188                         }
189
190                         return null;
191                 }
192
193                 static bool MethodMatch (MethodDefinition candidate, MethodDefinition method, ref Dictionary<string,string> genericParameters)
194                 {
195                         if (!candidate.IsVirtual)
196                                 return false;
197
198                         if (candidate.HasParameters != method.HasParameters)
199                                 return false;
200
201                         if (candidate.Name != method.Name)
202                                 return false;
203
204                         if (candidate.HasGenericParameters != method.HasGenericParameters)
205                                 return false;
206
207                         // we need to track what the generic parameter represent - as we cannot allow it to
208                         // differ between the return type or any parameter
209                         if (!TypeMatch (candidate.ReturnType, method.ReturnType, ref genericParameters))
210                                 return false;
211
212                         if (!candidate.HasParameters)
213                                 return true;
214
215                         var cp = candidate.Parameters;
216                         var mp = method.Parameters;
217                         if (cp.Count != mp.Count)
218                                 return false;
219
220                         for (int i = 0; i < cp.Count; i++) {
221                                 if (!TypeMatch (cp [i].ParameterType, mp [i].ParameterType, ref genericParameters))
222                                         return false;
223                         }
224
225                         return true;
226                 }
227
228                 static bool TypeMatch (IModifierType a, IModifierType b, ref Dictionary<string,string> gp)
229                 {
230                         if (!TypeMatch (a.ModifierType, b.ModifierType, ref gp))
231                                 return false;
232
233                         return TypeMatch (a.ElementType, b.ElementType, ref gp);
234                 }
235
236                 static bool TypeMatch (TypeSpecification a, TypeSpecification b, ref Dictionary<string,string> gp)
237                 {
238                         var gita = a as GenericInstanceType;
239                         if (gita != null)
240                                 return TypeMatch (gita, (GenericInstanceType) b, ref gp);
241
242                         var mta = a as IModifierType;
243                         if (mta != null)
244                                 return TypeMatch (mta, (IModifierType) b, ref gp);
245
246                         return TypeMatch (a.ElementType, b.ElementType, ref gp);
247                 }
248
249                 static bool TypeMatch (GenericInstanceType a, GenericInstanceType b, ref Dictionary<string,string> gp)
250                 {
251                         if (!TypeMatch (a.ElementType, b.ElementType, ref gp))
252                                 return false;
253
254                         if (a.HasGenericArguments != b.HasGenericArguments)
255                                 return false;
256
257                         if (!a.HasGenericArguments)
258                                 return true;
259
260                         var gaa = a.GenericArguments;
261                         var gab = b.GenericArguments;
262                         if (gaa.Count != gab.Count)
263                                 return false;
264
265                         for (int i = 0; i < gaa.Count; i++) {
266                                 if (!TypeMatch (gaa [i], gab [i], ref gp))
267                                         return false;
268                         }
269
270                         return true;
271                 }
272
273                 static bool TypeMatch (TypeReference a, TypeReference b, ref Dictionary<string,string> gp)
274                 {
275                         var gpa = a as GenericParameter;
276                         if (gpa != null) {
277                                 if (gp == null)
278                                         gp = new Dictionary<string, string> ();
279                                 string match;
280                                 if (!gp.TryGetValue (gpa.FullName, out match)) {
281                                         // first use, we assume it will always be used this way
282                                         gp.Add (gpa.FullName, b.ToString ());
283                                         return true;
284                                 }
285                                 // re-use, it should match the previous usage
286                                 return match == b.ToString ();
287                         }
288
289                         if (a is TypeSpecification || b is TypeSpecification) {
290                                 if (a.GetType () != b.GetType ())
291                                         return false;
292
293                                 return TypeMatch ((TypeSpecification) a, (TypeSpecification) b, ref gp);
294                         }
295
296                         return a.FullName == b.FullName;
297                 }
298
299                 static TypeDefinition GetBaseType (TypeDefinition type)
300                 {
301                         if (type == null || type.BaseType == null)
302                                 return null;
303
304                         return type.BaseType.Resolve ();
305                 }
306         }
307 }