[test] SRE ILGenerator test for methodref and fieldref token caching
[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                         try {
62                                 il_gen.DeclareLocal (null, false);
63                                 Assert.Fail ("#B1");
64                         } catch (ArgumentNullException ex) {
65                                 Assert.AreEqual (typeof (ArgumentNullException), ex.GetType (), "#B2");
66                                 Assert.IsNull (ex.InnerException, "#B3");
67                                 Assert.IsNotNull (ex.Message, "#B4");
68                                 Assert.IsNotNull (ex.ParamName, "#B5");
69                                 Assert.AreEqual ("localType", ex.ParamName, "#B6");
70                         }
71                 }
72
73                 [Test]
74                 [ExpectedException (typeof (ArgumentException))]
75                 public void DefineFilterBodyWithTypeNotNull ()
76                 {
77                         DefineBasicMethod ();
78                         il_gen.BeginExceptionBlock ();
79                         il_gen.EmitWriteLine ("in try");
80                         il_gen.BeginExceptFilterBlock ();
81                         il_gen.EmitWriteLine ("in filter head");
82                         il_gen.BeginCatchBlock (typeof (Exception));
83                         il_gen.EmitWriteLine ("in filter body");
84                         il_gen.EndExceptionBlock ();
85                 }
86
87                 [Test] // bug #81431
88                 public void FilterAndCatchBlock ()
89                 {
90                         DefineBasicMethod ();
91                         ILGenerator il = il_gen;
92                         il.BeginExceptionBlock ();
93                         il.BeginExceptFilterBlock ();
94                         il.BeginCatchBlock (null);
95                         il.BeginCatchBlock (typeof (SystemException));
96                 }
97                 
98                 [Test]
99                 [ExpectedException (typeof (InvalidOperationException))]
100                 public void InvalidFilterBlock1 ()
101                 {
102                         DefineBasicMethod ();
103                         ILGenerator il = il_gen;
104                         il.BeginExceptionBlock ();
105                         il.BeginExceptFilterBlock ();
106                         il.EndExceptionBlock ();
107                 }
108                 
109                 [Test]
110                 public void ValidFilterBlock1 ()
111                 {
112                         DefineBasicMethod ();
113                         ILGenerator il = il_gen;
114                         il.BeginExceptionBlock ();
115                         il.BeginExceptFilterBlock ();
116                         il.BeginFaultBlock ();
117                         il.EndExceptionBlock ();
118                 }
119                 
120                 [Test]
121                 public void ValidFilterBlock2 ()
122                 {
123                         DefineBasicMethod ();
124                         ILGenerator il = il_gen;
125                         il.BeginExceptionBlock ();
126                         il.BeginExceptFilterBlock ();
127                         il.BeginFinallyBlock ();
128                         il.EndExceptionBlock ();
129                 }
130                 
131                 /// <summary>
132                 /// Try to emit something like that:
133                 ///
134                 /// .method public static bool TestFilter (bool execute_handler)
135                 /// {
136                 ///     .locals init(bool)
137                 ///     try {
138                 ///             newobj  instance void [mscorlib]System.Exception::.ctor()
139                 ///             throw
140                 ///     } filter {
141                 ///             pop
142                 ///             ldarg.0
143                 ///             endfilter
144                 ///     } {
145                 ///             ldc.i4.1
146                 ///             stloc.0
147                 ///             leave quit
148                 ///     }
149                 ///     ldc.i4.0
150                 ///     stloc.0
151                 /// quit:
152                 ///     ldloc.0
153                 ///     ret
154                 /// }
155                 ///
156                 /// It should return true if the handler has been executed
157                 /// Otherwise, the exception should not be catched
158                 /// </summary>
159                 void DefineTestFilterMethod ()
160                 {
161                         MethodBuilder mb = tb.DefineMethod("TestFilter",
162                                 MethodAttributes.Public | MethodAttributes.Static, typeof(bool), new Type [] { typeof (bool) });
163
164                         ConstructorInfo exCtor = typeof (Exception).GetConstructor (new Type [0]);
165
166                         il_gen = mb.GetILGenerator ();
167                         il_gen.DeclareLocal (typeof (bool));
168                         Label quit = il_gen.DefineLabel ();
169                         il_gen.BeginExceptionBlock ();
170                         il_gen.Emit (OpCodes.Newobj, exCtor);
171                         il_gen.Emit (OpCodes.Throw);
172                         il_gen.BeginExceptFilterBlock ();
173                         il_gen.Emit (OpCodes.Pop);
174                         il_gen.Emit (OpCodes.Ldarg_0);
175                         il_gen.BeginCatchBlock (null);
176                         il_gen.Emit (OpCodes.Ldc_I4_1);
177                         il_gen.Emit (OpCodes.Stloc_0);
178                         il_gen.Emit (OpCodes.Leave, quit);
179                         il_gen.EndExceptionBlock ();
180                         il_gen.Emit (OpCodes.Ldc_I4_0);
181                         il_gen.Emit (OpCodes.Stloc_0);
182                         il_gen.MarkLabel (quit);
183                         il_gen.Emit (OpCodes.Ldloc_0);
184                         il_gen.Emit (OpCodes.Ret);
185                 }
186
187                 [Test] // Emit (OpCode, ConstructorInfo)
188                 [Category ("NotDotNet")] // MS bug: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=304610
189                 public void Emit3_Constructor_Null ()
190                 {
191                         DefineBasicMethod ();
192                         try {
193                                 il_gen.Emit (OpCodes.Newobj, (ConstructorInfo) null);
194                                 Assert.Fail ("#1");
195                         } catch (ArgumentNullException ex) {
196                                 Assert.AreEqual (typeof (ArgumentNullException), ex.GetType (), "#2");
197                                 Assert.IsNull (ex.InnerException, "#3");
198                                 Assert.IsNotNull (ex.Message, "#4");
199                                 Assert.IsNotNull (ex.ParamName, "#5");
200                         }
201                 }
202
203                 [Test] // Emit (OpCode, ConstructorInfo)
204                 [Category ("NotWorking")] // MS bug: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=304610
205                 public void Emit3_Constructor_Null_MS ()
206                 {
207                         DefineBasicMethod ();
208                         try {
209                                 il_gen.Emit (OpCodes.Newobj, (ConstructorInfo) null);
210                                 Assert.Fail ("#1");
211                         } catch (NullReferenceException) {
212                         }
213                 }
214
215                 [Test] // Emit (OpCode, FieldInfo)
216                 public void Emit5_Field_Null ()
217                 {
218                         DefineBasicMethod ();
219                         try {
220                                 il_gen.Emit (OpCodes.Ldsfld, (FieldInfo) null);
221                                 Assert.Fail ("#1");
222                         } catch (ArgumentNullException ex) {
223                                 Assert.AreEqual (typeof (ArgumentNullException), ex.GetType (), "#2");
224                                 Assert.IsNull (ex.InnerException, "#3");
225                                 Assert.IsNotNull (ex.Message, "#4");
226                                 Assert.IsNotNull (ex.ParamName, "#5");
227                         }
228                 }
229
230                 [Test] // Emit (OpCode, Label [])
231                 [Category ("NotDotNet")] // MS bug: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=304610
232                 public void Emit10_Labels_Null ()
233                 {
234                         DefineBasicMethod ();
235                         try {
236                                 il_gen.Emit (OpCodes.Switch, (Label []) null);
237                                 Assert.Fail ("#1");
238                         } catch (ArgumentNullException ex) {
239                                 Assert.AreEqual (typeof (ArgumentNullException), ex.GetType (), "#2");
240                                 Assert.IsNull (ex.InnerException, "#3");
241                                 Assert.IsNotNull (ex.Message, "#4");
242                                 Assert.IsNotNull (ex.ParamName, "#5");
243                                 Assert.AreEqual ("labels", ex.ParamName, "#6");
244                         }
245                 }
246
247                 [Test]
248                 [Category ("NotWorking")] // MS bug: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=304610
249                 public void Emit10_Labels_Null_MS ()
250                 {
251                         DefineBasicMethod ();
252                         try {
253                                 il_gen.Emit (OpCodes.Switch, (Label []) null);
254                                 Assert.Fail ("#1");
255                         } catch (NullReferenceException) {
256                         }
257                 }
258
259                 [Test] // Emit (OpCode, LocalBuilder)
260                 public void Emit11_Local_Null ()
261                 {
262                         DefineBasicMethod ();
263                         try {
264                                 il_gen.Emit (OpCodes.Switch, (LocalBuilder) null);
265                                 Assert.Fail ("#1");
266                         } catch (ArgumentNullException ex) {
267                                 Assert.AreEqual (typeof (ArgumentNullException), ex.GetType (), "#2");
268                                 Assert.IsNull (ex.InnerException, "#3");
269                                 Assert.IsNotNull (ex.Message, "#4");
270                                 Assert.IsNotNull (ex.ParamName, "#5");
271                                 Assert.AreEqual ("local", ex.ParamName, "#6");
272                         }
273                 }
274
275                 [Test] // Emit (OpCode, MethodInfo)
276                 public void Emit12_Method_Null ()
277                 {
278                         DefineBasicMethod ();
279                         try {
280                                 il_gen.Emit (OpCodes.Switch, (MethodInfo) null);
281                                 Assert.Fail ("#1");
282                         } catch (ArgumentNullException ex) {
283                                 Assert.AreEqual (typeof (ArgumentNullException), ex.GetType (), "#2");
284                                 Assert.IsNull (ex.InnerException, "#3");
285                                 Assert.IsNotNull (ex.Message, "#4");
286                                 Assert.IsNotNull (ex.ParamName, "#5");
287                                 Assert.AreEqual ("meth", ex.ParamName, "#6");
288                         }
289                 }
290
291                 [Test] // Emit (OpCode, SignatureHelper)
292                 public void Emit14_Signature_Null ()
293                 {
294                         DefineBasicMethod ();
295                         try {
296                                 il_gen.Emit (OpCodes.Switch, (SignatureHelper) null);
297                                 Assert.Fail ("#1");
298                         } catch (ArgumentNullException ex) {
299                                 Assert.AreEqual (typeof (ArgumentNullException), ex.GetType (), "#2");
300                                 Assert.IsNull (ex.InnerException, "#3");
301                                 Assert.IsNotNull (ex.Message, "#4");
302                                 Assert.IsNotNull (ex.ParamName, "#5");
303                         }
304                 }
305
306                 [Test] // Emit (OpCode, String)
307                 public void Emit16_String_Null ()
308                 {
309                         DefineBasicMethod ();
310                         try {
311                                 il_gen.Emit (OpCodes.Switch, (String) null);
312                                 Assert.Fail ("#1");
313                         } catch (ArgumentNullException ex) {
314                                 Assert.AreEqual (typeof (ArgumentNullException), ex.GetType (), "#2");
315                                 Assert.IsNull (ex.InnerException, "#3");
316                                 Assert.IsNotNull (ex.Message, "#4");
317                         }
318                 }
319
320                 [Test] // Emit (OpCode, Type)
321                 public void Emit16_Type_Null ()
322                 {
323                         DefineBasicMethod ();
324                         try {
325                                 il_gen.Emit (OpCodes.Switch, (Type) null);
326                                 Assert.Fail ("#1");
327                         } catch (ArgumentNullException ex) {
328                                 Assert.AreEqual (typeof (ArgumentNullException), ex.GetType (), "#2");
329                                 Assert.IsNull (ex.InnerException, "#3");
330                                 Assert.IsNotNull (ex.Message, "#4");
331                                 Assert.IsNotNull (ex.ParamName, "#5");
332                         }
333                 }
334
335                 [Test]
336                 public void EmitCall_MethodInfo_Null ()
337                 {
338                         DefineBasicMethod ();
339                         try {
340                                 il_gen.EmitCall (OpCodes.Call, (MethodInfo) null, null);
341                                 Assert.Fail ("#1");
342                         } catch (ArgumentNullException ex) {
343                                 Assert.AreEqual (typeof (ArgumentNullException), ex.GetType (), "#2");
344                                 Assert.IsNull (ex.InnerException, "#3");
345                                 Assert.IsNotNull (ex.Message, "#4");
346                                 Assert.IsNotNull (ex.ParamName, "#5");
347                                 Assert.AreEqual ("methodInfo", ex.ParamName, "#6");
348                         }
349                 }
350
351                 [Test]
352                 public void TestFilterEmittingWithHandlerExecution ()
353                 {
354                         DefineTestFilterMethod ();
355                         Type dynt = tb.CreateType ();
356                         
357                         MethodInfo tf = dynt.GetMethod ("TestFilter");
358                         Assert.IsTrue ((bool) tf.Invoke (null, new object [] { true }));
359                 }
360
361                 delegate void FooFoo ();
362
363                 static void Foo ()
364                 {
365                 }
366
367                 [Test]
368                 public void TestEmitCalliWithNullReturnType ()
369                 {
370                         MethodBuilder mb = tb.DefineMethod ("F",
371                                 MethodAttributes.Public | MethodAttributes.Static, null, new Type [] { typeof (IntPtr) });
372                         mb.SetImplementationFlags (MethodImplAttributes.NoInlining);
373                         il_gen = mb.GetILGenerator ();
374                         il_gen.Emit (OpCodes.Ldarg_0);
375                         il_gen.EmitCalli (OpCodes.Calli, CallingConvention.StdCall, null, Type.EmptyTypes);
376                         il_gen.Emit (OpCodes.Ret);
377         
378                         Type dynt = tb.CreateType ();
379                         dynt.GetMethod ("F", BindingFlags.Public | BindingFlags.Static).Invoke (
380                                 null, new object [] { Marshal.GetFunctionPointerForDelegate (new FooFoo (Foo)) });
381                 }
382
383                 //Test for #509131
384                 [Test]
385                 public void TestEmitCallIgnoresOptionalArgsForNonVarargMethod ()
386                 {
387                         DefineBasicMethod ();
388                         try {
389                                 il_gen.EmitCall (OpCodes.Call, typeof (object).GetMethod ("GetHashCode"), new Type[] { typeof (string) });
390                         } catch (InvalidOperationException ex) {
391                                 Assert.Fail ("#1");
392                         }
393                 }
394
395                 [Test]
396                 [ExpectedException (typeof (Exception))]
397                 public void TestFilterEmittingWithoutHandlerExecution ()
398                 {
399                         DefineTestFilterMethod ();
400                         Type dynt = tb.CreateType ();
401                         
402                         MethodInfo tf = dynt.GetMethod ("TestFilter");
403                         try {
404                                 tf.Invoke (null, new object [] { false });
405                         } catch (TargetInvocationException tie) {
406                                 throw tie.InnerException;
407                         }
408                 }
409
410                 [Test]
411                 public void TestEmitLocalInfoWithNopOpCode ()
412                 {
413                         var method_builder = tb.DefineMethod ("linop", MethodAttributes.Public | MethodAttributes.Static, typeof (bool), Type.EmptyTypes);
414                         il_gen = method_builder.GetILGenerator ();
415
416                         var local = il_gen.DeclareLocal (typeof (int));
417                         il_gen.Emit (OpCodes.Nop, local);
418                         il_gen.Emit (OpCodes.Ldc_I4_1);
419                         il_gen.Emit (OpCodes.Ret);
420
421                         var type = tb.CreateType ();
422                         var method = type.GetMethod ("linop");
423
424                         Assert.IsNotNull (method);
425                         Assert.IsTrue ((bool) method.Invoke (null, new object [0]));
426                 }
427
428                 [Test]
429                 [ExpectedException (typeof (ArgumentException))]
430                 public void LdObjByRef () {
431                         DefineBasicMethod ();
432                         ILGenerator ig = il_gen;
433
434                         ig.Emit (OpCodes.Ldtoken, typeof (int).MakeByRefType ());
435                 }
436
437
438
439                 [Test] //bug #649017
440                 public void GtdEncodingAsOpenInstance () {
441                 AssemblyName asmname = new AssemblyName ();
442                 asmname.Name = "test";
443                 AssemblyBuilder asmbuild = Thread.GetDomain ().DefineDynamicAssembly (asmname, AssemblyBuilderAccess.RunAndSave);
444                 ModuleBuilder modbuild = asmbuild.DefineDynamicModule ("modulename", "test.exe");
445         
446                 TypeBuilder myType = modbuild.DefineType ("Sample", TypeAttributes.Public);
447         
448                 string[] typeParamNames = { "TFirst" };
449                 myType.DefineGenericParameters (typeParamNames);
450         
451                 var nested = myType.DefineNestedType ("nested");
452                 nested.DefineGenericParameters (typeParamNames);
453         
454                 var m = myType.DefineMethod ("test", MethodAttributes.Public);
455                 m.SetParameters (myType);
456         
457                 var ilgen = m.GetILGenerator ();
458                 ilgen.Emit (OpCodes.Castclass, nested);
459                 ilgen.Emit (OpCodes.Castclass, typeof (List<>));
460                 ilgen.Emit (OpCodes.Ldtoken, nested);
461                 ilgen.Emit (OpCodes.Ldtoken, typeof (List<>));
462         
463                 var baked = myType.CreateType ();
464                 nested.CreateType ();
465         
466                         var method = baked.GetMethod ("test");
467                         var body = method.GetMethodBody ();
468                         /*
469                         The resulting IL is:
470                         [ 0] 0x74 token:uint
471                         [ 5] 0x74 token:uint
472                         [10] 0xd0 token:uint
473                         [10] 0xd0 token:uint
474                         The first two tokens must be to typespecs and the last two to typeref/typedef*/
475                         var il = body.GetILAsByteArray ();
476                 
477                         Assert.AreEqual (20, il.Length, "#1");
478                         Assert.AreEqual (0x1B, il [4]); //typespec
479                         Assert.AreEqual (0x1B, il [9]); //typespec
480                         Assert.AreEqual (0x02, il [14]); //typedef
481                         Assert.AreEqual (0x01, il [19]); //typeref
482                 }
483
484                 [Test]
485                 public void MethodRefTokenSame () {
486                         // Get the same non-virtual method from a base and a derived type so
487                         // that the MemberInfo:DeclaredType differs but the tokens are the same.
488                         //
489                         // Regression test for bugzilla #59364
490
491                         DefineBasicMethod ();
492
493                         var m1 = typeof (object).GetMethod ("GetType");
494                         var m2 = typeof (string).GetMethod ("GetType");
495
496                         var value_getter = typeof (RuntimeMethodHandle).GetProperty ("Value").GetMethod;
497
498                         var il = il_gen;
499
500                         var loc = il.DeclareLocal (typeof (RuntimeMethodHandle));
501
502                         // return ((int)(RuntimeMethodHandle (m1).Value == RuntimeMethodHandle (m2).Value)).ToString ()
503                         il.Emit (OpCodes.Ldtoken, m1);
504                         il.Emit (OpCodes.Stloc, loc);
505                         il.Emit (OpCodes.Ldloca, loc);
506                         il.Emit (OpCodes.Call, value_getter);
507                         il.Emit (OpCodes.Ldtoken, m2);
508                         il.Emit (OpCodes.Stloc, loc);
509                         il.Emit (OpCodes.Ldloca, loc);
510                         il.Emit (OpCodes.Call, value_getter);
511                         il.Emit (OpCodes.Ceq);
512                         il.Emit (OpCodes.Box, typeof (Int32));
513                         il.Emit (OpCodes.Callvirt, typeof (object).GetMethod ("ToString"));
514                         il.Emit (OpCodes.Ret);
515
516                         var baked = tb.CreateType ();
517
518                         var x = Activator.CreateInstance (baked);
519                         var m = baked.GetMethod ("F");
520
521                         var s = m.Invoke (x, null);
522
523                         Assert.AreEqual ("1", s);
524                         
525                 }
526
527                 public class Base {
528                         public int x;
529                 }
530
531                 public class Derived : Base {
532                 }
533
534                 [Test]
535                 public void FieldRefTokenSame () {
536                         DefineBasicMethod ();
537
538                         // Get the same field from a base and a derived type so hat
539                         // the MemberInfo:DeclaredType differs but the tokens are the same.
540                         //
541                         // Regression test for bugzilla #59364
542
543                         var f1 = typeof (Base).GetField ("x");
544                         var f2 = typeof (Derived).GetField ("x");
545
546                         var value_getter = typeof (RuntimeFieldHandle).GetProperty("Value").GetMethod;
547
548                         var il = il_gen;
549
550                         var loc = il.DeclareLocal (typeof (RuntimeFieldHandle));
551
552                         il.Emit (OpCodes.Ldtoken, f1);
553                         il.Emit (OpCodes.Stloc, loc);
554                         il.Emit (OpCodes.Ldloca, loc);
555                         il.Emit (OpCodes.Call, value_getter);
556                         il.Emit (OpCodes.Ldtoken, f2);
557                         il.Emit (OpCodes.Stloc, loc);
558                         il.Emit (OpCodes.Ldloca, loc);
559                         il.Emit (OpCodes.Call, value_getter);
560                         il.Emit (OpCodes.Ceq);
561                         il.Emit (OpCodes.Box, typeof (Int32));
562                         il.Emit (OpCodes.Callvirt, typeof (object).GetMethod ("ToString"));
563                         il.Emit (OpCodes.Ret);
564
565                         var baked = tb.CreateType ();
566
567                         var x = Activator.CreateInstance (baked);
568                         var m = baked.GetMethod ("F");
569
570                         var s = m.Invoke (x, null);
571
572                         Assert.AreEqual ("1", s);
573                 }
574
575         }
576 }