PageRenderTime 37ms CodeModel.GetById 11ms app.highlight 21ms RepoModel.GetById 2ms app.codeStats 0ms

/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
  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}