/Test/Mono.Cecil.Tests/ImportCecilTests.cs

http://github.com/jbevain/cecil · C# · 368 lines · 289 code · 75 blank · 4 comment · 6 complexity · c1be14c78177c4ed1256d6f6358d1f0b MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using SR = System.Reflection;
  6. using System.Runtime.CompilerServices;
  7. using Mono.Cecil.Cil;
  8. using NUnit.Framework;
  9. namespace Mono.Cecil.Tests {
  10. [TestFixture]
  11. public class ImportCecilTests : BaseTestFixture {
  12. [Test]
  13. public void ImportStringByRef ()
  14. {
  15. var get_string = Compile<Func<string, string>> ((module, body) => {
  16. var type = module.Types [1];
  17. var method_by_ref = new MethodDefinition {
  18. Name = "ModifyString",
  19. IsPrivate = true,
  20. IsStatic = true,
  21. };
  22. type.Methods.Add (method_by_ref);
  23. method_by_ref.MethodReturnType.ReturnType = module.ImportReference (typeof (void).ToDefinition ());
  24. method_by_ref.Parameters.Add (new ParameterDefinition (module.ImportReference (typeof (string).ToDefinition ())));
  25. method_by_ref.Parameters.Add (new ParameterDefinition (module.ImportReference (new ByReferenceType (typeof (string).ToDefinition ()))));
  26. var m_il = method_by_ref.Body.GetILProcessor ();
  27. m_il.Emit (OpCodes.Ldarg_1);
  28. m_il.Emit (OpCodes.Ldarg_0);
  29. m_il.Emit (OpCodes.Stind_Ref);
  30. m_il.Emit (OpCodes.Ret);
  31. var v_0 = new VariableDefinition (module.ImportReference (typeof (string).ToDefinition ()));
  32. body.Variables.Add (v_0);
  33. var il = body.GetILProcessor ();
  34. il.Emit (OpCodes.Ldnull);
  35. il.Emit (OpCodes.Stloc, v_0);
  36. il.Emit (OpCodes.Ldarg_0);
  37. il.Emit (OpCodes.Ldloca, v_0);
  38. il.Emit (OpCodes.Call, method_by_ref);
  39. il.Emit (OpCodes.Ldloc_0);
  40. il.Emit (OpCodes.Ret);
  41. });
  42. Assert.AreEqual ("foo", get_string ("foo"));
  43. }
  44. [Test]
  45. public void ImportStringArray ()
  46. {
  47. var identity = Compile<Func<string [,], string [,]>> ((module, body) => {
  48. var il = body.GetILProcessor ();
  49. il.Emit (OpCodes.Ldarg_0);
  50. il.Emit (OpCodes.Ret);
  51. });
  52. var array = new string [2, 2];
  53. Assert.AreEqual (array, identity (array));
  54. }
  55. [Test]
  56. public void ImportFieldStringEmpty ()
  57. {
  58. var get_empty = Compile<Func<string>> ((module, body) => {
  59. var il = body.GetILProcessor ();
  60. il.Emit (OpCodes.Ldsfld, module.ImportReference (typeof (string).GetField ("Empty").ToDefinition ()));
  61. il.Emit (OpCodes.Ret);
  62. });
  63. Assert.AreEqual ("", get_empty ());
  64. }
  65. [Test]
  66. public void ImportStringConcat ()
  67. {
  68. var concat = Compile<Func<string, string, string>> ((module, body) => {
  69. var il = body.GetILProcessor ();
  70. il.Emit (OpCodes.Ldarg_0);
  71. il.Emit (OpCodes.Ldarg_1);
  72. il.Emit (OpCodes.Call, module.ImportReference (typeof (string).GetMethod ("Concat", new [] { typeof (string), typeof (string) }).ToDefinition ()));
  73. il.Emit (OpCodes.Ret);
  74. });
  75. Assert.AreEqual ("FooBar", concat ("Foo", "Bar"));
  76. }
  77. public class Generic<T> {
  78. public T Field;
  79. public T Method (T t)
  80. {
  81. return t;
  82. }
  83. public TS GenericMethod<TS> (T t, TS s)
  84. {
  85. return s;
  86. }
  87. public Generic<TS> ComplexGenericMethod<TS> (T t, TS s)
  88. {
  89. return new Generic<TS> { Field = s };
  90. }
  91. }
  92. [Test]
  93. public void ImportGenericField ()
  94. {
  95. var get_field = Compile<Func<Generic<string>, string>> ((module, body) => {
  96. var generic_def = module.ImportReference (typeof (Generic<>)).Resolve ();
  97. var field_def = generic_def.Fields.Where (f => f.Name == "Field").First ();
  98. var field_string = field_def.MakeGeneric (module.ImportReference (typeof (string)));
  99. var field_ref = module.ImportReference (field_string);
  100. var il = body.GetILProcessor ();
  101. il.Emit (OpCodes.Ldarg_0);
  102. il.Emit (OpCodes.Ldfld, field_ref);
  103. il.Emit (OpCodes.Ret);
  104. });
  105. var generic = new Generic<string> {
  106. Field = "foo",
  107. };
  108. Assert.AreEqual ("foo", get_field (generic));
  109. }
  110. [Test]
  111. public void ImportGenericMethod ()
  112. {
  113. var generic_identity = Compile<Func<Generic<int>, int, int>> ((module, body) => {
  114. var generic_def = module.ImportReference (typeof (Generic<>)).Resolve ();
  115. var method_def = generic_def.Methods.Where (m => m.Name == "Method").First ();
  116. var method_int = method_def.MakeGeneric (module.ImportReference (typeof (int)));
  117. var method_ref = module.ImportReference (method_int);
  118. var il = body.GetILProcessor ();
  119. il.Emit (OpCodes.Ldarg_0);
  120. il.Emit (OpCodes.Ldarg_1);
  121. il.Emit (OpCodes.Callvirt, method_ref);
  122. il.Emit (OpCodes.Ret);
  123. });
  124. Assert.AreEqual (42, generic_identity (new Generic<int> (), 42));
  125. }
  126. [Test]
  127. public void ImportGenericMethodSpec ()
  128. {
  129. var gen_spec_id = Compile<Func<Generic<string>, int, int>> ((module, body) => {
  130. var generic_def = module.ImportReference (typeof (Generic<>)).Resolve ();
  131. var method_def = generic_def.Methods.Where (m => m.Name == "GenericMethod").First ();
  132. var method_string = method_def.MakeGeneric (module.ImportReference (typeof (string)));
  133. var method_instance = method_string.MakeGenericMethod (module.ImportReference (typeof (int)));
  134. var method_ref = module.ImportReference (method_instance);
  135. var il = body.GetILProcessor ();
  136. il.Emit (OpCodes.Ldarg_0);
  137. il.Emit (OpCodes.Ldnull);
  138. il.Emit (OpCodes.Ldarg_1);
  139. il.Emit (OpCodes.Callvirt, method_ref);
  140. il.Emit (OpCodes.Ret);
  141. });
  142. Assert.AreEqual (42, gen_spec_id (new Generic<string> (), 42));
  143. }
  144. [Test]
  145. public void ImportComplexGenericMethodSpec ()
  146. {
  147. var gen_spec_id = Compile<Func<Generic<string>, int, int>> ((module, body) => {
  148. var generic_def = module.ImportReference (typeof (Generic<>)).Resolve ();
  149. var method_def = generic_def.Methods.Where (m => m.Name == "ComplexGenericMethod").First ();
  150. var method_string = method_def.MakeGeneric (module.ImportReference (typeof (string)));
  151. var method_instance = method_string.MakeGenericMethod (module.ImportReference (typeof (int)));
  152. var method_ref = module.ImportReference (method_instance);
  153. var field_def = generic_def.Fields.Where (f => f.Name == "Field").First ();
  154. var field_int = field_def.MakeGeneric (module.ImportReference (typeof (int)));
  155. var field_ref = module.ImportReference (field_int);
  156. var il = body.GetILProcessor ();
  157. il.Emit (OpCodes.Ldarg_0);
  158. il.Emit (OpCodes.Ldnull);
  159. il.Emit (OpCodes.Ldarg_1);
  160. il.Emit (OpCodes.Callvirt, method_ref);
  161. il.Emit (OpCodes.Ldfld, field_ref);
  162. il.Emit (OpCodes.Ret);
  163. });
  164. Assert.AreEqual (42, gen_spec_id (new Generic<string> (), 42));
  165. }
  166. [Test]
  167. public void ImportMethodOnOpenGeneric ()
  168. {
  169. var generic = typeof (Generic<>).ToDefinition ();
  170. using (var module = ModuleDefinition.CreateModule ("foo", ModuleKind.Dll)) {
  171. var method = module.ImportReference (generic.GetMethod ("Method"));
  172. Assert.AreEqual ("T Mono.Cecil.Tests.ImportCecilTests/Generic`1::Method(T)", method.FullName);
  173. }
  174. }
  175. public class ContextGeneric1Method2<G1>
  176. {
  177. public G1 GenericMethod<R1, S1> (R1 r, S1 s)
  178. {
  179. return default (G1);
  180. }
  181. }
  182. public class ContextGeneric2Method1<G2, H2>
  183. {
  184. public R2 GenericMethod<R2> (G2 g, H2 h)
  185. {
  186. return default (R2);
  187. }
  188. }
  189. public class NestedGenericsA<A>
  190. {
  191. public class NestedGenericsB<B>
  192. {
  193. public class NestedGenericsC<C>
  194. {
  195. public A GenericMethod (B b, C c)
  196. {
  197. return default (A);
  198. }
  199. }
  200. }
  201. }
  202. [Test]
  203. public void ContextGenericTest ()
  204. {
  205. if (Platform.OnCoreClr)
  206. return;
  207. var module = ModuleDefinition.ReadModule (typeof (ContextGeneric1Method2<>).Module.FullyQualifiedName);
  208. // by mixing open generics with 2 & 1 parameters, we make sure the right context is used (because otherwise, an exception will be thrown)
  209. var type = typeof (ContextGeneric1Method2<>).MakeGenericType (typeof (ContextGeneric2Method1<,>));
  210. var meth = type.GetMethod ("GenericMethod");
  211. var imported_type = module.ImportReference (type);
  212. var method = module.ImportReference (meth, imported_type);
  213. Assert.AreEqual ("G1 Mono.Cecil.Tests.ImportCecilTests/ContextGeneric1Method2`1<Mono.Cecil.Tests.ImportCecilTests/ContextGeneric2Method1`2<G2,H2>>::GenericMethod<R1,S1>(R1,S1)", method.FullName);
  214. // and the other way around
  215. type = typeof (ContextGeneric2Method1<,>).MakeGenericType (typeof (ContextGeneric1Method2<>), typeof (IList<>));
  216. meth = type.GetMethod ("GenericMethod");
  217. imported_type = module.ImportReference (type);
  218. method = module.ImportReference (meth, imported_type);
  219. Assert.AreEqual ("R2 Mono.Cecil.Tests.ImportCecilTests/ContextGeneric2Method1`2<Mono.Cecil.Tests.ImportCecilTests/ContextGeneric1Method2`1<G1>,System.Collections.Generic.IList`1<T>>::GenericMethod<R2>(G2,H2)", method.FullName);
  220. // not sure about this one
  221. type = typeof (NestedGenericsA<string>.NestedGenericsB<int>.NestedGenericsC<float>);
  222. meth = type.GetMethod ("GenericMethod");
  223. imported_type = module.ImportReference (type);
  224. method = module.ImportReference (meth, imported_type);
  225. Assert.AreEqual ("A Mono.Cecil.Tests.ImportCecilTests/NestedGenericsA`1/NestedGenericsB`1/NestedGenericsC`1<System.String,System.Int32,System.Single>::GenericMethod(B,C)", method.FullName);
  226. // We need both the method & type !
  227. type = typeof (Generic<>).MakeGenericType (typeof (string));
  228. meth = type.GetMethod ("ComplexGenericMethod");
  229. imported_type = module.ImportReference (type);
  230. method = module.ImportReference (meth, imported_type);
  231. Assert.AreEqual ("Mono.Cecil.Tests.ImportCecilTests/Generic`1<TS> Mono.Cecil.Tests.ImportCecilTests/Generic`1<System.String>::ComplexGenericMethod<TS>(T,TS)", method.FullName);
  232. }
  233. delegate void Emitter (ModuleDefinition module, MethodBody body);
  234. static TDelegate Compile<TDelegate> (Emitter emitter, [CallerMemberName] string testMethodName = null)
  235. where TDelegate : class
  236. {
  237. var name = "ImportCecil_" + testMethodName;
  238. var module = CreateTestModule<TDelegate> (name, emitter);
  239. var assembly = LoadTestModule (module);
  240. return CreateRunDelegate<TDelegate> (GetTestCase (name, assembly));
  241. }
  242. static TDelegate CreateRunDelegate<TDelegate> (Type type)
  243. where TDelegate : class
  244. {
  245. return (TDelegate) (object) Delegate.CreateDelegate (typeof (TDelegate), type.GetMethod ("Run"));
  246. }
  247. static Type GetTestCase (string name, SR.Assembly assembly)
  248. {
  249. return assembly.GetType (name);
  250. }
  251. static SR.Assembly LoadTestModule (ModuleDefinition module)
  252. {
  253. using (var stream = new MemoryStream ()) {
  254. module.Write (stream);
  255. File.WriteAllBytes (Path.Combine (Path.Combine (Path.GetTempPath (), "cecil"), module.Name + ".dll"), stream.ToArray ());
  256. return SR.Assembly.Load (stream.ToArray ());
  257. }
  258. }
  259. static ModuleDefinition CreateTestModule<TDelegate> (string name, Emitter emitter)
  260. {
  261. var module = CreateModule (name);
  262. var type = new TypeDefinition (
  263. "",
  264. name,
  265. TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Abstract,
  266. module.ImportReference (typeof (object)));
  267. module.Types.Add (type);
  268. var method = CreateMethod (type, typeof (TDelegate).GetMethod ("Invoke"));
  269. emitter (module, method.Body);
  270. return module;
  271. }
  272. static MethodDefinition CreateMethod (TypeDefinition type, SR.MethodInfo pattern)
  273. {
  274. var module = type.Module;
  275. var method = new MethodDefinition {
  276. Name = "Run",
  277. IsPublic = true,
  278. IsStatic = true,
  279. };
  280. type.Methods.Add (method);
  281. method.MethodReturnType.ReturnType = module.ImportReference (pattern.ReturnType);
  282. foreach (var parameter_pattern in pattern.GetParameters ())
  283. method.Parameters.Add (new ParameterDefinition (module.ImportReference (parameter_pattern.ParameterType)));
  284. return method;
  285. }
  286. static ModuleDefinition CreateModule (string name)
  287. {
  288. var resolver = new DefaultAssemblyResolver ();
  289. resolver.AddSearchDirectory (Path.GetDirectoryName (typeof (ImportCecilTests).Assembly.Location));
  290. return ModuleDefinition.CreateModule (name, new ModuleParameters { Kind = ModuleKind.Dll, AssemblyResolver = resolver });
  291. }
  292. }
  293. }