Skip to content

Commit

Permalink
Convert invalid C# uses of UnmanagedCallersOnly to IL. (dotnet#42146)
Browse files Browse the repository at this point in the history
* Convert invalid C# uses of UnmanagedCallersOnly to IL for negative testing.
  • Loading branch information
AaronRobinsonMSFT authored and ViktorHofer committed Sep 15, 2020
1 parent 22b4796 commit a7535bf
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 32 deletions.
8 changes: 8 additions & 0 deletions src/tests/Interop/UnmanagedCallersOnly/InvalidCSharp.ilproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk.IL">
<PropertyGroup>
<OutputType>library</OutputType>
</PropertyGroup>
<ItemGroup>
<Compile Include="InvalidCallbacks.il" />
</ItemGroup>
</Project>
75 changes: 75 additions & 0 deletions src/tests/Interop/UnmanagedCallersOnly/InvalidCallbacks.il
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

.assembly extern System.Runtime { }
.assembly extern System.Runtime.InteropServices { }

.assembly InvalidCSharp { }

.class public auto ansi beforefieldinit InvalidCSharp.GenericClass`1<T>
extends System.Object
{
.method public hidebysig static
void CallbackMethod (
int32 n
) cil managed preservesig
{
.custom instance void [System.Runtime.InteropServices]System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = (
01 00 00 00
)
.maxstack 8
IL_0000: ldstr "Functions with attribute UnmanagedCallersOnlyAttribute within a generic type are invalid"
IL_0005: newobj instance void [System.Runtime]System.Exception::.ctor(string)
IL_000a: throw
}

.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [System.Runtime]System.Object::.ctor()
IL_0006: ret
}
}

.class public auto ansi beforefieldinit InvalidCSharp.Callbacks
extends [System.Runtime]System.Object
{
.method public hidebysig static
int32 CallbackMethodGeneric<T> (
!!T arg
) cil managed preservesig
{
.custom instance void [System.Runtime.InteropServices]System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = (
01 00 00 00
)
.maxstack 8
IL_0000: ldstr "Functions with attribute UnmanagedCallersOnlyAttribute cannot have generic arguments"
IL_0005: newobj instance void [System.Runtime]System.Exception::.ctor(string)
IL_000a: throw
}

.method public hidebysig
instance int32 CallbackNonStatic (
int32 val
) cil managed preservesig
{
.custom instance void [System.Runtime.InteropServices]System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = (
01 00 00 00
)
.maxstack 8
IL_0000: ldstr "Instance functions with attribute UnmanagedCallersOnlyAttribute are invalid"
IL_0005: newobj instance void [System.Runtime]System.Exception::.ctor(string)
IL_000a: throw
}

.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [System.Runtime]System.Object::.ctor()
IL_0006: ret
}
}
54 changes: 23 additions & 31 deletions src/tests/Interop/UnmanagedCallersOnly/UnmanagedCallersOnlyTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using TestLibrary;
Expand Down Expand Up @@ -35,6 +35,20 @@ public static class UnmanagedCallersOnlyDll
public static extern int PInvokeMarkedWithUnmanagedCallersOnly(int n);
}

private const string InvalidCSharpAssemblyName = "InvalidCSharp";

public static Type GetCallbacksType()
{
var asm = Assembly.Load(InvalidCSharpAssemblyName);
return asm.GetType("InvalidCSharp.Callbacks");
}

public static Type GetGenericClassOfIntType()
{
var asm = Assembly.Load(InvalidCSharpAssemblyName);
return asm.GetType("InvalidCSharp.GenericClass`1").MakeGenericType(typeof(int));
}

private delegate int IntNativeMethodInvoker();
private delegate void NativeMethodInvoker();

Expand Down Expand Up @@ -338,12 +352,6 @@ void CallAsDelegate()
}
}

[UnmanagedCallersOnly]
public int CallbackNonStatic(int val)
{
Assert.Fail($"Instance functions with attribute {nameof(UnmanagedCallersOnlyAttribute)} are invalid");
return -1;
}

