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