Merge branch 'cecil-light'
[mono.git] / mcs / class / corlib / Test / System.Reflection.Emit / ILGeneratorTest.cs
1 //
2 // ILGeneratorTest.cs - NUnit Test Cases for the ILGenerator class
3 //
4 // Marek Safar (marek.safar@seznam.cz)
5 //
6 // (C) Novell, Inc.  http://www.novell.com
7
8 using System;
9 using System.Collections.Generic;
10 using System.Reflection;
11 using System.Reflection.Emit;
12 using System.Runtime.InteropServices;
13 using System.Threading;
14
15 using NUnit.Framework;
16
17 namespace MonoTests.System.Reflection.Emit
18 {
19         [TestFixture]
20         public class ILGeneratorTest
21         {
22                 TypeBuilder tb;
23                 ILGenerator il_gen;
24
25                 void DefineBasicMethod ()
26                 {
27                         MethodBuilder mb = tb.DefineMethod("F",
28                                 MethodAttributes.Public, typeof(string), null);
29                         il_gen = mb.GetILGenerator ();
30                 }
31
32                 [SetUp]
33                 public void SetUp ()
34                 {
35                         AssemblyName assemblyName = new AssemblyName ();
36                         assemblyName.Name = "MonoTests.System.Reflection.Emit.ILGeneratorTest";
37
38                         AssemblyBuilder assembly = Thread.GetDomain ().DefineDynamicAssembly (
39                                 assemblyName, AssemblyBuilderAccess.Run);
40
41                         ModuleBuilder module = assembly.DefineDynamicModule ("module1");
42                         tb = module.DefineType ("T", TypeAttributes.Public);
43                 }
44
45                 [Test]
46                 public void DeclareLocal_LocalType_Null ()
47                 {
48                         DefineBasicMethod ();
49
50                         try {
51                                 il_gen.DeclareLocal (null);
52                                 Assert.Fail ("#A1");
53                         } catch (ArgumentNullException ex) {
54                                 Assert.AreEqual (typeof (ArgumentNullException), ex.GetType (), "#A2");
55                                 Assert.IsNull (ex.InnerException, "#A3");
56                                 Assert.IsNotNull (ex.Message, "#A4");
57                                 Assert.IsNotNull (ex.ParamName, "#A5");
58                                 Assert.AreEqual ("localType", ex.ParamName, "#A");
59                         }
60
61 #if NET_2_0
62                         try {
63                                 il_gen.DeclareLocal (null, false);
64                                 Assert.Fail ("#B1");
65                         } catch (ArgumentNullException ex) {
66                                 Assert.AreEqual (typeof (ArgumentNullException), ex.GetType (), "#B2");
67                                 Assert.IsNull (ex.InnerException, "#B3");
68                                 Assert.IsNotNull (ex.Message, "#B4");
69                                 Assert.IsNotNull (ex.ParamName, "#B5");
70                                 Assert.AreEqual ("localType", ex.ParamName, "#B6");
71                         }
72 #endif
73                 }
74
75                 [Test]
76                 [ExpectedException (typeof (ArgumentException))]
77                 public void DefineFilterBodyWithTypeNotNull ()
78                 {
79                         DefineBasicMethod ();
80                         il_gen.BeginExceptionBlock ();
81                         il_gen.EmitWriteLine ("in try");
82                         il_gen.BeginExceptFilterBlock ();
83                         il_gen.EmitWriteLine ("in filter head");
84                         il_gen.BeginCatchBlock (typeof (Exception));
85                         il_gen.EmitWriteLine ("in filter body");
86                         il_gen.EndExceptionBlock ();
87                 }
88
89                 [Test] // bug #81431
90                 public void FilterAndCatchBlock ()
91                 {
92                         DefineBasicMethod ();
93                         ILGenerator il = il_gen;
94                         il.BeginExceptionBlock ();
95                         il.BeginExceptFilterBlock ();
96                         il.BeginCatchBlock (null);
97                         il.BeginCatchBlock (typeof (SystemException));
98                 }
99                 
100                 [Test]
101                 [ExpectedException (typeof (InvalidOperationException))]
102                 public void InvalidFilterBlock1 ()
103                 {
104                         DefineBasicMethod ();
105                         ILGenerator il = il_gen;
106                         il.BeginExceptionBlock ();
107                         il.BeginExceptFilterBlock ();
108                         il.EndExceptionBlock ();
109                 }
110                 
111                 [Test]
112                 public void ValidFilterBlock1 ()
113                 {
114                         DefineBasicMethod ();
115                         ILGenerator il = il_gen;
116                         il.BeginExceptionBlock ();
117                         il.BeginExceptFilterBlock ();
118                         il.BeginFaultBlock ();
119                         il.EndExceptionBlock ();
120                 }
121                 
122                 [Test]
123                 public void ValidFilterBlock2 ()
124                 {
125                         DefineBasicMethod ();
126                         ILGenerator il = il_gen;
127                         il.BeginExceptionBlock ();
128                         il.BeginExceptFilterBlock ();
129                         il.BeginFinallyBlock ();
130                         il.EndExceptionBlock ();
131                 }
132                 
133                 /// <summary>
134                 /// Try to emit something like that:
135                 ///
136                 /// .method public static bool TestFilter (bool execute_handler)
137                 /// {
138                 ///     .locals init(bool)
139                 ///     try {
140                 ///             newobj  instance void [mscorlib]System.Exception::.ctor()
141                 ///             throw
142                 ///     } filter {
143                 ///             pop
144                 ///             ldarg.0
145                 ///             endfilter
146                 ///     } {
147                 ///             ldc.i4.1
148                 ///             stloc.0
149                 ///             leave quit
150                 ///     }
151                 ///     ldc.i4.0
152                 ///     stloc.0
153                 /// quit:
154                 ///     ldloc.0
155                 ///     ret
156                 /// }
157                 ///
158                 /// It should return true if the handler has been executed
159                 /// Otherwise, the exception should not be catched
160                 /// </summary>
161                 void DefineTestFilterMethod ()
162                 {
163                         MethodBuilder mb = tb.DefineMethod("TestFilter",
164                                 MethodAttributes.Public | MethodAttributes.Static, typeof(bool), new Type [] { typeof (bool) });
165
166                         ConstructorInfo exCtor = typeof (Exception).GetConstructor (new Type [0]);
167
168                         il_gen = mb.GetILGenerator ();
169                         il_gen.DeclareLocal (typeof (bool));
170                         Label quit = il_gen.DefineLabel ();
171                         il_gen.BeginExceptionBlock ();
172                         il_gen.Emit (OpCodes.Newobj, exCtor);
173                         il_gen.Emit (OpCodes.Throw);
174                         il_gen.BeginExceptFilterBlock ();
175                         il_gen.Emit (OpCodes.Pop);
176                         il_gen.Emit (OpCodes.Ldarg_0);
177                         il_gen.BeginCatchBlock (null);
178                         il_gen.Emit (OpCodes.Ldc_I4_1);
179                         il_gen.Emit (OpCodes.Stloc_0);
180                         il_gen.Emit (OpCodes.Leave, quit);
181                         il_gen.EndExceptionBlock ();
182                         il_gen.Emit (OpCodes.Ldc_I4_0);
183                         il_gen.Emit (OpCodes.Stloc_0);
184                         il_gen.MarkLabel (quit);
185                         il_gen.Emit (OpCodes.Ldloc_0);
186                         il_gen.Emit (OpCodes.Ret);
187                 }
188
189                 [Test] // Emit (OpCode, ConstructorInfo)
190 #if NET_2_0
191                 [Category ("NotDotNet")] // MS bug: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=304610
192 #endif
193                 public void Emit3_Constructor_Null ()
194                 {
195                         DefineBasicMethod ();
196                         try {
197                                 il_gen.Emit (OpCodes.Newobj, (ConstructorInfo) null);
198                                 Assert.Fail ("#1");
199                         } catch (ArgumentNullException ex) {
200                                 Assert.AreEqual (typeof (ArgumentNullException), ex.GetType (), "#2");
201                                 Assert.IsNull (ex.InnerException, "#3");
202                                 Assert.IsNotNull (ex.Message, "#4");
203                                 Assert.IsNotNull (ex.ParamName, "#5");
204                         }
205                 }
206
207 #if NET_2_0
208                 [Test] // Emit (OpCode, ConstructorInfo)
209                 [Category ("NotWorking")] // MS bug: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=304610
210                 public void Emit3_Constructor_Null_MS ()
211                 {
212                         DefineBasicMethod ();
213                         try {
214                                 il_gen.Emit (OpCodes.Newobj, (ConstructorInfo) null);
215                                 Assert.Fail ("#1");
216                         } catch (NullReferenceException) {
217                         }
218                 }
219 #endif
220
221                 [Test] // Emit (OpCode, FieldInfo)
222                 public void Emit5_Field_Null ()
223                 {
224                         DefineBasicMethod ();
225                         try {
226                                 il_gen.Emit (OpCodes.Ldsfld, (FieldInfo) null);
227                                 Assert.Fail ("#1");
228                         } catch (ArgumentNullException ex) {
229                                 Assert.AreEqual (typeof (ArgumentNullException), ex.GetType (), "#2");
230                                 Assert.IsNull (ex.InnerException, "#3");
231                                 Assert.IsNotNull (ex.Message, "#4");
232                                 Assert.IsNotNull (ex.ParamName, "#5");
233                         }
234                 }
235
236                 [Test] // Emit (OpCode, Label [])
237                 [Category ("NotDotNet")] // MS bug: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=304610
238                 public void Emit10_Labels_Null ()
239                 {
240                         DefineBasicMethod ();
241                         try {
242                                 il_gen.Emit (OpCodes.Switch, (Label []) null);
243                                 Assert.Fail ("#1");
244                         } catch (ArgumentNullException ex) {
245                                 Assert.AreEqual (typeof (ArgumentNullException), ex.GetType (), "#2");
246                                 Assert.IsNull (ex.InnerException, "#3");
247                                 Assert.IsNotNull (ex.Message, "#4");
248                                 Assert.IsNotNull (ex.ParamName, "#5");
249                                 Assert.AreEqual ("labels", ex.ParamName, "#6");
250                         }
251                 }
252
253                 [Test]
254                 [Category ("NotWorking")] // MS bug: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=304610
255                 public void Emit10_Labels_Null_MS ()
256                 {
257                         DefineBasicMethod ();
258                         try {
259                                 il_gen.Emit (OpCodes.Switch, (Label []) null);
260                                 Assert.Fail ("#1");
261                         } catch (NullReferenceException) {
262                         }
263                 }
264
265                 [Test] // Emit (OpCode, LocalBuilder)
266                 public void Emit11_Local_Null ()
267                 {
268                         DefineBasicMethod ();
269                         try {
270                                 il_gen.Emit (OpCodes.Switch, (LocalBuilder) null);
271                                 Assert.Fail ("#1");
272                         } catch (ArgumentNullException ex) {
273                                 Assert.AreEqual (typeof (ArgumentNullException), ex.GetType (), "#2");
274                                 Assert.IsNull (ex.InnerException, "#3");
275                                 Assert.IsNotNull (ex.Message, "#4");
276                                 Assert.IsNotNull (ex.ParamName, "#5");
277                                 Assert.AreEqual ("local", ex.ParamName, "#6");
278                         }
279                 }
280
281                 [Test] // Emit (OpCode, MethodInfo)
282                 public void Emit12_Method_Null ()
283                 {
284                         DefineBasicMethod ();
285                         try {
286                                 il_gen.Emit (OpCodes.Switch, (MethodInfo) null);
287                                 Assert.Fail ("#1");
288                         } catch (ArgumentNullException ex) {
289                                 Assert.AreEqual (typeof (ArgumentNullException), ex.GetType (), "#2");
290                                 Assert.IsNull (ex.InnerException, "#3");
291                                 Assert.IsNotNull (ex.Message, "#4");
292                                 Assert.IsNotNull (ex.ParamName, "#5");
293                                 Assert.AreEqual ("meth", ex.ParamName, "#6");
294                         }
295                 }
296
297                 [Test] // Emit (OpCode, SignatureHelper)
298                 public void Emit14_Signature_Null ()
299                 {
300                         DefineBasicMethod ();
301                         try {
302                                 il_gen.Emit (OpCodes.Switch, (SignatureHelper) null);
303                                 Assert.Fail ("#1");
304                         } catch (ArgumentNullException ex) {
305                                 Assert.AreEqual (typeof (ArgumentNullException), ex.GetType (), "#2");
306                                 Assert.IsNull (ex.InnerException, "#3");
307                                 Assert.IsNotNull (ex.Message, "#4");
308                                 Assert.IsNotNull (ex.ParamName, "#5");
309                         }
310                 }
311
312                 [Test] // Emit (OpCode, String)
313                 public void Emit16_String_Null ()
314                 {
315                         DefineBasicMethod ();
316                         try {
317                                 il_gen.Emit (OpCodes.Switch, (String) null);
318                                 Assert.Fail ("#1");
319                         } catch (ArgumentNullException ex) {
320                                 Assert.AreEqual (typeof (ArgumentNullException), ex.GetType (), "#2");
321                                 Assert.IsNull (ex.InnerException, "#3");
322                                 Assert.IsNotNull (ex.Message, "#4");
323                         }
324                 }
325
326                 [Test] // Emit (OpCode, Type)
327                 public void Emit16_Type_Null ()
328                 {
329                         DefineBasicMethod ();
330                         try {
331                                 il_gen.Emit (OpCodes.Switch, (Type) null);
332                                 Assert.Fail ("#1");
333                         } catch (ArgumentNullException ex) {
334                                 Assert.AreEqual (typeof (ArgumentNullException), ex.GetType (), "#2");
335                                 Assert.IsNull (ex.InnerException, "#3");
336                                 Assert.IsNotNull (ex.Message, "#4");
337                                 Assert.IsNotNull (ex.ParamName, "#5");
338                         }
339                 }
340
341                 [Test]
342                 public void EmitCall_MethodInfo_Null ()
343                 {
344                         DefineBasicMethod ();
345                         try {
346                                 il_gen.EmitCall (OpCodes.Call, (MethodInfo) null, null);
347                                 Assert.Fail ("#1");
348                         } catch (ArgumentNullException ex) {
349                                 Assert.AreEqual (typeof (ArgumentNullException), ex.GetType (), "#2");
350                                 Assert.IsNull (ex.InnerException, "#3");
351                                 Assert.IsNotNull (ex.Message, "#4");
352                                 Assert.IsNotNull (ex.ParamName, "#5");
353                                 Assert.AreEqual ("methodInfo", ex.ParamName, "#6");
354                         }
355                 }
356
357                 [Test]
358                 public void TestFilterEmittingWithHandlerExecution ()
359                 {
360                         DefineTestFilterMethod ();
361                         Type dynt = tb.CreateType ();
362                         
363                         MethodInfo tf = dynt.GetMethod ("TestFilter");
364                         Assert.IsTrue ((bool) tf.Invoke (null, new object [] { true }));
365                 }
366
367 #if NET_2_0
368                 delegate void FooFoo ();
369
370                 static void Foo ()
371                 {
372                 }
373
374                 [Test]
375                 public void TestEmitCalliWithNullReturnType ()
376                 {
377                         MethodBuilder mb = tb.DefineMethod ("F",
378                                 MethodAttributes.Public | MethodAttributes.Static, null, new Type [] { typeof (IntPtr) });
379                         mb.SetImplementationFlags (MethodImplAttributes.NoInlining);
380                         il_gen = mb.GetILGenerator ();
381                         il_gen.Emit (OpCodes.Ldarg_0);
382                         il_gen.EmitCalli (OpCodes.Calli, CallingConvention.StdCall, null, Type.EmptyTypes);
383                         il_gen.Emit (OpCodes.Ret);
384         
385                         Type dynt = tb.CreateType ();
386                         dynt.GetMethod ("F", BindingFlags.Public | BindingFlags.Static).Invoke (
387                                 null, new object [] { Marshal.GetFunctionPointerForDelegate (new FooFoo (Foo)) });
388                 }
389 #endif
390
391 #if NET_2_0
392                 //Test for #509131
393                 [Test]
394                 public void TestEmitCallIgnoresOptionalArgsForNonVarargMethod ()
395                 {
396                         DefineBasicMethod ();
397                         try {
398                                 il_gen.EmitCall (OpCodes.Call, typeof (object).GetMethod ("GetHashCode"), new Type[] { typeof (string) });
399                         } catch (InvalidOperationException ex) {
400                                 Assert.Fail ("#1");
401                         }
402                 }
403 #else
404                 [Test]
405                 public void TestEmitCallThrowsOnOptionalArgsForNonVarargMethod ()
406                 {
407                         DefineBasicMethod ();
408                         try {
409                                 il_gen.EmitCall (OpCodes.Call, typeof (object).GetMethod ("GetHashCode"), new Type[] { typeof (string) });
410                                 Assert.Fail ("#1");
411                         } catch (InvalidOperationException ex) {
412                         }
413                 }
414 #endif
415
416                 [Test]
417                 [ExpectedException (typeof (Exception))]
418                 public void TestFilterEmittingWithoutHandlerExecution ()
419                 {
420                         DefineTestFilterMethod ();
421                         Type dynt = tb.CreateType ();
422                         
423                         MethodInfo tf = dynt.GetMethod ("TestFilter");
424                         try {
425                                 tf.Invoke (null, new object [] { false });
426                         } catch (TargetInvocationException tie) {
427                                 throw tie.InnerException;
428                         }
429                 }
430
431                 [Test]
432                 public void TestEmitLocalInfoWithNopOpCode ()
433                 {
434                         var method_builder = tb.DefineMethod ("linop", MethodAttributes.Public | MethodAttributes.Static, typeof (bool), Type.EmptyTypes);
435                         il_gen = method_builder.GetILGenerator ();
436
437                         var local = il_gen.DeclareLocal (typeof (int));
438                         il_gen.Emit (OpCodes.Nop, local);
439                         il_gen.Emit (OpCodes.Ldc_I4_1);
440                         il_gen.Emit (OpCodes.Ret);
441
442                         var type = tb.CreateType ();
443                         var method = type.GetMethod ("linop");
444
445                         Assert.IsNotNull (method);
446                         Assert.IsTrue ((bool) method.Invoke (null, new object [0]));
447                 }
448
449                 [Test]
450                 [ExpectedException (typeof (ArgumentException))]
451                 public void LdObjByRef () {
452                         DefineBasicMethod ();
453                         ILGenerator ig = il_gen;
454
455                         ig.Emit (OpCodes.Ldtoken, typeof (int).MakeByRefType ());
456                 }
457
458
459
460                 [Test] //bug #649017
461                 public void GtdEncodingAsOpenInstance () {
462                 AssemblyName asmname = new AssemblyName ();
463                 asmname.Name = "test";
464                 AssemblyBuilder asmbuild = Thread.GetDomain ().DefineDynamicAssembly (asmname, AssemblyBuilderAccess.RunAndSave);
465                 ModuleBuilder modbuild = asmbuild.DefineDynamicModule ("modulename", "test.exe");
466         
467                 TypeBuilder myType = modbuild.DefineType ("Sample", TypeAttributes.Public);
468         
469                 string[] typeParamNames = { "TFirst" };
470                 myType.DefineGenericParameters (typeParamNames);
471         
472                 var nested = myType.DefineNestedType ("nested");
473                 nested.DefineGenericParameters (typeParamNames);
474         
475                 var m = myType.DefineMethod ("test", MethodAttributes.Public);
476                 m.SetParameters (myType);
477         
478                 var ilgen = m.GetILGenerator ();
479                 ilgen.Emit (OpCodes.Castclass, nested);
480                 ilgen.Emit (OpCodes.Castclass, typeof (List<>));
481                 ilgen.Emit (OpCodes.Ldtoken, nested);
482                 ilgen.Emit (OpCodes.Ldtoken, typeof (List<>));
483         
484                 var baked = myType.CreateType ();
485                 nested.CreateType ();
486         
487                         var method = baked.GetMethod ("test");
488                         var body = method.GetMethodBody ();
489                         /*
490                         The resulting IL is:
491                         [ 0] 0x74 token:uint
492                         [ 5] 0x74 token:uint
493                         [10] 0xd0 token:uint
494                         [10] 0xd0 token:uint
495                         The first two tokens must be to typespecs and the last two to typeref/typedef*/
496                         var il = body.GetILAsByteArray ();
497                 
498                         Assert.AreEqual (20, il.Length, "#1");
499                         Assert.AreEqual (0x1B, il [4]); //typespec
500                         Assert.AreEqual (0x1B, il [9]); //typespec
501                         Assert.AreEqual (0x02, il [14]); //typedef
502                         Assert.AreEqual (0x01, il [19]); //typeref
503                 }
504         }
505 }