[corlib] Fix Delegate::DynamicInvoke for open delegates of instance methods and close...
authorMarek Safar <marek.safar@gmail.com>
Fri, 4 Jul 2014 08:14:56 +0000 (10:14 +0200)
committerMarek Safar <marek.safar@gmail.com>
Fri, 4 Jul 2014 10:24:16 +0000 (12:24 +0200)
mcs/class/corlib/System/Delegate.cs
mcs/class/corlib/Test/System/DelegateTest.cs

index 507153c421aa2432b48f47c7eddacc499c4639ba..8e13047ef304802ad72a0c58e0fded72d92d7a34 100644 (file)
@@ -5,12 +5,11 @@
 //   Miguel de Icaza (miguel@ximian.com)
 //   Daniel Stodden (stodden@in.tum.de)
 //   Dietmar Maurer (dietmar@ximian.com)
+//   Marek Safar (marek.safar@gmail.com)
 //
 // (C) Ximian, Inc.  http://www.ximian.com
-//
-
-//
 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+// Copyright 2014 Xamarin, Inc (http://www.xamarin.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -41,7 +40,10 @@ using System.Runtime.InteropServices;
 namespace System
 {
        /* Contains the rarely used fields of Delegate */
-       class DelegateData {
+       sealed class DelegateData
+       {
+               public static readonly DelegateData ClosedDelegateForStaticMethod = new DelegateData ();
+
                public Type target_type;
                public string method_name;
        }
@@ -230,6 +232,8 @@ namespace System
                                        return null;
 
                        bool argsMatch;
+                       DelegateData delegate_data = null;
+
                        if (target != null) {
                                if (!method.IsStatic) {
                                        argsMatch = arg_type_match_this (target.GetType (), method.DeclaringType, true);
@@ -259,6 +263,8 @@ namespace System
                                                argsMatch = !(args [0].ParameterType.IsValueType || args [0].ParameterType.IsByRef) && allowClosed;
                                                for (int i = 0; i < delargs.Length; i++)
                                                        argsMatch &= arg_type_match (delargs [i].ParameterType, args [i + 1].ParameterType);
+
+                                               delegate_data = DelegateData.ClosedDelegateForStaticMethod;
                                        } else {
                                                argsMatch = true;
                                                for (int i = 0; i < args.Length; i++)
@@ -276,6 +282,8 @@ namespace System
                        Delegate d = CreateDelegate_internal (type, target, method, throwOnBindFailure);
                        if (d != null)
                                d.original_method_info = method;
+                       if (delegate_data != null)
+                               d.data = delegate_data;
                        return d;
                }
 
@@ -408,20 +416,31 @@ namespace System
                                method_info = m_target.GetType ().GetMethod (data.method_name, mtypes);
                        }
 
-                       if (Method.IsStatic && (args != null ? args.Length : 0) == Method.GetParametersCount () - 1) {
+                       var target = m_target;
+                       if (Method.IsStatic) {
+                               //
                                // The delegate is bound to m_target
-                               if (args != null) {
-                                       object[] newArgs = new object [args.Length + 1];
-                                       args.CopyTo (newArgs, 1);
-                                       newArgs [0] = m_target;
-                                       args = newArgs;
-                               } else {
-                                       args = new object [] { m_target };
+                               //
+                               if (data == DelegateData.ClosedDelegateForStaticMethod) {
+                                       if (args == null) {
+                                               args = new [] { target };
+                                       } else {
+                                               Array.Resize (ref args, args.Length + 1);
+                                               Array.Copy (args, 0, args, 1, args.Length - 1);
+                                               args [0] = target;
+                                       }
+
+                                       target = null;
+                               }
+                       } else {
+                               if (m_target == null && args != null && args.Length > 0) {
+                                       target = args [0];
+                                       Array.Copy (args, 1, args, 0, args.Length - 1);
+                                       Array.Resize (ref args, args.Length - 1);
                                }
-                               return Method.Invoke (null, args);
                        }
 
-                       return Method.Invoke (m_target, args);
+                       return Method.Invoke (target, args);
                }
 
                public virtual object Clone ()
index ddfc099b73d747a675834ebb8a185976ad16a201..54b7ec13fa5f4ec340e821dc96d23cf95e013606 100644 (file)
@@ -1116,20 +1116,58 @@ namespace MonoTests.System
                                typeof (Action),
                                this.GetType ().GetMethod ("Banga"));
                }
-#if !MONOTOUCH
+
                [Test] // #664205
                public void DynamicInvokeNullTarget ()
                {
-                       var method = new DynamicMethod ("test", typeof (int), new [] { typeof (object) }, true);
-                       var il = method.GetILGenerator ();
-                       il.Emit (OpCodes.Ldc_I4, 42);
-                       il.Emit (OpCodes.Ret);
+                       var d1 = Delegate.CreateDelegate (typeof(Func<int>), null, typeof(DelegateTest).GetMethod ("DynamicInvokeClosedStaticDelegate_CB"));
+                       Assert.AreEqual (4, d1.DynamicInvoke ());
+               }
+
+               public static int DynamicInvokeClosedStaticDelegate_CB (object instance)
+               {
+                       Assert.IsNull (instance);
+                       return 4;
+               }
 
-                       var @delegate = method.CreateDelegate (typeof (Func<int>), null);
+               [Test]
+               public void DynamicInvokeOpenInstanceDelegate ()
+               {
+                       var d1 = Delegate.CreateDelegate (typeof (Func<DelegateTest, int>), typeof(DelegateTest).GetMethod ("DynamicInvokeOpenInstanceDelegate_CB"));
+                       Assert.AreEqual (5, d1.DynamicInvoke (new DelegateTest ()), "#1");
 
-                       Assert.AreEqual (42, (int) @delegate.DynamicInvoke ());
+                       var d3 = (Func<DelegateTest, int>) d1;
+                       Assert.AreEqual (5, d3 (null), "#2");
                }
-#endif
+
+               public int DynamicInvokeOpenInstanceDelegate_CB ()
+               {
+                       return 5;
+               }
+
+               [Test]
+               public void DynamicInvoke_InvalidArguments ()
+               {
+                       Delegate d = new Func<int, int> (TestMethod);
+
+                       try {
+                               d.DynamicInvoke (null);
+                               Assert.Fail ("#1");
+                       } catch (TargetParameterCountException) {
+                       }
+
+                       try {
+                               d.DynamicInvoke (new object [0]);
+                               Assert.Fail ("#2");
+                       } catch (TargetParameterCountException) {
+                       }
+               }
+
+               public static int TestMethod (int i)
+               {
+                       throw new NotSupportedException ();
+               }
+
 #endif
                public static void CreateDelegateOfStaticMethodBoundToNull_Helper (object[] args) {}
 
@@ -1329,7 +1367,7 @@ namespace MonoTests.System
                        } catch (ArgumentException) {}
                }
 
-        private static Func<Int32, Int32, bool> Int32D = (x, y) => (x & y) == y;
+               private static Func<Int32, Int32, bool> Int32D = (x, y) => (x & y) == y;
 
                [Test]
                public void EnumBaseTypeConversion () {