Skip to content

Commit 7088708

Browse files
authored
Add reflection introspection support for function pointers (#81006)
1 parent 92fc7ec commit 7088708

File tree

114 files changed

+3863
-342
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

114 files changed

+3863
-342
lines changed

src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@
190190
<Compile Include="$(BclSourcesRoot)\System\Reflection\Metadata\MetadataUpdater.cs" />
191191
<Compile Include="$(BclSourcesRoot)\System\Reflection\MethodBase.CoreCLR.cs" />
192192
<Compile Include="$(BclSourcesRoot)\System\Reflection\MethodInvoker.CoreCLR.cs" />
193+
<Compile Include="$(BclSourcesRoot)\System\Reflection\ModifiedType.CoreCLR.cs" />
193194
<Compile Include="$(BclSourcesRoot)\System\Reflection\RtFieldInfo.cs" />
194195
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeAssembly.cs" />
195196
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeConstructorInfo.CoreCLR.cs" />
@@ -242,6 +243,9 @@
242243
<Compile Include="$(BclSourcesRoot)\System\TypeLoadException.CoreCLR.cs" />
243244
<Compile Include="$(BclSourcesRoot)\System\TypeNameParser.cs" />
244245
<Compile Include="$(BclSourcesRoot)\System\ValueType.cs" />
246+
<Compile Include="$(CommonPath)System\Collections\Generic\ArrayBuilder.cs">
247+
<Link>Common\System\Collections\Generic\ArrayBuilder.cs</Link>
248+
</Compile>
245249
</ItemGroup>
246250
<ItemGroup Condition="'$(FeatureComWrappers)' == 'true'">
247251
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\ComWrappers.cs" />
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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+
4+
namespace System.Reflection
5+
{
6+
internal partial class ModifiedType
7+
{
8+
internal struct TypeSignature
9+
{
10+
internal Signature? _signature;
11+
internal int _offset;
12+
}
13+
14+
internal Type GetTypeParameter(Type unmodifiedType, int index) =>
15+
Create(unmodifiedType, new TypeSignature()
16+
{
17+
_signature = _typeSignature._signature,
18+
_offset = _typeSignature._signature?.GetTypeParameterOffset(_typeSignature._offset, index) ?? 0
19+
});
20+
21+
internal SignatureCallingConvention GetCallingConventionFromFunctionPointer() =>
22+
_typeSignature._signature?.GetCallingConventionFromFunctionPointerAtOffset(_typeSignature._offset) ?? default;
23+
24+
internal static Type Create(Type unmodifiedType, Signature? signature, int parameterIndex = 0) =>
25+
Create(unmodifiedType, new TypeSignature()
26+
{
27+
_signature = signature,
28+
_offset = signature?.GetParameterOffset(parameterIndex) ?? 0
29+
});
30+
31+
private Type[] GetCustomModifiers(bool required) =>
32+
(_typeSignature._signature != null) ?
33+
_typeSignature._signature.GetCustomModifiersAtOffset(_typeSignature._offset, required) : Type.EmptyTypes;
34+
}
35+
}

src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,19 +252,23 @@ public override Type FieldType
252252
[MethodImpl(MethodImplOptions.NoInlining)]
253253
private RuntimeType InitializeFieldType()
254254
{
255-
return m_fieldType = new Signature(this, m_declaringType).FieldType;
255+
return m_fieldType = GetSignature().FieldType;
256256
}
257257

258258
public override Type[] GetRequiredCustomModifiers()
259259
{
260-
return new Signature(this, m_declaringType).GetCustomModifiers(1, true);
260+
return GetSignature().GetCustomModifiers(1, true);
261261
}
262262

263263
public override Type[] GetOptionalCustomModifiers()
264264
{
265-
return new Signature(this, m_declaringType).GetCustomModifiers(1, false);
265+
return GetSignature().GetCustomModifiers(1, false);
266266
}
267267

268+
internal Signature GetSignature() => new Signature(this, m_declaringType);
269+
270+
public override Type GetModifiedFieldType() =>
271+
ModifiedType.Create(FieldType, GetSignature());
268272
#endregion
269273
}
270274
}

src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,9 @@ public override Type[] GetOptionalCustomModifiers()
440440
m_signature.GetCustomModifiers(PositionImpl + 1, false);
441441
}
442442

443+
public override Type GetModifiedParameterType() =>
444+
ModifiedType.Create(unmodifiedType: ParameterType, m_signature, parameterIndex: PositionImpl + 1);
445+
443446
#endregion
444447

445448
#region ICustomAttributeProvider

