2008-01-22 Miguel de Icaza <miguel@novell.com>
authorMiguel de Icaza <miguel@gnome.org>
Tue, 22 Jan 2008 07:01:58 +0000 (07:01 -0000)
committerMiguel de Icaza <miguel@gnome.org>
Tue, 22 Jan 2008 07:01:58 +0000 (07:01 -0000)
* BinaryExpression.cs (EmitCoalesce): Add support for emitting
code for Coalesce.

TODO: this does not use the "Conversion" Lambda, which am not sure
who generates this or what it is used for.

(EmitLogical): Fix a couple of bugs in AndAlso, OrElse.

* Expression.cs: Add support for Coalesce.
(BinaryCoreCheck): Move more checking here, instead of the helper
routines, will remove them next.

* LambdaExpression.cs (Compile): Create the delegate last, so we
manage to save the assembly while debugging in case of error

svn path=/trunk/mcs/; revision=93480

mcs/class/System.Core/System.Core_test.dll.sources
mcs/class/System.Core/System.Linq.Expressions/BinaryExpression.cs
mcs/class/System.Core/System.Linq.Expressions/ChangeLog
mcs/class/System.Core/System.Linq.Expressions/Expression.cs
mcs/class/System.Core/System.Linq.Expressions/LambdaExpression.cs
mcs/class/System.Core/Test/System.Linq.Expressions/ExpressionTest_And.cs
mcs/class/System.Core/Test/System.Linq.Expressions/ExpressionTest_Coalesce.cs [new file with mode: 0644]
mcs/class/System.Core/Test/System.Linq.Expressions/ExpressionTest_Or.cs

index c348280ced7d23aae90e8e202a4e46a35f830689..18f371465d6eb8eae5eb08850dc47440c8bf3aac 100644 (file)
@@ -10,6 +10,7 @@ System.Linq.Expressions/ExpressionTest_And.cs
 System.Linq.Expressions/ExpressionTest_ArrayIndex.cs
 System.Linq.Expressions/ExpressionTest_ArrayLength.cs
 System.Linq.Expressions/ExpressionTest_CallWithExpression.cs
+System.Linq.Expressions/ExpressionTest_Coalesce.cs
 System.Linq.Expressions/ExpressionTest_Condition.cs
 System.Linq.Expressions/ExpressionTest_Constant.cs
 System.Linq.Expressions/ExpressionTest_Divide.cs
index f7a381a7736bcda12e82ca7d3801ed5727d0c06a..d66ec0b42361144d84bf30cd91b402300bf4e812 100644 (file)
@@ -176,9 +176,13 @@ namespace System.Linq.Expressions {
                                ig.Emit (OpCodes.Ldloca, vright);
                                ig.Emit (OpCodes.Call, has_value);
                                ig.Emit (OpCodes.Brfalse, both_are_null);
+                               ig.Emit (OpCodes.Ldloca, vright);
                                ig.Emit (OpCodes.Call, get_value);
                                ig.Emit (OpCodes.Dup);
                                ig.Emit (and ? OpCodes.Brfalse : OpCodes.Brtrue, create);
+
+                       // right_is_null:
+                               ig.MarkLabel (right_is_null);
                                ig.Emit (OpCodes.Pop);
 
                        // both_are_null:
@@ -201,6 +205,53 @@ namespace System.Linq.Expressions {
                        }
                        
                }
