342025b2b3d0131a0b993b5fba95d09f7327f403
[mono.git] / mcs / mcs / codegen.cs
1 //
2 // codegen.cs: The code generator
3 //
4 // Authors:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //   Marek Safar (marek.safar@gmail.com)
7 //
8 // Copyright 2001, 2002, 2003 Ximian, Inc.
9 // Copyright 2004 Novell, Inc.
10 // Copyright 2011 Xamarin Inc
11 //
12
13 using System;
14 using System.Collections.Generic;
15 using Mono.CompilerServices.SymbolWriter;
16
17 #if STATIC
18 using MetaType = IKVM.Reflection.Type;
19 using IKVM.Reflection;
20 using IKVM.Reflection.Emit;
21 #else
22 using MetaType = System.Type;
23 using System.Reflection;
24 using System.Reflection.Emit;
25 #endif
26
27 namespace Mono.CSharp
28 {
29         /// <summary>
30         ///   An Emit Context is created for each body of code (from methods,
31         ///   properties bodies, indexer bodies or constructor bodies)
32         /// </summary>
33         public class EmitContext : BuilderContext
34         {
35                 // TODO: Has to be private
36                 public readonly ILGenerator ig;
37
38                 /// <summary>
39                 ///   The value that is allowed to be returned or NULL if there is no
40                 ///   return type.
41                 /// </summary>
42                 readonly TypeSpec return_type;
43
44                 /// <summary>
45                 ///   Keeps track of the Type to LocalBuilder temporary storage created
46                 ///   to store structures (used to compute the address of the structure
47                 ///   value on structure method invocations)
48                 /// </summary>
49                 Dictionary<TypeSpec, object> temporary_storage;
50
51                 /// <summary>
52                 ///   The location where we store the return value.
53                 /// </summary>
54                 public LocalBuilder return_value;
55
56
57                 /// <summary>
58                 ///   Current loop begin and end labels.
59                 /// </summary>
60                 public Label LoopBegin, LoopEnd;
61
62                 /// <summary>
63                 ///   Default target in a switch statement.   Only valid if
64                 ///   InSwitch is true
65                 /// </summary>
66                 public Label DefaultTarget;
67
68                 /// <summary>
69                 ///   If this is non-null, points to the current switch statement
70                 /// </summary>
71                 public Switch Switch;
72
73                 /// <summary>
74                 ///  Whether we are inside an anonymous method.
75                 /// </summary>
76                 public AnonymousExpression CurrentAnonymousMethod;
77                 
78                 readonly IMemberContext member_context;
79
80                 readonly SourceMethodBuilder methodSymbols;
81
82                 DynamicSiteClass dynamic_site_container;
83
84                 Label? return_label;
85
86                 public EmitContext (IMemberContext rc, ILGenerator ig, TypeSpec return_type, SourceMethodBuilder methodSymbols)
87                 {
88                         this.member_context = rc;
89                         this.ig = ig;
90                         this.return_type = return_type;
91
92                         if (rc.Module.Compiler.Settings.Checked)
93                                 flags |= Options.CheckedScope;
94
95                         if (methodSymbols != null) {
96                                 this.methodSymbols = methodSymbols;
97                                 if (!rc.Module.Compiler.Settings.Optimize)
98                                         flags |= Options.AccurateDebugInfo;
99                         } else {
100                                 flags |= Options.OmitDebugInfo;
101                         }
102
103 #if STATIC
104                         ig.__CleverExceptionBlockAssistance ();
105 #endif
106                 }
107
108                 #region Properties
109
110                 internal AsyncTaskStorey AsyncTaskStorey {
111                         get {
112                                 return CurrentAnonymousMethod.Storey as AsyncTaskStorey;
113                         }
114                 }
115
116                 public BuiltinTypes BuiltinTypes {
117                         get {
118                                 return MemberContext.Module.Compiler.BuiltinTypes;
119                         }
120                 }
121
122                 public TypeSpec CurrentType {
123                         get { return member_context.CurrentType; }
124                 }
125
126                 public TypeParameters CurrentTypeParameters {
127                     get { return member_context.CurrentTypeParameters; }
128                 }
129
130                 public MemberCore CurrentTypeDefinition {
131                         get { return member_context.CurrentMemberDefinition; }
132                 }
133
134                 public bool EmitAccurateDebugInfo {
135                         get {
136                                 return (flags & Options.AccurateDebugInfo) != 0;
137                         }
138                 }
139
140                 public bool HasReturnLabel {
141                         get {
142                                 return return_label.HasValue;
143                         }
144                 }
145
146                 public bool IsStatic {
147                         get { return member_context.IsStatic; }
148                 }
149
150                 public bool IsAnonymousStoreyMutateRequired {
151                         get {
152                                 return CurrentAnonymousMethod != null &&
153                                         CurrentAnonymousMethod.Storey != null &&
154                                         CurrentAnonymousMethod.Storey.Mutator != null;
155                         }
156                 }
157
158                 public IMemberContext MemberContext {
159                         get {
160                                 return member_context;
161                         }
162                 }
163
164                 public ModuleContainer Module {
165                         get {
166                                 return member_context.Module;
167                         }
168                 }
169
170                 // Has to be used for specific emitter errors only any
171                 // possible resolver errors have to be reported during Resolve
172                 public Report Report {
173                         get {
174                                 return member_context.Module.Compiler.Report;
175                         }
176                 }
177
178                 public TypeSpec ReturnType {
179                         get {
180                                 return return_type;
181                         }
182                 }
183
184                 //
185                 // The label where we have to jump before leaving the context
186                 //
187                 public Label ReturnLabel {
188                         get {
189                                 return return_label.Value;
190                         }
191                 }
192
193                 #endregion
194
195                 public void AssertEmptyStack ()
196                 {
197 #if STATIC
198                         if (ig.__StackHeight != 0)
199                                 throw new InternalErrorException ("Await yields with non-empty stack in `{0}",
200                                         member_context.GetSignatureForError ());
201 #endif
202                 }
203
204                 /// <summary>
205                 ///   This is called immediately before emitting an IL opcode to tell the symbol
206                 ///   writer to which source line this opcode belongs.
207                 /// </summary>
208                 public bool Mark (Location loc)
209                 {
210                         if ((flags & Options.OmitDebugInfo) != 0)
211                                 return false;
212
213                         if (loc.IsNull || methodSymbols == null)
214                                 return false;
215
216                         var sf = loc.SourceFile;
217                         if (sf.IsHiddenLocation (loc))
218                                 return false;
219
220 #if NET_4_0
221                         methodSymbols.MarkSequencePoint (ig.ILOffset, sf.SourceFileEntry, loc.Row, loc.Column, false);
222 #endif
223                         return true;
224                 }
225
226                 public void DefineLocalVariable (string name, LocalBuilder builder)
227                 {
228                         if ((flags & Options.OmitDebugInfo) != 0)
229                                 return;
230
231                         methodSymbols.AddLocal (builder.LocalIndex, name);
232                 }
233
234                 public void BeginCatchBlock (TypeSpec type)
235                 {
236                         ig.BeginCatchBlock (type.GetMetaInfo ());
237                 }
238
239                 public void BeginExceptionBlock ()
240                 {
241                         ig.BeginExceptionBlock ();
242                 }
243
244                 public void BeginFinallyBlock ()
245                 {
246                         ig.BeginFinallyBlock ();
247                 }
248
249                 public void BeginScope ()
250                 {
251                         if ((flags & Options.OmitDebugInfo) != 0)
252                                 return;
253
254 #if NET_4_0
255                         methodSymbols.StartBlock (CodeBlockEntry.Type.Lexical, ig.ILOffset);
256 #endif
257                 }
258
259                 public void EndExceptionBlock ()
260                 {
261                         ig.EndExceptionBlock ();
262                 }
263
264                 public void EndScope ()
265                 {
266                         if ((flags & Options.OmitDebugInfo) != 0)
267                                 return;
268
269 #if NET_4_0
270                         methodSymbols.EndBlock (ig.ILOffset);
271 #endif
272                 }
273
274                 //
275                 // Creates a nested container in this context for all dynamic compiler generated stuff
276                 //
277                 internal DynamicSiteClass CreateDynamicSite ()
278                 {
279                         if (dynamic_site_container == null) {
280                                 var mc = member_context.CurrentMemberDefinition as MemberBase;
281                                 dynamic_site_container = new DynamicSiteClass (CurrentTypeDefinition.Parent.PartialContainer, mc, member_context.CurrentTypeParameters);
282
283                                 CurrentTypeDefinition.Module.AddCompilerGeneratedClass (dynamic_site_container);
284                                 dynamic_site_container.CreateContainer ();
285                                 dynamic_site_container.DefineContainer ();
286                                 dynamic_site_container.Define ();
287
288                                 var inflator = new TypeParameterInflator (Module, CurrentType, TypeParameterSpec.EmptyTypes, TypeSpec.EmptyTypes);
289                                 var inflated = dynamic_site_container.CurrentType.InflateMember (inflator);
290                                 CurrentType.MemberCache.AddMember (inflated);
291                         }
292
293                         return dynamic_site_container;
294                 }
295
296                 public Label CreateReturnLabel ()
297                 {
298                         if (!return_label.HasValue)
299                                 return_label = DefineLabel ();
300
301                         return return_label.Value;
302                 }
303
304                 public LocalBuilder DeclareLocal (TypeSpec type, bool pinned)
305                 {
306                         if (IsAnonymousStoreyMutateRequired)
307                                 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
308
309                         return ig.DeclareLocal (type.GetMetaInfo (), pinned);
310                 }
311
312                 public Label DefineLabel ()
313                 {
314                         return ig.DefineLabel ();
315                 }
316
317                 //
318                 // Creates temporary field in current async storey
319                 //
320                 public FieldExpr GetTemporaryField (TypeSpec type)
321                 {
322                         var f = AsyncTaskStorey.AddCapturedLocalVariable (type);
323                         var fexpr = new StackFieldExpr (f);
324                         fexpr.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location.Null);
325                         return fexpr;
326                 }
327
328                 public void MarkLabel (Label label)
329                 {
330                         ig.MarkLabel (label);
331                 }
332
333                 public void Emit (OpCode opcode)
334                 {
335                         ig.Emit (opcode);
336                 }
337
338                 public void Emit (OpCode opcode, LocalBuilder local)
339                 {
340                         ig.Emit (opcode, local);
341                 }
342
343                 public void Emit (OpCode opcode, string arg)
344                 {
345                         ig.Emit (opcode, arg);
346                 }
347
348                 public void Emit (OpCode opcode, double arg)
349                 {
350                         ig.Emit (opcode, arg);
351                 }
352
353                 public void Emit (OpCode opcode, float arg)
354                 {
355                         ig.Emit (opcode, arg);
356                 }
357
358                 public void Emit (OpCode opcode, Label label)
359                 {
360                         ig.Emit (opcode, label);
361                 }
362
363                 public void Emit (OpCode opcode, Label[] labels)
364                 {
365                         ig.Emit (opcode, labels);
366                 }
367
368                 public void Emit (OpCode opcode, TypeSpec type)
369                 {
370                         if (IsAnonymousStoreyMutateRequired)
371                                 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
372
373                         ig.Emit (opcode, type.GetMetaInfo ());
374                 }
375
376                 public void Emit (OpCode opcode, FieldSpec field)
377                 {
378                         if (IsAnonymousStoreyMutateRequired)
379                                 field = field.Mutate (CurrentAnonymousMethod.Storey.Mutator);
380
381                         ig.Emit (opcode, field.GetMetaInfo ());
382                 }
383
384                 public void Emit (OpCode opcode, MethodSpec method)
385                 {
386                         if (IsAnonymousStoreyMutateRequired)
387                                 method = method.Mutate (CurrentAnonymousMethod.Storey.Mutator);
388
389                         if (method.IsConstructor)
390                                 ig.Emit (opcode, (ConstructorInfo) method.GetMetaInfo ());
391                         else
392                                 ig.Emit (opcode, (MethodInfo) method.GetMetaInfo ());
393                 }
394
395                 // TODO: REMOVE breaks mutator
396                 public void Emit (OpCode opcode, MethodInfo method)
397                 {
398                         ig.Emit (opcode, method);
399                 }
400
401                 public void Emit (OpCode opcode, MethodSpec method, MetaType[] vargs)
402                 {
403                         // TODO MemberCache: This should mutate too
404                         ig.EmitCall (opcode, (MethodInfo) method.GetMetaInfo (), vargs);
405                 }
406
407                 public void EmitArrayNew (ArrayContainer ac)
408                 {
409                         if (ac.Rank == 1) {
410                                 var type = IsAnonymousStoreyMutateRequired ?
411                                         CurrentAnonymousMethod.Storey.Mutator.Mutate (ac.Element) :
412                                         ac.Element;
413
414                                 ig.Emit (OpCodes.Newarr, type.GetMetaInfo ());
415                         } else {
416                                 if (IsAnonymousStoreyMutateRequired)
417                                         ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
418
419                                 ig.Emit (OpCodes.Newobj, ac.GetConstructor ());
420                         }
421                 }
422
423                 public void EmitArrayAddress (ArrayContainer ac)
424                 {
425                         if (ac.Rank > 1) {
426                                 if (IsAnonymousStoreyMutateRequired)
427                                         ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
428
429                                 ig.Emit (OpCodes.Call, ac.GetAddressMethod ());
430                         } else {
431                                 var type = IsAnonymousStoreyMutateRequired ?
432                                         CurrentAnonymousMethod.Storey.Mutator.Mutate (ac.Element) :
433                                         ac.Element;
434
435                                 ig.Emit (OpCodes.Ldelema, type.GetMetaInfo ());
436                         }
437                 }
438
439                 //
440                 // Emits the right opcode to load from an array
441                 //
442                 public void EmitArrayLoad (ArrayContainer ac)
443                 {
444                         if (ac.Rank > 1) {
445                                 if (IsAnonymousStoreyMutateRequired)
446                                         ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
447
448                                 ig.Emit (OpCodes.Call, ac.GetGetMethod ());
449                                 return;
450                         }
451
452
453                         var type = ac.Element;
454                         if (type.Kind == MemberKind.Enum)
455                                 type = EnumSpec.GetUnderlyingType (type);
456
457                         switch (type.BuiltinType) {
458                         case BuiltinTypeSpec.Type.Byte:
459                         case BuiltinTypeSpec.Type.Bool:
460                                 ig.Emit (OpCodes.Ldelem_U1);
461                                 break;
462                         case BuiltinTypeSpec.Type.SByte:
463                                 ig.Emit (OpCodes.Ldelem_I1);
464                                 break;
465                         case BuiltinTypeSpec.Type.Short:
466                                 ig.Emit (OpCodes.Ldelem_I2);
467                                 break;
468                         case BuiltinTypeSpec.Type.UShort:
469                         case BuiltinTypeSpec.Type.Char:
470                                 ig.Emit (OpCodes.Ldelem_U2);
471                                 break;
472                         case BuiltinTypeSpec.Type.Int:
473                                 ig.Emit (OpCodes.Ldelem_I4);
474                                 break;
475                         case BuiltinTypeSpec.Type.UInt:
476                                 ig.Emit (OpCodes.Ldelem_U4);
477                                 break;
478                         case BuiltinTypeSpec.Type.ULong:
479                         case BuiltinTypeSpec.Type.Long:
480                                 ig.Emit (OpCodes.Ldelem_I8);
481                                 break;
482                         case BuiltinTypeSpec.Type.Float:
483                                 ig.Emit (OpCodes.Ldelem_R4);
484                                 break;
485                         case BuiltinTypeSpec.Type.Double:
486                                 ig.Emit (OpCodes.Ldelem_R8);
487                                 break;
488                         case BuiltinTypeSpec.Type.IntPtr:
489                                 ig.Emit (OpCodes.Ldelem_I);
490                                 break;
491                         default:
492                                 switch (type.Kind) {
493                                 case MemberKind.Struct:
494                                         if (IsAnonymousStoreyMutateRequired)
495                                                 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
496
497                                         ig.Emit (OpCodes.Ldelema, type.GetMetaInfo ());
498                                         ig.Emit (OpCodes.Ldobj, type.GetMetaInfo ());
499                                         break;
500                                 case MemberKind.TypeParameter:
501                                         if (IsAnonymousStoreyMutateRequired)
502                                                 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
503
504                                         ig.Emit (OpCodes.Ldelem, type.GetMetaInfo ());
505                                         break;
506                                 case MemberKind.PointerType:
507                                         ig.Emit (OpCodes.Ldelem_I);
508                                         break;
509                                 default:
510                                         ig.Emit (OpCodes.Ldelem_Ref);
511                                         break;
512                                 }
513                                 break;
514                         }
515                 }
516
517                 //
518                 // Emits the right opcode to store to an array
519                 //
520                 public void EmitArrayStore (ArrayContainer ac)
521                 {
522                         if (ac.Rank > 1) {
523                                 if (IsAnonymousStoreyMutateRequired)
524                                         ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
525
526                                 ig.Emit (OpCodes.Call, ac.GetSetMethod ());
527                                 return;
528                         }
529
530                         var type = ac.Element;
531
532                         if (type.Kind == MemberKind.Enum)
533                                 type = EnumSpec.GetUnderlyingType (type);
534
535                         switch (type.BuiltinType) {
536                         case BuiltinTypeSpec.Type.Byte:
537                         case BuiltinTypeSpec.Type.SByte:
538                         case BuiltinTypeSpec.Type.Bool:
539                                 Emit (OpCodes.Stelem_I1);
540                                 return;
541                         case BuiltinTypeSpec.Type.Short:
542                         case BuiltinTypeSpec.Type.UShort:
543                         case BuiltinTypeSpec.Type.Char:
544                                 Emit (OpCodes.Stelem_I2);
545                                 return;
546                         case BuiltinTypeSpec.Type.Int:
547                         case BuiltinTypeSpec.Type.UInt:
548                                 Emit (OpCodes.Stelem_I4);
549                                 return;
550                         case BuiltinTypeSpec.Type.Long:
551                         case BuiltinTypeSpec.Type.ULong:
552                                 Emit (OpCodes.Stelem_I8);
553                                 return;
554                         case BuiltinTypeSpec.Type.Float:
555                                 Emit (OpCodes.Stelem_R4);
556                                 return;
557                         case BuiltinTypeSpec.Type.Double:
558                                 Emit (OpCodes.Stelem_R8);
559                                 return;
560                         }
561
562                         switch (type.Kind) {
563                         case MemberKind.Struct:
564                                 Emit (OpCodes.Stobj, type);
565                                 break;
566                         case MemberKind.TypeParameter:
567                                 Emit (OpCodes.Stelem, type);
568                                 break;
569                         case MemberKind.PointerType:
570                                 Emit (OpCodes.Stelem_I);
571                                 break;
572                         default:
573                                 Emit (OpCodes.Stelem_Ref);
574                                 break;
575                         }
576                 }
577
578                 public void EmitInt (int i)
579                 {
580                         EmitIntConstant (i);
581                 }
582
583                 void EmitIntConstant (int i)
584                 {
585                         switch (i) {
586                         case -1:
587                                 ig.Emit (OpCodes.Ldc_I4_M1);
588                                 break;
589
590                         case 0:
591                                 ig.Emit (OpCodes.Ldc_I4_0);
592                                 break;
593
594                         case 1:
595                                 ig.Emit (OpCodes.Ldc_I4_1);
596                                 break;
597
598                         case 2:
599                                 ig.Emit (OpCodes.Ldc_I4_2);
600                                 break;
601
602                         case 3:
603                                 ig.Emit (OpCodes.Ldc_I4_3);
604                                 break;
605
606                         case 4:
607                                 ig.Emit (OpCodes.Ldc_I4_4);
608                                 break;
609
610                         case 5:
611                                 ig.Emit (OpCodes.Ldc_I4_5);
612                                 break;
613
614                         case 6:
615                                 ig.Emit (OpCodes.Ldc_I4_6);
616                                 break;
617
618                         case 7:
619                                 ig.Emit (OpCodes.Ldc_I4_7);
620                                 break;
621
622                         case 8:
623                                 ig.Emit (OpCodes.Ldc_I4_8);
624                                 break;
625
626                         default:
627                                 if (i >= -128 && i <= 127) {
628                                         ig.Emit (OpCodes.Ldc_I4_S, (sbyte) i);
629                                 } else
630                                         ig.Emit (OpCodes.Ldc_I4, i);
631                                 break;
632                         }
633                 }
634
635                 public void EmitLong (long l)
636                 {
637                         if (l >= int.MinValue && l <= int.MaxValue) {
638                                 EmitIntConstant (unchecked ((int) l));
639                                 ig.Emit (OpCodes.Conv_I8);
640                         } else if (l >= 0 && l <= uint.MaxValue) {
641                                 EmitIntConstant (unchecked ((int) l));
642                                 ig.Emit (OpCodes.Conv_U8);
643                         } else {
644                                 ig.Emit (OpCodes.Ldc_I8, l);
645                         }
646                 }
647
648                 //
649                 // Load the object from the pointer.  
650                 //
651                 public void EmitLoadFromPtr (TypeSpec type)
652                 {
653                         if (type.Kind == MemberKind.Enum)
654                                 type = EnumSpec.GetUnderlyingType (type);
655
656                         switch (type.BuiltinType) {
657                         case BuiltinTypeSpec.Type.Int:
658                                 ig.Emit (OpCodes.Ldind_I4);
659                                 break;
660                         case BuiltinTypeSpec.Type.UInt:
661                                 ig.Emit (OpCodes.Ldind_U4);
662                                 break;
663                         case BuiltinTypeSpec.Type.Short:
664                                 ig.Emit (OpCodes.Ldind_I2);
665                                 break;
666                         case BuiltinTypeSpec.Type.UShort:
667                         case BuiltinTypeSpec.Type.Char:
668                                 ig.Emit (OpCodes.Ldind_U2);
669                                 break;
670                         case BuiltinTypeSpec.Type.Byte:
671                                 ig.Emit (OpCodes.Ldind_U1);
672                                 break;
673                         case BuiltinTypeSpec.Type.SByte:
674                         case BuiltinTypeSpec.Type.Bool:
675                                 ig.Emit (OpCodes.Ldind_I1);
676                                 break;
677                         case BuiltinTypeSpec.Type.ULong:
678                         case BuiltinTypeSpec.Type.Long:
679                                 ig.Emit (OpCodes.Ldind_I8);
680                                 break;
681                         case BuiltinTypeSpec.Type.Float:
682                                 ig.Emit (OpCodes.Ldind_R4);
683                                 break;
684                         case BuiltinTypeSpec.Type.Double:
685                                 ig.Emit (OpCodes.Ldind_R8);
686                                 break;
687                         case BuiltinTypeSpec.Type.IntPtr:
688                                 ig.Emit (OpCodes.Ldind_I);
689                                 break;
690                         default:
691                                 switch (type.Kind) {
692                                 case MemberKind.Struct:
693                                 case MemberKind.TypeParameter:
694                                         if (IsAnonymousStoreyMutateRequired)
695                                                 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
696
697                                         ig.Emit (OpCodes.Ldobj, type.GetMetaInfo ());
698                                         break;
699                                 case MemberKind.PointerType:
700                                         ig.Emit (OpCodes.Ldind_I);
701                                         break;
702                                 default:
703                                         ig.Emit (OpCodes.Ldind_Ref);
704                                         break;
705                                 }
706                                 break;
707                         }
708                 }
709
710                 public void EmitNull ()
711                 {
712                         ig.Emit (OpCodes.Ldnull);
713                 }
714
715                 public void EmitArgumentAddress (int pos)
716                 {
717                         if (!IsStatic)
718                                 ++pos;
719
720                         if (pos > byte.MaxValue)
721                                 ig.Emit (OpCodes.Ldarga, pos);
722                         else
723                                 ig.Emit (OpCodes.Ldarga_S, (byte) pos);
724                 }
725
726                 public void EmitArgumentLoad (int pos)
727                 {
728                         if (!IsStatic)
729                                 ++pos;
730
731                         switch (pos) {
732                         case 0: ig.Emit (OpCodes.Ldarg_0); break;
733                         case 1: ig.Emit (OpCodes.Ldarg_1); break;
734                         case 2: ig.Emit (OpCodes.Ldarg_2); break;
735                         case 3: ig.Emit (OpCodes.Ldarg_3); break;
736                         default:
737                                 if (pos > byte.MaxValue)
738                                         ig.Emit (OpCodes.Ldarg, pos);
739                                 else
740                                         ig.Emit (OpCodes.Ldarg_S, (byte) pos);
741                                 break;
742                         }
743                 }
744
745                 public void EmitArgumentStore (int pos)
746                 {
747                         if (!IsStatic)
748                                 ++pos;
749
750                         if (pos > byte.MaxValue)
751                                 ig.Emit (OpCodes.Starg, pos);
752                         else
753                                 ig.Emit (OpCodes.Starg_S, (byte) pos);
754                 }
755
756                 //
757                 // The stack contains the pointer and the value of type `type'
758                 //
759                 public void EmitStoreFromPtr (TypeSpec type)
760                 {
761                         if (type.IsEnum)
762                                 type = EnumSpec.GetUnderlyingType (type);
763
764                         switch (type.BuiltinType) {
765                         case BuiltinTypeSpec.Type.Int:
766                         case BuiltinTypeSpec.Type.UInt:
767                                 ig.Emit (OpCodes.Stind_I4);
768                                 return;
769                         case BuiltinTypeSpec.Type.Long:
770                         case BuiltinTypeSpec.Type.ULong:
771                                 ig.Emit (OpCodes.Stind_I8);
772                                 return;
773                         case BuiltinTypeSpec.Type.Char:
774                         case BuiltinTypeSpec.Type.Short:
775                         case BuiltinTypeSpec.Type.UShort:
776                                 ig.Emit (OpCodes.Stind_I2);
777                                 return;
778                         case BuiltinTypeSpec.Type.Float:
779                                 ig.Emit (OpCodes.Stind_R4);
780                                 return;
781                         case BuiltinTypeSpec.Type.Double:
782                                 ig.Emit (OpCodes.Stind_R8);
783                                 return;
784                         case BuiltinTypeSpec.Type.Byte:
785                         case BuiltinTypeSpec.Type.SByte:
786                         case BuiltinTypeSpec.Type.Bool:
787                                 ig.Emit (OpCodes.Stind_I1);
788                                 return;
789                         case BuiltinTypeSpec.Type.IntPtr:
790                                 ig.Emit (OpCodes.Stind_I);
791                                 return;
792                         }
793
794                         switch (type.Kind) {
795                         case MemberKind.Struct:
796                         case MemberKind.TypeParameter:
797                                 if (IsAnonymousStoreyMutateRequired)
798                                         type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
799
800                                 ig.Emit (OpCodes.Stobj, type.GetMetaInfo ());
801                                 break;
802                         default:
803                                 ig.Emit (OpCodes.Stind_Ref);
804                                 break;
805                         }
806                 }
807
808                 public void EmitThis ()
809                 {
810                         ig.Emit (OpCodes.Ldarg_0);
811                 }
812
813                 /// <summary>
814                 ///   Returns a temporary storage for a variable of type t as 
815                 ///   a local variable in the current body.
816                 /// </summary>
817                 public LocalBuilder GetTemporaryLocal (TypeSpec t)
818                 {
819                         if (temporary_storage != null) {
820                                 object o;
821                                 if (temporary_storage.TryGetValue (t, out o)) {
822                                         if (o is Stack<LocalBuilder>) {
823                                                 var s = (Stack<LocalBuilder>) o;
824                                                 o = s.Count == 0 ? null : s.Pop ();
825                                         } else {
826                                                 temporary_storage.Remove (t);
827                                         }
828                                 }
829                                 if (o != null)
830                                         return (LocalBuilder) o;
831                         }
832                         return DeclareLocal (t, false);
833                 }
834
835                 public void FreeTemporaryLocal (LocalBuilder b, TypeSpec t)
836                 {
837                         if (temporary_storage == null) {
838                                 temporary_storage = new Dictionary<TypeSpec, object> (ReferenceEquality<TypeSpec>.Default);
839                                 temporary_storage.Add (t, b);
840                                 return;
841                         }
842                         object o;
843                         
844                         if (!temporary_storage.TryGetValue (t, out o)) {
845                                 temporary_storage.Add (t, b);
846                                 return;
847                         }
848                         var s = o as Stack<LocalBuilder>;
849                         if (s == null) {
850                                 s = new Stack<LocalBuilder> ();
851                                 s.Push ((LocalBuilder)o);
852                                 temporary_storage [t] = s;
853                         }
854                         s.Push (b);
855                 }
856
857                 /// <summary>
858                 ///   ReturnValue creates on demand the LocalBuilder for the
859                 ///   return value from the function.  By default this is not
860                 ///   used.  This is only required when returns are found inside
861                 ///   Try or Catch statements.
862                 ///
863                 ///   This method is typically invoked from the Emit phase, so
864                 ///   we allow the creation of a return label if it was not
865                 ///   requested during the resolution phase.   Could be cleaned
866                 ///   up, but it would replicate a lot of logic in the Emit phase
867                 ///   of the code that uses it.
868                 /// </summary>
869                 public LocalBuilder TemporaryReturn ()
870                 {
871                         if (return_value == null){
872                                 return_value = DeclareLocal (return_type, false);
873                         }
874
875                         return return_value;
876                 }
877         }
878
879         struct CallEmitter
880         {
881                 public Expression InstanceExpression;
882
883                 //
884                 // When set leaves an extra copy of all arguments on the stack
885                 //
886                 public bool DuplicateArguments;
887
888                 //
889                 // Does not emit InstanceExpression load when InstanceExpressionOnStack
890                 // is set. Used by compound assignments.
891                 //
892                 public bool InstanceExpressionOnStack;
893
894                 //
895                 // Any of arguments contains await expression
896                 //
897                 public bool HasAwaitArguments;
898
899                 //
900                 // When dealing with await arguments the original arguments are converted
901                 // into a new set with hoisted stack results
902                 //
903                 public Arguments EmittedArguments;
904
905                 public void Emit (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc)
906                 {
907                         // Speed up the check by not doing it on not allowed targets
908                         if (method.ReturnType.Kind == MemberKind.Void && method.IsConditionallyExcluded (ec.MemberContext, loc))
909                                 return;
910
911                         EmitPredefined (ec, method, Arguments);
912                 }
913
914                 public void EmitPredefined (EmitContext ec, MethodSpec method, Arguments Arguments)
915                 {
916                         Expression instance_copy = null;
917
918                         if (!HasAwaitArguments && ec.HasSet (BuilderContext.Options.AsyncBody)) {
919                                 HasAwaitArguments = Arguments != null && Arguments.ContainsEmitWithAwait ();
920                                 if (HasAwaitArguments && InstanceExpressionOnStack) {
921                                         throw new NotSupportedException ();
922                                 }
923                         }
924
925                         OpCode call_op;
926                         LocalTemporary lt = null;
927
928                         if (method.IsStatic) {
929                                 call_op = OpCodes.Call;
930                         } else {
931                                 if (IsVirtualCallRequired (InstanceExpression, method)) {
932                                         call_op = OpCodes.Callvirt;
933                                 } else {
934                                         call_op = OpCodes.Call;
935                                 }
936
937                                 if (HasAwaitArguments) {
938                                         instance_copy = InstanceExpression.EmitToField (ec);
939                                         if (Arguments == null)
940                                                 EmitCallInstance (ec, instance_copy, method.DeclaringType, call_op);
941                                 } else if (!InstanceExpressionOnStack) {
942                                         var instance_on_stack_type = EmitCallInstance (ec, InstanceExpression, method.DeclaringType, call_op);
943
944                                         if (DuplicateArguments) {
945                                                 ec.Emit (OpCodes.Dup);
946                                                 if (Arguments != null && Arguments.Count != 0) {
947                                                         lt = new LocalTemporary (instance_on_stack_type);
948                                                         lt.Store (ec);
949                                                         instance_copy = lt;
950                                                 }
951                                         }
952                                 }
953                         }
954
955                         if (Arguments != null && !InstanceExpressionOnStack) {
956                                 EmittedArguments = Arguments.Emit (ec, DuplicateArguments, HasAwaitArguments);
957                                 if (EmittedArguments != null) {
958                                         if (instance_copy != null) {
959                                                 EmitCallInstance (ec, instance_copy, method.DeclaringType, call_op);
960
961                                                 if (lt != null)
962                                                         lt.Release (ec);
963                                         }
964
965                                         EmittedArguments.Emit (ec);
966                                 }
967                         }
968
969                         if (call_op == OpCodes.Callvirt && (InstanceExpression.Type.IsGenericParameter || InstanceExpression.Type.IsStruct)) {
970                                 ec.Emit (OpCodes.Constrained, InstanceExpression.Type);
971                         }
972
973                         //
974                         // Set instance expression to actual result expression. When it contains await it can be
975                         // picked up by caller
976                         //
977                         InstanceExpression = instance_copy;
978
979                         if (method.Parameters.HasArglist) {
980                                 var varargs_types = GetVarargsTypes (method, Arguments);
981                                 ec.Emit (call_op, method, varargs_types);
982                                 return;
983                         }
984
985                         //
986                         // If you have:
987                         // this.DoFoo ();
988                         // and DoFoo is not virtual, you can omit the callvirt,
989                         // because you don't need the null checking behavior.
990                         //
991                         ec.Emit (call_op, method);
992                 }
993
994                 static TypeSpec EmitCallInstance (EmitContext ec, Expression instance, TypeSpec declaringType, OpCode callOpcode)
995                 {
996                         var instance_type = instance.Type;
997
998                         //
999                         // Push the instance expression
1000                         //
1001                         if ((instance_type.IsStruct && (callOpcode == OpCodes.Callvirt || (callOpcode == OpCodes.Call && declaringType.IsStruct))) ||
1002                                 instance_type.IsGenericParameter || declaringType.IsNullableType) {
1003                                 //
1004                                 // If the expression implements IMemoryLocation, then
1005                                 // we can optimize and use AddressOf on the
1006                                 // return.
1007                                 //
1008                                 // If not we have to use some temporary storage for
1009                                 // it.
1010                                 var iml = instance as IMemoryLocation;
1011                                 if (iml != null) {
1012                                         iml.AddressOf (ec, AddressOp.Load);
1013                                 } else {
1014                                         LocalTemporary temp = new LocalTemporary (instance_type);
1015                                         instance.Emit (ec);
1016                                         temp.Store (ec);
1017                                         temp.AddressOf (ec, AddressOp.Load);
1018                                 }
1019
1020                                 return ReferenceContainer.MakeType (ec.Module, instance_type);
1021                         }
1022
1023                         if (instance_type.IsEnum || instance_type.IsStruct) {
1024                                 instance.Emit (ec);
1025                                 ec.Emit (OpCodes.Box, instance_type);
1026                                 return ec.BuiltinTypes.Object;
1027                         }
1028
1029                         instance.Emit (ec);
1030                         return instance_type;
1031                 }
1032
1033                 static MetaType[] GetVarargsTypes (MethodSpec method, Arguments arguments)
1034                 {
1035                         AParametersCollection pd = method.Parameters;
1036
1037                         Argument a = arguments[pd.Count - 1];
1038                         Arglist list = (Arglist) a.Expr;
1039
1040                         return list.ArgumentTypes;
1041                 }
1042
1043                 //
1044                 // Used to decide whether call or callvirt is needed
1045                 //
1046                 static bool IsVirtualCallRequired (Expression instance, MethodSpec method)
1047                 {
1048                         //
1049                         // There are 2 scenarious where we emit callvirt
1050                         //
1051                         // Case 1: A method is virtual and it's not used to call base
1052                         // Case 2: A method instance expression can be null. In this casen callvirt ensures
1053                         // correct NRE exception when the method is called
1054                         //
1055                         var decl_type = method.DeclaringType;
1056                         if (decl_type.IsStruct || decl_type.IsEnum)
1057                                 return false;
1058
1059                         if (instance is BaseThis)
1060                                 return false;
1061
1062                         //
1063                         // It's non-virtual and will never be null
1064                         //
1065                         if (!method.IsVirtual && (instance is This || instance is New || instance is ArrayCreation || instance is DelegateCreation))
1066                                 return false;
1067
1068                         return true;
1069                 }
1070         }
1071 }