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))
265 methodSymbols.MarkSequencePoint (ig.ILOffset, sf.SourceFileEntry, loc.Row, loc.Column, false);
269 public void MarkCallEntry (Location loc)
271 if (!EmitAccurateDebugInfo)
275 // TODO: This should emit different kind of sequence point to make
276 // step-over work for statement over multiple lines
278 // Debugging experience for Foo (A () + B ()) where A and B are
279 // on separate lines is not great
284 public void DefineLocalVariable (string name, LocalBuilder builder)
286 if ((flags & Options.OmitDebugInfo) != 0)
289 methodSymbols.AddLocal (builder.LocalIndex, name);
292 public void BeginCatchBlock (TypeSpec type)
294 if (IsAnonymousStoreyMutateRequired)
295 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
297 ig.BeginCatchBlock (type.GetMetaInfo ());
300 public void BeginFilterHandler ()
302 ig.BeginCatchBlock (null);
305 public void BeginExceptionBlock ()
307 ig.BeginExceptionBlock ();
310 public void BeginExceptionFilterBlock ()
312 ig.BeginExceptFilterBlock ();
315 public void BeginFinallyBlock ()
317 ig.BeginFinallyBlock ();
320 public void BeginScope (int scopeIndex)
322 if ((flags & Options.OmitDebugInfo) != 0)
325 methodSymbols.StartBlock (CodeBlockEntry.Type.Lexical, ig.ILOffset, scopeIndex);
328 public void BeginCompilerScope (int scopeIndex)
330 if ((flags & Options.OmitDebugInfo) != 0)
333 methodSymbols.StartBlock (CodeBlockEntry.Type.CompilerGenerated, ig.ILOffset, scopeIndex);
336 public void EndExceptionBlock ()
338 ig.EndExceptionBlock ();
341 public void EndScope ()
343 if ((flags & Options.OmitDebugInfo) != 0)
346 methodSymbols.EndBlock (ig.ILOffset);
349 public void CloseConditionalAccess (TypeSpec type)
352 Emit (OpCodes.Newobj, Nullable.NullableInfo.GetConstructor (type));
354 MarkLabel (ConditionalAccess.EndLabel);
355 ConditionalAccess = null;
359 // Creates a nested container in this context for all dynamic compiler generated stuff
361 internal DynamicSiteClass CreateDynamicSite ()
363 if (dynamic_site_container == null) {
364 var mc = member_context.CurrentMemberDefinition as MemberBase;
365 dynamic_site_container = new DynamicSiteClass (CurrentTypeDefinition.Parent.PartialContainer, mc, member_context.CurrentTypeParameters);
367 CurrentTypeDefinition.Module.AddCompilerGeneratedClass (dynamic_site_container);
368 dynamic_site_container.CreateContainer ();
369 dynamic_site_container.DefineContainer ();
370 dynamic_site_container.Define ();
372 var inflator = new TypeParameterInflator (Module, CurrentType, TypeParameterSpec.EmptyTypes, TypeSpec.EmptyTypes);
373 var inflated = dynamic_site_container.CurrentType.InflateMember (inflator);
374 CurrentType.MemberCache.AddMember (inflated);
377 return dynamic_site_container;
380 public Label CreateReturnLabel ()
382 if (!return_label.HasValue)
383 return_label = DefineLabel ();
385 return return_label.Value;
388 public LocalBuilder DeclareLocal (TypeSpec type, bool pinned)
390 if (IsAnonymousStoreyMutateRequired)
391 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
395 // This is for .net compatibility. I am not sure why pinned
396 // pointer temps are converted to & even if they are pointers to
399 var pt = type as PointerContainer;
402 if (type.Kind == MemberKind.Void)
403 type = Module.Compiler.BuiltinTypes.IntPtr;
405 return ig.DeclareLocal (type.GetMetaInfo ().MakeByRefType (), true);
409 return ig.DeclareLocal (type.GetMetaInfo (), pinned);
412 public Label DefineLabel ()
414 return ig.DefineLabel ();
418 // Creates temporary field in current async storey
420 public StackFieldExpr GetTemporaryField (TypeSpec type, bool initializedFieldRequired = false)
422 var f = AsyncTaskStorey.AddCapturedLocalVariable (type, initializedFieldRequired);
423 var fexpr = new StackFieldExpr (f);
424 fexpr.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location.Null);
428 public void MarkLabel (Label label)
430 ig.MarkLabel (label);
433 public void Emit (OpCode opcode)
438 public void Emit (OpCode opcode, LocalBuilder local)
440 ig.Emit (opcode, local);
443 public void Emit (OpCode opcode, string arg)
445 ig.Emit (opcode, arg);
448 public void Emit (OpCode opcode, double arg)
450 ig.Emit (opcode, arg);
453 public void Emit (OpCode opcode, float arg)
455 ig.Emit (opcode, arg);
458 public void Emit (OpCode opcode, Label label)
460 ig.Emit (opcode, label);
463 public void Emit (OpCode opcode, Label[] labels)
465 ig.Emit (opcode, labels);
468 public void Emit (OpCode opcode, TypeSpec type)
470 if (IsAnonymousStoreyMutateRequired)
471 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
473 ig.Emit (opcode, type.GetMetaInfo ());
476 public void Emit (OpCode opcode, FieldSpec field)
478 if (IsAnonymousStoreyMutateRequired)
479 field = field.Mutate (CurrentAnonymousMethod.Storey.Mutator);
481 ig.Emit (opcode, field.GetMetaInfo ());
484 public void Emit (OpCode opcode, MethodSpec method)
486 if (IsAnonymousStoreyMutateRequired)
487 method = method.Mutate (CurrentAnonymousMethod.Storey.Mutator);
489 if (method.IsConstructor)
490 ig.Emit (opcode, (ConstructorInfo) method.GetMetaInfo ());
492 ig.Emit (opcode, (MethodInfo) method.GetMetaInfo ());
495 // TODO: REMOVE breaks mutator
496 public void Emit (OpCode opcode, MethodInfo method)
498 ig.Emit (opcode, method);
501 public void Emit (OpCode opcode, MethodSpec method, MetaType[] vargs)
503 // TODO MemberCache: This should mutate too
504 ig.EmitCall (opcode, (MethodInfo) method.GetMetaInfo (), vargs);
507 public void EmitArrayNew (ArrayContainer ac)
510 var type = IsAnonymousStoreyMutateRequired ?
511 CurrentAnonymousMethod.Storey.Mutator.Mutate (ac.Element) :
514 ig.Emit (OpCodes.Newarr, type.GetMetaInfo ());
516 if (IsAnonymousStoreyMutateRequired)
517 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
519 ig.Emit (OpCodes.Newobj, ac.GetConstructor ());
523 public void EmitArrayAddress (ArrayContainer ac)
526 if (IsAnonymousStoreyMutateRequired)
527 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
529 ig.Emit (OpCodes.Call, ac.GetAddressMethod ());
531 var type = IsAnonymousStoreyMutateRequired ?
532 CurrentAnonymousMethod.Storey.Mutator.Mutate (ac.Element) :
535 ig.Emit (OpCodes.Ldelema, type.GetMetaInfo ());
540 // Emits the right opcode to load from an array
542 public void EmitArrayLoad (ArrayContainer ac)
545 if (IsAnonymousStoreyMutateRequired)
546 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
548 ig.Emit (OpCodes.Call, ac.GetGetMethod ());
553 var type = ac.Element;
554 if (type.Kind == MemberKind.Enum)
555 type = EnumSpec.GetUnderlyingType (type);
557 switch (type.BuiltinType) {
558 case BuiltinTypeSpec.Type.Bool:
560 // bool array can actually store any byte value in underlying byte slot
561 // and C# spec does not specify any normalization rule, except the result
564 case BuiltinTypeSpec.Type.Byte:
565 ig.Emit (OpCodes.Ldelem_U1);
567 case BuiltinTypeSpec.Type.SByte:
568 ig.Emit (OpCodes.Ldelem_I1);
570 case BuiltinTypeSpec.Type.Short:
571 ig.Emit (OpCodes.Ldelem_I2);
573 case BuiltinTypeSpec.Type.UShort:
574 case BuiltinTypeSpec.Type.Char:
575 ig.Emit (OpCodes.Ldelem_U2);
577 case BuiltinTypeSpec.Type.Int:
578 ig.Emit (OpCodes.Ldelem_I4);
580 case BuiltinTypeSpec.Type.UInt:
581 ig.Emit (OpCodes.Ldelem_U4);
583 case BuiltinTypeSpec.Type.ULong:
584 case BuiltinTypeSpec.Type.Long:
585 ig.Emit (OpCodes.Ldelem_I8);
587 case BuiltinTypeSpec.Type.Float:
588 ig.Emit (OpCodes.Ldelem_R4);
590 case BuiltinTypeSpec.Type.Double:
591 ig.Emit (OpCodes.Ldelem_R8);
593 case BuiltinTypeSpec.Type.IntPtr:
594 ig.Emit (OpCodes.Ldelem_I);
598 case MemberKind.Struct:
599 if (IsAnonymousStoreyMutateRequired)
600 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
602 ig.Emit (OpCodes.Ldelema, type.GetMetaInfo ());
603 ig.Emit (OpCodes.Ldobj, type.GetMetaInfo ());
605 case MemberKind.TypeParameter:
606 if (IsAnonymousStoreyMutateRequired)
607 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
609 ig.Emit (OpCodes.Ldelem, type.GetMetaInfo ());
611 case MemberKind.PointerType:
612 ig.Emit (OpCodes.Ldelem_I);
615 ig.Emit (OpCodes.Ldelem_Ref);
623 // Emits the right opcode to store to an array
625 public void EmitArrayStore (ArrayContainer ac)
628 if (IsAnonymousStoreyMutateRequired)
629 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
631 ig.Emit (OpCodes.Call, ac.GetSetMethod ());
635 var type = ac.Element;
637 if (type.Kind == MemberKind.Enum)
638 type = EnumSpec.GetUnderlyingType (type);
640 switch (type.BuiltinType) {
641 case BuiltinTypeSpec.Type.Byte:
642 case BuiltinTypeSpec.Type.SByte:
643 case BuiltinTypeSpec.Type.Bool:
644 Emit (OpCodes.Stelem_I1);
646 case BuiltinTypeSpec.Type.Short:
647 case BuiltinTypeSpec.Type.UShort:
648 case BuiltinTypeSpec.Type.Char:
649 Emit (OpCodes.Stelem_I2);
651 case BuiltinTypeSpec.Type.Int:
652 case BuiltinTypeSpec.Type.UInt:
653 Emit (OpCodes.Stelem_I4);
655 case BuiltinTypeSpec.Type.Long:
656 case BuiltinTypeSpec.Type.ULong:
657 Emit (OpCodes.Stelem_I8);
659 case BuiltinTypeSpec.Type.Float:
660 Emit (OpCodes.Stelem_R4);
662 case BuiltinTypeSpec.Type.Double:
663 Emit (OpCodes.Stelem_R8);
668 case MemberKind.Struct:
669 Emit (OpCodes.Stobj, type);
671 case MemberKind.TypeParameter:
672 Emit (OpCodes.Stelem, type);
674 case MemberKind.PointerType:
675 Emit (OpCodes.Stelem_I);
678 Emit (OpCodes.Stelem_Ref);
683 public void EmitInt (int i)
688 void EmitIntConstant (int i)
692 ig.Emit (OpCodes.Ldc_I4_M1);
696 ig.Emit (OpCodes.Ldc_I4_0);
700 ig.Emit (OpCodes.Ldc_I4_1);
704 ig.Emit (OpCodes.Ldc_I4_2);
708 ig.Emit (OpCodes.Ldc_I4_3);
712 ig.Emit (OpCodes.Ldc_I4_4);
716 ig.Emit (OpCodes.Ldc_I4_5);
720 ig.Emit (OpCodes.Ldc_I4_6);
724 ig.Emit (OpCodes.Ldc_I4_7);
728 ig.Emit (OpCodes.Ldc_I4_8);
732 if (i >= -128 && i <= 127) {
733 ig.Emit (OpCodes.Ldc_I4_S, (sbyte) i);
735 ig.Emit (OpCodes.Ldc_I4, i);
740 public void EmitLong (long l)
742 if (l >= int.MinValue && l <= int.MaxValue) {
743 EmitIntConstant (unchecked ((int) l));
744 ig.Emit (OpCodes.Conv_I8);
745 } else if (l >= 0 && l <= uint.MaxValue) {
746 EmitIntConstant (unchecked ((int) l));
747 ig.Emit (OpCodes.Conv_U8);
749 ig.Emit (OpCodes.Ldc_I8, l);
754 // Load the object from the pointer.
756 public void EmitLoadFromPtr (TypeSpec type)
758 if (type.Kind == MemberKind.Enum)
759 type = EnumSpec.GetUnderlyingType (type);
761 switch (type.BuiltinType) {
762 case BuiltinTypeSpec.Type.Int:
763 ig.Emit (OpCodes.Ldind_I4);
765 case BuiltinTypeSpec.Type.UInt:
766 ig.Emit (OpCodes.Ldind_U4);
768 case BuiltinTypeSpec.Type.Short:
769 ig.Emit (OpCodes.Ldind_I2);
771 case BuiltinTypeSpec.Type.UShort:
772 case BuiltinTypeSpec.Type.Char:
773 ig.Emit (OpCodes.Ldind_U2);
775 case BuiltinTypeSpec.Type.Byte:
776 ig.Emit (OpCodes.Ldind_U1);
778 case BuiltinTypeSpec.Type.SByte:
779 case BuiltinTypeSpec.Type.Bool:
780 ig.Emit (OpCodes.Ldind_I1);
782 case BuiltinTypeSpec.Type.ULong:
783 case BuiltinTypeSpec.Type.Long:
784 ig.Emit (OpCodes.Ldind_I8);
786 case BuiltinTypeSpec.Type.Float:
787 ig.Emit (OpCodes.Ldind_R4);
789 case BuiltinTypeSpec.Type.Double:
790 ig.Emit (OpCodes.Ldind_R8);
792 case BuiltinTypeSpec.Type.IntPtr:
793 ig.Emit (OpCodes.Ldind_I);
797 case MemberKind.Struct:
798 case MemberKind.TypeParameter:
799 if (IsAnonymousStoreyMutateRequired)
800 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
802 ig.Emit (OpCodes.Ldobj, type.GetMetaInfo ());
804 case MemberKind.PointerType:
805 ig.Emit (OpCodes.Ldind_I);
808 ig.Emit (OpCodes.Ldind_Ref);
815 public void EmitNull ()
817 ig.Emit (OpCodes.Ldnull);
820 public void EmitArgumentAddress (int pos)
825 if (pos > byte.MaxValue)
826 ig.Emit (OpCodes.Ldarga, pos);
828 ig.Emit (OpCodes.Ldarga_S, (byte) pos);
831 public void EmitArgumentLoad (int pos)
837 case 0: ig.Emit (OpCodes.Ldarg_0); break;
838 case 1: ig.Emit (OpCodes.Ldarg_1); break;
839 case 2: ig.Emit (OpCodes.Ldarg_2); break;
840 case 3: ig.Emit (OpCodes.Ldarg_3); break;
842 if (pos > byte.MaxValue)
843 ig.Emit (OpCodes.Ldarg, pos);
845 ig.Emit (OpCodes.Ldarg_S, (byte) pos);
850 public void EmitArgumentStore (int pos)
855 if (pos > byte.MaxValue)
856 ig.Emit (OpCodes.Starg, pos);
858 ig.Emit (OpCodes.Starg_S, (byte) pos);
862 // The stack contains the pointer and the value of type `type'
864 public void EmitStoreFromPtr (TypeSpec type)
867 type = EnumSpec.GetUnderlyingType (type);
869 switch (type.BuiltinType) {
870 case BuiltinTypeSpec.Type.Int:
871 case BuiltinTypeSpec.Type.UInt:
872 ig.Emit (OpCodes.Stind_I4);
874 case BuiltinTypeSpec.Type.Long:
875 case BuiltinTypeSpec.Type.ULong:
876 ig.Emit (OpCodes.Stind_I8);
878 case BuiltinTypeSpec.Type.Char:
879 case BuiltinTypeSpec.Type.Short:
880 case BuiltinTypeSpec.Type.UShort:
881 ig.Emit (OpCodes.Stind_I2);
883 case BuiltinTypeSpec.Type.Float:
884 ig.Emit (OpCodes.Stind_R4);
886 case BuiltinTypeSpec.Type.Double:
887 ig.Emit (OpCodes.Stind_R8);
889 case BuiltinTypeSpec.Type.Byte:
890 case BuiltinTypeSpec.Type.SByte:
891 case BuiltinTypeSpec.Type.Bool:
892 ig.Emit (OpCodes.Stind_I1);
894 case BuiltinTypeSpec.Type.IntPtr:
895 ig.Emit (OpCodes.Stind_I);
900 case MemberKind.Struct:
901 case MemberKind.TypeParameter:
902 if (IsAnonymousStoreyMutateRequired)
903 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
905 ig.Emit (OpCodes.Stobj, type.GetMetaInfo ());
907 case MemberKind.PointerType:
908 ig.Emit (OpCodes.Stind_I);
911 ig.Emit (OpCodes.Stind_Ref);
916 public void EmitThis ()
918 ig.Emit (OpCodes.Ldarg_0);
921 public void EmitEpilogue ()
923 if (epilogue_expressions == null)
926 foreach (var e in epilogue_expressions)
927 e.EmitCleanup (this);
929 epilogue_expressions = null;
933 /// Returns a temporary storage for a variable of type t as
934 /// a local variable in the current body.
936 public LocalBuilder GetTemporaryLocal (TypeSpec t)
938 if (temporary_storage != null) {
940 if (temporary_storage.TryGetValue (t, out o)) {
941 if (o is Stack<LocalBuilder>) {
942 var s = (Stack<LocalBuilder>) o;
943 o = s.Count == 0 ? null : s.Pop ();
945 temporary_storage.Remove (t);
949 return (LocalBuilder) o;
951 return DeclareLocal (t, false);
954 public void FreeTemporaryLocal (LocalBuilder b, TypeSpec t)
956 if (temporary_storage == null) {
957 temporary_storage = new Dictionary<TypeSpec, object> (ReferenceEquality<TypeSpec>.Default);
958 temporary_storage.Add (t, b);
963 if (!temporary_storage.TryGetValue (t, out o)) {
964 temporary_storage.Add (t, b);
967 var s = o as Stack<LocalBuilder>;
969 s = new Stack<LocalBuilder> ();
970 s.Push ((LocalBuilder)o);
971 temporary_storage [t] = s;
977 /// ReturnValue creates on demand the LocalBuilder for the
978 /// return value from the function. By default this is not
979 /// used. This is only required when returns are found inside
980 /// Try or Catch statements.
982 /// This method is typically invoked from the Emit phase, so
983 /// we allow the creation of a return label if it was not
984 /// requested during the resolution phase. Could be cleaned
985 /// up, but it would replicate a lot of logic in the Emit phase
986 /// of the code that uses it.
988 public LocalBuilder TemporaryReturn ()
990 if (return_value == null){
991 return_value = DeclareLocal (return_type, false);
998 public class ConditionalAccessContext
1000 public ConditionalAccessContext (TypeSpec type, Label endLabel)
1003 EndLabel = endLabel;
1006 public bool Statement { get; set; }
1007 public Label EndLabel { get; private set; }
1008 public TypeSpec Type { get; private set; }
1013 public Expression InstanceExpression;
1016 // When call has to leave an extra copy of all arguments on the stack
1018 public bool DuplicateArguments;
1021 // Does not emit InstanceExpression load when InstanceExpressionOnStack
1022 // is set. Used by compound assignments.
1024 public bool InstanceExpressionOnStack;
1027 // Any of arguments contains await expression
1029 public bool HasAwaitArguments;
1031 public bool ConditionalAccess;
1034 // When dealing with await arguments the original arguments are converted
1035 // into a new set with hoisted stack results
1037 public Arguments EmittedArguments;
1039 public void Emit (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc)
1041 EmitPredefined (ec, method, Arguments, false, loc);
1044 public void EmitStatement (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc)
1046 EmitPredefined (ec, method, Arguments, true, loc);
1049 public void EmitPredefined (EmitContext ec, MethodSpec method, Arguments Arguments, bool statement = false, Location? loc = null)
1051 Expression instance_copy = null;
1053 if (!HasAwaitArguments && ec.HasSet (BuilderContext.Options.AsyncBody)) {
1054 HasAwaitArguments = Arguments != null && Arguments.ContainsEmitWithAwait ();
1055 if (HasAwaitArguments && InstanceExpressionOnStack) {
1056 throw new NotSupportedException ();
1061 LocalTemporary lt = null;
1063 if (method.IsStatic) {
1064 call_op = OpCodes.Call;
1066 call_op = IsVirtualCallRequired (InstanceExpression, method) ? OpCodes.Callvirt : OpCodes.Call;
1068 if (HasAwaitArguments) {
1069 instance_copy = InstanceExpression.EmitToField (ec);
1070 var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
1072 if (Arguments == null) {
1073 if (ConditionalAccess)
1076 ie.EmitLoad (ec, true);
1078 } else if (!InstanceExpressionOnStack) {
1079 var ie = new InstanceEmitter (InstanceExpression, IsAddressCall (InstanceExpression, call_op, method.DeclaringType));
1080 ie.Emit (ec, ConditionalAccess);
1082 if (DuplicateArguments) {
1083 ec.Emit (OpCodes.Dup);
1084 if (Arguments != null && Arguments.Count != 0) {
1085 lt = new LocalTemporary (ie.GetStackType (ec));
1093 if (Arguments != null && !InstanceExpressionOnStack) {
1094 EmittedArguments = Arguments.Emit (ec, DuplicateArguments, HasAwaitArguments);
1095 if (EmittedArguments != null) {
1096 if (instance_copy != null) {
1097 var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
1098 ie.Emit (ec, ConditionalAccess);
1104 EmittedArguments.Emit (ec);
1108 if (call_op == OpCodes.Callvirt && (InstanceExpression.Type.IsGenericParameter || InstanceExpression.Type.IsStructOrEnum)) {
1109 ec.Emit (OpCodes.Constrained, InstanceExpression.Type);
1114 // Emit explicit sequence point for expressions like Foo.Bar () to help debugger to
1115 // break at right place when LHS expression can be stepped-into
1117 ec.MarkCallEntry (loc.Value);
1121 // Set instance expression to actual result expression. When it contains await it can be
1122 // picked up by caller
1124 InstanceExpression = instance_copy;
1126 if (method.Parameters.HasArglist) {
1127 var varargs_types = GetVarargsTypes (method, Arguments);
1128 ec.Emit (call_op, method, varargs_types);
1133 // and DoFoo is not virtual, you can omit the callvirt,
1134 // because you don't need the null checking behavior.
1136 ec.Emit (call_op, method);
1140 // Pop the return value if there is one and stack should be empty
1142 if (statement && method.ReturnType.Kind != MemberKind.Void)
1143 ec.Emit (OpCodes.Pop);
1146 static MetaType[] GetVarargsTypes (MethodSpec method, Arguments arguments)
1148 AParametersCollection pd = method.Parameters;
1150 Argument a = arguments[pd.Count - 1];
1151 Arglist list = (Arglist) a.Expr;
1153 return list.ArgumentTypes;
1157 // Used to decide whether call or callvirt is needed
1159 static bool IsVirtualCallRequired (Expression instance, MethodSpec method)
1162 // There are 2 scenarious where we emit callvirt
1164 // Case 1: A method is virtual and it's not used to call base
1165 // Case 2: A method instance expression can be null. In this casen callvirt ensures
1166 // correct NRE exception when the method is called
1168 var decl_type = method.DeclaringType;
1169 if (decl_type.IsStruct || decl_type.IsEnum)
1172 if (instance is BaseThis)
1176 // It's non-virtual and will never be null and it can be determined
1177 // whether it's known value or reference type by verifier
1179 if (!method.IsVirtual && Expression.IsNeverNull (instance) && !instance.Type.IsGenericParameter)
1185 static bool IsAddressCall (Expression instance, OpCode callOpcode, TypeSpec declaringType)
1187 var instance_type = instance.Type;
1188 return (instance_type.IsStructOrEnum && (callOpcode == OpCodes.Callvirt || (callOpcode == OpCodes.Call && declaringType.IsStruct))) ||
1189 instance_type.IsGenericParameter || declaringType.IsNullableType;
1193 public struct InstanceEmitter
1195 readonly Expression instance;
1196 readonly bool addressRequired;
1198 public InstanceEmitter (Expression instance, bool addressLoad)
1200 this.instance = instance;
1201 this.addressRequired = addressLoad;
1204 public void Emit (EmitContext ec, bool conditionalAccess)
1206 Label NullOperatorLabel;
1207 Nullable.Unwrap unwrap;
1209 if (conditionalAccess && Expression.IsNeverNull (instance))
1210 conditionalAccess = false;
1212 if (conditionalAccess) {
1213 NullOperatorLabel = ec.DefineLabel ();
1214 unwrap = instance as Nullable.Unwrap;
1216 NullOperatorLabel = new Label ();
1220 IMemoryLocation instance_address = null;
1221 bool conditional_access_dup = false;
1223 if (unwrap != null) {
1225 unwrap.EmitCheck (ec);
1226 ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel);
1228 if (conditionalAccess && addressRequired) {
1230 // Don't allocate temp variable when instance load is cheap and load and load-address
1231 // operate on same memory
1233 instance_address = instance as VariableReference;
1234 if (instance_address == null)
1235 instance_address = instance as LocalTemporary;
1237 if (instance_address == null) {
1238 EmitLoad (ec, false);
1239 ec.Emit (OpCodes.Dup);
1240 ec.EmitLoadFromPtr (instance.Type);
1242 conditional_access_dup = true;
1247 EmitLoad (ec, !conditionalAccess);
1249 if (conditionalAccess) {
1250 conditional_access_dup = !ExpressionAnalyzer.IsInexpensiveLoad (instance);
1251 if (conditional_access_dup)
1252 ec.Emit (OpCodes.Dup);
1256 if (conditionalAccess) {
1257 if (instance.Type.Kind == MemberKind.TypeParameter)
1258 ec.Emit (OpCodes.Box, instance.Type);
1260 ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel);
1262 if (conditional_access_dup)
1263 ec.Emit (OpCodes.Pop);
1267 if (conditionalAccess) {
1268 if (!ec.ConditionalAccess.Statement) {
1269 var t = ec.ConditionalAccess.Type;
1270 if (t.IsNullableType)
1271 Nullable.LiftedNull.Create (t, Location.Null).Emit (ec);
1275 if (t.IsGenericParameter)
1276 ec.Emit (OpCodes.Unbox_Any, t);
1280 ec.Emit (OpCodes.Br, ec.ConditionalAccess.EndLabel);
1281 ec.MarkLabel (NullOperatorLabel);
1283 if (instance_address != null) {
1284 instance_address.AddressOf (ec, AddressOp.Load);
1285 } else if (unwrap != null) {
1287 if (addressRequired) {
1288 var tmp = ec.GetTemporaryLocal (unwrap.Type);
1289 ec.Emit (OpCodes.Stloc, tmp);
1290 ec.Emit (OpCodes.Ldloca, tmp);
1291 ec.FreeTemporaryLocal (tmp, unwrap.Type);
1293 } else if (!conditional_access_dup) {
1299 public void EmitLoad (EmitContext ec, bool boxInstance)
1301 var instance_type = instance.Type;
1304 // Push the instance expression
1306 if (addressRequired) {
1308 // If the expression implements IMemoryLocation, then
1309 // we can optimize and use AddressOf on the
1312 // If not we have to use some temporary storage for
1314 var iml = instance as IMemoryLocation;
1316 iml.AddressOf (ec, AddressOp.Load);
1318 LocalTemporary temp = new LocalTemporary (instance_type);
1321 temp.AddressOf (ec, AddressOp.Load);
1329 // Only to make verifier happy
1330 if (boxInstance && ExpressionAnalyzer.RequiresBoxing (instance)) {
1331 ec.Emit (OpCodes.Box, instance_type);
1335 public TypeSpec GetStackType (EmitContext ec)
1337 var instance_type = instance.Type;
1339 if (addressRequired)
1340 return ReferenceContainer.MakeType (ec.Module, instance_type);
1342 if (instance_type.IsStructOrEnum)
1343 return ec.Module.Compiler.BuiltinTypes.Object;
1345 return instance_type;
1351 static class ExpressionAnalyzer
1353 public static bool RequiresBoxing (Expression instance)
1355 var instance_type = instance.Type;
1356 if (instance_type.IsGenericParameter && !(instance is This) && TypeSpec.IsReferenceType (instance_type))
1359 if (instance_type.IsStructOrEnum)
1366 // Returns true for cheap race-free load, where we can avoid using dup
1368 public static bool IsInexpensiveLoad (Expression instance)
1370 if (instance is Constant)
1371 return instance.IsSideEffectFree;
1373 if (RequiresBoxing (instance))
1376 var vr = instance as VariableReference;
1378 // Load from captured local would be racy without dup
1379 return !vr.IsRef && !vr.IsHoisted;
1382 if (instance is LocalTemporary)
1385 var fe = instance as FieldExpr;
1387 return fe.IsStatic || fe.InstanceExpression is This;