Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add reflection introspection support for function pointers #81006

Merged
merged 24 commits into from
Feb 16, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
173b84d
Add reflection introspection support for function pointers
steveharter May 31, 2022
e546ee2
Fix compiler issue; test MLC to NetFramework
steveharter Jan 23, 2023
36e07d6
Fix compile issue; prep for review
steveharter Jan 23, 2023
e755c1c
Compile issue with MLC under certain conditions
steveharter Jan 23, 2023
b3b64ff
compile issue cont'd; lazy load mods on root
steveharter Jan 23, 2023
3aef789
compile issues cont'd; prepare for review
steveharter Jan 24, 2023
f76c75d
Remove Node back reference; fix edge case test failure
steveharter Jan 24, 2023
73b15b3
Increase test coverage; add missing recursive check
steveharter Jan 24, 2023
1361142
Various feedback; support mods on generic type parameters
steveharter Jan 26, 2023
520a3f5
Merge branch 'main' of https://github.com/steveharter/runtime into Fc…
steveharter Jan 26, 2023
64a987e
Fix MLC edge case; Native AOT test enable\disable
steveharter Jan 26, 2023
5f8a81e
Native AOT test enable\disable; add a generic test
steveharter Jan 26, 2023
2df9d2b
Native AOT test enable\disable; fix missing overload
steveharter Jan 26, 2023
0a6ff32
Add TODOs based on review discussion; MLC field optimization
steveharter Jan 27, 2023
784df92
Merge branch 'main' into FcnPtr
jkotas Feb 1, 2023
8c06d3c
Replace nested signature indices by TypeSignature type
jkotas Jan 31, 2023
f40e506
Move tests into separate classes; other misc non-functional
steveharter Feb 13, 2023
4610e93
Throw NSE for modified type members than may return an unmodified type
steveharter Feb 14, 2023
ca45bd8
Merge branch 'main' of https://github.com/dotnet/runtime into FcnPtr
steveharter Feb 14, 2023
70529f0
Throw NSE on modified type Equals() and GetHashCode()
steveharter Feb 15, 2023
9770a2d
Fix tests for WASI
steveharter Feb 15, 2023
14a9007
Remove unnecessary high overhead tests
steveharter Feb 16, 2023
69ccd75
Merge branch 'main' of https://github.com/dotnet/runtime into FcnPtr
steveharter Feb 16, 2023
f76331c
Fix merge error
steveharter Feb 16, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@
<Compile Include="$(BclSourcesRoot)\System\Reflection\Metadata\MetadataUpdater.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\MethodBase.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\MethodInvoker.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\ModifiedFunctionPointerType.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\ModifiedType.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RtFieldInfo.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeAssembly.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeConstructorInfo.CoreCLR.cs" />
Expand Down Expand Up @@ -243,6 +245,9 @@
<Compile Include="$(BclSourcesRoot)\System\TypeLoadException.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\TypeNameParser.cs" />
<Compile Include="$(BclSourcesRoot)\System\ValueType.cs" />
<Compile Include="$(CommonPath)System\Collections\Generic\ArrayBuilder.cs">
<Link>Common\System\Collections\Generic\ArrayBuilder.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup Condition="'$(FeatureComWrappers)' == 'true'">
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\ComWrappers.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,6 @@

