diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILGenerator.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILGenerator.cs
index b782d0ef86e321..d4fa1049fedde8 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILGenerator.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILGenerator.cs
@@ -244,6 +244,18 @@ public override void EmitCalli(OpCode opcode, CallingConvention unmanagedCallCon
PutInteger4(token);
}
+ ///
+ public override void EmitCalli(Type functionPointerType)
+ {
+ ArgumentNullException.ThrowIfNull(functionPointerType);
+
+ if (!functionPointerType.IsFunctionPointer)
+ throw new ArgumentException(SR.Argument_MustBeFunctionPointer, nameof(functionPointerType));
+
+ SignatureHelper sig = SignatureHelper.GetMethodSigHelper(m_scope, functionPointerType);
+ Emit(OpCodes.Calli, sig);
+ }
+
public override void EmitCall(OpCode opcode, MethodInfo methodInfo, Type[]? optionalParameterTypes)
{
ArgumentNullException.ThrowIfNull(methodInfo);
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/SignatureHelper.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/SignatureHelper.cs
index a117d29a3857c3..8a6287f24a5cc7 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/SignatureHelper.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/SignatureHelper.cs
@@ -105,6 +105,51 @@ internal static SignatureHelper GetMethodSigHelper(Module? mod, CallingConventio
return new SignatureHelper(mod, intCall, returnType, null, null);
}
+ internal static SignatureHelper GetMethodSigHelper(DynamicScope scope, Type functionPointerType)
+ {
+ Debug.Assert(functionPointerType.IsFunctionPointer);
+
+ Type retType = functionPointerType.GetFunctionPointerReturnType();
+ Type[] retTypeModReqs = retType.GetRequiredCustomModifiers();
+ Type[] retTypeModOpts = retType.GetOptionalCustomModifiers();
+ Type[] paramTypes = functionPointerType.GetFunctionPointerParameterTypes();
+ Type[][] paramModReqs = new Type[paramTypes.Length][];
+ Type[][] paramModOpts = new Type[paramTypes.Length][];
+
+ retType = retType.UnderlyingSystemType;
+
+ for (int i = 0; i < paramTypes.Length; i++)
+ {
+ paramModReqs[i] = paramTypes[i].GetRequiredCustomModifiers();
+ paramModOpts[i] = paramTypes[i].GetOptionalCustomModifiers();
+ paramTypes[i] = paramTypes[i].UnderlyingSystemType;
+ }
+
+ MdSigCallingConvention callConv = MdSigCallingConvention.Default;
+
+ if (functionPointerType.IsUnmanagedFunctionPointer)
+ {
+ callConv = MdSigCallingConvention.Unmanaged;
+
+ if (functionPointerType.GetFunctionPointerCallingConventions() is { Length: 1 } conventions)
+ {
+ callConv = conventions[0].FullName switch
+ {
+ "System.Runtime.CompilerServices.CallConvCdecl" => MdSigCallingConvention.C,
+ "System.Runtime.CompilerServices.CallConvStdcall" => MdSigCallingConvention.StdCall,
+ "System.Runtime.CompilerServices.CallConvThiscall" => MdSigCallingConvention.ThisCall,
+ "System.Runtime.CompilerServices.CallConvFastcall" => MdSigCallingConvention.FastCall,
+ _ => MdSigCallingConvention.Unmanaged
+ };
+ }
+ }
+
+ SignatureHelper sig = new(null, callConv);
+ sig.AddDynamicArgument(scope, retType, retTypeModReqs, retTypeModOpts, false);
+ sig.AddArguments(scope, paramTypes, paramModReqs, paramModOpts);
+ return sig;
+ }
+
public static SignatureHelper GetLocalVarSigHelper()
{
return GetLocalVarSigHelper(null);
@@ -320,8 +365,8 @@ private void AddOneArgTypeHelper(Type clsArgument, Type[]? requiredCustomModifie
AddOneArgTypeHelper(clsArgument);
}
- private void AddOneArgTypeHelper(Type clsArgument) { AddOneArgTypeHelperWorker(clsArgument, false); }
- private void AddOneArgTypeHelperWorker(Type clsArgument, bool lastWasGenericInst)
+ private void AddOneArgTypeHelper(Type clsArgument, DynamicScope? scope = null) { AddOneArgTypeHelperWorker(clsArgument, false, scope); }
+ private void AddOneArgTypeHelperWorker(Type clsArgument, bool lastWasGenericInst, DynamicScope? scope)
{
if (clsArgument.IsGenericParameter)
{
@@ -336,7 +381,7 @@ private void AddOneArgTypeHelperWorker(Type clsArgument, bool lastWasGenericInst
{
AddElementType(CorElementType.ELEMENT_TYPE_GENERICINST);
- AddOneArgTypeHelperWorker(clsArgument.GetGenericTypeDefinition(), true);
+ AddOneArgTypeHelperWorker(clsArgument.GetGenericTypeDefinition(), true, scope);
Type[] args = clsArgument.GetGenericArguments();
@@ -424,6 +469,25 @@ private void AddOneArgTypeHelperWorker(Type clsArgument, bool lastWasGenericInst
AddData(0);
}
}
+ else if (clsArgument.IsFunctionPointer)
+ {
+ if (scope == null)
+ throw new NotSupportedException(SR.NotSupported_FunctionPointerSignature);
+
+ AddData((int)CorElementType.ELEMENT_TYPE_FNPTR);
+ SignatureHelper sig = GetMethodSigHelper(scope, clsArgument);
+ byte[] bytes = sig.GetSignature();
+
+ if (m_currSig + bytes.Length > m_signature.Length)
+ {
+ m_signature = ExpandArray(m_signature, m_signature.Length + bytes.Length);
+ }
+
+ for (int i = 0; i < bytes.Length; i++)
+ {
+ m_signature[m_currSig++] = bytes[i];
+ }
+ }
else
{
CorElementType type = CorElementType.ELEMENT_TYPE_MAX;
@@ -729,9 +793,10 @@ internal byte[] InternalGetSignatureArray()
return temp;
}
- internal void AddDynamicArgument(DynamicScope dynamicScope, Type clsArgument, Type[]? requiredCustomModifiers, Type[]? optionalCustomModifiers)
+ internal void AddDynamicArgument(DynamicScope dynamicScope, Type clsArgument, Type[]? requiredCustomModifiers, Type[]? optionalCustomModifiers, bool incrementArgCount = true)
{
- IncrementArgCounts();
+ if (incrementArgCount)
+ IncrementArgCounts();
Debug.Assert(clsArgument != null);
@@ -750,6 +815,9 @@ internal void AddDynamicArgument(DynamicScope dynamicScope, Type clsArgument, Ty
if (t.ContainsGenericParameters)
throw new ArgumentException(SR.Argument_GenericsInvalid, nameof(optionalCustomModifiers));
+ if (t.IsFunctionPointer)
+ throw new ArgumentException(SR.Argument_FunctionPointersInvalid, nameof(optionalCustomModifiers));
+
AddElementType(CorElementType.ELEMENT_TYPE_CMOD_OPT);
int token = dynamicScope.GetTokenFor(rtType.TypeHandle);
@@ -773,6 +841,9 @@ internal void AddDynamicArgument(DynamicScope dynamicScope, Type clsArgument, Ty
if (t.ContainsGenericParameters)
throw new ArgumentException(SR.Argument_GenericsInvalid, nameof(requiredCustomModifiers));
+ if (t.IsFunctionPointer)
+ throw new ArgumentException(SR.Argument_FunctionPointersInvalid, nameof(requiredCustomModifiers));
+
AddElementType(CorElementType.ELEMENT_TYPE_CMOD_REQD);
int token = dynamicScope.GetTokenFor(rtType.TypeHandle);
@@ -781,7 +852,18 @@ internal void AddDynamicArgument(DynamicScope dynamicScope, Type clsArgument, Ty
}
}
- AddOneArgTypeHelper(clsArgument);
+ AddOneArgTypeHelper(clsArgument, dynamicScope);
+ }
+
+ internal void AddArguments(DynamicScope dynamicScope, Type[]? arguments, Type[][]? requiredCustomModifiers, Type[][]? optionalCustomModifiers)
+ {
+ if (arguments is null)
+ return;
+
+ for (int i = 0; i < arguments.Length; i++)
+ {
+ AddDynamicArgument(dynamicScope, arguments[i], requiredCustomModifiers?[i], optionalCustomModifiers?[i]);
+ }
}
#endregion
diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
index 4115a1d3c05a4d..2b170e8affabc9 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
@@ -813,6 +813,29 @@ internal RuntimeType MakeByRef()
return type!;
}
+ [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeTypeHandle_MakeFunctionPointer")]
+ private static partial void MakeFunctionPointer(nint* retAndParamTypes, int numArgs, [MarshalAs(UnmanagedType.Bool)] bool isUnmanaged, ObjectHandleOnStack type);
+
+ internal RuntimeType MakeFunctionPointer(Type[] parameterTypes, bool isUnmanaged)
+ {
+ int count = 1 + parameterTypes.Length;
+ nint[] retAndParamTypeHandles = new nint[count];
+
+ retAndParamTypeHandles[0] = GetNativeHandle().Value;
+ for (int i = 0; i < parameterTypes.Length; i++)
+ retAndParamTypeHandles[i + 1] = parameterTypes[i].TypeHandle.Value;
+
+ RuntimeType? type = null;
+ fixed (nint* pRetAndParamTypeHandles = retAndParamTypeHandles)
+ {
+ MakeFunctionPointer(pRetAndParamTypeHandles, parameterTypes.Length, isUnmanaged, ObjectHandleOnStack.Create(ref type));
+ }
+
+ GC.KeepAlive(m_type);
+ GC.KeepAlive(parameterTypes);
+ return type!;
+ }
+
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeTypeHandle_MakePointer")]
private static partial void MakePointer(QCallTypeHandle handle, ObjectHandleOnStack type);
diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs
index 764599e2aae6dc..4fe4d8f0de7361 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs
@@ -3729,6 +3729,24 @@ public override Type MakeArrayType(int rank)
return new RuntimeTypeHandle(this).MakeArray(rank);
}
+ public override Type MakeFunctionPointerType(Type[]? parameterTypes, bool isUnmanaged = false)
+ {
+ parameterTypes = (parameterTypes != null) ? (Type[])parameterTypes.Clone() : [];
+ for (int i = 0; i < parameterTypes.Length; i++)
+ {
+ Type paramType = parameterTypes[i];
+ ArgumentNullException.ThrowIfNull(paramType, nameof(parameterTypes));
+
+ if (paramType is not RuntimeType)
+ return Type.MakeFunctionPointerSignatureType(this, parameterTypes, isUnmanaged);
+
+ if (paramType == typeof(void) || paramType.IsGenericTypeDefinition)
+ throw new ArgumentException(string.Format(SR.FunctionPointer_ParameterInvalid, paramType.ToString()), nameof(parameterTypes));
+ }
+
+ return new RuntimeTypeHandle(this).MakeFunctionPointer(parameterTypes, isUnmanaged);
+ }
+
public override StructLayoutAttribute? StructLayoutAttribute => PseudoCustomAttribute.GetStructLayoutCustomAttribute(this);
#endregion
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/TypeUnifier.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/TypeUnifier.cs
index 7cd2e9588f5125..c75c2007535aef 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/TypeUnifier.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/TypeUnifier.cs
@@ -57,6 +57,11 @@ public static RuntimeTypeInfo GetPointerType(this RuntimeTypeInfo targetType)
return RuntimePointerTypeInfo.GetPointerTypeInfo(targetType);
}
+ public static RuntimeTypeInfo GetFunctionPointerType(this RuntimeTypeInfo returnType, RuntimeTypeInfo[] parameterTypes, bool isUnmanaged)
+ {
+ return RuntimeFunctionPointerTypeInfo.GetFunctionPointerTypeInfo(returnType, parameterTypes, isUnmanaged);
+ }
+
public static RuntimeTypeInfo GetConstructedGenericTypeNoConstraintCheck(this RuntimeTypeInfo genericTypeDefinition, RuntimeTypeInfo[] genericTypeArguments)
{
return RuntimeConstructedGenericTypeInfo.GetRuntimeConstructedGenericTypeInfoNoConstraintCheck(genericTypeDefinition, genericTypeArguments);
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs
index da86ebb4499cf1..8fd54ee8f44c92 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs
@@ -414,6 +414,28 @@ public Type MakeArrayType(int rank)
return this.GetMultiDimArrayType(rank).ToType();
}
+ public Type MakeFunctionPointerType(Type[]? parameterTypes, bool isUnmanaged = false)
+ {
+ parameterTypes ??= [];
+ RuntimeTypeInfo[] runtimeParameterTypes = new RuntimeTypeInfo[parameterTypes.Length];
+
+ for (int i = 0; i < parameterTypes.Length; i++)
+ {
+ Type paramType = parameterTypes[i];
+ ArgumentNullException.ThrowIfNull(paramType, nameof(parameterTypes));
+
+ if (paramType is not RuntimeType rtType)
+ return Type.MakeFunctionPointerSignatureType(this.ToType(), parameterTypes, isUnmanaged);
+
+ if (rtType == typeof(void) || rtType.IsGenericTypeDefinition)
+ throw new ArgumentException(string.Format(SR.FunctionPointer_ParameterInvalid, rtType.ToString()), nameof(parameterTypes));
+
+ runtimeParameterTypes[i] = rtType.GetRuntimeTypeInfo();
+ }
+
+ return this.GetFunctionPointerType(runtimeParameterTypes, isUnmanaged).ToType();
+ }
+
public Type MakePointerType()
{
return this.GetPointerType().ToType();
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.NativeAot.cs
index 0163a849c521cd..a0285f166c0eca 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.NativeAot.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.NativeAot.cs
@@ -856,6 +856,9 @@ public override Type MakeArrayType()
public override Type MakeArrayType(int rank)
=> GetRuntimeTypeInfo().MakeArrayType(rank);
+ public override Type MakeFunctionPointerType(Type[]? parameterTypes, bool isUnmanaged = false)
+ => GetRuntimeTypeInfo().MakeFunctionPointerType(parameterTypes, isUnmanaged);
+
[RequiresDynamicCode("The native code for this instantiation might not be available at runtime.")]
[RequiresUnreferencedCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")]
public override Type MakeGenericType(params Type[] instantiation)
diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp
index 0a03fbce6b82a2..2c1c69675d4df5 100644
--- a/src/coreclr/vm/qcallentrypoints.cpp
+++ b/src/coreclr/vm/qcallentrypoints.cpp
@@ -132,6 +132,7 @@ static const Entry s_QCall[] =
DllImportEntry(RuntimeTypeHandle_MakeByRef)
DllImportEntry(RuntimeTypeHandle_MakeSZArray)
DllImportEntry(RuntimeTypeHandle_MakeArray)
+ DllImportEntry(RuntimeTypeHandle_MakeFunctionPointer)
DllImportEntry(RuntimeTypeHandle_GetConstraints)
DllImportEntry(RuntimeTypeHandle_GetArgumentTypesFromFunctionPointer)
DllImportEntry(RuntimeTypeHandle_GetAssemblySlow)
diff --git a/src/coreclr/vm/runtimehandles.cpp b/src/coreclr/vm/runtimehandles.cpp
index 3cb83744e9e03b..0d9fc007142f2c 100644
--- a/src/coreclr/vm/runtimehandles.cpp
+++ b/src/coreclr/vm/runtimehandles.cpp
@@ -1091,6 +1091,22 @@ extern "C" void QCALLTYPE RuntimeTypeHandle_MakeByRef(QCall::TypeHandle pTypeHan
return;
}
+extern "C" void QCALLTYPE RuntimeTypeHandle_MakeFunctionPointer(TypeHandle* pRetAndArgTypes, INT32 numArgs, BOOL isUnmanaged, QCall::ObjectHandleOnStack retType)
+{
+ QCALL_CONTRACT;
+
+ TypeHandle fnPtrHandle;
+
+ BEGIN_QCALL;
+ BYTE callConv = (BYTE)(isUnmanaged ? IMAGE_CEE_CS_CALLCONV_UNMANAGED : IMAGE_CEE_CS_CALLCONV_DEFAULT);
+ fnPtrHandle = ClassLoader::LoadFnptrTypeThrowing(callConv, numArgs, pRetAndArgTypes);
+ GCX_COOP();
+ retType.Set(fnPtrHandle.GetManagedClassObject());
+ END_QCALL;
+
+ return;
+}
+
extern "C" void QCALLTYPE RuntimeTypeHandle_Instantiate(QCall::TypeHandle pTypeHandle, TypeHandle * pInstArray, INT32 cInstArray, QCall::ObjectHandleOnStack retType)
{
QCALL_CONTRACT;
diff --git a/src/coreclr/vm/runtimehandles.h b/src/coreclr/vm/runtimehandles.h
index fb3bbf2b475c41..de19a93f279f55 100644
--- a/src/coreclr/vm/runtimehandles.h
+++ b/src/coreclr/vm/runtimehandles.h
@@ -160,6 +160,7 @@ extern "C" void QCALLTYPE RuntimeTypeHandle_MakeByRef(QCall::TypeHandle pTypeHan
extern "C" void QCALLTYPE RuntimeTypeHandle_MakePointer(QCall::TypeHandle pTypeHandle, QCall::ObjectHandleOnStack retType);
extern "C" void QCALLTYPE RuntimeTypeHandle_MakeSZArray(QCall::TypeHandle pTypeHandle, QCall::ObjectHandleOnStack retType);
extern "C" void QCALLTYPE RuntimeTypeHandle_MakeArray(QCall::TypeHandle pTypeHandle, INT32 rank, QCall::ObjectHandleOnStack retType);
+extern "C" void QCALLTYPE RuntimeTypeHandle_MakeFunctionPointer(TypeHandle* pRetAndArgTypes, INT32 numArgs, BOOL isUnmanaged, QCall::ObjectHandleOnStack retType);
extern "C" void QCALLTYPE RuntimeTypeHandle_PrepareMemberInfoCache(QCall::TypeHandle pMemberInfoCache);
extern "C" void QCALLTYPE RuntimeTypeHandle_ConstructName(QCall::TypeHandle pTypeHandle, DWORD format, QCall::StringHandleOnStack retString);
extern "C" void QCALLTYPE RuntimeTypeHandle_GetInterfaces(MethodTable* pMT, QCall::ObjectHandleOnStack result);
diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
index f511ebeb21810d..3f25e87d269c4b 100644
--- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
+++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
@@ -4441,4 +4441,19 @@
The directory '{0}' does not have the expected owner permissions: {1}.
+
+ Argument must represent a function pointer type.
+
+
+ Unmanaged calling conventions cannot be specified for managed function pointers.
+
+
+ '{0}' cannot be used as a parameter type for a function pointer.
+
+
+ Function pointer signature can only be encoded by dynamic ILGenerator.
+
+
+ Function pointer types are not valid.
+
diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
index 56fdc095a74eed..bdae2abd118f5d 100644
--- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
+++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
@@ -778,9 +778,11 @@
+
+
@@ -2938,4 +2940,4 @@
-
\ No newline at end of file
+
diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EnumBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EnumBuilder.cs
index e013510453c281..b8b250fb8507b3 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EnumBuilder.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EnumBuilder.cs
@@ -62,5 +62,10 @@ public override Type MakeArrayType(int rank)
string s = GetRankString(rank);
return SymbolType.FormCompoundType(s, this, 0)!;
}
+
+ public override Type MakeFunctionPointerType(Type[]? parameterTypes, bool isUnmanaged = false)
+ {
+ return Type.MakeFunctionPointerSignatureType(this, parameterTypes, isUnmanaged);
+ }
}
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs
index fb0703fe37d9a7..8342c98bb6deb9 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs
@@ -44,6 +44,16 @@ public abstract void EmitCalli(OpCode opcode, CallingConventions callingConventi
public abstract void EmitCalli(OpCode opcode, CallingConvention unmanagedCallConv, Type? returnType, Type[]? parameterTypes);
+ ///
+ /// Puts a instruction onto the Microsoft intermediate language (MSIL) stream,
+ /// specifying the type of the function pointer to indirectly call.
+ ///
+ ///
+ /// The type of the function pointer to indirectly call.
+ /// The specified type must represent a function pointer type.
+ ///
+ public virtual void EmitCalli(Type functionPointerType) => throw new NotSupportedException(SR.NotSupported_SubclassOverride);
+
public abstract void EmitCall(OpCode opcode, MethodInfo methodInfo, Type[]? optionalParameterTypes);
public abstract void Emit(OpCode opcode, SignatureHelper signature);
diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.cs
index aaf64eeb3ceebe..b1a0c16f804bd4 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.cs
@@ -321,6 +321,11 @@ public override Type MakeGenericType(params Type[] typeArguments)
return TypeBuilderInstantiation.MakeGenericType(this, typeArguments);
}
+ public override Type MakeFunctionPointerType(Type[]? parameterTypes, bool isUnmanaged = false)
+ {
+ return Type.MakeFunctionPointerSignatureType(this, parameterTypes, isUnmanaged);
+ }
+
#region Public Static Methods
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2055:UnrecognizedReflectionPattern",
Justification = "MakeGenericType is only called on a TypeBuilder which is not subject to trimming")]
diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/SignatureFunctionPointerType.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/SignatureFunctionPointerType.cs
new file mode 100644
index 00000000000000..0d4e5c9c732d7a
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/SignatureFunctionPointerType.cs
@@ -0,0 +1,93 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text;
+
+namespace System.Reflection
+{
+ internal sealed class SignatureFunctionPointerType : SignatureType
+ {
+ internal SignatureFunctionPointerType(Type returnType, Type[] parameterTypes, bool isUnmanaged, Type[] callingConventions)
+ {
+ _returnType = returnType;
+ _parameterTypes = parameterTypes;
+ _isUnmanaged = isUnmanaged;
+ _callingConventions = callingConventions;
+ }
+
+ private readonly Type _returnType;
+ private readonly Type[] _parameterTypes;
+ private readonly bool _isUnmanaged;
+ private readonly Type[] _callingConventions;
+
+ public override bool IsFunctionPointer => true;
+ public override bool IsUnmanagedFunctionPointer => _isUnmanaged;
+
+ public override Type[] GetFunctionPointerCallingConventions() => (Type[])_callingConventions.Clone();
+ public override Type[] GetFunctionPointerParameterTypes() => (Type[])_parameterTypes.Clone();
+ public override Type GetFunctionPointerReturnType() => _returnType;
+
+ public override bool IsEnum => false;
+ public override bool IsTypeDefinition => false;
+ public override bool IsSZArray => false;
+ public override bool IsVariableBoundArray => false;
+ public override bool IsByRefLike => false;
+ public override bool IsGenericTypeDefinition => false;
+ public override bool IsConstructedGenericType => false;
+ public override bool IsGenericParameter => false;
+ public override bool IsGenericTypeParameter => false;
+ public override bool IsGenericMethodParameter => false;
+ public override bool ContainsGenericParameters
+ {
+ get
+ {
+ if (_returnType.ContainsGenericParameters)
+ {
+ return true;
+ }
+
+ for (int i = 0; i < _parameterTypes.Length; i++)
+ {
+ if (_parameterTypes[i].ContainsGenericParameters)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+ public override Type[] GenericTypeArguments => [];
+ public override int GenericParameterPosition => 0;
+ internal override SignatureType? ElementType => null;
+ public override string Name => string.Empty;
+ public override string? Namespace => null;
+ protected override bool HasElementTypeImpl() => false;
+ protected override bool IsArrayImpl() => false;
+ protected override bool IsByRefImpl() => false;
+ protected override bool IsPointerImpl() => false;
+ public sealed override int GetArrayRank() => throw new ArgumentException(SR.Argument_HasToBeArrayClass);
+ public sealed override Type GetGenericTypeDefinition() => throw new InvalidOperationException(SR.InvalidOperation_NotGenericType);
+ public override Type[] GetGenericArguments() => [];
+ public override Type[] GetOptionalCustomModifiers() => [];
+ public override Type[] GetRequiredCustomModifiers() => [];
+ protected override bool IsValueTypeImpl() => false;
+
+ public override string ToString()
+ {
+ StringBuilder sb = new();
+ sb.Append(_returnType.ToString());
+ sb.Append('(');
+
+ for (int i = 0; i < _parameterTypes.Length; i++)
+ {
+ sb.Append(_parameterTypes[i].ToString());
+ if (i < _parameterTypes.Length - 1)
+ sb.Append(", ");
+ }
+
+ sb.Append(')');
+ return sb.ToString();
+ }
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/SignatureModifiedType.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/SignatureModifiedType.cs
new file mode 100644
index 00000000000000..799e025506bbbb
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/SignatureModifiedType.cs
@@ -0,0 +1,61 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Reflection
+{
+ internal sealed class SignatureModifiedType : SignatureType
+ {
+ internal SignatureModifiedType(Type baseType, Type[] requiredCustomModifiers, Type[] optionalCustomModifiers)
+ {
+ if (baseType is SignatureModifiedType modifiedType)
+ {
+ baseType = modifiedType.UnderlyingSystemType;
+ requiredCustomModifiers = [.. modifiedType.GetRequiredCustomModifiers(), .. requiredCustomModifiers];
+ optionalCustomModifiers = [.. modifiedType.GetOptionalCustomModifiers(), .. optionalCustomModifiers];
+ }
+
+ _unmodifiedType = baseType;
+ _requiredCustomModifiers = requiredCustomModifiers;
+ _optionalCustomModifiers = optionalCustomModifiers;
+ }
+
+ private readonly Type _unmodifiedType;
+ private readonly Type[] _requiredCustomModifiers;
+ private readonly Type[] _optionalCustomModifiers;
+
+ public override Type UnderlyingSystemType => _unmodifiedType;
+ public override Type[] GetRequiredCustomModifiers() => (Type[])_requiredCustomModifiers.Clone();
+ public override Type[] GetOptionalCustomModifiers() => (Type[])_optionalCustomModifiers.Clone();
+
+ public override bool IsTypeDefinition => _unmodifiedType.IsTypeDefinition;
+ public override bool IsSZArray => _unmodifiedType.IsSZArray;
+ public override bool IsVariableBoundArray => _unmodifiedType.IsVariableBoundArray;
+ public override bool IsByRefLike => _unmodifiedType.IsByRefLike;
+ public override bool IsFunctionPointer => _unmodifiedType.IsFunctionPointer;
+ public override bool IsGenericTypeDefinition => _unmodifiedType.IsGenericTypeDefinition;
+ public override bool IsConstructedGenericType => _unmodifiedType.IsConstructedGenericType;
+ public override bool IsGenericParameter => _unmodifiedType.IsGenericParameter;
+ public override bool IsGenericTypeParameter => _unmodifiedType.IsGenericTypeParameter;
+ public override bool IsGenericMethodParameter => _unmodifiedType.IsGenericMethodParameter;
+ public override bool IsUnmanagedFunctionPointer => _unmodifiedType.IsUnmanagedFunctionPointer;
+ public override bool ContainsGenericParameters => _unmodifiedType.ContainsGenericParameters;
+ public override Type[] GenericTypeArguments => _unmodifiedType.GenericTypeArguments;
+ public override int GenericParameterPosition => _unmodifiedType.GenericParameterPosition;
+ internal override SignatureType? ElementType => HasElementType ? new SignatureModifiedType(_unmodifiedType.GetElementType()!, [], []) : null;
+ public override string Name => _unmodifiedType.Name;
+ public override string? Namespace => _unmodifiedType.Namespace;
+ public override bool IsEnum => _unmodifiedType.IsEnum;
+ protected override bool HasElementTypeImpl() => _unmodifiedType.HasElementType;
+ protected override bool IsArrayImpl() => _unmodifiedType.IsArray;
+ protected override bool IsByRefImpl() => _unmodifiedType.IsByRef;
+ protected override bool IsPointerImpl() => _unmodifiedType.IsPointer;
+ public override int GetArrayRank() => _unmodifiedType.GetArrayRank();
+ public override Type[] GetFunctionPointerCallingConventions() => _unmodifiedType.GetFunctionPointerCallingConventions();
+ public override Type[] GetFunctionPointerParameterTypes() => _unmodifiedType.GetFunctionPointerParameterTypes();
+ public override Type GetFunctionPointerReturnType() => _unmodifiedType.GetFunctionPointerReturnType();
+ public override Type GetGenericTypeDefinition() => _unmodifiedType.GetGenericTypeDefinition();
+ public override Type[] GetGenericArguments() => _unmodifiedType.GetGenericArguments();
+ public override string ToString() => _unmodifiedType.ToString();
+ protected override bool IsValueTypeImpl() => _unmodifiedType.IsValueType;
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/SignatureType.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/SignatureType.cs
index 90832ed5f1711b..a29ac0441f48d7 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Reflection/SignatureType.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/SignatureType.cs
@@ -48,6 +48,7 @@ public sealed override Type MakeArrayType(int rank)
}
public sealed override Type MakeByRefType() => new SignatureByRefType(this);
public sealed override Type MakePointerType() => new SignaturePointerType(this);
+ public override Type MakeFunctionPointerType(Type[]? parameterTypes, bool isUnmanaged = false) => Type.MakeFunctionPointerSignatureType(this, parameterTypes, isUnmanaged);
[RequiresDynamicCode("The native code for this instantiation might not be available at runtime.")]
[RequiresUnreferencedCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")]
@@ -68,7 +69,7 @@ public sealed override Type MakeArrayType(int rank)
public sealed override bool Equals(Type? o) => base.Equals(o);
public sealed override int GetHashCode() => base.GetHashCode();
#endif
- public sealed override Type UnderlyingSystemType => this; // Equals(Type) depends on this.
+ public override Type UnderlyingSystemType => this; // Equals(Type) depends on this.
// Naming and diagnostics
public abstract override string Name { get; }
diff --git a/src/libraries/System.Private.CoreLib/src/System/Type.cs b/src/libraries/System.Private.CoreLib/src/System/Type.cs
index 61ce2deace997a..c9e9ab23dffb1f 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Type.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Type.cs
@@ -645,10 +645,135 @@ public virtual Array GetEnumValues()
public virtual Type MakeArrayType(int rank) => throw new NotSupportedException();
public virtual Type MakeByRefType() => throw new NotSupportedException();
+ ///
+ /// Creates a function pointer signature type with the specified return type, parameter types and calling conventions.
+ ///
+ /// The return type of the function pointer.
+ /// An array of types representing the parameters of the function pointer.
+ /// if the function pointer uses unmanaged calling conventions; otherwise, .
+ /// An array of types representing the calling conventions applied to the function pointer.
+ /// A object representing the constructed function pointer signature.
+ ///
+ /// Thrown when , any supplied parameter type
+ /// or any supplied calling convention is .
+ ///
+ public static Type MakeFunctionPointerSignatureType(Type returnType, Type[]? parameterTypes, bool isUnmanaged = false, Type[]? callingConventions = null)
+ {
+ ArgumentNullException.ThrowIfNull(returnType);
+ parameterTypes = (parameterTypes != null) ? (Type[])parameterTypes.Clone() : [];
+ callingConventions = (callingConventions != null) ? (Type[])callingConventions.Clone() : [];
+
+ for (int i = 0; i < parameterTypes.Length; i++)
+ {
+ Type paramType = parameterTypes[i];
+ ArgumentNullException.ThrowIfNull(paramType, nameof(parameterTypes));
+
+ if (paramType == typeof(void) || paramType.IsGenericTypeDefinition)
+ throw new ArgumentException(string.Format(SR.FunctionPointer_ParameterInvalid, paramType.ToString()), nameof(parameterTypes));
+ }
+
+ for (int i = 0; i < callingConventions.Length; i++)
+ ArgumentNullException.ThrowIfNull(callingConventions[i], nameof(callingConventions));
+
+ if (!isUnmanaged && callingConventions.Length >= 1)
+ throw new ArgumentException(SR.ManagedFunctionPointer_CallingConventionsNotAllowed, nameof(callingConventions));
+
+ // Reverse calling conventions for consistency with Roslyn:
+ // delegate* unmanaged[Cdecl, MemberFunction] => int32 modopt(CallConvMemberFunction) modopt(CallConvCdecl) *()
+ Array.Reverse(callingConventions);
+
+ bool builtInCallConv = false;
+ if (callingConventions.Length == 1)
+ {
+ // Known calling conventions with direct IL equivalent
+ string? callConv = callingConventions[0].FullName;
+ if (!string.IsNullOrEmpty(callConv) &&
+ (callConv == "System.Runtime.CompilerServices.CallConvCdecl"
+ || callConv == "System.Runtime.CompilerServices.CallConvStdcall"
+ || callConv == "System.Runtime.CompilerServices.CallConvThiscall"
+ || callConv == "System.Runtime.CompilerServices.CallConvFastcall"))
+ builtInCallConv = true;
+ }
+
+ if (isUnmanaged && !builtInCallConv && callingConventions.Length > 0)
+ {
+ // Newer or multiple calling conventions specified -> encoded as modopts
+ returnType = MakeModifiedSignatureType(
+ returnType,
+ requiredCustomModifiers: [],
+ optionalCustomModifiers: callingConventions);
+ }
+
+ return new SignatureFunctionPointerType(
+ returnType,
+ parameterTypes,
+ isUnmanaged,
+ callingConventions);
+ }
+
+ ///
+ /// Creates a object that represents a function pointer type
+ /// with the specified parameter types. The return type is represented by the
+ /// current instance.
+ ///
+ /// An array of objects that represent the parameter types of
+ /// the function pointer. If , an empty parameter list
+ /// is assumed.
+ ///
+ ///
+ /// to create an unmanaged function pointer type; otherwise,
+ /// to create a managed function pointer type.
+ ///
+ ///
+ /// A object that represents the function pointer type whose
+ /// return type is the current and whose parameter types are
+ /// specified by .
+ ///
+ public virtual Type MakeFunctionPointerType(Type[]? parameterTypes, bool isUnmanaged = false) => throw new NotSupportedException(SR.NotSupported_SubclassOverride);
+
[RequiresDynamicCode("The native code for this instantiation might not be available at runtime.")]
[RequiresUnreferencedCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")]
public virtual Type MakeGenericType(params Type[] typeArguments) => throw new NotSupportedException(SR.NotSupported_SubclassOverride);
+ ///
+ /// Creates a that represents with the specified
+ /// required and optional custom modifiers applied to its signature.
+ ///
+ /// The to modify. This parameter cannot be .
+ ///
+ /// An array of objects that represent required custom modifiers
+ /// (modreq) to apply to the returned type.
+ /// If , no required custom modifiers are applied.
+ ///
+ ///
+ /// An array of objects that represent optional custom modifiers
+ /// (modopt) to apply to the returned type.
+ /// If , no optional custom modifiers are applied.
+ ///
+ /// A new instance that represents the specified
+ /// with the given required and optional custom modifiers.
+ ///
+ /// Thrown when
+ /// or any specified modifier type is .
+ ///
+ public static Type MakeModifiedSignatureType(Type type, Type[]? requiredCustomModifiers, Type[]? optionalCustomModifiers)
+ {
+ ArgumentNullException.ThrowIfNull(type);
+ requiredCustomModifiers = (requiredCustomModifiers != null) ? (Type[])requiredCustomModifiers.Clone() : [];
+ optionalCustomModifiers = (optionalCustomModifiers != null) ? (Type[])optionalCustomModifiers.Clone() : [];
+
+ for (int i = 0; i < requiredCustomModifiers.Length; i++)
+ ArgumentNullException.ThrowIfNull(requiredCustomModifiers[i], nameof(requiredCustomModifiers));
+
+ for (int i = 0; i < optionalCustomModifiers.Length; i++)
+ ArgumentNullException.ThrowIfNull(optionalCustomModifiers[i], nameof(optionalCustomModifiers));
+
+ return new SignatureModifiedType(
+ type,
+ requiredCustomModifiers,
+ optionalCustomModifiers);
+ }
+
public virtual Type MakePointerType() => throw new NotSupportedException();
public static Type MakeGenericSignatureType(Type genericTypeDefinition, params Type[] typeArguments)
diff --git a/src/libraries/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.cs b/src/libraries/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.cs
index c5dcbd65fb1888..747b82c32b4eb8 100644
--- a/src/libraries/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.cs
+++ b/src/libraries/System.Reflection.Emit.ILGeneration/ref/System.Reflection.Emit.ILGeneration.cs
@@ -48,6 +48,7 @@ public void Emit(System.Reflection.Emit.OpCode opcode, sbyte arg) { }
public abstract void EmitCall(System.Reflection.Emit.OpCode opcode, System.Reflection.MethodInfo methodInfo, System.Type[]? optionalParameterTypes);
public abstract void EmitCalli(System.Reflection.Emit.OpCode opcode, System.Reflection.CallingConventions callingConvention, System.Type? returnType, System.Type[]? parameterTypes, System.Type[]? optionalParameterTypes);
public abstract void EmitCalli(System.Reflection.Emit.OpCode opcode, System.Runtime.InteropServices.CallingConvention unmanagedCallConv, System.Type? returnType, System.Type[]? parameterTypes);
+ public virtual void EmitCalli(System.Type functionPointerType) { }
public virtual void EmitWriteLine(System.Reflection.Emit.LocalBuilder localBuilder) { }
public virtual void EmitWriteLine(System.Reflection.FieldInfo fld) { }
public virtual void EmitWriteLine(string value) { }
diff --git a/src/libraries/System.Reflection.Emit.ILGeneration/tests/ILGenerator/Emit4Tests.cs b/src/libraries/System.Reflection.Emit.ILGeneration/tests/ILGenerator/Emit4Tests.cs
index ec38955ac9b4ae..b75edf40756c33 100644
--- a/src/libraries/System.Reflection.Emit.ILGeneration/tests/ILGenerator/Emit4Tests.cs
+++ b/src/libraries/System.Reflection.Emit.ILGeneration/tests/ILGenerator/Emit4Tests.cs
@@ -1,7 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Collections.Generic;
using System.Linq;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Xunit;
@@ -170,6 +172,116 @@ public void TestDynamicMethodEmitCalliNonBlittable()
Assert.Equal(result, resultValue);
}
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/124149", TestRuntimes.Mono)]
+ public void TestDynamicMethodEmitCalliFnPtrManaged()
+ {
+ Type fnPtrType = Type.MakeFunctionPointerSignatureType(typeof(int), [typeof(int), typeof(int)]);
+ DynamicMethod dynamicMethod = new("Test", typeof(int), [typeof(nint)]);
+
+ ILGenerator il = dynamicMethod.GetILGenerator();
+ il.Emit(OpCodes.Ldc_I4_S, (byte)10);
+ il.Emit(OpCodes.Ldc_I4_S, (byte)5);
+ il.Emit(OpCodes.Ldarg_0);
+ il.EmitCalli(fnPtrType);
+ il.Emit(OpCodes.Ret);
+
+ object resultValue = dynamicMethod.Invoke(null, [((Func)Int32Sum).Method.MethodHandle.GetFunctionPointer()]);
+
+ Assert.Equal(15, resultValue);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/124149", TestRuntimes.Mono)]
+ public void TestDynamicMethodEmitCalliFnPtrStdCall()
+ {
+ int a = 1, b = 1, result = 2;
+
+ Type returnType = typeof(int);
+
+ var dynamicMethod = new DynamicMethod("F", returnType, [typeof(nint), typeof(int), typeof(int)]);
+
+ ILGenerator il = dynamicMethod.GetILGenerator();
+ il.Emit(OpCodes.Ldarg_1);
+ il.Emit(OpCodes.Ldarg_2);
+ il.Emit(OpCodes.Ldarg_0);
+ il.EmitCalli(Type.MakeFunctionPointerSignatureType(
+ returnType,
+ parameterTypes: [typeof(int), typeof(int)],
+ isUnmanaged: true,
+ callingConventions: [typeof(CallConvStdcall)]));
+ il.Emit(OpCodes.Ret);
+
+ var del = new Int32SumStdCall(Int32Sum);
+ IntPtr funcPtr = Marshal.GetFunctionPointerForDelegate(del);
+
+ object resultValue = dynamicMethod
+ .Invoke(null, [funcPtr, a, b]);
+
+ GC.KeepAlive(del);
+
+ Assert.IsType(returnType, resultValue);
+ Assert.Equal(result, resultValue);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/124149", TestRuntimes.Mono)]
+ public unsafe void TestDynamicMethodEmitCalli_NestedFunctionPointer()
+ {
+ // delegate*, int>
+ Type type = Type.MakeFunctionPointerSignatureType(
+ typeof(int),
+ [Type.MakeFunctionPointerSignatureType(typeof(bool), [typeof(short)], true, [typeof(CallConvStdcall), typeof(CallConvMemberFunction)])]);
+
+ DynamicMethod dynamicMethod = new("F", typeof(int), [typeof(nint)]);
+ ILGenerator il = dynamicMethod.GetILGenerator();
+ il.Emit(OpCodes.Ldc_I4_0);
+ il.Emit(OpCodes.Conv_I);
+ il.Emit(OpCodes.Ldarg_0);
+ il.EmitCalli(type);
+ il.Emit(OpCodes.Ret);
+
+ object result = dynamicMethod.Invoke(null, [(nint)(delegate*, int>)(&ComplexSignature)]);
+ Assert.Equal(5, (int)result);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/124149", TestRuntimes.Mono)]
+ public unsafe void TestDynamicMethodEmitCalli_NestedFunctionPointer2()
+ {
+ // delegate**>>>, delegate*, int>>
+ Type type = Type.MakeFunctionPointerSignatureType(
+ Type.MakeFunctionPointerSignatureType(
+ typeof(int),
+ [Type.MakeFunctionPointerSignatureType(
+ typeof(bool),
+ [typeof(short)], true,
+ [typeof(CallConvStdcall), typeof(CallConvMemberFunction)])], false),
+ [Type.MakeFunctionPointerSignatureType(
+ Type.MakeFunctionPointerSignatureType(
+ Type.MakeFunctionPointerSignatureType(
+ typeof(List<>).MakeGenericType(typeof(short)).MakePointerType(),
+ [], true,
+ [typeof(CallConvFastcall), typeof(CallConvSuppressGCTransition)]),
+ [], false),
+ [], true,
+ [typeof(CallConvCdecl)])]);
+
+ DynamicMethod dynamicMethod = new("F", typeof(nint), [typeof(nint)]);
+ ILGenerator il = dynamicMethod.GetILGenerator();
+ il.Emit(OpCodes.Ldc_I4_0);
+ il.Emit(OpCodes.Conv_I);
+ il.Emit(OpCodes.Ldarg_0);
+ il.EmitCalli(type);
+ il.Emit(OpCodes.Ret);
+
+ object result = dynamicMethod.Invoke(
+ null,
+ [(nint)(delegate**>>>,
+ delegate*, int>>)(&EvenMoreComplexSignature)]);
+ Assert.Equal((nint)(delegate*, int>)(&ComplexSignature), (nint)result);
+ }
+
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate int Int32SumStdCall(int a, int b);
@@ -179,5 +291,10 @@ public void TestDynamicMethodEmitCalliNonBlittable()
private static int Int32Sum(int a, int b) => a + b;
private static string StringReverse(string a) => string.Join("", a.Reverse());
+
+ private unsafe static int ComplexSignature(delegate* unmanaged[Stdcall, MemberFunction] arg) => 5;
+
+ private unsafe static delegate*, int> EvenMoreComplexSignature(
+ delegate* unmanaged[Cdecl]*>>> arg) => &ComplexSignature;
}
}
diff --git a/src/libraries/System.Reflection.Emit.ILGeneration/tests/SignatureHelper/SignatureHelperGetMethodSigHelper.cs b/src/libraries/System.Reflection.Emit.ILGeneration/tests/SignatureHelper/SignatureHelperGetMethodSigHelper.cs
index b6c289f95f005b..2ba72bf58cfa99 100644
--- a/src/libraries/System.Reflection.Emit.ILGeneration/tests/SignatureHelper/SignatureHelperGetMethodSigHelper.cs
+++ b/src/libraries/System.Reflection.Emit.ILGeneration/tests/SignatureHelper/SignatureHelperGetMethodSigHelper.cs
@@ -56,5 +56,12 @@ public void GetMethodSigHelper_Module_Type_TypeArray_NullObjectInParameterType_T
ModuleBuilder module = Helpers.DynamicModule();
AssertExtensions.Throws("argument", () => SignatureHelper.GetMethodSigHelper(module, typeof(string), new Type[] { typeof(char), null }));
}
+
+ [Fact]
+ public void GetMethodSigHelper_FunctionPointerParameter_ThrowsNotSupportedException()
+ {
+ SignatureHelper sig = SignatureHelper.GetMethodSigHelper(CallingConventions.Standard, typeof(void));
+ AssertExtensions.Throws(() => sig.AddArgument(Type.MakeFunctionPointerSignatureType(typeof(int), [typeof(bool)])));
+ }
}
}
diff --git a/src/libraries/System.Reflection.Emit.ILGeneration/tests/System.Reflection.Emit.ILGeneration.Tests.csproj b/src/libraries/System.Reflection.Emit.ILGeneration/tests/System.Reflection.Emit.ILGeneration.Tests.csproj
index fbd22531f9188b..fe4cd116a9e751 100644
--- a/src/libraries/System.Reflection.Emit.ILGeneration/tests/System.Reflection.Emit.ILGeneration.Tests.csproj
+++ b/src/libraries/System.Reflection.Emit.ILGeneration/tests/System.Reflection.Emit.ILGeneration.Tests.csproj
@@ -3,6 +3,7 @@
$(NetCoreAppCurrent)
true
true
+ true
diff --git a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx
index b22c9a53610fee..0409122e6627e6 100644
--- a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx
+++ b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx
@@ -303,4 +303,7 @@
Type provided must be an Enum.
+
+ Argument must represent a function pointer type.
+
diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs
index d8f3445eb2434f..abc4449dbe9be1 100644
--- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs
+++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs
@@ -645,7 +645,7 @@ private static int GetStackChange(OpCode opcode, MethodInfo methodInfo, Type voi
int stackChange = 0;
// Push the return value if there is one.
- if (methodInfo.ReturnType != voidType)
+ if (methodInfo.ReturnType.UnderlyingSystemType != voidType)
{
stackChange++;
}
@@ -720,11 +720,29 @@ public override void EmitCalli(OpCode opcode, CallingConvention unmanagedCallCon
_il.Token(_moduleBuilder.GetSignatureToken(unmanagedCallConv, returnType, parameterTypes));
}
+ ///
+ public override void EmitCalli(Type functionPointerType)
+ {
+ ArgumentNullException.ThrowIfNull(functionPointerType);
+
+ if (!functionPointerType.IsFunctionPointer)
+ throw new ArgumentException(SR.Argument_MustBeFunctionPointer, nameof(functionPointerType));
+
+ int stackChange = GetStackChange(
+ functionPointerType.GetFunctionPointerReturnType(),
+ _moduleBuilder.GetTypeFromCoreAssembly(CoreTypeId.Void),
+ functionPointerType.GetFunctionPointerParameterTypes());
+
+ UpdateStackSize(stackChange);
+ Emit(OpCodes.Calli);
+ _il.Token(_moduleBuilder.GetFunctionPointerSignatureToken(functionPointerType));
+ }
+
private static int GetStackChange(Type? returnType, Type voidType, Type[]? parameterTypes)
{
int stackChange = 0;
// If there is a non-void return type, push one.
- if (returnType != voidType)
+ if (returnType?.UnderlyingSystemType != voidType)
{
stackChange++;
}
diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs
index e87fa61926efb9..33b9cd9656192c 100644
--- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs
+++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs
@@ -696,7 +696,8 @@ private EntityHandle GetTypeReferenceOrSpecificationHandle(Type type)
if (!_typeReferences.TryGetValue(type, out var typeHandle))
{
if (type.HasElementType || type.IsGenericParameter ||
- (type.IsGenericType && !type.IsGenericTypeDefinition))
+ (type.IsGenericType && !type.IsGenericTypeDefinition)
+ || type.IsFunctionPointer)
{
typeHandle = AddTypeSpecification(type);
}
@@ -1103,6 +1104,19 @@ private bool IsConstructedFromTypeBuilder(Type type)
return (elementType is TypeBuilderImpl tbi && Equals(tbi.Module)) || IsConstructedFromTypeBuilder(elementType);
}
+ if (type.IsFunctionPointer)
+ {
+ Type ret = type.GetFunctionPointerReturnType();
+ if ((ret is TypeBuilderImpl tb && Equals(tb.Module)) || IsConstructedFromTypeBuilder(ret))
+ return true;
+
+ foreach (Type paramType in type.GetFunctionPointerParameterTypes())
+ {
+ if ((paramType is TypeBuilderImpl _tb && Equals(_tb.Module)) || IsConstructedFromTypeBuilder(paramType))
+ return true;
+ }
+ }
+
return false;
}
@@ -1359,6 +1373,16 @@ internal int GetSignatureToken(CallingConvention callingConvention, Type? return
MetadataTokens.GetToken(_metadataBuilder.AddStandaloneSignature(_metadataBuilder.GetOrAddBlob(
MetadataSignatureHelper.GetMethodSignature(this, parameterTypes, returnType, GetSignatureConvention(callingConvention)))));
+ internal int GetFunctionPointerSignatureToken(Type functionPointerType)
+ {
+ BlobBuilder blobBuilder = new();
+ SignatureTypeEncoder encoder = new(blobBuilder);
+ MetadataSignatureHelper.WriteSignatureForFunctionPointerType(encoder, functionPointerType, this);
+
+ byte[] blob = blobBuilder.ToArray()[1..]; // Strip away ELEMENT_TYPE_FNPTR
+ return MetadataTokens.GetToken(_metadataBuilder.AddStandaloneSignature(_metadataBuilder.GetOrAddBlob(blob)));
+ }
+
private static SignatureCallingConvention GetSignatureConvention(CallingConvention callingConvention) =>
callingConvention switch
{
diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs
index 2ec00b90743525..ebaa831e4b20f2 100644
--- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs
+++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs
@@ -252,7 +252,7 @@ private static void WriteSignatureForType(SignatureTypeEncoder signature, Type t
}
}
- private static void WriteSignatureForFunctionPointerType(SignatureTypeEncoder signature, Type type, ModuleBuilderImpl module)
+ internal static void WriteSignatureForFunctionPointerType(SignatureTypeEncoder signature, Type type, ModuleBuilderImpl module)
{
SignatureCallingConvention callConv = SignatureCallingConvention.Default;
FunctionPointerAttributes attribs = FunctionPointerAttributes.None;
@@ -295,7 +295,11 @@ private static void WriteSignatureForFunctionPointerType(SignatureTypeEncoder si
if (returnType.GetRequiredCustomModifiers() is Type[] retModReqs)
WriteCustomModifiers(retModifiersEncoder, retModReqs, isOptional: false, module);
- WriteSignatureForType(retTypeEncoder.Type(), returnType, module);
+ Type returnTypeToWrite = returnType;
+ if (returnTypeToWrite.IsSignatureType)
+ returnTypeToWrite = returnTypeToWrite.UnderlyingSystemType;
+
+ WriteSignatureForType(retTypeEncoder.Type(), returnTypeToWrite, module);
foreach (Type paramType in paramTypes)
{
@@ -308,7 +312,11 @@ private static void WriteSignatureForFunctionPointerType(SignatureTypeEncoder si
if (paramType.GetRequiredCustomModifiers() is Type[] paramModReqs)
WriteCustomModifiers(paramModifiersEncoder, paramModReqs, isOptional: false, module);
- WriteSignatureForType(paramEncoder.Type(), paramType, module);
+ Type paramTypeToWrite = paramType;
+ if (paramTypeToWrite.IsSignatureType)
+ paramTypeToWrite = paramTypeToWrite.UnderlyingSystemType;
+
+ WriteSignatureForType(paramEncoder.Type(), paramTypeToWrite, module);
}
}
diff --git a/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveILGeneratorTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveILGeneratorTests.cs
index 9c7f21ed239990..6470db395f4224 100644
--- a/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveILGeneratorTests.cs
+++ b/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveILGeneratorTests.cs
@@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Loader;
using Xunit;
@@ -3066,5 +3067,133 @@ public void ReferenceGenericMembersInOtherGeneratedAssembly()
tlc.Unload();
}
}
+
+ [Fact]
+ public void EmitCalliFunctionPointerField()
+ {
+ using TempFile file = TempFile.Create();
+ PersistedAssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyAndModule(out ModuleBuilder mb);
+ TypeBuilder tb = mb.DefineType("Program", TypeAttributes.Public);
+ MethodBuilder methb = tb.DefineMethod("Test", MethodAttributes.Public | MethodAttributes.Static);
+ methb.SetReturnType(typeof(int));
+ ILGenerator il = methb.GetILGenerator();
+ il.Emit(OpCodes.Call, typeof(ClassWithFunctionPointers).GetMethod("Init"));
+ il.Emit(OpCodes.Ldc_I4_2);
+ il.Emit(OpCodes.Ldc_I4_3);
+ il.Emit(OpCodes.Ldsfld, typeof(ClassWithFunctionPointers).GetField("FuncManaged"));
+ il.EmitCalli(Type.MakeFunctionPointerSignatureType(typeof(int), [typeof(int), typeof(int)]));
+ il.Emit(OpCodes.Ret);
+ tb.CreateType();
+ ab.Save(file.Path);
+
+ TestAssemblyLoadContext tlc = new();
+ Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path);
+ Type typeFromDisk = assemblyFromDisk.GetType("Program");
+ MethodInfo methodFromDisk = typeFromDisk.GetMethod("Test");
+ int result = (int)methodFromDisk.Invoke(null, null);
+
+ Assert.Equal(5, result);
+ tlc.Unload();
+ }
+
+ [Fact]
+ public void EmitCalliVerifySignatures()
+ {
+ Type t1 = Type.MakeFunctionPointerSignatureType(typeof(int), [typeof(int), typeof(int)]);
+ Type t2 = Type.MakeFunctionPointerSignatureType(typeof(bool), [typeof(string)], true, [typeof(CallConvCdecl)]);
+ Type t3 = Type.MakeFunctionPointerSignatureType(typeof(void), [typeof(int)], true, [typeof(CallConvFastcall), typeof(CallConvSuppressGCTransition)]);
+ Type t4 = Type.MakeFunctionPointerSignatureType(
+ typeof(string),
+ [Type.MakeFunctionPointerSignatureType(typeof(bool), [typeof(short)], true, [typeof(CallConvStdcall), typeof(CallConvMemberFunction)])],
+ true, [typeof(CallConvSwift)]);
+
+ using TempFile file = TempFile.Create();
+ PersistedAssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyAndModule(out ModuleBuilder mb);
+ TypeBuilder tb = mb.DefineType("Program", TypeAttributes.Public);
+ MethodBuilder methb = tb.DefineMethod("Test", MethodAttributes.Public | MethodAttributes.Static);
+ ILGenerator il = methb.GetILGenerator();
+ Label labelAfter = il.DefineLabel();
+
+ il.Emit(OpCodes.Br, labelAfter);
+ il.EmitCalli(t1);
+ il.EmitCalli(t2);
+ il.EmitCalli(t3);
+ il.EmitCalli(t4);
+ il.MarkLabel(labelAfter);
+ il.Emit(OpCodes.Ret);
+ tb.CreateType();
+ ab.Save(file.Path);
+
+ // Verifies EmitCalli produces valid signatures -> InvalidProgramException is thrown otherwise
+ TestAssemblyLoadContext tlc = new();
+ Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path);
+ Type typeFromDisk = assemblyFromDisk.GetType("Program");
+ MethodInfo methodFromDisk = typeFromDisk.GetMethod("Test");
+ methodFromDisk.Invoke(null, null);
+ tlc.Unload();
+ }
+
+ [Fact]
+ public void EmitCalliVerifyStackChange()
+ {
+ Type fieldType = Type.MakeFunctionPointerSignatureType(
+ Type.MakeModifiedSignatureType(typeof(void), [typeof(IsConst)], null),
+ null);
+
+ using TempFile file = TempFile.Create();
+ PersistedAssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyAndModule(out ModuleBuilder mb);
+ TypeBuilder tb = mb.DefineType("Program", TypeAttributes.Public);
+ FieldBuilder fb = tb.DefineField("Func", fieldType, FieldAttributes.Public | FieldAttributes.Static);
+ MethodBuilder methb = tb.DefineMethod("Test", MethodAttributes.Public | MethodAttributes.Static);
+ ILGenerator il = methb.GetILGenerator();
+
+ il.Emit(OpCodes.Ldftn, typeof(Console).GetMethod("WriteLine", BindingFlags.Public | BindingFlags.Static, []));
+ il.Emit(OpCodes.Stsfld, fb);
+ il.Emit(OpCodes.Ldsfld, fb);
+ il.EmitCalli(fieldType);
+ il.Emit(OpCodes.Ret);
+ tb.CreateType();
+ ab.Save(file.Path);
+
+ TestAssemblyLoadContext tlc = new();
+ Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path);
+ Type typeFromDisk = assemblyFromDisk.GetType("Program");
+ MethodInfo methodFromDisk = typeFromDisk.GetMethod("Test");
+ methodFromDisk.Invoke(null, null);
+ tlc.Unload();
+ }
+
+ [Fact]
+ public void EmitLdtokenSignatureType()
+ {
+ using TempFile file = TempFile.Create();
+ PersistedAssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyAndModule(out ModuleBuilder mb);
+ TypeBuilder tb = mb.DefineType("MyType", TypeAttributes.Class | TypeAttributes.Public);
+ MethodBuilder methb = tb.DefineMethod("M", MethodAttributes.Public | MethodAttributes.Static);
+ methb.SetReturnType(typeof(Type));
+ GenericTypeParameterBuilder genericTypeParam = methb.DefineGenericParameters(["T"])[0];
+ ILGenerator il = methb.GetILGenerator();
+
+ Type sigType = Type.MakeFunctionPointerSignatureType(
+ typeof(DateTime),
+ [genericTypeParam, tb]);
+
+ il.Emit(OpCodes.Ldtoken, sigType);
+ il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"));
+ il.Emit(OpCodes.Ret);
+ tb.CreateType();
+ ab.Save(file.Path);
+
+ TestAssemblyLoadContext tlc = new();
+ Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path);
+ Type typeFromDisk = assemblyFromDisk.GetType("MyType");
+ MethodInfo methodFromDisk = typeFromDisk.GetMethod("M").MakeGenericMethod(typeof(int));
+ Type retType = (Type)methodFromDisk.Invoke(null, null);
+ tlc.Unload();
+
+ Assert.True(retType.IsFunctionPointer);
+ Assert.Equal(typeof(DateTime), retType.GetFunctionPointerReturnType());
+ Assert.Equal([typeof(int).FullName, tb.FullName], retType.GetFunctionPointerParameterTypes().Select(t => t.FullName));
+ }
}
}
diff --git a/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj b/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj
index 0552a0ae8f6473..d44c7b9995b8b3 100644
--- a/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj
+++ b/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj
@@ -122,6 +122,7 @@
+
diff --git a/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderMakeFunctionPointerType.cs b/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderMakeFunctionPointerType.cs
new file mode 100644
index 00000000000000..dc7e32fa5f177f
--- /dev/null
+++ b/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderMakeFunctionPointerType.cs
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Xunit;
+
+namespace System.Reflection.Emit.Tests
+{
+ public class TypeBuilderMakeFunctionPointerType
+ {
+ [Fact]
+ public void MakeFunctionPointerType()
+ {
+ TypeBuilder retType = Helpers.DynamicType(TypeAttributes.Public);
+ Type[] paramTypes = [typeof(int), typeof(bool)];
+ Type fnPtrType = retType.MakeFunctionPointerType(paramTypes);
+
+ Assert.True(fnPtrType.IsFunctionPointer);
+ Assert.False(fnPtrType.IsUnmanagedFunctionPointer);
+ Assert.Equal(retType, fnPtrType.GetFunctionPointerReturnType());
+ Assert.Equal(paramTypes, fnPtrType.GetFunctionPointerParameterTypes());
+ }
+ }
+}
diff --git a/src/libraries/System.Reflection.Emit/tests/Utilities.cs b/src/libraries/System.Reflection.Emit/tests/Utilities.cs
index 446b33f8628aa9..efd754177e0c08 100644
--- a/src/libraries/System.Reflection.Emit/tests/Utilities.cs
+++ b/src/libraries/System.Reflection.Emit/tests/Utilities.cs
@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using Xunit;
namespace System.Reflection.Emit.Tests
@@ -214,4 +216,15 @@ public ModifiedType(Type delegatingType, Type[] requiredMods = null, Type[] opti
public override Type[] GetOptionalCustomModifiers() => optionalModifiers;
}
}
+
+ public unsafe class ClassWithFunctionPointers
+ {
+ public static delegate* FuncManaged;
+ public static int Add(int a, int b) => a + b;
+ public static void Init() => FuncManaged = &Add;
+
+ public static delegate* unmanaged[Cdecl] FuncUnmanaged1;
+ public static delegate* unmanaged[Fastcall, SuppressGCTransition] FuncUnmanaged2;
+ public static delegate* unmanaged[Swift], string> FuncUnmanaged3;
+ }
}
diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs
index ffa9bf58bdcecf..ca4358fdf536d9 100644
--- a/src/libraries/System.Runtime/ref/System.Runtime.cs
+++ b/src/libraries/System.Runtime/ref/System.Runtime.cs
@@ -6771,11 +6771,14 @@ protected Type() { }
[System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("The code for an array of the specified type might not be available.")]
public virtual System.Type MakeArrayType(int rank) { throw null; }
public virtual System.Type MakeByRefType() { throw null; }
+ public static System.Type MakeFunctionPointerSignatureType(System.Type returnType, System.Type[]? parameterTypes, bool isUnmanaged = false, System.Type[]? callingConventions = null) { throw null; }
+ public virtual System.Type MakeFunctionPointerType(System.Type[]? parameterTypes, bool isUnmanaged = false) { throw null; }
public static System.Type MakeGenericMethodParameter(int position) { throw null; }
public static System.Type MakeGenericSignatureType(System.Type genericTypeDefinition, params System.Type[] typeArguments) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("The native code for this instantiation might not be available at runtime.")]
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")]
public virtual System.Type MakeGenericType(params System.Type[] typeArguments) { throw null; }
+ public static System.Type MakeModifiedSignatureType(System.Type type, System.Type[]? requiredCustomModifiers, System.Type[]? optionalCustomModifiers) { throw null; }
public virtual System.Type MakePointerType() { throw null; }
public static bool operator ==(System.Type? left, System.Type? right) { throw null; }
public static bool operator !=(System.Type? left, System.Type? right) { throw null; }
diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/SignatureTypes.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/SignatureTypes.cs
index 2c346f23e05796..16edcceb23f7d4 100644
--- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/SignatureTypes.cs
+++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/SignatureTypes.cs
@@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using System.Runtime.CompilerServices;
using Xunit;
namespace System.Reflection.Tests
@@ -298,6 +299,215 @@ public static void MakeSignaturePointerType()
TestSignatureTypeInvariants(t);
}
+ [Fact]
+ public static void MakeSignatureFunctionPointerTypeManaged()
+ {
+ Type[] paramTypes = [typeof(string), typeof(bool)];
+ Type t = Type.MakeFunctionPointerSignatureType(typeof(int), paramTypes);
+
+ Assert.True(t.IsFunctionPointer);
+ Assert.False(t.IsUnmanagedFunctionPointer);
+ Assert.Equal(typeof(int).ToString(), t.GetFunctionPointerReturnType().ToString());
+ Assert.Equal(paramTypes.Select(t => t.ToString()), t.GetFunctionPointerParameterTypes().Select(t => t.ToString()));
+ Assert.Equal(0, t.GetFunctionPointerCallingConventions().Length);
+ }
+
+ [Fact]
+ public static void MakeSignatureFunctionPointerTypeManaged_InvalidCallingConventions()
+ {
+ Assert.Throws(() =>
+ {
+ Type[] paramTypes = [typeof(string), typeof(bool)];
+ Type t = Type.MakeFunctionPointerSignatureType(typeof(int), paramTypes, false, [typeof(CallConvCdecl)]);
+ });
+ }
+
+ [Fact]
+ public static void MakeSignatureFunctionPointerTypeUnmanaged1()
+ {
+ Type[] paramTypes = [typeof(short)];
+ Type[] callConvs = [typeof(CallConvCdecl)];
+ Type t = Type.MakeFunctionPointerSignatureType(typeof(void), paramTypes, true, callConvs);
+
+ Type fnPtrRet = t.GetFunctionPointerReturnType();
+ Type[] fnPtrParams = t.GetFunctionPointerParameterTypes();
+ Type[] fnPtrCallConvs = t.GetFunctionPointerCallingConventions();
+ Assert.True(t.IsFunctionPointer);
+ Assert.True(t.IsUnmanagedFunctionPointer);
+ Assert.Equal(typeof(void).ToString(), fnPtrRet.ToString());
+ Assert.Equal(paramTypes.Select(t => t.ToString()), fnPtrParams.Select(t => t.ToString()));
+ Assert.Equal(callConvs.Select(t => t.ToString()), fnPtrCallConvs.Select(t => t.ToString()));
+ Assert.Equal(0, fnPtrRet.GetOptionalCustomModifiers().Length);
+ }
+
+ [Fact]
+ public static void MakeSignatureFunctionPointerTypeUnmanaged2()
+ {
+ Type[] paramTypes = [typeof(short)];
+ Type[] callConvs = [typeof(CallConvSwift)];
+ Type t = Type.MakeFunctionPointerSignatureType(typeof(void), paramTypes, true, callConvs);
+
+ Type fnPtrRet = t.GetFunctionPointerReturnType();
+ Type[] fnPtrParams = t.GetFunctionPointerParameterTypes();
+ Type[] fnPtrCallConvs = t.GetFunctionPointerCallingConventions();
+ Assert.True(t.IsFunctionPointer);
+ Assert.True(t.IsUnmanagedFunctionPointer);
+ Assert.Equal(typeof(void).ToString(), fnPtrRet.ToString());
+ Assert.Equal(paramTypes.Select(t => t.ToString()), fnPtrParams.Select(t => t.ToString()));
+ Assert.Equal(callConvs.Select(t => t.ToString()), fnPtrCallConvs.Select(t => t.ToString()));
+ Assert.Equal(1, fnPtrRet.GetOptionalCustomModifiers().Length);
+ }
+
+ [Fact]
+ public static void MakeSignatureFunctionPointerTypeUnmanaged3()
+ {
+ Type[] paramTypes = [typeof(short)];
+ Type[] callConvs = [typeof(CallConvCdecl), typeof(CallConvSuppressGCTransition)];
+ Type t = Type.MakeFunctionPointerSignatureType(typeof(void), paramTypes, true, callConvs);
+ callConvs.Reverse();
+
+ Type fnPtrRet = t.GetFunctionPointerReturnType();
+ Type[] fnPtrParams = t.GetFunctionPointerParameterTypes();
+ Type[] fnPtrCallConvs = t.GetFunctionPointerCallingConventions();
+ Assert.True(t.IsFunctionPointer);
+ Assert.True(t.IsUnmanagedFunctionPointer);
+ Assert.Equal(typeof(void).ToString(), fnPtrRet.ToString());
+ Assert.Equal(paramTypes.Select(t => t.ToString()), fnPtrParams.Select(t => t.ToString()));
+ Assert.Equal(callConvs.Select(t => t.ToString()), fnPtrCallConvs.Select(t => t.ToString()));
+ Assert.Equal(2, fnPtrRet.GetOptionalCustomModifiers().Length);
+ }
+
+ private static void AssertFunctionPointerTypesEqual(Type expected, Type actual)
+ {
+ Assert.Equal(expected.ToString(), actual.ToString());
+ Assert.Equal(expected.IsFunctionPointer, actual.IsFunctionPointer);
+ Assert.Equal(expected.IsUnmanagedFunctionPointer, actual.IsUnmanagedFunctionPointer);
+ Assert.Equal(expected.GetFunctionPointerReturnType().UnderlyingSystemType, actual.GetFunctionPointerReturnType().UnderlyingSystemType);
+ Assert.Equal(expected.GetFunctionPointerReturnType().GetOptionalCustomModifiers(), actual.GetFunctionPointerReturnType().GetOptionalCustomModifiers());
+ Assert.Equal(expected.GetFunctionPointerParameterTypes().Select(p => p.UnderlyingSystemType), actual.GetFunctionPointerParameterTypes().Select(p => p.UnderlyingSystemType));
+ Assert.Equal(expected.GetFunctionPointerCallingConventions(), actual.GetFunctionPointerCallingConventions());
+ }
+
+ public static IEnumerable