src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ internal bool EqualsSig(RuntimePropertyInfo target)
7979
{
8080
// @Asymmetry - Legacy policy is to remove duplicate properties, including hidden properties.
8181
// The comparison is done by name and by sig. The EqualsSig comparison is expensive
82-
// but forutnetly it is only called when an inherited property is hidden by name or
82+
// but fortunately it is only called when an inherited property is hidden by name or
8383
// when an interfaces declare properies with the same signature.
8484
// Note that we intentionally don't resolve generic arguments so that we don't treat
8585
// signatures that only match in certain instantiations as duplicates. This has the
@@ -205,6 +205,8 @@ public override Type[] GetOptionalCustomModifiers()
205205
return Signature.GetCustomModifiers(0, false);
206206
}
207207

208+
public override Type GetModifiedPropertyType() => ModifiedType.Create(PropertyType, Signature);
209+
208210
internal object GetConstantValue(bool raw)
209211
{
210212
object? defaultValue = MdConstant.GetValue(GetRuntimeModule().MetadataImport, m_token, PropertyType.TypeHandle, raw);

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

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,12 @@ internal static bool IsSZArray(RuntimeType type)
159159
return corElemType == CorElementType.ELEMENT_TYPE_SZARRAY;
160160
}
161161

162+
internal static bool IsFunctionPointer(RuntimeType type)
163+
{
164+
CorElementType corElemType = GetCorElementType(type);
165+
return corElemType == CorElementType.ELEMENT_TYPE_FNPTR;
166+
}
167+
162168
internal static bool HasElementType(RuntimeType type)
163169
{
164170
CorElementType corElemType = GetCorElementType(type);
@@ -360,6 +366,12 @@ public ModuleHandle GetModuleHandle()
360366
[MethodImpl(MethodImplOptions.InternalCall)]
361367
internal static extern RuntimeMethodHandleInternal GetMethodAt(RuntimeType type, int slot);
362368

369+
[MethodImpl(MethodImplOptions.InternalCall)]
370+
internal static extern Type[] GetArgumentTypesFromFunctionPointer(RuntimeType type);
371+
372+
[MethodImpl(MethodImplOptions.InternalCall)]
373+
internal static extern bool IsUnmanagedFunctionPointer(RuntimeType type);
374+
363375
// This is managed wrapper for MethodTable::IntroducedMethodIterator
364376
internal struct IntroducedMethodEnumerator
365377
{
@@ -1557,36 +1569,13 @@ internal static MetadataImport GetMetadataImport(RuntimeModule module)
15571569

15581570
internal sealed unsafe class Signature
15591571
{
1560-
#region Definitions
1561-
internal enum MdSigCallingConvention : byte
1562-
{
1563-
Generics = 0x10,
1564-
HasThis = 0x20,
1565-
ExplicitThis = 0x40,
1566-
CallConvMask = 0x0F,
1567-
Default = 0x00,
1568-
C = 0x01,
1569-
StdCall = 0x02,
1570-
ThisCall = 0x03,
1571-
FastCall = 0x04,
1572-
Vararg = 0x05,
1573-
Field = 0x06,
1574-
LocalSig = 0x07,
1575-
Property = 0x08,
1576-
Unmanaged = 0x09,
1577-
GenericInst = 0x0A,
1578-
Max = 0x0B,
1579-
}
1580-
#endregion
1581-
15821572
#region FCalls
15831573
[MemberNotNull(nameof(m_arguments))]
15841574
[MemberNotNull(nameof(m_returnTypeORfieldType))]
15851575
[MethodImpl(MethodImplOptions.InternalCall)]
15861576
private extern void GetSignature(
15871577
void* pCorSig, int cCorSig,
15881578
RuntimeFieldHandleInternal fieldHandle, IRuntimeMethodInfo? methodHandle, RuntimeType? declaringType);
1589-
15901579
#endregion
15911580

15921581
#region Private Data Members
@@ -1645,8 +1634,20 @@ public Signature(void* pCorSig, int cCorSig, RuntimeType declaringType)
16451634
[MethodImpl(MethodImplOptions.InternalCall)]
16461635
internal static extern bool CompareSig(Signature sig1, Signature sig2);
16471636

1637+
internal Type[] GetCustomModifiers(int parameterIndex, bool required) =>
1638+
GetCustomModifiersAtOffset(GetParameterOffset(parameterIndex), required);
1639+
1640+
[MethodImpl(MethodImplOptions.InternalCall)]
1641+
internal extern int GetParameterOffset(int parameterIndex);
1642+
1643+
[MethodImpl(MethodImplOptions.InternalCall)]
1644+
internal extern int GetTypeParameterOffset(int offset, int index);
1645+
1646+
[MethodImpl(MethodImplOptions.InternalCall)]
1647+
internal extern SignatureCallingConvention GetCallingConventionFromFunctionPointerAtOffset(int offset);
1648+
16481649
[MethodImpl(MethodImplOptions.InternalCall)]
1649-
internal extern Type[] GetCustomModifiers(int position, bool required);
1650+
internal extern Type[] GetCustomModifiersAtOffset(int offset, bool required);
16501651
#endregion
16511652
}
16521653

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

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,7 +1010,7 @@ private void PopulateLiteralFields(Filter filter, RuntimeType declaringType, ref
10101010
#endregion
10111011

10121012
RuntimeFieldInfo runtimeFieldInfo =
1013-
new MdFieldInfo(tkField, fieldAttributes, declaringType.TypeHandle, m_runtimeTypeCache, bindingFlags);
1013+
new MdFieldInfo(tkField, fieldAttributes, declaringType.TypeHandle, m_runtimeTypeCache, bindingFlags);
10141014

10151015
list.Add(runtimeFieldInfo);
10161016
}
@@ -1535,12 +1535,33 @@ private MemberInfoCache<T> GetMemberCache<T>(ref MemberInfoCache<T>? m_cache)
15351535

15361536
#region Internal Members
15371537

1538+
1539+
/// <summary>
1540+
/// Generic cache for rare scenario specific data. It is used to cache either Enum names, Enum values,
1541+
/// the Activator cache or function pointer parameters.
1542+
/// </summary>
15381543
internal object? GenericCache
15391544
{
15401545
get => m_genericCache;
15411546
set => m_genericCache = value;
15421547
}
15431548

1549+
internal Type[] FunctionPointerReturnAndParameterTypes
1550+
{
1551+
get
1552+
{
1553+
Debug.Assert(m_runtimeType.IsFunctionPointer);
1554+
Type[]? value = (Type[]?)GenericCache;
1555+
if (value == null)
1556+
{
1557+
GenericCache = value = RuntimeTypeHandle.GetArgumentTypesFromFunctionPointer(m_runtimeType);
1558+
Debug.Assert(value.Length > 0);
1559+
}
1560+
1561+
return value;
1562+
}
1563+
}
1564+
15441565
internal bool DomainInitialized
15451566
{
15461567
get => m_bIsDomainInitialized;
@@ -1564,6 +1585,11 @@ internal bool DomainInitialized
15641585
if (!m_runtimeType.GetRootElementType().IsGenericTypeDefinition && m_runtimeType.ContainsGenericParameters)
15651586
return null;
15661587

1588+
// Exclude function pointer; it requires a grammar update and parsing support for Type.GetType() and friends.
1589+
// See https://learn.microsoft.com/dotnet/framework/reflection-and-codedom/specifying-fully-qualified-type-names.
1590+
if (m_runtimeType.IsFunctionPointer)
1591+
return null;
1592+
15671593
// No assembly.
15681594
return ConstructName(ref m_fullname, TypeNameFormatFlags.FormatNamespace | TypeNameFormatFlags.FormatFullInst);
15691595

@@ -1576,12 +1602,16 @@ internal bool DomainInitialized
15761602
}
15771603
}
15781604

1579-
internal string GetNameSpace()
1605+
internal string? GetNameSpace()
15801606
{
15811607
// @Optimization - Use ConstructName to populate m_namespace
15821608
if (m_namespace == null)
15831609
{
15841610
Type type = m_runtimeType;
1611+
1612+
if (type.IsFunctionPointer)
1613+
return null;
1614+
15851615
type = type.GetRootElementType();
15861616

15871617
while (type.IsNested)
@@ -3350,7 +3380,8 @@ public override string? AssemblyQualifiedName
33503380
{
33513381
string? fullname = FullName;
33523382

3353-
// FullName is null if this type contains generic parameters but is not a generic type definition.
3383+
// FullName is null if this type contains generic parameters but is not a generic type definition
3384+
// or if it is a function pointer.
33543385
if (fullname == null)
33553386
return null;
33563387

@@ -3362,7 +3393,7 @@ public override string? Namespace
33623393
{
33633394
get
33643395
{
3365-
string ns = Cache.GetNameSpace();
3396+
string? ns = Cache.GetNameSpace();
33663397
if (string.IsNullOrEmpty(ns))
33673398
{
33683399
return null;
@@ -3693,6 +3724,50 @@ private CheckValueStatus TryChangeTypeSpecial(
36933724

36943725
#endregion
36953726

3727+
#region Function Pointer
3728+
public override bool IsFunctionPointer => RuntimeTypeHandle.IsFunctionPointer(this);
3729+
public override bool IsUnmanagedFunctionPointer => RuntimeTypeHandle.IsUnmanagedFunctionPointer(this);
3730+
3731+
public override Type[] GetFunctionPointerCallingConventions()
3732+
{
3733+
if (!IsFunctionPointer)
3734+
{
3735+
throw new InvalidOperationException(SR.InvalidOperation_NotFunctionPointer);
3736+
}
3737+
3738+
// Requires a modified type to return the modifiers.
3739+
return EmptyTypes;
3740+
}
3741+
3742+
public override Type[] GetFunctionPointerParameterTypes()
3743+
{
3744+
if (!IsFunctionPointer)
3745+
{
3746+
throw new InvalidOperationException(SR.InvalidOperation_NotFunctionPointer);
3747+
}
3748+
3749+
Type[] parameters = Cache.FunctionPointerReturnAndParameterTypes;
3750+
Debug.Assert(parameters.Length > 0);
3751+
3752+
if (parameters.Length == 1)
3753+
{
3754+
return EmptyTypes;
3755+
}
3756+
3757+
return parameters.AsSpan(1).ToArray();
3758+
}
3759+
3760+
public override Type GetFunctionPointerReturnType()
3761+
{
3762+
if (!IsFunctionPointer)
3763+
{
3764+
throw new InvalidOperationException(SR.InvalidOperation_NotFunctionPointer);
3765+
}
3766+
3767+
return Cache.FunctionPointerReturnAndParameterTypes[0];
3768+
}
3769+
#endregion
3770+
36963771
#endregion
36973772

36983773
public override string ToString() => GetCachedName(TypeNameKind.ToString)!;

src/coreclr/inc/sigparser.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
//
44
// sigparser.h
5-
//
6-
7-
//
85

96
#ifndef _H_SIGPARSER
107
#define _H_SIGPARSER
@@ -716,7 +713,7 @@ class SigParser
716713
// the arguments.
717714
//------------------------------------------------------------------------
718715
__checkReturn
719-
HRESULT SkipMethodHeaderSignature(uint32_t *pcArgs);
716+
HRESULT SkipMethodHeaderSignature(uint32_t *pcArgs, bool skipReturnType = true);
720717

721718
//------------------------------------------------------------------------
722719
// Skip a sub signature (as immediately follows an ELEMENT_TYPE_FNPTR).

src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@
153153
<Compile Include="System\Reflection\MethodBase.NativeAot.cs" />
154154
<Compile Include="System\Reflection\Metadata\AssemblyExtensions.cs" />
155155
<Compile Include="System\Reflection\Metadata\MetadataUpdater.cs" />
156+
<Compile Include="System\Reflection\ModifiedType.NativeAot.cs" />
156157
<Compile Include="System\Runtime\InteropServices\CriticalHandle.NativeAot.cs" />
157158
<Compile Include="System\Activator.NativeAot.cs" />
158159
<Compile Include="System\AppContext.NativeAot.cs" />
@@ -487,7 +488,7 @@
487488
<Compile Include="System\Reflection\Runtime\TypeInfos\NativeFormat\NativeFormatRuntimeTypeInfo.CoreGetDeclared.cs" />
488489
<Compile Include="System\Reflection\Runtime\TypeInfos\RuntimeArrayTypeInfo.cs" />
489490
<Compile Include="System\Reflection\Runtime\TypeInfos\RuntimeByRefTypeInfo.cs" />
490-
<Compile Include="System\Reflection\Runtime\TypeInfos\RuntimeClsIdTypeInfo.cs" Condition="'$(FeatureComInterop)' == 'true'"/>
491+
<Compile Include="System\Reflection\Runtime\TypeInfos\RuntimeClsIdTypeInfo.cs" Condition="'$(FeatureComInterop)' == 'true'" />
491492
<Compile Include="System\Reflection\Runtime\TypeInfos\RuntimeClsIdTypeInfo.UnificationKey.cs" Condition="'$(FeatureComInterop)' == 'true'" />
492493
<Compile Include="System\Reflection\Runtime\TypeInfos\RuntimeConstructedGenericTypeInfo.cs" />
493494
<Compile Include="System\Reflection\Runtime\TypeInfos\RuntimeConstructedGenericTypeInfo.UnificationKey.cs" />
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
4+
namespace System.Reflection
5+
{
6+
internal partial class ModifiedType
7+
{
8+
internal struct TypeSignature
9+
{
10+
}
11+
12+
#pragma warning disable IDE0060
13+
internal Type GetTypeParameter(Type unmodifiedType, int index) => throw new NotSupportedException();
14+
15+
internal SignatureCallingConvention GetCallingConventionFromFunctionPointer() => throw new NotSupportedException();
16+
17+
private Type[] GetCustomModifiers(bool required) => throw new NotSupportedException();
18+
#pragma warning restore IDE0060
19+
}
20+
}

0 commit comments

Comments
 (0)