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