+
+               void EmitCoalesce (EmitContext ec)
+               {
+                       ILGenerator ig = ec.ig;
+                       
+                       LocalBuilder vleft;
+                       LocalBuilder vright;
+                                       
+                       MethodInfo has_value = left.Type.GetMethod ("get_HasValue");
+                       
+                       Label exit = ig.DefineLabel ();
+                       Label try_right = ig.DefineLabel ();
+                       Label setup_null = ig.DefineLabel ();
+
+                       vleft = EmitStored (ec, left);
+                       if (IsNullable (left.Type)){
+                               ig.Emit (OpCodes.Ldloca, vleft);
+                               ig.Emit (OpCodes.Call, has_value);
+                       } else 
+                               ig.Emit (OpCodes.Ldloc, vleft);
+                       
+                       ig.Emit (OpCodes.Brfalse, try_right);
+                       ig.Emit (OpCodes.Ldloc, vleft);
+                       ig.Emit (OpCodes.Br, exit);
+                       
+               // try_right;
+                       ig.MarkLabel (try_right);
+                       vright = EmitStored (ec, right);
+                       if (IsNullable (right.Type)){
+                               ig.Emit (OpCodes.Ldloca, vright);
+                               ig.Emit (OpCodes.Call, has_value);
+                       } else
+                               ig.Emit (OpCodes.Ldloc, vright);
+                       
+                       ig.Emit (OpCodes.Brfalse, setup_null);
+                       ig.Emit (OpCodes.Ldloc, vright);
+                       ig.Emit (OpCodes.Br, exit);
+
+               // setup_null:
+                       ig.MarkLabel (setup_null);
+                       LocalBuilder ret = ig.DeclareLocal (Type);
+                       ig.Emit (OpCodes.Ldloca, ret);
+                       ig.Emit (OpCodes.Initobj, Type);
+                       
+               // exit:
+                       ig.MarkLabel (exit);
+               }
                
                internal override void Emit (EmitContext ec)
                {
@@ -230,6 +281,10 @@ namespace System.Linq.Expressions {
                                Console.WriteLine ("LINQ/BinaryExpression: OrElse code path not yet reviewed");
                                EmitLogical (ec, false, true);
                                return;
+
+                       case ExpressionType.Coalesce:
+                               EmitCoalesce (ec);
+                               return;
                        }
                        
                        Label? empty_value = null;
index 09f2443ecbb57903cf767dd0c2abf3f243a8cdb1..6f43e90079031db670a316c5e11d0b09311ebf31 100644 (file)
@@ -1,3 +1,20 @@
+2008-01-22  Miguel de Icaza  <miguel@novell.com>
+
+       * BinaryExpression.cs (EmitCoalesce): Add support for emitting
+       code for Coalesce.
+
+       TODO: this does not use the "Conversion" Lambda, which am not sure
+       who generates this or what it is used for.
+
+       (EmitLogical): Fix a couple of bugs in AndAlso, OrElse. 
+
+       * Expression.cs: Add support for Coalesce.
+       (BinaryCoreCheck): Move more checking here, instead of the helper
+       routines, will remove them next.
+       
+       * LambdaExpression.cs (Compile): Create the delegate last, so we
+       manage to save the assembly while debugging in case of error
+
 2008-01-21  Miguel de Icaza  <miguel@novell.com>
 
        * Expression.cs (BinaryCoreCheck): Add checking for a few
index c71ce40940b67848a73f51cb03ad2f69ee11d46f..b40de6ebbb81e3fb06d75962308209e4b415380a 100644 (file)
@@ -215,6 +215,13 @@ namespace System.Linq.Expressions {
                                if (IsNullable (rtype))
                                        urtype = GetNullableOf (rtype);
 
+                               if (oper_name == "op_BitwiseOr" || oper_name == "op_BitwiseAnd"){
+                                       if (ultype == typeof (bool)){
+                                               if (ultype == urtype && ltype == rtype)
+                                                       return null;
+                                       }
+                               }
+                               
                                if (oper_name == "op_LogicalAnd" || oper_name == "op_LogicalOr"){
                                        if (ultype == typeof (bool)){
                                                if (ultype == urtype && ltype == rtype)
@@ -271,7 +278,6 @@ namespace System.Linq.Expressions {
                                // avoid reflection shortcut and catches Ints/bools before we check Numbers in general
                                if (left.Type == right.Type && (left.Type == typeof (bool) || IsInt (left.Type)))
                                        return method;
-
                        }
 
                        method = BinaryCoreCheck (oper_name, left, right, method);
@@ -280,7 +286,8 @@ namespace System.Linq.Expressions {
                                // The check in BinaryCoreCheck allows a bit more than we do
                                // (floats and doubles).  Catch this here
                                //
-                               throw new InvalidOperationException ("Types not supported");
+                               if (left.Type == typeof(double) || left.Type == typeof(float))
+                                       throw new InvalidOperationException ("Types not supported");
                        }
                        return method;
                }
@@ -704,12 +711,44 @@ namespace System.Linq.Expressions {
                        return Coalesce (left, right, null);
                }
 
-               [MonoTODO]
                public static BinaryExpression Coalesce (Expression left, Expression right, LambdaExpression conversion)
                {
-                       BinaryCoreCheck (null, left, right, null);
+                       if (left == null)
+                               throw new ArgumentNullException ("left");
+                       if (right == null)
+                               throw new ArgumentNullException ("right");
 
-                       throw new NotImplementedException ();
+                       //
+                       // First arg must ne nullable (either Nullable<T> or a reference type
+                       //
+                       if (left.Type.IsValueType && !IsNullable (left.Type))
+                               throw new InvalidOperationException ("Left expression can never be null");
+                       
+                       Type result = null;
+
+                       if (IsNullable (left.Type)){
+                               Type lbase = GetNullableOf (left.Type);
+                               
+                               if (!IsNullable (right.Type) && lbase.IsAssignableFrom (right.Type))
+                                       result = lbase;
+                       }
+
+                       if (result == null && left.Type.IsAssignableFrom (right.Type))
+                               result = left.Type;
+
+                       if (result == null){
+                               if (IsNullable (left.Type) && right.Type.IsAssignableFrom (GetNullableOf (left.Type))){
+                                       result = right.Type;
+                               }
+                       }
+
+                       if (result == null)
+                               throw new ArgumentException ("Incompatible argument types");
+
+                       //
+                       // FIXME: What do we do with "conversion"?
+                       //
+                       return new BinaryExpression (ExpressionType.Coalesce, result, left, right, false, false, null, conversion);
                }
 
                //
@@ -1390,12 +1429,12 @@ namespace System.Linq.Expressions {
                        return MakeSimpleUnary (ExpressionType.UnaryPlus, expression, method);
                }
 
-               static bool IsNullable (Type type)
+               internal static bool IsNullable (Type type)
                {
                        return type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>);
                }
 
-               protected static bool IsUnsigned (Type t)
+               internal static bool IsUnsigned (Type t)
                {
                        if (t.IsPointer)
                                return IsUnsigned (t.GetElementType ());
@@ -1406,7 +1445,7 @@ namespace System.Linq.Expressions {
                //
                // returns the T in a a Nullable<T> type.
                //
-               static Type GetNullableOf (Type type)
+               internal static Type GetNullableOf (Type type)
                {
                        return type.GetGenericArguments () [0];
                }
index 66f28ecab2876167f4ee428fa3de0e9ac58d3c07..cf37e82bef85cad8d2f210fec4751dd70d6eae3e 100644 (file)
@@ -171,8 +171,6 @@ namespace System.Linq.Expressions {
                                body.Emit (ec);
                                ec.ig.Emit (OpCodes.Ret);
 
-                               lambda_delegate = ec.CreateDelegate ();
-
                                if (Environment.GetEnvironmentVariable ("LINQ_DBG") != null){
                                        string fname = "linq" + (EmitContext.method_count-1) + ".dll";
                                        AssemblyBuilder ab = Thread.GetDomain ().DefineDynamicAssembly (
@@ -189,6 +187,8 @@ namespace System.Linq.Expressions {
                                        tb.CreateType ();
                                        ab.Save (fname);
                                }
+                               
+                               lambda_delegate = ec.CreateDelegate ();
                        }
                        return lambda_delegate;
                }
index b8a655e41bbe08e0bdd8e76b9a315c65bcab96ba..d735d7d210201b28b41ef2a5bb2e3e7aaf41e5ac 100644 (file)
@@ -100,5 +100,38 @@ namespace MonoTests.System.Linq.Expressions
                        Assert.AreEqual ("(value(MonoTests.System.Linq.Expressions.OpClass) & value(MonoTests.System.Linq.Expressions.OpClass))",
                                expr.ToString(), "And#13");
                }
+
+               [Test]
+               public void AndTest ()
+               {
+                       Expression<Func<bool, bool, bool>> e = (bool a, bool b) => a & b;
+                       
+                       Func<bool,bool,bool> c = e.Compile ();
+                       
+                       Assert.AreEqual (true,  c (true, true), "t1");
+                       Assert.AreEqual (false, c (true, false), "t2");
+                       Assert.AreEqual (false, c (false, true), "t3");
+                       Assert.AreEqual (false, c (false, false), "t4");
+               }
+               
+               [Test]
+               public void AndNullableTest ()
+               {
+                       Expression<Func<bool?, bool?, bool?>> e = (bool? a, bool? b) => a & b;
+                       
+                       Func<bool?,bool?,bool?> c = e.Compile ();
+                       
+                       
+                       Assert.AreEqual (true,  c (true, true), "a1");
+                       Assert.AreEqual (false, c (true, false), "a2");
+                       Assert.AreEqual (false, c (false, true), "a3");
+                       Assert.AreEqual (false, c (false, false), "a4");
+                       
+                       Assert.AreEqual (null,  c (true, null),  "a5");
+                       Assert.AreEqual (false, c (false, null), "a6");
+                       Assert.AreEqual (false, c (null, false), "a7");
+                       Assert.AreEqual (null,  c (true, null),  "a8");
+                       Assert.AreEqual (null, c (null, null),   "a9");
+               }
        }
 }
diff --git a/mcs/class/System.Core/Test/System.Linq.Expressions/ExpressionTest_Coalesce.cs b/mcs/class/System.Core/Test/System.Linq.Expressions/ExpressionTest_Coalesce.cs
new file mode 100644 (file)
index 0000000..e3c4b99
--- /dev/null
@@ -0,0 +1,80 @@
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+//
+// Authors:
+//             Federico Di Gregorio <fog@initd.org>
+
+using System;
+using System.Reflection;
+using System.Linq;
+using System.Linq.Expressions;
+using NUnit.Framework;
+
+namespace MonoTests.System.Linq.Expressions
+{
+       [TestFixture]
+       public class ExpressionTest_Coalesce
+       {
+               [Test]
+               [ExpectedException (typeof (ArgumentNullException))]
+               public void Arg1Null ()
+               {
+                       Expression.Coalesce (null, Expression.Constant (1));
+               }
+
+               [Test]
+               [ExpectedException (typeof (ArgumentNullException))]
+               public void Arg2Null ()
+               {
+                       Expression.Coalesce (Expression.Constant (1), null);
+               }
+
+               [Test]
+               [ExpectedException (typeof (InvalidOperationException))]
+               public void NonNullLeftParameter ()
+               {
+                       // This throws because they are both doubles, which are never 
+                       Expression.Coalesce (Expression.Constant (1.0), Expression.Constant (2.0));
+               }
+
+               [Test]
+               [ExpectedException (typeof (ArgumentException))]
+               public void Incompatible_Arguments ()
+               {
+                       // The artuments are not compatible
+                       Expression.Coalesce (Expression.Parameter (typeof (int?), "a"),
+                                            Expression.Parameter (typeof (bool), "b"));
+               }
+
+               [Test]
+               public void Coalesce_1 ()
+               {
+                       // Build the expression by hand for now:
+                       ParameterExpression pa = Expression.Parameter (typeof (int?), "a");
+                       ParameterExpression pb = Expression.Parameter (typeof (int?), "b");
+                       BinaryExpression c = Expression.Coalesce (pa, pb);
+                       
+                       Expression<Func<int?, int?, int?>> e = Expression.Lambda<Func<int?,int?,int?>>(
+                               c, new ParameterExpression [] { pa, pb });
+
+                       Func<int?,int?,int?> comp = e.Compile ();
+
+                       Assert.AreEqual (10, comp (10, 20));
+               }
+       }
+}
index 8e7a68aa6fa1dbea7c84035a61c8d10a0ff43e23..9c80c882e4c63575410619ffca092bc9e4b9ccc0 100644 (file)
@@ -100,5 +100,38 @@ namespace MonoTests.System.Linq.Expressions
                        Assert.AreEqual ("(value(MonoTests.System.Linq.Expressions.OpClass) | value(MonoTests.System.Linq.Expressions.OpClass))",
                                expr.ToString(), "Or#13");
                }
+
+               [Test]
+               public void OrTest ()
+               {
+                       Expression<Func<bool, bool, bool>> e = (bool a, bool b) => a | b;
+                       
+                       Func<bool,bool,bool> c = e.Compile ();
+                       
+                       Assert.AreEqual (true,  c (true, true), "o1");
+                       Assert.AreEqual (true, c (true, false), "o2");
+                       Assert.AreEqual (true, c (false, true), "o3");
+                       Assert.AreEqual (false, c (false, false), "o4");
+               }
+               
+               [Test]
+               public void OrNullableTest ()
+               {
+                       Expression<Func<bool?, bool?, bool?>> e = (bool? a, bool? b) => a | b;
+                       
+                       Func<bool?,bool?,bool?> c = e.Compile ();
+                       
+                       Assert.AreEqual (true,  c (true, true),   "o1");
+                       Assert.AreEqual (true,  c (true, false),  "o2");
+                       Assert.AreEqual (true,  c (false, true),  "o3");
+                       Assert.AreEqual (false, c (false, false), "o4");
+       
+                       Assert.AreEqual (true, c (true, null),  "o5");
+                       Assert.AreEqual (null, c (false, null), "o6");
+                       Assert.AreEqual (null, c (null, false), "o7");
+                       Assert.AreEqual (true, c (true, null),  "o8");
+                       Assert.AreEqual (null, c (null, null),  "o9");
+               }
+       
        }
 }