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 TypeSpec CurrentType {
125 get { return member_context.CurrentType; }
128 public TypeParameters CurrentTypeParameters {
129 get { return member_context.CurrentTypeParameters; }
132 public MemberCore CurrentTypeDefinition {
133 get { return member_context.CurrentMemberDefinition; }
136 public bool EmitAccurateDebugInfo {
138 return (flags & Options.AccurateDebugInfo) != 0;
142 public bool HasMethodSymbolBuilder {
144 return methodSymbols != null;
148 public bool HasReturnLabel {
150 return return_label.HasValue;
154 public bool IsStatic {
155 get { return member_context.IsStatic; }
158 public bool IsStaticConstructor {
160 return member_context.IsStatic && (flags & Options.ConstructorScope) != 0;
164 public bool IsAnonymousStoreyMutateRequired {
166 return CurrentAnonymousMethod != null &&
167 CurrentAnonymousMethod.Storey != null &&
168 CurrentAnonymousMethod.Storey.Mutator != null;
172 public IMemberContext MemberContext {
174 return member_context;
178 public ModuleContainer Module {
180 return member_context.Module;
184 public bool NotifyEvaluatorOnStore {
186 return Module.Evaluator != null && Module.Evaluator.ModificationListener != null;
190 // Has to be used for specific emitter errors only any
191 // possible resolver errors have to be reported during Resolve
192 public Report Report {
194 return member_context.Module.Compiler.Report;
198 public TypeSpec ReturnType {
205 // The label where we have to jump before leaving the context
207 public Label ReturnLabel {
209 return return_label.Value;
213 public List<IExpressionCleanup> StatementEpilogue {
215 return epilogue_expressions;
219 public LocalVariable AsyncThrowVariable { get; set; }
221 public List<TryFinally> TryFinallyUnwind { get; set; }
225 public void AddStatementEpilog (IExpressionCleanup cleanupExpression)
227 if (epilogue_expressions == null) {
228 epilogue_expressions = new List<IExpressionCleanup> ();
229 } else if (epilogue_expressions.Contains (cleanupExpression)) {
233 epilogue_expressions.Add (cleanupExpression);
236 public void AssertEmptyStack ()
239 if (ig.__StackHeight != 0)
240 throw new InternalErrorException ("Await yields with non-empty stack in `{0}",
241 member_context.GetSignatureForError ());
246 /// This is called immediately before emitting an IL opcode to tell the symbol
247 /// writer to which source line this opcode belongs.
249 public bool Mark (Location loc)
251 if ((flags & Options.OmitDebugInfo) != 0)
254 if (loc.IsNull || methodSymbols == null)
257 var sf = loc.SourceFile;
258 if (sf.IsHiddenLocation (loc))
262 methodSymbols.MarkSequencePoint (ig.ILOffset, sf.SourceFileEntry, loc.Row, loc.Column, false);
267 public void MarkCallEntry (Location loc)
269 if (!EmitAccurateDebugInfo)
273 // TODO: This should emit different kind of sequence point to make
274 // step-over work for statement over multiple lines
276 // Debugging experience for Foo (A () + B ()) where A and B are
277 // on separate lines is not great
282 public void DefineLocalVariable (string name, LocalBuilder builder)
284 if ((flags & Options.OmitDebugInfo) != 0)
287 methodSymbols.AddLocal (builder.LocalIndex, name);
290 public void BeginCatchBlock (TypeSpec type)
292 if (IsAnonymousStoreyMutateRequired)
293 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
295 ig.BeginCatchBlock (type.GetMetaInfo ());
298 public void BeginFilterHandler ()
300 ig.BeginCatchBlock (null);
303 public void BeginExceptionBlock ()
305 ig.BeginExceptionBlock ();
308 public void BeginExceptionFilterBlock ()
310 ig.BeginExceptFilterBlock ();
313 public void BeginFinallyBlock ()
315 ig.BeginFinallyBlock ();
318 public void BeginScope ()
320 if ((flags & Options.OmitDebugInfo) != 0)
324 methodSymbols.StartBlock (CodeBlockEntry.Type.Lexical, ig.ILOffset);
328 public void BeginCompilerScope ()
330 if ((flags & Options.OmitDebugInfo) != 0)
334 methodSymbols.StartBlock (CodeBlockEntry.Type.CompilerGenerated, ig.ILOffset);
338 public void EndExceptionBlock ()
340 ig.EndExceptionBlock ();
343 public void EndScope ()
345 if ((flags & Options.OmitDebugInfo) != 0)
349 methodSymbols.EndBlock (ig.ILOffset);
354 // Creates a nested container in this context for all dynamic compiler generated stuff
356 internal DynamicSiteClass CreateDynamicSite ()
358 if (dynamic_site_container == null) {
359 var mc = member_context.CurrentMemberDefinition as MemberBase;
360 dynamic_site_container = new DynamicSiteClass (CurrentTypeDefinition.Parent.PartialContainer, mc, member_context.CurrentTypeParameters);
362 CurrentTypeDefinition.Module.AddCompilerGeneratedClass (dynamic_site_container);
363 dynamic_site_container.CreateContainer ();
364 dynamic_site_container.DefineContainer ();
365 dynamic_site_container.Define ();
367 var inflator = new TypeParameterInflator (Module, CurrentType, TypeParameterSpec.EmptyTypes, TypeSpec.EmptyTypes);
368 var inflated = dynamic_site_container.CurrentType.InflateMember (inflator);
369 CurrentType.MemberCache.AddMember (inflated);
372 return dynamic_site_container;
375 public Label CreateReturnLabel ()
377 if (!return_label.HasValue)
378 return_label = DefineLabel ();
380 return return_label.Value;
383 public LocalBuilder DeclareLocal (TypeSpec type, bool pinned)
385 if (IsAnonymousStoreyMutateRequired)
386 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
388 return ig.DeclareLocal (type.GetMetaInfo (), pinned);
391 public Label DefineLabel ()
393 return ig.DefineLabel ();
397 // Creates temporary field in current async storey
399 public StackFieldExpr GetTemporaryField (TypeSpec type, bool initializedFieldRequired = false)
401 var f = AsyncTaskStorey.AddCapturedLocalVariable (type, initializedFieldRequired);
402 var fexpr = new StackFieldExpr (f);
403 fexpr.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location.Null);
407 public void MarkLabel (Label label)
409 ig.MarkLabel (label);
412 public void Emit (OpCode opcode)
417 public void Emit (OpCode opcode, LocalBuilder local)
419 ig.Emit (opcode, local);
422 public void Emit (OpCode opcode, string arg)
424 ig.Emit (opcode, arg);
427 public void Emit (OpCode opcode, double arg)
429 ig.Emit (opcode, arg);
432 public void Emit (OpCode opcode, float arg)
434 ig.Emit (opcode, arg);
437 public void Emit (OpCode opcode, Label label)
439 ig.Emit (opcode, label);
442 public void Emit (OpCode opcode, Label[] labels)
444 ig.Emit (opcode, labels);
447 public void Emit (OpCode opcode, TypeSpec type)
449 if (IsAnonymousStoreyMutateRequired)
450 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
452 ig.Emit (opcode, type.GetMetaInfo ());
455 public void Emit (OpCode opcode, FieldSpec field)
457 if (IsAnonymousStoreyMutateRequired)
458 field = field.Mutate (CurrentAnonymousMethod.Storey.Mutator);
460 ig.Emit (opcode, field.GetMetaInfo ());
463 public void Emit (OpCode opcode, MethodSpec method)
465 if (IsAnonymousStoreyMutateRequired)
466 method = method.Mutate (CurrentAnonymousMethod.Storey.Mutator);
468 if (method.IsConstructor)
469 ig.Emit (opcode, (ConstructorInfo) method.GetMetaInfo ());
471 ig.Emit (opcode, (MethodInfo) method.GetMetaInfo ());
474 // TODO: REMOVE breaks mutator
475 public void Emit (OpCode opcode, MethodInfo method)
477 ig.Emit (opcode, method);
480 public void Emit (OpCode opcode, MethodSpec method, MetaType[] vargs)
482 // TODO MemberCache: This should mutate too
483 ig.EmitCall (opcode, (MethodInfo) method.GetMetaInfo (), vargs);
486 public void EmitArrayNew (ArrayContainer ac)
489 var type = IsAnonymousStoreyMutateRequired ?
490 CurrentAnonymousMethod.Storey.Mutator.Mutate (ac.Element) :
493 ig.Emit (OpCodes.Newarr, type.GetMetaInfo ());
495 if (IsAnonymousStoreyMutateRequired)
496 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
498 ig.Emit (OpCodes.Newobj, ac.GetConstructor ());
502 public void EmitArrayAddress (ArrayContainer ac)
505 if (IsAnonymousStoreyMutateRequired)
506 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
508 ig.Emit (OpCodes.Call, ac.GetAddressMethod ());
510 var type = IsAnonymousStoreyMutateRequired ?
511 CurrentAnonymousMethod.Storey.Mutator.Mutate (ac.Element) :
514 ig.Emit (OpCodes.Ldelema, type.GetMetaInfo ());
519 // Emits the right opcode to load from an array
521 public void EmitArrayLoad (ArrayContainer ac)
524 if (IsAnonymousStoreyMutateRequired)
525 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
527 ig.Emit (OpCodes.Call, ac.GetGetMethod ());
532 var type = ac.Element;
533 if (type.Kind == MemberKind.Enum)
534 type = EnumSpec.GetUnderlyingType (type);
536 switch (type.BuiltinType) {
537 case BuiltinTypeSpec.Type.Bool:
539 // Workaround MSIL limitation. Load bool element as single bit,
540 // bool array can actually store any byte value
542 ig.Emit (OpCodes.Ldelem_U1);
543 ig.Emit (OpCodes.Ldc_I4_0);
544 ig.Emit (OpCodes.Cgt_Un);
546 case BuiltinTypeSpec.Type.Byte:
547 ig.Emit (OpCodes.Ldelem_U1);
549 case BuiltinTypeSpec.Type.SByte:
550 ig.Emit (OpCodes.Ldelem_I1);
552 case BuiltinTypeSpec.Type.Short:
553 ig.Emit (OpCodes.Ldelem_I2);
555 case BuiltinTypeSpec.Type.UShort:
556 case BuiltinTypeSpec.Type.Char:
557 ig.Emit (OpCodes.Ldelem_U2);
559 case BuiltinTypeSpec.Type.Int:
560 ig.Emit (OpCodes.Ldelem_I4);
562 case BuiltinTypeSpec.Type.UInt:
563 ig.Emit (OpCodes.Ldelem_U4);
565 case BuiltinTypeSpec.Type.ULong:
566 case BuiltinTypeSpec.Type.Long:
567 ig.Emit (OpCodes.Ldelem_I8);
569 case BuiltinTypeSpec.Type.Float:
570 ig.Emit (OpCodes.Ldelem_R4);
572 case BuiltinTypeSpec.Type.Double:
573 ig.Emit (OpCodes.Ldelem_R8);
575 case BuiltinTypeSpec.Type.IntPtr:
576 ig.Emit (OpCodes.Ldelem_I);
580 case MemberKind.Struct:
581 if (IsAnonymousStoreyMutateRequired)
582 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
584 ig.Emit (OpCodes.Ldelema, type.GetMetaInfo ());
585 ig.Emit (OpCodes.Ldobj, type.GetMetaInfo ());
587 case MemberKind.TypeParameter:
588 if (IsAnonymousStoreyMutateRequired)
589 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
591 ig.Emit (OpCodes.Ldelem, type.GetMetaInfo ());
593 case MemberKind.PointerType:
594 ig.Emit (OpCodes.Ldelem_I);
597 ig.Emit (OpCodes.Ldelem_Ref);
605 // Emits the right opcode to store to an array
607 public void EmitArrayStore (ArrayContainer ac)
610 if (IsAnonymousStoreyMutateRequired)
611 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
613 ig.Emit (OpCodes.Call, ac.GetSetMethod ());
617 var type = ac.Element;
619 if (type.Kind == MemberKind.Enum)
620 type = EnumSpec.GetUnderlyingType (type);
622 switch (type.BuiltinType) {
623 case BuiltinTypeSpec.Type.Byte:
624 case BuiltinTypeSpec.Type.SByte:
625 case BuiltinTypeSpec.Type.Bool:
626 Emit (OpCodes.Stelem_I1);
628 case BuiltinTypeSpec.Type.Short:
629 case BuiltinTypeSpec.Type.UShort:
630 case BuiltinTypeSpec.Type.Char:
631 Emit (OpCodes.Stelem_I2);
633 case BuiltinTypeSpec.Type.Int:
634 case BuiltinTypeSpec.Type.UInt:
635 Emit (OpCodes.Stelem_I4);
637 case BuiltinTypeSpec.Type.Long:
638 case BuiltinTypeSpec.Type.ULong:
639 Emit (OpCodes.Stelem_I8);
641 case BuiltinTypeSpec.Type.Float:
642 Emit (OpCodes.Stelem_R4);
644 case BuiltinTypeSpec.Type.Double:
645 Emit (OpCodes.Stelem_R8);
650 case MemberKind.Struct:
651 Emit (OpCodes.Stobj, type);
653 case MemberKind.TypeParameter:
654 Emit (OpCodes.Stelem, type);
656 case MemberKind.PointerType:
657 Emit (OpCodes.Stelem_I);
660 Emit (OpCodes.Stelem_Ref);
665 public void EmitInt (int i)
670 void EmitIntConstant (int i)
674 ig.Emit (OpCodes.Ldc_I4_M1);
678 ig.Emit (OpCodes.Ldc_I4_0);
682 ig.Emit (OpCodes.Ldc_I4_1);
686 ig.Emit (OpCodes.Ldc_I4_2);
690 ig.Emit (OpCodes.Ldc_I4_3);
694 ig.Emit (OpCodes.Ldc_I4_4);
698 ig.Emit (OpCodes.Ldc_I4_5);
702 ig.Emit (OpCodes.Ldc_I4_6);
706 ig.Emit (OpCodes.Ldc_I4_7);
710 ig.Emit (OpCodes.Ldc_I4_8);
714 if (i >= -128 && i <= 127) {
715 ig.Emit (OpCodes.Ldc_I4_S, (sbyte) i);
717 ig.Emit (OpCodes.Ldc_I4, i);
722 public void EmitLong (long l)
724 if (l >= int.MinValue && l <= int.MaxValue) {
725 EmitIntConstant (unchecked ((int) l));
726 ig.Emit (OpCodes.Conv_I8);
727 } else if (l >= 0 && l <= uint.MaxValue) {
728 EmitIntConstant (unchecked ((int) l));
729 ig.Emit (OpCodes.Conv_U8);
731 ig.Emit (OpCodes.Ldc_I8, l);
736 // Load the object from the pointer.
738 public void EmitLoadFromPtr (TypeSpec type)
740 if (type.Kind == MemberKind.Enum)
741 type = EnumSpec.GetUnderlyingType (type);
743 switch (type.BuiltinType) {
744 case BuiltinTypeSpec.Type.Int:
745 ig.Emit (OpCodes.Ldind_I4);
747 case BuiltinTypeSpec.Type.UInt:
748 ig.Emit (OpCodes.Ldind_U4);
750 case BuiltinTypeSpec.Type.Short:
751 ig.Emit (OpCodes.Ldind_I2);
753 case BuiltinTypeSpec.Type.UShort:
754 case BuiltinTypeSpec.Type.Char:
755 ig.Emit (OpCodes.Ldind_U2);
757 case BuiltinTypeSpec.Type.Byte:
758 ig.Emit (OpCodes.Ldind_U1);
760 case BuiltinTypeSpec.Type.SByte:
761 ig.Emit (OpCodes.Ldind_I1);
763 case BuiltinTypeSpec.Type.Bool:
764 ig.Emit (OpCodes.Ldind_I1);
765 ig.Emit (OpCodes.Ldc_I4_0);
766 ig.Emit (OpCodes.Cgt_Un);
768 case BuiltinTypeSpec.Type.ULong:
769 case BuiltinTypeSpec.Type.Long:
770 ig.Emit (OpCodes.Ldind_I8);
772 case BuiltinTypeSpec.Type.Float:
773 ig.Emit (OpCodes.Ldind_R4);
775 case BuiltinTypeSpec.Type.Double:
776 ig.Emit (OpCodes.Ldind_R8);
778 case BuiltinTypeSpec.Type.IntPtr:
779 ig.Emit (OpCodes.Ldind_I);
783 case MemberKind.Struct:
784 case MemberKind.TypeParameter:
785 if (IsAnonymousStoreyMutateRequired)
786 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
788 ig.Emit (OpCodes.Ldobj, type.GetMetaInfo ());
790 case MemberKind.PointerType:
791 ig.Emit (OpCodes.Ldind_I);
794 ig.Emit (OpCodes.Ldind_Ref);
801 public void EmitNull ()
803 ig.Emit (OpCodes.Ldnull);
806 public void EmitArgumentAddress (int pos)
811 if (pos > byte.MaxValue)
812 ig.Emit (OpCodes.Ldarga, pos);
814 ig.Emit (OpCodes.Ldarga_S, (byte) pos);
817 public void EmitArgumentLoad (int pos)
823 case 0: ig.Emit (OpCodes.Ldarg_0); break;
824 case 1: ig.Emit (OpCodes.Ldarg_1); break;
825 case 2: ig.Emit (OpCodes.Ldarg_2); break;
826 case 3: ig.Emit (OpCodes.Ldarg_3); break;
828 if (pos > byte.MaxValue)
829 ig.Emit (OpCodes.Ldarg, pos);
831 ig.Emit (OpCodes.Ldarg_S, (byte) pos);
836 public void EmitArgumentStore (int pos)
841 if (pos > byte.MaxValue)
842 ig.Emit (OpCodes.Starg, pos);
844 ig.Emit (OpCodes.Starg_S, (byte) pos);
848 // The stack contains the pointer and the value of type `type'
850 public void EmitStoreFromPtr (TypeSpec type)
853 type = EnumSpec.GetUnderlyingType (type);
855 switch (type.BuiltinType) {
856 case BuiltinTypeSpec.Type.Int:
857 case BuiltinTypeSpec.Type.UInt:
858 ig.Emit (OpCodes.Stind_I4);
860 case BuiltinTypeSpec.Type.Long:
861 case BuiltinTypeSpec.Type.ULong:
862 ig.Emit (OpCodes.Stind_I8);
864 case BuiltinTypeSpec.Type.Char:
865 case BuiltinTypeSpec.Type.Short:
866 case BuiltinTypeSpec.Type.UShort:
867 ig.Emit (OpCodes.Stind_I2);
869 case BuiltinTypeSpec.Type.Float:
870 ig.Emit (OpCodes.Stind_R4);
872 case BuiltinTypeSpec.Type.Double:
873 ig.Emit (OpCodes.Stind_R8);
875 case BuiltinTypeSpec.Type.Byte:
876 case BuiltinTypeSpec.Type.SByte:
877 case BuiltinTypeSpec.Type.Bool:
878 ig.Emit (OpCodes.Stind_I1);
880 case BuiltinTypeSpec.Type.IntPtr:
881 ig.Emit (OpCodes.Stind_I);
886 case MemberKind.Struct:
887 case MemberKind.TypeParameter:
888 if (IsAnonymousStoreyMutateRequired)
889 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
891 ig.Emit (OpCodes.Stobj, type.GetMetaInfo ());
894 ig.Emit (OpCodes.Stind_Ref);
899 public void EmitThis ()
901 ig.Emit (OpCodes.Ldarg_0);
904 public void EmitEpilogue ()
906 if (epilogue_expressions == null)
909 foreach (var e in epilogue_expressions)
910 e.EmitCleanup (this);
912 epilogue_expressions = null;
916 /// Returns a temporary storage for a variable of type t as
917 /// a local variable in the current body.
919 public LocalBuilder GetTemporaryLocal (TypeSpec t)
921 if (temporary_storage != null) {
923 if (temporary_storage.TryGetValue (t, out o)) {
924 if (o is Stack<LocalBuilder>) {
925 var s = (Stack<LocalBuilder>) o;
926 o = s.Count == 0 ? null : s.Pop ();
928 temporary_storage.Remove (t);
932 return (LocalBuilder) o;
934 return DeclareLocal (t, false);
937 public void FreeTemporaryLocal (LocalBuilder b, TypeSpec t)
939 if (temporary_storage == null) {
940 temporary_storage = new Dictionary<TypeSpec, object> (ReferenceEquality<TypeSpec>.Default);
941 temporary_storage.Add (t, b);
946 if (!temporary_storage.TryGetValue (t, out o)) {
947 temporary_storage.Add (t, b);
950 var s = o as Stack<LocalBuilder>;
952 s = new Stack<LocalBuilder> ();
953 s.Push ((LocalBuilder)o);
954 temporary_storage [t] = s;
960 /// ReturnValue creates on demand the LocalBuilder for the
961 /// return value from the function. By default this is not
962 /// used. This is only required when returns are found inside
963 /// Try or Catch statements.
965 /// This method is typically invoked from the Emit phase, so
966 /// we allow the creation of a return label if it was not
967 /// requested during the resolution phase. Could be cleaned
968 /// up, but it would replicate a lot of logic in the Emit phase
969 /// of the code that uses it.
971 public LocalBuilder TemporaryReturn ()
973 if (return_value == null){
974 return_value = DeclareLocal (return_type, false);
983 public Expression InstanceExpression;
986 // When call has to leave an extra copy of all arguments on the stack
988 public bool DuplicateArguments;
991 // Does not emit InstanceExpression load when InstanceExpressionOnStack
992 // is set. Used by compound assignments.
994 public bool InstanceExpressionOnStack;
997 // Any of arguments contains await expression
999 public bool HasAwaitArguments;
1001 public bool NullShortCircuit;
1004 // When dealing with await arguments the original arguments are converted
1005 // into a new set with hoisted stack results
1007 public Arguments EmittedArguments;
1009 public Label NullOperatorLabel;
1011 public void Emit (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc)
1013 EmitPredefined (ec, method, Arguments, false, loc);
1016 public void EmitStatement (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc)
1018 EmitPredefined (ec, method, Arguments, true, loc);
1021 public void EmitPredefined (EmitContext ec, MethodSpec method, Arguments Arguments, bool statement = false, Location? loc = null)
1023 Expression instance_copy = null;
1025 if (!HasAwaitArguments && ec.HasSet (BuilderContext.Options.AsyncBody)) {
1026 HasAwaitArguments = Arguments != null && Arguments.ContainsEmitWithAwait ();
1027 if (HasAwaitArguments && InstanceExpressionOnStack) {
1028 throw new NotSupportedException ();
1033 LocalTemporary lt = null;
1034 InstanceEmitter ie = new InstanceEmitter ();
1036 if (method.IsStatic) {
1037 call_op = OpCodes.Call;
1039 if (IsVirtualCallRequired (InstanceExpression, method)) {
1040 call_op = OpCodes.Callvirt;
1042 call_op = OpCodes.Call;
1045 if (HasAwaitArguments) {
1046 instance_copy = InstanceExpression.EmitToField (ec);
1047 ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
1049 if (Arguments == null) {
1052 } else if (!InstanceExpressionOnStack) {
1053 ie = new InstanceEmitter (InstanceExpression, IsAddressCall (InstanceExpression, call_op, method.DeclaringType));
1054 ie.NullShortCircuit = NullShortCircuit;
1057 if (NullShortCircuit) {
1058 NullOperatorLabel = ie.NullOperatorLabel;
1061 if (DuplicateArguments) {
1062 ec.Emit (OpCodes.Dup);
1063 if (Arguments != null && Arguments.Count != 0) {
1064 lt = new LocalTemporary (ie.GetStackType (ec));
1072 if (Arguments != null && !InstanceExpressionOnStack) {
1073 EmittedArguments = Arguments.Emit (ec, DuplicateArguments, HasAwaitArguments);
1074 if (EmittedArguments != null) {
1075 if (instance_copy != null) {
1076 ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
1083 EmittedArguments.Emit (ec);
1087 if (call_op == OpCodes.Callvirt && (InstanceExpression.Type.IsGenericParameter || InstanceExpression.Type.IsStructOrEnum)) {
1088 ec.Emit (OpCodes.Constrained, InstanceExpression.Type);
1093 // Emit explicit sequence point for expressions like Foo.Bar () to help debugger to
1094 // break at right place when LHS expression can be stepped-into
1096 ec.MarkCallEntry (loc.Value);
1100 // Set instance expression to actual result expression. When it contains await it can be
1101 // picked up by caller
1103 InstanceExpression = instance_copy;
1105 if (method.Parameters.HasArglist) {
1106 var varargs_types = GetVarargsTypes (method, Arguments);
1107 ec.Emit (call_op, method, varargs_types);
1112 // and DoFoo is not virtual, you can omit the callvirt,
1113 // because you don't need the null checking behavior.
1115 ec.Emit (call_op, method);
1119 // Pop the return value if there is one and stack should be empty
1121 if (statement && method.ReturnType.Kind != MemberKind.Void)
1122 ec.Emit (OpCodes.Pop);
1124 if (NullShortCircuit && !DuplicateArguments) {
1125 ie.EmitResultLift (ec, method.ReturnType, statement);
1129 static MetaType[] GetVarargsTypes (MethodSpec method, Arguments arguments)
1131 AParametersCollection pd = method.Parameters;
1133 Argument a = arguments[pd.Count - 1];
1134 Arglist list = (Arglist) a.Expr;
1136 return list.ArgumentTypes;
1140 // Used to decide whether call or callvirt is needed
1142 static bool IsVirtualCallRequired (Expression instance, MethodSpec method)
1145 // There are 2 scenarious where we emit callvirt
1147 // Case 1: A method is virtual and it's not used to call base
1148 // Case 2: A method instance expression can be null. In this casen callvirt ensures
1149 // correct NRE exception when the method is called
1151 var decl_type = method.DeclaringType;
1152 if (decl_type.IsStruct || decl_type.IsEnum)
1155 if (instance is BaseThis)
1159 // It's non-virtual and will never be null and it can be determined
1160 // whether it's known value or reference type by verifier
1162 if (!method.IsVirtual && (instance is This || instance is New || instance is ArrayCreation || instance is DelegateCreation) &&
1163 !instance.Type.IsGenericParameter)
1169 static bool IsAddressCall (Expression instance, OpCode callOpcode, TypeSpec declaringType)
1171 var instance_type = instance.Type;
1172 return (instance_type.IsStructOrEnum && (callOpcode == OpCodes.Callvirt || (callOpcode == OpCodes.Call && declaringType.IsStruct))) ||
1173 instance_type.IsGenericParameter || declaringType.IsNullableType;
1177 public struct InstanceEmitter
1179 readonly Expression instance;
1180 readonly bool addressRequired;
1181 bool value_on_stack;
1183 public bool NullShortCircuit;
1184 public Label NullOperatorLabel;
1186 public InstanceEmitter (Expression instance, bool addressLoad)
1188 this.instance = instance;
1189 this.addressRequired = addressLoad;
1190 NullShortCircuit = false;
1191 NullOperatorLabel = new Label ();
1192 value_on_stack = false;
1195 public void Emit (EmitContext ec)
1197 Nullable.Unwrap unwrap;
1199 if (NullShortCircuit) {
1200 NullOperatorLabel = ec.DefineLabel ();
1201 unwrap = instance as Nullable.Unwrap;
1206 if (unwrap != null) {
1208 unwrap.EmitCheck (ec);
1209 ec.Emit (OpCodes.Brfalse, NullOperatorLabel);
1211 var tmp = ec.GetTemporaryLocal (unwrap.Type);
1212 ec.Emit (OpCodes.Stloc, tmp);
1213 ec.Emit (OpCodes.Ldloca, tmp);
1214 ec.FreeTemporaryLocal (tmp, unwrap.Type);
1220 if (NullShortCircuit) {
1221 ec.Emit (OpCodes.Dup);
1222 ec.Emit (OpCodes.Brfalse, NullOperatorLabel);
1225 value_on_stack = true;
1228 public void EmitLoad (EmitContext ec)
1230 var instance_type = instance.Type;
1233 // Push the instance expression
1235 if (addressRequired) {
1237 // If the expression implements IMemoryLocation, then
1238 // we can optimize and use AddressOf on the
1241 // If not we have to use some temporary storage for
1243 var iml = instance as IMemoryLocation;
1245 iml.AddressOf (ec, AddressOp.Load);
1247 LocalTemporary temp = new LocalTemporary (instance_type);
1250 temp.AddressOf (ec, AddressOp.Load);
1258 // Only to make verifier happy
1259 if (instance_type.IsGenericParameter && !(instance is This) && TypeSpec.IsReferenceType (instance_type)) {
1260 ec.Emit (OpCodes.Box, instance_type);
1261 } else if (instance_type.IsStructOrEnum) {
1262 ec.Emit (OpCodes.Box, instance_type);
1266 public void EmitResultLift (EmitContext ec, TypeSpec type, bool statement)
1268 if (!NullShortCircuit)
1269 throw new InternalErrorException ();
1271 bool value_rt = TypeSpec.IsValueType (type);
1274 if (type.IsNullableType)
1277 lifted = Nullable.NullableInfo.MakeType (ec.Module, type);
1278 ec.Emit (OpCodes.Newobj, Nullable.NullableInfo.GetConstructor (lifted));
1284 var end = ec.DefineLabel ();
1285 if (value_on_stack || !statement) {
1286 ec.Emit (OpCodes.Br_S, end);
1289 ec.MarkLabel (NullOperatorLabel);
1292 ec.Emit (OpCodes.Pop);
1296 Nullable.LiftedNull.Create (lifted, Location.Null).Emit (ec);
1304 public TypeSpec GetStackType (EmitContext ec)
1306 var instance_type = instance.Type;
1308 if (addressRequired)
1309 return ReferenceContainer.MakeType (ec.Module, instance_type);
1311 if (instance_type.IsStructOrEnum)
1312 return ec.Module.Compiler.BuiltinTypes.Object;
1314 return instance_type;