2008-01-21 Miguel de Icaza <miguel@novell.com>
authorMiguel de Icaza <miguel@gnome.org>
Mon, 21 Jan 2008 06:05:28 +0000 (06:05 -0000)
committerMiguel de Icaza <miguel@gnome.org>
Mon, 21 Jan 2008 06:05:28 +0000 (06:05 -0000)
* Start code generation for nullables, currently this generates
incorrect code for things like:

Expression<Func<int?, int?, int?>> e2 = (a, b) => a + b;
e2.Compile ().Invoke (null, 3))

This should return null, but returns something else.

* Introduce LINQ_DBG env variable, which generates a linq file in
/tmp;   It currently does not work as well as it should, as the
Func<> parameters do not mwatch the generated method.
Investigate.

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

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_Add.cs
mcs/class/System.Core/Test/System.Linq.Expressions/ExpressionTest_Lambda.cs

index fb36c84a9a88421c5ebecb559e059546279f2b69..e22cd998d253b1a64dc0eeb1de0ac0ea5693293f 100644 (file)
@@ -105,13 +105,70 @@ namespace System.Linq.Expressions {
                        return t == typeof (ushort) || t == typeof (uint) || t == typeof (ulong) || t == typeof (byte);
                }
 
+               static void EmitMethod ()
+               {
+                       throw new NotImplementedException ("Support for MethodInfo-based BinaryExpressions not yet supported");
+               }
+
+               static MethodInfo GetMethodNoPar (Type t, string name)
+               {
+                       MethodInfo [] methods = t.GetMethods ();
+                       foreach (MethodInfo m in methods){
+                               if (m.Name != name)
+                                       continue;
+                               if (m.GetParameters ().Length == 0)
+                                       return m;
+                       }
+                       throw new Exception (String.Format ("Internal error: method {0} with no parameters not found on {1}",
+                                                           name, t));
+               }
+               
                internal override void Emit (EmitContext ec)
                {
                        ILGenerator ig = ec.ig;
                        OpCode opcode;
 
-                       left.Emit (ec);
-                       right.Emit (ec);
+                       if (method != null){
+                               EmitMethod ();
+                               return;
+                       }
+
+                       Label? empty_value = null;
+                       LocalBuilder ret = null;
+
+                       if (IsLifted){
+                               LocalBuilder vleft, vright;
+
+                               empty_value = ig.DefineLabel ();
+                               ret = ig.DeclareLocal (Type);
+                               
+                               vleft = ig.DeclareLocal (left.Type);
+                               left.Emit (ec);
+                               ig.Emit (OpCodes.Stloc, vleft);
+
+                               vright = ig.DeclareLocal (right.Type);
+                               right.Emit (ec);
+                               ig.Emit (OpCodes.Stloc, vright);
+
+                               MethodInfo has_value = left.Type.GetMethod ("get_HasValue");
+                               
+                               ig.Emit (OpCodes.Ldloca, vleft);
+                               ig.Emit (OpCodes.Call, has_value);
+                               ig.Emit (OpCodes.Brfalse, empty_value.Value);
+                               ig.Emit (OpCodes.Ldloca, vright);
+                               ig.Emit (OpCodes.Call, has_value);
+                               ig.Emit (OpCodes.Brfalse, empty_value.Value);
+
+                               MethodInfo get_value_or_default = GetMethodNoPar (left.Type, "GetValueOrDefault");
+                               
+                               ig.Emit (OpCodes.Ldloca, vleft);
+                               ig.Emit (OpCodes.Call, get_value_or_default);
+                               ig.Emit (OpCodes.Ldloca, vright);
+                               ig.Emit (OpCodes.Call, get_value_or_default);
+                       } else {
+                               left.Emit (ec);
+                               right.Emit (ec);
+                       }
 
                        bool is_unsigned = IsUnsigned (left.Type);
 
@@ -208,6 +265,20 @@ namespace System.Linq.Expressions {
                                throw new Exception (String.Format ("Internal error: BinaryExpression contains non-Binary nodetype {0}", NodeType));
                        }
                        ig.Emit (opcode);
+
+                       if (IsLifted){
+                               Label skip = ig.DefineLabel ();
+                               ig.Emit (OpCodes.Br, skip);
+                               ig.MarkLabel (empty_value.Value);
+                               ig.Emit (OpCodes.Ldloc, ret);
+                               ig.Emit (OpCodes.Initobj, Type);
+                               Label end = ig.DefineLabel ();
+                               ig.Emit (OpCodes.Br, end);
+                               
+                               ig.MarkLabel (skip);
+                               ig.Emit (OpCodes.Newobj, left.Type.GetConstructors ()[0]);
+                               ig.MarkLabel (end);
+                       }
                }
        }
 }
