Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
CecilExtensions.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under MIT License. See LICENSE.md for details.
3 // Source: http://stackoverflow.com/questions/4968755/mono-cecil-call-generic-base-class-method-from-other-assembly
4 using System;
5 using System.Collections.Generic;
6 using System.IO;
7 using System.Linq;
8 using System.Reflection;
9 using System.Text;
10 using Mono.Cecil;
11 using SiliconStudio.Core.Serialization;
12 using SiliconStudio.Core.Storage;
13 
14 namespace SiliconStudio.AssemblyProcessor
15 {
16  public static class CecilExtensions
17  {
18  // Not sure why Cecil made ContainsGenericParameter internal, but let's work around it by reflection.
19  private static readonly MethodInfo containsGenericParameterGetMethod = typeof(MemberReference).GetProperty("ContainsGenericParameter", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).GetMethod;
20 
21  public static IEnumerable<TypeDefinition> EnumerateTypes(this AssemblyDefinition assembly)
22  {
23  foreach (var type in assembly.MainModule.Types)
24  {
25  yield return type;
26  foreach (var nestedType in type.NestedTypes)
27  {
28  yield return nestedType;
29  }
30  }
31  }
32 
33  public static TypeReference MakeGenericType(this TypeReference self, params TypeReference[] arguments)
34  {
35  if (self.GenericParameters.Count != arguments.Length)
36  throw new ArgumentException();
37 
38  var instance = new GenericInstanceType(self);
39  foreach (var argument in arguments)
40  instance.GenericArguments.Add(argument);
41 
42  return instance;
43  }
44 
45  public static FieldReference MakeGeneric(this FieldReference self, params TypeReference[] arguments)
46  {
47  return new FieldReference(self.Name, self.FieldType, self.DeclaringType.MakeGenericType(arguments));
48  }
49 
50  public static MethodReference MakeGeneric(this MethodReference self, params TypeReference[] arguments)
51  {
52  var reference = new MethodReference(self.Name, self.ReturnType, self.DeclaringType.MakeGenericType(arguments))
53  {
54  HasThis = self.HasThis,
55  ExplicitThis = self.ExplicitThis,
56  CallingConvention = self.CallingConvention,
57  };
58 
59  foreach (var parameter in self.Parameters)
60  reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType));
61 
62  foreach (var generic_parameter in self.GenericParameters)
63  reference.GenericParameters.Add(new GenericParameter(generic_parameter.Name, reference));
64 
65  return reference;
66  }
67 
68  public static MethodReference MakeGenericMethod(this MethodReference self, params TypeReference[] arguments)
69  {
70  var method = new GenericInstanceMethod(self);
71  foreach(var argument in arguments)
72  method.GenericArguments.Add(argument);
73  return method;
74  }
75 
76  public static TypeDefinition GetTypeResolved(this ModuleDefinition moduleDefinition, string typeName)
77  {
78  foreach (var exportedType in moduleDefinition.ExportedTypes)
79  {
80  if (exportedType.FullName == typeName)
81  {
82  var typeDefinition = exportedType.Resolve();
83  return typeDefinition;
84  }
85  }
86 
87  return moduleDefinition.GetType(typeName);
88  }
89 
90  public static TypeDefinition GetTypeResolved(this ModuleDefinition moduleDefinition, string @namespace, string typeName)
91  {
92  foreach (var exportedType in moduleDefinition.ExportedTypes)
93  {
94  if (exportedType.Namespace == @namespace && exportedType.Name == typeName)
95  {
96  var typeDefinition = exportedType.Resolve();
97  return typeDefinition;
98  }
99  }
100 
101  return moduleDefinition.GetType(@namespace, typeName);
102  }
103 
104  /// <summary>
105  /// Finds the corlib assembly.
106  /// </summary>
107  /// <param name="assembly">The assembly.</param>
108  /// <returns></returns>
109  /// <exception cref="System.InvalidOperationException">Missing mscorlib.dll from assembly</exception>
110  public static AssemblyDefinition FindCorlibAssembly(AssemblyDefinition assembly)
111  {
112  AssemblyDefinition mscorlibAssembly = null;
113 
114  foreach (var assemblyNameReference in assembly.MainModule.AssemblyReferences)
115  {
116  if (assemblyNameReference.Name.ToLower() == "mscorlib")
117  {
118  mscorlibAssembly = assembly.MainModule.AssemblyResolver.Resolve(assemblyNameReference);
119  break;
120  }
121  }
122  return mscorlibAssembly;
123  }
124 
125  /// <summary>
126  /// Get Program Files x86
127  /// </summary>
128  /// <returns></returns>
129  public static string ProgramFilesx86()
130  {
131  if (8 == IntPtr.Size
132  || (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("PROCESSOR_ARCHITEW6432"))))
133  {
134  return Environment.GetEnvironmentVariable("ProgramFiles(x86)");
135  }
136 
137  return Environment.GetEnvironmentVariable("ProgramFiles");
138  }
139 
140  public static GenericInstanceType ChangeGenericInstanceType(this GenericInstanceType type, TypeReference elementType, IEnumerable<TypeReference> genericArguments)
141  {
142  if (elementType != type.ElementType || genericArguments != type.GenericArguments)
143  {
144  var result = new GenericInstanceType(elementType);
145  foreach (var genericArgument in genericArguments)
146  result.GenericArguments.Add(genericArgument);
147  if (type.HasGenericParameters)
148  SetGenericParameters(result, type.GenericParameters);
149  return result;
150  }
151  return type;
152  }
153 
154  public static ArrayType ChangeArrayType(this ArrayType type, TypeReference elementType, int rank)
155  {
156  if (elementType != type.ElementType || rank != type.Rank)
157  {
158  var result = new ArrayType(elementType, rank);
159  if (type.HasGenericParameters)
160  SetGenericParameters(result, type.GenericParameters);
161  return result;
162  }
163  return type;
164  }
165 
166  public static TypeReference ChangeGenericParameters(this TypeReference type, IEnumerable<GenericParameter> genericParameters)
167  {
168  if (type.GenericParameters == genericParameters)
169  return type;
170 
171  TypeReference result;
172  var arrayType = type as ArrayType;
173  if (arrayType != null)
174  {
175  result = new ArrayType(arrayType.ElementType, arrayType.Rank);
176  }
177  else
178  {
179  var genericInstanceType = type as GenericInstanceType;
180  if (genericInstanceType != null)
181  {
182  result = new GenericInstanceType(genericInstanceType.ElementType);
183  }
184  else if (type.GetType() == typeof(TypeReference).GetType())
185  {
186  result = new TypeReference(type.Namespace, type.Name, type.Module, type.Scope, type.IsValueType);
187  }
188  else
189  {
190  throw new NotSupportedException();
191  }
192  }
193 
194  SetGenericParameters(result, genericParameters);
195 
196  return result;
197  }
198 
199  private static void SetGenericParameters(TypeReference result, IEnumerable<GenericParameter> genericParameters)
200  {
201  foreach (var genericParameter in genericParameters)
202  result.GenericParameters.Add(genericParameter);
203  }
204 
205  public static string GenerateGenerics(this TypeReference type, bool empty = false)
206  {
207  var genericInstanceType = type as GenericInstanceType;
208  if (!type.HasGenericParameters && genericInstanceType == null)
209  return string.Empty;
210 
211  var result = new StringBuilder();
212 
213  // Try to process generic instantiations
214  if (genericInstanceType != null)
215  {
216  result.Append("<");
217 
218  bool first = true;
219  foreach (var genericArgument in genericInstanceType.GenericArguments)
220  {
221  if (!first)
222  result.Append(",");
223  first = false;
224  if (!empty)
225  result.Append(ConvertCSharp(genericArgument, empty));
226  }
227 
228  result.Append(">");
229 
230  return result.ToString();
231  }
232 
233  if (type.HasGenericParameters)
234  {
235  result.Append("<");
236 
237  bool first = true;
238  foreach (var genericParameter in type.GenericParameters)
239  {
240  if (!first)
241  result.Append(",");
242  first = false;
243  if (!empty)
244  result.Append(ConvertCSharp(genericParameter, empty));
245  }
246 
247  result.Append(">");
248 
249  return result.ToString();
250  }
251 
252  return result.ToString();
253  }
254 
255  public unsafe static string ConvertTypeId(this TypeReference type)
256  {
257  var typeName = type.ConvertCSharp(false);
258  var typeId = ObjectId.FromBytes(Encoding.UTF8.GetBytes(typeName));
259 
260  var typeIdHash = (uint*)&typeId;
261  return string.Format("new {0}(0x{1:x8}, 0x{2:x8}, 0x{3:x8}, 0x{4:x8})", typeof(ObjectId).FullName, typeIdHash[0], typeIdHash[1], typeIdHash[2], typeIdHash[3]);
262  }
263 
264  /// <summary>
265  /// Generates type name valid to use from C# source file.
266  /// </summary>
267  /// <param name="type"></param>
268  /// <param name="empty"></param>
269  /// <returns></returns>
270  public static string ConvertCSharp(this TypeReference type, bool empty = false)
271  {
272  // Try to process arrays
273  var arrayType = type as ArrayType;
274  if (arrayType != null)
275  {
276  return ConvertCSharp(arrayType.ElementType, empty) + "[]";
277  }
278 
279  // Remove the `X at end of generic definition.
280  var typeName = type.GetElementType().FullName;
281  var genericSeparatorIndex = typeName.LastIndexOf('`');
282  if (genericSeparatorIndex != -1)
283  typeName = typeName.Substring(0, genericSeparatorIndex);
284 
285  // Replace / into . (nested types)
286  typeName = typeName.Replace('/', '.');
287 
288  // Try to process generic instantiations
289  var genericInstanceType = type as GenericInstanceType;
290  if (genericInstanceType != null)
291  {
292  var result = new StringBuilder();
293 
294  // Use ElementType so that we have only the name without the <> part.
295  result.Append(typeName);
296  result.Append("<");
297 
298  bool first = true;
299  foreach (var genericArgument in genericInstanceType.GenericArguments)
300  {
301  if (!first)
302  result.Append(",");
303  first = false;
304  if (!empty)
305  result.Append(ConvertCSharp(genericArgument, empty));
306  }
307 
308  result.Append(">");
309 
310  return result.ToString();
311  }
312 
313  if (type.HasGenericParameters)
314  {
315  var result = new StringBuilder();
316 
317  // Use ElementType so that we have only the name without the <> part.
318  result.Append(typeName);
319  result.Append("<");
320 
321  bool first = true;
322  foreach (var genericParameter in type.GenericParameters)
323  {
324  if (!first)
325  result.Append(",");
326  first = false;
327  if (!empty)
328  result.Append(ConvertCSharp(genericParameter, empty));
329  }
330 
331  result.Append(">");
332 
333  return result.ToString();
334  }
335 
336  return typeName;
337  }
338 
339  /// <summary>
340  /// Generates the Mono.Cecil TypeReference from its .NET <see cref="Type"/> counterpart.
341  /// </summary>
342  /// <param name="type">The type.</param>
343  /// <param name="assemblyResolver">The assembly resolver.</param>
344  /// <returns></returns>
345  public static TypeReference GenerateTypeCecil(this Type type, BaseAssemblyResolver assemblyResolver)
346  {
347  var assemblyDefinition = assemblyResolver.Resolve(type.Assembly.FullName);
348  TypeReference typeReference;
349 
350  if (type.IsNested)
351  {
352  var declaringType = GenerateTypeCecil(type.DeclaringType, assemblyResolver);
353  typeReference = declaringType.Resolve().NestedTypes.FirstOrDefault(x => x.Name == type.Name);
354  }
355  else if (type.IsArray)
356  {
357  var elementType = GenerateTypeCecil(type.GetElementType(), assemblyResolver);
358  typeReference = new ArrayType(elementType, type.GetArrayRank());
359  }
360  else
361  {
362  typeReference = assemblyDefinition.MainModule.GetTypeResolved(type.IsGenericType ? type.GetGenericTypeDefinition().FullName : type.FullName);
363  }
364 
365  if (typeReference == null)
366  throw new InvalidOperationException("Could not resolve cecil type.");
367 
368  if (type.IsGenericType)
369  {
370  var genericInstanceType = new GenericInstanceType(typeReference);
371  foreach (var argType in type.GetGenericArguments())
372  {
373  TypeReference argTypeReference;
374  if (argType.IsGenericParameter)
375  {
376  argTypeReference = new GenericParameter(argType.Name, typeReference);
377  }
378  else
379  {
380  argTypeReference = GenerateTypeCecil(argType, assemblyResolver);
381  }
382  genericInstanceType.GenericArguments.Add(argTypeReference);
383  }
384 
385  typeReference = genericInstanceType;
386  }
387 
388  return typeReference;
389  }
390 
391  public static bool ContainsGenericParameter(this MemberReference memberReference)
392  {
393  return (bool)containsGenericParameterGetMethod.Invoke(memberReference, null);
394  }
395 
396  /// <summary>
397  /// Generates type name similar to Type.AssemblyQualifiedName.
398  /// </summary>
399  /// <param name="type"></param>
400  /// <returns></returns>
401  public static string ConvertAssemblyQualifiedName(this TypeReference type)
402  {
403  var result = new StringBuilder(256);
404  ConvertAssemblyQualifiedName(type, result);
405  return result.ToString();
406  }
407 
408  private static void ConvertAssemblyQualifiedName(this TypeReference type, StringBuilder result)
409  {
410  int start, end;
411 
412  var arrayType = type as ArrayType;
413  if (arrayType != null)
414  {
415  // If it's an array, process element type, and add [] after
416  type = arrayType.ElementType;
417  }
418 
419  // Add FUllName from GetElementType() (remove generics etc...)
420  start = result.Length;
421  result.Append(type.GetElementType().FullName);
422  end = result.Length;
423 
424  // Replace / into + (nested types)
425  result = result.Replace('/', '+', start, end);
426 
427  // Try to process generic instantiations
428  var genericInstanceType = type as GenericInstanceType;
429  if (genericInstanceType != null)
430  {
431  // Ideally we would like to have access to Mono.Cecil TypeReference.ContainsGenericParameter, but it's internal.
432  // This doesn't cover every case but hopefully this should be enough for serialization
433  bool containsGenericParameter = false;
434  foreach (var genericArgument in genericInstanceType.GenericArguments)
435  {
436  if (genericArgument.IsGenericParameter)
437  containsGenericParameter = true;
438  }
439 
440  if (!containsGenericParameter)
441  {
442  // Use ElementType so that we have only the name without the <> part.
443  result.Append('[');
444 
445  bool first = true;
446  foreach (var genericArgument in genericInstanceType.GenericArguments)
447  {
448  if (!first)
449  result.Append(",");
450  result.Append('[');
451  first = false;
452  result.Append(ConvertAssemblyQualifiedName(genericArgument));
453  result.Append(']');
454  }
455 
456  result.Append(']');
457  }
458  }
459 
460  // Try to process arrays
461  if (arrayType != null)
462  {
463  result.Append('[');
464  if (arrayType.Rank > 1)
465  result.Append(',', arrayType.Rank - 1);
466  result.Append(']');
467  }
468 
469  result.Append(", ");
470  start = result.Length;
471  result.Append(type.Module.Assembly.FullName);
472  end = result.Length;
473 
474 #if SILICONSTUDIO_PLATFORM_MONO_MOBILE
475  // Xamarin iOS and Android remap some assemblies
476  const string oldTypeEnding = "2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e";
477  const string newTypeEnding = "4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
478  result = result.Replace(oldTypeEnding, newTypeEnding, start, end);
479 #endif
480  }
481  }
482 }
static string ConvertCSharp(this TypeReference type, bool empty=false)
Generates type name valid to use from C# source file.
static MethodReference MakeGenericMethod(this MethodReference self, params TypeReference[] arguments)
static AssemblyDefinition FindCorlibAssembly(AssemblyDefinition assembly)
Finds the corlib assembly.
static IEnumerable< TypeDefinition > EnumerateTypes(this AssemblyDefinition assembly)
static TypeDefinition GetTypeResolved(this ModuleDefinition moduleDefinition, string typeName)
static TypeReference GenerateTypeCecil(this Type type, BaseAssemblyResolver assemblyResolver)
Generates the Mono.Cecil TypeReference from its .NET Type counterpart.
static GenericInstanceType ChangeGenericInstanceType(this GenericInstanceType type, TypeReference elementType, IEnumerable< TypeReference > genericArguments)
static string ProgramFilesx86()
Get Program Files x86
static MethodReference MakeGeneric(this MethodReference self, params TypeReference[] arguments)
static TypeDefinition GetTypeResolved(this ModuleDefinition moduleDefinition, string @namespace, string typeName)
static string ConvertAssemblyQualifiedName(this TypeReference type)
Generates type name similar to Type.AssemblyQualifiedName.
static TypeReference MakeGenericType(this TypeReference self, params TypeReference[] arguments)
static FieldReference MakeGeneric(this FieldReference self, params TypeReference[] arguments)
static unsafe string ConvertTypeId(this TypeReference type)
A hash to uniquely identify data.
Definition: ObjectId.cs:13
static ArrayType ChangeArrayType(this ArrayType type, TypeReference elementType, int rank)
static bool ContainsGenericParameter(this MemberReference memberReference)
static TypeReference ChangeGenericParameters(this TypeReference type, IEnumerable< GenericParameter > genericParameters)
static string GenerateGenerics(this TypeReference type, bool empty=false)