diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
index 5ee434a3fec9c..ac9e8d9c3a7c5 100644
--- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -190,6 +190,7 @@
+
@@ -242,6 +243,9 @@
+
+ Common\System\Collections\Generic\ArrayBuilder.cs
+
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/ModifiedType.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/ModifiedType.CoreCLR.cs
new file mode 100644
index 0000000000000..60ab5dd00d102
--- /dev/null
+++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/ModifiedType.CoreCLR.cs
@@ -0,0 +1,35 @@
+// 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
+ {
+ internal struct TypeSignature
+ {
+ internal Signature? _signature;
+ internal int _offset;
+ }
+
+ internal Type GetTypeParameter(Type unmodifiedType, int index) =>
+ Create(unmodifiedType, new TypeSignature()
+ {
+ _signature = _typeSignature._signature,
+ _offset = _typeSignature._signature?.GetTypeParameterOffset(_typeSignature._offset, index) ?? 0
+ });
+
+ internal SignatureCallingConvention GetCallingConventionFromFunctionPointer() =>
+ _typeSignature._signature?.GetCallingConventionFromFunctionPointerAtOffset(_typeSignature._offset) ?? default;
+
+ internal static Type Create(Type unmodifiedType, Signature? signature, int parameterIndex = 0) =>
+ Create(unmodifiedType, new TypeSignature()
+ {
+ _signature = signature,
+ _offset = signature?.GetParameterOffset(parameterIndex) ?? 0
+ });
+
+ private Type[] GetCustomModifiers(bool required) =>
+ (_typeSignature._signature != null) ?
+ _typeSignature._signature.GetCustomModifiersAtOffset(_typeSignature._offset, required) : Type.EmptyTypes;
+ }
+}
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs
index 388d49354486f..54b04e77bd281 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs
@@ -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.Create(FieldType, GetSignature());
#endregion
}
}
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs
index cec302e60a014..1a7eae373f9c5 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeParameterInfo.cs
@@ -440,6 +440,9 @@ public override Type[] GetOptionalCustomModifiers()
m_signature.GetCustomModifiers(PositionImpl + 1, false);
}
+ public override Type GetModifiedParameterType() =>
+ ModifiedType.Create(unmodifiedType: ParameterType, m_signature, parameterIndex: PositionImpl + 1);
+
#endregion
#region ICustomAttributeProvider
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs
index a6b97f36353b8..d617b32d8b3ae 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs
@@ -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
@@ -205,6 +205,8 @@ public override Type[] GetOptionalCustomModifiers()
return Signature.GetCustomModifiers(0, false);
}
+ public override Type GetModifiedPropertyType() => ModifiedType.Create(PropertyType, Signature);
+
internal object GetConstantValue(bool raw)
{
object? defaultValue = MdConstant.GetValue(GetRuntimeModule().MetadataImport, m_token, PropertyType.TypeHandle, raw);
diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
index b924242cfe746..70252bff3e7c0 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
@@ -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);
@@ -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
{
@@ -1557,28 +1569,6 @@ 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))]
@@ -1586,7 +1576,6 @@ internal enum MdSigCallingConvention : byte
private extern void GetSignature(
void* pCorSig, int cCorSig,
RuntimeFieldHandleInternal fieldHandle, IRuntimeMethodInfo? methodHandle, RuntimeType? declaringType);
-
#endregion
#region Private Data Members
@@ -1645,8 +1634,20 @@ public Signature(void* pCorSig, int cCorSig, RuntimeType declaringType)
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool CompareSig(Signature sig1, Signature sig2);
+ internal Type[] GetCustomModifiers(int parameterIndex, bool required) =>
+ GetCustomModifiersAtOffset(GetParameterOffset(parameterIndex), required);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern int GetParameterOffset(int parameterIndex);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern int GetTypeParameterOffset(int offset, int index);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern SignatureCallingConvention GetCallingConventionFromFunctionPointerAtOffset(int offset);
+
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern Type[] GetCustomModifiers(int position, bool required);
+ internal extern Type[] GetCustomModifiersAtOffset(int offset, bool required);
#endregion
}
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 342ce64ad15ca..2b5cb9270a8a3 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs
@@ -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);
}
@@ -1535,12 +1535,33 @@ private MemberInfoCache GetMemberCache(ref MemberInfoCache? m_cache)
#region Internal Members
+
+ ///
+ /// Generic cache for rare scenario specific data. It is used to cache either Enum names, Enum values,
+ /// the Activator cache or function pointer parameters.
+ ///
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;
@@ -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);
@@ -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)
@@ -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;
@@ -3362,7 +3393,7 @@ public override string? Namespace
{
get
{
- string ns = Cache.GetNameSpace();
+ string? ns = Cache.GetNameSpace();
if (string.IsNullOrEmpty(ns))
{
return null;
@@ -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 parameters.AsSpan(1).ToArray();
+ }
+
+ 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)!;
diff --git a/src/coreclr/inc/sigparser.h b/src/coreclr/inc/sigparser.h
index 417660e2df4c2..4446a37e372b5 100644
--- a/src/coreclr/inc/sigparser.h
+++ b/src/coreclr/inc/sigparser.h
@@ -2,9 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
//
// sigparser.h
-//
-
-//
#ifndef _H_SIGPARSER
#define _H_SIGPARSER
@@ -716,7 +713,7 @@ class SigParser
// the arguments.
//------------------------------------------------------------------------
__checkReturn
- HRESULT SkipMethodHeaderSignature(uint32_t *pcArgs);
+ HRESULT SkipMethodHeaderSignature(uint32_t *pcArgs, bool skipReturnType = true);
//------------------------------------------------------------------------
// Skip a sub signature (as immediately follows an ELEMENT_TYPE_FNPTR).
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
index e9a18dcf92a57..31e77129e95d8 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
@@ -153,6 +153,7 @@
+
@@ -487,7 +488,7 @@
-
+
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/ModifiedType.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/ModifiedType.NativeAot.cs
new file mode 100644
index 0000000000000..7cf1313aed901
--- /dev/null
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/ModifiedType.NativeAot.cs
@@ -0,0 +1,20 @@
+// 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
+ {
+ internal struct TypeSignature
+ {
+ }
+
+#pragma warning disable IDE0060
+ internal Type GetTypeParameter(Type unmodifiedType, int index) => throw new NotSupportedException();
+
+ internal SignatureCallingConvention GetCallingConventionFromFunctionPointer() => throw new NotSupportedException();
+
+ private Type[] GetCustomModifiers(bool required) => throw new NotSupportedException();
+#pragma warning restore IDE0060
+ }
+}
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/MetadataReaderExtensions.NativeFormat.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/MetadataReaderExtensions.NativeFormat.cs
index 3ceed38784688..8d9e6cde0dbed 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/MetadataReaderExtensions.NativeFormat.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/MetadataReaderExtensions.NativeFormat.cs
@@ -18,6 +18,7 @@
using Internal.Metadata.NativeFormat;
using NativeFormatAssemblyFlags = global::Internal.Metadata.NativeFormat.AssemblyFlags;
+using NativeFormatModifiedType = global::Internal.Metadata.NativeFormat.ModifiedType;
namespace System.Reflection.Runtime.General
{
@@ -151,7 +152,7 @@ internal static Type[] GetCustomModifiers(this Handle handle, MetadataReader rea
LowLevelList customModifiers = new LowLevelList();
do
{
- ModifiedType modifiedType = handle.ToModifiedTypeHandle(reader).GetModifiedType(reader);
+ NativeFormatModifiedType modifiedType = handle.ToModifiedTypeHandle(reader).GetModifiedType(reader);
if (optional == modifiedType.IsOptional)
{
Type customModifier = modifiedType.ModifierType.Resolve(reader, typeContext);
@@ -174,7 +175,7 @@ public static Handle SkipCustomModifiers(this Handle handle, MetadataReader read
do
{
- ModifiedType modifiedType = handle.ToModifiedTypeHandle(reader).GetModifiedType(reader);
+ NativeFormatModifiedType modifiedType = handle.ToModifiedTypeHandle(reader).GetModifiedType(reader);
handle = modifiedType.Type;
handleType = handle.HandleType;
}
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/TypeResolver.NativeFormat.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/TypeResolver.NativeFormat.cs
index 3051f2e4c0ec9..659185ccd744b 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/TypeResolver.NativeFormat.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/TypeResolver.NativeFormat.cs
@@ -17,6 +17,7 @@
using Internal.Reflection.Core.Execution;
using Internal.Metadata.NativeFormat;
+using NativeFormatModifiedType = global::Internal.Metadata.NativeFormat.ModifiedType;
namespace System.Reflection.Runtime.General
{
@@ -45,7 +46,7 @@ internal static RuntimeTypeInfo Resolve(this Handle typeDefRefOrSpec, MetadataRe
return typeDefRefOrSpec.ToTypeSpecificationHandle(reader).TryResolveTypeSignature(reader, typeContext, ref exception);
else if (handleType == HandleType.ModifiedType)
{
- ModifiedType modifiedType = typeDefRefOrSpec.ToModifiedTypeHandle(reader).GetModifiedType(reader);
+ NativeFormatModifiedType modifiedType = typeDefRefOrSpec.ToModifiedTypeHandle(reader).GetModifiedType(reader);
return modifiedType.Type.TryResolve(reader, typeContext, ref exception);
}
else
diff --git a/src/coreclr/utilcode/sigparser.cpp b/src/coreclr/utilcode/sigparser.cpp
index 99fbe0a083ace..272b97ae68b88 100644
--- a/src/coreclr/utilcode/sigparser.cpp
+++ b/src/coreclr/utilcode/sigparser.cpp
@@ -126,7 +126,7 @@ HRESULT SigParser::SkipExactlyOne()
//
HRESULT
SigParser::SkipMethodHeaderSignature(
- uint32_t * pcArgs)
+ uint32_t * pcArgs, bool skipReturnType /*= true*/)
{
CONTRACTL
{
@@ -157,8 +157,11 @@ SigParser::SkipMethodHeaderSignature(
// Get arg count;
IfFailRet(GetData(pcArgs));
- // Skip return type;
- IfFailRet(SkipExactlyOne());
+ if (skipReturnType)
+ {
+ // Skip return type;
+ IfFailRet(SkipExactlyOne());
+ }
return hr;
} // SigParser::SkipMethodHeaderSignature
diff --git a/src/coreclr/vm/clsload.cpp b/src/coreclr/vm/clsload.cpp
index d7eb67b1bfe44..2953398ae9ac1 100644
--- a/src/coreclr/vm/clsload.cpp
+++ b/src/coreclr/vm/clsload.cpp
@@ -958,7 +958,6 @@ void DECLSPEC_NORETURN ClassLoader::ThrowTypeLoadException(TypeKey *pKey,
#endif
-
TypeHandle ClassLoader::LoadConstructedTypeThrowing(TypeKey *pKey,
LoadTypesFlag fLoadTypes /*= LoadTypes*/,
ClassLoadLevel level /*=CLASS_LOADED*/,
@@ -2966,9 +2965,9 @@ TypeHandle ClassLoader::CreateTypeHandleForTypeKey(TypeKey* pKey, AllocMemTracke
else if (pKey->GetKind() == ELEMENT_TYPE_FNPTR)
{
Module *pLoaderModule = ComputeLoaderModule(pKey);
+ PREFIX_ASSUME(pLoaderModule != NULL);
pLoaderModule->GetLoaderAllocator()->EnsureInstantiation(NULL, Instantiation(pKey->GetRetAndArgTypes(), pKey->GetNumArgs() + 1));
- PREFIX_ASSUME(pLoaderModule!=NULL);
DWORD numArgs = pKey->GetNumArgs();
BYTE* mem = (BYTE*) pamTracker->Track(pLoaderModule->GetAssembly()->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(FnPtrTypeDesc)) + S_SIZE_T(sizeof(TypeHandle)) * S_SIZE_T(numArgs)));
diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h
index 363da7d684abe..a31c4687cd21d 100644
--- a/src/coreclr/vm/ecalllist.h
+++ b/src/coreclr/vm/ecalllist.h
@@ -164,6 +164,9 @@ FCFuncStart(gCOMTypeHandleFuncs)
FCFuncElement("IsGenericVariable", RuntimeTypeHandle::IsGenericVariable)
FCFuncElement("ContainsGenericVariables", RuntimeTypeHandle::ContainsGenericVariables)
FCFuncElement("SatisfiesConstraints", RuntimeTypeHandle::SatisfiesConstraints)
+ FCFuncElement("GetArgumentTypesFromFunctionPointer", RuntimeTypeHandle::GetArgumentTypesFromFunctionPointer)
+ FCFuncElement("IsUnmanagedFunctionPointer", RuntimeTypeHandle::IsUnmanagedFunctionPointer)
+
#ifdef FEATURE_COMINTEROP
FCFuncElement("AllocateComObject", RuntimeTypeHandle::AllocateComObject)
#endif // FEATURE_COMINTEROP
@@ -201,8 +204,11 @@ FCFuncEnd()
FCFuncStart(gSignatureNative)
FCFuncElement("GetSignature", SignatureNative::GetSignature)
- FCFuncElement("GetCustomModifiers", SignatureNative::GetCustomModifiers)
FCFuncElement("CompareSig", SignatureNative::CompareSig)
+ FCFuncElement("GetParameterOffset", SignatureNative::GetParameterOffset)
+ FCFuncElement("GetTypeParameterOffset", SignatureNative::GetTypeParameterOffset)
+ FCFuncElement("GetCustomModifiersAtOffset", SignatureNative::GetCustomModifiersAtOffset)
+ FCFuncElement("GetCallingConventionFromFunctionPointerAtOffset", SignatureNative::GetCallingConventionFromFunctionPointerAtOffset)
FCFuncEnd()
FCFuncStart(gRuntimeMethodHandle)
diff --git a/src/coreclr/vm/runtimehandles.cpp b/src/coreclr/vm/runtimehandles.cpp
index 0cbfa4835f034..037c9e91402e5 100644
--- a/src/coreclr/vm/runtimehandles.cpp
+++ b/src/coreclr/vm/runtimehandles.cpp
@@ -338,7 +338,6 @@ FCIMPL1(AssemblyBaseObject*, RuntimeTypeHandle::GetAssembly, ReflectClassBaseObj
}
FCIMPLEND
-
FCIMPL1(FC_BOOL_RET, RuntimeFieldHandle::AcquiresContextFromThis, FieldDesc* pField)
{
CONTRACTL {
@@ -876,6 +875,69 @@ FCIMPL1(FC_BOOL_RET, RuntimeTypeHandle::IsByRefLike, ReflectClassBaseObject *pTy
}
FCIMPLEND
+FCIMPL1(Object *, RuntimeTypeHandle::GetArgumentTypesFromFunctionPointer, ReflectClassBaseObject *pTypeUNSAFE)
+{
+ CONTRACTL {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(pTypeUNSAFE));
+ }
+ CONTRACTL_END;
+
+ struct
+ {
+ PTRARRAYREF retVal;
+ } gc;
+
+ gc.retVal = NULL;
+
+ REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE);
+ TypeHandle typeHandle = refType->GetType();
+ if (!typeHandle.IsFnPtrType())
+ FCThrowRes(kArgumentException, W("Arg_InvalidHandle"));
+
+ FnPtrTypeDesc* fnPtr = typeHandle.AsFnPtrType();
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+ {
+ MethodTable *pMT = CoreLibBinder::GetClass(CLASS__TYPE);
+ TypeHandle arrayHandle = ClassLoader::LoadArrayTypeThrowing(TypeHandle(pMT), ELEMENT_TYPE_SZARRAY);
+ DWORD cArgs = fnPtr->GetNumArgs();
+ gc.retVal = (PTRARRAYREF) AllocateSzArray(arrayHandle, cArgs + 1);
+
+ for (DWORD position = 0; position <= cArgs; position++)
+ {
+ TypeHandle typeHandle = fnPtr->GetRetAndArgTypes()[position];
+ OBJECTREF refType = typeHandle.GetManagedClassObject();
+ gc.retVal->SetAt(position, refType);
+ }
+ }
+ HELPER_METHOD_FRAME_END();
+
+ return OBJECTREFToObject(gc.retVal);
+}
+FCIMPLEND
+
+FCIMPL1(FC_BOOL_RET, RuntimeTypeHandle::IsUnmanagedFunctionPointer, ReflectClassBaseObject *pTypeUNSAFE);
+{
+ CONTRACTL {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(pTypeUNSAFE));
+ }
+ CONTRACTL_END;
+
+ REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE);
+ BOOL unmanaged = FALSE;
+ TypeHandle typeHandle = refType->GetType();
+ if (typeHandle.IsFnPtrType())
+ {
+ FnPtrTypeDesc* fnPtr = typeHandle.AsFnPtrType();
+ unmanaged = (fnPtr->GetCallConv() & IMAGE_CEE_CS_CALLCONV_MASK) == IMAGE_CEE_CS_CALLCONV_UNMANAGED;
+ }
+
+ FC_RETURN_BOOL(unmanaged);
+}
+FCIMPLEND
+
extern "C" BOOL QCALLTYPE RuntimeTypeHandle_IsVisible(QCall::TypeHandle pTypeHandle)
{
CONTRACTL
@@ -1760,57 +1822,179 @@ FCIMPL1(INT32, RuntimeMethodHandle::GetSlot, MethodDesc *pMethod) {
}
FCIMPLEND
-FCIMPL3(Object *, SignatureNative::GetCustomModifiers, SignatureNative* pSignatureUNSAFE,
- INT32 parameter, CLR_BOOL fRequired)
+FCIMPL2(INT32, SignatureNative::GetParameterOffset, SignatureNative* pSignatureUNSAFE, INT32 parameterIndex)
{
- CONTRACTL {
- FCALL_CHECK;
- }
- CONTRACTL_END;
+ FCALL_CONTRACT;
struct
{
SIGNATURENATIVEREF pSig;
- PTRARRAYREF retVal;
} gc;
gc.pSig = (SIGNATURENATIVEREF)pSignatureUNSAFE;
- gc.retVal = NULL;
+
+ INT32 offset = 0;
HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
{
+ SigPointer sp(gc.pSig->GetCorSig(), gc.pSig->GetCorSigSize());
- BYTE callConv = *(BYTE*)gc.pSig->GetCorSig();
- SigTypeContext typeContext;
- gc.pSig->GetTypeContext(&typeContext);
- MetaSig sig(gc.pSig->GetCorSig(),
- gc.pSig->GetCorSigSize(),
- gc.pSig->GetModule(),
- &typeContext,
- (callConv & IMAGE_CEE_CS_CALLCONV_MASK) == IMAGE_CEE_CS_CALLCONV_FIELD ? MetaSig::sigField : MetaSig::sigMember);
- _ASSERTE(callConv == sig.GetCallingConventionInfo());
+ uint32_t callConv = 0;
+ IfFailThrow(sp.GetCallingConvInfo(&callConv));
- SigPointer argument(NULL, 0);
+ if ((callConv & IMAGE_CEE_CS_CALLCONV_MASK) != IMAGE_CEE_CS_CALLCONV_FIELD)
+ {
+ if (callConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ {
+ IfFailThrow(sp.GetData(NULL));
+ }
- PRECONDITION(sig.GetCallingConvention() != IMAGE_CEE_CS_CALLCONV_FIELD || parameter == 1);
+ uint32_t numArgs;
+ IfFailThrow(sp.GetData(&numArgs));
+ _ASSERTE((uint32_t)parameterIndex <= numArgs);
- if (parameter == 0)
- {
- argument = sig.GetReturnProps();
+ for (int i = 0; i < parameterIndex; i++)
+ IfFailThrow(sp.SkipExactlyOne());
}
else
{
- for(INT32 i = 0; i < parameter; i++)
- sig.NextArg();
+ _ASSERTE(parameterIndex == 0);
+ }
+
+ offset = (INT32)(sp.GetPtr() - gc.pSig->GetCorSig());
+ }
+ HELPER_METHOD_FRAME_END();
+
+ return offset;
+}
+FCIMPLEND
- argument = sig.GetArgProps();
+FCIMPL3(INT32, SignatureNative::GetTypeParameterOffset, SignatureNative* pSignatureUNSAFE, INT32 offset, INT32 index)
+{
+ FCALL_CONTRACT;
+
+ struct
+ {
+ SIGNATURENATIVEREF pSig;
+ } gc;
+
+ if (offset < 0)
+ {
+ _ASSERTE(offset == -1);
+ return offset;
+ }
+
+ gc.pSig = (SIGNATURENATIVEREF)pSignatureUNSAFE;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+ {
+ SigPointer sp(gc.pSig->GetCorSig() + offset, gc.pSig->GetCorSigSize() - offset);
+
+ CorElementType etype;
+ IfFailThrow(sp.GetElemType(&etype));
+
+ uint32_t argCnt;
+
+ switch (etype)
+ {
+ case ELEMENT_TYPE_FNPTR:
+ IfFailThrow(sp.SkipMethodHeaderSignature(&argCnt, /* skipReturnType */ false));
+ _ASSERTE((uint32_t)index <= argCnt);
+ break;
+ case ELEMENT_TYPE_GENERICINST:
+ IfFailThrow(sp.SkipExactlyOne());
+
+ IfFailThrow(sp.GetData(&argCnt));
+ _ASSERTE((uint32_t)index < argCnt);
+ break;
+ case ELEMENT_TYPE_ARRAY:
+ case ELEMENT_TYPE_SZARRAY:
+ case ELEMENT_TYPE_BYREF:
+ case ELEMENT_TYPE_PTR:
+ _ASSERTE(index == 0);
+ break;
+ case ELEMENT_TYPE_VAR:
+ case ELEMENT_TYPE_MVAR:
+ offset = -1; // Use offset -1 to signal method substituted method variable. We do not have full signature for those.
+ goto Done;
+ default:
+ _ASSERTE(false); // Unexpected element type
+ offset = -1;
+ goto Done;
}
- //if (parameter < 0 || parameter > (INT32)sig.NumFixedArgs())
- // FCThrowResVoid(kArgumentNullException, W("Arg_ArgumentOutOfRangeException"));
+ for (int i = 0; i < index; i++)
+ IfFailThrow(sp.SkipExactlyOne());
+
+ offset = (INT32)(sp.GetPtr() - gc.pSig->GetCorSig());
+ Done: ;
+ }
+ HELPER_METHOD_FRAME_END();
+
+ return offset;
+}
+FCIMPLEND
+
+FCIMPL2(FC_INT8_RET, SignatureNative::GetCallingConventionFromFunctionPointerAtOffset, SignatureNative* pSignatureUNSAFE, INT32 offset)
+{
+ FCALL_CONTRACT;
+
+ struct
+ {
+ SIGNATURENATIVEREF pSig;
+ } gc;
+
+ if (offset < 0)
+ {
+ _ASSERTE(offset == -1);
+ return 0;
+ }
+
+ gc.pSig = (SIGNATURENATIVEREF)pSignatureUNSAFE;
+
+ uint32_t callConv = 0;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+ {
+ SigPointer sp(gc.pSig->GetCorSig() + offset, gc.pSig->GetCorSigSize() - offset);
+
+ CorElementType etype;
+ IfFailThrow(sp.GetElemType(&etype));
+ _ASSERTE(etype == ELEMENT_TYPE_FNPTR);
+
+ IfFailThrow(sp.GetCallingConv(&callConv));
+ }
+ HELPER_METHOD_FRAME_END();
+
+ return (FC_INT8_RET)(callConv);
+}
+FCIMPLEND
+
+FCIMPL3(Object *, SignatureNative::GetCustomModifiersAtOffset,
+ SignatureNative* pSignatureUNSAFE,
+ INT32 offset,
+ CLR_BOOL fRequired)
+{
+ FCALL_CONTRACT;
+
+ struct
+ {
+ SIGNATURENATIVEREF pSig;
+ PTRARRAYREF retVal;
+ } gc;
+
+ gc.pSig = (SIGNATURENATIVEREF)pSignatureUNSAFE;
+ gc.retVal = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+ {
+ SigTypeContext typeContext;
+ gc.pSig->GetTypeContext(&typeContext);
+
+ SigPointer argument(gc.pSig->GetCorSig() + offset, gc.pSig->GetCorSigSize() - offset);
SigPointer sp = argument;
- Module* pModule = sig.GetModule();
+ Module* pModule = gc.pSig->GetModule();
INT32 cMods = 0;
CorElementType cmodType;
@@ -1975,6 +2159,7 @@ FCIMPL6(void, SignatureNative::GetSignature,
pMethod, declType.GetClassOrArrayInstantiation(), pMethod->LoadMethodInstantiation(), &typeContext);
else
SigTypeContext::InitTypeContext(declType, &typeContext);
+
MetaSig msig(pCorSig, cCorSig, pModule, &typeContext,
(callConv & IMAGE_CEE_CS_CALLCONV_MASK) == IMAGE_CEE_CS_CALLCONV_FIELD ? MetaSig::sigField : MetaSig::sigMember);
diff --git a/src/coreclr/vm/runtimehandles.h b/src/coreclr/vm/runtimehandles.h
index 2db8a56288fc3..f9275671a9f44 100644
--- a/src/coreclr/vm/runtimehandles.h
+++ b/src/coreclr/vm/runtimehandles.h
@@ -1,8 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-
-
#ifndef _RUNTIMEHANDLES_H_
#define _RUNTIMEHANDLES_H_
@@ -138,6 +136,9 @@ class RuntimeTypeHandle {
static FCDECL1(FC_BOOL_RET, IsInterface, ReflectClassBaseObject* pType);
static FCDECL1(FC_BOOL_RET, IsByRefLike, ReflectClassBaseObject* pType);
+ static FCDECL1(Object *, GetArgumentTypesFromFunctionPointer, ReflectClassBaseObject *pTypeUNSAFE);
+ static FCDECL1(FC_BOOL_RET, IsUnmanagedFunctionPointer, ReflectClassBaseObject *pTypeUNSAFE);
+
static FCDECL2(FC_BOOL_RET, CanCastTo, ReflectClassBaseObject *pType, ReflectClassBaseObject *pTarget);
static FCDECL2(FC_BOOL_RET, IsInstanceOfType, ReflectClassBaseObject *pType, Object *object);
@@ -373,9 +374,16 @@ class SignatureNative : public Object
PCCOR_SIGNATURE pCorSig, DWORD cCorSig,
FieldDesc *pFieldDesc, ReflectMethodObject *pMethodUNSAFE,
ReflectClassBaseObject *pDeclaringType);
- static FCDECL3(Object *, GetCustomModifiers, SignatureNative* pSig, INT32 parameter, CLR_BOOL fRequired);
+
static FCDECL2(FC_BOOL_RET, CompareSig, SignatureNative* pLhs, SignatureNative* pRhs);
+ static FCDECL2(INT32, GetParameterOffset, SignatureNative* pSig, INT32 parameterIndex);
+
+ static FCDECL3(INT32, GetTypeParameterOffset, SignatureNative* pSig, INT32 offset, INT32 index);
+
+ static FCDECL2(FC_INT8_RET, GetCallingConventionFromFunctionPointerAtOffset, SignatureNative* pSig, INT32 offset);
+
+ static FCDECL3(Object *, GetCustomModifiersAtOffset, SignatureNative* pSig, INT32 offset, CLR_BOOL fRequired);
BOOL HasThis() { LIMITED_METHOD_CONTRACT; return (m_managedCallingConvention & CALLCONV_HasThis); }
INT32 NumFixedArgs() { WRAPPER_NO_CONTRACT; return m_PtrArrayarguments->GetNumComponents(); }
@@ -556,4 +564,3 @@ class ReflectionPointer : public Object
};
#endif
-
diff --git a/src/coreclr/vm/siginfo.cpp b/src/coreclr/vm/siginfo.cpp
index c35e9148fec35..0a15fbb91488f 100644
--- a/src/coreclr/vm/siginfo.cpp
+++ b/src/coreclr/vm/siginfo.cpp
@@ -615,9 +615,7 @@ void MetaSig::Init(
}
}
-
m_pStart = psig;
-
m_flags = 0;
// Reset the iterator fields
@@ -661,7 +659,7 @@ MetaSig::MetaSig(MethodDesc *pMD, Instantiation classInst, Instantiation methodI
DWORD cbSigSize;
pMD->GetSig(&pSig, &cbSigSize);
- Init(pSig, cbSigSize, pMD->GetModule(),&typeContext);
+ Init(pSig, cbSigSize, pMD->GetModule(), &typeContext);
if (pMD->RequiresInstArg())
SetHasParamTypeArg();
@@ -682,7 +680,7 @@ MetaSig::MetaSig(MethodDesc *pMD, TypeHandle declaringType)
DWORD cbSigSize;
pMD->GetSig(&pSig, &cbSigSize);
- Init(pSig, cbSigSize, pMD->GetModule(),&typeContext);
+ Init(pSig, cbSigSize, pMD->GetModule(), &typeContext);
if (pMD->RequiresInstArg())
SetHasParamTypeArg();
@@ -1481,9 +1479,9 @@ TypeHandle SigPointer::GetTypeHandleThrowing(
{
if (TypeFromToken(typeToken) == mdtTypeRef)
{
- loadedType = TypeHandle(CoreLibBinder::GetElementType(ELEMENT_TYPE_VOID));
- thRet = loadedType;
- break;
+ loadedType = TypeHandle(CoreLibBinder::GetElementType(ELEMENT_TYPE_VOID));
+ thRet = loadedType;
+ break;
}
}
@@ -1535,8 +1533,8 @@ TypeHandle SigPointer::GetTypeHandleThrowing(
pZapSigContext);
if (elemType.IsNull())
{
- thRet = elemType;
- break;
+ thRet = elemType;
+ break;
}
uint32_t rank = 0;
@@ -1547,7 +1545,7 @@ TypeHandle SigPointer::GetTypeHandleThrowing(
_ASSERTE(0 < rank);
}
thRet = ClassLoader::LoadArrayTypeThrowing(elemType, typ, rank, fLoadTypes, level);
- break;
+ break;
}
case ELEMENT_TYPE_PINNED:
@@ -1559,7 +1557,7 @@ TypeHandle SigPointer::GetTypeHandleThrowing(
dropGenericArgumentLevel,
pSubst,
pZapSigContext);
- break;
+ break;
case ELEMENT_TYPE_BYREF:
case ELEMENT_TYPE_PTR:
@@ -1577,69 +1575,84 @@ TypeHandle SigPointer::GetTypeHandleThrowing(
}
else
{
- thRet = ClassLoader::LoadPointerOrByrefTypeThrowing(typ, baseType, fLoadTypes, level);
+ thRet = ClassLoader::LoadPointerOrByrefTypeThrowing(typ, baseType, fLoadTypes, level);
}
- break;
+ break;
}
case ELEMENT_TYPE_FNPTR:
- {
+ {
#ifndef DACCESS_COMPILE
- uint32_t uCallConv = 0;
- IfFailThrowBF(psig.GetData(&uCallConv), BFA_BAD_SIGNATURE, pOrigModule);
-
- if ((uCallConv & IMAGE_CEE_CS_CALLCONV_MASK) == IMAGE_CEE_CS_CALLCONV_FIELD)
- THROW_BAD_FORMAT(BFA_FNPTR_CANNOT_BE_A_FIELD, pOrigModule);
-
- if ((uCallConv & IMAGE_CEE_CS_CALLCONV_GENERIC) > 0)
- THROW_BAD_FORMAT(BFA_FNPTR_CANNOT_BE_GENERIC, pOrigModule);
+ uint32_t uCallConv = 0;
+ IfFailThrowBF(psig.GetData(&uCallConv), BFA_BAD_SIGNATURE, pOrigModule);
- // Get arg count;
- uint32_t cArgs = 0;
- IfFailThrowBF(psig.GetData(&cArgs), BFA_BAD_SIGNATURE, pOrigModule);
+ if ((uCallConv & IMAGE_CEE_CS_CALLCONV_MASK) == IMAGE_CEE_CS_CALLCONV_FIELD)
+ THROW_BAD_FORMAT(BFA_FNPTR_CANNOT_BE_A_FIELD, pOrigModule);
- uint32_t cAllocaSize;
- if (!ClrSafeInt::addition(cArgs, 1, cAllocaSize) ||
- !ClrSafeInt::multiply(cAllocaSize, sizeof(TypeHandle), cAllocaSize))
- {
- ThrowHR(COR_E_OVERFLOW);
- }
+ if ((uCallConv & IMAGE_CEE_CS_CALLCONV_GENERIC) > 0)
+ THROW_BAD_FORMAT(BFA_FNPTR_CANNOT_BE_GENERIC, pOrigModule);
- TypeHandle *retAndArgTypes = (TypeHandle*) _alloca(cAllocaSize);
- bool fReturnTypeOrParameterNotLoaded = false;
+ // Get the arg count.
+ uint32_t cArgs = 0;
+ IfFailThrowBF(psig.GetData(&cArgs), BFA_BAD_SIGNATURE, pOrigModule);
- for (unsigned i = 0; i <= cArgs; i++)
- {
- retAndArgTypes[i] = psig.GetTypeHandleThrowing(pOrigModule,
- pTypeContext,
- fLoadTypes,
- level,
- dropGenericArgumentLevel,
- pSubst,
- pZapSigContext);
- if (retAndArgTypes[i].IsNull())
- {
- thRet = TypeHandle();
- fReturnTypeOrParameterNotLoaded = true;
- break;
- }
+ uint32_t cAllocaSize;
+ if (!ClrSafeInt::addition(cArgs, 1, cAllocaSize) ||
+ !ClrSafeInt::multiply(cAllocaSize, sizeof(TypeHandle), cAllocaSize))
+ {
+ ThrowHR(COR_E_OVERFLOW);
+ }
- IfFailThrowBF(psig.SkipExactlyOne(), BFA_BAD_SIGNATURE, pOrigModule);
- }
+ TypeHandle *retAndArgTypes = (TypeHandle*) _alloca(cAllocaSize);
+ bool fReturnTypeOrParameterNotLoaded = false;
- if (fReturnTypeOrParameterNotLoaded)
+ for (unsigned i = 0; i <= cArgs; i++)
+ {
+ // Lookup type handle.
+ retAndArgTypes[i] = psig.GetTypeHandleThrowing(pOrigModule,
+ pTypeContext,
+ fLoadTypes,
+ level,
+ dropGenericArgumentLevel,
+ pSubst,
+ pZapSigContext);
+
+ if (retAndArgTypes[i].IsNull())
{
+ thRet = TypeHandle();
+ fReturnTypeOrParameterNotLoaded = true;
break;
}
- // Now make the function pointer type
- thRet = ClassLoader::LoadFnptrTypeThrowing((BYTE) uCallConv, cArgs, retAndArgTypes, fLoadTypes, level);
+ IfFailThrowBF(psig.SkipExactlyOne(), BFA_BAD_SIGNATURE, pOrigModule);
+ }
+
+ if (fReturnTypeOrParameterNotLoaded)
+ {
+ break;
+ }
+
+ // Only have an unmanaged\managed status, and not the unmanaged CALLCONV_ value.
+ switch (uCallConv & IMAGE_CEE_CS_CALLCONV_MASK)
+ {
+ case IMAGE_CEE_CS_CALLCONV_C:
+ case IMAGE_CEE_CS_CALLCONV_STDCALL:
+ case IMAGE_CEE_CS_CALLCONV_THISCALL:
+ case IMAGE_CEE_CS_CALLCONV_FASTCALL:
+ // Strip the calling convention.
+ uCallConv &= ~IMAGE_CEE_CS_CALLCONV_MASK;
+ // Normalize to unmanaged.
+ uCallConv |= IMAGE_CEE_CS_CALLCONV_UNMANAGED;
+ }
+
+ // Find an existing function pointer or make a new one
+ thRet = ClassLoader::LoadFnptrTypeThrowing((BYTE) uCallConv, cArgs, retAndArgTypes, fLoadTypes, level);
#else
DacNotImpl();
- thRet = TypeHandle();
+ thRet = TypeHandle();
#endif
break;
- }
+ }
case ELEMENT_TYPE_INTERNAL :
{
diff --git a/src/coreclr/vm/typedesc.cpp b/src/coreclr/vm/typedesc.cpp
index 08b65beacc3b3..fe69f958dd4db 100644
--- a/src/coreclr/vm/typedesc.cpp
+++ b/src/coreclr/vm/typedesc.cpp
@@ -507,6 +507,47 @@ OBJECTREF ParamTypeDesc::GetManagedClassObject()
#endif // #ifndef DACCESS_COMPILE
+#ifndef DACCESS_COMPILE
+
+OBJECTREF FnPtrTypeDesc::GetManagedClassObject()
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+
+ INJECT_FAULT(COMPlusThrowOM());
+
+ PRECONDITION(GetInternalCorElementType() == ELEMENT_TYPE_FNPTR);
+ }
+ CONTRACTL_END;
+
+ if (m_hExposedClassObject == NULL) {
+ REFLECTCLASSBASEREF refClass = NULL;
+ GCPROTECT_BEGIN(refClass);
+ refClass = (REFLECTCLASSBASEREF) AllocateObject(g_pRuntimeTypeClass);
+
+ LoaderAllocator *pLoaderAllocator = GetLoaderAllocator();
+ TypeHandle th = TypeHandle(this);
+ ((ReflectClassBaseObject*)OBJECTREFToObject(refClass))->SetType(th);
+ ((ReflectClassBaseObject*)OBJECTREFToObject(refClass))->SetKeepAlive(pLoaderAllocator->GetExposedObject());
+
+ // Let all threads fight over who wins using InterlockedCompareExchange.
+ // Only the winner can set m_hExposedClassObject from NULL.
+ LOADERHANDLE hExposedClassObject = pLoaderAllocator->AllocateHandle(refClass);
+
+ if (InterlockedCompareExchangeT(&m_hExposedClassObject, hExposedClassObject, static_cast(NULL)))
+ {
+ pLoaderAllocator->FreeHandle(hExposedClassObject);
+ }
+
+ GCPROTECT_END();
+ }
+ return GetManagedClassObjectIfExists();
+}
+
+#endif // #ifndef DACCESS_COMPILE
+
BOOL TypeDesc::IsRestored()
{
STATIC_CONTRACT_NOTHROW;
diff --git a/src/coreclr/vm/typedesc.h b/src/coreclr/vm/typedesc.h
index c4a0a83043e59..fe228deedd30b 100644
--- a/src/coreclr/vm/typedesc.h
+++ b/src/coreclr/vm/typedesc.h
@@ -3,10 +3,7 @@
//
// File: typedesc.h
//
-
-
//
-
//
// ============================================================================
@@ -25,7 +22,7 @@ class TypeHandleList;
ParamTypeDescs only include byref, array and pointer types. They do NOT
- include instantaitions of generic types, which are represented by MethodTables.
+ include instantiations of generic types, which are represented by MethodTables.
*/
@@ -43,7 +40,7 @@ class TypeDesc
#endif
// This is the ELEMENT_TYPE* that would be used in the type sig for this type
- // For enums this is the uderlying type
+ // For enums this is the underlying type
inline CorElementType GetInternalCorElementType() {
LIMITED_METHOD_DAC_CONTRACT;
@@ -543,7 +540,36 @@ class FnPtrTypeDesc : public TypeDesc
void EnumMemoryRegions(CLRDataEnumMemoryFlags flags);
#endif //DACCESS_COMPILE
+ OBJECTREF GetManagedClassObject();
+
+ OBJECTREF GetManagedClassObjectIfExists()
+ {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ OBJECTREF objRet = NULL;
+ GET_LOADERHANDLE_VALUE_FAST(GetLoaderAllocator(), m_hExposedClassObject, &objRet);
+ return objRet;
+ }
+
+ OBJECTREF GetManagedClassObjectFast()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ OBJECTREF objRet = NULL;
+ LoaderAllocator::GetHandleValueFast(m_hExposedClassObject, &objRet);
+ return objRet;
+ }
+
protected:
+ // Handle back to the internal reflection Type object
+ LOADERHANDLE m_hExposedClassObject;
+
// Number of arguments
DWORD m_NumArgs;
diff --git a/src/coreclr/vm/typehandle.cpp b/src/coreclr/vm/typehandle.cpp
index 70c0397470a8e..b4eb741831f4e 100644
--- a/src/coreclr/vm/typehandle.cpp
+++ b/src/coreclr/vm/typehandle.cpp
@@ -1156,8 +1156,7 @@ OBJECTREF TypeHandle::GetManagedClassObject() const
return ((TypeVarTypeDesc*)AsTypeDesc())->GetManagedClassObject();
case ELEMENT_TYPE_FNPTR:
- // A function pointer is mapped into typeof(IntPtr). It results in a loss of information.
- return CoreLibBinder::GetElementType(ELEMENT_TYPE_I)->GetManagedClassObject();
+ return ((FnPtrTypeDesc*)AsTypeDesc())->GetManagedClassObject();
default:
_ASSERTE(!"Bad Element Type");
diff --git a/src/coreclr/vm/typehandle.h b/src/coreclr/vm/typehandle.h
index 037bc10205e17..38251e8d9cbe3 100644
--- a/src/coreclr/vm/typehandle.h
+++ b/src/coreclr/vm/typehandle.h
@@ -486,6 +486,8 @@ class TypeHandle
// PTR
BOOL IsPointer() const;
+ BOOL IsUnmanagedFunctionPointer() const;
+
// True if this type *is* a formal generic type parameter or any component of it is a formal generic type parameter
BOOL ContainsGenericVariables(BOOL methodOnly=FALSE) const;
diff --git a/src/coreclr/vm/typehandle.inl b/src/coreclr/vm/typehandle.inl
index bd98e25ab0e23..dca823aff6d16 100644
--- a/src/coreclr/vm/typehandle.inl
+++ b/src/coreclr/vm/typehandle.inl
@@ -265,8 +265,7 @@ FORCEINLINE OBJECTREF TypeHandle::GetManagedClassObjectFast() const
break;
case ELEMENT_TYPE_FNPTR:
- // A function pointer is mapped into typeof(IntPtr). It results in a loss of information.
- o = CoreLibBinder::GetElementType(ELEMENT_TYPE_I)->GetManagedClassObjectIfExists();
+ o = dac_cast(AsTypeDesc())->GetManagedClassObjectFast();
break;
default:
diff --git a/src/coreclr/vm/typestring.cpp b/src/coreclr/vm/typestring.cpp
index 0a05972721a1a..88550d898378a 100644
--- a/src/coreclr/vm/typestring.cpp
+++ b/src/coreclr/vm/typestring.cpp
@@ -223,6 +223,36 @@ HRESULT TypeNameBuilder::AddName(LPCWSTR szName, LPCWSTR szNamespace)
return hr;
}
+HRESULT TypeNameBuilder::AddNameNoEscaping(LPCWSTR szName)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (!szName)
+ return Fail();
+
+ if (!CheckParseState(ParseStateSTART | ParseStateNAME))
+ return Fail();
+
+ HRESULT hr = S_OK;
+
+ m_parseState = ParseStateNAME;
+
+ if (m_bNestedName)
+ Append(W('+'));
+
+ m_bNestedName = TRUE;
+
+ Append(szName);
+
+ return hr;
+}
+
HRESULT TypeNameBuilder::OpenGenericArguments()
{
WRAPPER_NO_CONTRACT;
@@ -760,8 +790,48 @@ void TypeString::AppendType(TypeNameBuilder& tnb, TypeHandle ty, Instantiation t
// ...or function pointer
else if (ty.IsFnPtrType())
{
- // Don't attempt to format this currently, it may trigger GC due to fixups.
- tnb.AddName(W("(fnptr)"));
+ // Currently function pointers return NULL for FullName and AssemblyQualifiedName and "" for Name.
+ // We need a grammar update in order to support parsing.
+ // See https://learn.microsoft.com/dotnet/framework/reflection-and-codedom/specifying-fully-qualified-type-names
+ if (format & FormatNamespace)
+ {
+ FnPtrTypeDesc* fnPtr = ty.AsFnPtrType();
+ TypeHandle *retAndArgTypes = fnPtr->GetRetAndArgTypesPointer();
+
+ StackSString ss;
+ AppendType(ss, retAndArgTypes[0], format);
+
+ SString ssOpening(SString::Literal, "(");
+ ss += ssOpening;
+
+ SString ssComma(SString::Literal, ", ");
+ DWORD cArgs = fnPtr->GetNumArgs();
+ for (DWORD i = 1; i <= cArgs; i++)
+ {
+ if (i != 1)
+ ss += ssComma;
+
+ AppendType(ss, retAndArgTypes[i], format);
+ }
+
+ if ((fnPtr->GetCallConv() & IMAGE_CEE_CS_CALLCONV_MASK) == IMAGE_CEE_CS_CALLCONV_VARARG)
+ {
+ if (cArgs)
+ ss += ssComma;
+
+ SString ssEllipsis(SString::Literal, "...");
+ ss += ssEllipsis;
+ }
+
+ SString ssClosing(SString::Literal, ")");
+ ss += ssClosing;
+
+ tnb.AddNameNoEscaping(ss);
+ }
+ else
+ {
+ tnb.AddNameNoEscaping(W(""));
+ }
}
// ...otherwise it's just a plain type def or an instantiated type
diff --git a/src/coreclr/vm/typestring.h b/src/coreclr/vm/typestring.h
index f003984633c35..0fdca90c389a4 100644
--- a/src/coreclr/vm/typestring.h
+++ b/src/coreclr/vm/typestring.h
@@ -43,6 +43,7 @@ class TypeNameBuilder
HRESULT CloseGenericArgument();
HRESULT AddName(LPCWSTR szName);
HRESULT AddName(LPCWSTR szName, LPCWSTR szNamespace);
+ HRESULT AddNameNoEscaping(LPCWSTR szName);
HRESULT AddPointer();
HRESULT AddByRef();
HRESULT AddSzArray();
diff --git a/src/libraries/Common/tests/System/FunctionPointerCallingConventionTests.cs b/src/libraries/Common/tests/System/FunctionPointerCallingConventionTests.cs
new file mode 100644
index 0000000000000..6150907e43d81
--- /dev/null
+++ b/src/libraries/Common/tests/System/FunctionPointerCallingConventionTests.cs
@@ -0,0 +1,216 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Reflection;
+using System.Reflection.Tests;
+using System.Runtime.CompilerServices;
+using Xunit;
+
+namespace System.Tests.Types
+{
+ public partial class FunctionPointerCallingConventionTests
+ {
+ private const BindingFlags Bindings = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly;
+
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void ManagedCallingConvention(bool modified)
+ {
+ Type t = typeof(FunctionPointerHolder).Project();
+ MethodInfo m = t.GetMethod(nameof(FunctionPointerHolder.MethodCallConv_Managed), Bindings);
+ Type fnPtrType = modified ? m.GetParameters()[0].ParameterType : m.GetParameters()[0].GetModifiedParameterType();
+
+ Type[] callConvs = fnPtrType.GetFunctionPointerCallingConventions();
+ Assert.Equal(0, callConvs.Length);
+ Assert.False(fnPtrType.IsUnmanagedFunctionPointer);
+
+ Type returnType = fnPtrType.GetFunctionPointerReturnType();
+ Assert.Equal(0, returnType.GetOptionalCustomModifiers().Length);
+ Assert.Equal(0, returnType.GetRequiredCustomModifiers().Length);
+ }
+
+ [Theory]
+ [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Cdecl))]
+ [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Stdcall))]
+ [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Thiscall))]
+ [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Fastcall))]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void UnmanagedCallConv_Param_Unmodified(string methodName)
+ {
+ Type t = typeof(FunctionPointerHolder).Project();
+ MethodInfo m = t.GetMethod(methodName, Bindings);
+
+ Type fnPtrType = m.GetParameters()[0].ParameterType;
+ Assert.True(fnPtrType.IsUnmanagedFunctionPointer);
+ Assert.Equal(0, fnPtrType.GetFunctionPointerReturnType().GetOptionalCustomModifiers().Length);
+ Assert.Equal(0, fnPtrType.GetFunctionPointerReturnType().GetRequiredCustomModifiers().Length);
+ Assert.Equal(0, fnPtrType.GetFunctionPointerCallingConventions().Length);
+ }
+
+ [Theory]
+ [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Cdecl), typeof(CallConvCdecl))]
+ [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Stdcall), typeof(CallConvStdcall))]
+ [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Thiscall), typeof(CallConvThiscall))]
+ [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Fastcall), typeof(CallConvFastcall))]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void UnmanagedCallConv_Param_Modified(string methodName, Type callingConventionRuntime)
+ {
+ Type callingConvention = callingConventionRuntime.Project();
+ Type t = typeof(FunctionPointerHolder).Project();
+ MethodInfo m = t.GetMethod(methodName, Bindings);
+
+ Type fnPtrType = m.GetParameters()[0].GetModifiedParameterType();
+ Assert.True(fnPtrType.IsUnmanagedFunctionPointer);
+ Assert.Equal(0, fnPtrType.GetFunctionPointerReturnType().GetOptionalCustomModifiers().Length);
+ Assert.Equal(0, fnPtrType.GetFunctionPointerReturnType().GetRequiredCustomModifiers().Length);
+ Type[] callConvs = fnPtrType.GetFunctionPointerCallingConventions();
+ Assert.Equal(1, callConvs.Length);
+ Assert.Equal(callingConvention, callConvs[0]);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void UnmanagedCallConvs_Return_Unmodified()
+ {
+ Type t = typeof(FunctionPointerHolder).Project();
+
+ MethodInfo m1 = t.GetMethod(nameof(FunctionPointerHolder.MethodUnmanagedReturnValue_DifferentCallingConventions1), Bindings);
+ Type fcnPtr1 = m1.ReturnType;
+ Assert.True(fcnPtr1.IsUnmanagedFunctionPointer);
+
+ MethodInfo m2 = t.GetMethod(nameof(FunctionPointerHolder.MethodUnmanagedReturnValue_DifferentCallingConventions2), Bindings);
+ Type fcnPtr2 = m2.ReturnType;
+ Assert.True(fcnPtr2.IsUnmanagedFunctionPointer);
+
+ Assert.Equal(0, fcnPtr1.GetFunctionPointerCallingConventions().Length);
+ Assert.Equal(0, fcnPtr2.GetFunctionPointerCallingConventions().Length);
+
+ Assert.True(fcnPtr1.IsFunctionPointerEqual(fcnPtr2));
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void UnmanagedCallConvs_Return_Modified()
+ {
+ Type t = typeof(FunctionPointerHolder).Project();
+
+ MethodInfo m1 = t.GetMethod(nameof(FunctionPointerHolder.MethodUnmanagedReturnValue_DifferentCallingConventions1), Bindings);
+ Type fcnPtr1 = m1.ReturnParameter.GetModifiedParameterType();
+ Assert.True(fcnPtr1.IsUnmanagedFunctionPointer);
+
+ MethodInfo m2 = t.GetMethod(nameof(FunctionPointerHolder.MethodUnmanagedReturnValue_DifferentCallingConventions2), Bindings);
+ Type fcnPtr2 = m2.ReturnParameter.GetModifiedParameterType();
+ Assert.True(fcnPtr2.IsUnmanagedFunctionPointer);
+
+ Assert.NotSame(fcnPtr1, fcnPtr2);
+
+ Type retType = fcnPtr1.GetFunctionPointerReturnType();
+ Assert.True(typeof(int).Project().IsFunctionPointerEqual(retType.UnderlyingSystemType));
+
+ Type[] modOpts = fcnPtr1.GetFunctionPointerReturnType().GetOptionalCustomModifiers();
+ Assert.Equal(2, modOpts.Length);
+ }
+
+ [Theory]
+ [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Cdecl_SuppressGCTransition))]
+ [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Stdcall_SuppressGCTransition))]
+ [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Thiscall_SuppressGCTransition))]
+ [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Fastcall_SuppressGCTransition))]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void UnmanagedCallConv_PhysicalModifiers_Unmodified(string methodName)
+ {
+ Type t = typeof(FunctionPointerHolder).Project();
+ MethodInfo m = t.GetMethod(methodName, Bindings);
+
+ Type fnPtrType = m.GetParameters()[0].ParameterType;
+ Assert.True(fnPtrType.IsUnmanagedFunctionPointer);
+
+ Assert.Equal(0, fnPtrType.GetFunctionPointerCallingConventions().Length);
+ Assert.Equal(0, fnPtrType.GetOptionalCustomModifiers().Length);
+ Assert.Equal(0, fnPtrType.GetRequiredCustomModifiers().Length);
+ }
+
+ [Theory]
+ [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Cdecl_SuppressGCTransition), typeof(CallConvCdecl))]
+ [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Stdcall_SuppressGCTransition), typeof(CallConvStdcall))]
+ [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Thiscall_SuppressGCTransition), typeof(CallConvThiscall))]
+ [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Fastcall_SuppressGCTransition), typeof(CallConvFastcall))]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void UnmanagedCallConv_PhysicalModifiers_Modified(string methodName, Type callingConventionRuntime)
+ {
+ Type suppressGcTransitionType = typeof(CallConvSuppressGCTransition).Project();
+ Type callingConvention = callingConventionRuntime.Project();
+ Type t = typeof(FunctionPointerHolder).Project();
+ MethodInfo m = t.GetMethod(methodName, Bindings);
+
+ Type fnPtrType = m.GetParameters()[0].GetModifiedParameterType();
+ Assert.True(fnPtrType.IsUnmanagedFunctionPointer);
+
+ Type[] callConvs = fnPtrType.GetFunctionPointerCallingConventions();
+ Assert.Equal(2, callConvs.Length);
+ Assert.Equal(suppressGcTransitionType, callConvs[0]);
+ Assert.Equal(callingConvention, callConvs[1]);
+
+ Type returnType = fnPtrType.GetFunctionPointerReturnType();
+ Assert.Equal(2, returnType.GetOptionalCustomModifiers().Length);
+ Assert.Equal(suppressGcTransitionType, returnType.GetOptionalCustomModifiers()[0]);
+ Assert.Equal(callingConvention, returnType.GetOptionalCustomModifiers()[1]);
+ Assert.Equal(0, returnType.GetRequiredCustomModifiers().Length);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void GenericTypeParameter()
+ {
+ Type holder = typeof(FunctionPointerHolder).Project();
+ FieldInfo f = holder.GetField(nameof(FunctionPointerHolder._genericType), Bindings);
+ Type propType = f.FieldType.GetProperty("MyProp").GetModifiedPropertyType();
+
+ // Requires skipping past the return parameter metadata in order to get to the metadata for the first parameter.
+ Type paramType = propType.GetFunctionPointerParameterTypes()[1];
+ Type[] cc = paramType.GetFunctionPointerCallingConventions();
+ Assert.Equal(1, cc.Length);
+ Assert.Equal(typeof(CallConvCdecl).Project(), cc[0]);
+ }
+
+ private unsafe partial class FunctionPointerHolder
+ {
+ public delegate* unmanaged[Cdecl, MemberFunction] MethodUnmanagedReturnValue_DifferentCallingConventions1() => default;
+ public delegate* unmanaged[Stdcall, MemberFunction] MethodUnmanagedReturnValue_DifferentCallingConventions2() => default;
+
+#pragma warning disable 0649
+ public MyGenericClass[]> _genericType;
+#pragma warning restore 0649
+
+ // Methods to verify calling conventions and synthesized modopts.
+ // The non-SuppressGCTransition variants are encoded with the CallKind byte.
+ // The SuppressGCTransition variants are encoded as modopts (CallKind is "Unmananged").
+ public void MethodCallConv_Managed(delegate* managed[ f) { }
+ public void MethodCallConv_Cdecl(delegate* unmanaged[Cdecl] f) { }
+ public void MethodCallConv_Cdecl_SuppressGCTransition(delegate* unmanaged[Cdecl, SuppressGCTransition] f) { }
+ public void MethodCallConv_Stdcall(delegate* unmanaged[Stdcall] f) { }
+ public void MethodCallConv_Stdcall_SuppressGCTransition(delegate* unmanaged[Stdcall, SuppressGCTransition] f) { }
+ public void MethodCallConv_Thiscall(delegate* unmanaged[Thiscall] f) { }
+ public void MethodCallConv_Thiscall_SuppressGCTransition(delegate* unmanaged[Thiscall, SuppressGCTransition] f) { }
+ public void MethodCallConv_Fastcall(delegate* unmanaged[Fastcall] f) { }
+ public void MethodCallConv_Fastcall_SuppressGCTransition(delegate* unmanaged[Fastcall, SuppressGCTransition] f) { }
+
+ public class MyClass { }
+
+ public unsafe class MyGenericClass
+ {
+ public delegate*, void> MyProp { get; }
+ }
+ }
+ }
+}
diff --git a/src/libraries/Common/tests/System/FunctionPointerEqualityTests.cs b/src/libraries/Common/tests/System/FunctionPointerEqualityTests.cs
new file mode 100644
index 0000000000000..ad392c7e4042f
--- /dev/null
+++ b/src/libraries/Common/tests/System/FunctionPointerEqualityTests.cs
@@ -0,0 +1,133 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Reflection;
+using System.Reflection.Tests;
+using Xunit;
+
+namespace System.Tests.Types
+{
+ // Also see ModifiedTypeTests which tests custom modifiers.
+ // Unmodified Type instances are cached and keyed by the runtime.
+ // Modified Type instances are created for each member.
+ public partial class FunctionPointerEqualityTests
+ {
+ private const BindingFlags Bindings = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly;
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void DifferentReturnValue()
+ {
+ Type t = typeof(FunctionPointerHolder).Project();
+
+ MethodInfo m1 = t.GetMethod(nameof(FunctionPointerHolder.MethodUnmanagedReturnValue1), Bindings);
+ Type fcnPtr1 = m1.ReturnType;
+ Assert.True(fcnPtr1.IsFunctionPointer);
+
+ MethodInfo m2 = t.GetMethod(nameof(FunctionPointerHolder.MethodUnmanagedReturnValue2), Bindings);
+ Type fcnPtr2 = m2.ReturnType;
+ Assert.True(fcnPtr2.IsFunctionPointer);
+ Assert.False(fcnPtr1.IsFunctionPointerEqual(fcnPtr2));
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void ObjectEquals_ModifiedTypes()
+ {
+ Type holder = typeof(FunctionPointerHolder).Project();
+
+ MethodInfo m1 = holder.GetMethod(nameof(FunctionPointerHolder.MethodIntReturnValue1), Bindings);
+ Type t1 = m1.ReturnParameter.GetModifiedParameterType();
+ Assert.NotSame(t1, t1.UnderlyingSystemType);
+
+ MethodInfo m2 = holder.GetMethod(nameof(FunctionPointerHolder.MethodIntReturnValue2), Bindings);
+ Type t2 = m2.ReturnParameter.GetModifiedParameterType();
+
+ Assert.NotSame(t1, t2);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void ObjectEquals_OneSideModifiedType()
+ {
+ Type holder = typeof(FunctionPointerHolder).Project();
+
+ MethodInfo m1 = holder.GetMethod(nameof(FunctionPointerHolder.MethodIntReturnValue1), Bindings);
+ Type modifiedType = m1.ReturnParameter.GetModifiedParameterType();
+ Type t = typeof(int).Project();
+
+ Assert.NotSame(modifiedType, modifiedType.UnderlyingSystemType);
+ Assert.NotSame(modifiedType, t);
+ }
+
+ [Theory]
+ [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Cdecl), nameof(FunctionPointerHolder.MethodCallConv_Cdecl_SuppressGCTransition))]
+ [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Cdecl), nameof(FunctionPointerHolder.MethodCallConv_Stdcall))]
+ [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Cdecl), nameof(FunctionPointerHolder.MethodCallConv_Thiscall))]
+ [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Cdecl), nameof(FunctionPointerHolder.MethodCallConv_Fastcall))]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void CallingConvention_Unmodified(string methodName1, string methodName2)
+ {
+ Type t = typeof(FunctionPointerHolder).Project();
+ MethodInfo m1 = t.GetMethod(methodName1, Bindings);
+ MethodInfo m2 = t.GetMethod(methodName2, Bindings);
+
+ Type fnPtrType1 = m1.GetParameters()[0].ParameterType;
+ Type fnPtrType2 = m2.GetParameters()[0].ParameterType;
+
+ Assert.True(fnPtrType1.IsFunctionPointerEqual(fnPtrType2));
+ }
+
+ [Theory]
+ [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Cdecl), nameof(FunctionPointerHolder.MethodCallConv_Cdecl_SuppressGCTransition))]
+ [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Cdecl), nameof(FunctionPointerHolder.MethodCallConv_Stdcall))]
+ [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Cdecl), nameof(FunctionPointerHolder.MethodCallConv_Thiscall))]
+ [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Cdecl), nameof(FunctionPointerHolder.MethodCallConv_Fastcall))]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void CallingConvention_Modified(string methodName1, string methodName2)
+ {
+ Type t = typeof(FunctionPointerHolder).Project();
+ MethodInfo m1 = t.GetMethod(methodName1, Bindings);
+ MethodInfo m2 = t.GetMethod(methodName2, Bindings);
+
+ Type fnPtrType1 = m1.GetParameters()[0].GetModifiedParameterType();
+ Type fnPtrType2 = m2.GetParameters()[0].GetModifiedParameterType();
+
+ // Modified types don't support Equals, so just verify instance.
+ Assert.NotSame(fnPtrType1, fnPtrType2);
+ }
+
+ private unsafe class FunctionPointerHolder
+ {
+#pragma warning disable 0649
+ public delegate* managed Field_Int;
+ public delegate* managed Field_DateOnly; // Verify non-primitive
+#pragma warning restore 0649
+
+ public delegate* managed Prop_Int { get; }
+ public delegate* managed Prop_DateOnly { get; }
+ public delegate* managed MethodReturnValue_Int() => default;
+ public delegate* managed MethodReturnValue_DateOnly() => default;
+
+ public delegate* unmanaged MethodUnmanagedReturnValue1() => default;
+ public delegate* unmanaged MethodUnmanagedReturnValue2() => default;
+
+ public int MethodIntReturnValue1() => default;
+ public int MethodIntReturnValue2() => default;
+
+ // Methods to verify calling conventions and synthesized modopts.
+ // The non-SuppressGCTransition variants are encoded with the CallKind byte.
+ // The SuppressGCTransition variants are encoded as modopts (CallKind is "Unmananged").
+ public void MethodCallConv_Cdecl(delegate* unmanaged[Cdecl] f) { }
+ public void MethodCallConv_Cdecl_SuppressGCTransition(delegate* unmanaged[Cdecl, SuppressGCTransition] f) { }
+ public void MethodCallConv_Stdcall(delegate* unmanaged[Stdcall] f) { }
+ public void MethodCallConv_Thiscall(delegate* unmanaged[Thiscall] f) { }
+ public void MethodCallConv_Fastcall(delegate* unmanaged[Fastcall] f) { }
+ }
+ }
+}
diff --git a/src/libraries/Common/tests/System/FunctionPointerTests.cs b/src/libraries/Common/tests/System/FunctionPointerTests.cs
new file mode 100644
index 0000000000000..e80a1528d27a7
--- /dev/null
+++ b/src/libraries/Common/tests/System/FunctionPointerTests.cs
@@ -0,0 +1,301 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Tests;
+using Xunit;
+
+namespace System.Tests.Types
+{
+ // Also see ModifiedTypeTests which tests custom modifiers.
+ public partial class FunctionPointerTests
+ {
+ private const BindingFlags Bindings = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly;
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void TypeMembers()
+ {
+ // Get an arbitrary function pointer
+ TypeInfo t = (TypeInfo)typeof(FunctionPointerHolder).Project().GetField(nameof(FunctionPointerHolder.ToString_1), Bindings).FieldType;
+
+ // Function pointer relevant members:
+ Assert.Equal("System.Void()", t.ToString());
+ Assert.Null(t.FullName);
+ Assert.Null(t.AssemblyQualifiedName);
+ Assert.Equal(string.Empty, t.Name);
+ Assert.Null(t.Namespace);
+ Assert.True(t.IsFunctionPointer);
+ Assert.False(t.IsPointer);
+ Assert.False(t.IsUnmanagedFunctionPointer);
+
+ // Common for all function pointers:
+ Assert.NotNull(t.Assembly);
+ Assert.Equal(TypeAttributes.Public, t.Attributes);
+ Assert.Null(t.BaseType);
+ Assert.False(t.ContainsGenericParameters);
+ Assert.False(t.DeclaredConstructors.Any());
+ Assert.False(t.DeclaredEvents.Any());
+ Assert.False(t.DeclaredFields.Any());
+ Assert.False(t.DeclaredMembers.Any());
+ Assert.False(t.DeclaredMethods.Any());
+ Assert.False(t.DeclaredNestedTypes.Any());
+ Assert.False(t.DeclaredProperties.Any());
+ Assert.Null(t.DeclaringType);
+ Assert.Equal(Guid.Empty, t.GUID);
+ Assert.Throws(() => t.GenericParameterAttributes);
+ Assert.Throws(() => t.GenericParameterPosition);
+ Assert.Equal(0, t.GenericTypeArguments.Length);
+ Assert.Equal(0, t.GenericTypeParameters.Length);
+ Assert.False(t.HasElementType);
+ Assert.False(t.IsAbstract);
+ Assert.True(t.IsAnsiClass);
+ Assert.False(t.IsArray);
+ Assert.False(t.IsAutoClass);
+ Assert.True(t.IsAutoLayout);
+ Assert.False(t.IsByRef);
+ Assert.False(t.IsByRefLike);
+ Assert.False(t.IsCOMObject);
+ Assert.True(t.IsClass);
+ Assert.False(t.IsAbstract);
+ Assert.False(t.IsConstructedGenericType);
+ Assert.False(t.IsContextful);
+ Assert.False(t.IsEnum);
+ Assert.False(t.IsExplicitLayout);
+ Assert.False(t.IsGenericMethodParameter);
+ Assert.False(t.IsGenericParameter);
+ Assert.False(t.IsGenericType);
+ Assert.False(t.IsGenericTypeDefinition);
+ Assert.False(t.IsGenericTypeParameter);
+ Assert.False(t.IsImport);
+ Assert.False(t.IsInterface);
+ Assert.False(t.IsLayoutSequential);
+ Assert.False(t.IsMarshalByRef);
+ Assert.False(t.IsNested);
+ Assert.False(t.IsNestedAssembly);
+ Assert.False(t.IsNestedFamANDAssem);
+ Assert.False(t.IsNestedFamORAssem);
+ Assert.False(t.IsNestedFamily);
+ Assert.False(t.IsNestedPrivate);
+ Assert.False(t.IsNestedPublic);
+ Assert.False(t.IsNotPublic);
+ Assert.False(t.IsPrimitive);
+ Assert.True(t.IsPublic);
+ Assert.False(t.IsSZArray);
+ Assert.False(t.IsSealed);
+
+ if (FunctionPointerTestsExtensions.IsMetadataLoadContext)
+ {
+ Assert.Throws(() => t.IsSecurityCritical);
+ Assert.Throws(() => t.IsSecuritySafeCritical);
+ Assert.Throws(() => t.IsSecurityTransparent);
+ }
+ else
+ {
+ Assert.True(t.IsSecurityCritical);
+ Assert.False(t.IsSecuritySafeCritical);
+ Assert.False(t.IsSecurityTransparent);
+ }
+
+ Assert.False(t.IsSerializable);
+ Assert.False(t.IsSignatureType);
+ Assert.False(t.IsSpecialName);
+ Assert.False(t.IsTypeDefinition);
+ Assert.False(t.IsUnicodeClass);
+ Assert.False(t.IsValueType);
+ Assert.False(t.IsVariableBoundArray);
+ Assert.True(t.IsVisible);
+ Assert.Equal(MemberTypes.TypeInfo, t.MemberType);
+ Assert.True(t.MetadataToken != 0);
+ Assert.NotNull(t.Module);
+ Assert.Null(t.ReflectedType);
+ Assert.Null(t.TypeInitializer);
+ Assert.Throws(() => t.GetArrayRank());
+ Assert.Null(t.GetElementType());
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void NonFunctionPointerThrows()
+ {
+ Assert.Throws(() => typeof(int).GetFunctionPointerCallingConventions());
+ Assert.Throws(() => typeof(int).GetFunctionPointerParameterTypes());
+ Assert.Throws(() => typeof(int).GetFunctionPointerReturnType());
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void TestToString()
+ {
+ // Function pointer types are inline in metadata and can't be loaded independently so they do not support the
+ // MetadataLoadContext Type.Project() test extension so we use fields and project on the owning class.
+ Assert.Equal("System.Void()", GetType(1).ToString()); // delegate*
+ Assert.Equal("System.Void()", GetType(2).ToString()); // delegate*unmanaged
+ Assert.Equal("System.Int32()", GetType(3).ToString()); // delegate*
+ Assert.Equal("System.Int32()*", GetType(4).ToString()); // delegate**
+ Assert.Equal("System.Int32()[]", GetType(5).ToString()); // delegate*[]
+ Assert.Equal("System.Int32()", GetType(6).
+ GetElementType().ToString()); // delegate*[]
+ Assert.Equal("System.Int32()*[]", GetType(7).ToString()); // delegate**[]
+ Assert.Equal("System.Int32()()", GetType(8).ToString()); // delegate*>
+ Assert.Equal("System.Boolean(System.String(System.Int32))",
+ GetType(9).ToString()); // delegate*, bool>
+
+ Type GetType(int i) => typeof(FunctionPointerHolder).Project().GetField("ToString_" + i, Bindings).FieldType;
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void FunctionPointerReturn()
+ {
+ Type t = typeof(FunctionPointerHolder).Project();
+
+ MethodInfo m1 = t.GetMethod(nameof(FunctionPointerHolder.MethodReturnValue1), Bindings);
+ Type fcnPtr1 = m1.ReturnType;
+ Assert.True(fcnPtr1.IsFunctionPointer);
+
+ MethodInfo m2 = t.GetMethod(nameof(FunctionPointerHolder.MethodReturnValue2), Bindings);
+ Type fcnPtr2 = m2.ReturnType;
+ Assert.True(fcnPtr2.IsFunctionPointer);
+
+ Assert.True(fcnPtr1.IsFunctionPointerEqual(fcnPtr2));
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void RequiredModifiers()
+ {
+ Type t = typeof(FunctionPointerHolder).Project();
+ MethodInfo m = t.GetMethod(nameof(FunctionPointerHolder.RequiredModifiers), Bindings);
+ Type fcnPtr1 = m.ReturnParameter.GetModifiedParameterType();
+
+ Type[] parameters = fcnPtr1.GetFunctionPointerParameterTypes();
+ Assert.Equal(2, parameters.Length);
+ Assert.Equal(1, parameters[0].GetRequiredCustomModifiers().Length);
+ Assert.Equal(typeof(Runtime.InteropServices.InAttribute).Project(), parameters[0].GetRequiredCustomModifiers()[0]);
+ Assert.Equal(1, parameters[1].GetRequiredCustomModifiers().Length);
+ Assert.Equal(typeof(Runtime.InteropServices.OutAttribute).Project(), parameters[1].GetRequiredCustomModifiers()[0]);
+ }
+
+ [Theory]
+ [InlineData(nameof(FunctionPointerHolder.MethodReturnValue1),
+ "MethodReturnValue1()",
+ "Int32",
+ "System.Int32()")]
+ [InlineData(nameof(FunctionPointerHolder.SeveralArguments),
+ "SeveralArguments()",
+ "Double",
+ "System.Double(System.String, System.Boolean*&, System.Tests.Types.FunctionPointerTests+FunctionPointerHolder+MyClass, System.Tests.Types.FunctionPointerTests+FunctionPointerHolder+MyStruct&)",
+ "String", "Boolean*&", "MyClass", "MyStruct&")]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void MethodInfo(
+ string methodName,
+ string methodToStringPostfix,
+ string expectedFcnPtrReturnName,
+ string expectedFcnPtrFullName,
+ params string[] expectedArgNames)
+ {
+ Type t = typeof(FunctionPointerHolder).Project();
+ MethodInfo m = t.GetMethod(methodName, Bindings);
+ Assert.Equal(expectedFcnPtrFullName + " " + methodToStringPostfix, m.ToString());
+
+ Type fnPtrType = m.ReturnParameter.GetModifiedParameterType();
+ Assert.Null(fnPtrType.FullName);
+ Assert.Null(fnPtrType.AssemblyQualifiedName);
+ Assert.Equal("", fnPtrType.Name);
+
+ Assert.Equal(fnPtrType.GetFunctionPointerReturnType().Name, expectedFcnPtrReturnName);
+
+ for (int i = 0; i < expectedArgNames.Length; i++)
+ {
+ Assert.Equal(fnPtrType.GetFunctionPointerParameterTypes()[i].Name, expectedArgNames[i]);
+ }
+ }
+
+ [Theory]
+ [InlineData(nameof(FunctionPointerHolder.Prop_Int), "System.Int32()")]
+ [InlineData(nameof(FunctionPointerHolder.Prop_MyClass), "System.Tests.Types.FunctionPointerTests+FunctionPointerHolder+MyClass()")]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void Property(string name, string expectedToString)
+ {
+ Type t = typeof(FunctionPointerHolder).Project();
+ PropertyInfo p = t.GetProperty(name, Bindings);
+ Assert.Equal(expectedToString + " " + name, p.ToString());
+
+ Type fnPtrType = p.PropertyType;
+ Assert.Equal(expectedToString, fnPtrType.ToString());
+ VerifyFieldOrProperty(fnPtrType);
+
+ fnPtrType = p.GetModifiedPropertyType();
+ Assert.Equal(expectedToString, fnPtrType.ToString());
+ VerifyFieldOrProperty(fnPtrType);
+ }
+
+ [Theory]
+ [InlineData(nameof(FunctionPointerHolder.Field_Int), "System.Int32()")]
+ [InlineData(nameof(FunctionPointerHolder.Field_MyClass), "System.Tests.Types.FunctionPointerTests+FunctionPointerHolder+MyClass()")]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void Field(string name, string expectedToString)
+ {
+ Type t = typeof(FunctionPointerHolder).Project();
+ FieldInfo f = t.GetField(name, Bindings);
+ Assert.Equal(expectedToString + " " + name, f.ToString());
+
+ Type fnPtrType = f.FieldType;
+ Assert.Equal(expectedToString, fnPtrType.ToString());
+ VerifyFieldOrProperty(fnPtrType);
+
+ fnPtrType = f.GetModifiedFieldType();
+ Assert.Equal(expectedToString, fnPtrType.ToString());
+ VerifyFieldOrProperty(fnPtrType);
+ }
+
+ private static void VerifyFieldOrProperty(Type fnPtrType)
+ {
+ Assert.Null(fnPtrType.FullName);
+ Assert.Null(fnPtrType.AssemblyQualifiedName);
+ Assert.Equal("", fnPtrType.Name);
+ Assert.Equal(Type.EmptyTypes, fnPtrType.GetFunctionPointerCallingConventions());
+ Assert.Equal(Type.EmptyTypes, fnPtrType.GetFunctionPointerReturnType().GetRequiredCustomModifiers());
+ }
+
+ private unsafe class FunctionPointerHolder
+ {
+#pragma warning disable 0649
+ public delegate* ToString_1;
+ public delegate*unmanaged ToString_2;
+ public delegate* ToString_3;
+ public delegate** ToString_4;
+ public delegate*[] ToString_5;
+ public delegate*[] ToString_6;
+ public delegate**[] ToString_7;
+ public delegate*> ToString_8;
+ public delegate*, bool> ToString_9;
+
+ public delegate* managed Field_Int;
+ public delegate* managed Field_MyClass;
+#pragma warning restore 0649
+
+ public delegate* managed Prop_Int { get; }
+ public delegate* managed Prop_MyClass { get; }
+
+ public delegate* managed MethodReturnValue1() => default;
+ public delegate* managed MethodReturnValue2() => default;
+
+ public delegate* unmanaged[Stdcall, MemberFunction] SeveralArguments() => default;
+ public delegate* RequiredModifiers() => default;
+
+ public class MyClass { }
+ public struct MyStruct { }
+ }
+ }
+}
diff --git a/src/libraries/Common/tests/System/ModifiedTypeTests.cs b/src/libraries/Common/tests/System/ModifiedTypeTests.cs
new file mode 100644
index 0000000000000..ba973b17bb25c
--- /dev/null
+++ b/src/libraries/Common/tests/System/ModifiedTypeTests.cs
@@ -0,0 +1,782 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Tests;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Xunit;
+
+namespace System.Tests.Types
+{
+ // The "_Unmodified" tests use GetXxxType() and are essentially a baseline since they don't return modifiers.
+ // The "_Modified" tests are based on the same types but use GetModifiedXxxType() in order to return the modifiers.
+ public partial class ModifiedTypeTests
+ {
+ private const BindingFlags Bindings = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly;
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void TypeMembers()
+ {
+ FieldInfo fi = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._volatileInt), Bindings);
+
+ Type unmodifiedType = fi.FieldType;
+ CommonMembers(unmodifiedType);
+ UnmodifiedTypeMembers(unmodifiedType);
+
+ Type modifiedType = fi.GetModifiedFieldType();
+ CommonMembers(modifiedType);
+ ModifiedTypeMembers(modifiedType);
+
+ void CommonMembers(Type t)
+ {
+ Assert.Equal("System.Int32", t.ToString());
+ Assert.NotNull(t.AssemblyQualifiedName);
+ Assert.Equal("Int32", t.Name);
+ Assert.Equal("System", t.Namespace);
+ Assert.False(t.IsFunctionPointer);
+ Assert.False(t.IsPointer);
+ Assert.False(t.IsUnmanagedFunctionPointer);
+ Assert.NotNull(t.Assembly);
+ Assert.True(t.Attributes != default);
+ Assert.False(t.ContainsGenericParameters);
+ Assert.False(t.ContainsGenericParameters);
+ var _ = t.GUID; // Just ensure it doesn't throw.
+ Assert.Throws(() => t.GenericParameterAttributes);
+ Assert.Throws(() => t.GenericParameterPosition);
+ Assert.Throws(() => t.GetFunctionPointerCallingConventions());
+ Assert.Throws(() => t.GetFunctionPointerParameterTypes());
+ Assert.Throws(() => t.GetFunctionPointerReturnType());
+ Assert.False(t.HasElementType);
+ Assert.False(t.IsAbstract);
+ Assert.True(t.IsAnsiClass);
+ Assert.False(t.IsArray);
+ Assert.False(t.IsAutoClass);
+ Assert.False(t.IsAutoLayout);
+ Assert.False(t.IsByRef);
+ Assert.False(t.IsByRefLike);
+ Assert.False(t.IsCOMObject);
+ Assert.False(t.IsClass);
+ Assert.False(t.IsAbstract);
+ Assert.False(t.IsConstructedGenericType);
+ Assert.False(t.IsContextful);
+ Assert.False(t.IsEnum);
+ Assert.False(t.IsExplicitLayout);
+ Assert.False(t.IsGenericMethodParameter);
+ Assert.False(t.IsGenericParameter);
+ Assert.False(t.IsGenericType);
+ Assert.False(t.IsGenericTypeDefinition);
+ Assert.False(t.IsGenericTypeParameter);
+ Assert.False(t.IsImport);
+ Assert.False(t.IsInterface);
+ Assert.True(t.IsLayoutSequential);
+ Assert.False(t.IsMarshalByRef);
+ Assert.False(t.IsNotPublic);
+ Assert.True(t.IsPublic);
+ Assert.False(t.IsSZArray);
+ Assert.True(t.IsSealed);
+
+ if (FunctionPointerTestsExtensions.IsMetadataLoadContext)
+ {
+ Assert.Throws(() => t.IsSecurityCritical);
+ Assert.Throws(() => t.IsSecuritySafeCritical);
+ Assert.Throws(() => t.IsSecurityTransparent);
+ }
+ else
+ {
+ Assert.True(t.IsSecurityCritical);
+ Assert.False(t.IsSecuritySafeCritical);
+ Assert.False(t.IsSecurityTransparent);
+ }
+
+ Assert.True(t.IsSerializable);
+ Assert.False(t.IsSignatureType);
+ Assert.False(t.IsSpecialName);
+ Assert.True(t.IsTypeDefinition);
+ Assert.False(t.IsUnicodeClass);
+ Assert.True(t.IsValueType);
+ Assert.False(t.IsVariableBoundArray);
+ Assert.Equal(MemberTypes.TypeInfo, t.MemberType);
+ Assert.NotNull(t.Module);
+
+ Assert.False(t.IsNestedAssembly);
+ Assert.False(t.IsNestedFamANDAssem);
+ Assert.False(t.IsNestedFamORAssem);
+ Assert.False(t.IsNestedFamily);
+ Assert.False(t.IsNestedPrivate);
+ Assert.False(t.IsNestedPublic);
+ Assert.Throws(() => t.GetArrayRank());
+ Assert.Null(t.GetElementType());
+ Assert.False(t.GenericTypeArguments.Any());
+ }
+
+ void UnmodifiedTypeMembers(Type t)
+ {
+ Assert.False(t.GetOptionalCustomModifiers().Any());
+ Assert.False(t.GetRequiredCustomModifiers().Any());
+ Assert.True(t.IsPrimitive);
+ Assert.True(((object)t).Equals(t));
+ Assert.True(t.Equals(t));
+ Assert.Equal(t.GetHashCode(), t.GetHashCode());
+ Assert.NotNull(t.BaseType);
+ Assert.Null(t.DeclaringType);
+ Assert.Null(t.ReflectedType);
+ Assert.Null(t.TypeInitializer);
+ Assert.True(t.MetadataToken != 0);
+ Assert.False(t.IsNested);
+ Assert.True(t.IsVisible);
+ }
+
+ void ModifiedTypeMembers(Type t)
+ {
+ Assert.False(t.GetOptionalCustomModifiers().Any());
+ Assert.True(t.GetRequiredCustomModifiers().Any()); // The volatile modifier.
+ Assert.True(t.IsPrimitive);
+ Assert.Throws(() => ((object)t).Equals(t));
+ Assert.Throws(() => t.Equals(t));
+ Assert.Throws(() => t.GetHashCode());
+ Assert.Throws(() => t.BaseType);
+ Assert.Throws(() => t.DeclaringType);
+ Assert.Throws(() => t.ReflectedType);
+ Assert.Throws(() => t.TypeInitializer);
+ Assert.Throws(() => t.MetadataToken);
+
+ // These can call DeclaringType which is not supported.
+ Assert.Throws(() => t.IsNested);
+ Assert.Throws(() => t.IsVisible);
+ }
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void Fields_Modified()
+ {
+ Type volatileInt = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._volatileInt), Bindings).GetModifiedFieldType();
+ Verify(volatileInt);
+
+ Type volatileIntArray = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._volatileIntArray), Bindings).GetModifiedFieldType();
+ Verify(volatileIntArray);
+
+ Type volatileIntPointer = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._volatileIntPointer), Bindings).GetModifiedFieldType();
+ Verify(volatileIntPointer);
+ Type volatileIntPointerElementType = volatileIntPointer.GetElementType();
+ Assert.True(IsModifiedType(volatileIntPointerElementType));
+ Assert.True(ReferenceEquals(volatileIntPointerElementType.UnderlyingSystemType, typeof(int).Project()));
+ Assert.Equal(0, volatileIntPointerElementType.UnderlyingSystemType.GetRequiredCustomModifiers().Length);
+ Assert.Equal(0, volatileIntPointerElementType.UnderlyingSystemType.GetOptionalCustomModifiers().Length);
+
+ Type volatileFcnPtr = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._volatileFcnPtr), Bindings).GetModifiedFieldType();
+ Verify(volatileFcnPtr);
+ Assert.True(IsModifiedType(volatileFcnPtr.GetFunctionPointerReturnType()));
+ Assert.Equal(1, volatileFcnPtr.GetFunctionPointerParameterTypes().Length);
+ Assert.True(IsModifiedType(volatileFcnPtr.GetFunctionPointerParameterTypes()[0]));
+
+ void Verify(Type type)
+ {
+ Assert.True(IsModifiedType(type));
+
+ Assert.Equal(1, type.GetRequiredCustomModifiers().Length);
+ Assert.Equal(typeof(IsVolatile).Project(), type.GetRequiredCustomModifiers()[0]);
+
+ Assert.Equal(0, type.GetOptionalCustomModifiers().Length);
+
+ Assert.Equal(0, type.UnderlyingSystemType.GetRequiredCustomModifiers().Length);
+ Assert.Equal(0, type.UnderlyingSystemType.GetOptionalCustomModifiers().Length);
+ }
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void Fields_Generic_Unmodified()
+ {
+ Type arrayGenericFcnPtr = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._arrayGenericFcnPtr), Bindings).FieldType;
+ Assert.True(arrayGenericFcnPtr.IsGenericType);
+ Assert.False(arrayGenericFcnPtr.IsGenericTypeDefinition);
+ Assert.False(IsModifiedType(arrayGenericFcnPtr));
+
+ Type genericArg = arrayGenericFcnPtr.GetGenericArguments()[0];
+ Assert.False(IsModifiedType(genericArg));
+
+ Type fcnPtr = genericArg.GetElementType();
+ Assert.True(fcnPtr.IsFunctionPointer);
+ Assert.False(IsModifiedType(fcnPtr));
+
+ Assert.Equal(1, fcnPtr.GetFunctionPointerParameterTypes().Length);
+ Type paramType = fcnPtr.GetFunctionPointerParameterTypes()[0];
+ Assert.False(IsModifiedType(paramType));
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void Fields_Generic_Modified()
+ {
+ Type arrayGenericFcnPtr = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._arrayGenericFcnPtr), Bindings).GetModifiedFieldType();
+ Assert.True(IsModifiedType(arrayGenericFcnPtr));
+ Assert.True(arrayGenericFcnPtr.IsGenericType);
+ Assert.False(arrayGenericFcnPtr.IsGenericTypeDefinition);
+
+ Type genericArg = arrayGenericFcnPtr.GetGenericArguments()[0];
+ Assert.True(IsModifiedType(genericArg));
+
+ Type fcnPtr = genericArg.GetElementType();
+ Assert.True(fcnPtr.IsFunctionPointer);
+ Assert.True(IsModifiedType(fcnPtr));
+
+ Assert.Equal(1, fcnPtr.GetFunctionPointerParameterTypes().Length);
+ Type paramType = fcnPtr.GetFunctionPointerParameterTypes()[0];
+ Assert.True(IsModifiedType(paramType));
+ Assert.Equal(1, paramType.GetRequiredCustomModifiers().Length);
+ Assert.Equal(typeof(OutAttribute).Project(), paramType.GetRequiredCustomModifiers()[0]);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void Methods_OpenGeneric_Unmodified()
+ {
+ MethodInfo mi = typeof(ModifiedTypeHolder).Project().GetMethod(nameof(ModifiedTypeHolder.M_ArrayOpenGenericFcnPtr), Bindings);
+ Assert.Equal(1, mi.GetGenericArguments().Length);
+ Type p0 = mi.GetGenericArguments()[0];
+ Assert.True(p0.IsGenericMethodParameter);
+ Assert.False(IsModifiedType(p0));
+
+ Type arr = mi.GetParameters()[1].ParameterType;
+ Assert.False(IsModifiedType(arr));
+
+ Type p1 = arr.GetElementType();
+ Assert.True(p1.IsFunctionPointer);
+ Assert.False(p1.IsGenericTypeParameter);
+ Assert.False(IsModifiedType(p1));
+ Assert.Equal(1, p1.GetFunctionPointerParameterTypes().Length);
+ Type paramType = p1.GetFunctionPointerParameterTypes()[0];
+ Assert.Equal(0, paramType.GetRequiredCustomModifiers().Length);
+ Assert.False(IsModifiedType(paramType));
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void Methods_OpenGeneric_Modified()
+ {
+ MethodInfo mi = typeof(ModifiedTypeHolder).Project().GetMethod(nameof(ModifiedTypeHolder.M_ArrayOpenGenericFcnPtr), Bindings);
+ Assert.Equal(1, mi.GetGenericArguments().Length);
+ Type p0 = mi.GetGenericArguments()[0];
+ Assert.True(p0.IsGenericMethodParameter);
+ Assert.False(IsModifiedType(p0));
+
+ Type arr = mi.GetParameters()[1].GetModifiedParameterType();
+ Assert.True(IsModifiedType(arr));
+
+ Type p1 = arr.GetElementType();
+ Assert.True(p1.IsFunctionPointer);
+ Assert.False(p1.IsGenericTypeParameter);
+ Assert.True(IsModifiedType(p1));
+
+ Assert.Equal(1, p1.GetFunctionPointerParameterTypes().Length);
+ Type paramType = p1.GetFunctionPointerParameterTypes()[0];
+ Assert.True(IsModifiedType(paramType));
+ Assert.Equal(1, paramType.GetRequiredCustomModifiers().Length);
+ Assert.Equal(typeof(OutAttribute).Project(), paramType.GetRequiredCustomModifiers()[0]);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void Fields_Unmodified()
+ {
+ Type volatileInt = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._volatileInt), Bindings).FieldType;
+ Verify(volatileInt);
+
+ Type volatileIntArray = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._volatileIntArray), Bindings).FieldType;
+ Verify(volatileIntArray);
+
+ Type volatileIntPointer = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._volatileIntPointer), Bindings).FieldType;
+ Verify(volatileIntPointer);
+ Type volatileIntPointerElementType = volatileIntPointer.GetElementType();
+ Assert.False(IsModifiedType(volatileIntPointerElementType));
+ Assert.True(ReferenceEquals(volatileIntPointerElementType, typeof(int).Project()));
+ Assert.Equal(0, volatileIntPointerElementType.GetRequiredCustomModifiers().Length);
+ Assert.Equal(0, volatileIntPointerElementType.GetOptionalCustomModifiers().Length);
+
+ Type volatileFcnPtr = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._volatileFcnPtr), Bindings).FieldType;
+ Verify(volatileFcnPtr);
+ Assert.False(IsModifiedType(volatileFcnPtr.GetFunctionPointerReturnType()));
+ Assert.Equal(1, volatileFcnPtr.GetFunctionPointerParameterTypes().Length);
+ Assert.False(IsModifiedType(volatileFcnPtr.GetFunctionPointerParameterTypes()[0]));
+
+ void Verify(Type type)
+ {
+ Assert.False(IsModifiedType(type));
+ Assert.Equal(0, type.GetRequiredCustomModifiers().Length);
+ Assert.Equal(0, type.GetOptionalCustomModifiers().Length);
+ }
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void Fields_Parameterized_Basic()
+ {
+ Type ptr_ptr_int = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._ptr_ptr_int), Bindings).GetModifiedFieldType();
+ Verify(ptr_ptr_int);
+ Assert.True(ptr_ptr_int.UnderlyingSystemType.IsPointer);
+
+ Type array_ptr_int = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._array_ptr_int), Bindings).GetModifiedFieldType();
+ Verify(array_ptr_int);
+ Assert.True(array_ptr_int.UnderlyingSystemType.IsArray);
+
+ void Verify(Type type)
+ {
+ Assert.True(IsModifiedType(type));
+ Assert.False(IsModifiedType(type.UnderlyingSystemType));
+ Assert.True(IsModifiedType(type.GetElementType()));
+ Assert.Equal(typeof(int).Project(), ptr_ptr_int.GetElementType().UnderlyingSystemType.GetElementType());
+ }
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void Fields_Parameterized_FcnPtr()
+ {
+ Type ptr_fcnPtr = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._ptr_fcnPtr), Bindings).GetModifiedFieldType();
+ Assert.True(ptr_fcnPtr.IsPointer);
+ Assert.True(IsModifiedType(ptr_fcnPtr));
+ Verify(ptr_fcnPtr.GetElementType());
+
+ Type array_ptr_fcnPtr = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._array_ptr_fcnPtr), Bindings).GetModifiedFieldType();
+ Assert.True(array_ptr_fcnPtr.IsArray);
+ Assert.True(IsModifiedType(array_ptr_fcnPtr));
+ Assert.True(array_ptr_fcnPtr.GetElementType().IsPointer);
+ Assert.True(IsModifiedType(array_ptr_fcnPtr.GetElementType()));
+ Verify(array_ptr_fcnPtr.GetElementType().GetElementType());
+
+ Type fcnPtr_fcnPtrReturn = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._fcnPtr_fcnPtrReturn), Bindings).GetModifiedFieldType();
+ Assert.True(fcnPtr_fcnPtrReturn.GetFunctionPointerReturnType().IsFunctionPointer);
+ Assert.True(IsModifiedType(fcnPtr_fcnPtrReturn.GetFunctionPointerReturnType()));
+ Verify(fcnPtr_fcnPtrReturn.GetFunctionPointerReturnType());
+
+ void Verify(Type type)
+ {
+ Assert.True(IsModifiedType(type));
+ Assert.True(type.IsFunctionPointer);
+ Assert.Equal(0, type.GetFunctionPointerParameterTypes().Length);
+ Assert.NotSame(typeof(void).Project(), type.GetFunctionPointerReturnType());
+ Assert.Same(typeof(void).Project(), type.GetFunctionPointerReturnType().UnderlyingSystemType);
+ }
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void Fields_VerifyIdempotency()
+ {
+ // Call these again to ensure any backing caching strategy works.
+ Fields_Modified();
+ Fields_Unmodified();
+ Fields_Parameterized_Basic();
+ Fields_Parameterized_FcnPtr();
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void MethodParameters()
+ {
+ ParameterInfo[] parameters = typeof(ModifiedTypeHolder).Project().GetMethod(nameof(ModifiedTypeHolder.M_P0IntOut), Bindings).GetParameters();
+ Assert.True(IsModifiedType(parameters[0].GetModifiedParameterType()));
+
+ parameters = typeof(ModifiedTypeHolder).Project().GetMethod(nameof(ModifiedTypeHolder.M_P0FcnPtrOut), Bindings).GetParameters();
+ Type[] fnParameters = parameters[0].GetModifiedParameterType().GetFunctionPointerParameterTypes();
+ Assert.Equal(1, fnParameters.Length);
+ Assert.Equal(typeof(OutAttribute).Project(), fnParameters[0].GetRequiredCustomModifiers()[0]);
+
+ ParameterInfo returnParameter = typeof(ModifiedTypeHolder).Project().GetProperty(nameof(ModifiedTypeHolder.InitProperty_Int), Bindings).GetSetMethod().ReturnParameter;
+ Assert.Equal(1, returnParameter.GetRequiredCustomModifiers().Length);
+ Assert.Equal(1, returnParameter.GetModifiedParameterType().GetRequiredCustomModifiers().Length);
+ Assert.Equal(typeof(IsExternalInit).Project(), returnParameter.GetModifiedParameterType().GetRequiredCustomModifiers()[0]);
+
+ returnParameter = typeof(ModifiedTypeHolder).Project().GetProperty(nameof(ModifiedTypeHolder.Property_FcnPtr), Bindings).GetGetMethod().ReturnParameter;
+ Assert.True(returnParameter.ParameterType.IsFunctionPointer);
+ Assert.Equal(0, returnParameter.ParameterType.GetFunctionPointerParameterTypes()[0].GetRequiredCustomModifiers().Length);
+ Assert.Equal(1, returnParameter.GetModifiedParameterType().GetFunctionPointerParameterTypes()[0].GetRequiredCustomModifiers().Length);
+ Assert.Equal(typeof(OutAttribute).Project(), returnParameter.GetModifiedParameterType().GetFunctionPointerParameterTypes()[0].GetRequiredCustomModifiers()[0]);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void ConstructorParameters_Unmodified()
+ {
+ ParameterInfo[] parameters = typeof(ModifiedTypeHolder).Project().GetConstructors()[0].GetParameters();
+
+ Type param0 = parameters[0].ParameterType;
+ Assert.True(param0.IsFunctionPointer);
+ Assert.False(IsModifiedType(param0));
+ Type[] mods = param0.GetFunctionPointerParameterTypes()[0].GetRequiredCustomModifiers();
+ Assert.Equal(0, mods.Length);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void ConstructorParameters_Modified()
+ {
+ ParameterInfo[] parameters = typeof(ModifiedTypeHolder).Project().GetConstructors()[0].GetParameters();
+
+ Type param0 = parameters[0].GetModifiedParameterType();
+ Assert.True(param0.IsFunctionPointer);
+ Assert.True(IsModifiedType(param0));
+ Type[] mods = param0.GetFunctionPointerParameterTypes()[0].GetRequiredCustomModifiers();
+ Assert.Equal(1, mods.Length);
+ Assert.Equal(typeof(OutAttribute).Project(), mods[0]);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void FunctionPointerParameters_fcnPtrP0Out_Unmodified()
+ {
+ Type fcnPtr = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._fcnPtrP0Out), Bindings).GetModifiedFieldType();
+ Assert.True(fcnPtr.IsFunctionPointer);
+ Assert.Equal(1, fcnPtr.GetFunctionPointerParameterTypes().Length);
+ Assert.True(IsModifiedType(fcnPtr));
+ Assert.Equal(typeof(int).Project().MakeByRefType(), fcnPtr.GetFunctionPointerParameterTypes()[0].UnderlyingSystemType);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void FunctionPointerParameters_fcnPtrP0Out_Modified()
+ {
+ Type fcnPtr = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._fcnPtrP0Out), Bindings).FieldType;
+ Assert.True(fcnPtr.IsFunctionPointer);
+ Assert.Equal(1, fcnPtr.GetFunctionPointerParameterTypes().Length);
+ Assert.False(IsModifiedType(fcnPtr));
+ Assert.Equal(typeof(int).Project().MakeByRefType(), fcnPtr.GetFunctionPointerParameterTypes()[0]);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void FunctionPointerParameters_fcnPtr_fcnPtrP0Out_Unmodified()
+ {
+ Type fcnPtr = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._fcnPtr_fcnPtrP0Out), Bindings).FieldType;
+ Type param0 = fcnPtr.GetFunctionPointerParameterTypes()[0];
+ Assert.True(param0.IsFunctionPointer);
+ Assert.Equal(1, param0.GetFunctionPointerParameterTypes().Length);
+ Assert.False(IsModifiedType(param0));
+ Assert.Equal(typeof(int).Project().MakeByRefType(), param0.GetFunctionPointerParameterTypes()[0]);
+ Assert.Equal(0, param0.GetFunctionPointerParameterTypes()[0].GetRequiredCustomModifiers().Length);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void FunctionPointerParameters_fcnPtr_fcnPtrP0Out_Modified()
+ {
+ // Modified
+ Type fcnPtr = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._fcnPtr_fcnPtrP0Out), Bindings).GetModifiedFieldType();
+ Type param0 = fcnPtr.GetFunctionPointerParameterTypes()[0];
+ Assert.True(param0.IsFunctionPointer);
+ Assert.Equal(1, param0.GetFunctionPointerParameterTypes().Length);
+ Assert.True(IsModifiedType(param0));
+ Assert.Equal(typeof(int).Project().MakeByRefType(), param0.GetFunctionPointerParameterTypes()[0].UnderlyingSystemType);
+ Assert.Equal(1, param0.GetFunctionPointerParameterTypes()[0].GetRequiredCustomModifiers().Length);
+ Assert.Equal(typeof(OutAttribute).Project(), param0.GetFunctionPointerParameterTypes()[0].GetRequiredCustomModifiers()[0]);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void FunctionPointerParameters_fcnPtr_fcnPtrP0Ref_Unmodified()
+ {
+ Type fcnPtr = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._fcnPtr_fcnPtrP0Ref), Bindings).FieldType;
+ Type param0 = fcnPtr.GetFunctionPointerParameterTypes()[0];
+ Assert.True(param0.IsFunctionPointer);
+ Assert.Equal(1, param0.GetFunctionPointerParameterTypes().Length);
+ Assert.False(IsModifiedType(param0));
+ Assert.Equal(typeof(int).Project().MakeByRefType(), param0.GetFunctionPointerParameterTypes()[0]);
+ Assert.Equal(0, param0.GetRequiredCustomModifiers().Length);
+ Assert.Equal(0, param0.GetOptionalCustomModifiers().Length);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void FunctionPointerParameters_fcnPtr_fcnPtrP0Ref_Modified()
+ {
+ Type fcnPtr = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._fcnPtr_fcnPtrP0Ref), Bindings).GetModifiedFieldType();
+ Type param0 = fcnPtr.GetFunctionPointerParameterTypes()[0];
+ Assert.True(param0.IsFunctionPointer);
+ Assert.Equal(1, param0.GetFunctionPointerParameterTypes().Length);
+ Assert.True(IsModifiedType(param0));
+ Assert.Equal(typeof(int).Project().MakeByRefType(), param0.GetFunctionPointerParameterTypes()[0].UnderlyingSystemType);
+ Assert.Equal(0, param0.GetRequiredCustomModifiers().Length);
+ Assert.Equal(0, param0.GetOptionalCustomModifiers().Length);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void FunctionPointerParameters_fcnPtr_fcnPtrRetP0Ref_Unmodified()
+ {
+ Type fcnPtr = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._fcnPtr_fcnPtrRetP0Out), Bindings).FieldType;
+ Type param0 = fcnPtr.GetFunctionPointerReturnType().GetFunctionPointerParameterTypes()[0];
+ Assert.False(IsModifiedType(param0));
+ Assert.Equal(0, param0.GetRequiredCustomModifiers().Length);
+ Assert.Equal(0, param0.GetOptionalCustomModifiers().Length);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void FunctionPointerParameters_fcnPtr_fcnPtrRetP0Ref_Modified()
+ {
+ Type fcnPtr = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._fcnPtr_fcnPtrRetP0Out), Bindings).GetModifiedFieldType();
+ Type param0 = fcnPtr.GetFunctionPointerReturnType().GetFunctionPointerParameterTypes()[0];
+ Assert.True(IsModifiedType(param0));
+ Assert.Equal(typeof(int).Project().MakeByRefType(), param0.UnderlyingSystemType);
+ Assert.Equal(1, param0.GetRequiredCustomModifiers().Length);
+ Assert.Equal(0, param0.GetOptionalCustomModifiers().Length);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void FunctionPointerParameters_fcnPtr_complex_Unmodified()
+ {
+ Type fcnPtr = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._fcnPtr_complex), Bindings).FieldType;
+ Assert.Equal("System.SByte()(System.Byte(), System.Void(System.Int32)()[][], System.Void(System.Int32, System.String, System.Boolean&)()())", fcnPtr.ToString());
+
+ Type f1 = fcnPtr.GetFunctionPointerParameterTypes()[2];
+ Assert.Equal("System.Void(System.Int32, System.String, System.Boolean&)()()", f1.ToString());
+
+ Type f2 = f1.GetFunctionPointerReturnType();
+ Assert.Equal("System.Void(System.Int32, System.String, System.Boolean&)()", f2.ToString());
+
+ Type f3 = f2.GetFunctionPointerReturnType();
+ Assert.Equal("System.Void(System.Int32, System.String, System.Boolean&)", f3.ToString());
+
+ Type target = f3.GetFunctionPointerParameterTypes()[2];
+ Assert.Equal("System.Boolean&", target.ToString());
+
+ Assert.False(IsModifiedType(target));
+ Assert.Equal(0, target.GetRequiredCustomModifiers().Length);
+ Assert.Equal(0, target.GetOptionalCustomModifiers().Length);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void FunctionPointerParameters_fcnPtr_complex_Modified()
+ {
+ Type fcnPtr = typeof(ModifiedTypeHolder).Project().GetField(nameof(ModifiedTypeHolder._fcnPtr_complex), Bindings).GetModifiedFieldType();
+ Assert.Equal("System.SByte()(System.Byte(), System.Void(System.Int32)()[][], System.Void(System.Int32, System.String, System.Boolean&)()())", fcnPtr.ToString());
+
+ Type f1 = fcnPtr.GetFunctionPointerParameterTypes()[2];
+ Assert.Equal("System.Void(System.Int32, System.String, System.Boolean&)()()", f1.ToString());
+
+ Type f2 = f1.GetFunctionPointerReturnType();
+ Assert.Equal("System.Void(System.Int32, System.String, System.Boolean&)()", f2.ToString());
+
+ Type f3 = f2.GetFunctionPointerReturnType();
+ Assert.Equal("System.Void(System.Int32, System.String, System.Boolean&)", f3.ToString());
+
+ Type target = f3.GetFunctionPointerParameterTypes()[2];
+ Assert.Equal("System.Boolean&", target.ToString());
+
+ Assert.True(IsModifiedType(target));
+ Assert.Equal(1, target.GetRequiredCustomModifiers().Length);
+ Assert.Equal(0, target.GetOptionalCustomModifiers().Length);
+ Assert.Equal(typeof(OutAttribute).Project(), target.GetRequiredCustomModifiers()[0]);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void Property_FcnPtr_Complex_Unmodified()
+ {
+ Type mt = typeof(ModifiedTypeHolder).Project().GetProperty(nameof(ModifiedTypeHolder.Property_FcnPtr_Complex), Bindings).PropertyType;
+ Type f1 = mt.GetElementType();
+ Assert.Equal("System.Boolean(System.Int32(), System.Void(System.Byte(), System.Int32(), System.Int64()))", f1.ToString());
+
+ Type f2 = f1.GetFunctionPointerParameterTypes()[1];
+ Assert.Equal("System.Void(System.Byte(), System.Int32(), System.Int64())", f2.ToString());
+
+ Type target = f2.GetFunctionPointerParameterTypes()[2];
+ Assert.Equal("System.Int64()", target.ToString());
+
+ Assert.Equal(0, target.GetFunctionPointerCallingConventions().Length);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void Property_FcnPtr_Complex_Modified()
+ {
+ Type mt = typeof(ModifiedTypeHolder).Project().GetProperty(nameof(ModifiedTypeHolder.Property_FcnPtr_Complex), Bindings).GetModifiedPropertyType();
+
+ Type f1 = mt.GetElementType();
+ Assert.Equal("System.Boolean(System.Int32(), System.Void(System.Byte(), System.Int32(), System.Int64()))", f1.ToString());
+
+ Type f2 = f1.GetFunctionPointerParameterTypes()[1];
+ Assert.Equal("System.Void(System.Byte(), System.Int32(), System.Int64())", f2.ToString());
+
+ Type target = f2.GetFunctionPointerParameterTypes()[2];
+ Assert.Equal("System.Int64()", target.ToString());
+ Assert.Equal(1, target.GetFunctionPointerCallingConventions().Length);
+ Assert.Equal(typeof(CallConvCdecl).Project(), target.GetFunctionPointerCallingConventions()[0]);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void MethodWithGenericParameter_Unmodified()
+ {
+ MethodInfo mi = typeof(GenericWithModifiers).Project().GetMethod(nameof(GenericWithModifiers.MethodWithGenericParameter), Bindings);
+ Assert.False(mi.ContainsGenericParameters);
+
+ Type a1 = mi.GetParameters()[0].ParameterType;
+ Assert.False(IsModifiedType(a1));
+ Assert.Equal(typeof(Tuple).Project(), a1.Project());
+
+ Type ga1 = a1.GetGenericArguments()[0];
+ Assert.False(IsModifiedType(ga1));
+ Assert.Equal(typeof(int).Project(), ga1);
+
+ Type ga2 = a1.GetGenericArguments()[1];
+ Assert.False(IsModifiedType(ga2));
+ Assert.Equal(typeof(bool).Project(), ga2);
+ Assert.Equal(0, ga2.GetOptionalCustomModifiers().Length);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void MethodWithGenericParameter_Modified()
+ {
+ MethodInfo mi = typeof(GenericWithModifiers).Project().GetMethod(nameof(GenericWithModifiers.MethodWithGenericParameter), Bindings);
+ Assert.False(mi.ContainsGenericParameters);
+
+ Type a1 = mi.GetParameters()[0].GetModifiedParameterType();
+ Assert.True(IsModifiedType(a1));
+ Assert.Equal(typeof(Tuple).Project(), a1.UnderlyingSystemType.Project());
+
+ Type ga1 = a1.GetGenericArguments()[0];
+ Assert.True(IsModifiedType(ga1));
+ Assert.Equal(typeof(int).Project(), ga1.UnderlyingSystemType);
+ Assert.Equal(0, ga1.GetOptionalCustomModifiers().Length);
+
+ Type ga2 = a1.GetGenericArguments()[1];
+ Assert.True(IsModifiedType(ga2));
+ Assert.Equal(typeof(bool).Project(), ga2.UnderlyingSystemType);
+ Assert.Equal(1, ga2.GetOptionalCustomModifiers().Length);
+ Assert.Equal(typeof(IsConst).Project(), ga2.GetOptionalCustomModifiers()[0]);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void GenericMethod_Unmodified()
+ {
+ MethodInfo mi = typeof(GenericWithModifiers).Project().GetMethod(nameof(GenericWithModifiers.GenericMethod), Bindings);
+ Assert.True(mi.ContainsGenericParameters);
+
+ Type a1 = mi.GetParameters()[0].ParameterType;
+ Assert.False(IsModifiedType(a1));
+ Assert.True(a1.ContainsGenericParameters);
+ Assert.Equal(0, a1.GetOptionalCustomModifiers().Length);
+ }
+
+ [Fact]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
+ public static unsafe void GenericMethod_Modified()
+ {
+ MethodInfo mi = typeof(GenericWithModifiers).Project().GetMethod(nameof(GenericWithModifiers.GenericMethod), Bindings);
+ Assert.True(mi.ContainsGenericParameters);
+
+ Type a1 = mi.GetParameters()[0].GetModifiedParameterType();
+ Assert.True(IsModifiedType(a1));
+ Assert.True(a1.ContainsGenericParameters);
+ Assert.Equal(1, a1.GetOptionalCustomModifiers().Length);
+ }
+
+ private static bool IsModifiedType(Type type)
+ {
+ return !ReferenceEquals(type, type.UnderlyingSystemType);
+ }
+
+ public unsafe class ModifiedTypeHolder
+ {
+ public ModifiedTypeHolder(delegate* d) { }
+
+ public static volatile int _volatileInt;
+ public static volatile int[] _volatileIntArray;
+ public static volatile int* _volatileIntPointer;
+ public static volatile delegate* unmanaged[Cdecl] _volatileFcnPtr;
+
+ // Although function pointer types can't be used as generic parameters, they can be used indirectly
+ // as an array element type.
+ public static volatile Tuple[]> _arrayGenericFcnPtr;
+
+ public static int** _ptr_ptr_int;
+ public static int*[] _array_ptr_int;
+ public static delegate** _ptr_fcnPtr;
+ public static delegate**[] _array_ptr_fcnPtr;
+ public static delegate*> _fcnPtr_fcnPtrReturn;
+
+ public static void M_P0IntOut(out int i) { i = 42; }
+ public static void M_P0FcnPtrOut(delegate* fp) { }
+ public static void M_ArrayOpenGenericFcnPtr(T t, delegate*[] fp) { }
+
+ public int InitProperty_Int { get; init; }
+ public static delegate* Property_FcnPtr { get; set; }
+
+ public static delegate* _fcnPtrP0Out;
+ public static delegate*, void> _fcnPtr_fcnPtrP0Out;
+ public static delegate*, void> _fcnPtr_fcnPtrP0Ref;
+ public static delegate*> _fcnPtr_fcnPtrRetP0Out;
+
+ public delegate*
+ <
+ delegate*, // p0
+ delegate* // p1
+ <
+ delegate*, // p0
+ delegate*, // p1
+ delegate* unmanaged[Cdecl], // p2
+ void // ret
+ >,
+ bool // ret
+ >[] Property_FcnPtr_Complex { get; }
+
+ public static delegate*
+ <
+ delegate*, // p0
+ delegate* // p1
+ <
+ delegate* // ret
+ >[][],
+ delegate* // p2
+ <
+ delegate* // ret
+ <
+ delegate* // ret
+ <
+ int, string, out bool, void // p0-3
+ >
+ >
+ >,
+ delegate* // ret
+ > _fcnPtr_complex;
+ }
+ }
+}
diff --git a/src/libraries/Common/tests/System/TestILAssembly/TestILAssembly.il b/src/libraries/Common/tests/System/TestILAssembly/TestILAssembly.il
new file mode 100644
index 0000000000000..ca2582be13414
--- /dev/null
+++ b/src/libraries/Common/tests/System/TestILAssembly/TestILAssembly.il
@@ -0,0 +1,46 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+.assembly extern System.Runtime
+{
+ .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A)
+ .ver 4:0:0:0
+}
+
+.assembly TestILAssembly
+{
+ .ver 1:0:0:0
+}
+
+.module TestILAssembly.dll
+
+.namespace System.Tests
+{
+ .class public auto ansi beforefieldinit GenericWithModifiers
+ extends [System.Runtime]System.Object
+ {
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor() cil managed
+ {
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: call instance void [System.Runtime]System.Object::.ctor()
+ IL_0006: nop
+ IL_0007: ret
+ }
+
+ .method public hidebysig instance void MethodWithGenericParameter(class [System.Runtime]System.Tuple`2 t) cil managed
+ {
+ .maxstack 8
+ IL_0000: nop
+ IL_0001: ret
+ }
+
+ .method public hidebysig instance void GenericMethod(!!T modopt([System.Runtime]System.Runtime.CompilerServices.IsConst) t) cil managed
+ {
+ .maxstack 8
+ IL_0000: nop
+ IL_0001: ret
+ }
+ }
+}
diff --git a/src/libraries/Common/tests/System/TestILAssembly/TestILAssembly.ilproj b/src/libraries/Common/tests/System/TestILAssembly/TestILAssembly.ilproj
new file mode 100644
index 0000000000000..770a84293ac27
--- /dev/null
+++ b/src/libraries/Common/tests/System/TestILAssembly/TestILAssembly.ilproj
@@ -0,0 +1,10 @@
+
+
+ 1.0.0.0
+ netstandard2.0
+ Microsoft
+
+
+
+
+
diff --git a/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs b/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs
index 8dc832cd3e42d..3aae34cc629ef 100644
--- a/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs
+++ b/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs
@@ -316,9 +316,10 @@ public void ToString_NullFrame_ThrowsNullReferenceException()
[ActiveIssue("https://github.com/dotnet/runtime/issues/11354", TestRuntimes.Mono)]
public unsafe void ToString_FunctionPointerSignature()
{
- // This is sepate from ToString_Invoke_ReturnsExpected since unsafe cannot be used for iterators
+ // This is separate from ToString_Invoke_ReturnsExpected since unsafe cannot be used for iterators
var stackTrace = FunctionPointerParameter(null);
- Assert.Contains("System.Diagnostics.Tests.StackTraceTests.FunctionPointerParameter(IntPtr x)", stackTrace.ToString());
+ // Function pointers have no Name.
+ Assert.Contains("System.Diagnostics.Tests.StackTraceTests.FunctionPointerParameter( x)", stackTrace.ToString());
}
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
index 8cbe0345d4c20..3d9d7ae1ca364 100644
--- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
+++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
@@ -3992,6 +3992,12 @@
The value may not contain directory separator characters.
+
+ Method may only be called on a Type for which Type.IsFunctionPointer is true.
+
+
+ Modified types do not support this member. Use the UnderlyingSystemType property to call this member.
+
An unexpected state object was encountered. This is usually a sign of a bug in async method custom infrastructure, such as a custom awaiter or IValueTaskSource implementation.
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 03d6724bb8ebd..7fa24c7ab00f8 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
@@ -671,6 +671,7 @@
+
@@ -686,8 +687,11 @@
-
+
+
+
+
@@ -713,6 +717,7 @@
+
diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/FieldInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/FieldInfo.cs
index a2090f56d483a..a544be89c423e 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Reflection/FieldInfo.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/FieldInfo.cs
@@ -74,6 +74,8 @@ protected FieldInfo() { }
public virtual object? GetRawConstantValue() { throw new NotSupportedException(SR.NotSupported_AbstractNonCLS); }
+ public virtual Type GetModifiedFieldType() => throw new NotSupportedException();
+
public virtual Type[] GetOptionalCustomModifiers() { throw NotImplemented.ByDesign; }
public virtual Type[] GetRequiredCustomModifiers() { throw NotImplemented.ByDesign; }
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/ModifiedFunctionPointerType.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/ModifiedFunctionPointerType.cs
new file mode 100644
index 0000000000000..6971e1642d068
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/ModifiedFunctionPointerType.cs
@@ -0,0 +1,85 @@
+// 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.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Threading;
+
+namespace System.Reflection
+{
+ internal sealed partial class ModifiedFunctionPointerType : ModifiedType
+ {
+ private const string CallingConventionTypePrefix = "System.Runtime.CompilerServices.CallConv";
+
+ private Type[]? _parameterTypes;
+ private Type? _returnType;
+
+ internal ModifiedFunctionPointerType(Type unmodifiedType, TypeSignature typeSignature)
+ : base(unmodifiedType, typeSignature)
+ {
+ Debug.Assert(unmodifiedType.IsFunctionPointer);
+ }
+
+ public override Type GetFunctionPointerReturnType()
+ {
+ return _returnType ?? Initialize();
+
+ Type Initialize()
+ {
+ Interlocked.CompareExchange(ref _returnType, GetTypeParameter(UnmodifiedType.GetFunctionPointerReturnType(), 0), null);
+ return _returnType!;
+ }
+ }
+
+ public override Type[] GetFunctionPointerParameterTypes()
+ {
+ return (Type[])(_parameterTypes ?? Initialize()).Clone();
+
+ Type[] Initialize()
+ {
+ Type[] parameterTypes = UnmodifiedType.GetFunctionPointerParameterTypes();
+ for (int i = 0; i < parameterTypes.Length; i++)
+ {
+ parameterTypes[i] = GetTypeParameter(parameterTypes[i], i + 1);
+ }
+ Interlocked.CompareExchange(ref _parameterTypes, parameterTypes, null);
+ return _parameterTypes!;
+ }
+ }
+
+ public override Type[] GetFunctionPointerCallingConventions()
+ {
+ ArrayBuilder builder = default;
+
+ // Normalize the calling conventions by manufacturing a type.
+ switch (GetCallingConventionFromFunctionPointer())
+ {
+ case SignatureCallingConvention.Cdecl:
+ builder.Add(typeof(CallConvCdecl));
+ break;
+ case SignatureCallingConvention.StdCall:
+ builder.Add(typeof(CallConvStdcall));
+ break;
+ case SignatureCallingConvention.ThisCall:
+ builder.Add(typeof(CallConvThiscall));
+ break;
+ case SignatureCallingConvention.FastCall:
+ builder.Add(typeof(CallConvFastcall));
+ break;
+ case SignatureCallingConvention.Unmanaged:
+ // For the above cases, there will be no other custom calling convention modifiers.
+ foreach (Type type in GetFunctionPointerReturnType().GetOptionalCustomModifiers())
+ {
+ if (type.FullName!.StartsWith(CallingConventionTypePrefix, StringComparison.Ordinal))
+ {
+ builder.Add(type);
+ }
+ }
+ break;
+ }
+
+ return builder.Count == 0 ? EmptyTypes : builder.ToArray();
+ }
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/ModifiedGenericType.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/ModifiedGenericType.cs
new file mode 100644
index 0000000000000..c93caad06d9c3
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/ModifiedGenericType.cs
@@ -0,0 +1,35 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+using System.Threading;
+
+namespace System.Reflection
+{
+ internal sealed partial class ModifiedGenericType : ModifiedType
+ {
+ private Type[]? _genericArguments;
+
+ internal ModifiedGenericType(Type unmodifiedType, TypeSignature typeSignature)
+ : base(unmodifiedType, typeSignature)
+ {
+ Debug.Assert(unmodifiedType.IsGenericType);
+ }
+
+ public override Type[] GetGenericArguments()
+ {
+ return (Type[])(_genericArguments ?? Initialize()).Clone();
+
+ Type[] Initialize()
+ {
+ Type[] genericArguments = UnmodifiedType.GetGenericArguments();
+ for (int i = 0; i < genericArguments.Length; i++)
+ {
+ genericArguments[i] = GetTypeParameter(genericArguments[i], i);
+ }
+ Interlocked.CompareExchange(ref _genericArguments, genericArguments, null);
+ return _genericArguments!;
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/ModifiedHasElementType.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/ModifiedHasElementType.cs
new file mode 100644
index 0000000000000..19df3af6f7944
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/ModifiedHasElementType.cs
@@ -0,0 +1,33 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+using System.Threading;
+
+namespace System.Reflection
+{
+ ///
+ /// An array, pointer or reference type.
+ ///
+ internal sealed class ModifiedHasElementType : ModifiedType
+ {
+ private Type? _elementType;
+
+ internal ModifiedHasElementType(Type unmodifiedType, TypeSignature typeSignature)
+ : base(unmodifiedType, typeSignature)
+ {
+ Debug.Assert(unmodifiedType.HasElementType);
+ }
+
+ public override Type? GetElementType()
+ {
+ return _elementType ?? Initialize();
+
+ Type Initialize()
+ {
+ Interlocked.CompareExchange(ref _elementType, GetTypeParameter(UnmodifiedType.GetElementType()!, 0), null);
+ return _elementType!;
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/ModifiedType.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/ModifiedType.cs
new file mode 100644
index 0000000000000..9ab42fdc11d25
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/ModifiedType.cs
@@ -0,0 +1,200 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.CodeAnalysis;
+using CultureInfo = System.Globalization.CultureInfo;
+
+namespace System.Reflection
+{
+ ///
+ /// Base class for modified types and standalone modified type.
+ /// Design supports code sharing between different runtimes and lazy loading of custom modifiers.
+ ///
+ internal partial class ModifiedType : Type
+ {
+ private readonly TypeSignature _typeSignature;
+ private readonly Type _unmodifiedType;
+
+ internal ModifiedType(Type unmodifiedType, TypeSignature typeSignature)
+ {
+ _unmodifiedType = unmodifiedType;
+ _typeSignature = typeSignature;
+ }
+
+ ///
+ /// Factory to create a node recursively based on the underlying, unmodified type.
+ /// A type tree is formed due to arrays and pointers having an element type, function pointers
+ /// having a return type and parameter types, and generic types having argument types.
+ ///
+ protected static Type Create(Type unmodifiedType, TypeSignature typeSignature)
+ {
+ Type modifiedType;
+ if (unmodifiedType.IsFunctionPointer)
+ {
+ modifiedType = new ModifiedFunctionPointerType(unmodifiedType, typeSignature);
+ }
+ else if (unmodifiedType.HasElementType)
+ {
+ modifiedType = new ModifiedHasElementType(unmodifiedType, typeSignature);
+ }
+ else if (unmodifiedType.IsGenericType)
+ {
+ modifiedType = new ModifiedGenericType(unmodifiedType, typeSignature);
+ }
+ else
+ {
+ modifiedType = new ModifiedType(unmodifiedType, typeSignature);
+ }
+ return modifiedType;
+ }
+
+ protected Type UnmodifiedType => _unmodifiedType;
+
+ // Below are the multitude of Type overloads. We throw NSE for members that would return an unmodified Type
+ // directly or indirectly. We do this in case we want to support returning modified types instead.
+
+ public override Type[] GetRequiredCustomModifiers()
+ {
+ // No caching is performed; as is the case with FieldInfo.GetCustomModifiers and friends.
+ return GetCustomModifiers(required: true);
+ }
+
+ public override Type[] GetOptionalCustomModifiers()
+ {
+ // No caching is performed; as is the case with FieldInfo.GetCustomModifiers and friends.
+ return GetCustomModifiers(required: false);
+ }
+
+ // Modified types do not support Equals. That would need to include custom modifiers and any parameterized types recursively.
+ // UnderlyingSystemType.Equals() should should be used if basic equality is necessary.
+ public override bool Equals([NotNullWhen(true)] object? obj) => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+ public override bool Equals(Type? other) => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+ public override int GetHashCode() => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+
+ public override string ToString() => _unmodifiedType.ToString();
+ public override Type UnderlyingSystemType => _unmodifiedType;
+
+ public override GenericParameterAttributes GenericParameterAttributes => _unmodifiedType.GenericParameterAttributes;
+ public override bool ContainsGenericParameters => _unmodifiedType.ContainsGenericParameters;
+ public override Type GetGenericTypeDefinition() => _unmodifiedType.GetGenericTypeDefinition();
+ public override bool IsGenericType => _unmodifiedType.IsGenericType;
+
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
+ public override object? InvokeMember(string name, BindingFlags invokeAttr, Binder? binder, object? target,
+ object?[]? args, ParameterModifier[]? modifiers, CultureInfo? culture, string[]? namedParameters)
+ => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+
+ public override Guid GUID => _unmodifiedType.GUID;
+ public override int MetadataToken => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+ public override Module Module => _unmodifiedType.Module;
+ public override Assembly Assembly => _unmodifiedType.Assembly;
+ public override RuntimeTypeHandle TypeHandle => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+ public override string Name => _unmodifiedType.Name;
+ public override string? FullName => _unmodifiedType.FullName;
+ public override string? Namespace => _unmodifiedType.Namespace;
+ public override string? AssemblyQualifiedName => _unmodifiedType.AssemblyQualifiedName;
+ public override Type? BaseType => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+ public override Type? DeclaringType => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+ public override Type? ReflectedType => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
+ protected override ConstructorInfo? GetConstructorImpl(BindingFlags bindingAttr, Binder? binder,
+ CallingConventions callConvention, Type[] types, ParameterModifier[]? modifiers) => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
+ public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
+ protected override MethodInfo? GetMethodImpl(string name, BindingFlags bindingAttr, Binder? binder,
+ CallingConventions callConvention, Type[]? types, ParameterModifier[]? modifiers)
+ => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
+ public override MethodInfo[] GetMethods(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)]
+ public override FieldInfo? GetField(string name, BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)]
+ public override FieldInfo[] GetFields(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+
+ public override Type[] GetFunctionPointerCallingConventions() => _unmodifiedType.GetFunctionPointerCallingConventions();
+ public override Type[] GetFunctionPointerParameterTypes() => _unmodifiedType.GetFunctionPointerParameterTypes();
+ public override Type GetFunctionPointerReturnType() => _unmodifiedType.GetFunctionPointerReturnType();
+
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
+ [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
+ public override Type? GetInterface(string name, bool ignoreCase) => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
+ public override Type[] GetInterfaces() => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+ public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] Type interfaceType) => _unmodifiedType.GetInterfaceMap(interfaceType);
+
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)]
+ public override EventInfo? GetEvent(string name, BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents)]
+ public override EventInfo[] GetEvents() => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
+ protected override PropertyInfo? GetPropertyImpl(string name, BindingFlags bindingAttr, Binder? binder,
+ Type? returnType, Type[]? types, ParameterModifier[]? modifiers)
+ => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
+ public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)]
+ public override EventInfo[] GetEvents(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)]
+ public override Type[] GetNestedTypes(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)]
+ public override Type? GetNestedType(string name, BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+
+ [DynamicallyAccessedMembers(GetAllMembers)]
+ public override MemberInfo[] GetMember(string name, MemberTypes type, BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+
+ [DynamicallyAccessedMembers(GetAllMembers)]
+ public override MemberInfo[] GetMembers(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+
+ public override MemberInfo GetMemberWithSameMetadataDefinitionAs(MemberInfo member) => throw new NotSupportedException(SR.NotSupported_ModifiedType);
+
+ protected override TypeAttributes GetAttributeFlagsImpl() => _unmodifiedType.Attributes;
+ public override int GetArrayRank() => _unmodifiedType.GetArrayRank();
+
+ public override bool IsTypeDefinition => _unmodifiedType.IsTypeDefinition;
+ public override bool IsSZArray => _unmodifiedType.IsSZArray;
+ public override bool IsVariableBoundArray => _unmodifiedType.IsVariableBoundArray;
+
+ protected override bool IsArrayImpl() => _unmodifiedType.IsArray;
+ public override bool IsEnum => _unmodifiedType.IsEnum;
+ protected override bool IsPrimitiveImpl() => _unmodifiedType.IsPrimitive;
+ protected override bool IsByRefImpl() => _unmodifiedType.IsByRef;
+ public override bool IsGenericTypeParameter => _unmodifiedType.IsGenericTypeParameter;
+ public override bool IsGenericMethodParameter => _unmodifiedType.IsGenericMethodParameter;
+ protected override bool IsPointerImpl() => _unmodifiedType.IsPointer;
+ protected override bool IsValueTypeImpl() => _unmodifiedType.IsValueType;
+ protected override bool IsCOMObjectImpl() => _unmodifiedType.IsCOMObject;
+ public override bool IsByRefLike => _unmodifiedType.IsByRefLike;
+ public override bool IsConstructedGenericType => _unmodifiedType.IsConstructedGenericType;
+
+ public override bool IsCollectible => _unmodifiedType.IsCollectible;
+
+ public override bool IsFunctionPointer => _unmodifiedType.IsFunctionPointer;
+ public override bool IsUnmanagedFunctionPointer => _unmodifiedType.IsUnmanagedFunctionPointer;
+
+ public override bool IsSecurityCritical => _unmodifiedType.IsSecurityCritical;
+ public override bool IsSecuritySafeCritical => _unmodifiedType.IsSecuritySafeCritical;
+ public override bool IsSecurityTransparent => _unmodifiedType.IsSecurityTransparent;
+
+ public override Type? GetElementType() => _unmodifiedType.GetElementType(); // Supported
+ protected override bool HasElementTypeImpl() => _unmodifiedType.HasElementType;
+
+ // ICustomAttributeProvider
+ public override object[] GetCustomAttributes(bool inherit) => _unmodifiedType.GetCustomAttributes(inherit);
+ public override object[] GetCustomAttributes(Type attributeType, bool inherit) => _unmodifiedType.GetCustomAttributes(attributeType, inherit);
+ public override bool IsDefined(Type attributeType, bool inherit) => _unmodifiedType.IsDefined(attributeType, inherit);
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/ParameterInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/ParameterInfo.cs
index 319c7070d10b5..5a92ca202fc87 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Reflection/ParameterInfo.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/ParameterInfo.cs
@@ -42,6 +42,8 @@ public virtual object[] GetCustomAttributes(Type attributeType, bool inherit)
return Array.Empty]