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 IsAnonymousStoreyMutateRequired {
160 return CurrentAnonymousMethod != null &&
161 CurrentAnonymousMethod.Storey != null &&
162 CurrentAnonymousMethod.Storey.Mutator != null;
166 public IMemberContext MemberContext {
168 return member_context;
172 public ModuleContainer Module {
174 return member_context.Module;
178 public bool NotifyEvaluatorOnStore {
180 return Module.Evaluator != null && Module.Evaluator.ModificationListener != null;
184 // Has to be used for specific emitter errors only any
185 // possible resolver errors have to be reported during Resolve
186 public Report Report {
188 return member_context.Module.Compiler.Report;
192 public TypeSpec ReturnType {
199 // The label where we have to jump before leaving the context
201 public Label ReturnLabel {
203 return return_label.Value;
207 public List<IExpressionCleanup> StatementEpilogue {
209 return epilogue_expressions;
213 public LocalVariable AsyncThrowVariable { get; set; }
215 public List<TryFinally> TryFinallyUnwind { get; set; }
219 public void AddStatementEpilog (IExpressionCleanup cleanupExpression)
221 if (epilogue_expressions == null) {
222 epilogue_expressions = new List<IExpressionCleanup> ();
223 } else if (epilogue_expressions.Contains (cleanupExpression)) {
227 epilogue_expressions.Add (cleanupExpression);
230 public void AssertEmptyStack ()
233 if (ig.__StackHeight != 0)
234 throw new InternalErrorException ("Await yields with non-empty stack in `{0}",
235 member_context.GetSignatureForError ());
240 /// This is called immediately before emitting an IL opcode to tell the symbol
241 /// writer to which source line this opcode belongs.
243 public bool Mark (Location loc)
245 if ((flags & Options.OmitDebugInfo) != 0)
248 if (loc.IsNull || methodSymbols == null)
251 var sf = loc.SourceFile;
252 if (sf.IsHiddenLocation (loc))
256 methodSymbols.MarkSequencePoint (ig.ILOffset, sf.SourceFileEntry, loc.Row, loc.Column, false);
261 public void MarkCallEntry (Location loc)
263 if (!EmitAccurateDebugInfo)
267 // TODO: This should emit different kind of sequence point to make
268 // step-over work for statement over multiple lines
270 // Debugging experience for Foo (A () + B ()) where A and B are
271 // on separate lines is not great
276 public void DefineLocalVariable (string name, LocalBuilder builder)
278 if ((flags & Options.OmitDebugInfo) != 0)
281 methodSymbols.AddLocal (builder.LocalIndex, name);
284 public void BeginCatchBlock (TypeSpec type)
286 if (IsAnonymousStoreyMutateRequired)
287 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
289 ig.BeginCatchBlock (type.GetMetaInfo ());
292 public void BeginFilterHandler ()
294 ig.BeginCatchBlock (null);
297 public void BeginExceptionBlock ()
299 ig.BeginExceptionBlock ();
302 public void BeginExceptionFilterBlock ()
304 ig.BeginExceptFilterBlock ();
307 public void BeginFinallyBlock ()
309 ig.BeginFinallyBlock ();
312 public void BeginScope ()
314 if ((flags & Options.OmitDebugInfo) != 0)
318 methodSymbols.StartBlock (CodeBlockEntry.Type.Lexical, ig.ILOffset);
322 public void BeginCompilerScope ()
324 if ((flags & Options.OmitDebugInfo) != 0)
328 methodSymbols.StartBlock (CodeBlockEntry.Type.CompilerGenerated, ig.ILOffset);
332 public void EndExceptionBlock ()
334 ig.EndExceptionBlock ();
337 public void EndScope ()
339 if ((flags & Options.OmitDebugInfo) != 0)
343 methodSymbols.EndBlock (ig.ILOffset);
348 // Creates a nested container in this context for all dynamic compiler generated stuff
350 internal DynamicSiteClass CreateDynamicSite ()
352 if (dynamic_site_container == null) {
353 var mc = member_context.CurrentMemberDefinition as MemberBase;
354 dynamic_site_container = new DynamicSiteClass (CurrentTypeDefinition.Parent.PartialContainer, mc, member_context.CurrentTypeParameters);
356 CurrentTypeDefinition.Module.AddCompilerGeneratedClass (dynamic_site_container);
357 dynamic_site_container.CreateContainer ();
358 dynamic_site_container.DefineContainer ();
359 dynamic_site_container.Define ();
361 var inflator = new TypeParameterInflator (Module, CurrentType, TypeParameterSpec.EmptyTypes, TypeSpec.EmptyTypes);
362 var inflated = dynamic_site_container.CurrentType.InflateMember (inflator);
363 CurrentType.MemberCache.AddMember (inflated);
366 return dynamic_site_container;
369 public Label CreateReturnLabel ()
371 if (!return_label.HasValue)
372 return_label = DefineLabel ();
374 return return_label.Value;
377 public LocalBuilder DeclareLocal (TypeSpec type, bool pinned)
379 if (IsAnonymousStoreyMutateRequired)
380 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
382 return ig.DeclareLocal (type.GetMetaInfo (), pinned);
385 public Label DefineLabel ()
387 return ig.DefineLabel ();
391 // Creates temporary field in current async storey
393 public StackFieldExpr GetTemporaryField (TypeSpec type, bool initializedFieldRequired = false)
395 var f = AsyncTaskStorey.AddCapturedLocalVariable (type, initializedFieldRequired);
396 var fexpr = new StackFieldExpr (f);
397 fexpr.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location.Null);
401 public void MarkLabel (Label label)
403 ig.MarkLabel (label);
406 public void Emit (OpCode opcode)
411 public void Emit (OpCode opcode, LocalBuilder local)
413 ig.Emit (opcode, local);
416 public void Emit (OpCode opcode, string arg)
418 ig.Emit (opcode, arg);
421 public void Emit (OpCode opcode, double arg)
423 ig.Emit (opcode, arg);
426 public void Emit (OpCode opcode, float arg)
428 ig.Emit (opcode, arg);
431 public void Emit (OpCode opcode, Label label)
433 ig.Emit (opcode, label);
436 public void Emit (OpCode opcode, Label[] labels)
438 ig.Emit (opcode, labels);
441 public void Emit (OpCode opcode, TypeSpec type)
443 if (IsAnonymousStoreyMutateRequired)
444 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
446 ig.Emit (opcode, type.GetMetaInfo ());
449 public void Emit (OpCode opcode, FieldSpec field)
451 if (IsAnonymousStoreyMutateRequired)
452 field = field.Mutate (CurrentAnonymousMethod.Storey.Mutator);
454 ig.Emit (opcode, field.GetMetaInfo ());
457 public void Emit (OpCode opcode, MethodSpec method)
459 if (IsAnonymousStoreyMutateRequired)
460 method = method.Mutate (CurrentAnonymousMethod.Storey.Mutator);
462 if (method.IsConstructor)
463 ig.Emit (opcode, (ConstructorInfo) method.GetMetaInfo ());
465 ig.Emit (opcode, (MethodInfo) method.GetMetaInfo ());
468 // TODO: REMOVE breaks mutator
469 public void Emit (OpCode opcode, MethodInfo method)
471 ig.Emit (opcode, method);
474 public void Emit (OpCode opcode, MethodSpec method, MetaType[] vargs)
476 // TODO MemberCache: This should mutate too
477 ig.EmitCall (opcode, (MethodInfo) method.GetMetaInfo (), vargs);
480 public void EmitArrayNew (ArrayContainer ac)
483 var type = IsAnonymousStoreyMutateRequired ?
484 CurrentAnonymousMethod.Storey.Mutator.Mutate (ac.Element) :
487 ig.Emit (OpCodes.Newarr, type.GetMetaInfo ());
489 if (IsAnonymousStoreyMutateRequired)
490 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
492 ig.Emit (OpCodes.Newobj, ac.GetConstructor ());
496 public void EmitArrayAddress (ArrayContainer ac)
499 if (IsAnonymousStoreyMutateRequired)
500 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
502 ig.Emit (OpCodes.Call, ac.GetAddressMethod ());
504 var type = IsAnonymousStoreyMutateRequired ?
505 CurrentAnonymousMethod.Storey.Mutator.Mutate (ac.Element) :
508 ig.Emit (OpCodes.Ldelema, type.GetMetaInfo ());
513 // Emits the right opcode to load from an array
515 public void EmitArrayLoad (ArrayContainer ac)
518 if (IsAnonymousStoreyMutateRequired)
519 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
521 ig.Emit (OpCodes.Call, ac.GetGetMethod ());
526 var type = ac.Element;
527 if (type.Kind == MemberKind.Enum)
528 type = EnumSpec.GetUnderlyingType (type);
530 switch (type.BuiltinType) {
531 case BuiltinTypeSpec.Type.Bool:
533 // Workaround MSIL limitation. Load bool element as single bit,
534 // bool array can actually store any byte value
536 ig.Emit (OpCodes.Ldelem_U1);
537 ig.Emit (OpCodes.Ldc_I4_1);
538 ig.Emit (OpCodes.And);
540 case BuiltinTypeSpec.Type.Byte:
541 ig.Emit (OpCodes.Ldelem_U1);
543 case BuiltinTypeSpec.Type.SByte:
544 ig.Emit (OpCodes.Ldelem_I1);
546 case BuiltinTypeSpec.Type.Short:
547 ig.Emit (OpCodes.Ldelem_I2);
549 case BuiltinTypeSpec.Type.UShort:
550 case BuiltinTypeSpec.Type.Char:
551 ig.Emit (OpCodes.Ldelem_U2);
553 case BuiltinTypeSpec.Type.Int:
554 ig.Emit (OpCodes.Ldelem_I4);
556 case BuiltinTypeSpec.Type.UInt:
557 ig.Emit (OpCodes.Ldelem_U4);
559 case BuiltinTypeSpec.Type.ULong:
560 case BuiltinTypeSpec.Type.Long:
561 ig.Emit (OpCodes.Ldelem_I8);
563 case BuiltinTypeSpec.Type.Float:
564 ig.Emit (OpCodes.Ldelem_R4);
566 case BuiltinTypeSpec.Type.Double:
567 ig.Emit (OpCodes.Ldelem_R8);
569 case BuiltinTypeSpec.Type.IntPtr:
570 ig.Emit (OpCodes.Ldelem_I);
574 case MemberKind.Struct:
575 if (IsAnonymousStoreyMutateRequired)
576 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
578 ig.Emit (OpCodes.Ldelema, type.GetMetaInfo ());
579 ig.Emit (OpCodes.Ldobj, type.GetMetaInfo ());
581 case MemberKind.TypeParameter:
582 if (IsAnonymousStoreyMutateRequired)
583 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
585 ig.Emit (OpCodes.Ldelem, type.GetMetaInfo ());
587 case MemberKind.PointerType:
588 ig.Emit (OpCodes.Ldelem_I);
591 ig.Emit (OpCodes.Ldelem_Ref);
599 // Emits the right opcode to store to an array
601 public void EmitArrayStore (ArrayContainer ac)
604 if (IsAnonymousStoreyMutateRequired)
605 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
607 ig.Emit (OpCodes.Call, ac.GetSetMethod ());
611 var type = ac.Element;
613 if (type.Kind == MemberKind.Enum)
614 type = EnumSpec.GetUnderlyingType (type);
616 switch (type.BuiltinType) {
617 case BuiltinTypeSpec.Type.Byte:
618 case BuiltinTypeSpec.Type.SByte:
619 case BuiltinTypeSpec.Type.Bool:
620 Emit (OpCodes.Stelem_I1);
622 case BuiltinTypeSpec.Type.Short:
623 case BuiltinTypeSpec.Type.UShort:
624 case BuiltinTypeSpec.Type.Char:
625 Emit (OpCodes.Stelem_I2);
627 case BuiltinTypeSpec.Type.Int:
628 case BuiltinTypeSpec.Type.UInt:
629 Emit (OpCodes.Stelem_I4);
631 case BuiltinTypeSpec.Type.Long:
632 case BuiltinTypeSpec.Type.ULong:
633 Emit (OpCodes.Stelem_I8);
635 case BuiltinTypeSpec.Type.Float:
636 Emit (OpCodes.Stelem_R4);
638 case BuiltinTypeSpec.Type.Double:
639 Emit (OpCodes.Stelem_R8);
644 case MemberKind.Struct:
645 Emit (OpCodes.Stobj, type);
647 case MemberKind.TypeParameter:
648 Emit (OpCodes.Stelem, type);
650 case MemberKind.PointerType:
651 Emit (OpCodes.Stelem_I);
654 Emit (OpCodes.Stelem_Ref);
659 public void EmitInt (int i)
664 void EmitIntConstant (int i)
668 ig.Emit (OpCodes.Ldc_I4_M1);
672 ig.Emit (OpCodes.Ldc_I4_0);
676 ig.Emit (OpCodes.Ldc_I4_1);
680 ig.Emit (OpCodes.Ldc_I4_2);
684 ig.Emit (OpCodes.Ldc_I4_3);
688 ig.Emit (OpCodes.Ldc_I4_4);
692 ig.Emit (OpCodes.Ldc_I4_5);
696 ig.Emit (OpCodes.Ldc_I4_6);
700 ig.Emit (OpCodes.Ldc_I4_7);
704 ig.Emit (OpCodes.Ldc_I4_8);
708 if (i >= -128 && i <= 127) {
709 ig.Emit (OpCodes.Ldc_I4_S, (sbyte) i);
711 ig.Emit (OpCodes.Ldc_I4, i);
716 public void EmitLong (long l)
718 if (l >= int.MinValue && l <= int.MaxValue) {
719 EmitIntConstant (unchecked ((int) l));
720 ig.Emit (OpCodes.Conv_I8);
721 } else if (l >= 0 && l <= uint.MaxValue) {
722 EmitIntConstant (unchecked ((int) l));
723 ig.Emit (OpCodes.Conv_U8);
725 ig.Emit (OpCodes.Ldc_I8, l);
730 // Load the object from the pointer.
732 public void EmitLoadFromPtr (TypeSpec type)
734 if (type.Kind == MemberKind.Enum)
735 type = EnumSpec.GetUnderlyingType (type);
737 switch (type.BuiltinType) {
738 case BuiltinTypeSpec.Type.Int:
739 ig.Emit (OpCodes.Ldind_I4);
741 case BuiltinTypeSpec.Type.UInt:
742 ig.Emit (OpCodes.Ldind_U4);
744 case BuiltinTypeSpec.Type.Short:
745 ig.Emit (OpCodes.Ldind_I2);
747 case BuiltinTypeSpec.Type.UShort:
748 case BuiltinTypeSpec.Type.Char:
749 ig.Emit (OpCodes.Ldind_U2);
751 case BuiltinTypeSpec.Type.Byte:
752 ig.Emit (OpCodes.Ldind_U1);
754 case BuiltinTypeSpec.Type.SByte:
755 ig.Emit (OpCodes.Ldind_I1);
757 case BuiltinTypeSpec.Type.Bool:
758 ig.Emit (OpCodes.Ldind_I1);
759 ig.Emit (OpCodes.Ldc_I4_1);
760 ig.Emit (OpCodes.And);
762 case BuiltinTypeSpec.Type.ULong:
763 case BuiltinTypeSpec.Type.Long:
764 ig.Emit (OpCodes.Ldind_I8);
766 case BuiltinTypeSpec.Type.Float:
767 ig.Emit (OpCodes.Ldind_R4);
769 case BuiltinTypeSpec.Type.Double:
770 ig.Emit (OpCodes.Ldind_R8);
772 case BuiltinTypeSpec.Type.IntPtr:
773 ig.Emit (OpCodes.Ldind_I);
777 case MemberKind.Struct:
778 case MemberKind.TypeParameter:
779 if (IsAnonymousStoreyMutateRequired)
780 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
782 ig.Emit (OpCodes.Ldobj, type.GetMetaInfo ());
784 case MemberKind.PointerType:
785 ig.Emit (OpCodes.Ldind_I);
788 ig.Emit (OpCodes.Ldind_Ref);
795 public void EmitNull ()
797 ig.Emit (OpCodes.Ldnull);
800 public void EmitArgumentAddress (int pos)
805 if (pos > byte.MaxValue)
806 ig.Emit (OpCodes.Ldarga, pos);
808 ig.Emit (OpCodes.Ldarga_S, (byte) pos);
811 public void EmitArgumentLoad (int pos)
817 case 0: ig.Emit (OpCodes.Ldarg_0); break;
818 case 1: ig.Emit (OpCodes.Ldarg_1); break;
819 case 2: ig.Emit (OpCodes.Ldarg_2); break;
820 case 3: ig.Emit (OpCodes.Ldarg_3); break;
822 if (pos > byte.MaxValue)
823 ig.Emit (OpCodes.Ldarg, pos);
825 ig.Emit (OpCodes.Ldarg_S, (byte) pos);
830 public void EmitArgumentStore (int pos)
835 if (pos > byte.MaxValue)
836 ig.Emit (OpCodes.Starg, pos);
838 ig.Emit (OpCodes.Starg_S, (byte) pos);
842 // The stack contains the pointer and the value of type `type'
844 public void EmitStoreFromPtr (TypeSpec type)
847 type = EnumSpec.GetUnderlyingType (type);
849 switch (type.BuiltinType) {
850 case BuiltinTypeSpec.Type.Int:
851 case BuiltinTypeSpec.Type.UInt:
852 ig.Emit (OpCodes.Stind_I4);
854 case BuiltinTypeSpec.Type.Long:
855 case BuiltinTypeSpec.Type.ULong:
856 ig.Emit (OpCodes.Stind_I8);
858 case BuiltinTypeSpec.Type.Char:
859 case BuiltinTypeSpec.Type.Short:
860 case BuiltinTypeSpec.Type.UShort:
861 ig.Emit (OpCodes.Stind_I2);
863 case BuiltinTypeSpec.Type.Float:
864 ig.Emit (OpCodes.Stind_R4);
866 case BuiltinTypeSpec.Type.Double:
867 ig.Emit (OpCodes.Stind_R8);
869 case BuiltinTypeSpec.Type.Byte:
870 case BuiltinTypeSpec.Type.SByte:
871 case BuiltinTypeSpec.Type.Bool:
872 ig.Emit (OpCodes.Stind_I1);
874 case BuiltinTypeSpec.Type.IntPtr:
875 ig.Emit (OpCodes.Stind_I);
880 case MemberKind.Struct:
881 case MemberKind.TypeParameter:
882 if (IsAnonymousStoreyMutateRequired)
883 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
885 ig.Emit (OpCodes.Stobj, type.GetMetaInfo ());
888 ig.Emit (OpCodes.Stind_Ref);
893 public void EmitThis ()
895 ig.Emit (OpCodes.Ldarg_0);
898 public void EmitEpilogue ()
900 if (epilogue_expressions == null)
903 foreach (var e in epilogue_expressions)
904 e.EmitCleanup (this);
906 epilogue_expressions = null;
910 /// Returns a temporary storage for a variable of type t as
911 /// a local variable in the current body.
913 public LocalBuilder GetTemporaryLocal (TypeSpec t)
915 if (temporary_storage != null) {
917 if (temporary_storage.TryGetValue (t, out o)) {
918 if (o is Stack<LocalBuilder>) {
919 var s = (Stack<LocalBuilder>) o;
920 o = s.Count == 0 ? null : s.Pop ();
922 temporary_storage.Remove (t);
926 return (LocalBuilder) o;
928 return DeclareLocal (t, false);
931 public void FreeTemporaryLocal (LocalBuilder b, TypeSpec t)
933 if (temporary_storage == null) {
934 temporary_storage = new Dictionary<TypeSpec, object> (ReferenceEquality<TypeSpec>.Default);
935 temporary_storage.Add (t, b);
940 if (!temporary_storage.TryGetValue (t, out o)) {
941 temporary_storage.Add (t, b);
944 var s = o as Stack<LocalBuilder>;
946 s = new Stack<LocalBuilder> ();
947 s.Push ((LocalBuilder)o);
948 temporary_storage [t] = s;
954 /// ReturnValue creates on demand the LocalBuilder for the
955 /// return value from the function. By default this is not
956 /// used. This is only required when returns are found inside
957 /// Try or Catch statements.
959 /// This method is typically invoked from the Emit phase, so
960 /// we allow the creation of a return label if it was not
961 /// requested during the resolution phase. Could be cleaned
962 /// up, but it would replicate a lot of logic in the Emit phase
963 /// of the code that uses it.
965 public LocalBuilder TemporaryReturn ()
967 if (return_value == null){
968 return_value = DeclareLocal (return_type, false);
977 public Expression InstanceExpression;
980 // When set leaves an extra copy of all arguments on the stack
982 public bool DuplicateArguments;
985 // Does not emit InstanceExpression load when InstanceExpressionOnStack
986 // is set. Used by compound assignments.
988 public bool InstanceExpressionOnStack;
991 // Any of arguments contains await expression
993 public bool HasAwaitArguments;
996 // When dealing with await arguments the original arguments are converted
997 // into a new set with hoisted stack results
999 public Arguments EmittedArguments;
1001 public void Emit (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc)
1003 EmitPredefined (ec, method, Arguments, loc);
1006 public void EmitPredefined (EmitContext ec, MethodSpec method, Arguments Arguments, Location? loc = null)
1008 Expression instance_copy = null;
1010 if (!HasAwaitArguments && ec.HasSet (BuilderContext.Options.AsyncBody)) {
1011 HasAwaitArguments = Arguments != null && Arguments.ContainsEmitWithAwait ();
1012 if (HasAwaitArguments && InstanceExpressionOnStack) {
1013 throw new NotSupportedException ();
1018 LocalTemporary lt = null;
1020 if (method.IsStatic) {
1021 call_op = OpCodes.Call;
1023 if (IsVirtualCallRequired (InstanceExpression, method)) {
1024 call_op = OpCodes.Callvirt;
1026 call_op = OpCodes.Call;
1029 if (HasAwaitArguments) {
1030 instance_copy = InstanceExpression.EmitToField (ec);
1031 if (Arguments == null)
1032 EmitCallInstance (ec, instance_copy, method.DeclaringType, call_op);
1033 } else if (!InstanceExpressionOnStack) {
1034 var instance_on_stack_type = EmitCallInstance (ec, InstanceExpression, method.DeclaringType, call_op);
1036 if (DuplicateArguments) {
1037 ec.Emit (OpCodes.Dup);
1038 if (Arguments != null && Arguments.Count != 0) {
1039 lt = new LocalTemporary (instance_on_stack_type);
1047 if (Arguments != null && !InstanceExpressionOnStack) {
1048 EmittedArguments = Arguments.Emit (ec, DuplicateArguments, HasAwaitArguments);
1049 if (EmittedArguments != null) {
1050 if (instance_copy != null) {
1051 EmitCallInstance (ec, instance_copy, method.DeclaringType, call_op);
1057 EmittedArguments.Emit (ec);
1061 if (call_op == OpCodes.Callvirt && (InstanceExpression.Type.IsGenericParameter || InstanceExpression.Type.IsStructOrEnum)) {
1062 ec.Emit (OpCodes.Constrained, InstanceExpression.Type);
1067 // Emit explicit sequence point for expressions like Foo.Bar () to help debugger to
1068 // break at right place when LHS expression can be stepped-into
1070 ec.MarkCallEntry (loc.Value);
1074 // Set instance expression to actual result expression. When it contains await it can be
1075 // picked up by caller
1077 InstanceExpression = instance_copy;
1079 if (method.Parameters.HasArglist) {
1080 var varargs_types = GetVarargsTypes (method, Arguments);
1081 ec.Emit (call_op, method, varargs_types);
1088 // and DoFoo is not virtual, you can omit the callvirt,
1089 // because you don't need the null checking behavior.
1091 ec.Emit (call_op, method);
1094 static TypeSpec EmitCallInstance (EmitContext ec, Expression instance, TypeSpec declaringType, OpCode callOpcode)
1096 var instance_type = instance.Type;
1099 // Push the instance expression
1101 if ((instance_type.IsStructOrEnum && (callOpcode == OpCodes.Callvirt || (callOpcode == OpCodes.Call && declaringType.IsStruct))) ||
1102 instance_type.IsGenericParameter || declaringType.IsNullableType) {
1104 // If the expression implements IMemoryLocation, then
1105 // we can optimize and use AddressOf on the
1108 // If not we have to use some temporary storage for
1110 var iml = instance as IMemoryLocation;
1112 iml.AddressOf (ec, AddressOp.Load);
1114 LocalTemporary temp = new LocalTemporary (instance_type);
1117 temp.AddressOf (ec, AddressOp.Load);
1120 return ReferenceContainer.MakeType (ec.Module, instance_type);
1123 if (instance_type.IsStructOrEnum) {
1125 ec.Emit (OpCodes.Box, instance_type);
1126 return ec.BuiltinTypes.Object;
1130 return instance_type;
1133 static MetaType[] GetVarargsTypes (MethodSpec method, Arguments arguments)
1135 AParametersCollection pd = method.Parameters;
1137 Argument a = arguments[pd.Count - 1];
1138 Arglist list = (Arglist) a.Expr;
1140 return list.ArgumentTypes;
1144 // Used to decide whether call or callvirt is needed
1146 static bool IsVirtualCallRequired (Expression instance, MethodSpec method)
1149 // There are 2 scenarious where we emit callvirt
1151 // Case 1: A method is virtual and it's not used to call base
1152 // Case 2: A method instance expression can be null. In this casen callvirt ensures
1153 // correct NRE exception when the method is called
1155 var decl_type = method.DeclaringType;
1156 if (decl_type.IsStruct || decl_type.IsEnum)
1159 if (instance is BaseThis)
1163 // It's non-virtual and will never be null and it can be determined
1164 // whether it's known value or reference type by verifier
1166 if (!method.IsVirtual && (instance is This || instance is New || instance is ArrayCreation || instance is DelegateCreation) &&
1167 !instance.Type.IsGenericParameter)