public static void NegativeTest_NonStaticMethod()
{
Expand All @@ -354,7 +362,7 @@ void TestUnmanagedCallersOnlyNonStatic()
{
.locals init ([0] native int ptr)
nop
ldftn int CallbackNonStatic(int)
ldftn int GetCallbacksType().CallbackNonStatic(int)
stloc.0
ldloc.0
Expand All @@ -371,7 +379,7 @@ .locals init ([0] native int ptr)
il.Emit(OpCodes.Nop);

// Get native function pointer of the callback
il.Emit(OpCodes.Ldftn, typeof(Program).GetMethod(nameof(CallbackNonStatic)));
il.Emit(OpCodes.Ldftn, GetCallbacksType().GetMethod("CallbackNonStatic"));
il.Emit(OpCodes.Stloc_0);
il.Emit(OpCodes.Ldloc_0);

Expand Down Expand Up @@ -436,13 +444,6 @@ .locals init ([0] native int ptr)
Assert.Throws<InvalidProgramException>(() => { testNativeMethod(); });
}

[UnmanagedCallersOnly]
public static int CallbackMethodGeneric<T>(T arg)
{
Assert.Fail($"Functions with attribute {nameof(UnmanagedCallersOnlyAttribute)} cannot have generic arguments");
return -1;
}

public static void NegativeTest_NonInstantiatedGenericArguments()
{
Console.WriteLine($"Running {nameof(NegativeTest_NonInstantiatedGenericArguments)}...");
Expand All @@ -452,7 +453,7 @@ void TestUnmanagedCallersOnlyNonInstGenericArguments()
{
.locals init ([0] native int ptr)
IL_0000: nop
IL_0001: ldftn void CallbackMethodGeneric(T)
IL_0001: ldftn void InvalidCSharp.Callbacks.CallbackMethodGeneric(T)
IL_0007: stloc.0
IL_0008: ret
}
Expand All @@ -463,7 +464,7 @@ .locals init ([0] native int ptr)
il.Emit(OpCodes.Nop);

// Get native function pointer of the callback
il.Emit(OpCodes.Ldftn, typeof(Program).GetMethod(nameof(CallbackMethodGeneric)));
il.Emit(OpCodes.Ldftn, GetCallbacksType().GetMethod("CallbackMethodGeneric"));
il.Emit(OpCodes.Stloc_0);

il.Emit(OpCodes.Ret);
Expand All @@ -482,7 +483,7 @@ void TestUnmanagedCallersOnlyInstGenericArguments()
{
.locals init ([0] native int ptr)
nop
ldftn void CallbackMethodGeneric(int)
ldftn void InvalidCSharp.Callbacks.CallbackMethodGeneric(int)
stloc.0
ldloc.0
Expand All @@ -499,7 +500,7 @@ .locals init ([0] native int ptr)
il.Emit(OpCodes.Nop);

// Get native function pointer of the instantiated generic callback
il.Emit(OpCodes.Ldftn, typeof(Program).GetMethod(nameof(CallbackMethodGeneric)).MakeGenericMethod(new [] { typeof(int) }));
il.Emit(OpCodes.Ldftn, GetCallbacksType().GetMethod("CallbackMethodGeneric").MakeGenericMethod(new [] { typeof(int) }));
il.Emit(OpCodes.Stloc_0);
il.Emit(OpCodes.Ldloc_0);

Expand All @@ -515,15 +516,6 @@ .locals init ([0] native int ptr)
Assert.Throws<InvalidProgramException>(() => { testNativeMethod(); });
}

public class GenericClass<T>
{
[UnmanagedCallersOnly]
public static void CallbackMethod(int n)
{
Assert.Fail($"Functions with attribute {nameof(UnmanagedCallersOnlyAttribute)} within a generic type are invalid");
}
}

public static void NegativeTest_FromInstantiatedGenericClass()
{
Console.WriteLine($"Running {nameof(NegativeTest_FromInstantiatedGenericClass)}...");
Expand All @@ -533,7 +525,7 @@ void TestUnmanagedCallersOnlyInstGenericType()
{
.locals init ([0] native int ptr)
nop
ldftn int GenericClass<int>::CallbackMethod(int)
ldftn int InvalidCSharp.GenericClass<int>::CallbackMethod(int)
stloc.0
ldloc.0
Expand All @@ -550,7 +542,7 @@ .locals init ([0] native int ptr)
il.Emit(OpCodes.Nop);

// Get native function pointer of the callback from the instantiated generic class.
il.Emit(OpCodes.Ldftn, typeof(GenericClass<int>).GetMethod(nameof(GenericClass<int>.CallbackMethod)));
il.Emit(OpCodes.Ldftn, GetGenericClassOfIntType().GetMethod("CallbackMethod"));
il.Emit(OpCodes.Stloc_0);
il.Emit(OpCodes.Ldloc_0);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<CLRTestPriority>1</CLRTestPriority>
</PropertyGroup>
<ItemGroup>
<Compile Include="UnmanagedCallersOnlyTest.cs" />
Expand All @@ -10,5 +9,6 @@
<!-- This is needed to make sure native binary gets installed in the right location -->
<ProjectReference Include="CMakeLists.txt" />
<ProjectReference Include="$(TestSourceDir)Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
<ProjectReference Include="InvalidCSharp.ilproj" />
</ItemGroup>
</Project>

0 comments on commit a7535bf

Please sign in to comment.