Updated with Xamarin copyrights
[mono.git] / mcs / mcs / codegen.cs
index a538038850f38fa741b3a3bb4c08f0f278bdef3d..142f9409343c47bb26479f1702874ca3b3c8771d 100644 (file)
@@ -7,6 +7,7 @@
 //
 // Copyright 2001, 2002, 2003 Ximian, Inc.
 // Copyright 2004 Novell, Inc.
+// Copyright 2011 Xamarin Inc
 //
 
 using System;
@@ -51,16 +52,6 @@ namespace Mono.CSharp
                /// </summary>
                public LocalBuilder return_value;
 
-               /// <summary>
-               ///   The location where return has to jump to return the
-               ///   value
-               /// </summary>
-               public Label ReturnLabel;
-
-               /// <summary>
-               ///   If we already defined the ReturnLabel
-               /// </summary>
-               public bool HasReturnLabel;
 
                /// <summary>
                ///   Current loop begin and end labels.
@@ -87,13 +78,17 @@ namespace Mono.CSharp
 
                DynamicSiteClass dynamic_site_container;
 
+               Label? return_label;
+
                public EmitContext (IMemberContext rc, ILGenerator ig, TypeSpec return_type)
                {
                        this.member_context = rc;
                        this.ig = ig;
-
                        this.return_type = return_type;
 
+                       if (rc.Module.Compiler.Settings.Checked)
+                               flags |= Options.CheckedScope;
+
 #if STATIC
                        ig.__CleverExceptionBlockAssistance ();
 #endif
@@ -101,9 +96,15 @@ namespace Mono.CSharp
 
                #region Properties
 
-               public BuildinTypes BuildinTypes {
+               internal AsyncTaskStorey AsyncTaskStorey {
                        get {
-                               return MemberContext.Module.Compiler.BuildinTypes;
+                               return CurrentAnonymousMethod.Storey as AsyncTaskStorey;
+                       }
+               }
+
+               public BuiltinTypes BuiltinTypes {
+                       get {
+                               return MemberContext.Module.Compiler.BuiltinTypes;
                        }
                }
 
@@ -119,11 +120,17 @@ namespace Mono.CSharp
                        get { return member_context.CurrentMemberDefinition; }
                }
 
+               public bool HasReturnLabel {
+                       get {
+                               return return_label.HasValue;
+                       }
+               }
+
                public bool IsStatic {
                        get { return member_context.IsStatic; }
                }
 
-               bool IsAnonymousStoreyMutateRequired {
+               public bool IsAnonymousStoreyMutateRequired {
                        get {
                                return CurrentAnonymousMethod != null &&
                                        CurrentAnonymousMethod.Storey != null &&
@@ -156,8 +163,27 @@ namespace Mono.CSharp
                                return return_type;
                        }
                }
+
+               //
+               // The label where we have to jump before leaving the context
+               //
+               public Label ReturnLabel {
+                       get {
+                               return return_label.Value;
+                       }
+               }
+
                #endregion
 
+               public void AssertEmptyStack ()
+               {
+#if STATIC
+                       if (ig.__StackHeight != 0)
+                               throw new InternalErrorException ("Await yields with non-empty stack in `{0}",
+                                       member_context.GetSignatureForError ());
+#endif
+               }
+
                /// <summary>
                ///   This is called immediately before emitting an IL opcode to tell the symbol
                ///   writer to which source line this opcode belongs.
@@ -208,7 +234,7 @@ namespace Mono.CSharp
                //
                // Creates a nested container in this context for all dynamic compiler generated stuff
                //
-               public DynamicSiteClass CreateDynamicSite ()
+               internal DynamicSiteClass CreateDynamicSite ()
                {
                        if (dynamic_site_container == null) {
                                var mc = member_context.CurrentMemberDefinition as MemberBase;
@@ -219,11 +245,23 @@ namespace Mono.CSharp
                                dynamic_site_container.DefineType ();
                                dynamic_site_container.ResolveTypeParameters ();
                                dynamic_site_container.Define ();
+
+                               var inflator = new TypeParameterInflator (Module, CurrentType, TypeParameterSpec.EmptyTypes, TypeSpec.EmptyTypes);
+                               var inflated = dynamic_site_container.CurrentType.InflateMember (inflator);
+                               CurrentType.MemberCache.AddMember (inflated);
                        }
 
                        return dynamic_site_container;
                }
 
+               public Label CreateReturnLabel ()
+               {
+                       if (!return_label.HasValue)
+                               return_label = DefineLabel ();
+
+                       return return_label.Value;
+               }
+
                public LocalBuilder DeclareLocal (TypeSpec type, bool pinned)
                {
                        if (IsAnonymousStoreyMutateRequired)
@@ -237,6 +275,17 @@ namespace Mono.CSharp
                        return ig.DefineLabel ();
                }
 
+               //
+               // Creates temporary field in current async storey
+               //
+               public FieldExpr GetTemporaryField (TypeSpec type)
+               {
+                       var f = AsyncTaskStorey.AddCapturedLocalVariable (type);
+                       var fexpr = new StackFieldExpr (f);
+                       fexpr.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location.Null);
+                       return fexpr;
+               }
+
                public void MarkLabel (Label label)
                {
                        ig.MarkLabel (label);
@@ -267,16 +316,6 @@ namespace Mono.CSharp
                        ig.Emit (opcode, arg);
                }
 
-               public void Emit (OpCode opcode, int arg)
-               {
-                       ig.Emit (opcode, arg);
-               }
-
-               public void Emit (OpCode opcode, byte arg)
-               {
-                       ig.Emit (opcode, arg);
-               }
-
                public void Emit (OpCode opcode, Label label)
                {
                        ig.Emit (opcode, label);
@@ -329,7 +368,11 @@ namespace Mono.CSharp
                public void EmitArrayNew (ArrayContainer ac)
                {
                        if (ac.Rank == 1) {
-                               Emit (OpCodes.Newarr, ac.Element);
+                               var type = IsAnonymousStoreyMutateRequired ?
+                                       CurrentAnonymousMethod.Storey.Mutator.Mutate (ac.Element) :
+                                       ac.Element;
+
+                               ig.Emit (OpCodes.Newarr, type.GetMetaInfo ());
                        } else {
                                if (IsAnonymousStoreyMutateRequired)
                                        ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
@@ -340,16 +383,17 @@ namespace Mono.CSharp
 
                public void EmitArrayAddress (ArrayContainer ac)
                {
-                       if (ac.Element.IsGenericParameter)
-                               ig.Emit (OpCodes.Readonly);
-
                        if (ac.Rank > 1) {
                                if (IsAnonymousStoreyMutateRequired)
                                        ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
 
                                ig.Emit (OpCodes.Call, ac.GetAddressMethod ());
                        } else {
-                               Emit (OpCodes.Ldelema, ac.Element);
+                               var type = IsAnonymousStoreyMutateRequired ?
+                                       CurrentAnonymousMethod.Storey.Mutator.Mutate (ac.Element) :
+                                       ac.Element;
+
+                               ig.Emit (OpCodes.Ldelema, type.GetMetaInfo ());
                        }
                }
 
@@ -366,55 +410,69 @@ namespace Mono.CSharp
                                return;
                        }
 
+
                        var type = ac.Element;
                        if (type.Kind == MemberKind.Enum)
                                type = EnumSpec.GetUnderlyingType (type);
 
-                       switch (type.BuildinType) {
-                       case BuildinTypeSpec.Type.Byte:
-                       case BuildinTypeSpec.Type.Bool:
-                               Emit (OpCodes.Ldelem_U1);
-                               return;
-                       case BuildinTypeSpec.Type.SByte:
-                               Emit (OpCodes.Ldelem_I1);
-                               return;
-                       case BuildinTypeSpec.Type.Short:
-                               Emit (OpCodes.Ldelem_I2);
-                               return;
-                       case BuildinTypeSpec.Type.UShort:
-                       case BuildinTypeSpec.Type.Char:
-                               Emit (OpCodes.Ldelem_U2);
-                               return;
-                       case BuildinTypeSpec.Type.Int:
-                               Emit (OpCodes.Ldelem_I4);
-                               return;
-                       case BuildinTypeSpec.Type.UInt:
-                               Emit (OpCodes.Ldelem_U4);
-                               return;
-                       case BuildinTypeSpec.Type.ULong:
-                       case BuildinTypeSpec.Type.Long:
-                               Emit (OpCodes.Ldelem_I8);
-                               return;
-                       case BuildinTypeSpec.Type.Float:
-                               Emit (OpCodes.Ldelem_R4);
-                               return;
-                       case BuildinTypeSpec.Type.Double:
-                               Emit (OpCodes.Ldelem_R8);
-                               return;
-                       case BuildinTypeSpec.Type.IntPtr:
-                               Emit (OpCodes.Ldelem_I);
-                               return;
+                       switch (type.BuiltinType) {
+                       case BuiltinTypeSpec.Type.Byte:
+                       case BuiltinTypeSpec.Type.Bool:
+                               ig.Emit (OpCodes.Ldelem_U1);
+                               break;
+                       case BuiltinTypeSpec.Type.SByte:
+                               ig.Emit (OpCodes.Ldelem_I1);
+                               break;
+                       case BuiltinTypeSpec.Type.Short:
+                               ig.Emit (OpCodes.Ldelem_I2);
+                               break;
+                       case BuiltinTypeSpec.Type.UShort:
+                       case BuiltinTypeSpec.Type.Char:
+                               ig.Emit (OpCodes.Ldelem_U2);
+                               break;
+                       case BuiltinTypeSpec.Type.Int:
+                               ig.Emit (OpCodes.Ldelem_I4);
+                               break;
+                       case BuiltinTypeSpec.Type.UInt:
+                               ig.Emit (OpCodes.Ldelem_U4);
+                               break;
+                       case BuiltinTypeSpec.Type.ULong:
+                       case BuiltinTypeSpec.Type.Long:
+                               ig.Emit (OpCodes.Ldelem_I8);
+                               break;
+                       case BuiltinTypeSpec.Type.Float:
+                               ig.Emit (OpCodes.Ldelem_R4);
+                               break;
+                       case BuiltinTypeSpec.Type.Double:
+                               ig.Emit (OpCodes.Ldelem_R8);
+                               break;
+                       case BuiltinTypeSpec.Type.IntPtr:
+                               ig.Emit (OpCodes.Ldelem_I);
+                               break;
+                       default:
+                               switch (type.Kind) {
+                               case MemberKind.Struct:
+                                       if (IsAnonymousStoreyMutateRequired)
+                                               type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
+
+                                       ig.Emit (OpCodes.Ldelema, type.GetMetaInfo ());
+                                       ig.Emit (OpCodes.Ldobj, type.GetMetaInfo ());
+                                       break;
+                               case MemberKind.TypeParameter:
+                                       if (IsAnonymousStoreyMutateRequired)
+                                               type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
+
+                                       ig.Emit (OpCodes.Ldelem, type.GetMetaInfo ());
+                                       break;
+                               case MemberKind.PointerType:
+                                       ig.Emit (OpCodes.Ldelem_I);
+                                       break;
+                               default:
+                                       ig.Emit (OpCodes.Ldelem_Ref);
+                                       break;
+                               }
+                               break;
                        }
-
-                       if (TypeManager.IsStruct (type)) {
-                               Emit (OpCodes.Ldelema, type);
-                               Emit (OpCodes.Ldobj, type);
-                       } else if (type.IsGenericParameter) {
-                               Emit (OpCodes.Ldelem, type);
-                       } else if (type.IsPointer)
-                               Emit (OpCodes.Ldelem_I);
-                       else
-                               Emit (OpCodes.Ldelem_Ref);
                }
 
                //
@@ -435,44 +493,55 @@ namespace Mono.CSharp
                        if (type.Kind == MemberKind.Enum)
                                type = EnumSpec.GetUnderlyingType (type);
 
-                       switch (type.BuildinType) {
-                       case BuildinTypeSpec.Type.Byte:
-                       case BuildinTypeSpec.Type.SByte:
-                       case BuildinTypeSpec.Type.Bool:
+                       switch (type.BuiltinType) {
+                       case BuiltinTypeSpec.Type.Byte:
+                       case BuiltinTypeSpec.Type.SByte:
+                       case BuiltinTypeSpec.Type.Bool:
                                Emit (OpCodes.Stelem_I1);
                                return;
-                       case BuildinTypeSpec.Type.Short:
-                       case BuildinTypeSpec.Type.UShort:
-                       case BuildinTypeSpec.Type.Char:
+                       case BuiltinTypeSpec.Type.Short:
+                       case BuiltinTypeSpec.Type.UShort:
+                       case BuiltinTypeSpec.Type.Char:
                                Emit (OpCodes.Stelem_I2);
                                return;
-                       case BuildinTypeSpec.Type.Int:
-                       case BuildinTypeSpec.Type.UInt:
+                       case BuiltinTypeSpec.Type.Int:
+                       case BuiltinTypeSpec.Type.UInt:
                                Emit (OpCodes.Stelem_I4);
                                return;
-                       case BuildinTypeSpec.Type.Long:
-                       case BuildinTypeSpec.Type.ULong:
+                       case BuiltinTypeSpec.Type.Long:
+                       case BuiltinTypeSpec.Type.ULong:
                                Emit (OpCodes.Stelem_I8);
                                return;
-                       case BuildinTypeSpec.Type.Float:
+                       case BuiltinTypeSpec.Type.Float:
                                Emit (OpCodes.Stelem_R4);
                                return;
-                       case BuildinTypeSpec.Type.Double:
+                       case BuiltinTypeSpec.Type.Double:
                                Emit (OpCodes.Stelem_R8);
                                return;
                        }
 
-                       if (TypeManager.IsStruct (type))
+                       switch (type.Kind) {
+                       case MemberKind.Struct:
                                Emit (OpCodes.Stobj, type);
-                       else if (type.IsGenericParameter)
+                               break;
+                       case MemberKind.TypeParameter:
                                Emit (OpCodes.Stelem, type);
-                       else if (type.IsPointer)
+                               break;
+                       case MemberKind.PointerType:
                                Emit (OpCodes.Stelem_I);
-                       else
+                               break;
+                       default:
                                Emit (OpCodes.Stelem_Ref);
+                               break;
+                       }
                }
 
                public void EmitInt (int i)
+               {
+                       EmitIntConstant (i);
+               }
+
+               void EmitIntConstant (int i)
                {
                        switch (i) {
                        case -1:
@@ -527,18 +596,14 @@ namespace Mono.CSharp
                public void EmitLong (long l)
                {
                        if (l >= int.MinValue && l <= int.MaxValue) {
-                               EmitInt (unchecked ((int) l));
+                               EmitIntConstant (unchecked ((int) l));
                                ig.Emit (OpCodes.Conv_I8);
-                               return;
-                       }
-
-                       if (l >= 0 && l <= uint.MaxValue) {
-                               EmitInt (unchecked ((int) l));
+                       } else if (l >= 0 && l <= uint.MaxValue) {
+                               EmitIntConstant (unchecked ((int) l));
                                ig.Emit (OpCodes.Conv_U8);
-                               return;
+                       } else {
+                               ig.Emit (OpCodes.Ldc_I8, l);
                        }
-
-                       ig.Emit (OpCodes.Ldc_I8, l);
                }
 
                //
@@ -549,48 +614,104 @@ namespace Mono.CSharp
                        if (type.Kind == MemberKind.Enum)
                                type = EnumSpec.GetUnderlyingType (type);
 
-                       switch (type.BuildinType) {
-                       case BuildinTypeSpec.Type.Int:
+                       switch (type.BuiltinType) {
+                       case BuiltinTypeSpec.Type.Int:
                                ig.Emit (OpCodes.Ldind_I4);
-                               return;
-                       case BuildinTypeSpec.Type.UInt:
+                               break;
+                       case BuiltinTypeSpec.Type.UInt:
                                ig.Emit (OpCodes.Ldind_U4);
-                               return;
-                       case BuildinTypeSpec.Type.Short:
+                               break;
+                       case BuiltinTypeSpec.Type.Short:
                                ig.Emit (OpCodes.Ldind_I2);
-                               return;
-                       case BuildinTypeSpec.Type.UShort:
-                       case BuildinTypeSpec.Type.Char:
+                               break;
+                       case BuiltinTypeSpec.Type.UShort:
+                       case BuiltinTypeSpec.Type.Char:
                                ig.Emit (OpCodes.Ldind_U2);
-                               return;
-                       case BuildinTypeSpec.Type.Byte:
+                               break;
+                       case BuiltinTypeSpec.Type.Byte:
                                ig.Emit (OpCodes.Ldind_U1);
-                               return;
-                       case BuildinTypeSpec.Type.SByte:
-                       case BuildinTypeSpec.Type.Bool:
+                               break;
+                       case BuiltinTypeSpec.Type.SByte:
+                       case BuiltinTypeSpec.Type.Bool:
                                ig.Emit (OpCodes.Ldind_I1);
-                               return;
-                       case BuildinTypeSpec.Type.ULong:
-                       case BuildinTypeSpec.Type.Long:
+                               break;
+                       case BuiltinTypeSpec.Type.ULong:
+                       case BuiltinTypeSpec.Type.Long:
                                ig.Emit (OpCodes.Ldind_I8);
-                               return;
-                       case BuildinTypeSpec.Type.Float:
+                               break;
+                       case BuiltinTypeSpec.Type.Float:
                                ig.Emit (OpCodes.Ldind_R4);
-                               return;
-                       case BuildinTypeSpec.Type.Double:
+                               break;
+                       case BuiltinTypeSpec.Type.Double:
                                ig.Emit (OpCodes.Ldind_R8);
-                               return;
-                       case BuildinTypeSpec.Type.IntPtr:
+                               break;
+                       case BuiltinTypeSpec.Type.IntPtr:
                                ig.Emit (OpCodes.Ldind_I);
-                               return;
+                               break;
+                       default:
+                               switch (type.Kind) {
+                               case MemberKind.Struct:
+                               case MemberKind.TypeParameter:
+                                       if (IsAnonymousStoreyMutateRequired)
+                                               type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
+
+                                       ig.Emit (OpCodes.Ldobj, type.GetMetaInfo ());
+                                       break;
+                               case MemberKind.PointerType:
+                                       ig.Emit (OpCodes.Ldind_I);
+                                       break;
+                               default:
+                                       ig.Emit (OpCodes.Ldind_Ref);
+                                       break;
+                               }
+                               break;
                        }
+               }
 
-                       if (TypeManager.IsStruct (type) || TypeManager.IsGenericParameter (type))
-                               Emit (OpCodes.Ldobj, type);
-                       else if (type.IsPointer)
-                               ig.Emit (OpCodes.Ldind_I);
+               public void EmitNull ()
+               {
+                       ig.Emit (OpCodes.Ldnull);
+               }
+
+               public void EmitArgumentAddress (int pos)
+               {
+                       if (!IsStatic)
+                               ++pos;
+
+                       if (pos > byte.MaxValue)
+                               ig.Emit (OpCodes.Ldarga, pos);
+                       else
+                               ig.Emit (OpCodes.Ldarga_S, (byte) pos);
+               }
+
+               public void EmitArgumentLoad (int pos)
+               {
+                       if (!IsStatic)
+                               ++pos;
+
+                       switch (pos) {
+                       case 0: ig.Emit (OpCodes.Ldarg_0); break;
+                       case 1: ig.Emit (OpCodes.Ldarg_1); break;
+                       case 2: ig.Emit (OpCodes.Ldarg_2); break;
+                       case 3: ig.Emit (OpCodes.Ldarg_3); break;
+                       default:
+                               if (pos > byte.MaxValue)
+                                       ig.Emit (OpCodes.Ldarg, pos);
+                               else
+                                       ig.Emit (OpCodes.Ldarg_S, (byte) pos);
+                               break;
+                       }
+               }
+
+               public void EmitArgumentStore (int pos)
+               {
+                       if (!IsStatic)
+                               ++pos;
+
+                       if (pos > byte.MaxValue)
+                               ig.Emit (OpCodes.Starg, pos);
                        else
-                               ig.Emit (OpCodes.Ldind_Ref);
+                               ig.Emit (OpCodes.Starg_S, (byte) pos);
                }
 
                //
@@ -601,40 +722,53 @@ namespace Mono.CSharp
                        if (type.IsEnum)
                                type = EnumSpec.GetUnderlyingType (type);
 
-                       switch (type.BuildinType) {
-                       case BuildinTypeSpec.Type.Int:
-                       case BuildinTypeSpec.Type.UInt:
+                       switch (type.BuiltinType) {
+                       case BuiltinTypeSpec.Type.Int:
+                       case BuiltinTypeSpec.Type.UInt:
                                ig.Emit (OpCodes.Stind_I4);
                                return;
-                       case BuildinTypeSpec.Type.Long:
-                       case BuildinTypeSpec.Type.ULong:
+                       case BuiltinTypeSpec.Type.Long:
+                       case BuiltinTypeSpec.Type.ULong:
                                ig.Emit (OpCodes.Stind_I8);
                                return;
-                       case BuildinTypeSpec.Type.Char:
-                       case BuildinTypeSpec.Type.Short:
-                       case BuildinTypeSpec.Type.UShort:
+                       case BuiltinTypeSpec.Type.Char:
+                       case BuiltinTypeSpec.Type.Short:
+                       case BuiltinTypeSpec.Type.UShort:
                                ig.Emit (OpCodes.Stind_I2);
                                return;
-                       case BuildinTypeSpec.Type.Float:
+                       case BuiltinTypeSpec.Type.Float:
                                ig.Emit (OpCodes.Stind_R4);
                                return;
-                       case BuildinTypeSpec.Type.Double:
+                       case BuiltinTypeSpec.Type.Double:
                                ig.Emit (OpCodes.Stind_R8);
                                return;
-                       case BuildinTypeSpec.Type.Byte:
-                       case BuildinTypeSpec.Type.SByte:
-                       case BuildinTypeSpec.Type.Bool:
+                       case BuiltinTypeSpec.Type.Byte:
+                       case BuiltinTypeSpec.Type.SByte:
+                       case BuiltinTypeSpec.Type.Bool:
                                ig.Emit (OpCodes.Stind_I1);
                                return;
-                       case BuildinTypeSpec.Type.IntPtr:
+                       case BuiltinTypeSpec.Type.IntPtr:
                                ig.Emit (OpCodes.Stind_I);
                                return;
                        }
 
-                       if (TypeManager.IsStruct (type) || TypeManager.IsGenericParameter (type))
-                               Emit (OpCodes.Stobj, type);
-                       else
+                       switch (type.Kind) {
+                       case MemberKind.Struct:
+                       case MemberKind.TypeParameter:
+                               if (IsAnonymousStoreyMutateRequired)
+                                       type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
+
+                               ig.Emit (OpCodes.Stobj, type.GetMetaInfo ());
+                               break;
+                       default:
                                ig.Emit (OpCodes.Stind_Ref);
+                               break;
+                       }
+               }
+
+               public void EmitThis ()
+               {
+                       ig.Emit (OpCodes.Ldarg_0);
                }
 
                /// <summary>
@@ -697,13 +831,202 @@ namespace Mono.CSharp
                {
                        if (return_value == null){
                                return_value = DeclareLocal (return_type, false);
-                               if (!HasReturnLabel){
-                                       ReturnLabel = DefineLabel ();
-                                       HasReturnLabel = true;
-                               }
                        }
 
                        return return_value;
                }
        }
+
+       struct CallEmitter
+       {
+               public Expression InstanceExpression;
+
+               //
+               // When set leaves an extra copy of all arguments on the stack
+               //
+               public bool DuplicateArguments;
+
+               //
+               // Does not emit InstanceExpression load when InstanceExpressionOnStack
+               // is set. Used by compound assignments.
+               //
+               public bool InstanceExpressionOnStack;
+
+               //
+               // Any of arguments contains await expression
+               //
+               public bool HasAwaitArguments;
+
+               //
+               // When dealing with await arguments the original arguments are converted
+               // into a new set with hoisted stack results
+               //
+               public Arguments EmittedArguments;
+
+               public void Emit (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc)
+               {
+                       // Speed up the check by not doing it on not allowed targets
+                       if (method.ReturnType.Kind == MemberKind.Void && method.IsConditionallyExcluded (ec.Module.Compiler, loc))
+                               return;
+
+                       EmitPredefined (ec, method, Arguments);
+               }
+
+               public void EmitPredefined (EmitContext ec, MethodSpec method, Arguments Arguments)
+               {
+                       Expression instance_copy = null;
+
+                       if (!HasAwaitArguments && ec.HasSet (BuilderContext.Options.AsyncBody)) {
+                               HasAwaitArguments = Arguments != null && Arguments.ContainsEmitWithAwait ();
+                               if (HasAwaitArguments && InstanceExpressionOnStack) {
+                                       throw new NotSupportedException ();
+                               }
+                       }
+
+                       OpCode call_op;
+                       LocalTemporary lt = null;
+
+                       if (method.IsStatic) {
+                               call_op = OpCodes.Call;
+                       } else {
+                               if (IsVirtualCallRequired (InstanceExpression, method)) {
+                                       call_op = OpCodes.Callvirt;
+                               } else {
+                                       call_op = OpCodes.Call;
+                               }
+
+                               if (HasAwaitArguments) {
+                                       instance_copy = InstanceExpression.EmitToField (ec);
+                                       if (Arguments == null)
+                                               EmitCallInstance (ec, instance_copy, method.DeclaringType, call_op);
+                               } else if (!InstanceExpressionOnStack) {
+                                       var instance_on_stack_type = EmitCallInstance (ec, InstanceExpression, method.DeclaringType, call_op);
+
+                                       if (DuplicateArguments) {
+                                               ec.Emit (OpCodes.Dup);
+                                               if (Arguments != null && Arguments.Count != 0) {
+                                                       lt = new LocalTemporary (instance_on_stack_type);
+                                                       lt.Store (ec);
+                                                       instance_copy = lt;
+                                               }
+                                       }
+                               }
+                       }
+
+                       if (Arguments != null && !InstanceExpressionOnStack) {
+                               EmittedArguments = Arguments.Emit (ec, DuplicateArguments, HasAwaitArguments);
+                               if (EmittedArguments != null) {
+                                       if (instance_copy != null) {
+                                               EmitCallInstance (ec, instance_copy, method.DeclaringType, call_op);
+
+                                               if (lt != null)
+                                                       lt.Release (ec);
+                                       }
+
+                                       EmittedArguments.Emit (ec);
+                               }
+                       }
+
+                       if (call_op == OpCodes.Callvirt && (InstanceExpression.Type.IsGenericParameter || InstanceExpression.Type.IsStruct)) {
+                               ec.Emit (OpCodes.Constrained, InstanceExpression.Type);
+                       }
+
+                       //
+                       // Set instance expression to actual result expression. When it contains await it can be
+                       // picked up by caller
+                       //
+                       InstanceExpression = instance_copy;
+
+                       if (method.Parameters.HasArglist) {
+                               var varargs_types = GetVarargsTypes (method, Arguments);
+                               ec.Emit (call_op, method, varargs_types);
+                               return;
+                       }
+
+                       //
+                       // If you have:
+                       // this.DoFoo ();
+                       // and DoFoo is not virtual, you can omit the callvirt,
+                       // because you don't need the null checking behavior.
+                       //
+                       ec.Emit (call_op, method);
+               }
+
+               static TypeSpec EmitCallInstance (EmitContext ec, Expression instance, TypeSpec declaringType, OpCode callOpcode)
+               {
+                       var instance_type = instance.Type;
+
+                       //
+                       // Push the instance expression
+                       //
+                       if ((instance_type.IsStruct && (callOpcode == OpCodes.Callvirt || (callOpcode == OpCodes.Call && declaringType.IsStruct))) ||
+                               instance_type.IsGenericParameter || declaringType.IsNullableType) {
+                               //
+                               // If the expression implements IMemoryLocation, then
+                               // we can optimize and use AddressOf on the
+                               // return.
+                               //
+                               // If not we have to use some temporary storage for
+                               // it.
+                               var iml = instance as IMemoryLocation;
+                               if (iml != null) {
+                                       iml.AddressOf (ec, AddressOp.Load);
+                               } else {
+                                       LocalTemporary temp = new LocalTemporary (instance_type);
+                                       instance.Emit (ec);
+                                       temp.Store (ec);
+                                       temp.AddressOf (ec, AddressOp.Load);
+                               }
+
+                               return ReferenceContainer.MakeType (ec.Module, instance_type);
+                       }
+
+                       if (instance_type.IsEnum || instance_type.IsStruct) {
+                               instance.Emit (ec);
+                               ec.Emit (OpCodes.Box, instance_type);
+                               return ec.BuiltinTypes.Object;
+                       }
+
+                       instance.Emit (ec);
+                       return instance_type;
+               }
+
+               static MetaType[] GetVarargsTypes (MethodSpec method, Arguments arguments)
+               {
+                       AParametersCollection pd = method.Parameters;
+
+                       Argument a = arguments[pd.Count - 1];
+                       Arglist list = (Arglist) a.Expr;
+
+                       return list.ArgumentTypes;
+               }
+
+               //
+               // Used to decide whether call or callvirt is needed
+               //
+               static bool IsVirtualCallRequired (Expression instance, MethodSpec method)
+               {
+                       //
+                       // There are 2 scenarious where we emit callvirt
+                       //
+                       // Case 1: A method is virtual and it's not used to call base
+                       // Case 2: A method instance expression can be null. In this casen callvirt ensures
+                       // correct NRE exception when the method is called
+                       //
+                       var decl_type = method.DeclaringType;
+                       if (decl_type.IsStruct || decl_type.IsEnum)
+                               return false;
+
+                       if (instance is BaseThis)
+                               return false;
+
+                       //
+                       // It's non-virtual and will never be null
+                       //
+                       if (!method.IsVirtual && (instance is This || instance is New || instance is ArrayCreation || instance is DelegateCreation))
+                               return false;
+
+                       return true;
+               }
+       }
 }