Update NativeCallable attribute tests (dotnet/coreclr#18946)
authorAaron Robinson <arobins@microsoft.com>
Tue, 17 Jul 2018 18:12:02 +0000 (11:12 -0700)
committerGitHub <noreply@github.com>
Tue, 17 Jul 2018 18:12:02 +0000 (11:12 -0700)
* Update NativeCallable attribute tests

Commit migrated from https://github.com/dotnet/coreclr/commit/f69989e353d11a906bdbc4989b901fe82d7ca0bc

src/coreclr/tests/src/Interop/NativeCallable/NativeCallableDll.cpp
src/coreclr/tests/src/Interop/NativeCallable/NativeCallableTest.cs
src/coreclr/tests/src/Interop/NativeCallable/NativeCallableTest.csproj

index ea82352..b9fa381 100644 (file)
@@ -4,9 +4,9 @@
 
 #include <xplatform.h> 
 
-typedef int (NATIVEAPI *CALLBACKADDPROC)(int n);
+typedef int (NATIVEAPI *CALLBACKPROC)(int n);
 
-extern "C" DLL_EXPORT int NATIVEAPI CallManagedAdd(CALLBACKADDPROC pCallbackAddProc, int n)
+extern "C" DLL_EXPORT int NATIVEAPI CallManagedProc(CALLBACKPROC pCallbackProc, int n)
 {
-    return pCallbackAddProc(n);
+    return pCallbackProc(n);
 }
index c0b057b..5e18a39 100644 (file)
@@ -9,6 +9,7 @@ using System.Reflection;
 using System.Reflection.Emit;
 using System.Runtime.InteropServices;
 using System.Threading;
+using CoreFXTestLibrary;
 
 using Console = Internal.Console;
 
@@ -17,148 +18,240 @@ public class Program
     public static class NativeMethods
     {
         [DllImport("NativeCallableDll")]
-        public static extern int CallManagedAdd(IntPtr callbackProc, int n);
+        public static extern int CallManagedProc(IntPtr callbackProc, int n);
     }
 
-    private delegate int IntNativeMethodInvoker();    
+    private delegate int IntNativeMethodInvoker();
     private delegate void NativeMethodInvoker();
 
-    public static int Main()
+    public static int Main(string[] args)
     {
-        int ret;
-        //NegativeTest_NonBlittable();
-        ret = TestNativeCallableValid();
-        if (ret != 100)
-            return ret;
-        //NegativeTest_ViaDelegate();
-        //NegativeTest_ViaLdftn();
+        try
+        {
+            TestNativeCallableValid();
+            NegativeTest_ViaDelegate();
+            NegativeTest_NonBlittable();
+            NegativeTest_GenericArguments();
+
+            if (args.Length != 0 && args[0].Equals("calli"))
+            {
+                NegativeTest_ViaCalli();
+            }
+        }
+        catch (Exception e)
+        {
+            Console.WriteLine($"Test Failure: {e}");
+            return 101;
+        }
+
         return 100;
     }
 
-    public static int TestNativeCallableValid()
+    [NativeCallable]
+    public static int ManagedDoubleCallback(int n)
+    {
+        return DoubleImpl(n);
+    }
+
+    private static int DoubleImpl(int n)
     {
+        return 2 * n;
+    }
+
+    public static void TestNativeCallableValid()
+    {
+        Console.WriteLine($"{nameof(NativeCallableAttribute)} function");
+
         /*
            void TestNativeCallable()
            {
-                   .locals init ([0] native int ptr)
-                   IL_0000:  nop  
-                   IL_0002:  ldftn      int32 CallbackMethod(int32)
-
-                   IL_0012:  stloc.0
-                   IL_0013:  ldloc.0
-                   IL_0014:  ldc.i4     100
-                   IL_0019:  call       bool NativeMethods::CallNativeAdd(native int, int)
-                   IL_001e:  pop
-                   IL_001f:  ret
+                .locals init ([0] native int ptr)
+                IL_0000:  nop
+                IL_0001:  ldftn      int32 ManagedDoubleCallback(int32)
+                IL_0007:  stloc.0
+
+                IL_0008:  ldloc.0
+                IL_0009:  ldc.i4     <n> local
+                IL_000e:  call       bool NativeMethods::CallManagedProc(native int, int)
+
+                IL_0013:  ret
              }
-           */
+        */
         DynamicMethod testNativeCallable = new DynamicMethod("TestNativeCallable", typeof(int), null, typeof(Program).Module);
         ILGenerator il = testNativeCallable.GetILGenerator();
         il.DeclareLocal(typeof(IntPtr));
         il.Emit(OpCodes.Nop);
 
         // Get native function pointer of the callback
-        il.Emit(OpCodes.Ldftn, typeof(Program).GetMethod("ManagedAddCallback"));
+        il.Emit(OpCodes.Ldftn, typeof(Program).GetMethod(nameof(ManagedDoubleCallback)));
         il.Emit(OpCodes.Stloc_0);
         il.Emit(OpCodes.Ldloc_0);
 
-        // return 111+100
-        il.Emit(OpCodes.Ldc_I4, 111);
-        il.Emit(OpCodes.Call, typeof(NativeMethods).GetMethod("CallManagedAdd"));
+        int n = 12345;
+        il.Emit(OpCodes.Ldc_I4, n);
+        il.Emit(OpCodes.Call, typeof(NativeMethods).GetMethod("CallManagedProc"));
         il.Emit(OpCodes.Ret);
-        IntNativeMethodInvoker testNativeMethod = (IntNativeMethodInvoker)testNativeCallable.CreateDelegate(typeof(IntNativeMethodInvoker));
-        if (testNativeMethod() != 211)
-            return 0;
-        return 100;
+        var testNativeMethod = (IntNativeMethodInvoker)testNativeCallable.CreateDelegate(typeof(IntNativeMethodInvoker));
+
+        int expected = DoubleImpl(n);
+        Assert.AreEqual(expected, testNativeMethod());
     }
 
     public static void NegativeTest_ViaDelegate()
     {
-        // Try invoking method directly 
+        Console.WriteLine($"{nameof(NativeCallableAttribute)} function as delegate");
+
+        // Try invoking method directly
         try
         {
-            Func<int, int> invoker = ManagedAddCallback;
-            invoker(0);
+            CallAsDelegate();
+            Assert.Fail($"Invalid to call {nameof(ManagedDoubleCallback)} as delegate");
         }
-        catch (Exception)
+        catch (NotSupportedException)
         {
+        }
 
+        // Local function to delay exception thrown during JIT
+        void CallAsDelegate()
+        {
+            Func<int, int> invoker = ManagedDoubleCallback;
+            invoker(0);
         }
     }
 
+    [NativeCallable]
+    public static int CallbackMethodNonBlittable(bool x1)
+    {
+        Assert.Fail($"Functions with attribute {nameof(NativeCallableAttribute)} cannot have non-blittable arguments");
+        return -1;
+    }
+
     public static void NegativeTest_NonBlittable()
     {
-        // Try invoking method directly 
+        Console.WriteLine($"{nameof(NativeCallableAttribute)} function with non-blittable arguments");
+
+        /*
+           void TestNativeCallableNonBlittable()
+           {
+                .locals init ([0] native int ptr)
+                IL_0000:  nop
+                IL_0001:  ldftn      int32 CallbackMethodNonBlittable(bool)
+                IL_0007:  stloc.0
+                IL_0008:  ret
+             }
+        */
+        DynamicMethod testNativeCallable = new DynamicMethod("TestNativeCallableNonBlittable", null, null, typeof(Program).Module);
+        ILGenerator il = testNativeCallable.GetILGenerator();
+        il.DeclareLocal(typeof(IntPtr));
+        il.Emit(OpCodes.Nop);
+
+        // Get native function pointer of the callback
+        il.Emit(OpCodes.Ldftn, typeof(Program).GetMethod(nameof(CallbackMethodNonBlittable)));
+        il.Emit(OpCodes.Stloc_0);
+
+        il.Emit(OpCodes.Ret);
+        var testNativeMethod = (NativeMethodInvoker)testNativeCallable.CreateDelegate(typeof(NativeMethodInvoker));
+
+        // Try invoking method
         try
         {
-            Func<bool, int> invoker = CallbackMethodNonBlitabble;
-            invoker(true);
+            testNativeMethod();
+            Assert.Fail($"Function {nameof(CallbackMethodNonBlittable)} has non-blittable types");
         }
-        catch (Exception)
+        catch (NotSupportedException)
         {
-            Console.WriteLine(":bla");
         }
     }
 
+    [NativeCallable]
+    public static int CallbackMethodGeneric<T>(T arg)
+    {
+        Assert.Fail($"Functions with attribute {nameof(NativeCallableAttribute)} cannot have generic arguments");
+        return -1;
+    }
 
-    public static void NegativeTest_ViaLdftn()
+    public static void NegativeTest_GenericArguments()
     {
         /*
-           .locals init (native int V_0)
-           IL_0000:  nop
-           IL_0001:  ldftn      void ConsoleApplication1.Program::callback(int32)
-           IL_0007:  stloc.0
-           IL_0008:  ldc.i4.s   12
-           IL_000a:  ldloc.0
-           IL_000b:  calli      void(int32)
-           IL_0010:  nop
-           IL_0011:  ret
-       */
-        DynamicMethod testNativeCallable = new DynamicMethod("TestNativeCallableLdftn", null, null, typeof(Program).Module);
+           void TestNativeCallableGenericArguments()
+           {
+                .locals init ([0] native int ptr)
+                IL_0000:  nop
+                IL_0001:  ldftn      int32 CallbackMethodGeneric(T)
+                IL_0007:  stloc.0
+                IL_0008:  ret
+             }
+        */
+        DynamicMethod testNativeCallable = new DynamicMethod("TestNativeCallableGenericArguments", null, null, typeof(Program).Module);
         ILGenerator il = testNativeCallable.GetILGenerator();
         il.DeclareLocal(typeof(IntPtr));
         il.Emit(OpCodes.Nop);
-        il.Emit(OpCodes.Ldftn, typeof(Program).GetMethod("LdftnCallback"));
-        il.Emit(OpCodes.Stloc_0);
-        il.Emit(OpCodes.Ldc_I4,12);
-        il.Emit(OpCodes.Ldloc_0);
 
-        SignatureHelper sig =  SignatureHelper.GetMethodSigHelper(typeof(Program).Module, null, new Type[] { typeof(int) });
-        sig.AddArgument(typeof(int));
+        // Get native function pointer of the callback
+        il.Emit(OpCodes.Ldftn, typeof(Program).GetMethod(nameof(CallbackMethodGeneric)));
+        il.Emit(OpCodes.Stloc_0);
 
-        // il.EmitCalli is not available  and the below is not correct
-        il.Emit(OpCodes.Calli,sig);
-        il.Emit(OpCodes.Nop);
         il.Emit(OpCodes.Ret);
+        var testNativeMethod = (NativeMethodInvoker)testNativeCallable.CreateDelegate(typeof(NativeMethodInvoker));
 
-        NativeMethodInvoker testNativeMethod = (NativeMethodInvoker)testNativeCallable.CreateDelegate(typeof(NativeMethodInvoker));
-        testNativeMethod();
-
+        // Try invoking method
+        try
+        {
+            testNativeMethod();
+            Assert.Fail($"Function {nameof(CallbackMethodGeneric)} has generic types");
+        }
+        catch (InvalidProgramException)
+        {
+        }
     }
 
-    #region callbacks
     [NativeCallable]
-    public static void LdftnCallback(int val)
+    public static void CallbackViaCalli(int val)
     {
+        Assert.Fail($"Functions with attribute {nameof(NativeCallableAttribute)} cannot be called via calli");
     }
 
-    [NativeCallable]
-    public static int ManagedAddCallback(int n)
+    public static void NegativeTest_ViaCalli()
     {
-        return n + 100;
-    }
+        Console.WriteLine($"{nameof(NativeCallableAttribute)} function via calli instruction. The CLR _will_ crash.");
 
-    [NativeCallable]
-    public static int CallbackMethodGeneric<T>(IntPtr hWnd, IntPtr lParam)
-    {
-        return 1;
-    }
+        /*
+           void TestNativeCallableViaCalli()
+           {
+                .locals init (native int V_0)
+                IL_0000:  nop
+                IL_0001:  ldftn      void CallbackViaCalli(int32)
+                IL_0007:  stloc.0
 
-    [NativeCallable]
-    public static int CallbackMethodNonBlitabble(bool x1)
-    {
-        return 1;
-    }
-    #endregion //callbacks
+                IL_0008:  ldc.i4     1234
+                IL_000d:  ldloc.0
+                IL_000e:  calli      void(int32)
 
+                IL_0013:  nop
+                IL_0014:  ret
+           }
+        */
+        DynamicMethod testNativeCallable = new DynamicMethod("TestNativeCallableViaCalli", null, null, typeof(Program).Module);
+        ILGenerator il = testNativeCallable.GetILGenerator();
+        il.DeclareLocal(typeof(IntPtr));
+        il.Emit(OpCodes.Nop);
+
+        // Get native function pointer of the callback
+        il.Emit(OpCodes.Ldftn, typeof(Program).GetMethod(nameof(CallbackViaCalli)));
+        il.Emit(OpCodes.Stloc_0);
+
+        il.Emit(OpCodes.Ldc_I4, 1234);
+        il.Emit(OpCodes.Ldloc_0);
+        il.EmitCalli(OpCodes.Calli, CallingConventions.Standard, null, new Type[] { typeof(int) }, null);
+
+        il.Emit(OpCodes.Nop);
+        il.Emit(OpCodes.Ret);
+
+        NativeMethodInvoker testNativeMethod = (NativeMethodInvoker)testNativeCallable.CreateDelegate(typeof(NativeMethodInvoker));
+
+        // It is not possible to catch the resulting ExecutionEngineException exception.
+        // To observe the crashing behavior set a breakpoint in the ReversePInvokeBadTransition() function
+        // located in src/vm/dllimportcallback.cpp.
+        testNativeMethod();
+    }
 }
index efb6d08..af3e9af 100644 (file)
@@ -27,6 +27,7 @@
   </ItemGroup>
   <ItemGroup>
     <Compile Include="NativeCallableTest.cs" />
+    <Compile Include="..\common\Assertion.cs" />
   </ItemGroup>
   <ItemGroup>
     <!-- This is needed to make sure native binary gets installed in the right location -->