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
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:
}
}
+
+ 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)
{
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;
+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
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)
// 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);
// 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;
}
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);
}
//
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 ());
//
// returns the T in a a Nullable<T> type.
//
- static Type GetNullableOf (Type type)
+ internal static Type GetNullableOf (Type type)
{
return type.GetGenericArguments () [0];
}
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 (
tb.CreateType ();
ab.Save (fname);
}
+
+ lambda_delegate = ec.CreateDelegate ();
}
return lambda_delegate;
}
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");
+ }
}
}
--- /dev/null
+// 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));
+ }
+ }
+}
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");
+ }
+
}
}