2 // codegen.cs: The code generator
5 // Miguel de Icaza (miguel@ximian.com)
6 // Marek Safar (marek.safar@gmail.com)
8 // Copyright 2001, 2002, 2003 Ximian, Inc.
9 // Copyright 2004 Novell, Inc.
10 // Copyright 2011 Xamarin Inc
14 using System
.Collections
.Generic
;
15 using Mono
.CompilerServices
.SymbolWriter
;
18 using MetaType
= IKVM
.Reflection
.Type
;
19 using IKVM
.Reflection
;
20 using IKVM
.Reflection
.Emit
;
22 using MetaType
= System
.Type
;
23 using System
.Reflection
;
24 using System
.Reflection
.Emit
;
30 /// An Emit Context is created for each body of code (from methods,
31 /// properties bodies, indexer bodies or constructor bodies)
33 public class EmitContext
: BuilderContext
35 // TODO: Has to be private
36 public readonly ILGenerator ig
;
39 /// The value that is allowed to be returned or NULL if there is no
42 readonly TypeSpec return_type
;
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)
49 Dictionary
<TypeSpec
, object> temporary_storage
;
52 /// The location where we store the return value.
54 public LocalBuilder return_value
;
58 /// Current loop begin and end labels.
60 public Label LoopBegin
, LoopEnd
;
63 /// Default target in a switch statement. Only valid if
66 public Label DefaultTarget
;
69 /// If this is non-null, points to the current switch statement
74 /// Whether we are inside an anonymous method.
76 public AnonymousExpression CurrentAnonymousMethod
;
78 readonly IMemberContext member_context
;
80 readonly SourceMethodBuilder methodSymbols
;
82 DynamicSiteClass dynamic_site_container
;
86 List
<IExpressionCleanup
> epilogue_expressions
;
88 public EmitContext (IMemberContext rc
, ILGenerator ig
, TypeSpec return_type
, SourceMethodBuilder methodSymbols
)
90 this.member_context
= rc
;
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
;
102 flags
|= Options
.OmitDebugInfo
;
106 ig
.__CleverExceptionBlockAssistance ();
112 internal AsyncTaskStorey AsyncTaskStorey
{
114 return CurrentAnonymousMethod
.Storey
as AsyncTaskStorey
;
118 public BuiltinTypes BuiltinTypes
{
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
{
140 return (flags
& Options
.AccurateDebugInfo
) != 0;
144 public bool HasMethodSymbolBuilder
{
146 return methodSymbols
!= null;
150 public bool HasReturnLabel
{
152 return return_label
.HasValue
;
156 public bool IsStatic
{
157 get { return member_context.IsStatic; }
160 public bool IsStaticConstructor
{
162 return member_context
.IsStatic
&& (flags
& Options
.ConstructorScope
) != 0;
166 public bool IsAnonymousStoreyMutateRequired
{
168 return CurrentAnonymousMethod
!= null &&
169 CurrentAnonymousMethod
.Storey
!= null &&
170 CurrentAnonymousMethod
.Storey
.Mutator
!= null;
174 public IMemberContext MemberContext
{
176 return member_context
;
180 public ModuleContainer Module
{
182 return member_context
.Module
;
186 public bool NotifyEvaluatorOnStore
{
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
{
196 return member_context
.Module
.Compiler
.Report
;
200 public TypeSpec ReturnType
{
207 // The label where we have to jump before leaving the context
209 public Label ReturnLabel
{
211 return return_label
.Value
;
215 public List
<IExpressionCleanup
> StatementEpilogue
{
217 return epilogue_expressions
;
221 public LocalVariable AsyncThrowVariable { get; set; }
223 public List
<TryFinally
> TryFinallyUnwind { get; set; }
225 public Label RecursivePatternLabel { get; set; }
229 public void AddStatementEpilog (IExpressionCleanup cleanupExpression
)
231 if (epilogue_expressions
== null) {
232 epilogue_expressions
= new List
<IExpressionCleanup
> ();
233 } else if (epilogue_expressions
.Contains (cleanupExpression
)) {
237 epilogue_expressions
.Add (cleanupExpression
);
240 public void AssertEmptyStack ()
243 if (ig
.__StackHeight
!= 0)
244 throw new InternalErrorException ("Await yields with non-empty stack in `{0}",
245 member_context
.GetSignatureForError ());
250 /// This is called immediately before emitting an IL opcode to tell the symbol
251 /// writer to which source line this opcode belongs.
253 public bool Mark (Location loc
)
255 if ((flags
& Options
.OmitDebugInfo
) != 0)
258 if (loc
.IsNull
|| methodSymbols
== null)
261 var sf
= loc
.SourceFile
;
262 if (sf
.IsHiddenLocation (loc
))
266 methodSymbols
.MarkSequencePoint (ig
.ILOffset
, sf
.SourceFileEntry
, loc
.Row
, loc
.Column
, false);
271 public void MarkCallEntry (Location loc
)
273 if (!EmitAccurateDebugInfo
)
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
286 public void DefineLocalVariable (string name
, LocalBuilder builder
)
288 if ((flags
& Options
.OmitDebugInfo
) != 0)
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)
328 methodSymbols
.StartBlock (CodeBlockEntry
.Type
.Lexical
, ig
.ILOffset
);
332 public void BeginCompilerScope ()
334 if ((flags
& Options
.OmitDebugInfo
) != 0)
338 methodSymbols
.StartBlock (CodeBlockEntry
.Type
.CompilerGenerated
, ig
.ILOffset
);
342 public void EndExceptionBlock ()
344 ig
.EndExceptionBlock ();
347 public void EndScope ()
349 if ((flags
& Options
.OmitDebugInfo
) != 0)
353 methodSymbols
.EndBlock (ig
.ILOffset
);
357 public void CloseConditionalAccess (TypeSpec type
)
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
);
420 public void MarkLabel (Label label
)
422 ig
.MarkLabel (label
);
425 public void Emit (OpCode 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 ());
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
)
502 var type
= IsAnonymousStoreyMutateRequired
?
503 CurrentAnonymousMethod
.Storey
.Mutator
.Mutate (ac
.Element
) :
506 ig
.Emit (OpCodes
.Newarr
, type
.GetMetaInfo ());
508 if (IsAnonymousStoreyMutateRequired
)
509 ac
= (ArrayContainer
) ac
.Mutate (CurrentAnonymousMethod
.Storey
.Mutator
);
511 ig
.Emit (OpCodes
.Newobj
, ac
.GetConstructor ());
515 public void EmitArrayAddress (ArrayContainer ac
)
518 if (IsAnonymousStoreyMutateRequired
)
519 ac
= (ArrayContainer
) ac
.Mutate (CurrentAnonymousMethod
.Storey
.Mutator
);
521 ig
.Emit (OpCodes
.Call
, ac
.GetAddressMethod ());
523 var type
= IsAnonymousStoreyMutateRequired
?
524 CurrentAnonymousMethod
.Storey
.Mutator
.Mutate (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
)
537 if (IsAnonymousStoreyMutateRequired
)
538 ac
= (ArrayContainer
) ac
.Mutate (CurrentAnonymousMethod
.Storey
.Mutator
);
540 ig
.Emit (OpCodes
.Call
, ac
.GetGetMethod ());
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
556 case BuiltinTypeSpec
.Type
.Byte
:
557 ig
.Emit (OpCodes
.Ldelem_U1
);
559 case BuiltinTypeSpec
.Type
.SByte
:
560 ig
.Emit (OpCodes
.Ldelem_I1
);
562 case BuiltinTypeSpec
.Type
.Short
:
563 ig
.Emit (OpCodes
.Ldelem_I2
);
565 case BuiltinTypeSpec
.Type
.UShort
:
566 case BuiltinTypeSpec
.Type
.Char
:
567 ig
.Emit (OpCodes
.Ldelem_U2
);
569 case BuiltinTypeSpec
.Type
.Int
:
570 ig
.Emit (OpCodes
.Ldelem_I4
);
572 case BuiltinTypeSpec
.Type
.UInt
:
573 ig
.Emit (OpCodes
.Ldelem_U4
);
575 case BuiltinTypeSpec
.Type
.ULong
:
576 case BuiltinTypeSpec
.Type
.Long
:
577 ig
.Emit (OpCodes
.Ldelem_I8
);
579 case BuiltinTypeSpec
.Type
.Float
:
580 ig
.Emit (OpCodes
.Ldelem_R4
);
582 case BuiltinTypeSpec
.Type
.Double
:
583 ig
.Emit (OpCodes
.Ldelem_R8
);
585 case BuiltinTypeSpec
.Type
.IntPtr
:
586 ig
.Emit (OpCodes
.Ldelem_I
);
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 ());
597 case MemberKind
.TypeParameter
:
598 if (IsAnonymousStoreyMutateRequired
)
599 type
= CurrentAnonymousMethod
.Storey
.Mutator
.Mutate (type
);
601 ig
.Emit (OpCodes
.Ldelem
, type
.GetMetaInfo ());
603 case MemberKind
.PointerType
:
604 ig
.Emit (OpCodes
.Ldelem_I
);
607 ig
.Emit (OpCodes
.Ldelem_Ref
);
615 // Emits the right opcode to store to an array
617 public void EmitArrayStore (ArrayContainer ac
)
620 if (IsAnonymousStoreyMutateRequired
)
621 ac
= (ArrayContainer
) ac
.Mutate (CurrentAnonymousMethod
.Storey
.Mutator
);
623 ig
.Emit (OpCodes
.Call
, ac
.GetSetMethod ());
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
);
638 case BuiltinTypeSpec
.Type
.Short
:
639 case BuiltinTypeSpec
.Type
.UShort
:
640 case BuiltinTypeSpec
.Type
.Char
:
641 Emit (OpCodes
.Stelem_I2
);
643 case BuiltinTypeSpec
.Type
.Int
:
644 case BuiltinTypeSpec
.Type
.UInt
:
645 Emit (OpCodes
.Stelem_I4
);
647 case BuiltinTypeSpec
.Type
.Long
:
648 case BuiltinTypeSpec
.Type
.ULong
:
649 Emit (OpCodes
.Stelem_I8
);
651 case BuiltinTypeSpec
.Type
.Float
:
652 Emit (OpCodes
.Stelem_R4
);
654 case BuiltinTypeSpec
.Type
.Double
:
655 Emit (OpCodes
.Stelem_R8
);
660 case MemberKind
.Struct
:
661 Emit (OpCodes
.Stobj
, type
);
663 case MemberKind
.TypeParameter
:
664 Emit (OpCodes
.Stelem
, type
);
666 case MemberKind
.PointerType
:
667 Emit (OpCodes
.Stelem_I
);
670 Emit (OpCodes
.Stelem_Ref
);
675 public void EmitInt (int i
)
680 void EmitIntConstant (int i
)
684 ig
.Emit (OpCodes
.Ldc_I4_M1
);
688 ig
.Emit (OpCodes
.Ldc_I4_0
);
692 ig
.Emit (OpCodes
.Ldc_I4_1
);
696 ig
.Emit (OpCodes
.Ldc_I4_2
);
700 ig
.Emit (OpCodes
.Ldc_I4_3
);
704 ig
.Emit (OpCodes
.Ldc_I4_4
);
708 ig
.Emit (OpCodes
.Ldc_I4_5
);
712 ig
.Emit (OpCodes
.Ldc_I4_6
);
716 ig
.Emit (OpCodes
.Ldc_I4_7
);
720 ig
.Emit (OpCodes
.Ldc_I4_8
);
724 if (i
>= -128 && i
<= 127) {
725 ig
.Emit (OpCodes
.Ldc_I4_S
, (sbyte) i
);
727 ig
.Emit (OpCodes
.Ldc_I4
, i
);
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
);
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
);
757 case BuiltinTypeSpec
.Type
.UInt
:
758 ig
.Emit (OpCodes
.Ldind_U4
);
760 case BuiltinTypeSpec
.Type
.Short
:
761 ig
.Emit (OpCodes
.Ldind_I2
);
763 case BuiltinTypeSpec
.Type
.UShort
:
764 case BuiltinTypeSpec
.Type
.Char
:
765 ig
.Emit (OpCodes
.Ldind_U2
);
767 case BuiltinTypeSpec
.Type
.Byte
:
768 ig
.Emit (OpCodes
.Ldind_U1
);
770 case BuiltinTypeSpec
.Type
.SByte
:
771 case BuiltinTypeSpec
.Type
.Bool
:
772 ig
.Emit (OpCodes
.Ldind_I1
);
774 case BuiltinTypeSpec
.Type
.ULong
:
775 case BuiltinTypeSpec
.Type
.Long
:
776 ig
.Emit (OpCodes
.Ldind_I8
);
778 case BuiltinTypeSpec
.Type
.Float
:
779 ig
.Emit (OpCodes
.Ldind_R4
);
781 case BuiltinTypeSpec
.Type
.Double
:
782 ig
.Emit (OpCodes
.Ldind_R8
);
784 case BuiltinTypeSpec
.Type
.IntPtr
:
785 ig
.Emit (OpCodes
.Ldind_I
);
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 ());
796 case MemberKind
.PointerType
:
797 ig
.Emit (OpCodes
.Ldind_I
);
800 ig
.Emit (OpCodes
.Ldind_Ref
);
807 public void EmitNull ()
809 ig
.Emit (OpCodes
.Ldnull
);
812 public void EmitArgumentAddress (int pos
)
817 if (pos
> byte.MaxValue
)
818 ig
.Emit (OpCodes
.Ldarga
, pos
);
820 ig
.Emit (OpCodes
.Ldarga_S
, (byte) pos
);
823 public void EmitArgumentLoad (int 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;
834 if (pos
> byte.MaxValue
)
835 ig
.Emit (OpCodes
.Ldarg
, pos
);
837 ig
.Emit (OpCodes
.Ldarg_S
, (byte) pos
);
842 public void EmitArgumentStore (int pos
)
847 if (pos
> byte.MaxValue
)
848 ig
.Emit (OpCodes
.Starg
, pos
);
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
)
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
);
866 case BuiltinTypeSpec
.Type
.Long
:
867 case BuiltinTypeSpec
.Type
.ULong
:
868 ig
.Emit (OpCodes
.Stind_I8
);
870 case BuiltinTypeSpec
.Type
.Char
:
871 case BuiltinTypeSpec
.Type
.Short
:
872 case BuiltinTypeSpec
.Type
.UShort
:
873 ig
.Emit (OpCodes
.Stind_I2
);
875 case BuiltinTypeSpec
.Type
.Float
:
876 ig
.Emit (OpCodes
.Stind_R4
);
878 case BuiltinTypeSpec
.Type
.Double
:
879 ig
.Emit (OpCodes
.Stind_R8
);
881 case BuiltinTypeSpec
.Type
.Byte
:
882 case BuiltinTypeSpec
.Type
.SByte
:
883 case BuiltinTypeSpec
.Type
.Bool
:
884 ig
.Emit (OpCodes
.Stind_I1
);
886 case BuiltinTypeSpec
.Type
.IntPtr
:
887 ig
.Emit (OpCodes
.Stind_I
);
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 ());
900 ig
.Emit (OpCodes
.Stind_Ref
);
905 public void EmitThis ()
907 ig
.Emit (OpCodes
.Ldarg_0
);
910 public void EmitEpilogue ()
912 if (epilogue_expressions
== null)
915 foreach (var e
in epilogue_expressions
)
916 e
.EmitCleanup (this);
918 epilogue_expressions
= null;
922 /// Returns a temporary storage for a variable of type t as
923 /// a local variable in the current body.
925 public LocalBuilder
GetTemporaryLocal (TypeSpec t
)
927 if (temporary_storage
!= null) {
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 ();
934 temporary_storage
.Remove (t
);
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
);
952 if (!temporary_storage
.TryGetValue (t
, out o
)) {
953 temporary_storage
.Add (t
, b
);
956 var s
= o
as Stack
<LocalBuilder
>;
958 s
= new Stack
<LocalBuilder
> ();
959 s
.Push ((LocalBuilder
)o
);
960 temporary_storage
[t
] = s
;
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.
977 public LocalBuilder
TemporaryReturn ()
979 if (return_value
== null){
980 return_value
= DeclareLocal (return_type
, false);
987 public class ConditionalAccessContext
989 public ConditionalAccessContext (TypeSpec type
, Label endLabel
)
995 public bool Statement { get; set; }
996 public Label EndLabel { get; private set; }
997 public TypeSpec Type { get; private set; }
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 ();
1050 LocalTemporary lt
= null;
1052 if (method
.IsStatic
) {
1053 call_op
= OpCodes
.Call
;
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
));
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
);
1090 EmittedArguments
.Emit (ec
);
1094 if (call_op
== OpCodes
.Callvirt
&& (InstanceExpression
.Type
.IsGenericParameter
|| InstanceExpression
.Type
.IsStructOrEnum
)) {
1095 ec
.Emit (OpCodes
.Constrained
, InstanceExpression
.Type
);
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
);
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
)
1158 if (instance
is BaseThis
)
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
)
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
;
1202 NullOperatorLabel
= new Label ();
1206 IMemoryLocation instance_address
= null;
1207 bool conditional_access_dup
= false;
1209 if (unwrap
!= null) {
1211 unwrap
.EmitCheck (ec
);
1212 ec
.Emit (OpCodes
.Brtrue_S
, NullOperatorLabel
);
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;
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
);
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) {
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
) {
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
1291 // If not we have to use some temporary storage for
1293 var iml
= instance
as IMemoryLocation
;
1295 iml
.AddressOf (ec
, AddressOp
.Load
);
1297 LocalTemporary temp
= new LocalTemporary (instance_type
);
1300 temp
.AddressOf (ec
, AddressOp
.Load
);
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
))
1333 if (instance_type
.IsStructOrEnum
)
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 ())
1350 var vr
= instance
as VariableReference
;
1352 // Load from captured local would be racy without dup
1353 return !vr
.IsRef
&& !vr
.IsHoisted
;
1356 if (instance
is LocalTemporary
)
1359 var fe
= instance
as FieldExpr
;
1361 return fe
.IsStatic
|| fe
.InstanceExpression
is This
;