Implement dynamic mutator over dynamic indexer
authorMarek Safar <marek.safar@gmail.com>
Wed, 10 Nov 2010 17:49:01 +0000 (17:49 +0000)
committerMarek Safar <marek.safar@gmail.com>
Wed, 10 Nov 2010 17:57:08 +0000 (17:57 +0000)
mcs/mcs/argument.cs
mcs/mcs/dynamic.cs
mcs/tests/dtest-051.cs [new file with mode: 0644]

index c3fbe5d2a52804e0c5afa8c38b1b839b4fd0a6dc..604bbbb5e0ba653e3c9f3ed5fe3697987e754ed7 100644 (file)
@@ -48,8 +48,14 @@ namespace Mono.CSharp
                        this.Expr = expr;
                }
 
-               public TypeSpec Type {
-                       get { return Expr.Type; }
+               #region Properties
+
+               public bool IsByRef {
+                       get { return ArgType == AType.Ref || ArgType == AType.Out; }
+               }
+
+               public bool IsDefaultArgument {
+                       get { return ArgType == AType.Default; }
                }
 
                public Parameter.Modifier Modifier {
@@ -67,6 +73,24 @@ namespace Mono.CSharp
                        }
                }
 
+               public TypeSpec Type {
+                       get { return Expr.Type; }
+               }
+
+               #endregion
+
+               public Argument Clone (Expression expr)
+               {
+                       Argument a = (Argument) MemberwiseClone ();
+                       a.Expr = expr;
+                       return a;
+               }
+
+               public Argument Clone (CloneContext clonectx)
+               {
+                       return Clone (Expr.Clone (clonectx));
+               }
+
                public virtual Expression CreateExpressionTree (ResolveContext ec)
                {
                        if (ArgType == AType.Default)
@@ -83,14 +107,6 @@ namespace Mono.CSharp
                        return TypeManager.CSharpName (Expr.Type);
                }
 
-               public bool IsByRef {
-                       get { return ArgType == AType.Ref || ArgType == AType.Out; }
-               }
-
-               public bool IsDefaultArgument {
-                       get { return ArgType == AType.Default; }
-               }
-
                public bool ResolveMethodGroup (ResolveContext ec)
                {
                        SimpleName sn = Expr as SimpleName;
@@ -139,13 +155,6 @@ namespace Mono.CSharp
                        IMemoryLocation ml = (IMemoryLocation) Expr;
                        ml.AddressOf (ec, mode);
                }
-
-               public Argument Clone (CloneContext clonectx)
-               {
-                       Argument a = (Argument) MemberwiseClone ();
-                       a.Expr = Expr.Clone (clonectx);
-                       return a;
-               }
        }
 
        public class NamedArgument : Argument
@@ -395,7 +404,7 @@ namespace Mono.CSharp
                                if (!dup_args)
                                        continue;
 
-                               if (a.Expr is Constant) {
+                               if (a.Expr is Constant || a.Expr is This) {
                                        //
                                        // No need to create a temporary variable for constants
                                        //
index 6af6dad11b41a52ae905ff4f7fb7c7201a9c54c0..e6e042ea70d7903779a84cbfcd6bd065c6bea147 100644 (file)
@@ -562,6 +562,8 @@ namespace Mono.CSharp
 
        class DynamicIndexBinder : DynamicMemberAssignable
        {
+               bool can_be_mutator;
+
                public DynamicIndexBinder (Arguments args, Location loc)
                        : base (args, loc)
                {
@@ -573,6 +575,12 @@ namespace Mono.CSharp
                        base.flags = flags;
                }
 
+               protected override Expression DoResolve (ResolveContext ec)
+               {
+                       can_be_mutator = true;
+                       return base.DoResolve (ec);
+               }
+
                protected override Expression CreateCallSiteBinder (ResolveContext ec, Arguments args, bool isSet)
                {
                        Arguments binder_args = new Arguments (3);
@@ -584,6 +592,38 @@ namespace Mono.CSharp
                        isSet |= (flags & CSharpBinderFlags.ValueFromCompoundAssignment) != 0;
                        return new Invocation (GetBinder (isSet ? "SetIndex" : "GetIndex", loc), binder_args);
                }
+
+               protected override Arguments CreateSetterArguments (ResolveContext rc, Expression rhs)
+               {
+                       //
+                       // Indexer has arguments which complicates things as the setter and getter
+                       // are called in two steps when unary mutator is used. We have to make a
+                       // copy of all variable arguments to not duplicate any side effect.
+                       //
+                       // ++d[++arg, Foo ()]
+                       //
+
+                       if (!can_be_mutator)
+                               return base.CreateSetterArguments (rc, rhs);
+
+                       var setter_args = new Arguments (Arguments.Count + 1);
+                       for (int i = 0; i < Arguments.Count; ++i) {
+                               var expr = Arguments[i].Expr;
+
+                               if (expr is Constant || expr is VariableReference || expr is This) {
+                                       setter_args.Add (Arguments [i]);
+                                       continue;
+                               }
+
+                               LocalVariable temp = LocalVariable.CreateCompilerGenerated (expr.Type, rc.CurrentBlock, loc);
+                               expr = new SimpleAssign (temp.CreateReferenceExpression (rc, expr.Location), expr).Resolve (rc);
+                               Arguments[i].Expr = temp.CreateReferenceExpression (rc, expr.Location).Resolve (rc);
+                               setter_args.Add (Arguments [i].Clone (expr));
+                       }
+
+                       setter_args.Add (new Argument (rhs));
+                       return setter_args;
+               }
        }
 
        class DynamicInvocation : DynamicExpressionStatement, IDynamicBinder
@@ -722,6 +762,14 @@ namespace Mono.CSharp
 
                protected abstract Expression CreateCallSiteBinder (ResolveContext ec, Arguments args, bool isSet);
 
+               protected virtual Arguments CreateSetterArguments (ResolveContext rc, Expression rhs)
+               {
+                       var setter_args = new Arguments (Arguments.Count + 1);
+                       setter_args.AddRange (Arguments);
+                       setter_args.Add (new Argument (rhs));
+                       return setter_args;
+               }
+
                public override Expression DoResolveLValue (ResolveContext rc, Expression right_side)
                {
                        if (right_side == EmptyExpression.OutAccess.Instance) {
@@ -730,9 +778,7 @@ namespace Mono.CSharp
                        }
 
                        if (DoResolveCore (rc)) {
-                               setter_args = new Arguments (Arguments.Count + 1);
-                               setter_args.AddRange (Arguments);
-                               setter_args.Add (new Argument (right_side));
+                               setter_args = CreateSetterArguments (rc, right_side);
                                setter = CreateCallSiteBinder (rc, setter_args, true);
                        }
 
diff --git a/mcs/tests/dtest-051.cs b/mcs/tests/dtest-051.cs
new file mode 100644 (file)
index 0000000..5c9d432
--- /dev/null
@@ -0,0 +1,31 @@
+using System;
+
+class C
+{
+       int value = 1;
+       
+       public int this [int arg] {
+               get { return this.value; }
+               set { this.value = value + arg; }
+       }
+       
+       public static int Main ()
+       {
+               C c = new C ();
+               dynamic d = c;
+               int index = 1;
+
+               var x = ++d[++index];
+
+               if (index != 2)
+                       return 1;
+               
+               if (c.value != 4)
+                       return 2;
+               
+               if (x != 2)
+                       return 3;
+               
+               return 0;
+       }
+}
\ No newline at end of file