Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit f5aa41c

Browse files
petarpetrovtjkotas
authored andcommitted
Fix calling convention gap in ILGenerator.EmitCalli (#27429)
* Expose EmitCalli overload * Add EmitCalli overload test * Improve TestEmitCalliStdCall test method * Add EmitCalliStdCall test for dynamic ilgenerator * Add EmitCalliCdecl tests * Convert EmitCalli tests from theory to fact * Improve EmitCalli tests * Fix test name
1 parent e57157a commit f5aa41c

File tree

4 files changed

+136
-0
lines changed

4 files changed

+136
-0
lines changed

src/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public virtual void Emit(System.Reflection.Emit.OpCode opcode, string str) { }
4848
public virtual void Emit(System.Reflection.Emit.OpCode opcode, System.Type cls) { }
4949
public virtual void EmitCall(System.Reflection.Emit.OpCode opcode, System.Reflection.MethodInfo methodInfo, System.Type[] optionalParameterTypes) { }
5050
public virtual void EmitCalli(System.Reflection.Emit.OpCode opcode, System.Reflection.CallingConventions callingConvention, System.Type returnType, System.Type[] parameterTypes, System.Type[] optionalParameterTypes) { }
51+
public virtual void EmitCalli(System.Reflection.Emit.OpCode opcode, System.Runtime.InteropServices.CallingConvention unmanagedCallConv, Type returnType, Type[] parameterTypes) { }
5152
public virtual void EmitWriteLine(System.Reflection.Emit.LocalBuilder localBuilder) { }
5253
public virtual void EmitWriteLine(System.Reflection.FieldInfo fld) { }
5354
public virtual void EmitWriteLine(string value) { }

src/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
</ItemGroup>
1414
<ItemGroup>
1515
<ProjectReference Include="..\..\System.Runtime\ref\System.Runtime.csproj" />
16+
<ProjectReference Include="..\..\System.Runtime.InteropServices\ref\System.Runtime.InteropServices.csproj" />
1617
<ProjectReference Include="..\..\System.Reflection\ref\System.Reflection.csproj" />
1718
<ProjectReference Include="..\..\System.Reflection.Primitives\ref\System.Reflection.Primitives.csproj" />
1819
</ItemGroup>
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Linq;
6+
using System.Runtime.InteropServices;
7+
using Xunit;
8+
9+
namespace System.Reflection.Emit.Tests
10+
{
11+
public class ILGeneratorEmit4
12+
{
13+
[Fact]
14+
public void TestEmitCalliBlittable()
15+
{
16+
int a = 1, b = 1, result = 2;
17+
18+
ModuleBuilder moduleBuilder = Helpers.DynamicModule();
19+
TypeBuilder typeBuilder = moduleBuilder.DefineType("T", TypeAttributes.Public);
20+
Type returnType = typeof(int);
21+
22+
MethodBuilder methodBuilder = typeBuilder.DefineMethod("F",
23+
MethodAttributes.Public | MethodAttributes.Static, returnType, new Type[] { typeof(IntPtr), typeof(int), typeof(int) });
24+
methodBuilder.SetImplementationFlags(MethodImplAttributes.NoInlining);
25+
26+
ILGenerator il = methodBuilder.GetILGenerator();
27+
il.Emit(OpCodes.Ldarg_1);
28+
il.Emit(OpCodes.Ldarg_2);
29+
il.Emit(OpCodes.Ldarg_0);
30+
il.EmitCalli(OpCodes.Calli, CallingConvention.StdCall, returnType, new Type[] { typeof(int), typeof(int) });
31+
il.Emit(OpCodes.Ret);
32+
33+
Type dynamicType = typeBuilder.CreateType();
34+
IntPtr funcPtr = Marshal.GetFunctionPointerForDelegate(new Int32SumStdCall(Int32Sum));
35+
36+
object resultValue = dynamicType
37+
.GetMethod("F", BindingFlags.Public | BindingFlags.Static)
38+
.Invoke(null, new object[] { funcPtr, a, b });
39+
40+
Assert.IsType(returnType, resultValue);
41+
Assert.Equal(result, resultValue);
42+
}
43+
44+
[Fact]
45+
public void TestDynamicMethodEmitCalliBlittable()
46+
{
47+
int a = 1, b = 1, result = 2;
48+
49+
Type returnType = typeof(int);
50+
51+
var dynamicMethod = new DynamicMethod("F", returnType, new Type[] { typeof(IntPtr), typeof(int), typeof(int) });
52+
53+
ILGenerator il = dynamicMethod.GetILGenerator();
54+
il.Emit(OpCodes.Ldarg_1);
55+
il.Emit(OpCodes.Ldarg_2);
56+
il.Emit(OpCodes.Ldarg_0);
57+
il.EmitCalli(OpCodes.Calli, CallingConvention.StdCall, returnType, new Type[] { typeof(int), typeof(int) });
58+
il.Emit(OpCodes.Ret);
59+
60+
IntPtr funcPtr = Marshal.GetFunctionPointerForDelegate(new Int32SumStdCall(Int32Sum));
61+
62+
object resultValue = dynamicMethod
63+
.Invoke(null, new object[] { funcPtr, a, b });
64+
65+
Assert.IsType(returnType, resultValue);
66+
Assert.Equal(result, resultValue);
67+
}
68+
69+
[Fact]
70+
public void TestEmitCalliNonBlittable()
71+
{
72+
string input = "Test string!", result = "!gnirts tseT";
73+
74+
ModuleBuilder moduleBuilder = Helpers.DynamicModule();
75+
TypeBuilder typeBuilder = moduleBuilder.DefineType("T", TypeAttributes.Public);
76+
Type returnType = typeof(string);
77+
78+
MethodBuilder methodBuilder = typeBuilder.DefineMethod("F",
79+
MethodAttributes.Public | MethodAttributes.Static, returnType, new Type[] { typeof(IntPtr), typeof(string) });
80+
methodBuilder.SetImplementationFlags(MethodImplAttributes.NoInlining);
81+
82+
ILGenerator il = methodBuilder.GetILGenerator();
83+
il.Emit(OpCodes.Ldarg_1);
84+
il.Emit(OpCodes.Ldarg_0);
85+
il.EmitCalli(OpCodes.Calli, CallingConvention.Cdecl, returnType, new Type[] { typeof(string) });
86+
il.Emit(OpCodes.Ret);
87+
88+
Type dynamicType = typeBuilder.CreateType();
89+
IntPtr funcPtr = Marshal.GetFunctionPointerForDelegate(new StringReverseCdecl(StringReverse));
90+
91+
object resultValue = dynamicType
92+
.GetMethod("F", BindingFlags.Public | BindingFlags.Static)
93+
.Invoke(null, new object[] { funcPtr, input });
94+
95+
Assert.IsType(returnType, resultValue);
96+
Assert.Equal(result, resultValue);
97+
}
98+
99+
[Fact]
100+
public void TestDynamicMethodEmitCalliNonBlittable()
101+
{
102+
string input = "Test string!", result = "!gnirts tseT";
103+
104+
Type returnType = typeof(string);
105+
106+
var dynamicMethod = new DynamicMethod("F", returnType, new Type[] { typeof(IntPtr), typeof(string) });
107+
108+
ILGenerator il = dynamicMethod.GetILGenerator();
109+
il.Emit(OpCodes.Ldarg_1);
110+
il.Emit(OpCodes.Ldarg_0);
111+
il.EmitCalli(OpCodes.Calli, CallingConvention.Cdecl, returnType, new Type[] { typeof(string) });
112+
il.Emit(OpCodes.Ret);
113+
114+
IntPtr funcPtr = Marshal.GetFunctionPointerForDelegate(new StringReverseCdecl(StringReverse));
115+
116+
object resultValue = dynamicMethod
117+
.Invoke(null, new object[] { funcPtr, input });
118+
119+
Assert.IsType(returnType, resultValue);
120+
Assert.Equal(result, resultValue);
121+
}
122+
123+
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
124+
private delegate int Int32SumStdCall(int a, int b);
125+
126+
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
127+
private delegate string StringReverseCdecl(string a);
128+
129+
private static int Int32Sum(int a, int b) => a + b;
130+
131+
private static string StringReverse(string a) => string.Join("", a.Reverse());
132+
}
133+
}

src/System.Reflection.Emit.ILGeneration/tests/System.Reflection.Emit.ILGeneration.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<Compile Include="ILGenerator\Emit1Tests.cs" />
1313
<Compile Include="ILGenerator\Emit2Tests.cs" />
1414
<Compile Include="ILGenerator\Emit3Tests.cs" />
15+
<Compile Include="ILGenerator\Emit4Tests.cs" />
1516
<Compile Include="ILGenerator\EmitWriteLineTests.cs" />
1617
<Compile Include="ILGenerator\ExceptionEmitTests.cs" />
1718
<Compile Include="ILGenerator\ILOffsetTests.cs" />

0 commit comments

Comments
 (0)