Avoid creating a temporary variable when target await expression is does not include...
[mono.git] / mcs / mcs / ecore.cs
index 1f12df3dbf823ebb6af2ee2225dc789b85a7b4bc..b61fc91782f0880fad06edc990d10bb8eeedfe25 100644 (file)
@@ -130,13 +130,14 @@ namespace Mono.CSharp {
                        set { type = value; }
                }
 
-               public Location Location {
-                       get { return loc; }
+               public virtual bool IsSideEffectFree {
+                       get {
+                               return false;
+                       }
                }
 
-               public virtual string GetSignatureForError ()
-               {
-                       return type.GetDefinition ().GetSignatureForError ();
+               public Location Location {
+                       get { return loc; }
                }
 
                public virtual bool IsNull {
@@ -145,6 +146,15 @@ namespace Mono.CSharp {
                        }
                }
 
+               //
+               // Returns true when the expression during Emit phase breaks stack
+               // by using await expression
+               //
+               public virtual bool ContainsEmitWithAwait ()
+               {
+                       return false;
+               }
+
                /// <summary>
                ///   Performs semantic analysis on the Expression
                /// </summary>
@@ -340,6 +350,11 @@ namespace Mono.CSharp {
                                }
                        }
                }
+
+               public virtual string GetSignatureForError ()
+               {
+                       return type.GetDefinition ().GetSignatureForError ();
+               }
               
                /// <summary>
                ///   Resolves an expression and performs semantic analysis on it.
@@ -466,6 +481,107 @@ namespace Mono.CSharp {
                        ec.Emit (OpCodes.Pop);
                }
 
+               //
+               // Emits the expression into temporary field variable. The method
+               // should be used for await expressions only
+               //
+               public virtual Expression EmitToField (EmitContext ec)
+               {
+                       //
+                       // This is the await prepare Emit method. When emitting code like
+                       // a + b we emit code like
+                       //
+                       // a.Emit ()
+                       // b.Emit ()
+                       // Opcodes.Add
+                       //
+                       // For await a + await b we have to interfere the flow to keep the
+                       // stack clean because await yields from the expression. The emit
+                       // then changes to
+                       //
+                       // a = a.EmitToField () // a is changed to temporary field access
+                       // b = b.EmitToField ()
+                       // a.Emit ()
+                       // b.Emit ()
+                       // Opcodes.Add
+                       //
+                       //
+                       // The idea is to emit expression and leave the stack empty with
+                       // result value still available.
+                       //
+                       // Expressions should override this default implementation when
+                       // optimized version can be provided (e.g. FieldExpr)
+                       //
+                       //
+                       // We can optimize for side-effect free expressions, they can be
+                       // emitted out of order
+                       //
+                       if (IsSideEffectFree)
+                               return this;
+
+                       bool needs_temporary = ContainsEmitWithAwait ();
+                       if (!needs_temporary)
+                               ec.EmitThis ();
+
+                       // Emit original code
+                       EmitToFieldSource (ec);
+
+                       //
+                       // Store the result to temporary field when we
+                       // cannot load this directly
+                       //
+                       var field = ec.GetTemporaryField (type);
+                       if (needs_temporary) {
+                               //
+                               // Create temporary local (we cannot load this before Emit)
+                               //
+                               var temp = ec.GetTemporaryLocal (type);
+                               ec.Emit (OpCodes.Stloc, temp);
+
+                               ec.EmitThis ();
+                               ec.Emit (OpCodes.Ldloc, temp);
+                               field.EmitAssignFromStack (ec);
+
+                               ec.FreeTemporaryLocal (temp, type);
+                       } else {
+                               field.EmitAssignFromStack (ec);
+                       }
+
+                       return field;
+               }
+
+               protected virtual void EmitToFieldSource (EmitContext ec)
+               {
+                       //
+                       // Default implementation calls Emit method
+                       //
+                       Emit (ec);
+               }
+
+               protected static void EmitExpressionsList (EmitContext ec, List<Expression> expressions)
+               {
+                       if (ec.HasSet (BuilderContext.Options.AsyncBody)) {
+                               bool contains_await = false;
+
+                               for (int i = 1; i < expressions.Count; ++i) {
+                                       if (expressions[i].ContainsEmitWithAwait ()) {
+                                               contains_await = true;
+                                               break;
+                                       }
+                               }
+
+                               if (contains_await) {
+                                       for (int i = 0; i < expressions.Count; ++i) {
+                                               expressions[i] = expressions[i].EmitToField (ec);
+                                       }
+                               }
+                       }
+
+                       for (int i = 0; i < expressions.Count; ++i) {
+                               expressions[i].Emit (ec);
+                       }
+               }
+
                /// <summary>
                ///   Protected constructor.  Only derivate types should
                ///   be able to be created