namespace System.Reflection
{
[Flags]
internal enum MdSigCallingConvention : byte
{
CallConvMask = 0x0f, // Calling convention is bottom 4 bits

Default = 0x00,
C = 0x01,
StdCall = 0x02,
ThisCall = 0x03,
FastCall = 0x04,
Vararg = 0x05,
Field = 0x06,
LocalSig = 0x07,
Property = 0x08,
Unmanaged = 0x09,
GenericInst = 0x0a, // generic method instantiation

Generic = 0x10, // Generic method sig with explicit number of type arguments (precedes ordinary parameter count)
HasThis = 0x20, // Top bit indicates a 'this' parameter
ExplicitThis = 0x40, // This parameter is explicitly in the signature
}

[Flags]
internal enum PInvokeAttributes
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// 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 partial class ModifiedFunctionPointerType
{
private MdSigCallingConvention GetCallingConvention()
{
Signature? signature = GetSignature();
if (signature is not null)
{
return (MdSigCallingConvention)signature.GetCallingConventionFromFunctionPointer(RootSignatureParameterIndex, NestedSignatureIndex);
}

return MdSigCallingConvention.Default;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// 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 partial class ModifiedType
{
/// <summary>
/// Called from FieldInfo, PropertyInfo and ParameterInfo to create a modified type tree.
/// </summary>
public static ModifiedType CreateRoot(
Type unmodifiedType,
object? signatureProvider,
int rootSignatureParameterIndex) => Create(
unmodifiedType,
signatureProvider,
rootSignatureParameterIndex,
nestedSignatureIndex: -1,
nestedSignatureParameterIndex: -1,
isRoot: true);

private Type[] GetCustomModifiers(bool required)
{
Type[] modifiers = EmptyTypes;
Signature? signature = GetSignature();
if (signature is not null)
{
if (IsRoot)
{
// For a root node, which is the original field\parameter\property Type, get the root-level modifiers.
modifiers = signature.GetCustomModifiers(RootSignatureParameterIndex, required);
}
else if (NestedSignatureParameterIndex >= 0)
{
modifiers = signature.GetCustomModifiers(RootSignatureParameterIndex, required, NestedSignatureIndex, NestedSignatureParameterIndex);
}
}

return modifiers;
}

internal Signature? GetSignature()
{
return (Signature?)SignatureProvider; // Signature is a CoreClr-specific class.
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -252,19 +252,23 @@ public override Type FieldType
[MethodImpl(MethodImplOptions.NoInlining)]
private RuntimeType InitializeFieldType()
{
return m_fieldType = new Signature(this, m_declaringType).FieldType;
return m_fieldType = GetSignature().FieldType;
}

public override Type[] GetRequiredCustomModifiers()
{
return new Signature(this, m_declaringType).GetCustomModifiers(1, true);
return GetSignature().GetCustomModifiers(1, true);
}

public override Type[] GetOptionalCustomModifiers()
{
return new Signature(this, m_declaringType).GetCustomModifiers(1, false);
return GetSignature().GetCustomModifiers(1, false);
}

internal Signature GetSignature() => new Signature(this, m_declaringType);

public override Type GetModifiedFieldType() =>
ModifiedType.CreateRoot(FieldType, GetSignature(), rootSignatureParameterIndex: 1);
#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,12 @@ public override Type[] GetOptionalCustomModifiers()
m_signature.GetCustomModifiers(PositionImpl + 1, false);
}

public override Type GetModifiedParameterType() =>
ModifiedType.CreateRoot(
unmodifiedType: ParameterType,
m_signature,
rootSignatureParameterIndex: PositionImpl + 1);

#endregion

#region ICustomAttributeProvider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ internal bool EqualsSig(RuntimePropertyInfo target)
{
// @Asymmetry - Legacy policy is to remove duplicate properties, including hidden properties.
// The comparison is done by name and by sig. The EqualsSig comparison is expensive
// but forutnetly it is only called when an inherited property is hidden by name or
// but fortunately it is only called when an inherited property is hidden by name or
// when an interfaces declare properies with the same signature.
// Note that we intentionally don't resolve generic arguments so that we don't treat
// signatures that only match in certain instantiations as duplicates. This has the
Expand Down Expand Up @@ -205,6 +205,11 @@ public override Type[] GetOptionalCustomModifiers()
return Signature.GetCustomModifiers(0, false);
}

public override Type GetModifiedPropertyType() => ModifiedType.CreateRoot(
PropertyType,
Signature,
rootSignatureParameterIndex: 0);

internal object GetConstantValue(bool raw)
{
object? defaultValue = MdConstant.GetValue(GetRuntimeModule().MetadataImport, m_token, PropertyType.TypeHandle, raw);
Expand Down
43 changes: 19 additions & 24 deletions src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,12 @@ internal static bool IsSZArray(RuntimeType type)
return corElemType == CorElementType.ELEMENT_TYPE_SZARRAY;
}

internal static bool IsFunctionPointer(RuntimeType type)
{
CorElementType corElemType = GetCorElementType(type);
return corElemType == CorElementType.ELEMENT_TYPE_FNPTR;
}

internal static bool HasElementType(RuntimeType type)
{
CorElementType corElemType = GetCorElementType(type);
Expand Down Expand Up @@ -360,6 +366,12 @@ public ModuleHandle GetModuleHandle()
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern RuntimeMethodHandleInternal GetMethodAt(RuntimeType type, int slot);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern Type[] GetArgumentTypesFromFunctionPointer(RuntimeType type);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool IsUnmanagedFunctionPointer(RuntimeType type);

// This is managed wrapper for MethodTable::IntroducedMethodIterator
internal struct IntroducedMethodEnumerator
{
Expand Down Expand Up @@ -1557,36 +1569,13 @@ internal static MetadataImport GetMetadataImport(RuntimeModule module)

internal sealed unsafe class Signature
{
#region Definitions
internal enum MdSigCallingConvention : byte
{
Generics = 0x10,
HasThis = 0x20,
ExplicitThis = 0x40,
CallConvMask = 0x0F,
Default = 0x00,
C = 0x01,
StdCall = 0x02,
ThisCall = 0x03,
FastCall = 0x04,
Vararg = 0x05,
Field = 0x06,
LocalSig = 0x07,
Property = 0x08,
Unmanaged = 0x09,
GenericInst = 0x0A,
Max = 0x0B,
}
#endregion

#region FCalls
[MemberNotNull(nameof(m_arguments))]
[MemberNotNull(nameof(m_returnTypeORfieldType))]
[MethodImpl(MethodImplOptions.InternalCall)]
private extern void GetSignature(
void* pCorSig, int cCorSig,
RuntimeFieldHandleInternal fieldHandle, IRuntimeMethodInfo? methodHandle, RuntimeType? declaringType);

#endregion

#region Private Data Members
Expand Down Expand Up @@ -1646,7 +1635,13 @@ public Signature(void* pCorSig, int cCorSig, RuntimeType declaringType)
internal static extern bool CompareSig(Signature sig1, Signature sig2);

[MethodImpl(MethodImplOptions.InternalCall)]
internal extern Type[] GetCustomModifiers(int position, bool required);
internal extern Type[] GetCustomModifiers(int rootSignatureParameterIndex, bool required, int nestedSignatureIndex = -1, int nestedSignatureParameterIndex = -1);

[MethodImpl(MethodImplOptions.InternalCall)]
internal extern byte GetCallingConventionFromFunctionPointer(int rootSignatureParameterIndex, int nestedSignatureIndex);

[MethodImpl(MethodImplOptions.InternalCall)]
internal extern bool IsUnmanagedFunctionPointer();
#endregion
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1010,7 +1010,7 @@ private void PopulateLiteralFields(Filter filter, RuntimeType declaringType, ref
#endregion

RuntimeFieldInfo runtimeFieldInfo =
new MdFieldInfo(tkField, fieldAttributes, declaringType.TypeHandle, m_runtimeTypeCache, bindingFlags);
new MdFieldInfo(tkField, fieldAttributes, declaringType.TypeHandle, m_runtimeTypeCache, bindingFlags);

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

#region Internal Members


/// <summary>
/// Generic cache for rare scenario specific data. It is used to cache either Enum names, Enum values,
/// the Activator cache or function pointer parameters.
/// </summary>
internal object? GenericCache
{
get => m_genericCache;
set => m_genericCache = value;
}

internal Type[] FunctionPointerReturnAndParameterTypes
{
get
{
Debug.Assert(m_runtimeType.IsFunctionPointer);
Type[]? value = (Type[]?)GenericCache;
if (value == null)
{
GenericCache = value = RuntimeTypeHandle.GetArgumentTypesFromFunctionPointer(m_runtimeType);
Debug.Assert(value.Length > 0);
}

return value;
}
}

internal bool DomainInitialized
{
get => m_bIsDomainInitialized;
Expand All @@ -1564,6 +1585,11 @@ internal bool DomainInitialized
if (!m_runtimeType.GetRootElementType().IsGenericTypeDefinition && m_runtimeType.ContainsGenericParameters)
return null;

// Exclude function pointer; it requires a grammar update and parsing support for Type.GetType() and friends.
// See https://learn.microsoft.com/dotnet/framework/reflection-and-codedom/specifying-fully-qualified-type-names.
if (m_runtimeType.IsFunctionPointer)
return null;

// No assembly.
return ConstructName(ref m_fullname, TypeNameFormatFlags.FormatNamespace | TypeNameFormatFlags.FormatFullInst);

Expand All @@ -1576,12 +1602,16 @@ internal bool DomainInitialized
}
}

internal string GetNameSpace()
internal string? GetNameSpace()
{
// @Optimization - Use ConstructName to populate m_namespace
if (m_namespace == null)
{
Type type = m_runtimeType;

if (type.IsFunctionPointer)
return null;

type = type.GetRootElementType();

while (type.IsNested)
Expand Down Expand Up @@ -3350,7 +3380,8 @@ public override string? AssemblyQualifiedName
{
string? fullname = FullName;

// FullName is null if this type contains generic parameters but is not a generic type definition.
// FullName is null if this type contains generic parameters but is not a generic type definition
// or if it is a function pointer.
if (fullname == null)
return null;

Expand All @@ -3362,7 +3393,7 @@ public override string? Namespace
{
get
{
string ns = Cache.GetNameSpace();
string? ns = Cache.GetNameSpace();
if (string.IsNullOrEmpty(ns))
{
return null;
Expand Down Expand Up @@ -3693,6 +3724,50 @@ private CheckValueStatus TryChangeTypeSpecial(

#endregion

#region Function Pointer
public override bool IsFunctionPointer => RuntimeTypeHandle.IsFunctionPointer(this);
public override bool IsUnmanagedFunctionPointer => RuntimeTypeHandle.IsUnmanagedFunctionPointer(this);

public override Type[] GetFunctionPointerCallingConventions()
{
if (!IsFunctionPointer)
{
throw new InvalidOperationException(SR.InvalidOperation_NotFunctionPointer);
}

// Requires a modified type to return the modifiers.
return EmptyTypes;
}

public override Type[] GetFunctionPointerParameterTypes()
{
if (!IsFunctionPointer)
{
throw new InvalidOperationException(SR.InvalidOperation_NotFunctionPointer);
}

Type[] parameters = Cache.FunctionPointerReturnAndParameterTypes;
Debug.Assert(parameters.Length > 0);

if (parameters.Length == 1)
{
return EmptyTypes;
}

return ModifiedType.CloneArray(parameters, 1);
}

public override Type GetFunctionPointerReturnType()
{
if (!IsFunctionPointer)
{
throw new InvalidOperationException(SR.InvalidOperation_NotFunctionPointer);
}

return Cache.FunctionPointerReturnAndParameterTypes[0];
}
#endregion

#endregion

public override string ToString() => GetCachedName(TypeNameKind.ToString)!;
Expand Down
Loading