Skip to content

Commit 9f2179d

Browse files
jgh07Copilotjkotas
authored
New function pointer APIs (#123819)
This implements #75348. ``MakeFunctionPointerType`` is only implemented for CoreCLR, a Mono implementation will follow. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Jan Kotas <jkotas@microsoft.com>
1 parent f62984c commit 9f2179d

37 files changed

Lines changed: 1400 additions & 37 deletions

src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILGenerator.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,18 @@ public override void EmitCalli(OpCode opcode, CallingConvention unmanagedCallCon
244244
PutInteger4(token);
245245
}
246246

247+
/// <inheritdoc/>
248+
public override void EmitCalli(Type functionPointerType)
249+
{
250+
ArgumentNullException.ThrowIfNull(functionPointerType);
251+
252+
if (!functionPointerType.IsFunctionPointer)
253+
throw new ArgumentException(SR.Argument_MustBeFunctionPointer, nameof(functionPointerType));
254+
255+
SignatureHelper sig = SignatureHelper.GetMethodSigHelper(m_scope, functionPointerType);
256+
Emit(OpCodes.Calli, sig);
257+
}
258+
247259
public override void EmitCall(OpCode opcode, MethodInfo methodInfo, Type[]? optionalParameterTypes)
248260
{
249261
ArgumentNullException.ThrowIfNull(methodInfo);
@@ -299,6 +311,10 @@ public override void Emit(OpCode opcode, SignatureHelper signature)
299311
{
300312
Debug.Assert(opcode.Equals(OpCodes.Calli),
301313
"Unexpected opcode encountered for StackBehaviour VarPop.");
314+
315+
// If there is a non-void return type, push one.
316+
if (signature.ReturnType is Type retType && retType != typeof(void))
317+
stackchange++;
302318
// Pop the arguments..
303319
stackchange -= signature.ArgumentCount;
304320
// Pop native function pointer off the stack.

src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeILGenerator.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,10 @@ public override void Emit(OpCode opcode, SignatureHelper signature)
658658
{
659659
Debug.Assert(opcode.Equals(OpCodes.Calli),
660660
"Unexpected opcode encountered for StackBehaviour VarPop.");
661+
662+
// If there is a non-void return type, push one.
663+
if (signature.ReturnType is Type retType && retType != typeof(void))
664+
stackchange++;
661665
// Pop the arguments..
662666
stackchange -= signature.ArgumentCount;
663667
// Pop native function pointer off the stack.

src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/SignatureHelper.cs

Lines changed: 98 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ internal static SignatureHelper GetMethodSigHelper(
7070
sigHelp = new SignatureHelper(scope, intCall, cGenericParam, returnType,
7171
requiredReturnTypeCustomModifiers, optionalReturnTypeCustomModifiers);
7272

73+
sigHelp.m_returnType = returnType;
7374
sigHelp.AddArguments(parameterTypes, requiredParameterTypeCustomModifiers, optionalParameterTypeCustomModifiers);
7475

7576
return sigHelp;
@@ -105,6 +106,52 @@ internal static SignatureHelper GetMethodSigHelper(Module? mod, CallingConventio
105106
return new SignatureHelper(mod, intCall, returnType, null, null);
106107
}
107108

109+
internal static SignatureHelper GetMethodSigHelper(DynamicScope scope, Type functionPointerType)
110+
{
111+
Debug.Assert(functionPointerType.IsFunctionPointer);
112+
113+
Type retType = functionPointerType.GetFunctionPointerReturnType();
114+
Type[] retTypeModReqs = retType.GetRequiredCustomModifiers();
115+
Type[] retTypeModOpts = retType.GetOptionalCustomModifiers();
116+
Type[] paramTypes = functionPointerType.GetFunctionPointerParameterTypes();
117+
Type[][] paramModReqs = new Type[paramTypes.Length][];
118+
Type[][] paramModOpts = new Type[paramTypes.Length][];
119+
120+
retType = retType.UnderlyingSystemType;
121+
122+
for (int i = 0; i < paramTypes.Length; i++)
123+
{
124+
paramModReqs[i] = paramTypes[i].GetRequiredCustomModifiers();
125+
paramModOpts[i] = paramTypes[i].GetOptionalCustomModifiers();
126+
paramTypes[i] = paramTypes[i].UnderlyingSystemType;
127+
}
128+
129+
MdSigCallingConvention callConv = MdSigCallingConvention.Default;
130+
131+
if (functionPointerType.IsUnmanagedFunctionPointer)
132+
{
133+
callConv = MdSigCallingConvention.Unmanaged;
134+
135+
if (functionPointerType.GetFunctionPointerCallingConventions() is { Length: 1 } conventions)
136+
{
137+
callConv = conventions[0].FullName switch
138+
{
139+
"System.Runtime.CompilerServices.CallConvCdecl" => MdSigCallingConvention.C,
140+
"System.Runtime.CompilerServices.CallConvStdcall" => MdSigCallingConvention.StdCall,
141+
"System.Runtime.CompilerServices.CallConvThiscall" => MdSigCallingConvention.ThisCall,
142+
"System.Runtime.CompilerServices.CallConvFastcall" => MdSigCallingConvention.FastCall,
143+
_ => MdSigCallingConvention.Unmanaged
144+
};
145+
}
146+
}
147+
148+
SignatureHelper sig = new(null, callConv);
149+
sig.m_returnType = retType;
150+
sig.AddDynamicArgument(scope, retType, retTypeModReqs, retTypeModOpts, false);
151+
sig.AddArguments(scope, paramTypes, paramModReqs, paramModOpts);
152+
return sig;
153+
}
154+
108155
public static SignatureHelper GetLocalVarSigHelper()
109156
{
110157
return GetLocalVarSigHelper(null);
@@ -179,6 +226,7 @@ internal static SignatureHelper GetTypeSigToken(Module module, Type type)
179226
private ModuleBuilder? m_module;
180227
private bool m_sigDone;
181228
private int m_argCount; // tracking number of arguments in the signature
229+
private Type? m_returnType;
182230
#endregion
183231

184232
#region Constructor
@@ -197,6 +245,7 @@ private SignatureHelper(Module? mod, MdSigCallingConvention callingConvention, i
197245
if (callingConvention == MdSigCallingConvention.Field)
198246
throw new ArgumentException(SR.Argument_BadFieldSig);
199247

248+
m_returnType = returnType;
200249
AddOneArgTypeHelper(returnType, requiredCustomModifiers, optionalCustomModifiers);
201250
}
202251

@@ -320,8 +369,8 @@ private void AddOneArgTypeHelper(Type clsArgument, Type[]? requiredCustomModifie
320369
AddOneArgTypeHelper(clsArgument);
321370
}
322371

323-
private void AddOneArgTypeHelper(Type clsArgument) { AddOneArgTypeHelperWorker(clsArgument, false); }
324-
private void AddOneArgTypeHelperWorker(Type clsArgument, bool lastWasGenericInst)
372+
private void AddOneArgTypeHelper(Type clsArgument, DynamicScope? scope = null) { AddOneArgTypeHelperWorker(clsArgument, false, scope); }
373+
private void AddOneArgTypeHelperWorker(Type clsArgument, bool lastWasGenericInst, DynamicScope? scope)
325374
{
326375
if (clsArgument.IsGenericParameter)
327376
{
@@ -336,14 +385,14 @@ private void AddOneArgTypeHelperWorker(Type clsArgument, bool lastWasGenericInst
336385
{
337386
AddElementType(CorElementType.ELEMENT_TYPE_GENERICINST);
338387

339-
AddOneArgTypeHelperWorker(clsArgument.GetGenericTypeDefinition(), true);
388+
AddOneArgTypeHelperWorker(clsArgument.GetGenericTypeDefinition(), true, scope);
340389

341390
Type[] args = clsArgument.GetGenericArguments();
342391

343392
AddData(args.Length);
344393

345394
foreach (Type t in args)
346-
AddOneArgTypeHelper(t);
395+
AddOneArgTypeHelper(t, scope);
347396
}
348397
else if (clsArgument is RuntimeTypeBuilder clsBuilder)
349398
{
@@ -394,26 +443,26 @@ private void AddOneArgTypeHelperWorker(Type clsArgument, bool lastWasGenericInst
394443
{
395444
AddElementType(CorElementType.ELEMENT_TYPE_BYREF);
396445
clsArgument = clsArgument.GetElementType()!;
397-
AddOneArgTypeHelper(clsArgument);
446+
AddOneArgTypeHelper(clsArgument, scope);
398447
}
399448
else if (clsArgument.IsPointer)
400449
{
401450
AddElementType(CorElementType.ELEMENT_TYPE_PTR);
402-
AddOneArgTypeHelper(clsArgument.GetElementType()!);
451+
AddOneArgTypeHelper(clsArgument.GetElementType()!, scope);
403452
}
404453
else if (clsArgument.IsArray)
405454
{
406455
if (clsArgument.IsSZArray)
407456
{
408457
AddElementType(CorElementType.ELEMENT_TYPE_SZARRAY);
409458

410-
AddOneArgTypeHelper(clsArgument.GetElementType()!);
459+
AddOneArgTypeHelper(clsArgument.GetElementType()!, scope);
411460
}
412461
else
413462
{
414463
AddElementType(CorElementType.ELEMENT_TYPE_ARRAY);
415464

416-
AddOneArgTypeHelper(clsArgument.GetElementType()!);
465+
AddOneArgTypeHelper(clsArgument.GetElementType()!, scope);
417466

418467
// put the rank information
419468
int rank = clsArgument.GetArrayRank();
@@ -424,6 +473,25 @@ private void AddOneArgTypeHelperWorker(Type clsArgument, bool lastWasGenericInst
424473
AddData(0);
425474
}
426475
}
476+
else if (clsArgument.IsFunctionPointer)
477+
{
478+
if (scope == null)
479+
throw new NotSupportedException(SR.NotSupported_FunctionPointerSignature);
480+
481+
AddData((int)CorElementType.ELEMENT_TYPE_FNPTR);
482+
SignatureHelper sig = GetMethodSigHelper(scope, clsArgument);
483+
byte[] bytes = sig.GetSignature();
484+
485+
if (m_currSig + bytes.Length > m_signature.Length)
486+
{
487+
m_signature = ExpandArray(m_signature, m_signature.Length + bytes.Length);
488+
}
489+
490+
for (int i = 0; i < bytes.Length; i++)
491+
{
492+
m_signature[m_currSig++] = bytes[i];
493+
}
494+
}
427495
else
428496
{
429497
CorElementType type = CorElementType.ELEMENT_TYPE_MAX;
@@ -649,6 +717,7 @@ private void SetNumberOfSignatureElements(bool forceCopy)
649717

650718
#region Internal Members
651719
internal int ArgumentCount => m_argCount;
720+
internal Type? ReturnType => m_returnType;
652721

653722
internal static bool IsSimpleType(CorElementType type)
654723
{
@@ -729,9 +798,10 @@ internal byte[] InternalGetSignatureArray()
729798
return temp;
730799
}
731800

732-
internal void AddDynamicArgument(DynamicScope dynamicScope, Type clsArgument, Type[]? requiredCustomModifiers, Type[]? optionalCustomModifiers)
801+
internal void AddDynamicArgument(DynamicScope dynamicScope, Type clsArgument, Type[]? requiredCustomModifiers, Type[]? optionalCustomModifiers, bool incrementArgCount = true)
733802
{
734-
IncrementArgCounts();
803+
if (incrementArgCount)
804+
IncrementArgCounts();
735805

736806
Debug.Assert(clsArgument != null);
737807

@@ -750,6 +820,9 @@ internal void AddDynamicArgument(DynamicScope dynamicScope, Type clsArgument, Ty
750820
if (t.ContainsGenericParameters)
751821
throw new ArgumentException(SR.Argument_GenericsInvalid, nameof(optionalCustomModifiers));
752822

823+
if (t.IsFunctionPointer)
824+
throw new ArgumentException(SR.Argument_FunctionPointersInvalid, nameof(optionalCustomModifiers));
825+
753826
AddElementType(CorElementType.ELEMENT_TYPE_CMOD_OPT);
754827

755828
int token = dynamicScope.GetTokenFor(rtType.TypeHandle);
@@ -773,6 +846,9 @@ internal void AddDynamicArgument(DynamicScope dynamicScope, Type clsArgument, Ty
773846
if (t.ContainsGenericParameters)
774847
throw new ArgumentException(SR.Argument_GenericsInvalid, nameof(requiredCustomModifiers));
775848

849+
if (t.IsFunctionPointer)
850+
throw new ArgumentException(SR.Argument_FunctionPointersInvalid, nameof(requiredCustomModifiers));
851+
776852
AddElementType(CorElementType.ELEMENT_TYPE_CMOD_REQD);
777853

778854
int token = dynamicScope.GetTokenFor(rtType.TypeHandle);
@@ -781,7 +857,18 @@ internal void AddDynamicArgument(DynamicScope dynamicScope, Type clsArgument, Ty
781857
}
782858
}
783859

784-
AddOneArgTypeHelper(clsArgument);
860+
AddOneArgTypeHelper(clsArgument, dynamicScope);
861+
}
862+
863+
internal void AddArguments(DynamicScope dynamicScope, Type[]? arguments, Type[][]? requiredCustomModifiers, Type[][]? optionalCustomModifiers)
864+
{
865+
if (arguments is null)
866+
return;
867+
868+
for (int i = 0; i < arguments.Length; i++)
869+
{
870+
AddDynamicArgument(dynamicScope, arguments[i], requiredCustomModifiers?[i], optionalCustomModifiers?[i]);
871+
}
785872
}
786873

787874
#endregion

src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,29 @@ internal RuntimeType MakeByRef()
813813
return type!;
814814
}
815815

816+
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeTypeHandle_MakeFunctionPointer")]
817+
private static partial void MakeFunctionPointer(nint* retAndParamTypes, int numArgs, [MarshalAs(UnmanagedType.Bool)] bool isUnmanaged, ObjectHandleOnStack type);
818+
819+
internal RuntimeType MakeFunctionPointer(Type[] parameterTypes, bool isUnmanaged)
820+
{
821+
int count = 1 + parameterTypes.Length;
822+
nint[] retAndParamTypeHandles = new nint[count];
823+
824+
retAndParamTypeHandles[0] = GetNativeHandle().Value;
825+
for (int i = 0; i < parameterTypes.Length; i++)
826+
retAndParamTypeHandles[i + 1] = parameterTypes[i].TypeHandle.Value;
827+
828+
RuntimeType? type = null;
829+
fixed (nint* pRetAndParamTypeHandles = retAndParamTypeHandles)
830+
{
831+
MakeFunctionPointer(pRetAndParamTypeHandles, parameterTypes.Length, isUnmanaged, ObjectHandleOnStack.Create(ref type));
832+
}
833+
834+
GC.KeepAlive(m_type);
835+
GC.KeepAlive(parameterTypes);
836+
return type!;
837+
}
838+
816839
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeTypeHandle_MakePointer")]
817840
private static partial void MakePointer(QCallTypeHandle handle, ObjectHandleOnStack type);
818841

src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3729,6 +3729,26 @@ public override Type MakeArrayType(int rank)
37293729
return new RuntimeTypeHandle(this).MakeArray(rank);
37303730
}
37313731

3732+
public override Type MakeFunctionPointerType(Type[]? parameterTypes, bool isUnmanaged = false)
3733+
{
3734+
if (this.IsGenericTypeDefinition)
3735+
throw new InvalidOperationException(SR.Format(SR.FunctionPointer_ReturnTypeInvalid, this));
3736+
3737+
parameterTypes = (parameterTypes != null) ? (Type[])parameterTypes.Clone() : [];
3738+
foreach (Type? paramType in parameterTypes)
3739+
{
3740+
ArgumentNullException.ThrowIfNull(paramType, nameof(parameterTypes));
3741+
3742+
if (paramType is not RuntimeType)
3743+
return Type.MakeFunctionPointerSignatureType(this, parameterTypes, isUnmanaged);
3744+
3745+
if (paramType == typeof(void) || paramType.IsGenericTypeDefinition)
3746+
throw new ArgumentException(SR.Format(SR.FunctionPointer_ParameterInvalid, paramType), nameof(parameterTypes));
3747+
}
3748+
3749+
return new RuntimeTypeHandle(this).MakeFunctionPointer(parameterTypes, isUnmanaged);
3750+
}
3751+
37323752
public override StructLayoutAttribute? StructLayoutAttribute => PseudoCustomAttribute.GetStructLayoutCustomAttribute(this);
37333753

37343754
#endregion

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/TypeUnifier.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ public static RuntimeTypeInfo GetPointerType(this RuntimeTypeInfo targetType)
5757
return RuntimePointerTypeInfo.GetPointerTypeInfo(targetType);
5858
}
5959

60+
public static RuntimeTypeInfo GetFunctionPointerType(this RuntimeTypeInfo returnType, RuntimeTypeInfo[] parameterTypes, bool isUnmanaged)
61+
{
62+
return RuntimeFunctionPointerTypeInfo.GetFunctionPointerTypeInfo(returnType, parameterTypes, isUnmanaged);
63+
}
64+
6065
public static RuntimeTypeInfo GetConstructedGenericTypeNoConstraintCheck(this RuntimeTypeInfo genericTypeDefinition, RuntimeTypeInfo[] genericTypeArguments)
6166
{
6267
return RuntimeConstructedGenericTypeInfo.GetRuntimeConstructedGenericTypeInfoNoConstraintCheck(genericTypeDefinition, genericTypeArguments);

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,31 @@ public Type MakeArrayType(int rank)
414414
return this.GetMultiDimArrayType(rank).ToType();
415415
}
416416

417+
public Type MakeFunctionPointerType(Type[]? parameterTypes, bool isUnmanaged = false)
418+
{
419+
if (this.IsGenericTypeDefinition)
420+
throw new InvalidOperationException(SR.Format(SR.FunctionPointer_ReturnTypeInvalid, this));
421+
422+
parameterTypes ??= [];
423+
RuntimeTypeInfo[] runtimeParameterTypes = new RuntimeTypeInfo[parameterTypes.Length];
424+
425+
for (int i = 0; i < parameterTypes.Length; i++)
426+
{
427+
Type? paramType = parameterTypes[i];
428+
ArgumentNullException.ThrowIfNull(paramType, nameof(parameterTypes));
429+
430+
if (paramType is not RuntimeType rtType)
431+
return Type.MakeFunctionPointerSignatureType(this.ToType(), parameterTypes, isUnmanaged);
432+
433+
if (rtType == typeof(void) || rtType.IsGenericTypeDefinition)
434+
throw new ArgumentException(SR.Format(SR.FunctionPointer_ParameterInvalid, rtType), nameof(parameterTypes));
435+
436+
runtimeParameterTypes[i] = rtType.GetRuntimeTypeInfo();
437+
}
438+
439+
return this.GetFunctionPointerType(runtimeParameterTypes, isUnmanaged).ToType();
440+
}
441+
417442
public Type MakePointerType()
418443
{
419444
return this.GetPointerType().ToType();

src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.NativeAot.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -856,6 +856,9 @@ public override Type MakeArrayType()
856856
public override Type MakeArrayType(int rank)
857857
=> GetRuntimeTypeInfo().MakeArrayType(rank);
858858

859+
public override Type MakeFunctionPointerType(Type[]? parameterTypes, bool isUnmanaged = false)
860+
=> GetRuntimeTypeInfo().MakeFunctionPointerType(parameterTypes, isUnmanaged);
861+
859862
[RequiresDynamicCode("The native code for this instantiation might not be available at runtime.")]
860863
[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.")]
861864
public override Type MakeGenericType(params Type[] instantiation)

src/coreclr/vm/qcallentrypoints.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ static const Entry s_QCall[] =
132132
DllImportEntry(RuntimeTypeHandle_MakeByRef)
133133
DllImportEntry(RuntimeTypeHandle_MakeSZArray)
134134
DllImportEntry(RuntimeTypeHandle_MakeArray)
135+
DllImportEntry(RuntimeTypeHandle_MakeFunctionPointer)
135136
DllImportEntry(RuntimeTypeHandle_GetConstraints)
136137
DllImportEntry(RuntimeTypeHandle_GetArgumentTypesFromFunctionPointer)
137138
DllImportEntry(RuntimeTypeHandle_GetAssemblySlow)

0 commit comments

Comments
 (0)