@@ -495,7 +611,7 @@ namespace Mono.CSharp {
                        return null;
                }
 
-               protected static MethodSpec ConstructorLookup (ResolveContext rc, TypeSpec type, ref Arguments args, Location loc)
+               public static MethodSpec ConstructorLookup (ResolveContext rc, TypeSpec type, ref Arguments args, Location loc)
                {
                        var ctors = MemberCache.FindMembers (type, Constructor.ConstructorName, true);
                        if (ctors == null) {
@@ -585,9 +701,9 @@ namespace Mono.CSharp {
                                                        continue;
                                        }
 
-                                       if (non_method == null || member is MethodSpec) {
+                                       if (non_method == null || member is MethodSpec || non_method.IsNotCSharpCompatible) {
                                                non_method = member;
-                                       } else if (!errorMode) {
+                                       } else if (!errorMode && !member.IsNotCSharpCompatible) {
                                                ambig_non_method = member;
                                        }
                                }
@@ -953,6 +1069,11 @@ namespace Mono.CSharp {
                        }
                }
 
+               public override bool ContainsEmitWithAwait ()
+               {
+                       return child.ContainsEmitWithAwait ();
+               }
+
                public override Expression CreateExpressionTree (ResolveContext ec)
                {
                        Arguments args = new Arguments (2);
@@ -1132,6 +1253,12 @@ namespace Mono.CSharp {
                        get { return child.IsOneInteger; }
                }
 
+               public override bool IsSideEffectFree {
+                       get {
+                               return child.IsSideEffectFree;
+                       }
+               }
+
                public override bool IsZeroInteger {
                        get { return child.IsZeroInteger; }
                }
@@ -1269,6 +1396,12 @@ namespace Mono.CSharp {
                        }
                }
 
+               public override bool IsSideEffectFree {
+                       get {
+                               return Child.IsSideEffectFree;
+                       }
+               }
+
                public override bool IsZeroInteger {
                        get { return Child.IsZeroInteger; }
                }
@@ -1740,6 +1873,11 @@ namespace Mono.CSharp {
                                this.loc = orig.Location;
                        }
 
+                       public override bool ContainsEmitWithAwait ()
+                       {
+                               return stm.ContainsEmitWithAwait ();
+                       }
+
                        public override Expression CreateExpressionTree (ResolveContext ec)
                        {
                                return orig_expr.CreateExpressionTree (ec);
@@ -1782,6 +1920,11 @@ namespace Mono.CSharp {
 
                #endregion
 
+               public override bool ContainsEmitWithAwait ()
+               {
+                       return expr.ContainsEmitWithAwait ();
+               }
+
                //
                // Creates fully resolved expression switcher
                //
@@ -1865,18 +2008,23 @@ namespace Mono.CSharp {
                        this.loc = expr.Location;
                }
 
-               public override Expression CreateExpressionTree (ResolveContext ec)
+               public override bool ContainsEmitWithAwait ()
+               {
+                       return expr.ContainsEmitWithAwait ();
+               }
+
+               public override Expression CreateExpressionTree (ResolveContext rc)
                {
-                       return expr.CreateExpressionTree (ec);
+                       return expr.CreateExpressionTree (rc);
                }
 
                public Expression Child {
                        get { return expr; }
                }
 
-               protected override Expression DoResolve (ResolveContext ec)
+               protected override Expression DoResolve (ResolveContext rc)
                {
-                       expr = expr.Resolve (ec);
+                       expr = expr.Resolve (rc);
                        if (expr != null) {
                                type = expr.Type;
                                eclass = expr.eclass;
@@ -1907,6 +2055,12 @@ namespace Mono.CSharp {
                        this.expr = expr;
                }
 
+               public Expression Expr {
+                       get {
+                               return expr;
+                       }
+               }
+
                protected override void CloneTo (CloneContext clonectx, Expression t)
                {
                        if (expr == null)
@@ -1916,6 +2070,11 @@ namespace Mono.CSharp {
                        target.expr = expr.Clone (clonectx);
                }
 
+               public override bool ContainsEmitWithAwait ()
+               {
+                       return expr.ContainsEmitWithAwait ();
+               }
+
                public override Expression CreateExpressionTree (ResolveContext ec)
                {
                        throw new NotSupportedException ("ET");
@@ -1926,9 +2085,6 @@ namespace Mono.CSharp {
                        throw new InternalErrorException ("Missing Resolve call");
                }
 
-               public Expression Expr {
-                       get { return expr; }
-               }
        }
 
        //
@@ -2079,7 +2235,6 @@ namespace Mono.CSharp {
                                }
                        }
 
-                       // MSAF
                        var retval = ctx.LookupNamespaceOrType (Name, Arity, LookupMode.IgnoreAccessibility, loc);
                        if (retval != null) {
                                ctx.Module.Compiler.Report.SymbolRelatedToPreviousError (retval.Type);
@@ -2363,6 +2518,11 @@ namespace Mono.CSharp {
                        // resolved to different type
                }
 
+               public override bool ContainsEmitWithAwait ()
+               {
+                       return false;
+               }
+
                public override Expression CreateExpressionTree (ResolveContext ec)
                {
                        throw new NotSupportedException ("ET");
@@ -2509,7 +2669,6 @@ namespace Mono.CSharp {
                        get;
                }
 
-               // TODO: Not needed
                protected abstract TypeSpec DeclaringType {
                        get;
                }
@@ -2606,6 +2765,11 @@ namespace Mono.CSharp {
                        }
                }
 
+               public override bool ContainsEmitWithAwait ()
+               {
+                       return InstanceExpression != null && InstanceExpression.ContainsEmitWithAwait ();
+               }
+
                static bool IsSameOrBaseQualifier (TypeSpec type, TypeSpec qtype)
                {
                        do {
@@ -2794,6 +2958,7 @@ namespace Mono.CSharp {
                                        InstanceExpression.Emit (ec);
                                        t.Store (ec);
                                        t.AddressOf (ec, AddressOp.Store);
+                                       t.Release (ec);
                                }
                        } else {
                                InstanceExpression.Emit (ec);
@@ -2810,18 +2975,57 @@ namespace Mono.CSharp {
                public abstract void SetTypeArguments (ResolveContext ec, TypeArguments ta);
        }
 
+       public class ExtensionMethodCandidates
+       {
+               NamespaceContainer container;
+               Namespace ns;
+               IList<MethodSpec> methods;
+
+               public ExtensionMethodCandidates (IList<MethodSpec> methods, NamespaceContainer nsContainer)
+                       : this (methods, nsContainer, null)
+               {
+               }
+
+               public ExtensionMethodCandidates (IList<MethodSpec> methods, NamespaceContainer nsContainer, Namespace ns)
+               {
+                       this.methods = methods;
+                       this.container = nsContainer;
+                       this.ns = ns;
+               }
+
+               public NamespaceContainer Container {
+                       get {
+                               return container;
+                       }
+               }
+
+               public bool HasUninspectedMembers { get; set; }
+
+               public Namespace Namespace {
+                       get {
+                               return ns;
+                       }
+               }
+
+               public IList<MethodSpec> Methods {
+                       get {
+                               return methods;
+                       }
+               }
+       }
+
        // 
        // Represents a group of extension method candidates for whole namespace
        // 
        class ExtensionMethodGroupExpr : MethodGroupExpr, OverloadResolver.IErrorHandler
        {
-               NamespaceContainer namespace_entry;
+               ExtensionMethodCandidates candidates;
                public readonly Expression ExtensionExpression;
 
-               public ExtensionMethodGroupExpr (IList<MethodSpec> list, NamespaceContainer n, Expression extensionExpr, Location l)
-                       : base (list.Cast<MemberSpec>().ToList (), extensionExpr.Type, l)
+               public ExtensionMethodGroupExpr (ExtensionMethodCandidates candidates, Expression extensionExpr, Location loc)
+                       : base (candidates.Methods.Cast<MemberSpec>().ToList (), extensionExpr.Type, loc)
                {
-                       this.namespace_entry = n;
+                       this.candidates = candidates;
                        this.ExtensionExpression = extensionExpr;
                }
 
@@ -2829,21 +3033,55 @@ namespace Mono.CSharp {
                        get { return true; }
                }
 
+               //
+               // For extension methodgroup we are not looking for base members but parent
+               // namespace extension methods
+               //
                public override IList<MemberSpec> GetBaseMembers (TypeSpec baseType)
                {
-                       if (namespace_entry == null)
+                       // TODO: candidates are null only when doing error reporting, that's
+                       // incorrect. We have to discover same extension methods in error mode
+                       if (candidates == null)
                                return null;
 
+                       int arity = type_arguments == null ? 0 : type_arguments.Count;
+
                        //
-                       // For extension methodgroup we are not looking for base members but parent
-                       // namespace extension methods
+                       // Here we try to resume the search for extension method at the point
+                       // where the last bunch of candidates was found. It's more tricky than
+                       // it seems as we have to check both namespace containers and namespace
+                       // in correct order.
                        //
-                       int arity = type_arguments == null ? 0 : type_arguments.Count;
-                       var found = namespace_entry.LookupExtensionMethod (DeclaringType, Name, arity, ref namespace_entry);
-                       if (found == null)
+                       // Consider:
+                       // 
+                       // namespace A {
+                       //      using N1;
+                       //  namespace B.C.D {
+                       //              <our first search found candidates in A.B.C.D
+                       //  }
+                       // }
+                       //
+                       // In the example above namespace A.B.C.D, A.B.C and A.B have to be
+                       // checked before we hit A.N1 using
+                       //
+                       if (candidates.Namespace == null) {
+                               Namespace scope;
+                               var methods = candidates.Container.NS.LookupExtensionMethod (candidates.Container, ExtensionExpression.Type, Name, arity, out scope);
+                               if (methods != null) {
+                                       candidates = new ExtensionMethodCandidates (null, candidates.Container, scope);
+                                       return methods.Cast<MemberSpec> ().ToList ();
+                               }
+                       }
+
+                       var ns_container = candidates.HasUninspectedMembers ? candidates.Container : candidates.Container.Parent;
+                       if (ns_container == null)
+                               return null;
+
+                       candidates = ns_container.LookupExtensionMethod (ExtensionExpression.Type, Name, arity);
+                       if (candidates == null)
                                return null;
 
-                       return found.Cast<MemberSpec> ().ToList ();
+                       return candidates.Methods.Cast<MemberSpec> ().ToList ();
                }
 
                public override MethodGroupExpr LookupExtensionMethod (ResolveContext rc)
@@ -3052,7 +3290,9 @@ namespace Mono.CSharp {
                
                public void EmitCall (EmitContext ec, Arguments arguments)
                {
-                       Invocation.EmitCall (ec, InstanceExpression, best_candidate, arguments, loc);                   
+                       var call = new CallEmitter ();
+                       call.InstanceExpression = InstanceExpression;
+                       call.Emit (ec, best_candidate, arguments, loc);                 
                }
 
                public override void Error_ValueCannotBeConverted (ResolveContext ec, Location loc, TypeSpec target, bool expl)
@@ -3177,12 +3417,11 @@ namespace Mono.CSharp {
                                return null;
 
                        int arity = type_arguments == null ? 0 : type_arguments.Count;
-                       NamespaceContainer methods_scope = null;
-                       var methods = rc.LookupExtensionMethod (InstanceExpression.Type, Methods[0].Name, arity, ref methods_scope);
+                       var methods = rc.LookupExtensionMethod (InstanceExpression.Type, Methods[0].Name, arity);
                        if (methods == null)
                                return null;
 
-                       var emg = new ExtensionMethodGroupExpr (methods, methods_scope, InstanceExpression, loc);
+                       var emg = new ExtensionMethodGroupExpr (methods, InstanceExpression, loc);
                        emg.SetTypeArguments (rc, type_arguments);
                        return emg;
                }
@@ -3402,6 +3641,28 @@ namespace Mono.CSharp {
                                if (q.Kind == MemberKind.Void) {
                                        return p.Kind != MemberKind.Void ? 1: 0;
                                }
+
+                               //
+                               // When anonymous method is an asynchronous, and P has a return type Task<Y1>, and Q has a return type Task<Y2>
+                               // better conversion is performed between underlying types Y1 and Y2
+                               //
+                               if (p.IsGenericTask || q.IsGenericTask) {
+                                       if (p.IsGenericTask != q.IsGenericTask) {
+                                               return 0;
+                                       }
+
+                                       var async_am = a.Expr as AnonymousMethodExpression;
+                                       if (async_am == null || !async_am.IsAsync)
+                                               return 0;
+
+                                       q = q.TypeArguments[0];
+                                       p = p.TypeArguments[0];
+                               }
+
+                               //
+                               // The parameters are identicial and return type is not void, use better type conversion
+                               // on return type to determine better one
+                               //
                        } else {
                                if (argument_type == p)
                                        return 1;
@@ -4714,10 +4975,11 @@ namespace Mono.CSharp {
                }
        }
 
-       /// <summary>
-       ///   Fully resolved expression that evaluates to a Field
-       /// </summary>
-       public class FieldExpr : MemberExpr, IDynamicAssign, IMemoryLocation, IVariableReference {
+       //
+       // Fully resolved expression that references a Field
+       //
+       public class FieldExpr : MemberExpr, IDynamicAssign, IMemoryLocation, IVariableReference
+       {
                protected FieldSpec spec;
                VariableInfo variable_info;
                
@@ -4887,7 +5149,7 @@ namespace Mono.CSharp {
                                } else if (var != null && var.IsHoisted) {
                                        AnonymousMethodExpression.Error_AddressOfCapturedVar (ec, var, loc);
                                }
-                               
+
                                return new FixedBufferPtr (this, fb.ElementType, loc).Resolve (ec);
                        }
 
@@ -5066,13 +5328,27 @@ namespace Mono.CSharp {
                        }
                }
 
-               public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
+               public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
                {
-                       prepared = prepare_for_load && !(source is DynamicExpressionStatement);
-                       if (IsInstance)
+                       bool has_await_source = ec.HasSet (BuilderContext.Options.AsyncBody) && source.ContainsEmitWithAwait ();
+                       if (isCompound && !(source is DynamicExpressionStatement)) {
+                               if (has_await_source) {
+                                       if (IsInstance)
+                                               InstanceExpression = InstanceExpression.EmitToField (ec);
+                               } else {
+                                       prepared = true;
+                               }
+                       }
+
+                       if (IsInstance) {
+                               if (has_await_source)
+                                       source = source.EmitToField (ec);
+
                                EmitInstance (ec, prepared);
+                       }
 
                        source.Emit (ec);
+
                        if (leave_copy) {
                                ec.Emit (OpCodes.Dup);
                                if (!IsStatic) {
@@ -5098,6 +5374,18 @@ namespace Mono.CSharp {
                        }
                }
 
+               //
+               // Emits store to field with prepared values on stack
+               //
+               public void EmitAssignFromStack (EmitContext ec)
+               {
+                       if (IsStatic) {
+                               ec.Emit (OpCodes.Stsfld, spec);
+                       } else {
+                               ec.Emit (OpCodes.Stfld, spec);
+                       }
+               }
+
                public override void Emit (EmitContext ec)
                {
                        Emit (ec, false);
@@ -5135,12 +5423,12 @@ namespace Mono.CSharp {
                        } else
                                need_copy = false;
                        
-                       if (need_copy){
-                               LocalBuilder local;
+                       if (need_copy) {
                                Emit (ec);
-                               local = ec.DeclareLocal (type, false);
-                               ec.Emit (OpCodes.Stloc, local);
-                               ec.Emit (OpCodes.Ldloca, local);
+                               var temp = ec.GetTemporaryLocal (type);
+                               ec.Emit (OpCodes.Stloc, temp);
+                               ec.Emit (OpCodes.Ldloca, temp);
+                               ec.FreeTemporaryLocal (temp, type);
                                return;
                        }
 
@@ -5177,14 +5465,13 @@ namespace Mono.CSharp {
        }
 
        
-       /// <summary>
-       ///   Expression that evaluates to a Property.  The Assign class
-       ///   might set the `Value' expression if we are in an assignment.
-       ///
-       ///   This is not an LValue because we need to re-write the expression, we
-       ///   can not take data from the stack and store it.  
-       /// </summary>
-       class PropertyExpr : PropertyOrIndexerExpr<PropertySpec>
+       //
+       // Expression that evaluates to a Property.
+       //
+       // This is not an LValue because we need to re-write the expression. We
+       // can not take data from the stack and store it.
+       //
+       sealed class PropertyExpr : PropertyOrIndexerExpr<PropertySpec>
        {
                public PropertyExpr (PropertySpec spec, Location l)
                        : base (l)
@@ -5195,6 +5482,14 @@ namespace Mono.CSharp {
 
                #region Properties
 
+               protected override Arguments Arguments {
+                       get {
+                               return null;
+                       }
+                       set {
+                       }
+               }
+
                protected override TypeSpec DeclaringType {
                        get {
                                return best_candidate.DeclaringType;
@@ -5295,36 +5590,41 @@ namespace Mono.CSharp {
                        // Special case: length of single dimension array property is turned into ldlen
                        //
                        if (IsSingleDimensionalArrayLength ()) {
-                               if (!prepared)
-                                       EmitInstance (ec, false);
+                               EmitInstance (ec, false);
                                ec.Emit (OpCodes.Ldlen);
                                ec.Emit (OpCodes.Conv_I4);
                                return;
                        }
 
-                       Invocation.EmitCall (ec, InstanceExpression, Getter, null, loc, prepared, false);
-                       
-                       if (leave_copy) {
-                               ec.Emit (OpCodes.Dup);
-                               if (!IsStatic) {
-                                       temp = new LocalTemporary (this.Type);
-                                       temp.Store (ec);
-                               }
-                       }
+                       base.Emit (ec, leave_copy);
                }
 
-               public override void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
+               public override void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
                {
                        Arguments args;
+                       LocalTemporary await_source_arg = null;
 
-                       if (prepare_for_load && !(source is DynamicExpressionStatement)) {
-                               args = new Arguments (0);
-                               prepared = true;
+                       if (isCompound && !(source is DynamicExpressionStatement)) {
+                               emitting_compound_assignment = true;
                                source.Emit (ec);
-                               
-                               if (leave_copy) {
-                                       ec.Emit (OpCodes.Dup);
-                                       if (!IsStatic) {
+
+                               if (has_await_arguments) {
+                                       await_source_arg = new LocalTemporary (Type);
+                                       await_source_arg.Store (ec);
+
+                                       args = new Arguments (1);
+                                       args.Add (new Argument (await_source_arg));
+
+                                       if (leave_copy) {
+                                               temp = await_source_arg;
+                                       }
+
+                                       has_await_arguments = false;
+                               } else {
+                                       args = null;
+
+                                       if (leave_copy) {
+                                               ec.Emit (OpCodes.Dup);
                                                temp = new LocalTemporary (this.Type);
                                                temp.Store (ec);
                                        }
@@ -5342,19 +5642,30 @@ namespace Mono.CSharp {
                                }
                        }
 
-                       Invocation.EmitCall (ec, InstanceExpression, Setter, args, loc, false, prepared);
-                       
+                       emitting_compound_assignment = false;
+
+                       var call = new CallEmitter ();
+                       call.InstanceExpression = InstanceExpression;
+                       if (args == null)
+                               call.InstanceExpressionOnStack = true;
+
+                       call.Emit (ec, Setter, args, loc);
+
                        if (temp != null) {
                                temp.Emit (ec);
                                temp.Release (ec);
                        }
+
+                       if (await_source_arg != null) {
+                               await_source_arg.Release (ec);
+                       }
                }
 
                protected override Expression OverloadResolve (ResolveContext rc, Expression right_side)
                {
                        eclass = ExprClass.PropertyAccess;
 
-                       if (best_candidate.IsNotRealProperty) {
+                       if (best_candidate.IsNotCSharpCompatible) {
                                Error_PropertyNotValid (rc);
                        }
 
@@ -5385,7 +5696,8 @@ namespace Mono.CSharp {
                protected T best_candidate;
 
                protected LocalTemporary temp;
-               protected bool prepared;
+               protected bool emitting_compound_assignment;
+               protected bool has_await_arguments;
 
                protected PropertyOrIndexerExpr (Location l)
                {
@@ -5394,6 +5706,8 @@ namespace Mono.CSharp {
 
                #region Properties
 
+               protected abstract Arguments Arguments { get; set; }
+
                public MethodSpec Getter {
                        get {
                                return getter;
@@ -5468,14 +5782,43 @@ namespace Mono.CSharp {
                //
                // Implements the IAssignMethod interface for assignments
                //
-               public abstract void Emit (EmitContext ec, bool leave_copy);
-               public abstract void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load);
+               public virtual void Emit (EmitContext ec, bool leave_copy)
+               {
+                       var call = new CallEmitter ();
+                       call.InstanceExpression = InstanceExpression;
+                       if (has_await_arguments)
+                               call.HasAwaitArguments = true;
+                       else
+                               call.DuplicateArguments = emitting_compound_assignment;
+
+                       call.Emit (ec, Getter, Arguments, loc);
+
+                       if (call.HasAwaitArguments) {
+                               InstanceExpression = call.InstanceExpression;
+                               Arguments = call.EmittedArguments;
+                               has_await_arguments = true;
+                       }
+
+                       if (leave_copy) {
+                               ec.Emit (OpCodes.Dup);
+                               temp = new LocalTemporary (Type);
+                               temp.Store (ec);
+                       }
+               }
+
+               public abstract void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound);
 
                public override void Emit (EmitContext ec)
                {
                        Emit (ec, false);
                }
 
+               protected override void EmitToFieldSource (EmitContext ec)
+               {
+                       has_await_arguments = true;
+                       Emit (ec, false);
+               }
+
                public abstract SLE.Expression MakeAssignExpression (BuilderContext ctx, Expression source);
 
                protected abstract Expression OverloadResolve (ResolveContext rc, Expression right_side);
@@ -5664,14 +6007,17 @@ namespace Mono.CSharp {
                        throw new NotImplementedException ();
                }
 
-               public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
+               public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
                {
-                       if (leave_copy || !prepare_for_load)
+                       if (leave_copy || !isCompound)
                                throw new NotImplementedException ("EventExpr::EmitAssign");
 
                        Arguments args = new Arguments (1);
                        args.Add (new Argument (source));
-                       Invocation.EmitCall (ec, InstanceExpression, op, args, loc);
+
+                       var call = new CallEmitter ();
+                       call.InstanceExpression = InstanceExpression;
+                       call.Emit (ec, op, args, loc);
                }
 
                #endregion