index fa8c13793139a041acfbdb7825ae0cf29432de4c..55352cff0c713790f6915b2a71b60b7dda237ee2 100644 (file)
@@ -1,3 +1,19 @@
+2008-01-21  Miguel de Icaza  <miguel@novell.com>
+
+       * Start code generation for nullables, currently this generates
+       incorrect code for things like:
+       
+       Expression<Func<int?, int?, int?>> e2 = (a, b) => a + b;
+       e2.Compile ().Invoke (null, 3))
+
+       This should return null, but returns something else.
+
+       * Introduce LINQ_DBG env variable, which generates a linq file in
+       /tmp;   It currently does not work as well as it should, as the
+       Func<> parameters do not mwatch the generated method.
+       Investigate. 
+       
+
 2008-01-20  Miguel de Icaza  <miguel@novell.com>
 
        Introduce support for Nullable arguments, no code is generated for
index 3bf6774f8781f37dd07e4204000bb06dff36c1be..fe5594ddf532d9d4daccd011c6519c7c405a6f10 100644 (file)
@@ -277,8 +277,23 @@ namespace System.Linq.Expressions {
                static BinaryExpression MakeSimpleBinary (ExpressionType et, Expression left, Expression right, MethodInfo method)
                {
                        Type result = method == null ? left.Type : method.ReturnType;
-
-                       return new BinaryExpression (et, result, left, right, method);
+                       bool is_lifted;
+                       
+                       if (method == null){
+                               if (IsNullable (left.Type)){
+                                       if (!IsNullable (right.Type))
+                                               throw new Exception ("Assertion, internal error: left is nullable, requires right to be as well");
+                                       is_lifted = true;
+                               } else
+                                       is_lifted = false;
+                       } else {
+                               //
+                               // FIXME: implement
+                               //
+                               is_lifted = false;
+                       }
+                       
+                       return new BinaryExpression (et, result, left, right, false, is_lifted, method, null);
                }
 
                static UnaryExpression MakeSimpleUnary (ExpressionType et, Expression expression, MethodInfo method)
index a523f44067b2ec7b532e443d14f54c40635b6ea8..b4218b42914be963bb829f136b848404e804c0b1 100644 (file)
@@ -32,6 +32,7 @@ using System.Collections.ObjectModel;
 using System.Collections.Generic;
 using System.Reflection;
 using System.Reflection.Emit;
+using System.Threading;
 
 namespace System.Linq.Expressions {
 
@@ -40,6 +41,11 @@ namespace System.Linq.Expressions {
                internal Type [] ParamTypes;
                internal DynamicMethod Method;
                internal ILGenerator ig;
+
+               // When debugging:
+               internal AssemblyBuilder ab;
+               internal TypeBuilder tb;
+               internal MethodBuilder mb;
                
                static object mlock = new object ();
                static int method_count;
@@ -70,12 +76,39 @@ namespace System.Linq.Expressions {
                        // FIXME: Need to force this to be verifiable, see:
                        // https://bugzilla.novell.com/show_bug.cgi?id=355005
                        //
-                       Method = new DynamicMethod (GenName (), Owner.Type, ParamTypes, owner_of_code);
-                       ig = Method.GetILGenerator ();
+                       string name = GenName ();
+                       if (Environment.GetEnvironmentVariable ("LINQ_DBG") != null){
+                               string fname = "linq" + (method_count-1) + ".dll";
+                               ab = Thread.GetDomain ().DefineDynamicAssembly (new AssemblyName (fname),
+                                                                               AssemblyBuilderAccess.RunAndSave, "/tmp");
+                               ModuleBuilder b = ab.DefineDynamicModule (fname, fname);
+                               tb = b.DefineType ("LINQ", TypeAttributes.Public);
+                               mb = tb.DefineMethod ("A", MethodAttributes.Static, Owner.Type, ParamTypes);
+                               ig = mb.GetILGenerator ();
+                       } else {
+                               Method = new DynamicMethod (name, Owner.Type, ParamTypes, owner_of_code);
+                       
+                               ig = Method.GetILGenerator ();
+                       }
                }
 
                internal Delegate CreateDelegate ()
                {
+                       if (ab != null){
+                               tb.CreateType ();
+                               ab.Save ("linq" + (method_count-1) + ".dll");
+
+                               // This does not work, need to figure out why, for now
+                               // makes debugging harder (only one linq file will work unless
+                               // you do not compile them
+                               Delegate d = Delegate.CreateDelegate (Owner.delegate_type, tb, mb, true);
+
+                               ab = null;
+                               tb = null;
+
+                               //Console.WriteLine ("got: {0}", d);
+                               return null;
+                       }
                        return Method.CreateDelegate (Owner.delegate_type);                     
                }
 
@@ -167,6 +200,7 @@ namespace System.Linq.Expressions {
                                body.Emit (ec);
 
                                ec.ig.Emit (OpCodes.Ret);
+
                                lambda_delegate = ec.CreateDelegate ();
                        }
                        return lambda_delegate;
index 93bb5b43eeae760be2c87df6d3d9ea8818331c2a..99cb99ccc425a65230fc3a4c3835b88fc5b5f54c 100644 (file)
@@ -81,9 +81,10 @@ namespace MonoTests.System.Linq.Expressions
                        int? a = 1;
                        int? b = 2;
 
-                       BinaryExpression expr = Expression.Add (Expression.Constant (a), Expression.Constant (b));
+                       BinaryExpression expr = Expression.Add (Expression.Constant (a,typeof(int?)),
+                                                               Expression.Constant (b, typeof(int?)));
                        Assert.AreEqual (ExpressionType.Add, expr.NodeType, "Add#05");
-                       Assert.AreEqual (typeof (int), expr.Type, "Add#06");
+                       Assert.AreEqual (typeof (int?), expr.Type, "Add#06");
                        Assert.IsNull (expr.Method, "Add#07");
                        Assert.AreEqual ("(1 + 2)", expr.ToString(), "Add#08");
                }
index e123e6e781113f82278b712c40c7aa9f50c3993b..715bc0088a60b49a6222774498094a5cad7cfe5b 100644 (file)
@@ -141,5 +141,19 @@ namespace MonoTests.System.Linq.Expressions
                        Func<int> fi = l.Compile ();
                        fi ();
                }
+
+               [Test]
+               [ExpectedException(typeof(ArgumentException))]
+               public void ReturnValueCheck ()
+               {
+                       ParameterExpression p1 = Expression.Parameter(typeof(int?), "va");
+                       ParameterExpression p2 = Expression.Parameter(typeof(int?), "vb");
+                       Expression add = Expression.Add(p1, p2);
+                       
+
+                       // This should throw, since the add.Type is "int?" and the return
+                       // type we have here is int.
+                       Expression<Func<int?, int?, int>> efu = Expression.Lambda<Func<int?,int?,int>> (add, p1, p2);
+               }
        }
 }