[counters] Fix file handle leak in cpu_load ()
[mono-project.git] / mcs / mcs / codegen.cs
blob6dfe7b6ce6b6e47574e132822483c1b3b2e099af
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
13 using System;
14 using System.Collections.Generic;
15 using Mono.CompilerServices.SymbolWriter;
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
27 namespace Mono.CSharp
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
35 // TODO: Has to be private
36 public readonly ILGenerator ig;
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;
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;
51 /// <summary>
52 /// The location where we store the return value.
53 /// </summary>
54 public LocalBuilder return_value;
57 /// <summary>
58 /// Current loop begin and end labels.
59 /// </summary>
60 public Label LoopBegin, LoopEnd;
62 /// <summary>
63 /// Default target in a switch statement. Only valid if
64 /// InSwitch is true
65 /// </summary>
66 public Label DefaultTarget;
68 /// <summary>
69 /// If this is non-null, points to the current switch statement
70 /// </summary>
71 public Switch Switch;
73 /// <summary>
74 /// Whether we are inside an anonymous method.
75 /// </summary>
76 public AnonymousExpression CurrentAnonymousMethod;
78 readonly IMemberContext member_context;
80 readonly SourceMethodBuilder methodSymbols;
82 DynamicSiteClass dynamic_site_container;
84 Label? return_label;
86 List<IExpressionCleanup> epilogue_expressions;
88 public EmitContext (IMemberContext rc, ILGenerator ig, TypeSpec return_type, SourceMethodBuilder methodSymbols)
90 this.member_context = rc;
91 this.ig = ig;
92 this.return_type = return_type;
94 if (rc.Module.Compiler.Settings.Checked)
95 flags |= Options.CheckedScope;
97 if (methodSymbols != null) {
98 this.methodSymbols = methodSymbols;
99 if (!rc.Module.Compiler.Settings.Optimize)
100 flags |= Options.AccurateDebugInfo;
101 } else {
102 flags |= Options.OmitDebugInfo;
105 #if STATIC
106 ig.__CleverExceptionBlockAssistance ();
107 #endif
110 #region Properties
112 internal AsyncTaskStorey AsyncTaskStorey {
113 get {
114 return CurrentAnonymousMethod.Storey as AsyncTaskStorey;
118 public BuiltinTypes BuiltinTypes {
119 get {
120 return MemberContext.Module.Compiler.BuiltinTypes;
124 public ConditionalAccessContext ConditionalAccess { get; set; }
126 public TypeSpec CurrentType {
127 get { return member_context.CurrentType; }
130 public TypeParameters CurrentTypeParameters {
131 get { return member_context.CurrentTypeParameters; }
134 public MemberCore CurrentTypeDefinition {
135 get { return member_context.CurrentMemberDefinition; }
138 public bool EmitAccurateDebugInfo {
139 get {
140 return (flags & Options.AccurateDebugInfo) != 0;
144 public bool HasMethodSymbolBuilder {
145 get {
146 return methodSymbols != null;
150 public bool HasReturnLabel {
151 get {
152 return return_label.HasValue;
156 public bool IsStatic {
157 get { return member_context.IsStatic; }
160 public bool IsStaticConstructor {
161 get {
162 return member_context.IsStatic && (flags & Options.ConstructorScope) != 0;
166 public bool IsAnonymousStoreyMutateRequired {
167 get {
168 return CurrentAnonymousMethod != null &&
169 CurrentAnonymousMethod.Storey != null &&
170 CurrentAnonymousMethod.Storey.Mutator != null;
174 public IMemberContext MemberContext {
175 get {
176 return member_context;
180 public ModuleContainer Module {
181 get {
182 return member_context.Module;
186 public bool NotifyEvaluatorOnStore {
187 get {
188 return Module.Evaluator != null && Module.Evaluator.ModificationListener != null;
192 // Has to be used for specific emitter errors only any
193 // possible resolver errors have to be reported during Resolve
194 public Report Report {
195 get {
196 return member_context.Module.Compiler.Report;
200 public TypeSpec ReturnType {
201 get {
202 return return_type;
207 // The label where we have to jump before leaving the context
209 public Label ReturnLabel {
210 get {
211 return return_label.Value;
215 public List<IExpressionCleanup> StatementEpilogue {
216 get {
217 return epilogue_expressions;
221 public LocalVariable AsyncThrowVariable { get; set; }
223 public List<TryFinally> TryFinallyUnwind { get; set; }
225 public Label RecursivePatternLabel { get; set; }
227 #endregion
229 public void AddStatementEpilog (IExpressionCleanup cleanupExpression)
231 if (epilogue_expressions == null) {
232 epilogue_expressions = new List<IExpressionCleanup> ();
233 } else if (epilogue_expressions.Contains (cleanupExpression)) {
234 return;
237 epilogue_expressions.Add (cleanupExpression);
240 public void AssertEmptyStack ()
242 #if STATIC
243 if (ig.__StackHeight != 0)
244 throw new InternalErrorException ("Await yields with non-empty stack in `{0}",
245 member_context.GetSignatureForError ());
246 #endif
249 /// <summary>
250 /// This is called immediately before emitting an IL opcode to tell the symbol
251 /// writer to which source line this opcode belongs.
252 /// </summary>
253 public bool Mark (Location loc)
255 if ((flags & Options.OmitDebugInfo) != 0)
256 return false;
258 if (loc.IsNull || methodSymbols == null)
259 return false;
261 var sf = loc.SourceFile;
262 if (sf.IsHiddenLocation (loc))
263 return false;
265 #if NET_4_0
266 methodSymbols.MarkSequencePoint (ig.ILOffset, sf.SourceFileEntry, loc.Row, loc.Column, false);
267 #endif
268 return true;
271 public void MarkCallEntry (Location loc)
273 if (!EmitAccurateDebugInfo)
274 return;
277 // TODO: This should emit different kind of sequence point to make
278 // step-over work for statement over multiple lines
280 // Debugging experience for Foo (A () + B ()) where A and B are
281 // on separate lines is not great
283 Mark (loc);
286 public void DefineLocalVariable (string name, LocalBuilder builder)
288 if ((flags & Options.OmitDebugInfo) != 0)
289 return;
291 methodSymbols.AddLocal (builder.LocalIndex, name);
294 public void BeginCatchBlock (TypeSpec type)
296 if (IsAnonymousStoreyMutateRequired)
297 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
299 ig.BeginCatchBlock (type.GetMetaInfo ());
302 public void BeginFilterHandler ()
304 ig.BeginCatchBlock (null);
307 public void BeginExceptionBlock ()
309 ig.BeginExceptionBlock ();
312 public void BeginExceptionFilterBlock ()
314 ig.BeginExceptFilterBlock ();
317 public void BeginFinallyBlock ()
319 ig.BeginFinallyBlock ();
322 public void BeginScope ()
324 if ((flags & Options.OmitDebugInfo) != 0)
325 return;
327 #if NET_4_0
328 methodSymbols.StartBlock (CodeBlockEntry.Type.Lexical, ig.ILOffset);
329 #endif
332 public void BeginCompilerScope ()
334 if ((flags & Options.OmitDebugInfo) != 0)
335 return;
337 #if NET_4_0
338 methodSymbols.StartBlock (CodeBlockEntry.Type.CompilerGenerated, ig.ILOffset);
339 #endif
342 public void EndExceptionBlock ()
344 ig.EndExceptionBlock ();
347 public void EndScope ()
349 if ((flags & Options.OmitDebugInfo) != 0)
350 return;
352 #if NET_4_0
353 methodSymbols.EndBlock (ig.ILOffset);
354 #endif
357 public void CloseConditionalAccess (TypeSpec type)
359 if (type != null)
360 Emit (OpCodes.Newobj, Nullable.NullableInfo.GetConstructor (type));
362 MarkLabel (ConditionalAccess.EndLabel);
363 ConditionalAccess = null;
367 // Creates a nested container in this context for all dynamic compiler generated stuff
369 internal DynamicSiteClass CreateDynamicSite ()
371 if (dynamic_site_container == null) {
372 var mc = member_context.CurrentMemberDefinition as MemberBase;
373 dynamic_site_container = new DynamicSiteClass (CurrentTypeDefinition.Parent.PartialContainer, mc, member_context.CurrentTypeParameters);
375 CurrentTypeDefinition.Module.AddCompilerGeneratedClass (dynamic_site_container);
376 dynamic_site_container.CreateContainer ();
377 dynamic_site_container.DefineContainer ();
378 dynamic_site_container.Define ();
380 var inflator = new TypeParameterInflator (Module, CurrentType, TypeParameterSpec.EmptyTypes, TypeSpec.EmptyTypes);
381 var inflated = dynamic_site_container.CurrentType.InflateMember (inflator);
382 CurrentType.MemberCache.AddMember (inflated);
385 return dynamic_site_container;
388 public Label CreateReturnLabel ()
390 if (!return_label.HasValue)
391 return_label = DefineLabel ();
393 return return_label.Value;
396 public LocalBuilder DeclareLocal (TypeSpec type, bool pinned)
398 if (IsAnonymousStoreyMutateRequired)
399 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
401 return ig.DeclareLocal (type.GetMetaInfo (), pinned);
404 public Label DefineLabel ()
406 return ig.DefineLabel ();
410 // Creates temporary field in current async storey
412 public StackFieldExpr GetTemporaryField (TypeSpec type, bool initializedFieldRequired = false)
414 var f = AsyncTaskStorey.AddCapturedLocalVariable (type, initializedFieldRequired);
415 var fexpr = new StackFieldExpr (f);
416 fexpr.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location.Null);
417 return fexpr;
420 public void MarkLabel (Label label)
422 ig.MarkLabel (label);
425 public void Emit (OpCode opcode)
427 ig.Emit (opcode);
430 public void Emit (OpCode opcode, LocalBuilder local)
432 ig.Emit (opcode, local);
435 public void Emit (OpCode opcode, string arg)
437 ig.Emit (opcode, arg);
440 public void Emit (OpCode opcode, double arg)
442 ig.Emit (opcode, arg);
445 public void Emit (OpCode opcode, float arg)
447 ig.Emit (opcode, arg);
450 public void Emit (OpCode opcode, Label label)
452 ig.Emit (opcode, label);
455 public void Emit (OpCode opcode, Label[] labels)
457 ig.Emit (opcode, labels);
460 public void Emit (OpCode opcode, TypeSpec type)
462 if (IsAnonymousStoreyMutateRequired)
463 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
465 ig.Emit (opcode, type.GetMetaInfo ());
468 public void Emit (OpCode opcode, FieldSpec field)
470 if (IsAnonymousStoreyMutateRequired)
471 field = field.Mutate (CurrentAnonymousMethod.Storey.Mutator);
473 ig.Emit (opcode, field.GetMetaInfo ());
476 public void Emit (OpCode opcode, MethodSpec method)
478 if (IsAnonymousStoreyMutateRequired)
479 method = method.Mutate (CurrentAnonymousMethod.Storey.Mutator);
481 if (method.IsConstructor)
482 ig.Emit (opcode, (ConstructorInfo) method.GetMetaInfo ());
483 else
484 ig.Emit (opcode, (MethodInfo) method.GetMetaInfo ());
487 // TODO: REMOVE breaks mutator
488 public void Emit (OpCode opcode, MethodInfo method)
490 ig.Emit (opcode, method);
493 public void Emit (OpCode opcode, MethodSpec method, MetaType[] vargs)
495 // TODO MemberCache: This should mutate too
496 ig.EmitCall (opcode, (MethodInfo) method.GetMetaInfo (), vargs);
499 public void EmitArrayNew (ArrayContainer ac)
501 if (ac.Rank == 1) {
502 var type = IsAnonymousStoreyMutateRequired ?
503 CurrentAnonymousMethod.Storey.Mutator.Mutate (ac.Element) :
504 ac.Element;
506 ig.Emit (OpCodes.Newarr, type.GetMetaInfo ());
507 } else {
508 if (IsAnonymousStoreyMutateRequired)
509 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
511 ig.Emit (OpCodes.Newobj, ac.GetConstructor ());
515 public void EmitArrayAddress (ArrayContainer ac)
517 if (ac.Rank > 1) {
518 if (IsAnonymousStoreyMutateRequired)
519 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
521 ig.Emit (OpCodes.Call, ac.GetAddressMethod ());
522 } else {
523 var type = IsAnonymousStoreyMutateRequired ?
524 CurrentAnonymousMethod.Storey.Mutator.Mutate (ac.Element) :
525 ac.Element;
527 ig.Emit (OpCodes.Ldelema, type.GetMetaInfo ());
532 // Emits the right opcode to load from an array
534 public void EmitArrayLoad (ArrayContainer ac)
536 if (ac.Rank > 1) {
537 if (IsAnonymousStoreyMutateRequired)
538 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
540 ig.Emit (OpCodes.Call, ac.GetGetMethod ());
541 return;
545 var type = ac.Element;
546 if (type.Kind == MemberKind.Enum)
547 type = EnumSpec.GetUnderlyingType (type);
549 switch (type.BuiltinType) {
550 case BuiltinTypeSpec.Type.Bool:
552 // bool array can actually store any byte value in underlying byte slot
553 // and C# spec does not specify any normalization rule, except the result
554 // is undefined
556 case BuiltinTypeSpec.Type.Byte:
557 ig.Emit (OpCodes.Ldelem_U1);
558 break;
559 case BuiltinTypeSpec.Type.SByte:
560 ig.Emit (OpCodes.Ldelem_I1);
561 break;
562 case BuiltinTypeSpec.Type.Short:
563 ig.Emit (OpCodes.Ldelem_I2);
564 break;
565 case BuiltinTypeSpec.Type.UShort:
566 case BuiltinTypeSpec.Type.Char:
567 ig.Emit (OpCodes.Ldelem_U2);
568 break;
569 case BuiltinTypeSpec.Type.Int:
570 ig.Emit (OpCodes.Ldelem_I4);
571 break;
572 case BuiltinTypeSpec.Type.UInt:
573 ig.Emit (OpCodes.Ldelem_U4);
574 break;
575 case BuiltinTypeSpec.Type.ULong:
576 case BuiltinTypeSpec.Type.Long:
577 ig.Emit (OpCodes.Ldelem_I8);
578 break;
579 case BuiltinTypeSpec.Type.Float:
580 ig.Emit (OpCodes.Ldelem_R4);
581 break;
582 case BuiltinTypeSpec.Type.Double:
583 ig.Emit (OpCodes.Ldelem_R8);
584 break;
585 case BuiltinTypeSpec.Type.IntPtr:
586 ig.Emit (OpCodes.Ldelem_I);
587 break;
588 default:
589 switch (type.Kind) {
590 case MemberKind.Struct:
591 if (IsAnonymousStoreyMutateRequired)
592 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
594 ig.Emit (OpCodes.Ldelema, type.GetMetaInfo ());
595 ig.Emit (OpCodes.Ldobj, type.GetMetaInfo ());
596 break;
597 case MemberKind.TypeParameter:
598 if (IsAnonymousStoreyMutateRequired)
599 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
601 ig.Emit (OpCodes.Ldelem, type.GetMetaInfo ());
602 break;
603 case MemberKind.PointerType:
604 ig.Emit (OpCodes.Ldelem_I);
605 break;
606 default:
607 ig.Emit (OpCodes.Ldelem_Ref);
608 break;
610 break;
615 // Emits the right opcode to store to an array
617 public void EmitArrayStore (ArrayContainer ac)
619 if (ac.Rank > 1) {
620 if (IsAnonymousStoreyMutateRequired)
621 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
623 ig.Emit (OpCodes.Call, ac.GetSetMethod ());
624 return;
627 var type = ac.Element;
629 if (type.Kind == MemberKind.Enum)
630 type = EnumSpec.GetUnderlyingType (type);
632 switch (type.BuiltinType) {
633 case BuiltinTypeSpec.Type.Byte:
634 case BuiltinTypeSpec.Type.SByte:
635 case BuiltinTypeSpec.Type.Bool:
636 Emit (OpCodes.Stelem_I1);
637 return;
638 case BuiltinTypeSpec.Type.Short:
639 case BuiltinTypeSpec.Type.UShort:
640 case BuiltinTypeSpec.Type.Char:
641 Emit (OpCodes.Stelem_I2);
642 return;
643 case BuiltinTypeSpec.Type.Int:
644 case BuiltinTypeSpec.Type.UInt:
645 Emit (OpCodes.Stelem_I4);
646 return;
647 case BuiltinTypeSpec.Type.Long:
648 case BuiltinTypeSpec.Type.ULong:
649 Emit (OpCodes.Stelem_I8);
650 return;
651 case BuiltinTypeSpec.Type.Float:
652 Emit (OpCodes.Stelem_R4);
653 return;
654 case BuiltinTypeSpec.Type.Double:
655 Emit (OpCodes.Stelem_R8);
656 return;
659 switch (type.Kind) {
660 case MemberKind.Struct:
661 Emit (OpCodes.Stobj, type);
662 break;
663 case MemberKind.TypeParameter:
664 Emit (OpCodes.Stelem, type);
665 break;
666 case MemberKind.PointerType:
667 Emit (OpCodes.Stelem_I);
668 break;
669 default:
670 Emit (OpCodes.Stelem_Ref);
671 break;
675 public void EmitInt (int i)
677 EmitIntConstant (i);
680 void EmitIntConstant (int i)
682 switch (i) {
683 case -1:
684 ig.Emit (OpCodes.Ldc_I4_M1);
685 break;
687 case 0:
688 ig.Emit (OpCodes.Ldc_I4_0);
689 break;
691 case 1:
692 ig.Emit (OpCodes.Ldc_I4_1);
693 break;
695 case 2:
696 ig.Emit (OpCodes.Ldc_I4_2);
697 break;
699 case 3:
700 ig.Emit (OpCodes.Ldc_I4_3);
701 break;
703 case 4:
704 ig.Emit (OpCodes.Ldc_I4_4);
705 break;
707 case 5:
708 ig.Emit (OpCodes.Ldc_I4_5);
709 break;
711 case 6:
712 ig.Emit (OpCodes.Ldc_I4_6);
713 break;
715 case 7:
716 ig.Emit (OpCodes.Ldc_I4_7);
717 break;
719 case 8:
720 ig.Emit (OpCodes.Ldc_I4_8);
721 break;
723 default:
724 if (i >= -128 && i <= 127) {
725 ig.Emit (OpCodes.Ldc_I4_S, (sbyte) i);
726 } else
727 ig.Emit (OpCodes.Ldc_I4, i);
728 break;
732 public void EmitLong (long l)
734 if (l >= int.MinValue && l <= int.MaxValue) {
735 EmitIntConstant (unchecked ((int) l));
736 ig.Emit (OpCodes.Conv_I8);
737 } else if (l >= 0 && l <= uint.MaxValue) {
738 EmitIntConstant (unchecked ((int) l));
739 ig.Emit (OpCodes.Conv_U8);
740 } else {
741 ig.Emit (OpCodes.Ldc_I8, l);
746 // Load the object from the pointer.
748 public void EmitLoadFromPtr (TypeSpec type)
750 if (type.Kind == MemberKind.Enum)
751 type = EnumSpec.GetUnderlyingType (type);
753 switch (type.BuiltinType) {
754 case BuiltinTypeSpec.Type.Int:
755 ig.Emit (OpCodes.Ldind_I4);
756 break;
757 case BuiltinTypeSpec.Type.UInt:
758 ig.Emit (OpCodes.Ldind_U4);
759 break;
760 case BuiltinTypeSpec.Type.Short:
761 ig.Emit (OpCodes.Ldind_I2);
762 break;
763 case BuiltinTypeSpec.Type.UShort:
764 case BuiltinTypeSpec.Type.Char:
765 ig.Emit (OpCodes.Ldind_U2);
766 break;
767 case BuiltinTypeSpec.Type.Byte:
768 ig.Emit (OpCodes.Ldind_U1);
769 break;
770 case BuiltinTypeSpec.Type.SByte:
771 case BuiltinTypeSpec.Type.Bool:
772 ig.Emit (OpCodes.Ldind_I1);
773 break;
774 case BuiltinTypeSpec.Type.ULong:
775 case BuiltinTypeSpec.Type.Long:
776 ig.Emit (OpCodes.Ldind_I8);
777 break;
778 case BuiltinTypeSpec.Type.Float:
779 ig.Emit (OpCodes.Ldind_R4);
780 break;
781 case BuiltinTypeSpec.Type.Double:
782 ig.Emit (OpCodes.Ldind_R8);
783 break;
784 case BuiltinTypeSpec.Type.IntPtr:
785 ig.Emit (OpCodes.Ldind_I);
786 break;
787 default:
788 switch (type.Kind) {
789 case MemberKind.Struct:
790 case MemberKind.TypeParameter:
791 if (IsAnonymousStoreyMutateRequired)
792 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
794 ig.Emit (OpCodes.Ldobj, type.GetMetaInfo ());
795 break;
796 case MemberKind.PointerType:
797 ig.Emit (OpCodes.Ldind_I);
798 break;
799 default:
800 ig.Emit (OpCodes.Ldind_Ref);
801 break;
803 break;
807 public void EmitNull ()
809 ig.Emit (OpCodes.Ldnull);
812 public void EmitArgumentAddress (int pos)
814 if (!IsStatic)
815 ++pos;
817 if (pos > byte.MaxValue)
818 ig.Emit (OpCodes.Ldarga, pos);
819 else
820 ig.Emit (OpCodes.Ldarga_S, (byte) pos);
823 public void EmitArgumentLoad (int pos)
825 if (!IsStatic)
826 ++pos;
828 switch (pos) {
829 case 0: ig.Emit (OpCodes.Ldarg_0); break;
830 case 1: ig.Emit (OpCodes.Ldarg_1); break;
831 case 2: ig.Emit (OpCodes.Ldarg_2); break;
832 case 3: ig.Emit (OpCodes.Ldarg_3); break;
833 default:
834 if (pos > byte.MaxValue)
835 ig.Emit (OpCodes.Ldarg, pos);
836 else
837 ig.Emit (OpCodes.Ldarg_S, (byte) pos);
838 break;
842 public void EmitArgumentStore (int pos)
844 if (!IsStatic)
845 ++pos;
847 if (pos > byte.MaxValue)
848 ig.Emit (OpCodes.Starg, pos);
849 else
850 ig.Emit (OpCodes.Starg_S, (byte) pos);
854 // The stack contains the pointer and the value of type `type'
856 public void EmitStoreFromPtr (TypeSpec type)
858 if (type.IsEnum)
859 type = EnumSpec.GetUnderlyingType (type);
861 switch (type.BuiltinType) {
862 case BuiltinTypeSpec.Type.Int:
863 case BuiltinTypeSpec.Type.UInt:
864 ig.Emit (OpCodes.Stind_I4);
865 return;
866 case BuiltinTypeSpec.Type.Long:
867 case BuiltinTypeSpec.Type.ULong:
868 ig.Emit (OpCodes.Stind_I8);
869 return;
870 case BuiltinTypeSpec.Type.Char:
871 case BuiltinTypeSpec.Type.Short:
872 case BuiltinTypeSpec.Type.UShort:
873 ig.Emit (OpCodes.Stind_I2);
874 return;
875 case BuiltinTypeSpec.Type.Float:
876 ig.Emit (OpCodes.Stind_R4);
877 return;
878 case BuiltinTypeSpec.Type.Double:
879 ig.Emit (OpCodes.Stind_R8);
880 return;
881 case BuiltinTypeSpec.Type.Byte:
882 case BuiltinTypeSpec.Type.SByte:
883 case BuiltinTypeSpec.Type.Bool:
884 ig.Emit (OpCodes.Stind_I1);
885 return;
886 case BuiltinTypeSpec.Type.IntPtr:
887 ig.Emit (OpCodes.Stind_I);
888 return;
891 switch (type.Kind) {
892 case MemberKind.Struct:
893 case MemberKind.TypeParameter:
894 if (IsAnonymousStoreyMutateRequired)
895 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
897 ig.Emit (OpCodes.Stobj, type.GetMetaInfo ());
898 break;
899 default:
900 ig.Emit (OpCodes.Stind_Ref);
901 break;
905 public void EmitThis ()
907 ig.Emit (OpCodes.Ldarg_0);
910 public void EmitEpilogue ()
912 if (epilogue_expressions == null)
913 return;
915 foreach (var e in epilogue_expressions)
916 e.EmitCleanup (this);
918 epilogue_expressions = null;
921 /// <summary>
922 /// Returns a temporary storage for a variable of type t as
923 /// a local variable in the current body.
924 /// </summary>
925 public LocalBuilder GetTemporaryLocal (TypeSpec t)
927 if (temporary_storage != null) {
928 object o;
929 if (temporary_storage.TryGetValue (t, out o)) {
930 if (o is Stack<LocalBuilder>) {
931 var s = (Stack<LocalBuilder>) o;
932 o = s.Count == 0 ? null : s.Pop ();
933 } else {
934 temporary_storage.Remove (t);
937 if (o != null)
938 return (LocalBuilder) o;
940 return DeclareLocal (t, false);
943 public void FreeTemporaryLocal (LocalBuilder b, TypeSpec t)
945 if (temporary_storage == null) {
946 temporary_storage = new Dictionary<TypeSpec, object> (ReferenceEquality<TypeSpec>.Default);
947 temporary_storage.Add (t, b);
948 return;
950 object o;
952 if (!temporary_storage.TryGetValue (t, out o)) {
953 temporary_storage.Add (t, b);
954 return;
956 var s = o as Stack<LocalBuilder>;
957 if (s == null) {
958 s = new Stack<LocalBuilder> ();
959 s.Push ((LocalBuilder)o);
960 temporary_storage [t] = s;
962 s.Push (b);
965 /// <summary>
966 /// ReturnValue creates on demand the LocalBuilder for the
967 /// return value from the function. By default this is not
968 /// used. This is only required when returns are found inside
969 /// Try or Catch statements.
971 /// This method is typically invoked from the Emit phase, so
972 /// we allow the creation of a return label if it was not
973 /// requested during the resolution phase. Could be cleaned
974 /// up, but it would replicate a lot of logic in the Emit phase
975 /// of the code that uses it.
976 /// </summary>
977 public LocalBuilder TemporaryReturn ()
979 if (return_value == null){
980 return_value = DeclareLocal (return_type, false);
983 return return_value;
987 public class ConditionalAccessContext
989 public ConditionalAccessContext (TypeSpec type, Label endLabel)
991 Type = type;
992 EndLabel = endLabel;
995 public bool Statement { get; set; }
996 public Label EndLabel { get; private set; }
997 public TypeSpec Type { get; private set; }
1000 struct CallEmitter
1002 public Expression InstanceExpression;
1005 // When call has to leave an extra copy of all arguments on the stack
1007 public bool DuplicateArguments;
1010 // Does not emit InstanceExpression load when InstanceExpressionOnStack
1011 // is set. Used by compound assignments.
1013 public bool InstanceExpressionOnStack;
1016 // Any of arguments contains await expression
1018 public bool HasAwaitArguments;
1020 public bool ConditionalAccess;
1023 // When dealing with await arguments the original arguments are converted
1024 // into a new set with hoisted stack results
1026 public Arguments EmittedArguments;
1028 public void Emit (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc)
1030 EmitPredefined (ec, method, Arguments, false, loc);
1033 public void EmitStatement (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc)
1035 EmitPredefined (ec, method, Arguments, true, loc);
1038 public void EmitPredefined (EmitContext ec, MethodSpec method, Arguments Arguments, bool statement = false, Location? loc = null)
1040 Expression instance_copy = null;
1042 if (!HasAwaitArguments && ec.HasSet (BuilderContext.Options.AsyncBody)) {
1043 HasAwaitArguments = Arguments != null && Arguments.ContainsEmitWithAwait ();
1044 if (HasAwaitArguments && InstanceExpressionOnStack) {
1045 throw new NotSupportedException ();
1049 OpCode call_op;
1050 LocalTemporary lt = null;
1052 if (method.IsStatic) {
1053 call_op = OpCodes.Call;
1054 } else {
1055 call_op = IsVirtualCallRequired (InstanceExpression, method) ? OpCodes.Callvirt : OpCodes.Call;
1057 if (HasAwaitArguments) {
1058 instance_copy = InstanceExpression.EmitToField (ec);
1059 var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
1061 if (Arguments == null) {
1062 ie.EmitLoad (ec, true);
1064 } else if (!InstanceExpressionOnStack) {
1065 var ie = new InstanceEmitter (InstanceExpression, IsAddressCall (InstanceExpression, call_op, method.DeclaringType));
1066 ie.Emit (ec, ConditionalAccess);
1068 if (DuplicateArguments) {
1069 ec.Emit (OpCodes.Dup);
1070 if (Arguments != null && Arguments.Count != 0) {
1071 lt = new LocalTemporary (ie.GetStackType (ec));
1072 lt.Store (ec);
1073 instance_copy = lt;
1079 if (Arguments != null && !InstanceExpressionOnStack) {
1080 EmittedArguments = Arguments.Emit (ec, DuplicateArguments, HasAwaitArguments);
1081 if (EmittedArguments != null) {
1082 if (instance_copy != null) {
1083 var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
1084 ie.Emit (ec, ConditionalAccess);
1086 if (lt != null)
1087 lt.Release (ec);
1090 EmittedArguments.Emit (ec);
1094 if (call_op == OpCodes.Callvirt && (InstanceExpression.Type.IsGenericParameter || InstanceExpression.Type.IsStructOrEnum)) {
1095 ec.Emit (OpCodes.Constrained, InstanceExpression.Type);
1098 if (loc != null) {
1100 // Emit explicit sequence point for expressions like Foo.Bar () to help debugger to
1101 // break at right place when LHS expression can be stepped-into
1103 ec.MarkCallEntry (loc.Value);
1107 // Set instance expression to actual result expression. When it contains await it can be
1108 // picked up by caller
1110 InstanceExpression = instance_copy;
1112 if (method.Parameters.HasArglist) {
1113 var varargs_types = GetVarargsTypes (method, Arguments);
1114 ec.Emit (call_op, method, varargs_types);
1115 } else {
1117 // If you have:
1118 // this.DoFoo ();
1119 // and DoFoo is not virtual, you can omit the callvirt,
1120 // because you don't need the null checking behavior.
1122 ec.Emit (call_op, method);
1126 // Pop the return value if there is one and stack should be empty
1128 if (statement && method.ReturnType.Kind != MemberKind.Void)
1129 ec.Emit (OpCodes.Pop);
1132 static MetaType[] GetVarargsTypes (MethodSpec method, Arguments arguments)
1134 AParametersCollection pd = method.Parameters;
1136 Argument a = arguments[pd.Count - 1];
1137 Arglist list = (Arglist) a.Expr;
1139 return list.ArgumentTypes;
1143 // Used to decide whether call or callvirt is needed
1145 static bool IsVirtualCallRequired (Expression instance, MethodSpec method)
1148 // There are 2 scenarious where we emit callvirt
1150 // Case 1: A method is virtual and it's not used to call base
1151 // Case 2: A method instance expression can be null. In this casen callvirt ensures
1152 // correct NRE exception when the method is called
1154 var decl_type = method.DeclaringType;
1155 if (decl_type.IsStruct || decl_type.IsEnum)
1156 return false;
1158 if (instance is BaseThis)
1159 return false;
1162 // It's non-virtual and will never be null and it can be determined
1163 // whether it's known value or reference type by verifier
1165 if (!method.IsVirtual && Expression.IsNeverNull (instance) && !instance.Type.IsGenericParameter)
1166 return false;
1168 return true;
1171 static bool IsAddressCall (Expression instance, OpCode callOpcode, TypeSpec declaringType)
1173 var instance_type = instance.Type;
1174 return (instance_type.IsStructOrEnum && (callOpcode == OpCodes.Callvirt || (callOpcode == OpCodes.Call && declaringType.IsStruct))) ||
1175 instance_type.IsGenericParameter || declaringType.IsNullableType;
1179 public struct InstanceEmitter
1181 readonly Expression instance;
1182 readonly bool addressRequired;
1184 public InstanceEmitter (Expression instance, bool addressLoad)
1186 this.instance = instance;
1187 this.addressRequired = addressLoad;
1190 public void Emit (EmitContext ec, bool conditionalAccess)
1192 Label NullOperatorLabel;
1193 Nullable.Unwrap unwrap;
1195 if (conditionalAccess && Expression.IsNeverNull (instance))
1196 conditionalAccess = false;
1198 if (conditionalAccess) {
1199 NullOperatorLabel = ec.DefineLabel ();
1200 unwrap = instance as Nullable.Unwrap;
1201 } else {
1202 NullOperatorLabel = new Label ();
1203 unwrap = null;
1206 IMemoryLocation instance_address = null;
1207 bool conditional_access_dup = false;
1209 if (unwrap != null) {
1210 unwrap.Store (ec);
1211 unwrap.EmitCheck (ec);
1212 ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel);
1213 } else {
1214 if (conditionalAccess && addressRequired) {
1216 // Don't allocate temp variable when instance load is cheap and load and load-address
1217 // operate on same memory
1219 instance_address = instance as VariableReference;
1220 if (instance_address == null)
1221 instance_address = instance as LocalTemporary;
1223 if (instance_address == null) {
1224 EmitLoad (ec, false);
1225 ec.Emit (OpCodes.Dup);
1226 ec.EmitLoadFromPtr (instance.Type);
1228 conditional_access_dup = true;
1229 } else {
1230 instance.Emit (ec);
1232 } else {
1233 EmitLoad (ec, !conditionalAccess);
1235 if (conditionalAccess) {
1236 conditional_access_dup = !IsInexpensiveLoad ();
1237 if (conditional_access_dup)
1238 ec.Emit (OpCodes.Dup);
1242 if (conditionalAccess) {
1243 if (instance.Type.Kind == MemberKind.TypeParameter)
1244 ec.Emit (OpCodes.Box, instance.Type);
1246 ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel);
1248 if (conditional_access_dup)
1249 ec.Emit (OpCodes.Pop);
1253 if (conditionalAccess) {
1254 if (!ec.ConditionalAccess.Statement) {
1255 if (ec.ConditionalAccess.Type.IsNullableType)
1256 Nullable.LiftedNull.Create (ec.ConditionalAccess.Type, Location.Null).Emit (ec);
1257 else
1258 ec.EmitNull ();
1261 ec.Emit (OpCodes.Br, ec.ConditionalAccess.EndLabel);
1262 ec.MarkLabel (NullOperatorLabel);
1264 if (instance_address != null) {
1265 instance_address.AddressOf (ec, AddressOp.Load);
1266 } else if (unwrap != null) {
1267 unwrap.Emit (ec);
1268 var tmp = ec.GetTemporaryLocal (unwrap.Type);
1269 ec.Emit (OpCodes.Stloc, tmp);
1270 ec.Emit (OpCodes.Ldloca, tmp);
1271 ec.FreeTemporaryLocal (tmp, unwrap.Type);
1272 } else if (!conditional_access_dup) {
1273 instance.Emit (ec);
1278 public void EmitLoad (EmitContext ec, bool boxInstance)
1280 var instance_type = instance.Type;
1283 // Push the instance expression
1285 if (addressRequired) {
1287 // If the expression implements IMemoryLocation, then
1288 // we can optimize and use AddressOf on the
1289 // return.
1291 // If not we have to use some temporary storage for
1292 // it.
1293 var iml = instance as IMemoryLocation;
1294 if (iml != null) {
1295 iml.AddressOf (ec, AddressOp.Load);
1296 } else {
1297 LocalTemporary temp = new LocalTemporary (instance_type);
1298 instance.Emit (ec);
1299 temp.Store (ec);
1300 temp.AddressOf (ec, AddressOp.Load);
1303 return;
1306 instance.Emit (ec);
1308 // Only to make verifier happy
1309 if (boxInstance && RequiresBoxing ()) {
1310 ec.Emit (OpCodes.Box, instance_type);
1314 public TypeSpec GetStackType (EmitContext ec)
1316 var instance_type = instance.Type;
1318 if (addressRequired)
1319 return ReferenceContainer.MakeType (ec.Module, instance_type);
1321 if (instance_type.IsStructOrEnum)
1322 return ec.Module.Compiler.BuiltinTypes.Object;
1324 return instance_type;
1327 bool RequiresBoxing ()
1329 var instance_type = instance.Type;
1330 if (instance_type.IsGenericParameter && !(instance is This) && TypeSpec.IsReferenceType (instance_type))
1331 return true;
1333 if (instance_type.IsStructOrEnum)
1334 return true;
1336 return false;
1340 // Returns true for cheap race-free load, where we can avoid using dup
1342 bool IsInexpensiveLoad ()
1344 if (instance is Constant)
1345 return instance.IsSideEffectFree;
1347 if (RequiresBoxing ())
1348 return false;
1350 var vr = instance as VariableReference;
1351 if (vr != null) {
1352 // Load from captured local would be racy without dup
1353 return !vr.IsRef && !vr.IsHoisted;
1356 if (instance is LocalTemporary)
1357 return true;
1359 var fe = instance as FieldExpr;
1360 if (fe != null)
1361 return fe.IsStatic || fe.InstanceExpression is This;
1363 return false;