2008-10-27 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mcs / class / Mono.Cecil / Mono.Cecil / ReflectionHelper.cs
1 //
2 // ReflectionHelper.cs
3 //
4 // Author:
5 //   Jb Evain (jbevain@gmail.com)
6 //
7 // (C) 2005 Jb Evain
8 // (C) 2006 Evaluant RC S.A.
9 // (C) 2007 Jb Evain
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 namespace Mono.Cecil {
32
33         using System;
34         using System.Collections;
35         using SR = System.Reflection;
36         using System.Text;
37
38         internal sealed class ReflectionHelper {
39
40                 ModuleDefinition m_module;
41
42                 public ReflectionHelper (ModuleDefinition module)
43                 {
44                         m_module = module;
45                 }
46
47                 public AssemblyNameReference ImportAssembly (SR.Assembly asm)
48                 {
49                         AssemblyNameReference asmRef = GetAssemblyNameReference (asm.GetName ());
50                         if (asmRef != null)
51                                 return asmRef;
52
53                         SR.AssemblyName asmName = asm.GetName ();
54                         asmRef = new AssemblyNameReference (
55                                 asmName.Name, asmName.CultureInfo.Name, asmName.Version);
56                         asmRef.PublicKeyToken = asmName.GetPublicKeyToken ();
57                         asmRef.HashAlgorithm = (AssemblyHashAlgorithm) asmName.HashAlgorithm;
58                         asmRef.Culture = asmName.CultureInfo.ToString ();
59                         m_module.AssemblyReferences.Add (asmRef);
60                         return asmRef;
61                 }
62
63                 AssemblyNameReference GetAssemblyNameReference (SR.AssemblyName name)
64                 {
65                         foreach (AssemblyNameReference reference in m_module.AssemblyReferences)
66                                 if (reference.FullName == name.FullName)
67                                         return reference;
68
69                         return null;
70                 }
71
72                 public static string GetTypeSignature (Type t)
73                 {
74                         if (t.HasElementType) {
75                                 if (t.IsPointer)
76                                         return string.Concat (GetTypeSignature (t.GetElementType ()), "*");
77                                 else if (t.IsArray) {
78                                         int rank = t.GetArrayRank ();
79                                         if (rank == 1)
80                                                 return string.Concat (GetTypeSignature (t.GetElementType ()), "[]");
81
82                                         StringBuilder sb = new StringBuilder ();
83                                         sb.Append ('[');
84                                         for (int i = 1; i < rank; i++)
85                                                 sb.Append (',');
86                                         sb.Append (']');
87
88                                         return string.Concat (GetTypeSignature (t.GetElementType ()), sb.ToString ());
89                                 } else if (t.IsByRef)
90                                         return string.Concat(GetTypeSignature(t.GetElementType()), "&");
91                         }
92
93                         if (IsGenericTypeSpec (t)) {
94                                 StringBuilder sb = new StringBuilder ();
95                                 sb.Append (GetTypeSignature (GetGenericTypeDefinition (t)));
96                                 sb.Append ("<");
97                                 Type [] genArgs = GetGenericArguments (t);
98                                 for (int i = 0; i < genArgs.Length; i++) {
99                                         if (i > 0)
100                                                 sb.Append (",");
101                                         sb.Append (GetTypeSignature (genArgs [i]));
102                                 }
103                                 sb.Append (">");
104                                 return sb.ToString ();
105                         }
106
107                         if (IsGenericParameter (t))
108                                 return t.Name;
109
110                         if (t.DeclaringType != null)
111                                 return string.Concat (t.DeclaringType.FullName, "/", t.Name);
112
113                         if (t.Namespace == null || t.Namespace.Length == 0)
114                                 return t.Name;
115
116                         return string.Concat (t.Namespace, ".", t.Name);
117                 }
118
119                 static bool GetProperty (object o, string prop)
120                 {
121                         SR.PropertyInfo pi = o.GetType ().GetProperty (prop);
122                         if (pi == null)
123                                 return false;
124
125                         return (bool) pi.GetValue (o, null);
126                 }
127
128                 public static bool IsGenericType (Type t)
129                 {
130                         return GetProperty (t, "IsGenericType");
131                 }
132
133                 static bool IsGenericParameter (Type t)
134                 {
135                         return GetProperty (t, "IsGenericParameter");
136                 }
137
138                 static bool IsGenericTypeDefinition (Type t)
139                 {
140                         return GetProperty (t, "IsGenericTypeDefinition");
141                 }
142
143                 static bool IsGenericTypeSpec (Type t)
144                 {
145                         return IsGenericType (t) && !IsGenericTypeDefinition (t);
146                 }
147
148                 static Type GetGenericTypeDefinition (Type t)
149                 {
150                         return (Type) t.GetType ().GetMethod ("GetGenericTypeDefinition").Invoke (t, null);
151                 }
152
153                 static Type [] GetGenericArguments (Type t)
154                 {
155                         return (Type []) t.GetType ().GetMethod ("GetGenericArguments").Invoke (t, null);
156                 }
157
158                 GenericInstanceType GetGenericType (Type t, TypeReference element, ImportContext context)
159                 {
160                         GenericInstanceType git = new GenericInstanceType (element);
161                         foreach (Type genArg in GetGenericArguments (t))
162                                 git.GenericArguments.Add (ImportSystemType (genArg, context));
163
164                         return git;
165                 }
166
167                 static bool GenericParameterOfMethod (Type t)
168                 {
169                         return t.GetType ().GetProperty ("DeclaringMethod").GetValue (t, null) != null;
170                 }
171
172                 static GenericParameter GetGenericParameter (Type t, ImportContext context)
173                 {
174                         int pos = (int) t.GetType ().GetProperty ("GenericParameterPosition").GetValue (t, null);
175                         if (GenericParameterOfMethod (t))
176                                 return context.GenericContext.Method.GenericParameters [pos];
177                         else
178                                 return context.GenericContext.Type.GenericParameters [pos];
179                 }
180
181                 TypeReference GetTypeSpec (Type t, ImportContext context)
182                 {
183                         Stack s = new Stack ();
184                         while (t.HasElementType || IsGenericTypeSpec (t)) {
185                                 s.Push (t);
186                                 if (t.HasElementType)
187                                         t = t.GetElementType ();
188                                 else if (IsGenericTypeSpec (t)) {
189                                         t = (Type) t.GetType ().GetMethod ("GetGenericTypeDefinition").Invoke (t, null);
190                                         break;
191                                 }
192                         }
193
194                         TypeReference elementType = ImportSystemType (t, context);
195                         while (s.Count > 0) {
196                                 t = (Type) s.Pop ();
197                                 if (t.IsPointer)
198                                         elementType = new PointerType (elementType);
199                                 else if (t.IsArray)
200                                         elementType = new ArrayType (elementType, t.GetArrayRank ());
201                                 else if (t.IsByRef)
202                                         elementType = new ReferenceType (elementType);
203                                 else if (IsGenericTypeSpec (t))
204                                         elementType = GetGenericType (t, elementType, context);
205                                 else
206                                         throw new ReflectionException ("Unknown element type");
207                         }
208
209                         return elementType;
210                 }
211
212                 public TypeReference ImportSystemType (Type t, ImportContext context)
213                 {
214                         if (t.HasElementType || IsGenericTypeSpec (t))
215                                 return GetTypeSpec (t, context);
216
217                         if (IsGenericParameter (t))
218                                 return GetGenericParameter (t, context);
219
220                         TypeReference type = m_module.TypeReferences [GetTypeSignature (t)];
221                         if (type != null) {
222                                 if (t.IsValueType && !type.IsValueType)
223                                         type.IsValueType = true;
224
225                                 return type;
226                         }
227
228                         AssemblyNameReference asm = ImportAssembly (t.Assembly);
229                         if (t.DeclaringType != null) {
230                                 type = new TypeReference (t.Name, string.Empty, asm, t.IsValueType);
231                                 type.DeclaringType = ImportSystemType (t.DeclaringType, context);
232                         } else
233                                 type = new TypeReference (t.Name, t.Namespace, asm, t.IsValueType);
234
235                         if (IsGenericTypeDefinition (t))
236                                 foreach (Type genParam in GetGenericArguments (t))
237                                         type.GenericParameters.Add (new GenericParameter (genParam.Name, type));
238
239                         context.GenericContext.Type = type;
240
241                         m_module.TypeReferences.Add (type);
242                         return type;
243                 }
244
245                 static string GetMethodBaseSignature (SR.MethodBase meth, Type declaringType, Type retType)
246                 {
247                         StringBuilder sb = new StringBuilder ();
248                         sb.Append (GetTypeSignature (retType));
249                         sb.Append (' ');
250                         sb.Append (GetTypeSignature (declaringType));
251                         sb.Append ("::");
252                         sb.Append (meth.Name);
253                         if (IsGenericMethodSpec (meth)) {
254                                 sb.Append ("<");
255                                 Type [] genArgs = GetGenericArguments (meth as SR.MethodInfo);
256                                 for (int i = 0; i < genArgs.Length; i++) {
257                                         if (i > 0)
258                                                 sb.Append (",");
259                                         sb.Append (GetTypeSignature (genArgs [i]));
260                                 }
261                                 sb.Append (">");
262                         }
263                         sb.Append ("(");
264                         SR.ParameterInfo [] parameters = meth.GetParameters ();
265                         for (int i = 0; i < parameters.Length; i++) {
266                                 if (i > 0)
267                                         sb.Append (",");
268                                 sb.Append (GetTypeSignature (parameters [i].ParameterType));
269                         }
270                         sb.Append (")");
271                         return sb.ToString ();
272                 }
273
274                 static bool IsGenericMethod (SR.MethodBase mb)
275                 {
276                         return GetProperty (mb, "IsGenericMethod");
277                 }
278
279                 static bool IsGenericMethodDefinition (SR.MethodBase mb)
280                 {
281                         return GetProperty (mb, "IsGenericMethodDefinition");
282                 }
283
284                 static bool IsGenericMethodSpec (SR.MethodBase mb)
285                 {
286                         return IsGenericMethod (mb) && !IsGenericMethodDefinition (mb);
287                 }
288
289                 static Type [] GetGenericArguments (SR.MethodInfo mi)
290                 {
291                         return (Type []) mi.GetType ().GetMethod ("GetGenericArguments").Invoke (mi, null);
292                 }
293
294                 static int GetMetadataToken (SR.MethodInfo mi)
295                 {
296                         return (int) mi.GetType ().GetProperty ("MetadataToken").GetValue (mi, null);
297                 }
298
299                 MethodReference ImportGenericInstanceMethod (SR.MethodInfo mi, ImportContext context)
300                 {
301                         SR.MethodInfo gmd = (SR.MethodInfo) mi.GetType ().GetMethod ("GetGenericMethodDefinition").Invoke (mi, null);
302                         GenericInstanceMethod gim = new GenericInstanceMethod (
303                                 ImportMethodBase (gmd, gmd.ReturnType, context));
304
305                         foreach (Type genArg in GetGenericArguments (mi))
306                                 gim.GenericArguments.Add (ImportSystemType (genArg, context));
307
308                         return gim;
309                 }
310
311                 MethodReference ImportMethodBase (SR.MethodBase mb, Type retType, ImportContext context)
312                 {
313                         if (IsGenericMethod (mb) && !IsGenericMethodDefinition (mb))
314                                 return ImportGenericInstanceMethod ((SR.MethodInfo) mb, context);
315
316                         Type originalDecType = mb.DeclaringType;
317                         Type declaringTypeDef = originalDecType;
318                         while (IsGenericTypeSpec (declaringTypeDef))
319                                 declaringTypeDef = GetGenericTypeDefinition (declaringTypeDef);
320
321                         if (mb.DeclaringType != declaringTypeDef && mb is SR.MethodInfo) {
322                                 int mt = GetMetadataToken (mb as SR.MethodInfo);
323                                 // hack to get the generic method definition from the constructed method
324                                 foreach (SR.MethodInfo mi in declaringTypeDef.GetMethods ()) {
325                                         if (GetMetadataToken (mi) == mt) {
326                                                 mb = mi;
327                                                 retType = mi.ReturnType;
328                                                 break;
329                                         }
330                                 }
331                         }
332
333                         string sig = GetMethodBaseSignature (mb, originalDecType, retType);
334                         MethodReference meth = (MethodReference) GetMemberReference (sig);
335                         if (meth != null)
336                                 return meth;
337
338                         meth = new MethodReference (
339                                 mb.Name,
340                                 (mb.CallingConvention & SR.CallingConventions.HasThis) > 0,
341                                 (mb.CallingConvention & SR.CallingConventions.ExplicitThis) > 0,
342                                 MethodCallingConvention.Default); // TODO: get the real callconv
343                         meth.DeclaringType = ImportSystemType (originalDecType, context);
344
345                         if (IsGenericMethod (mb))
346                                 foreach (Type genParam in GetGenericArguments (mb as SR.MethodInfo))
347                                         meth.GenericParameters.Add (new GenericParameter (genParam.Name, meth));
348
349                         TypeReference contextType = context.GenericContext.Type;
350                         MethodReference contextMethod = context.GenericContext.Method;
351
352                         context.GenericContext.Method = meth;
353                         context.GenericContext.Type = ImportSystemType (declaringTypeDef, context);
354
355                         meth.ReturnType.ReturnType = ImportSystemType (retType, context);
356
357                         SR.ParameterInfo [] parameters = mb.GetParameters ();
358                         for (int i = 0; i < parameters.Length; i++)
359                                 meth.Parameters.Add (new ParameterDefinition (
360                                         ImportSystemType (parameters [i].ParameterType, context)));
361
362                         context.GenericContext.Type = contextType;
363                         context.GenericContext.Method = contextMethod;
364
365                         m_module.MemberReferences.Add (meth);
366                         return meth;
367                 }
368
369                 public MethodReference ImportConstructorInfo (SR.ConstructorInfo ci, ImportContext context)
370                 {
371                         return ImportMethodBase (ci, typeof (void), context);
372                 }
373
374                 public MethodReference ImportMethodInfo (SR.MethodInfo mi, ImportContext context)
375                 {
376                         return ImportMethodBase (mi, mi.ReturnType, context);
377                 }
378
379                 static string GetFieldSignature (SR.FieldInfo field)
380                 {
381                         StringBuilder sb = new StringBuilder ();
382                         sb.Append (GetTypeSignature (field.FieldType));
383                         sb.Append (' ');
384                         sb.Append (GetTypeSignature (field.DeclaringType));
385                         sb.Append ("::");
386                         sb.Append (field.Name);
387                         return sb.ToString ();
388                 }
389
390                 public FieldReference ImportFieldInfo (SR.FieldInfo fi, ImportContext context)
391                 {
392                         string sig = GetFieldSignature (fi);
393                         FieldReference f = (FieldReference) GetMemberReference (sig);
394                         if (f != null)
395                                 return f;
396
397                         f = new FieldReference (
398                                 fi.Name,
399                                 ImportSystemType (fi.DeclaringType, context),
400                                 ImportSystemType (fi.FieldType, context));
401
402                         m_module.MemberReferences.Add (f);
403                         return f;
404                 }
405
406                 MemberReference GetMemberReference (string signature)
407                 {
408                         foreach (MemberReference reference in m_module.MemberReferences)
409                                 if (reference.ToString () == signature)
410                                         return reference;
411
412                         return null;
413                 }
414         }
415 }