From 5417704dcaab4c0a7f2c0a904b08e4d838226271 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Tue, 31 May 2022 11:59:19 -0500 Subject: [PATCH 1/9] Add reflection introspection support for function pointers --- .../System.Private.CoreLib.csproj | 2 + .../Reflection/FunctionPointerInfo.CoreCLR.cs | 40 ++ .../src/System/Reflection/MdImport.cs | 22 - ...imeFunctionPointerParameterInfo.CoreCLR.cs | 37 ++ .../src/System/RuntimeHandles.cs | 47 +- .../src/System/RuntimeType.CoreCLR.cs | 51 +- src/coreclr/debug/daccess/dacdbiimpl.cpp | 5 +- .../src/System.Private.CoreLib.csproj | 5 +- ...eFunctionPointerParameterInfo.NativeAot.cs | 11 + src/coreclr/vm/clsload.cpp | 29 +- src/coreclr/vm/clsload.hpp | 5 +- src/coreclr/vm/ecalllist.h | 2 + src/coreclr/vm/eedbginterfaceimpl.cpp | 14 +- src/coreclr/vm/runtimehandles.cpp | 86 ++++ src/coreclr/vm/runtimehandles.h | 6 + src/coreclr/vm/sigformat.h | 1 + src/coreclr/vm/siginfo.cpp | 30 +- src/coreclr/vm/typedesc.cpp | 48 +- src/coreclr/vm/typedesc.h | 67 ++- src/coreclr/vm/typehandle.cpp | 10 +- src/coreclr/vm/typehandle.h | 4 +- src/coreclr/vm/typehandle.inl | 3 +- src/coreclr/vm/typehash.cpp | 47 +- src/coreclr/vm/typehash.h | 2 +- src/coreclr/vm/typekey.h | 59 ++- src/coreclr/vm/typestring.cpp | 73 ++- src/coreclr/vm/typestring.h | 1 + .../tests/System/FunctionPointerTests.cs | 474 ++++++++++++++++++ .../FunctionPointerHolder.cs | 18 + .../TestFunctionPointerAssembly.csproj | 11 + .../tests/StackTraceTests.cs | 4 +- .../src/Resources/Strings.resx | 3 + .../System.Private.CoreLib.Shared.projitems | 8 +- .../System/Reflection/FunctionPointerInfo.cs | 94 ++++ .../FunctionPointerParameterInfo.cs | 13 + .../Reflection/MdSigCallingConvention.cs | 31 ++ .../RuntimeFunctionPointerParameterInfo.cs | 13 + .../src/System/Reflection/TypeDelegator.cs | 9 +- .../System.Private.CoreLib/src/System/Type.cs | 7 + .../Decoding/SignatureDecoderTests.cs | 4 + .../System.Reflection.MetadataLoadContext.sln | 7 + .../src/Resources/Strings.resx | 10 +- ...stem.Reflection.MetadataLoadContext.csproj | 12 +- .../TypeLoading/Fields/Ecma/EcmaField.cs | 1 - .../RoFunctionPointerParameterInfo.cs | 46 ++ .../TypeLoading/General/CoreType.cs | 10 + .../EcmaSignatureTypeProviderForToString.cs | 21 +- .../General/TypeExtensions.netcoreapp.cs | 11 +- .../General/TypeExtensions.netstandard.cs | 8 + .../TypeLoading/General/Utf8Constants.cs | 4 + .../Modules/Ecma/EcmaModule.TypeProvider.cs | 26 + .../TypeLoading/Types/RoArrayType.cs | 4 +- .../TypeLoading/Types/RoByRefType.cs | 4 +- .../Types/RoConstructedGenericType.cs | 11 +- .../TypeLoading/Types/RoDefinitionType.cs | 14 +- .../Types/RoFunctionPointerDelegator.cs | 27 + .../Types/RoFunctionPointerType.cs | 265 ++++++++++ .../Types/RoGenericParameterType.cs | 11 +- .../TypeLoading/Types/RoHasElementType.cs | 7 + .../TypeLoading/Types/RoPointerType.cs | 4 +- .../TypeLoading/Types/RoStubType.cs | 11 +- .../Reflection/TypeLoading/Types/RoType.cs | 50 +- .../TypeLoading/Types/RoTypeDelegator.cs | 98 ++++ ...eflection.MetadataLoadContext.Tests.csproj | 15 +- .../src/SampleMetadata/SampleMetadata.cs | 11 + .../src/TestUtils/TestUtils.JittedRuntimes.cs | 10 + .../tests/src/Tests/Method/MethodTests.cs | 2 +- .../ref/System.Reflection.Forwards.cs | 1 + .../ConvertToLibraryImportAnalyzerTests.cs | 1 - .../System.Runtime/System.Runtime.sln | 21 + .../System.Runtime/ref/System.Runtime.cs | 25 +- .../tests/System.Runtime.Tests.csproj | 3 + .../System/Reflection/TypeDelegatorTests.cs | 15 + .../Type/FunctionPointerTestExtensions.cs | 18 + .../System.Private.CoreLib.csproj | 2 + .../Reflection/FunctionPointerInfo.Mono.cs | 19 + ...untimeFunctionPointerParameterInfo.Mono.cs | 19 + src/tests/reflection/ldtoken/modifiers.il | 36 +- 78 files changed, 2112 insertions(+), 144 deletions(-) create mode 100644 src/coreclr/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.CoreCLR.cs create mode 100644 src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeFunctionPointerParameterInfo.CoreCLR.cs create mode 100644 src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/RuntimeFunctionPointerParameterInfo.NativeAot.cs create mode 100644 src/libraries/Common/tests/System/FunctionPointerTests.cs create mode 100644 src/libraries/Common/tests/System/TestFunctionPointerAssembly/FunctionPointerHolder.cs create mode 100644 src/libraries/Common/tests/System/TestFunctionPointerAssembly/TestFunctionPointerAssembly.csproj create mode 100644 src/libraries/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Reflection/FunctionPointerParameterInfo.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Reflection/MdSigCallingConvention.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Reflection/RuntimeFunctionPointerParameterInfo.cs create mode 100644 src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/FunctionPointer/RoFunctionPointerParameterInfo.cs create mode 100644 src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Types/RoFunctionPointerDelegator.cs create mode 100644 src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Types/RoFunctionPointerType.cs create mode 100644 src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Types/RoTypeDelegator.cs create mode 100644 src/libraries/System.Runtime/tests/System/Type/FunctionPointerTestExtensions.cs create mode 100644 src/mono/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.Mono.cs create mode 100644 src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFunctionPointerParameterInfo.Mono.cs diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 60560e43fc7a6..4cc10049c3d4a 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -178,6 +178,7 @@ + @@ -194,6 +195,7 @@ + diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.CoreCLR.cs new file mode 100644 index 0000000000000..761d15951f982 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.CoreCLR.cs @@ -0,0 +1,40 @@ +// 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; + +namespace System.Reflection +{ + internal partial class FunctionPointerInfo + { + private readonly Signature _signature; + private readonly Type _type; + + internal FunctionPointerInfo(Type type, Signature signature) + { + Debug.Assert(signature.m_csig > 0); + + _type = type; + _signature = signature; + _returnInfo = new RuntimeFunctionPointerParameterInfo(signature.ReturnType.AsType(), -1, signature); + + RuntimeType[] arguments = signature.Arguments; + int count = arguments.Length; + if (count == 0) + { + _parameterInfos = Array.Empty(); + } + else + { + RuntimeFunctionPointerParameterInfo[] parameterInfos = new RuntimeFunctionPointerParameterInfo[count]; + for (int i = 0; i < count; i++) + { + parameterInfos[i] = new RuntimeFunctionPointerParameterInfo(arguments[i].AsType(), i - 1, signature); + } + _parameterInfos = parameterInfos; + } + } + + private unsafe MdSigCallingConvention CallingConvention => (MdSigCallingConvention)((byte*)_signature.m_sig)[0] & MdSigCallingConvention.CallConvMask; + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.cs index cdc15dc8fb6e0..4bfdfee70accc 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.cs @@ -8,28 +8,6 @@ namespace System.Reflection { - [Flags] - internal enum MdSigCallingConvention : byte - { - CallConvMask = 0x0f, // Calling convention is bottom 4 bits - - Default = 0x00, - C = 0x01, - StdCall = 0x02, - ThisCall = 0x03, - FastCall = 0x04, - Vararg = 0x05, - Field = 0x06, - LocalSig = 0x07, - Property = 0x08, - Unmanaged = 0x09, - GenericInst = 0x0a, // generic method instantiation - - Generic = 0x10, // Generic method sig with explicit number of type arguments (precedes ordinary parameter count) - HasThis = 0x20, // Top bit indicates a 'this' parameter - ExplicitThis = 0x40, // This parameter is explicitly in the signature - } - [Flags] internal enum PInvokeAttributes { diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeFunctionPointerParameterInfo.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeFunctionPointerParameterInfo.CoreCLR.cs new file mode 100644 index 0000000000000..6d9f1c5219d40 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeFunctionPointerParameterInfo.CoreCLR.cs @@ -0,0 +1,37 @@ +// 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; + +namespace System.Reflection +{ + internal partial class RuntimeFunctionPointerParameterInfo + { + private readonly int _position; + private readonly Signature _signature; + private List? _optionalModifiers; + + public RuntimeFunctionPointerParameterInfo(Type parameterType, int position, Signature signature) + { + _parameterType = parameterType; + _position = position; + _signature = signature; + } + + public override Type[] GetOptionalCustomModifiers() + { + return _optionalModifiers == null ? + _signature.GetCustomModifiers(_position + 1, required: false) : + _optionalModifiers.ToArray(); + } + + // Expose the List in order to add calling conventions later. + internal List GetOptionalCustomModifiersList() + { + _optionalModifiers ??= new List(_signature.GetCustomModifiers(_position + 1, required: false)); + return _optionalModifiers; + } + + public override Type[] GetRequiredCustomModifiers() => _signature.GetCustomModifiers(_position + 1, required: true); + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs index 2639b5e62df87..cbf13f486b2ec 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -182,6 +182,18 @@ 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 IsUnmanagedFunctionPointer(RuntimeType type) + { + // Fast native path that does not need to create FunctionPointerInfo and parse the Signature. + return _IsUnmanagedFunctionPointer(type); + } + internal static bool HasElementType(RuntimeType type) { CorElementType corElemType = GetCorElementType(type); @@ -522,6 +534,9 @@ internal static bool IsVisible(RuntimeType type) [MethodImpl(MethodImplOptions.InternalCall)] internal static extern bool IsValueType(RuntimeType type); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern bool _IsUnmanagedFunctionPointer(RuntimeType type); + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeTypeHandle_ConstructName")] private static partial void ConstructName(QCallTypeHandle handle, TypeNameFormatFlags formatFlags, StringHandleOnStack retString); @@ -1600,28 +1615,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))] @@ -1673,6 +1666,16 @@ public Signature(IRuntimeFieldInfo fieldHandle, RuntimeType declaringType) GC.KeepAlive(fieldHandle); } + [MethodImpl(MethodImplOptions.InternalCall)] + private extern void GetSignatureFromFunctionPointer(Type functionPointerType); + + public Signature(RuntimeType functionPointerType) + { + m_arguments = default!; + m_returnTypeORfieldType = default!; + GetSignatureFromFunctionPointer(functionPointerType); + } + public Signature(void* pCorSig, int cCorSig, RuntimeType declaringType) { GetSignature(pCorSig, cCorSig, default, null, declaringType); 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 65f34c67e00e1..232cf55750c83 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -1489,7 +1489,7 @@ internal T[] GetMemberList(MemberListType listType, string? name, CacheType cach private static CerHashtable s_methodInstantiations; private static object? s_methodInstantiationsLock; private string? m_defaultMemberName; - private object? m_genericCache; // Generic cache for rare scenario specific data. It is used to cache Enum names and values. + private object? m_genericCache; private object[]? _emptyArray; // Object array cache for Attribute.GetCustomAttributes() pathological no-result case. #endregion @@ -1532,6 +1532,10 @@ 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 a function pointer. + /// internal object? GenericCache { get => m_genericCache; @@ -1561,6 +1565,11 @@ internal bool DomainInitialized if (!m_runtimeType.GetRootElementType().IsGenericTypeDefinition && m_runtimeType.ContainsGenericParameters) return null; + // Exclude function pointer; it requires a grammar update (see https://docs.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/specifying-fully-qualified-type-names) + // and parsing support for Type.GetType(...) and related GetType() methods. + if (m_runtimeType.IsFunctionPointer) + return null; + // No assembly. return ConstructName(ref m_fullname, TypeNameFormatFlags.FormatNamespace | TypeNameFormatFlags.FormatFullInst); @@ -1579,6 +1588,11 @@ internal string GetNameSpace() if (m_namespace == null) { Type type = m_runtimeType; + + // Since Function pointers don't have a TypeDef metadata record just use the namespace for System.Type. + if (type.IsFunctionPointer) + return typeof(Type).Namespace!; + type = type.GetRootElementType(); while (type.IsNested) @@ -1757,6 +1771,12 @@ internal FieldInfo GetField(RuntimeFieldHandleInternal field) return m_fieldInfoCache!.AddField(field); } + internal FunctionPointerInfo? FunctionPointerInfo + { + get => GenericCache as FunctionPointerInfo; + set => GenericCache = value; + } + #endregion } #endregion @@ -3321,7 +3341,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; @@ -3766,6 +3787,32 @@ internal static CorElementType GetUnderlyingType(RuntimeType type) #endregion + #region Function Pointer + public override bool IsFunctionPointer => RuntimeTypeHandle.IsFunctionPointer(this); + public override bool IsUnmanagedFunctionPointer => RuntimeTypeHandle.IsUnmanagedFunctionPointer(this); + public override FunctionPointerParameterInfo[] GetFunctionPointerParameterInfos() => GetFunctionPointerInfo().ParameterInfos; + public override FunctionPointerParameterInfo GetFunctionPointerReturnParameter() => GetFunctionPointerInfo().ReturnParameter; + public override Type[] GetFunctionPointerCallingConventions() => GetFunctionPointerInfo().GetCallingConventions(); + + internal FunctionPointerInfo GetFunctionPointerInfo() + { + FunctionPointerInfo? fnPtr = Cache.FunctionPointerInfo; + + if (fnPtr == null) + { + if (!IsFunctionPointer) + { + throw new InvalidOperationException(SR.InvalidOperation_NotFunctionPointer); + } + + fnPtr = new FunctionPointerInfo(this, new Signature(this)); + Cache.FunctionPointerInfo = fnPtr; + } + + return fnPtr; + } + #endregion + #endregion public override string ToString() => GetCachedName(TypeNameKind.ToString)!; diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 6987ed2dbac6e..75e995b6c08de 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -2370,7 +2370,10 @@ TypeHandle DacDbiInterfaceImpl::FindLoadedFnptrType(DWORD numTypeArgs, TypeHandl // @dbgtodo : Do we need to worry about calling convention here? // LoadFnptrTypeThrowing expects the count of arguments, not // including return value, so we subtract 1 from numTypeArgs. - return ClassLoader::LoadFnptrTypeThrowing(0, + return ClassLoader::LoadFnptrTypeThrowing(NULL, /*module*/ + NULL, /*sig*/ + 0, /*sigLen*/ + 0, /*callConv*/ numTypeArgs - 1, pInst, ClassLoader::DontLoadTypes); 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 57906233af649..913288ddc4e2c 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 @@ -1,4 +1,4 @@ - + true @@ -159,6 +159,7 @@ + @@ -509,7 +510,7 @@ - + diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/RuntimeFunctionPointerParameterInfo.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/RuntimeFunctionPointerParameterInfo.NativeAot.cs new file mode 100644 index 0000000000000..d16d4f1c5fb87 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/RuntimeFunctionPointerParameterInfo.NativeAot.cs @@ -0,0 +1,11 @@ +// 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 RuntimeFunctionPointerParameterInfo + { + public override Type[] GetOptionalCustomModifiers() => throw new NotSupportedException(); + public override Type[] GetRequiredCustomModifiers() => throw new NotSupportedException(); + } +} diff --git a/src/coreclr/vm/clsload.cpp b/src/coreclr/vm/clsload.cpp index daebf64ea9f30..110fc790d8a9d 100644 --- a/src/coreclr/vm/clsload.cpp +++ b/src/coreclr/vm/clsload.cpp @@ -1615,7 +1615,10 @@ TypeHandle ClassLoader::LoadNativeValueTypeThrowing(TypeHandle baseType, } /* static */ -TypeHandle ClassLoader::LoadFnptrTypeThrowing(BYTE callConv, +TypeHandle ClassLoader::LoadFnptrTypeThrowing(Module *pModule, + PCOR_SIGNATURE sig, + uint32_t sigLen, + BYTE callConv, DWORD ntypars, TypeHandle* inst, LoadTypesFlag fLoadTypes/*=LoadTypes*/, @@ -1634,7 +1637,7 @@ TypeHandle ClassLoader::LoadFnptrTypeThrowing(BYTE callConv, } CONTRACT_END - TypeKey key(callConv, ntypars, inst); + TypeKey key(pModule, sig, sigLen, callConv, ntypars, inst); RETURN(LoadConstructedTypeThrowing(&key, fLoadTypes, level)); } @@ -2955,11 +2958,25 @@ TypeHandle ClassLoader::CreateTypeHandleForTypeKey(TypeKey* pKey, AllocMemTracke Module *pLoaderModule = ComputeLoaderModule(pKey); 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))); + PREFIX_ASSUME(pLoaderModule != NULL); + PTR_LoaderHeap loaderHeap = pLoaderModule->GetAssembly()->GetLowFrequencyHeap(); - typeHnd = TypeHandle(new(mem) FnPtrTypeDesc(pKey->GetCallConv(), numArgs, pKey->GetRetAndArgTypes())); + int32_t sigLen = pKey->GetSignatureLen(); + PCOR_SIGNATURE pCopyOfSig = (PCOR_SIGNATURE)pamTracker->Track(loaderHeap->AllocMem(S_SIZE_T(sigLen))); + memcpy(pCopyOfSig, pKey->GetSignature(), sigLen); + + DWORD numArgs = pKey->GetNumArgs(); + BYTE* mem = (BYTE*) pamTracker->Track(loaderHeap->AllocMem( + S_SIZE_T(sizeof(FnPtrTypeDesc)) + + S_SIZE_T(sizeof(TypeHandle)) * S_SIZE_T(numArgs))); + + typeHnd = TypeHandle(new(mem) FnPtrTypeDesc( + pKey->GetModule(), + pCopyOfSig, + sigLen, + pKey->GetCallConv(), + numArgs, + pKey->GetRetAndArgTypes())); } else { diff --git a/src/coreclr/vm/clsload.hpp b/src/coreclr/vm/clsload.hpp index e446e239350af..c046b0f036909 100644 --- a/src/coreclr/vm/clsload.hpp +++ b/src/coreclr/vm/clsload.hpp @@ -693,7 +693,10 @@ class ClassLoader LoadTypesFlag fLoadTypes = LoadTypes, ClassLoadLevel level = CLASS_LOADED); - static TypeHandle LoadFnptrTypeThrowing(BYTE callConv, + static TypeHandle LoadFnptrTypeThrowing(Module *pModule, + PCOR_SIGNATURE sig, + uint32_t sigLen, + BYTE callConv, DWORD numArgs, TypeHandle* retAndArgTypes, LoadTypesFlag fLoadTypes = LoadTypes, diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index d2c3822f9de83..0030a8bf98b02 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -161,6 +161,7 @@ FCFuncStart(gCOMTypeHandleFuncs) FCFuncElement("IsValueType", RuntimeTypeHandle::IsValueType) FCFuncElement("IsInterface", RuntimeTypeHandle::IsInterface) FCFuncElement("IsByRefLike", RuntimeTypeHandle::IsByRefLike) + FCFuncElement("_IsUnmanagedFunctionPointer", RuntimeTypeHandle::IsUnmanagedFunctionPointer) FCFuncElement("CanCastTo", RuntimeTypeHandle::CanCastTo) FCFuncElement("HasInstantiation", RuntimeTypeHandle::HasInstantiation) FCFuncElement("GetGenericVariableIndex", RuntimeTypeHandle::GetGenericVariableIndex) @@ -206,6 +207,7 @@ FCFuncEnd() FCFuncStart(gSignatureNative) FCFuncElement("GetSignature", SignatureNative::GetSignature) + FCFuncElement("GetSignatureFromFunctionPointer", SignatureNative::GetSignatureFromFunctionPointer) FCFuncElement("GetCustomModifiers", SignatureNative::GetCustomModifiers) FCFuncElement("CompareSig", SignatureNative::CompareSig) FCFuncEnd() diff --git a/src/coreclr/vm/eedbginterfaceimpl.cpp b/src/coreclr/vm/eedbginterfaceimpl.cpp index 5d2edfb2c0292..1f4abe1d68e1d 100644 --- a/src/coreclr/vm/eedbginterfaceimpl.cpp +++ b/src/coreclr/vm/eedbginterfaceimpl.cpp @@ -884,7 +884,12 @@ TypeHandle EEDbgInterfaceImpl::FindLoadedFnptrType(TypeHandle *inst, ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); // : CALLCONV? - return ClassLoader::LoadFnptrTypeThrowing(0, ntypars, inst, + return ClassLoader::LoadFnptrTypeThrowing(NULL, /*module*/ + NULL, /*sig*/ + 0, /*sigLen*/ + 0, /*callConv*/ + ntypars, + inst, // should this be FailIfNotLoaded? - NO - although we may // want to debug unrestored VCs, we can't do it because the debug API // is not set up to handle them @@ -1009,7 +1014,12 @@ TypeHandle EEDbgInterfaceImpl::LoadFnptrType(TypeHandle *inst, CONTRACTL_END; /* @TODO : CALLCONV? */ - return ClassLoader::LoadFnptrTypeThrowing(0, ntypars, inst); + return ClassLoader::LoadFnptrTypeThrowing(NULL, /*module*/ + NULL, /*sig*/ + 0, /*sigLen*/ + 0, /*callConv*/ + ntypars, + inst); } TypeHandle EEDbgInterfaceImpl::LoadElementType(CorElementType et) diff --git a/src/coreclr/vm/runtimehandles.cpp b/src/coreclr/vm/runtimehandles.cpp index c849a0a53d5eb..d3d485bf46a94 100644 --- a/src/coreclr/vm/runtimehandles.cpp +++ b/src/coreclr/vm/runtimehandles.cpp @@ -899,6 +899,39 @@ FCIMPL1(FC_BOOL_RET, RuntimeTypeHandle::IsByRefLike, ReflectClassBaseObject *pTy } FCIMPLEND +FCIMPL1(FC_BOOL_RET, RuntimeTypeHandle::IsUnmanagedFunctionPointer, ReflectClassBaseObject *pTypeUNSAFE) +{ + CONTRACTL { + FCALL_CHECK; + PRECONDITION(CheckPointer(pTypeUNSAFE)); + } + CONTRACTL_END; + + REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); + _ASSERTE(refType != NULL); + + TypeHandle typeHandle = refType->GetType(); + if (!typeHandle.IsFnPtrType()) + FC_RETURN_BOOL(FALSE); + + FnPtrTypeDesc* fnPtr = typeHandle.AsFnPtrType(); + if (fnPtr == NULL) + FCThrowRes(kArgumentException, W("Arg_InvalidHandle")); + + switch ((CorCallingConvention)(fnPtr->GetCallConv() & 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: + case IMAGE_CEE_CS_CALLCONV_UNMANAGED: + FC_RETURN_BOOL(TRUE); + default: + FC_RETURN_BOOL(FALSE); + } +} +FCIMPLEND; + extern "C" BOOL QCALLTYPE RuntimeTypeHandle_IsVisible(QCall::TypeHandle pTypeHandle) { CONTRACTL @@ -2024,6 +2057,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); @@ -2062,6 +2096,58 @@ FCIMPL6(void, SignatureNative::GetSignature, } FCIMPLEND +FCIMPL2(void, SignatureNative::GetSignatureFromFunctionPointer, + SignatureNative* pSignatureNativeUNSAFE, + ReflectClassBaseObject *pTypeUNSAFE) { + CONTRACTL { + FCALL_CHECK; + PRECONDITION(CheckPointer(pTypeUNSAFE)); + } + CONTRACTL_END; + + struct + { + REFLECTCLASSBASEREF refType; + SIGNATURENATIVEREF pSig; + } gc; + + gc.refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); + gc.pSig = (SIGNATURENATIVEREF)pSignatureNativeUNSAFE; + + TypeHandle typeHandle = gc.refType->GetType(); + if (!typeHandle.IsFnPtrType()) + FCThrowResVoid(kArgumentException, W("Arg_InvalidHandle")); + + FnPtrTypeDesc* fnPtr = typeHandle.AsFnPtrType(); + + HELPER_METHOD_FRAME_BEGIN_PROTECT(gc); + { + gc.pSig->m_sig = fnPtr->GetSignature(); + gc.pSig->m_cSig = fnPtr->GetSignatureLen(); + gc.pSig->SetCallingConvention(fnPtr->GetCallConv()); + + REFLECTCLASSBASEREF refDeclType = (REFLECTCLASSBASEREF)fnPtr->GetManagedClassObject(); + gc.pSig->SetDeclaringType(refDeclType); + + INT32 numArgs = fnPtr->GetNumArgs(); + TypeHandle arrayHandle = ClassLoader::LoadArrayTypeThrowing(TypeHandle(g_pRuntimeTypeClass), ELEMENT_TYPE_SZARRAY); + PTRARRAYREF ptrArrayarguments = (PTRARRAYREF) AllocateSzArray(arrayHandle, numArgs); + gc.pSig->SetArgumentArray(ptrArrayarguments); + + TypeHandle *retAndArgTypes = fnPtr->GetRetAndArgTypesPointer(); + OBJECTREF refRetType = retAndArgTypes[0].GetManagedClassObject(); + gc.pSig->SetReturnType(refRetType); + + for (INT32 i = 0; i < numArgs; i++) + { + OBJECTREF refArgType = retAndArgTypes[i + 1].GetManagedClassObject(); + gc.pSig->SetArgument(i, refArgType); + } + } + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + FCIMPL2(FC_BOOL_RET, SignatureNative::CompareSig, SignatureNative* pLhsUNSAFE, SignatureNative* pRhsUNSAFE) { FCALL_CONTRACT; diff --git a/src/coreclr/vm/runtimehandles.h b/src/coreclr/vm/runtimehandles.h index a4bddd1e3ca9f..ba0c1a1b22c58 100644 --- a/src/coreclr/vm/runtimehandles.h +++ b/src/coreclr/vm/runtimehandles.h @@ -140,6 +140,7 @@ class RuntimeTypeHandle { static FCDECL1(FC_BOOL_RET, IsValueType, ReflectClassBaseObject* pType); static FCDECL1(FC_BOOL_RET, IsInterface, ReflectClassBaseObject* pType); static FCDECL1(FC_BOOL_RET, IsByRefLike, ReflectClassBaseObject* pType); + static FCDECL1(FC_BOOL_RET, IsUnmanagedFunctionPointer, ReflectClassBaseObject* pType); static FCDECL2(FC_BOOL_RET, CanCastTo, ReflectClassBaseObject *pType, ReflectClassBaseObject *pTarget); static FCDECL2(FC_BOOL_RET, IsInstanceOfType, ReflectClassBaseObject *pType, Object *object); @@ -382,6 +383,11 @@ class SignatureNative : public Object PCCOR_SIGNATURE pCorSig, DWORD cCorSig, FieldDesc *pFieldDesc, ReflectMethodObject *pMethodUNSAFE, ReflectClassBaseObject *pDeclaringType); + + static FCDECL2(void, GetSignatureFromFunctionPointer, + SignatureNative* pSignatureNativeUNSAFE, + ReflectClassBaseObject* pTypeUNSAFE); + static FCDECL3(Object *, GetCustomModifiers, SignatureNative* pSig, INT32 parameter, CLR_BOOL fRequired); static FCDECL2(FC_BOOL_RET, CompareSig, SignatureNative* pLhs, SignatureNative* pRhs); diff --git a/src/coreclr/vm/sigformat.h b/src/coreclr/vm/sigformat.h index 65a50db0a8448..7e043af08c5ee 100644 --- a/src/coreclr/vm/sigformat.h +++ b/src/coreclr/vm/sigformat.h @@ -19,6 +19,7 @@ class SigFormat //@GENERICS: the owning type handle is required because pMeth may be shared between instantiations SigFormat(MethodDesc* pMeth, TypeHandle owner, BOOL fIgnoreMethodName = false); SigFormat(MetaSig &metaSig, LPCUTF8 memberName, LPCUTF8 className = NULL, LPCUTF8 ns = NULL); + SigFormat(FnPtrTypeDesc* pFnPtr); ~SigFormat(); diff --git a/src/coreclr/vm/siginfo.cpp b/src/coreclr/vm/siginfo.cpp index a8d18ec63a293..3bc26276d4d79 100644 --- a/src/coreclr/vm/siginfo.cpp +++ b/src/coreclr/vm/siginfo.cpp @@ -1488,9 +1488,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; } } @@ -1542,8 +1542,8 @@ TypeHandle SigPointer::GetTypeHandleThrowing( pZapSigContext); if (elemType.IsNull()) { - thRet = elemType; - break; + thRet = elemType; + break; } uint32_t rank = 0; @@ -1554,7 +1554,7 @@ TypeHandle SigPointer::GetTypeHandleThrowing( _ASSERTE(0 < rank); } thRet = ClassLoader::LoadArrayTypeThrowing(elemType, typ, rank, fLoadTypes, level); - break; + break; } case ELEMENT_TYPE_PINNED: @@ -1566,7 +1566,7 @@ TypeHandle SigPointer::GetTypeHandleThrowing( dropGenericArgumentLevel, pSubst, pZapSigContext); - break; + break; case ELEMENT_TYPE_BYREF: case ELEMENT_TYPE_PTR: @@ -1584,14 +1584,15 @@ 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 sigStart = psig.m_dwLen; uint32_t uCallConv = 0; IfFailThrowBF(psig.GetData(&uCallConv), BFA_BAD_SIGNATURE, pOrigModule); @@ -1639,13 +1640,16 @@ TypeHandle SigPointer::GetTypeHandleThrowing( break; } - // Now make the function pointer type - thRet = ClassLoader::LoadFnptrTypeThrowing((BYTE) uCallConv, cArgs, retAndArgTypes, fLoadTypes, level); + uint32_t sigLen = sigStart - psig.m_dwLen; + PCOR_SIGNATURE sig = (PCOR_SIGNATURE)psig.m_ptr - sigLen; + + // Now find an existing function pointer or make a new one + thRet = ClassLoader::LoadFnptrTypeThrowing(static_cast(pOrigModule), sig, sigLen, (BYTE) uCallConv, cArgs, retAndArgTypes, fLoadTypes, level); #else - DacNotImpl(); + DacNotImpl(); thRet = TypeHandle(); #endif - break; + break; } case ELEMENT_TYPE_INTERNAL : diff --git a/src/coreclr/vm/typedesc.cpp b/src/coreclr/vm/typedesc.cpp index 41abdebda71ae..2eda469d4c16d 100644 --- a/src/coreclr/vm/typedesc.cpp +++ b/src/coreclr/vm/typedesc.cpp @@ -108,8 +108,6 @@ PTR_Module TypeDesc::GetModule() { GC_NOTRIGGER; FORBID_FAULT; SUPPORTS_DAC; - // Function pointer types belong to no module - //PRECONDITION(GetInternalCorElementType() != ELEMENT_TYPE_FNPTR); } CONTRACTL_END @@ -129,7 +127,10 @@ PTR_Module TypeDesc::GetModule() { _ASSERTE(GetInternalCorElementType() == ELEMENT_TYPE_FNPTR); - return GetLoaderModule(); + // A Function pointer keeps the reference to the original module in order to lazily resolve + // custom modifier types from its signature. + PTR_FnPtrTypeDesc asFn = dac_cast(this); + return asFn->GetModule(); } Assembly* TypeDesc::GetAssembly() { @@ -518,6 +519,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 19833eede708b..e5ac11ff26aef 100644 --- a/src/coreclr/vm/typedesc.h +++ b/src/coreclr/vm/typedesc.h @@ -439,17 +439,24 @@ class TypeVarTypeDesc : public TypeDesc /*************************************************************************/ /* represents a function type. */ -typedef SPTR(class FnPtrTypeDesc) PTR_FnPtrTypeDesc; +typedef DPTR(class FnPtrTypeDesc) PTR_FnPtrTypeDesc; class FnPtrTypeDesc : public TypeDesc { public: #ifndef DACCESS_COMPILE - FnPtrTypeDesc(BYTE callConv, DWORD numArgs, TypeHandle * retAndArgTypes) - : TypeDesc(ELEMENT_TYPE_FNPTR), m_NumArgs(numArgs), m_CallConv(callConv) + FnPtrTypeDesc( + PTR_Module module, + PCOR_SIGNATURE sig /*copy made by caller*/, + int32_t sigLen, + BYTE callConv, + DWORD numArgs, + TypeHandle * retAndArgTypes) + : TypeDesc(ELEMENT_TYPE_FNPTR), m_pModule(module), m_hExposedClassObject(0), m_NumArgs(numArgs), m_CallConv(callConv), m_sig(sig), m_sigLen(sigLen) { LIMITED_METHOD_CONTRACT; + for (DWORD i = 0; i <= numArgs; i++) { m_RetAndArgTypes[i] = retAndArgTypes[i]; @@ -472,6 +479,27 @@ class FnPtrTypeDesc : public TypeDesc return static_cast(m_CallConv); } + PCOR_SIGNATURE GetSignature() + { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + return m_sig; + } + + uint32_t GetSignatureLen() + { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + return m_sigLen; + } + + PTR_Module GetModule() + { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + return m_pModule; + } + // Return a pointer to the types of the signature, return type followed by argument types // The type handles are guaranteed to be fixed up TypeHandle * GetRetAndArgTypes(); @@ -507,13 +535,46 @@ 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: + PTR_Module m_pModule; + + // Handle back to the internal reflection Type object + LOADERHANDLE m_hExposedClassObject; + // Number of arguments DWORD m_NumArgs; // Calling convention (actually just a single byte) DWORD m_CallConv; + PCOR_SIGNATURE m_sig; + uint32_t m_sigLen; + // Return type first, then argument types TypeHandle m_RetAndArgTypes[1]; }; // class FnPtrTypeDesc diff --git a/src/coreclr/vm/typehandle.cpp b/src/coreclr/vm/typehandle.cpp index c0b194c86e9fa..46cdfac29a718 100644 --- a/src/coreclr/vm/typehandle.cpp +++ b/src/coreclr/vm/typehandle.cpp @@ -1098,8 +1098,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"); @@ -1459,7 +1458,12 @@ TypeKey TypeHandle::GetTypeKey() const { CONSISTENCY_CHECK(etype == ELEMENT_TYPE_FNPTR); FnPtrTypeDesc* pFTD = (FnPtrTypeDesc*) pTD; - TypeKey tk(pFTD->GetCallConv(), pFTD->GetNumArgs(), pFTD->GetRetAndArgTypesPointer()); + TypeKey tk(NULL /*module*/, + pFTD->GetSignature(), + pFTD->GetSignatureLen(), + pFTD->GetCallConv(), + pFTD->GetNumArgs(), + pFTD->GetRetAndArgTypesPointer()); return tk; } } diff --git a/src/coreclr/vm/typehandle.h b/src/coreclr/vm/typehandle.h index 80a0e78865802..73da23b733cae 100644 --- a/src/coreclr/vm/typehandle.h +++ b/src/coreclr/vm/typehandle.h @@ -37,7 +37,7 @@ namespace Generics { class RecursionGraph; } struct CORINFO_CLASS_STRUCT_; typedef DPTR(class TypeVarTypeDesc) PTR_TypeVarTypeDesc; -typedef SPTR(class FnPtrTypeDesc) PTR_FnPtrTypeDesc; +typedef DPTR(class FnPtrTypeDesc) PTR_FnPtrTypeDesc; typedef DPTR(class ParamTypeDesc) PTR_ParamTypeDesc; typedef DPTR(class TypeDesc) PTR_TypeDesc; typedef DPTR(class TypeHandle) PTR_TypeHandle; @@ -460,6 +460,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 ffe668a901f76..58300c18f471b 100644 --- a/src/coreclr/vm/typehandle.inl +++ b/src/coreclr/vm/typehandle.inl @@ -264,8 +264,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();//GetManagedClassObjectIfExists(); break; default: diff --git a/src/coreclr/vm/typehash.cpp b/src/coreclr/vm/typehash.cpp index dfc355c3fafba..596ea0d2621c2 100644 --- a/src/coreclr/vm/typehash.cpp +++ b/src/coreclr/vm/typehash.cpp @@ -167,7 +167,7 @@ static DWORD HashPossiblyInstantiatedType(mdTypeDef token, Instantiation inst) } // Calculate hash value for a function pointer type -static DWORD HashFnPtrType(BYTE callConv, DWORD numArgs, TypeHandle *retAndArgTypes) +static DWORD HashFnPtrType(uint32_t sigLen, BYTE callConv, DWORD numArgs, TypeHandle *retAndArgTypes) { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; @@ -223,7 +223,11 @@ static DWORD HashTypeHandle(TypeHandle t) else if (t.IsFnPtrType()) { FnPtrTypeDesc* pTD = t.AsFnPtrType(); - retVal = HashFnPtrType(pTD->GetCallConv(), pTD->GetNumArgs(), pTD->GetRetAndArgTypesPointer()); + retVal = HashFnPtrType( + pTD->GetSignatureLen(), + pTD->GetCallConv(), + pTD->GetNumArgs(), + pTD->GetRetAndArgTypesPointer()); } else if (t.IsGenericVariable()) { @@ -257,7 +261,7 @@ DWORD HashTypeKey(TypeKey* pKey) } else if (pKey->GetKind() == ELEMENT_TYPE_FNPTR) { - return HashFnPtrType(pKey->GetCallConv(), pKey->GetNumArgs(), pKey->GetRetAndArgTypes()); + return HashFnPtrType(pKey->GetSignatureLen(), pKey->GetCallConv(), pKey->GetNumArgs(), pKey->GetRetAndArgTypes()); } else { @@ -311,6 +315,9 @@ EETypeHashEntry_t *EETypeHashTable::FindItem(TypeKey* pKey) } else if (kind == ELEMENT_TYPE_FNPTR) { + Module* pModule = pKey->GetModule(); + PCOR_SIGNATURE sig = pKey->GetSignature(); + uint32_t sigLen = pKey->GetSignatureLen(); BYTE callConv = pKey->GetCallConv(); DWORD numArgs = pKey->GetNumArgs(); TypeHandle *retAndArgTypes = pKey->GetRetAndArgTypes(); @@ -318,7 +325,7 @@ EETypeHashEntry_t *EETypeHashTable::FindItem(TypeKey* pKey) pSearch = BaseFindFirstEntryByHash(dwHash, &sContext); while (pSearch) { - if (CompareFnPtrType(pSearch->GetTypeHandle(), callConv, numArgs, retAndArgTypes)) + if (CompareFnPtrType(pSearch->GetTypeHandle(), pModule, sig, sigLen, callConv, numArgs, retAndArgTypes)) { result = pSearch; break; @@ -436,7 +443,8 @@ BOOL EETypeHashTable::CompareInstantiatedType(TypeHandle t, Module *pModule, mdT return TRUE; } -BOOL EETypeHashTable::CompareFnPtrType(TypeHandle t, BYTE callConv, DWORD numArgs, TypeHandle *retAndArgTypes) +// See also TypeKey::Equals +BOOL EETypeHashTable::CompareFnPtrType(TypeHandle t, Module* pModule, PCOR_SIGNATURE sig, uint32_t sigLen, BYTE callConv, DWORD numArgs, TypeHandle *retAndArgTypes) { CONTRACTL { @@ -445,6 +453,7 @@ BOOL EETypeHashTable::CompareFnPtrType(TypeHandle t, BYTE callConv, DWORD numArg GC_NOTRIGGER; MODE_ANY; PRECONDITION(CheckPointer(t)); + PRECONDITION(CheckPointer(sig, NULL_OK)); PRECONDITION(CheckPointer(retAndArgTypes)); SUPPORTS_DAC; } @@ -455,6 +464,13 @@ BOOL EETypeHashTable::CompareFnPtrType(TypeHandle t, BYTE callConv, DWORD numArg #ifndef DACCESS_COMPILE + // A FnPtrTypeDesc has all signature information already extracted except for + // custom mods and it contains a copy of the raw signature. Not all keys however have the + // raw signature information, so we first check against the already extracted information, + // and then optionally compare the signature in order to verify against custom mods. + // If every key (such as DacDbiInterfaceImpl::FindLoadedFnptrType) had a signature, we could + // just compare against the signature. + FnPtrTypeDesc* pTD = t.AsFnPtrType(); if (pTD->GetNumArgs() != numArgs || pTD->GetCallConv() != callConv) @@ -470,7 +486,26 @@ BOOL EETypeHashTable::CompareFnPtrType(TypeHandle t, BYTE callConv, DWORD numArg } } - return TRUE; + // The checks below are made optional to support DacDbiInterfaceImpl::FindLoadedFnptrType which doesn't + // consider calling convention (including unmanaged custom modifiers), module or signature. If the signature length + // is not provided do not include the comparison against the signature. + + if (sigLen == 0 && pTD->GetSignatureLen() == 0) + { + // Some keys don't have a signature; just skip in that case. + return TRUE; + } + + if (sigLen == 0 || pTD->GetSignatureLen() == 0) + { + // Only one side has a signature. + return FALSE; + } + + return MetaSig::CompareMethodSigs( + pTD->GetSignature(), pTD->GetSignatureLen(), pTD->GetModule(), NULL, + sig, sigLen, pModule, NULL, + FALSE); #else DacNotImpl(); diff --git a/src/coreclr/vm/typehash.h b/src/coreclr/vm/typehash.h index b706ee64a4493..188ec1e0bb052 100644 --- a/src/coreclr/vm/typehash.h +++ b/src/coreclr/vm/typehash.h @@ -136,7 +136,7 @@ class EETypeHashTable : public DacEnumerableHashTablem_kind == ELEMENT_TYPE_FNPTR); - if (pKey1->u.asFnPtr.m_callConv != pKey2->u.asFnPtr.m_callConv || + + // See also EETypeHashTable::CompareFnPtrType + + BYTE callConv = pKey1->u.asFnPtr.m_callConv; + if (callConv != pKey2->u.asFnPtr.m_callConv || pKey1->u.asFnPtr.m_numArgs != pKey2->u.asFnPtr.m_numArgs) return FALSE; @@ -253,7 +281,26 @@ class TypeKey if (pKey1->u.asFnPtr.m_pRetAndArgTypes[i] != pKey2->u.asFnPtr.m_pRetAndArgTypes[i]) return FALSE; } - return TRUE; + + // Match the signatures. + uint32_t sigLen1 = pKey1->u.asFnPtr.m_sigLen; + uint32_t sigLen2 = pKey2->u.asFnPtr.m_sigLen; + if (sigLen1 == 0 && sigLen2 == 0) + { + // Some keys don't have a signature; just skip in that case. + return TRUE; + } + + if (sigLen1 == 0 || sigLen2 == 0) + { + // Only one key has a signature. + return FALSE; + } + + return MetaSig::CompareMethodSigs( + pKey1->u.asFnPtr.m_sig, sigLen1, pKey1->u.asFnPtr.m_pModule, NULL, + pKey2->u.asFnPtr.m_sig, sigLen2, pKey2->u.asFnPtr.m_pModule, NULL, + FALSE); } } }; diff --git a/src/coreclr/vm/typestring.cpp b/src/coreclr/vm/typestring.cpp index 0a05972721a1a..d9e7302684474 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,47 @@ 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://docs.microsoft.com/en-us/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 df54ed76b5bec..13ec1f4e42a3d 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/FunctionPointerTests.cs b/src/libraries/Common/tests/System/FunctionPointerTests.cs new file mode 100644 index 0000000000000..eb84377e516e7 --- /dev/null +++ b/src/libraries/Common/tests/System/FunctionPointerTests.cs @@ -0,0 +1,474 @@ +// 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 Xunit; + +namespace System.Tests.Types +{ + 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)] + public static unsafe void TestTypeMembers() + { + // 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("*()", t.Name); + Assert.Equal("System", t.Namespace); // All function pointers report "System" + Assert.True(t.IsFunctionPointer); + Assert.False(t.IsPointer); // A function pointer is not compatible with IsPointer semantics. + Assert.False(t.IsUnmanagedFunctionPointer); + + // Common for all function pointers: + Assert.Equal(typeof(FunctionPointerTests).Project().Assembly, t.Assembly); + Assert.Equal(TypeAttributes.Public, t.Attributes); + Assert.Null(t.BaseType); + Assert.False(t.ContainsGenericParameters); + 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); + + try + { + // MetadataLoadContext throws here. + Assert.True(t.IsSecurityCritical); + Assert.False(t.IsSecuritySafeCritical); + Assert.False(t.IsSecurityTransparent); + } + catch (InvalidOperationException) { } + + 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.Equal(typeof(FunctionPointerTests).Project().Module, t.Module); + Assert.Null(t.ReflectedType); + Assert.Null(t.TypeInitializer); + + // Select methods + Assert.Throws(() => t.GetArrayRank()); + Assert.Null(t.GetElementType()); + } + + private static void MyMethod(){} + + + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)] + public static unsafe void TestNonFunctionPointerThrows() + { + Assert.Throws(() => typeof(int).GetFunctionPointerCallingConventions()); + Assert.Throws(() => typeof(int).GetFunctionPointerParameterInfos()); + Assert.Throws(() => typeof(int).GetFunctionPointerReturnParameter()); + } + + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)] + 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)] + public static unsafe void TestFunctionPointerReturn() + { + 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.Equals(fcnPtr2)); + } + + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)] + public static unsafe void TestFunctionUnmanagedPointerReturn_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.Equals(fcnPtr2)); + } + + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)] + public static unsafe void TestFunctionUnmanagedPointerReturn_DifferentCallingConventions() + { + Type t = typeof(FunctionPointerHolder).Project(); + + MethodInfo m1 = t.GetMethod(nameof(FunctionPointerHolder.MethodUnmanagedReturnValue_DifferentCallingConventions1), Bindings); + Type fcnPtr1 = m1.ReturnType; + Assert.True(fcnPtr1.IsFunctionPointer); + Assert.True(fcnPtr1.IsUnmanagedFunctionPointer); + + MethodInfo m2 = t.GetMethod(nameof(FunctionPointerHolder.MethodUnmanagedReturnValue_DifferentCallingConventions2), Bindings); + Type fcnPtr2 = m2.ReturnType; + Assert.True(fcnPtr2.IsFunctionPointer); + Assert.True(fcnPtr2.IsUnmanagedFunctionPointer); + + // The modopts are considered part of the "type key" + Assert.NotSame(fcnPtr1, fcnPtr2); + Assert.False(fcnPtr1.Equals(fcnPtr2)); + + FunctionPointerParameterInfo retInfo = fcnPtr1.GetFunctionPointerReturnParameter(); + Assert.Equal(typeof(int).Project(), retInfo.ParameterType); + + Type[] modOpts = fcnPtr1.GetFunctionPointerReturnParameter().GetOptionalCustomModifiers(); + Assert.Equal(2, modOpts.Length); + } + + [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)] + public static unsafe void TestMethod( + 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.ReturnType; + Assert.Null(fnPtrType.FullName); + Assert.Null(fnPtrType.AssemblyQualifiedName); + Assert.Equal("System", fnPtrType.Namespace); + Assert.Equal("*()", fnPtrType.Name); + Assert.Equal(t.Module, fnPtrType.Module); + + VerifyArg(fnPtrType.GetFunctionPointerReturnParameter(), expectedFcnPtrReturnName); + + for (int i = 0; i < expectedArgNames.Length; i++) + { + VerifyArg(fnPtrType.GetFunctionPointerParameterInfos()[i], expectedArgNames[i]); + } + + static void VerifyArg(FunctionPointerParameterInfo paramInfo, string expected) + { + Assert.Equal(expected, paramInfo.ParameterType.Name); + Assert.Equal(paramInfo.ParameterType.ToString(), paramInfo.ToString()); + Assert.Null(paramInfo.GetType().DeclaringType); + } + } + + [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)] + public static unsafe void TestProperty(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(t.Module, fnPtrType.Module); + 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)] + public static unsafe void TestField(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(t.Module, fnPtrType.Module); + 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("System", fnPtrType.Namespace); + Assert.Null(fnPtrType.DeclaringType); + Assert.Null(fnPtrType.BaseType); + Assert.Equal(Type.EmptyTypes, fnPtrType.GetFunctionPointerCallingConventions()); + Assert.Equal(Type.EmptyTypes, fnPtrType.GetFunctionPointerReturnParameter().GetRequiredCustomModifiers()); + } + + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)] + public static unsafe void TestManagedCallingConvention() + { + Type t = typeof(FunctionPointerHolder).Project(); + MethodInfo m = t.GetMethod(nameof(FunctionPointerHolder.MethodCallConv_Managed), Bindings); + Type fnPtrType = m.GetParameters()[0].ParameterType; + Type[] callConvs = fnPtrType.GetFunctionPointerCallingConventions(); + Assert.Equal(0, callConvs.Length); + Assert.True(fnPtrType.IsFunctionPointer); + Assert.False(fnPtrType.IsUnmanagedFunctionPointer); + + FunctionPointerParameterInfo returnParam = fnPtrType.GetFunctionPointerReturnParameter(); + Assert.Equal(0, returnParam.GetOptionalCustomModifiers().Length); + Assert.Equal(0, returnParam.GetRequiredCustomModifiers().Length); + } + + [Theory] + [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Cdecl), typeof(CallConvCdecl))] + [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Fastcall), typeof(CallConvFastcall))] + [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Stdcall), typeof(CallConvStdcall))] + [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Thiscall), typeof(CallConvThiscall))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)] + public static unsafe void TestBaseCallingConventions(string methodName, Type callingConventionRuntime) + { + Type callingConvention = callingConventionRuntime.Project(); + Type t = typeof(FunctionPointerHolder).Project(); + MethodInfo m = t.GetMethod(methodName, Bindings); + Type fnPtrType = m.GetParameters()[0].ParameterType; + Type[] callConvs = fnPtrType.GetFunctionPointerCallingConventions(); + + Assert.Equal(1, callConvs.Length); + Assert.Equal(callingConvention, callConvs[0]); + Assert.True(fnPtrType.IsFunctionPointer); + Assert.True(fnPtrType.IsUnmanagedFunctionPointer); + + FunctionPointerParameterInfo returnParam = fnPtrType.GetFunctionPointerReturnParameter(); + Assert.Equal(1, returnParam.GetOptionalCustomModifiers().Length); + Assert.Equal(callingConvention, returnParam.GetOptionalCustomModifiers()[0]); + } + + [Theory] + [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Cdecl_SuppressGCTransition), typeof(CallConvCdecl))] + [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Fastcall_SuppressGCTransition), typeof(CallConvFastcall))] + [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Stdcall_SuppressGCTransition), typeof(CallConvStdcall))] + [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Thiscall_SuppressGCTransition), typeof(CallConvThiscall))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)] + public static unsafe void TestOptionalCallingConventions(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].ParameterType; + + FunctionPointerParameterInfo returnParam = fnPtrType.GetFunctionPointerReturnParameter(); + Type[] callConvs = fnPtrType.GetFunctionPointerCallingConventions(); + Assert.Equal(2, callConvs.Length); + Assert.Equal(suppressGcTransitionType, callConvs[0]); + Assert.Equal(callingConvention, callConvs[1]); + Assert.True(fnPtrType.IsFunctionPointer); + Assert.True(fnPtrType.IsUnmanagedFunctionPointer); + Assert.Equal(2, returnParam.GetOptionalCustomModifiers().Length); + Assert.Equal(suppressGcTransitionType, returnParam.GetOptionalCustomModifiers()[0]); + Assert.Equal(callingConvention, returnParam.GetOptionalCustomModifiers()[1]); + Assert.Equal(0, returnParam.GetRequiredCustomModifiers().Length); + } + + [Theory] + [InlineData(nameof(FunctionPointerHolder.Field_Int), nameof(FunctionPointerHolder.Field_DateOnly))] + [InlineData(nameof(FunctionPointerHolder.Field_DateOnly), nameof(FunctionPointerHolder.Field_Int))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)] + public static unsafe void TestSigEqualityInDifferentModule_Field(string name, string otherName) + { + Type fph1 = typeof(FunctionPointerHolder).Project(); + Type fph2 = typeof(FunctionPointerHolderSeparateModule).Project(); + Assert.True(GetFuncPtr(fph1, name).IsEqualOrReferenceEquals(GetFuncPtr(fph2, name))); + + // Verify other combinations fail + Assert.False(GetFuncPtr(fph1, name).IsEqualOrReferenceEquals(GetFuncPtr(fph1, otherName))); + Assert.False(GetFuncPtr(fph1, name).IsEqualOrReferenceEquals(GetFuncPtr(fph1, otherName))); + Assert.False(GetFuncPtr(fph2, name).IsEqualOrReferenceEquals(GetFuncPtr(fph2, otherName))); + Assert.False(GetFuncPtr(fph2, name).IsEqualOrReferenceEquals(GetFuncPtr(fph2, otherName))); + + static Type GetFuncPtr(Type owner, string name) => owner.GetField(name, Bindings).FieldType; + } + + [Theory] + [InlineData(nameof(FunctionPointerHolder.Prop_Int), nameof(FunctionPointerHolder.Prop_DateOnly))] + [InlineData(nameof(FunctionPointerHolder.Prop_DateOnly), nameof(FunctionPointerHolder.Prop_Int))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)] + public static unsafe void TestSigEqualityInDifferentModule_Property(string name, string otherName) + { + Type fph1 = typeof(FunctionPointerHolder).Project(); + Type fph2 = typeof(FunctionPointerHolderSeparateModule).Project(); + Assert.True(GetFuncPtr(fph1, name).IsEqualOrReferenceEquals(GetFuncPtr(fph2, name))); + + // Verify other combinations fail + Assert.False(GetFuncPtr(fph1, name).IsEqualOrReferenceEquals(GetFuncPtr(fph1, otherName))); + Assert.False(GetFuncPtr(fph1, name).IsEqualOrReferenceEquals(GetFuncPtr(fph1, otherName))); + Assert.False(GetFuncPtr(fph2, name).IsEqualOrReferenceEquals(GetFuncPtr(fph2, otherName))); + Assert.False(GetFuncPtr(fph2, name).IsEqualOrReferenceEquals(GetFuncPtr(fph2, otherName))); + + static Type GetFuncPtr(Type owner, string name) => owner.GetProperty(name, Bindings).PropertyType; + } + + [Theory] + [InlineData(nameof(FunctionPointerHolder.MethodReturnValue_Int), nameof(FunctionPointerHolder.MethodReturnValue_DateOnly))] + [InlineData(nameof(FunctionPointerHolder.MethodReturnValue_DateOnly), nameof(FunctionPointerHolder.MethodReturnValue_Int))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)] + public static unsafe void TestSigEqualityInDifferentModule_MethodReturn(string name, string otherName) + { + Type fph1 = typeof(FunctionPointerHolder).Project(); + Type fph2 = typeof(FunctionPointerHolderSeparateModule).Project(); + Assert.True(GetFuncPtr(fph1, name).IsEqualOrReferenceEquals(GetFuncPtr(fph2, name))); + + // Verify other combinations fail + Assert.False(GetFuncPtr(fph1, name).IsEqualOrReferenceEquals(GetFuncPtr(fph1, otherName))); + Assert.False(GetFuncPtr(fph1, name).IsEqualOrReferenceEquals(GetFuncPtr(fph1, otherName))); + Assert.False(GetFuncPtr(fph2, name).IsEqualOrReferenceEquals(GetFuncPtr(fph2, otherName))); + Assert.False(GetFuncPtr(fph2, name).IsEqualOrReferenceEquals(GetFuncPtr(fph2, otherName))); + + static Type GetFuncPtr(Type owner, string name) => owner.GetMethod(name, Bindings).ReturnParameter.ParameterType; + } + + public unsafe class FunctionPointerHolder + { + 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_DateOnly; // Verify non-primitive + public delegate* managed Field_MyClass; + public delegate* managed Prop_Int { get; } + public delegate* managed Prop_DateOnly { get; } + public delegate* managed Prop_MyClass { get; } + public delegate* managed MethodReturnValue_Int() => default; + public delegate* managed MethodReturnValue_DateOnly() => default; + public delegate* unmanaged MethodUnmanagedReturnValue_Int() => default; + public delegate* unmanaged MethodUnmanagedReturnValue_DateOnly() => default; + + public delegate* managed MethodReturnValue1() => default; + public delegate* managed MethodReturnValue2() => default; + public delegate* unmanaged MethodUnmanagedReturnValue1() => default; + public delegate* unmanaged MethodUnmanagedReturnValue2() => default; + + + public delegate* unmanaged[Cdecl, MemberFunction] MethodUnmanagedReturnValue_DifferentCallingConventions1() => default; + public delegate* unmanaged[Stdcall, MemberFunction] MethodUnmanagedReturnValue_DifferentCallingConventions2() => default; + + public delegate* unmanaged[Stdcall, MemberFunction] SeveralArguments() => 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_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_Fastcall(delegate* unmanaged[Fastcall] f) { } + public void MethodCallConv_Fastcall_SuppressGCTransition(delegate* unmanaged[Fastcall, 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 class MyClass { } + public struct MyStruct { } + } + } +} diff --git a/src/libraries/Common/tests/System/TestFunctionPointerAssembly/FunctionPointerHolder.cs b/src/libraries/Common/tests/System/TestFunctionPointerAssembly/FunctionPointerHolder.cs new file mode 100644 index 0000000000000..0013f93e0f9ab --- /dev/null +++ b/src/libraries/Common/tests/System/TestFunctionPointerAssembly/FunctionPointerHolder.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Threading; + +public unsafe class FunctionPointerHolderSeparateModule +{ + public delegate* managed Field_Int; + public delegate* managed Field_DateOnly; // Verify non-primitive which will have its own Rid + 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 MethodUnmanagedReturnValue_Int() => default; + public delegate* unmanaged MethodUnmanagedReturnValue_DateOnly() => default; +} diff --git a/src/libraries/Common/tests/System/TestFunctionPointerAssembly/TestFunctionPointerAssembly.csproj b/src/libraries/Common/tests/System/TestFunctionPointerAssembly/TestFunctionPointerAssembly.csproj new file mode 100644 index 0000000000000..0a8720fa6bbda --- /dev/null +++ b/src/libraries/Common/tests/System/TestFunctionPointerAssembly/TestFunctionPointerAssembly.csproj @@ -0,0 +1,11 @@ + + + true + 1.0.0.0 + $(NetCoreAppCurrent) + True + + + + + diff --git a/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs b/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs index e4f909176df16..556a07ed18de3 100644 --- a/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs +++ b/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs @@ -316,9 +316,9 @@ 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()); + 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 3fc2d2014809e..089f7d149befe 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -3988,4 +3988,7 @@ Cannot load assembly '{0}'. No metadata found for this assembly. + + Method may only be called on a Type for which Type.IsFunctionPointer is true. + 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 a079ceb5f11ab..3ccb7899e6c00 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 @@ -616,6 +616,8 @@ + + @@ -628,6 +630,7 @@ + @@ -660,6 +663,7 @@ + @@ -2369,7 +2373,7 @@ - + @@ -2441,4 +2445,4 @@ - + \ No newline at end of file diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.cs new file mode 100644 index 0000000000000..ab65623823caf --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.cs @@ -0,0 +1,94 @@ +// 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; + +namespace System.Reflection +{ + internal sealed partial class FunctionPointerInfo + { + private const string CallingConventionTypePrefix = "System.Runtime.CompilerServices.CallConv"; + private readonly RuntimeFunctionPointerParameterInfo _returnInfo; + private readonly RuntimeFunctionPointerParameterInfo[] _parameterInfos; + private Type[]? _callingConventions; + + public FunctionPointerParameterInfo ReturnParameter => _returnInfo; + public FunctionPointerParameterInfo[] ParameterInfos => CloneArray(_parameterInfos); + + public Type[] GetCallingConventions() + { + if (_callingConventions == null) + { + Type[] customModifiers = _returnInfo.GetOptionalCustomModifiers(); + List? list = null; + bool foundCallingConvention = false; + + for (int i = 0; i < customModifiers.Length; i++) + { + Type type = customModifiers[i]; + if (type.FullName!.StartsWith(CallingConventionTypePrefix)) + { + list ??= new List(); + list.Add(type); + + if (type == typeof(CallConvCdecl) || + type == typeof(CallConvFastcall) || + type == typeof(CallConvStdcall) || + type == typeof(CallConvThiscall)) + { + foundCallingConvention = true; + } + } + } + + // Normalize the calling conventions. + if (!foundCallingConvention) + { + Type? callConv = null; + + switch (CallingConvention) + { + case MdSigCallingConvention.C: + callConv = typeof(CallConvCdecl); + break; + case MdSigCallingConvention.FastCall: + callConv = typeof(CallConvFastcall); + break; + case MdSigCallingConvention.StdCall: + callConv = typeof(CallConvStdcall); + break; + case MdSigCallingConvention.ThisCall: + callConv = typeof(CallConvThiscall); + break; + } + + if (callConv != null) + { + list ??= new List(); + list.Add(callConv); + _returnInfo.GetOptionalCustomModifiersList().Add(callConv); + } + } + + _callingConventions = list == null ? Type.EmptyTypes : list.ToArray(); + Debug.Assert(_callingConventions != null); + } + + return CloneArray(_callingConventions); + } + + private static T[] CloneArray(T[] original) + { + if (original.Length == 0) + { + return original; + } + + T[] copy = new T[original.Length]; + Array.Copy(sourceArray: original, sourceIndex: 0, destinationArray: copy, destinationIndex: 0, length: original.Length); + return copy; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/FunctionPointerParameterInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/FunctionPointerParameterInfo.cs new file mode 100644 index 0000000000000..b0870c0e61c62 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/FunctionPointerParameterInfo.cs @@ -0,0 +1,13 @@ +// 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 +{ + public abstract class FunctionPointerParameterInfo + { + public abstract Type ParameterType { get; } + public abstract Type[] GetOptionalCustomModifiers(); + public abstract Type[] GetRequiredCustomModifiers(); + public override string? ToString() => ParameterType.ToString(); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/MdSigCallingConvention.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/MdSigCallingConvention.cs new file mode 100644 index 0000000000000..eaa3aa2be4c9a --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/MdSigCallingConvention.cs @@ -0,0 +1,31 @@ +// 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 +{ + /// + /// Represents the first byte of a method signature. + /// Calling conventions have been extended into modopts for Unmanaged. + /// + [Flags] + internal enum MdSigCallingConvention : byte + { + CallConvMask = 0x0f, // Calling convention is bottom 4 bits + + Default = 0x00, + C = 0x01, + StdCall = 0x02, + ThisCall = 0x03, + FastCall = 0x04, + Vararg = 0x05, + Field = 0x06, + LocalSig = 0x07, + Property = 0x08, + Unmanaged = 0x09, + GenericInst = 0x0a, // generic method instantiation + + Generic = 0x10, // Generic method sig with explicit number of type arguments (precedes ordinary parameter count) + HasThis = 0x20, // Top bit indicates a 'this' parameter + ExplicitThis = 0x40, // This parameter is explicitly in the signature + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/RuntimeFunctionPointerParameterInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/RuntimeFunctionPointerParameterInfo.cs new file mode 100644 index 0000000000000..660b292ccd36a --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/RuntimeFunctionPointerParameterInfo.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Reflection +{ + internal sealed partial class RuntimeFunctionPointerParameterInfo : FunctionPointerParameterInfo + { + private readonly Type _parameterType; + + public override Type ParameterType => _parameterType; + public override string ToString() => _parameterType.FullName ?? string.Empty; + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/TypeDelegator.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/TypeDelegator.cs index ce3e75cde2606..f51096d1f5f40 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/TypeDelegator.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/TypeDelegator.cs @@ -10,7 +10,7 @@ namespace System.Reflection { - public class TypeDelegator : TypeInfo + public partial class TypeDelegator : TypeInfo { public override bool IsAssignableFrom([NotNullWhen(true)] TypeInfo? typeInfo) { @@ -84,6 +84,10 @@ public TypeDelegator([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes. [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] public override FieldInfo[] GetFields(BindingFlags bindingAttr) => typeImpl.GetFields(bindingAttr); + public override Type[] GetFunctionPointerCallingConventions() => typeImpl.GetFunctionPointerCallingConventions(); + public override FunctionPointerParameterInfo[] GetFunctionPointerParameterInfos() => typeImpl.GetFunctionPointerParameterInfos(); + public override FunctionPointerParameterInfo GetFunctionPointerReturnParameter() => typeImpl.GetFunctionPointerReturnParameter(); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] public override Type? GetInterface(string name, bool ignoreCase) => typeImpl.GetInterface(name, ignoreCase); @@ -146,6 +150,9 @@ public TypeDelegator([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes. public override bool IsCollectible => typeImpl.IsCollectible; + public override bool IsFunctionPointer => typeImpl.IsFunctionPointer; + public override bool IsUnmanagedFunctionPointer => typeImpl.IsUnmanagedFunctionPointer; + public override Type? GetElementType() => typeImpl.GetElementType(); protected override bool HasElementTypeImpl() => typeImpl.HasElementType; diff --git a/src/libraries/System.Private.CoreLib/src/System/Type.cs b/src/libraries/System.Private.CoreLib/src/System/Type.cs index 4907875d591c5..09b8c69e22bc4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Type.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Type.cs @@ -53,6 +53,9 @@ protected Type() { } public virtual bool IsByRefLike { [Intrinsic] get => throw new NotSupportedException(SR.NotSupported_SubclassOverride); } + public virtual bool IsFunctionPointer => false; + public virtual bool IsUnmanagedFunctionPointer => false; + public bool HasElementType => HasElementTypeImpl(); protected abstract bool HasElementTypeImpl(); public abstract Type? GetElementType(); @@ -201,6 +204,10 @@ public ConstructorInfo? TypeInitializer [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] public abstract FieldInfo[] GetFields(BindingFlags bindingAttr); + public virtual Type[] GetFunctionPointerCallingConventions() => throw new InvalidOperationException(SR.InvalidOperation_NotFunctionPointer); + public virtual FunctionPointerParameterInfo GetFunctionPointerReturnParameter() => throw new InvalidOperationException(SR.InvalidOperation_NotFunctionPointer); + public virtual FunctionPointerParameterInfo[] GetFunctionPointerParameterInfos() => throw new InvalidOperationException(SR.InvalidOperation_NotFunctionPointer); + [DynamicallyAccessedMembers( DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods | diff --git a/src/libraries/System.Reflection.Metadata/tests/Metadata/Decoding/SignatureDecoderTests.cs b/src/libraries/System.Reflection.Metadata/tests/Metadata/Decoding/SignatureDecoderTests.cs index 7669fbcc2ef31..df0e0ba6ee1a5 100644 --- a/src/libraries/System.Reflection.Metadata/tests/Metadata/Decoding/SignatureDecoderTests.cs +++ b/src/libraries/System.Reflection.Metadata/tests/Metadata/Decoding/SignatureDecoderTests.cs @@ -241,6 +241,8 @@ public void ByReference(ref int i) { } public struct Nested { } public Nested Property { get { throw null; } } public event EventHandler Event { add { } remove { } } + public delegate* managed ManagedFunctionPointer; + public delegate* unmanaged[Stdcall] NativeFunctionPointer; } [Fact] @@ -329,6 +331,8 @@ private static Dictionary GetExpectedFieldSignatures() { "Array", "int32[0...,0...]" }, { "GenericTypeParameter", "!T" }, { "GenericInstantiation", $"[{CollectionsAssemblyName}]System.Collections.Generic.List`1" }, + { "ManagedFunctionPointer", "method bool *(int32)" }, + { "NativeFunctionPointer", "method bool *(int32)" }, }; } diff --git a/src/libraries/System.Reflection.MetadataLoadContext/System.Reflection.MetadataLoadContext.sln b/src/libraries/System.Reflection.MetadataLoadContext/System.Reflection.MetadataLoadContext.sln index 702ac482ae494..9580f27200044 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/System.Reflection.MetadataLoadContext.sln +++ b/src/libraries/System.Reflection.MetadataLoadContext/System.Reflection.MetadataLoadContext.sln @@ -17,6 +17,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Reflection.MetadataL EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Reflection.MetadataLoadContext.Tests", "tests\System.Reflection.MetadataLoadContext.Tests.csproj", "{D28B6414-C82C-4BDE-B8BB-A4E3297A0651}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestFunctionPointerAssembly", "..\Common\tests\System\TestFunctionPointerAssembly\TestFunctionPointerAssembly.csproj", "{F85BDD51-AC29-4D8D-8257-C509BED9A448}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibraryImportGenerator", "..\System.Runtime.InteropServices\gen\LibraryImportGenerator\LibraryImportGenerator.csproj", "{4361CEFA-8238-4247-9CC5-D99DF794843C}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Interop.SourceGeneration", "..\System.Runtime.InteropServices\gen\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj", "{7393C7CD-4C31-4B1C-96DC-1D46D240538A}" @@ -73,6 +75,10 @@ Global {D28B6414-C82C-4BDE-B8BB-A4E3297A0651}.Debug|Any CPU.Build.0 = Debug|Any CPU {D28B6414-C82C-4BDE-B8BB-A4E3297A0651}.Release|Any CPU.ActiveCfg = Release|Any CPU {D28B6414-C82C-4BDE-B8BB-A4E3297A0651}.Release|Any CPU.Build.0 = Release|Any CPU + {F85BDD51-AC29-4D8D-8257-C509BED9A448}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F85BDD51-AC29-4D8D-8257-C509BED9A448}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F85BDD51-AC29-4D8D-8257-C509BED9A448}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F85BDD51-AC29-4D8D-8257-C509BED9A448}.Release|Any CPU.Build.0 = Release|Any CPU {4361CEFA-8238-4247-9CC5-D99DF794843C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4361CEFA-8238-4247-9CC5-D99DF794843C}.Debug|Any CPU.Build.0 = Debug|Any CPU {4361CEFA-8238-4247-9CC5-D99DF794843C}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -92,6 +98,7 @@ Global GlobalSection(NestedProjects) = preSolution {6A69770F-4F95-411F-ACAE-2B902EB62161} = {F45DECCA-03D3-4087-AB01-F099C027DC33} {D28B6414-C82C-4BDE-B8BB-A4E3297A0651} = {F45DECCA-03D3-4087-AB01-F099C027DC33} + {F85BDD51-AC29-4D8D-8257-C509BED9A448} = {F45DECCA-03D3-4087-AB01-F099C027DC33} {22BDB23C-24DE-4C3C-9A18-A048C445EDC1} = {B3731232-B2FE-401B-A9F1-5DFB1A90D687} {E524DAF8-3F2C-4EC5-833D-E7D182055A66} = {B3731232-B2FE-401B-A9F1-5DFB1A90D687} {7AE8D7FD-6CEE-4F70-8675-0896AA6487BD} = {B3731232-B2FE-401B-A9F1-5DFB1A90D687} diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/Resources/Strings.resx b/src/libraries/System.Reflection.MetadataLoadContext/src/Resources/Strings.resx index d5a40f23b3169..69a5165b83230 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/Resources/Strings.resx +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/Resources/Strings.resx @@ -1,4 +1,5 @@ - + + $(NoWarn);SYSLIB0005;SYSLIB0037 + + $(DefineConstants);FUNCTIONPOINTER_SUPPORT + - - + + @@ -72,9 +73,15 @@ + + + + + + diff --git a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/SampleMetadata/SampleMetadata.cs b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/SampleMetadata/SampleMetadata.cs index 52075da13b5ff..69bb51454dcbb 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/SampleMetadata/SampleMetadata.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/SampleMetadata/SampleMetadata.cs @@ -558,4 +558,15 @@ public class PublicClass { internal class InternalNestedClass { } } + +#if FUNCTIONPOINTER_SUPPORT + public unsafe class FunctionPointerHolder + { + public delegate* managed Field; + public delegate* managed Prop { get; } + public delegate* managed MethodReturnValue() => default; + public void MethodArg(delegate* managed f) { } + public void MethodArgNative(delegate* unmanaged[Stdcall, SuppressGCTransition] f) { } + } +#endif } diff --git a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/TestUtils/TestUtils.JittedRuntimes.cs b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/TestUtils/TestUtils.JittedRuntimes.cs index cdbd7d4f6ec1f..76c5f3436c8a0 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/TestUtils/TestUtils.JittedRuntimes.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/TestUtils/TestUtils.JittedRuntimes.cs @@ -15,6 +15,11 @@ public static Type Project(this Type type) { if (type == null) return null; +#if FUNCTIONPOINTER_SUPPORT + // Function pointers don't support Type.GetType() so they can't be dynamically created. + if (type.IsFunctionPointer) + throw new NotSupportedException("Function pointers don't support Project()"); +#endif Assembly assembly = type.Assembly; string location = assembly.Location; @@ -75,5 +80,10 @@ public static string GetNameOfCoreAssembly() { return typeof(object).Assembly.GetName().Name; } + + /// + /// Do a type comparison; RO Types compare via .Equals, not ReferenceEquals + /// + public static bool IsEqualOrReferenceEquals(this Type type, Type other) => type.Equals(other); } } diff --git a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Method/MethodTests.cs b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Method/MethodTests.cs index 6f0f25ae95a63..9640e8fcbf750 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Method/MethodTests.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Method/MethodTests.cs @@ -248,7 +248,7 @@ public static unsafe void TestCustomModifiers1() { using (MetadataLoadContext lc = new MetadataLoadContext(new CoreMetadataAssemblyResolver(), "mscorlib")) { - + Assembly a = lc.LoadFromByteArray(TestData.s_CustomModifiersImage); Type t = a.GetType("N", throwOnError: true); Type reqA = a.GetType("ReqA", throwOnError: true); diff --git a/src/libraries/System.Reflection/ref/System.Reflection.Forwards.cs b/src/libraries/System.Reflection/ref/System.Reflection.Forwards.cs index c539b15e1336f..a6bb642c2b8f2 100644 --- a/src/libraries/System.Reflection/ref/System.Reflection.Forwards.cs +++ b/src/libraries/System.Reflection/ref/System.Reflection.Forwards.cs @@ -15,6 +15,7 @@ [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Reflection.CustomAttributeTypedArgument))] [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Reflection.EventInfo))] [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Reflection.FieldInfo))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Reflection.FunctionPointerParameterInfo))] [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Reflection.ICustomAttributeProvider))] [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Reflection.IntrospectionExtensions))] [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Reflection.InvalidFilterCriteriaException))] diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportAnalyzerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportAnalyzerTests.cs index da3ce81fd3a4c..0ca217b9918e4 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportAnalyzerTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportAnalyzerTests.cs @@ -35,7 +35,6 @@ public static IEnumerable NoMarshallingRequiredTypes() => new[] new object[] { typeof(int*) }, new object[] { typeof(bool*) }, new object[] { typeof(char*) }, - new object[] { typeof(delegate* ) }, new object[] { typeof(IntPtr) }, new object[] { typeof(ConsoleKey) }, // enum }; diff --git a/src/libraries/System.Runtime/System.Runtime.sln b/src/libraries/System.Runtime/System.Runtime.sln index ac278789e353e..b5208d44b35e2 100644 --- a/src/libraries/System.Runtime/System.Runtime.sln +++ b/src/libraries/System.Runtime/System.Runtime.sln @@ -35,6 +35,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.Tests", "tes EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestCollectibleAssembly", "tests\TestCollectibleAssembly\TestCollectibleAssembly.csproj", "{C230AC88-A377-4BEB-824F-AB174C14DC86}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestFunctionPointerAssembly", "..\Common\tests\System\TestFunctionPointerAssembly\TestFunctionPointerAssembly.csproj", "{B7975A39-2E87-4C6C-A7EC-1F5926676800}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestLoadAssembly", "tests\TestLoadAssembly\TestLoadAssembly.csproj", "{1BCCD2F5-A561-4641-8A0B-51F3EDCA35DC}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Reflection.TestModule", "tests\TestModule\System.Reflection.TestModule.ilproj", "{0F83B07B-2E3F-4708-BE6D-7A8DA8168803}" @@ -364,6 +366,24 @@ Global {4EE36055-AD7C-4779-B3F6-08687960DCC3}.Checked|x64.Build.0 = Debug|Any CPU {4EE36055-AD7C-4779-B3F6-08687960DCC3}.Checked|x86.ActiveCfg = Debug|Any CPU {4EE36055-AD7C-4779-B3F6-08687960DCC3}.Checked|x86.Build.0 = Debug|Any CPU + {B7975A39-2E87-4C6C-A7EC-1F5926676800}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B7975A39-2E87-4C6C-A7EC-1F5926676800}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B7975A39-2E87-4C6C-A7EC-1F5926676800}.Debug|x64.ActiveCfg = Debug|Any CPU + {B7975A39-2E87-4C6C-A7EC-1F5926676800}.Debug|x64.Build.0 = Debug|Any CPU + {B7975A39-2E87-4C6C-A7EC-1F5926676800}.Debug|x86.ActiveCfg = Debug|Any CPU + {B7975A39-2E87-4C6C-A7EC-1F5926676800}.Debug|x86.Build.0 = Debug|Any CPU + {B7975A39-2E87-4C6C-A7EC-1F5926676800}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B7975A39-2E87-4C6C-A7EC-1F5926676800}.Release|Any CPU.Build.0 = Release|Any CPU + {B7975A39-2E87-4C6C-A7EC-1F5926676800}.Release|x64.ActiveCfg = Release|Any CPU + {B7975A39-2E87-4C6C-A7EC-1F5926676800}.Release|x64.Build.0 = Release|Any CPU + {B7975A39-2E87-4C6C-A7EC-1F5926676800}.Release|x86.ActiveCfg = Release|Any CPU + {B7975A39-2E87-4C6C-A7EC-1F5926676800}.Release|x86.Build.0 = Release|Any CPU + {B7975A39-2E87-4C6C-A7EC-1F5926676800}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {B7975A39-2E87-4C6C-A7EC-1F5926676800}.Checked|Any CPU.Build.0 = Debug|Any CPU + {B7975A39-2E87-4C6C-A7EC-1F5926676800}.Checked|x64.ActiveCfg = Debug|Any CPU + {B7975A39-2E87-4C6C-A7EC-1F5926676800}.Checked|x64.Build.0 = Debug|Any CPU + {B7975A39-2E87-4C6C-A7EC-1F5926676800}.Checked|x86.ActiveCfg = Debug|Any CPU + {B7975A39-2E87-4C6C-A7EC-1F5926676800}.Checked|x86.Build.0 = Debug|Any CPU {C230AC88-A377-4BEB-824F-AB174C14DC86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C230AC88-A377-4BEB-824F-AB174C14DC86}.Debug|Any CPU.Build.0 = Debug|Any CPU {C230AC88-A377-4BEB-824F-AB174C14DC86}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -597,6 +617,7 @@ Global {FB17AC52-1633-4845-932B-9218DF895957} = {FD72C125-C10D-457B-8AFC-6B4E5237AF6A} {3B79DD71-8C2F-41BC-A1A7-86A490D6C726} = {FD72C125-C10D-457B-8AFC-6B4E5237AF6A} {4EE36055-AD7C-4779-B3F6-08687960DCC3} = {FD72C125-C10D-457B-8AFC-6B4E5237AF6A} + {B7975A39-2E87-4C6C-A7EC-1F5926676800} = {FD72C125-C10D-457B-8AFC-6B4E5237AF6A} {C230AC88-A377-4BEB-824F-AB174C14DC86} = {FD72C125-C10D-457B-8AFC-6B4E5237AF6A} {1BCCD2F5-A561-4641-8A0B-51F3EDCA35DC} = {FD72C125-C10D-457B-8AFC-6B4E5237AF6A} {0F83B07B-2E3F-4708-BE6D-7A8DA8168803} = {FD72C125-C10D-457B-8AFC-6B4E5237AF6A} diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 62dd38cc8725b..a877980f29441 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -5748,6 +5748,7 @@ protected Type() { } public bool IsContextful { get { throw null; } } public virtual bool IsEnum { get { throw null; } } public bool IsExplicitLayout { get { throw null; } } + public virtual bool IsFunctionPointer { get { throw null; } } public virtual bool IsGenericMethodParameter { get { throw null; } } public virtual bool IsGenericParameter { get { throw null; } } public virtual bool IsGenericType { get { throw null; } } @@ -5778,6 +5779,7 @@ protected Type() { } public virtual bool IsSZArray { get { throw null; } } public virtual bool IsTypeDefinition { get { throw null; } } public bool IsUnicodeClass { get { throw null; } } + public virtual bool IsUnmanagedFunctionPointer { get { throw null; } } public bool IsValueType { get { throw null; } } public virtual bool IsVariableBoundArray { get { throw null; } } public bool IsVisible { get { throw null; } } @@ -5817,7 +5819,7 @@ protected Type() { } public virtual string? GetEnumName(object value) { throw null; } public virtual string[] GetEnumNames() { throw null; } public virtual System.Type GetEnumUnderlyingType() { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use Enum.GetValues instead.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("It might not be possible to create an array of the enum type at runtime. Use Enum.GetValues instead.")] public virtual System.Array GetEnumValues() { throw null; } [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicEvents)] public System.Reflection.EventInfo? GetEvent(string name) { throw null; } @@ -5835,6 +5837,9 @@ protected Type() { } public System.Reflection.FieldInfo[] GetFields() { throw null; } [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields)] public abstract System.Reflection.FieldInfo[] GetFields(System.Reflection.BindingFlags bindingAttr); + public virtual System.Type[] GetFunctionPointerCallingConventions() { throw null; } + public virtual System.Reflection.FunctionPointerParameterInfo[] GetFunctionPointerParameterInfos() { throw null; } + public virtual System.Reflection.FunctionPointerParameterInfo GetFunctionPointerReturnParameter() { throw null; } public virtual System.Type[] GetGenericArguments() { throw null; } public virtual System.Type[] GetGenericParameterConstraints() { throw null; } public virtual System.Type GetGenericTypeDefinition() { throw null; } @@ -5972,14 +5977,14 @@ protected Type() { } protected abstract bool IsPrimitiveImpl(); public virtual bool IsSubclassOf(System.Type c) { throw null; } protected virtual bool IsValueTypeImpl() { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("The code for an array of the specified type might not be available.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("The code for an array of the specified type might not be available.")] public virtual System.Type MakeArrayType() { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("The code for an array of the specified type might not be available.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("The code for an array of the specified type might not be available.")] public virtual System.Type MakeArrayType(int rank) { throw null; } public virtual System.Type MakeByRefType() { throw null; } public static System.Type MakeGenericMethodParameter(int position) { throw null; } public static System.Type MakeGenericSignatureType(System.Type genericTypeDefinition, params System.Type[] typeArguments) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("The native code for this instantiation might not be available at runtime.")] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("The native code for this instantiation might not be available at runtime.")] [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")] public virtual System.Type MakeGenericType(params System.Type[] typeArguments) { throw null; } public virtual System.Type MakePointerType() { throw null; } @@ -11209,6 +11214,13 @@ public void SetValue(object? obj, object? value) { } [System.CLSCompliantAttribute(false)] public virtual void SetValueDirect(System.TypedReference obj, object value) { } } + public abstract class FunctionPointerParameterInfo + { + public abstract Type ParameterType { get; } + public abstract Type[] GetOptionalCustomModifiers(); + public abstract Type[] GetRequiredCustomModifiers(); + public override string ToString() { throw null; } + } [System.FlagsAttribute] public enum GenericParameterAttributes { @@ -11832,10 +11844,12 @@ public TypeDelegator([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers public override bool IsByRefLike { get { throw null; } } public override bool IsCollectible { get { throw null; } } public override bool IsConstructedGenericType { get { throw null; } } + public override bool IsFunctionPointer { get { throw null; } } public override bool IsGenericMethodParameter { get { throw null; } } public override bool IsGenericTypeParameter { get { throw null; } } public override bool IsSZArray { get { throw null; } } public override bool IsTypeDefinition { get { throw null; } } + public override bool IsUnmanagedFunctionPointer { get { throw null; } } public override bool IsVariableBoundArray { get { throw null; } } public override int MetadataToken { get { throw null; } } public override System.Reflection.Module Module { get { throw null; } } @@ -11861,6 +11875,9 @@ public TypeDelegator([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers public override System.Reflection.FieldInfo? GetField(string name, System.Reflection.BindingFlags bindingAttr) { throw null; } [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields)] public override System.Reflection.FieldInfo[] GetFields(System.Reflection.BindingFlags bindingAttr) { throw null; } + public override System.Type[] GetFunctionPointerCallingConventions() { throw null; } + public override System.Reflection.FunctionPointerParameterInfo[] GetFunctionPointerParameterInfos() { throw null; } + public override System.Reflection.FunctionPointerParameterInfo GetFunctionPointerReturnParameter() { throw null; } [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] [return: System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] public override System.Type? GetInterface(string name, bool ignoreCase) { throw null; } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj index 036977e8d25f8..384ea8f6cafd6 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj @@ -22,6 +22,7 @@ + @@ -142,6 +143,7 @@ + @@ -321,6 +323,7 @@ + diff --git a/src/libraries/System.Runtime/tests/System/Reflection/TypeDelegatorTests.cs b/src/libraries/System.Runtime/tests/System/Reflection/TypeDelegatorTests.cs index ca6dccd6a8c9b..9856193cc665c 100644 --- a/src/libraries/System.Runtime/tests/System/Reflection/TypeDelegatorTests.cs +++ b/src/libraries/System.Runtime/tests/System/Reflection/TypeDelegatorTests.cs @@ -43,15 +43,30 @@ public void Properties() Assert.False(new TypeDelegator(typeof(IComparable)).IsValueType); Assert.False(new TypeDelegator(typeof(IComparable)).IsEnum); Assert.True(new TypeDelegator(typeof(IComparable)).IsInterface); + Assert.False(new TypeDelegator(typeof(IComparable)).IsFunctionPointer); Assert.True(new TypeDelegator(typeof(TypeDelegatorTests)).IsClass); Assert.False(new TypeDelegator(typeof(TypeDelegatorTests)).IsValueType); Assert.False(new TypeDelegator(typeof(TypeDelegatorTests)).IsInterface); + Assert.False(new TypeDelegator(typeof(IComparable)).IsFunctionPointer); Assert.False(new TypeDelegator(typeof(TypeCode)).IsClass); Assert.False(new TypeDelegator(typeof(TypeCode)).IsInterface); Assert.True(new TypeDelegator(typeof(TypeCode)).IsValueType); Assert.True(new TypeDelegator(typeof(TypeCode)).IsEnum); + Assert.False(new TypeDelegator(typeof(IComparable)).IsFunctionPointer); + } + + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)] + public void FunctionPointers() + { + Assert.True(new TypeDelegator(typeof(delegate* unmanaged)).IsClass); + Assert.True(new TypeDelegator(typeof(delegate* unmanaged)).IsFunctionPointer); + Assert.True(new TypeDelegator(typeof(delegate* unmanaged)).IsUnmanagedFunctionPointer); + Assert.NotNull(new TypeDelegator(typeof(delegate* unmanaged)).GetFunctionPointerCallingConventions()); + Assert.NotNull(new TypeDelegator(typeof(delegate* unmanaged)).GetFunctionPointerParameterInfos()); + Assert.NotNull(new TypeDelegator(typeof(delegate* unmanaged)).GetFunctionPointerReturnParameter()); } public static IEnumerable SZArrayOrNotTypes() diff --git a/src/libraries/System.Runtime/tests/System/Type/FunctionPointerTestExtensions.cs b/src/libraries/System.Runtime/tests/System/Type/FunctionPointerTestExtensions.cs new file mode 100644 index 0000000000000..ac3cf2562ab1b --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/Type/FunctionPointerTestExtensions.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Tests.Types +{ + internal static class FunctionPointerTestExtensions + { + /// + /// A noop to allow tests to be shared with MetadataLoadContext. + /// + public static Type Project(this Type type) => type; + + /// + /// Check for type equality; runtime Types compare via Equals and ReferenceEquals + /// + public static bool IsEqualOrReferenceEquals(this Type type, Type other) => type.Equals(other) && ReferenceEquals(type, other); + } +} diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index 4945fcc682af3..a0e1f008b825f 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -210,6 +210,7 @@ + @@ -217,6 +218,7 @@ + diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.Mono.cs new file mode 100644 index 0000000000000..5d30777f17d02 --- /dev/null +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.Mono.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +namespace System.Reflection +{ + internal partial class FunctionPointerInfo + { + internal FunctionPointerInfo() + { + _returnInfo = default!; + _parameterInfos = default!; + } + + internal List GetOptionalCustomModifiersList() => throw new NotSupportedException(); + private unsafe MdSigCallingConvention CallingConvention => throw new NotSupportedException(); + } +} diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFunctionPointerParameterInfo.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFunctionPointerParameterInfo.Mono.cs new file mode 100644 index 0000000000000..fb842e62f9229 --- /dev/null +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFunctionPointerParameterInfo.Mono.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +namespace System.Reflection +{ + internal partial class RuntimeFunctionPointerParameterInfo + { + public RuntimeFunctionPointerParameterInfo() + { + _parameterType = default!; + } + + public override Type[] GetOptionalCustomModifiers() => throw new NotSupportedException(); + public override Type[] GetRequiredCustomModifiers() => throw new NotSupportedException(); + internal List GetOptionalCustomModifiersList() => throw new NotSupportedException(); + } +} diff --git a/src/tests/reflection/ldtoken/modifiers.il b/src/tests/reflection/ldtoken/modifiers.il index 5b0ea00e458a0..9a472c9b2efdb 100644 --- a/src/tests/reflection/ldtoken/modifiers.il +++ b/src/tests/reflection/ldtoken/modifiers.il @@ -6,8 +6,8 @@ .method private hidebysig static int32 Main() cil managed { - .locals init (valuetype [System.Runtime]System.RuntimeTypeHandle) .entrypoint + .locals init (valuetype [System.Runtime]System.RuntimeTypeHandle) ldtoken string[] stloc.0 @@ -59,6 +59,40 @@ UIntArrayModifiedUIntArrayOK: ret MyStructPtrModifiedMyStructPtrOK: + ldtoken method int32 *() + stloc.0 + ldloca 0 + ldtoken method int32 modopt(MyModifier) *() + + call instance bool valuetype [System.Runtime]System.RuntimeTypeHandle::Equals(valuetype [System.Runtime]System.RuntimeTypeHandle) + // All modifiers are considered as part of identity. + brfalse IntReturnManagedFunctionPointerModifiedOK + ldc.i4.6 + ret +IntReturnManagedFunctionPointerModifiedOK: + + ldtoken method unmanaged cdecl int32 modopt(MyModifier) *() + stloc.0 + ldloca 0 + ldtoken method unmanaged cdecl int32 modopt(MyModifier) *() + + call instance bool valuetype [System.Runtime]System.RuntimeTypeHandle::Equals(valuetype [System.Runtime]System.RuntimeTypeHandle) + // CallKind and modifiers are the same + brtrue IntReturnUnmanagedFunctionPointerModifiedOK + ldc.i4.7 + ret +IntReturnUnmanagedFunctionPointerModifiedOK: + + ldtoken method unmanaged cdecl int32 modopt([System.Runtime]System.Runtime.CompilerServices.CallConvStdcall) *() + stloc.0 + ldloca 0 + ldtoken method unmanaged cdecl int32 modopt([System.Runtime]System.Runtime.CompilerServices.CallConvSuppressGCTransition) modopt([System.Runtime]System.Runtime.CompilerServices.CallConvStdcall) *() + call instance bool valuetype [System.Runtime]System.RuntimeTypeHandle::Equals(valuetype [System.Runtime]System.RuntimeTypeHandle) + brfalse IntReturnUnmanagedFunctionPointerWithCallConvModifiedOK + ldc.i4.8 + ret + +IntReturnUnmanagedFunctionPointerWithCallConvModifiedOK: ldc.i4 100 ret } From 7d9dc856e103fe1654c6c67be0bd95ff7b754245 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Fri, 8 Jul 2022 18:34:42 -0500 Subject: [PATCH 2/9] Refactor to remove copy of sig on FP; avoids contract issues --- .../System.Private.CoreLib.csproj | 5 +- .../Reflection/FunctionPointerInfo.CoreCLR.cs | 26 +-- ...imeFunctionPointerParameterInfo.CoreCLR.cs | 48 ++-- .../src/System/RuntimeHandles.cs | 28 +-- .../src/System/RuntimeType.CoreCLR.cs | 21 +- src/coreclr/debug/daccess/dacdbiimpl.cpp | 7 +- src/coreclr/vm/clsload.cpp | 35 +-- src/coreclr/vm/clsload.hpp | 8 +- src/coreclr/vm/ecalllist.h | 6 +- src/coreclr/vm/eedbginterfaceimpl.cpp | 18 +- src/coreclr/vm/runtimehandles.cpp | 206 ++++++++++++------ src/coreclr/vm/runtimehandles.h | 9 +- src/coreclr/vm/sigformat.cpp | 3 +- src/coreclr/vm/siginfo.cpp | 94 +++++++- src/coreclr/vm/typedesc.cpp | 7 +- src/coreclr/vm/typedesc.h | 61 +++--- src/coreclr/vm/typehandle.cpp | 8 +- src/coreclr/vm/typehash.cpp | 55 ++--- src/coreclr/vm/typehash.h | 2 +- src/coreclr/vm/typekey.h | 65 +++--- .../tests/System/FunctionPointerTests.cs | 8 +- .../System/Reflection/FunctionPointerInfo.cs | 54 +++-- .../src/System/Reflection/TypeDelegator.cs | 2 +- .../src/SampleMetadata/SampleMetadata.cs | 13 +- .../tests/src/Tests/Method/MethodTests.cs | 1 - .../tests/MethodInfoTests.cs | 56 ++++- .../System.Private.CoreLib.csproj | 5 +- .../Reflection/FunctionPointerInfo.Mono.cs | 5 +- ...untimeFunctionPointerParameterInfo.Mono.cs | 2 +- 29 files changed, 541 insertions(+), 317 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 4cc10049c3d4a..d275df663ece2 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -1,4 +1,4 @@ - + false @@ -264,6 +264,9 @@ Common\System\Runtime\InteropServices\Variant.cs + + Common\System\Collections\Generic\ArrayBuilder.cs + diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.CoreCLR.cs index 761d15951f982..77942e052c495 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.CoreCLR.cs @@ -7,34 +7,34 @@ namespace System.Reflection { internal partial class FunctionPointerInfo { - private readonly Signature _signature; - private readonly Type _type; + private readonly RuntimeType _type; - internal FunctionPointerInfo(Type type, Signature signature) + internal FunctionPointerInfo(RuntimeType type) { - Debug.Assert(signature.m_csig > 0); - _type = type; - _signature = signature; - _returnInfo = new RuntimeFunctionPointerParameterInfo(signature.ReturnType.AsType(), -1, signature); + Type[] arguments = RuntimeTypeHandle.GetArgumentTypesFromFunctionPointer(type); + Debug.Assert(arguments.Length >= 1); - RuntimeType[] arguments = signature.Arguments; + _returnInfo = new RuntimeFunctionPointerParameterInfo(this, arguments[0], 0); int count = arguments.Length; - if (count == 0) + if (count == 1) { _parameterInfos = Array.Empty(); } else { - RuntimeFunctionPointerParameterInfo[] parameterInfos = new RuntimeFunctionPointerParameterInfo[count]; - for (int i = 0; i < count; i++) + RuntimeFunctionPointerParameterInfo[] parameterInfos = new RuntimeFunctionPointerParameterInfo[count - 1]; + for (int i = 0; i < count - 1; i++) { - parameterInfos[i] = new RuntimeFunctionPointerParameterInfo(arguments[i].AsType(), i - 1, signature); + parameterInfos[i] = new RuntimeFunctionPointerParameterInfo(this, arguments[i + 1], i + 1); } _parameterInfos = parameterInfos; } } - private unsafe MdSigCallingConvention CallingConvention => (MdSigCallingConvention)((byte*)_signature.m_sig)[0] & MdSigCallingConvention.CallConvMask; + internal RuntimeType FunctionPointerType => _type; + internal unsafe MdSigCallingConvention CallingConvention => (MdSigCallingConvention)RuntimeTypeHandle.GetRawCallingConventionsFromFunctionPointer(_type); + internal Type[]? GetCustomModifiersFromFunctionPointer(int position, bool required) => + RuntimeTypeHandle.GetCustomModifiersFromFunctionPointer(_type, 0, required: false); } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeFunctionPointerParameterInfo.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeFunctionPointerParameterInfo.CoreCLR.cs index 6d9f1c5219d40..f693b9864f9fd 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeFunctionPointerParameterInfo.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeFunctionPointerParameterInfo.CoreCLR.cs @@ -1,37 +1,57 @@ // 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; namespace System.Reflection { internal partial class RuntimeFunctionPointerParameterInfo { + private readonly FunctionPointerInfo _functionPointer; private readonly int _position; - private readonly Signature _signature; - private List? _optionalModifiers; + private Type[]? _optionalModifiers; - public RuntimeFunctionPointerParameterInfo(Type parameterType, int position, Signature signature) + public RuntimeFunctionPointerParameterInfo(FunctionPointerInfo functionPointer, Type parameterType, int position) { + _functionPointer = functionPointer; _parameterType = parameterType; - _position = position; - _signature = signature; + _position = position; // 0 = return value; 1 = first parameter } public override Type[] GetOptionalCustomModifiers() { - return _optionalModifiers == null ? - _signature.GetCustomModifiers(_position + 1, required: false) : - _optionalModifiers.ToArray(); + if (_optionalModifiers == null) + { + if (_position == 0) + { + // Return value should be normalized. This will call SetCustomModifiersForReturnType. + _functionPointer.ComputeCallingConventions(); + Debug.Assert(_optionalModifiers != null); + } + else + { + Type[]? mods = RuntimeTypeHandle.GetCustomModifiersFromFunctionPointer(_functionPointer.FunctionPointerType, _position, required: false); + if (mods == null) + { + _optionalModifiers = Type.EmptyTypes; + } + } + } + + Debug.Assert(_optionalModifiers != null); + return _optionalModifiers; } - // Expose the List in order to add calling conventions later. - internal List GetOptionalCustomModifiersList() + internal void SetCustomModifiersForReturnType(Type[] modifiers) { - _optionalModifiers ??= new List(_signature.GetCustomModifiers(_position + 1, required: false)); - return _optionalModifiers; + Debug.Assert(_position == 0); + _optionalModifiers = modifiers; } - public override Type[] GetRequiredCustomModifiers() => _signature.GetCustomModifiers(_position + 1, required: true); + public override Type[] GetRequiredCustomModifiers() + { + Type[]? value = RuntimeTypeHandle.GetCustomModifiersFromFunctionPointer(_functionPointer.FunctionPointerType, _position, required: true); + return value ??= Type.EmptyTypes; + } } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs index cbf13f486b2ec..d21ca01554f19 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -188,12 +188,6 @@ internal static bool IsFunctionPointer(RuntimeType type) return corElemType == CorElementType.ELEMENT_TYPE_FNPTR; } - internal static bool IsUnmanagedFunctionPointer(RuntimeType type) - { - // Fast native path that does not need to create FunctionPointerInfo and parse the Signature. - return _IsUnmanagedFunctionPointer(type); - } - internal static bool HasElementType(RuntimeType type) { CorElementType corElemType = GetCorElementType(type); @@ -395,6 +389,15 @@ public ModuleHandle GetModuleHandle() [MethodImpl(MethodImplOptions.InternalCall)] internal static extern RuntimeMethodHandleInternal GetMethodAt(RuntimeType type, int slot); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern Type[]? GetCustomModifiersFromFunctionPointer(RuntimeType functionPointerType, int position, bool required); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern byte GetRawCallingConventionsFromFunctionPointer(RuntimeType type); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern Type[] GetArgumentTypesFromFunctionPointer(RuntimeType type); + // This is managed wrapper for MethodTable::IntroducedMethodIterator internal struct IntroducedMethodEnumerator { @@ -534,9 +537,6 @@ internal static bool IsVisible(RuntimeType type) [MethodImpl(MethodImplOptions.InternalCall)] internal static extern bool IsValueType(RuntimeType type); - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool _IsUnmanagedFunctionPointer(RuntimeType type); - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeTypeHandle_ConstructName")] private static partial void ConstructName(QCallTypeHandle handle, TypeNameFormatFlags formatFlags, StringHandleOnStack retString); @@ -1666,16 +1666,6 @@ public Signature(IRuntimeFieldInfo fieldHandle, RuntimeType declaringType) GC.KeepAlive(fieldHandle); } - [MethodImpl(MethodImplOptions.InternalCall)] - private extern void GetSignatureFromFunctionPointer(Type functionPointerType); - - public Signature(RuntimeType functionPointerType) - { - m_arguments = default!; - m_returnTypeORfieldType = default!; - GetSignatureFromFunctionPointer(functionPointerType); - } - public Signature(void* pCorSig, int cCorSig, RuntimeType declaringType) { GetSignature(pCorSig, cCorSig, default, null, declaringType); 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 232cf55750c83..50e417a437bad 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -3789,7 +3789,24 @@ internal static CorElementType GetUnderlyingType(RuntimeType type) #region Function Pointer public override bool IsFunctionPointer => RuntimeTypeHandle.IsFunctionPointer(this); - public override bool IsUnmanagedFunctionPointer => RuntimeTypeHandle.IsUnmanagedFunctionPointer(this); + public override bool IsUnmanagedFunctionPointer + { + get + { + switch (GetFunctionPointerInfo().CallingConvention) + { + case MdSigCallingConvention.C: + case MdSigCallingConvention.StdCall: + case MdSigCallingConvention.ThisCall: + case MdSigCallingConvention.FastCall: + case MdSigCallingConvention.Unmanaged: + return true; + default: + return false; + } + } + } + public override FunctionPointerParameterInfo[] GetFunctionPointerParameterInfos() => GetFunctionPointerInfo().ParameterInfos; public override FunctionPointerParameterInfo GetFunctionPointerReturnParameter() => GetFunctionPointerInfo().ReturnParameter; public override Type[] GetFunctionPointerCallingConventions() => GetFunctionPointerInfo().GetCallingConventions(); @@ -3805,7 +3822,7 @@ internal FunctionPointerInfo GetFunctionPointerInfo() throw new InvalidOperationException(SR.InvalidOperation_NotFunctionPointer); } - fnPtr = new FunctionPointerInfo(this, new Signature(this)); + fnPtr = new FunctionPointerInfo(this); Cache.FunctionPointerInfo = fnPtr; } diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 75e995b6c08de..00137a8c753c3 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -2370,12 +2370,11 @@ TypeHandle DacDbiInterfaceImpl::FindLoadedFnptrType(DWORD numTypeArgs, TypeHandl // @dbgtodo : Do we need to worry about calling convention here? // LoadFnptrTypeThrowing expects the count of arguments, not // including return value, so we subtract 1 from numTypeArgs. - return ClassLoader::LoadFnptrTypeThrowing(NULL, /*module*/ - NULL, /*sig*/ - 0, /*sigLen*/ - 0, /*callConv*/ + return ClassLoader::LoadFnptrTypeThrowing(0, // callConv numTypeArgs - 1, pInst, + 0, // numMods + NULL, // customModTypes ClassLoader::DontLoadTypes); } // DacDbiInterfaceImpl::FindLoadedFnptrType diff --git a/src/coreclr/vm/clsload.cpp b/src/coreclr/vm/clsload.cpp index 110fc790d8a9d..6ea9d66c4333d 100644 --- a/src/coreclr/vm/clsload.cpp +++ b/src/coreclr/vm/clsload.cpp @@ -1615,12 +1615,11 @@ TypeHandle ClassLoader::LoadNativeValueTypeThrowing(TypeHandle baseType, } /* static */ -TypeHandle ClassLoader::LoadFnptrTypeThrowing(Module *pModule, - PCOR_SIGNATURE sig, - uint32_t sigLen, - BYTE callConv, +TypeHandle ClassLoader::LoadFnptrTypeThrowing(BYTE callConv, DWORD ntypars, TypeHandle* inst, + DWORD numMods, + FnPtrTypeDescCustomMod* customModTypes, LoadTypesFlag fLoadTypes/*=LoadTypes*/, ClassLoadLevel level/*=CLASS_LOADED*/) { @@ -1637,7 +1636,7 @@ TypeHandle ClassLoader::LoadFnptrTypeThrowing(Module *pModule, } CONTRACT_END - TypeKey key(pModule, sig, sigLen, callConv, ntypars, inst); + TypeKey key(callConv, ntypars, inst, numMods, customModTypes); RETURN(LoadConstructedTypeThrowing(&key, fLoadTypes, level)); } @@ -2961,22 +2960,32 @@ TypeHandle ClassLoader::CreateTypeHandleForTypeKey(TypeKey* pKey, AllocMemTracke PREFIX_ASSUME(pLoaderModule != NULL); PTR_LoaderHeap loaderHeap = pLoaderModule->GetAssembly()->GetLowFrequencyHeap(); - int32_t sigLen = pKey->GetSignatureLen(); - PCOR_SIGNATURE pCopyOfSig = (PCOR_SIGNATURE)pamTracker->Track(loaderHeap->AllocMem(S_SIZE_T(sigLen))); - memcpy(pCopyOfSig, pKey->GetSignature(), sigLen); - DWORD numArgs = pKey->GetNumArgs(); BYTE* mem = (BYTE*) pamTracker->Track(loaderHeap->AllocMem( S_SIZE_T(sizeof(FnPtrTypeDesc)) + S_SIZE_T(sizeof(TypeHandle)) * S_SIZE_T(numArgs))); + DWORD numMods = pKey->GetNumMods(); + FnPtrTypeDescCustomMod* modTypes = NULL; + if (numMods) + { + FnPtrTypeDescCustomMod* srcMods = pKey->GetCustomModTypes(); + + modTypes = (FnPtrTypeDescCustomMod*) pamTracker->Track(loaderHeap->AllocMem( + S_SIZE_T(sizeof(FnPtrTypeDescCustomMod)) * S_SIZE_T(numMods))); + + for (DWORD i = 0; i <= numMods; i++) + { + modTypes[i] = srcMods[i]; + } + } + typeHnd = TypeHandle(new(mem) FnPtrTypeDesc( - pKey->GetModule(), - pCopyOfSig, - sigLen, pKey->GetCallConv(), numArgs, - pKey->GetRetAndArgTypes())); + pKey->GetRetAndArgTypes(), + numMods, + modTypes)); } else { diff --git a/src/coreclr/vm/clsload.hpp b/src/coreclr/vm/clsload.hpp index c046b0f036909..82cc323a5db52 100644 --- a/src/coreclr/vm/clsload.hpp +++ b/src/coreclr/vm/clsload.hpp @@ -37,6 +37,7 @@ class Thread; class EETypeHashTable; class DynamicResolver; class SigPointer; +struct FnPtrTypeDescCustomMod; // Hash table parameter for unresolved class hash #define UNRESOLVED_CLASS_HASH_BUCKETS 8 @@ -693,12 +694,11 @@ class ClassLoader LoadTypesFlag fLoadTypes = LoadTypes, ClassLoadLevel level = CLASS_LOADED); - static TypeHandle LoadFnptrTypeThrowing(Module *pModule, - PCOR_SIGNATURE sig, - uint32_t sigLen, - BYTE callConv, + static TypeHandle LoadFnptrTypeThrowing(BYTE callConv, DWORD numArgs, TypeHandle* retAndArgTypes, + DWORD numMods, + FnPtrTypeDescCustomMod* customModTypes, LoadTypesFlag fLoadTypes = LoadTypes, ClassLoadLevel level = CLASS_LOADED); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 0030a8bf98b02..b210f5404f6f3 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -161,7 +161,6 @@ FCFuncStart(gCOMTypeHandleFuncs) FCFuncElement("IsValueType", RuntimeTypeHandle::IsValueType) FCFuncElement("IsInterface", RuntimeTypeHandle::IsInterface) FCFuncElement("IsByRefLike", RuntimeTypeHandle::IsByRefLike) - FCFuncElement("_IsUnmanagedFunctionPointer", RuntimeTypeHandle::IsUnmanagedFunctionPointer) FCFuncElement("CanCastTo", RuntimeTypeHandle::CanCastTo) FCFuncElement("HasInstantiation", RuntimeTypeHandle::HasInstantiation) FCFuncElement("GetGenericVariableIndex", RuntimeTypeHandle::GetGenericVariableIndex) @@ -169,6 +168,10 @@ FCFuncStart(gCOMTypeHandleFuncs) FCFuncElement("IsGenericTypeDefinition", RuntimeTypeHandle::IsGenericTypeDefinition) FCFuncElement("ContainsGenericVariables", RuntimeTypeHandle::ContainsGenericVariables) FCFuncElement("SatisfiesConstraints", RuntimeTypeHandle::SatisfiesConstraints) + FCFuncElement("GetCustomModifiersFromFunctionPointer", RuntimeTypeHandle::GetCustomModifiersFromFunctionPointer) + FCFuncElement("GetRawCallingConventionsFromFunctionPointer", RuntimeTypeHandle::GetRawCallingConventionsFromFunctionPointer) + FCFuncElement("GetArgumentTypesFromFunctionPointer", RuntimeTypeHandle::GetArgumentTypesFromFunctionPointer) + #ifdef FEATURE_COMINTEROP FCFuncElement("AllocateComObject", RuntimeTypeHandle::AllocateComObject) #endif // FEATURE_COMINTEROP @@ -207,7 +210,6 @@ FCFuncEnd() FCFuncStart(gSignatureNative) FCFuncElement("GetSignature", SignatureNative::GetSignature) - FCFuncElement("GetSignatureFromFunctionPointer", SignatureNative::GetSignatureFromFunctionPointer) FCFuncElement("GetCustomModifiers", SignatureNative::GetCustomModifiers) FCFuncElement("CompareSig", SignatureNative::CompareSig) FCFuncEnd() diff --git a/src/coreclr/vm/eedbginterfaceimpl.cpp b/src/coreclr/vm/eedbginterfaceimpl.cpp index 1f4abe1d68e1d..1e695784fde0b 100644 --- a/src/coreclr/vm/eedbginterfaceimpl.cpp +++ b/src/coreclr/vm/eedbginterfaceimpl.cpp @@ -884,12 +884,12 @@ TypeHandle EEDbgInterfaceImpl::FindLoadedFnptrType(TypeHandle *inst, ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); // : CALLCONV? - return ClassLoader::LoadFnptrTypeThrowing(NULL, /*module*/ - NULL, /*sig*/ - 0, /*sigLen*/ - 0, /*callConv*/ + return ClassLoader::LoadFnptrTypeThrowing(0, // callConv ntypars, inst, + 0, // numMods + NULL, // customModTypes + // should this be FailIfNotLoaded? - NO - although we may // want to debug unrestored VCs, we can't do it because the debug API // is not set up to handle them @@ -1014,12 +1014,12 @@ TypeHandle EEDbgInterfaceImpl::LoadFnptrType(TypeHandle *inst, CONTRACTL_END; /* @TODO : CALLCONV? */ - return ClassLoader::LoadFnptrTypeThrowing(NULL, /*module*/ - NULL, /*sig*/ - 0, /*sigLen*/ - 0, /*callConv*/ + return ClassLoader::LoadFnptrTypeThrowing(0, // callConv ntypars, - inst); + inst, + 0, // numMods + NULL // customModTypes + ); } TypeHandle EEDbgInterfaceImpl::LoadElementType(CorElementType et) diff --git a/src/coreclr/vm/runtimehandles.cpp b/src/coreclr/vm/runtimehandles.cpp index d3d485bf46a94..f59b30a91140d 100644 --- a/src/coreclr/vm/runtimehandles.cpp +++ b/src/coreclr/vm/runtimehandles.cpp @@ -361,7 +361,6 @@ FCIMPL1(AssemblyBaseObject*, RuntimeTypeHandle::GetAssembly, ReflectClassBaseObj } FCIMPLEND - FCIMPL1(FC_BOOL_RET, RuntimeFieldHandle::AcquiresContextFromThis, FieldDesc* pField) { CONTRACTL { @@ -899,7 +898,8 @@ FCIMPL1(FC_BOOL_RET, RuntimeTypeHandle::IsByRefLike, ReflectClassBaseObject *pTy } FCIMPLEND -FCIMPL1(FC_BOOL_RET, RuntimeTypeHandle::IsUnmanagedFunctionPointer, ReflectClassBaseObject *pTypeUNSAFE) +FCIMPL3(Object *, RuntimeTypeHandle::GetCustomModifiersFromFunctionPointer, ReflectClassBaseObject* pTypeUNSAFE, + INT32 position, CLR_BOOL fRequired) { CONTRACTL { FCALL_CHECK; @@ -907,30 +907,152 @@ FCIMPL1(FC_BOOL_RET, RuntimeTypeHandle::IsUnmanagedFunctionPointer, ReflectClass } CONTRACTL_END; - REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); - _ASSERTE(refType != NULL); + struct + { + PTRARRAYREF retVal; + } gc; + + gc.retVal = NULL; + REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); TypeHandle typeHandle = refType->GetType(); + if (!typeHandle.IsFnPtrType()) - FC_RETURN_BOOL(FALSE); + FCThrowRes(kArgumentException, W("Arg_InvalidHandle")); FnPtrTypeDesc* fnPtr = typeHandle.AsFnPtrType(); - if (fnPtr == NULL) + + CorElementType cmodTypeExpected = fRequired ? ELEMENT_TYPE_CMOD_REQD : ELEMENT_TYPE_CMOD_OPT; + + // Discover the number of required and optional custom modifiers. + INT32 currentPos = 0; + INT32 cMods = 0; + DWORD numMods = fnPtr->GetNumMods(); + FnPtrTypeDescCustomMod* mods = fnPtr->GetCustomModTypesPointer(); + + for (DWORD i = 0; i < numMods; i++) + { + CorElementType cmodType = mods[i].elementType; + + if (currentPos == position) + { + if (cmodType == cmodTypeExpected) + { + cMods++; + } + } + + if (cmodType == ELEMENT_TYPE_END) + { + currentPos++; + if (currentPos > position) + { + break; + } + } + } + + if (cMods == 0) + return NULL; + + HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); + { + MethodTable *pMT = CoreLibBinder::GetClass(CLASS__TYPE); + TypeHandle arrayHandle = ClassLoader::LoadArrayTypeThrowing(TypeHandle(pMT), ELEMENT_TYPE_SZARRAY); + gc.retVal = (PTRARRAYREF) AllocateSzArray(arrayHandle, cMods); + + currentPos = 0; + for (DWORD i = 0; i < numMods; i++) + { + CorElementType cmodType = mods[i].elementType; + + if (currentPos == position) + { + if (cmodType == cmodTypeExpected) + { + TypeHandle typeHandle = mods[i].typeHandle; + OBJECTREF refType = typeHandle.GetManagedClassObject(); + gc.retVal->SetAt(--cMods, refType); + } + } + + if (cmodType == ELEMENT_TYPE_END) + { + currentPos++; + if (currentPos > position) + { + break; + } + } + } + } + HELPER_METHOD_FRAME_END(); + + return OBJECTREFToObject(gc.retVal); +} +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")); - switch ((CorCallingConvention)(fnPtr->GetCallConv() & IMAGE_CEE_CS_CALLCONV_MASK)) + FnPtrTypeDesc* fnPtr = typeHandle.AsFnPtrType(); + + HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); { - case IMAGE_CEE_CS_CALLCONV_C: - case IMAGE_CEE_CS_CALLCONV_STDCALL: - case IMAGE_CEE_CS_CALLCONV_THISCALL: - case IMAGE_CEE_CS_CALLCONV_FASTCALL: - case IMAGE_CEE_CS_CALLCONV_UNMANAGED: - FC_RETURN_BOOL(TRUE); - default: - FC_RETURN_BOOL(FALSE); + 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; +FCIMPLEND + +FCIMPL1(FC_INT8_RET, RuntimeTypeHandle::GetRawCallingConventionsFromFunctionPointer, ReflectClassBaseObject *pTypeUNSAFE) +{ + CONTRACTL { + FCALL_CHECK; + PRECONDITION(CheckPointer(pTypeUNSAFE)); + } + CONTRACTL_END; + + REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); + TypeHandle typeHandle = refType->GetType(); + + if (!typeHandle.IsFnPtrType()) + FCThrowRes(kArgumentException, W("Arg_InvalidHandle")); + + FnPtrTypeDesc* fnPtr = typeHandle.AsFnPtrType(); + return FC_INT8_RET(fnPtr->GetCallConv() & IMAGE_CEE_CS_CALLCONV_MASK); +} +FCIMPLEND extern "C" BOOL QCALLTYPE RuntimeTypeHandle_IsVisible(QCall::TypeHandle pTypeHandle) { @@ -2096,58 +2218,6 @@ FCIMPL6(void, SignatureNative::GetSignature, } FCIMPLEND -FCIMPL2(void, SignatureNative::GetSignatureFromFunctionPointer, - SignatureNative* pSignatureNativeUNSAFE, - ReflectClassBaseObject *pTypeUNSAFE) { - CONTRACTL { - FCALL_CHECK; - PRECONDITION(CheckPointer(pTypeUNSAFE)); - } - CONTRACTL_END; - - struct - { - REFLECTCLASSBASEREF refType; - SIGNATURENATIVEREF pSig; - } gc; - - gc.refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); - gc.pSig = (SIGNATURENATIVEREF)pSignatureNativeUNSAFE; - - TypeHandle typeHandle = gc.refType->GetType(); - if (!typeHandle.IsFnPtrType()) - FCThrowResVoid(kArgumentException, W("Arg_InvalidHandle")); - - FnPtrTypeDesc* fnPtr = typeHandle.AsFnPtrType(); - - HELPER_METHOD_FRAME_BEGIN_PROTECT(gc); - { - gc.pSig->m_sig = fnPtr->GetSignature(); - gc.pSig->m_cSig = fnPtr->GetSignatureLen(); - gc.pSig->SetCallingConvention(fnPtr->GetCallConv()); - - REFLECTCLASSBASEREF refDeclType = (REFLECTCLASSBASEREF)fnPtr->GetManagedClassObject(); - gc.pSig->SetDeclaringType(refDeclType); - - INT32 numArgs = fnPtr->GetNumArgs(); - TypeHandle arrayHandle = ClassLoader::LoadArrayTypeThrowing(TypeHandle(g_pRuntimeTypeClass), ELEMENT_TYPE_SZARRAY); - PTRARRAYREF ptrArrayarguments = (PTRARRAYREF) AllocateSzArray(arrayHandle, numArgs); - gc.pSig->SetArgumentArray(ptrArrayarguments); - - TypeHandle *retAndArgTypes = fnPtr->GetRetAndArgTypesPointer(); - OBJECTREF refRetType = retAndArgTypes[0].GetManagedClassObject(); - gc.pSig->SetReturnType(refRetType); - - for (INT32 i = 0; i < numArgs; i++) - { - OBJECTREF refArgType = retAndArgTypes[i + 1].GetManagedClassObject(); - gc.pSig->SetArgument(i, refArgType); - } - } - HELPER_METHOD_FRAME_END(); -} -FCIMPLEND - FCIMPL2(FC_BOOL_RET, SignatureNative::CompareSig, SignatureNative* pLhsUNSAFE, SignatureNative* pRhsUNSAFE) { FCALL_CONTRACT; diff --git a/src/coreclr/vm/runtimehandles.h b/src/coreclr/vm/runtimehandles.h index ba0c1a1b22c58..1a459384b6612 100644 --- a/src/coreclr/vm/runtimehandles.h +++ b/src/coreclr/vm/runtimehandles.h @@ -140,7 +140,10 @@ class RuntimeTypeHandle { static FCDECL1(FC_BOOL_RET, IsValueType, ReflectClassBaseObject* pType); static FCDECL1(FC_BOOL_RET, IsInterface, ReflectClassBaseObject* pType); static FCDECL1(FC_BOOL_RET, IsByRefLike, ReflectClassBaseObject* pType); - static FCDECL1(FC_BOOL_RET, IsUnmanagedFunctionPointer, ReflectClassBaseObject* pType); + + static FCDECL3(Object *, GetCustomModifiersFromFunctionPointer, ReflectClassBaseObject* pTypeUNSAFE, INT32 position, CLR_BOOL fRequired); + static FCDECL1(Object *, GetArgumentTypesFromFunctionPointer, ReflectClassBaseObject *pTypeUNSAFE); + static FCDECL1(FC_INT8_RET, GetRawCallingConventionsFromFunctionPointer, ReflectClassBaseObject *pTypeUNSAFE); static FCDECL2(FC_BOOL_RET, CanCastTo, ReflectClassBaseObject *pType, ReflectClassBaseObject *pTarget); static FCDECL2(FC_BOOL_RET, IsInstanceOfType, ReflectClassBaseObject *pType, Object *object); @@ -384,10 +387,6 @@ class SignatureNative : public Object FieldDesc *pFieldDesc, ReflectMethodObject *pMethodUNSAFE, ReflectClassBaseObject *pDeclaringType); - static FCDECL2(void, GetSignatureFromFunctionPointer, - SignatureNative* pSignatureNativeUNSAFE, - ReflectClassBaseObject* pTypeUNSAFE); - static FCDECL3(Object *, GetCustomModifiers, SignatureNative* pSig, INT32 parameter, CLR_BOOL fRequired); static FCDECL2(FC_BOOL_RET, CompareSig, SignatureNative* pLhs, SignatureNative* pRhs); diff --git a/src/coreclr/vm/sigformat.cpp b/src/coreclr/vm/sigformat.cpp index 268c255b531ab..ebfc7aae27c6b 100644 --- a/src/coreclr/vm/sigformat.cpp +++ b/src/coreclr/vm/sigformat.cpp @@ -545,12 +545,11 @@ void SigFormat::AddType(TypeHandle th) AddType(pRetAndArgTypes[i+1]); if (i != (cArgs - 1)) - AddString(", "); + AddString(", "); } if ((pTD->GetCallConv() & IMAGE_CEE_CS_CALLCONV_MASK) == IMAGE_CEE_CS_CALLCONV_VARARG) { AddString(", "); - AddString("..."); } AddString(")"); diff --git a/src/coreclr/vm/siginfo.cpp b/src/coreclr/vm/siginfo.cpp index 3bc26276d4d79..fb37eb54bc5fc 100644 --- a/src/coreclr/vm/siginfo.cpp +++ b/src/coreclr/vm/siginfo.cpp @@ -1616,15 +1616,35 @@ TypeHandle SigPointer::GetTypeHandleThrowing( TypeHandle *retAndArgTypes = (TypeHandle*) _alloca(cAllocaSize); bool fReturnTypeOrParameterNotLoaded = false; + int cMods = 0; + BYTE data; + SigPointer psigModReread = psig; for (unsigned i = 0; i <= cArgs; i++) { + IfFailThrowBF(psig.PeekByte(&data), BFA_BAD_SIGNATURE, pOrigModule); + CorElementType etyp = (CorElementType)data; + if (etyp == ELEMENT_TYPE_CMOD_OPT || typ == ELEMENT_TYPE_CMOD_REQD) + { + mdToken tk; + SigPointer psigTemp = psig; + + do { + cMods++; + psigTemp.SkipBytes(1); + IfFailThrowBF(psigTemp.GetToken(&tk), BFA_BAD_SIGNATURE, pOrigModule); + IfFailThrowBF(psigTemp.GetByte(&data), BFA_BAD_SIGNATURE, pOrigModule); + etyp = (CorElementType)data; + } while (etyp == ELEMENT_TYPE_CMOD_OPT || etyp == ELEMENT_TYPE_CMOD_REQD); + } + retAndArgTypes[i] = psig.GetTypeHandleThrowing(pOrigModule, - pTypeContext, - fLoadTypes, - level, - dropGenericArgumentLevel, - pSubst, - pZapSigContext); + pTypeContext, + fLoadTypes, + level, + dropGenericArgumentLevel, + pSubst, + pZapSigContext); + if (retAndArgTypes[i].IsNull()) { thRet = TypeHandle(); @@ -1640,11 +1660,71 @@ TypeHandle SigPointer::GetTypeHandleThrowing( break; } + FnPtrTypeDescCustomMod *customMods = NULL; + + if (cMods) + { + uint32_t cAllocaSize; + if (!ClrSafeInt::addition(cArgs, cMods, cAllocaSize) || + !ClrSafeInt::multiply(cAllocaSize, sizeof(FnPtrTypeDescCustomMod), cAllocaSize)) + { + ThrowHR(COR_E_OVERFLOW); + } + + customMods = (FnPtrTypeDescCustomMod*) _alloca(cAllocaSize); + + // Re-parse and store the custom mods + int iCurrent = 0; + for (unsigned i = 0; i <= cArgs; i++) + { + IfFailThrowBF(psigModReread.PeekByte(&data), BFA_BAD_SIGNATURE, pOrigModule); + CorElementType etyp = (CorElementType)data; + if (etyp == ELEMENT_TYPE_CMOD_OPT || typ == ELEMENT_TYPE_CMOD_REQD) + { + do + { + psigModReread.SkipBytes(1); + + mdToken token; + IfFailThrow(psigModReread.GetToken(&token)); + + TypeHandle typeHandle = ClassLoader::LoadTypeDefOrRefOrSpecThrowing( + static_cast(pOrigModule), + token, + pTypeContext, + ClassLoader::ThrowIfNotFound, + ClassLoader::FailIfUninstDefOrRef); + + FnPtrTypeDescCustomMod mod; + mod.elementType = etyp; + mod.typeHandle = typeHandle; + customMods[iCurrent++] = mod; + + IfFailThrowBF(psigModReread.PeekByte(&data), BFA_BAD_SIGNATURE, pOrigModule); + etyp = (CorElementType)data; + } while (etyp == ELEMENT_TYPE_CMOD_OPT || etyp == ELEMENT_TYPE_CMOD_REQD); + } + + IfFailThrowBF(psigModReread.SkipExactlyOne(), BFA_BAD_SIGNATURE, pOrigModule); + + if (i != cArgs) + { + // Create a mod separator for the parameter. + FnPtrTypeDescCustomMod mod; + mod.elementType = ELEMENT_TYPE_END; + mod.typeHandle = TypeHandle(); + customMods[iCurrent++] = mod; + } + } + + _ASSERT(iCurrent = cMods + cArgs); + } + uint32_t sigLen = sigStart - psig.m_dwLen; PCOR_SIGNATURE sig = (PCOR_SIGNATURE)psig.m_ptr - sigLen; // Now find an existing function pointer or make a new one - thRet = ClassLoader::LoadFnptrTypeThrowing(static_cast(pOrigModule), sig, sigLen, (BYTE) uCallConv, cArgs, retAndArgTypes, fLoadTypes, level); + thRet = ClassLoader::LoadFnptrTypeThrowing((BYTE) uCallConv, cArgs, retAndArgTypes, cMods, customMods, fLoadTypes, level); #else DacNotImpl(); thRet = TypeHandle(); diff --git a/src/coreclr/vm/typedesc.cpp b/src/coreclr/vm/typedesc.cpp index 2eda469d4c16d..f0f7f29e758ac 100644 --- a/src/coreclr/vm/typedesc.cpp +++ b/src/coreclr/vm/typedesc.cpp @@ -108,6 +108,8 @@ PTR_Module TypeDesc::GetModule() { GC_NOTRIGGER; FORBID_FAULT; SUPPORTS_DAC; + // Function pointer types belong to no module + //PRECONDITION(GetInternalCorElementType() != ELEMENT_TYPE_FNPTR); } CONTRACTL_END @@ -127,10 +129,7 @@ PTR_Module TypeDesc::GetModule() { _ASSERTE(GetInternalCorElementType() == ELEMENT_TYPE_FNPTR); - // A Function pointer keeps the reference to the original module in order to lazily resolve - // custom modifier types from its signature. - PTR_FnPtrTypeDesc asFn = dac_cast(this); - return asFn->GetModule(); + return GetLoaderModule(); } Assembly* TypeDesc::GetAssembly() { diff --git a/src/coreclr/vm/typedesc.h b/src/coreclr/vm/typedesc.h index e5ac11ff26aef..e27c54ee0768a 100644 --- a/src/coreclr/vm/typedesc.h +++ b/src/coreclr/vm/typedesc.h @@ -3,10 +3,7 @@ // // File: typedesc.h // - - // - // // ============================================================================ @@ -439,6 +436,12 @@ class TypeVarTypeDesc : public TypeDesc /*************************************************************************/ /* represents a function type. */ +struct FnPtrTypeDescCustomMod +{ + CorElementType elementType; + TypeHandle typeHandle; +}; + typedef DPTR(class FnPtrTypeDesc) PTR_FnPtrTypeDesc; class FnPtrTypeDesc : public TypeDesc @@ -447,13 +450,12 @@ class FnPtrTypeDesc : public TypeDesc public: #ifndef DACCESS_COMPILE FnPtrTypeDesc( - PTR_Module module, - PCOR_SIGNATURE sig /*copy made by caller*/, - int32_t sigLen, BYTE callConv, DWORD numArgs, - TypeHandle * retAndArgTypes) - : TypeDesc(ELEMENT_TYPE_FNPTR), m_pModule(module), m_hExposedClassObject(0), m_NumArgs(numArgs), m_CallConv(callConv), m_sig(sig), m_sigLen(sigLen) + TypeHandle * retAndArgTypes, + DWORD numMods, + FnPtrTypeDescCustomMod * customModTypes) + : TypeDesc(ELEMENT_TYPE_FNPTR), m_hExposedClassObject(0), m_NumArgs(numArgs), m_CallConv(callConv), m_NumMods(numMods), m_pCustomModTypes(customModTypes) { LIMITED_METHOD_CONTRACT; @@ -479,27 +481,6 @@ class FnPtrTypeDesc : public TypeDesc return static_cast(m_CallConv); } - PCOR_SIGNATURE GetSignature() - { - LIMITED_METHOD_CONTRACT; - SUPPORTS_DAC; - return m_sig; - } - - uint32_t GetSignatureLen() - { - LIMITED_METHOD_CONTRACT; - SUPPORTS_DAC; - return m_sigLen; - } - - PTR_Module GetModule() - { - LIMITED_METHOD_CONTRACT; - SUPPORTS_DAC; - return m_pModule; - } - // Return a pointer to the types of the signature, return type followed by argument types // The type handles are guaranteed to be fixed up TypeHandle * GetRetAndArgTypes(); @@ -519,6 +500,21 @@ class FnPtrTypeDesc : public TypeDesc return PTR_TypeHandle(m_RetAndArgTypes); } + DWORD GetNumMods() + { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + return m_NumMods; + } + + FnPtrTypeDescCustomMod* GetCustomModTypesPointer() + { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + + return m_pCustomModTypes; + } + #ifndef DACCESS_COMPILE // Returns TRUE if all return and argument types are externally visible. BOOL IsExternallyVisible() const; @@ -561,8 +557,6 @@ class FnPtrTypeDesc : public TypeDesc } protected: - PTR_Module m_pModule; - // Handle back to the internal reflection Type object LOADERHANDLE m_hExposedClassObject; @@ -572,8 +566,9 @@ class FnPtrTypeDesc : public TypeDesc // Calling convention (actually just a single byte) DWORD m_CallConv; - PCOR_SIGNATURE m_sig; - uint32_t m_sigLen; + // Custom modifiers + DWORD m_NumMods; + FnPtrTypeDescCustomMod* m_pCustomModTypes; // Return type first, then argument types TypeHandle m_RetAndArgTypes[1]; diff --git a/src/coreclr/vm/typehandle.cpp b/src/coreclr/vm/typehandle.cpp index 46cdfac29a718..ed5f6c947d2dc 100644 --- a/src/coreclr/vm/typehandle.cpp +++ b/src/coreclr/vm/typehandle.cpp @@ -1458,12 +1458,12 @@ TypeKey TypeHandle::GetTypeKey() const { CONSISTENCY_CHECK(etype == ELEMENT_TYPE_FNPTR); FnPtrTypeDesc* pFTD = (FnPtrTypeDesc*) pTD; - TypeKey tk(NULL /*module*/, - pFTD->GetSignature(), - pFTD->GetSignatureLen(), + TypeKey tk( pFTD->GetCallConv(), pFTD->GetNumArgs(), - pFTD->GetRetAndArgTypesPointer()); + pFTD->GetRetAndArgTypesPointer(), + pFTD->GetNumMods(), + pFTD->GetCustomModTypesPointer()); return tk; } } diff --git a/src/coreclr/vm/typehash.cpp b/src/coreclr/vm/typehash.cpp index 596ea0d2621c2..60e995db370e5 100644 --- a/src/coreclr/vm/typehash.cpp +++ b/src/coreclr/vm/typehash.cpp @@ -167,7 +167,7 @@ static DWORD HashPossiblyInstantiatedType(mdTypeDef token, Instantiation inst) } // Calculate hash value for a function pointer type -static DWORD HashFnPtrType(uint32_t sigLen, BYTE callConv, DWORD numArgs, TypeHandle *retAndArgTypes) +static DWORD HashFnPtrType(BYTE callConv, DWORD numArgs, TypeHandle *retAndArgTypes) { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; @@ -182,6 +182,8 @@ static DWORD HashFnPtrType(uint32_t sigLen, BYTE callConv, DWORD numArgs, TypeHa dwHash = ((dwHash << 5) + dwHash) ^ retAndArgTypes[i].AsTAddr(); } + // Currently we are not hashing on the custom mods. + return (DWORD)dwHash; } @@ -224,7 +226,6 @@ static DWORD HashTypeHandle(TypeHandle t) { FnPtrTypeDesc* pTD = t.AsFnPtrType(); retVal = HashFnPtrType( - pTD->GetSignatureLen(), pTD->GetCallConv(), pTD->GetNumArgs(), pTD->GetRetAndArgTypesPointer()); @@ -261,7 +262,7 @@ DWORD HashTypeKey(TypeKey* pKey) } else if (pKey->GetKind() == ELEMENT_TYPE_FNPTR) { - return HashFnPtrType(pKey->GetSignatureLen(), pKey->GetCallConv(), pKey->GetNumArgs(), pKey->GetRetAndArgTypes()); + return HashFnPtrType(pKey->GetCallConv(), pKey->GetNumArgs(), pKey->GetRetAndArgTypes()); } else { @@ -315,17 +316,16 @@ EETypeHashEntry_t *EETypeHashTable::FindItem(TypeKey* pKey) } else if (kind == ELEMENT_TYPE_FNPTR) { - Module* pModule = pKey->GetModule(); - PCOR_SIGNATURE sig = pKey->GetSignature(); - uint32_t sigLen = pKey->GetSignatureLen(); BYTE callConv = pKey->GetCallConv(); DWORD numArgs = pKey->GetNumArgs(); TypeHandle *retAndArgTypes = pKey->GetRetAndArgTypes(); + DWORD numMods = pKey->GetNumMods(); + FnPtrTypeDescCustomMod *customModTypes = pKey->GetCustomModTypes(); pSearch = BaseFindFirstEntryByHash(dwHash, &sContext); while (pSearch) { - if (CompareFnPtrType(pSearch->GetTypeHandle(), pModule, sig, sigLen, callConv, numArgs, retAndArgTypes)) + if (CompareFnPtrType(pSearch->GetTypeHandle(), callConv, numArgs, retAndArgTypes, numMods, customModTypes)) { result = pSearch; break; @@ -443,8 +443,8 @@ BOOL EETypeHashTable::CompareInstantiatedType(TypeHandle t, Module *pModule, mdT return TRUE; } -// See also TypeKey::Equals -BOOL EETypeHashTable::CompareFnPtrType(TypeHandle t, Module* pModule, PCOR_SIGNATURE sig, uint32_t sigLen, BYTE callConv, DWORD numArgs, TypeHandle *retAndArgTypes) +// See also TypeKey::Equals for similar comparison logic. +BOOL EETypeHashTable::CompareFnPtrType(TypeHandle t, BYTE callConv, DWORD numArgs, TypeHandle *retAndArgTypes, DWORD numMods, FnPtrTypeDescCustomMod *customModTypes) { CONTRACTL { @@ -453,8 +453,8 @@ BOOL EETypeHashTable::CompareFnPtrType(TypeHandle t, Module* pModule, PCOR_SIGNA GC_NOTRIGGER; MODE_ANY; PRECONDITION(CheckPointer(t)); - PRECONDITION(CheckPointer(sig, NULL_OK)); PRECONDITION(CheckPointer(retAndArgTypes)); + PRECONDITION(CheckPointer(customModTypes, NULL_OK)); SUPPORTS_DAC; } CONTRACTL_END @@ -464,13 +464,6 @@ BOOL EETypeHashTable::CompareFnPtrType(TypeHandle t, Module* pModule, PCOR_SIGNA #ifndef DACCESS_COMPILE - // A FnPtrTypeDesc has all signature information already extracted except for - // custom mods and it contains a copy of the raw signature. Not all keys however have the - // raw signature information, so we first check against the already extracted information, - // and then optionally compare the signature in order to verify against custom mods. - // If every key (such as DacDbiInterfaceImpl::FindLoadedFnptrType) had a signature, we could - // just compare against the signature. - FnPtrTypeDesc* pTD = t.AsFnPtrType(); if (pTD->GetNumArgs() != numArgs || pTD->GetCallConv() != callConv) @@ -486,27 +479,23 @@ BOOL EETypeHashTable::CompareFnPtrType(TypeHandle t, Module* pModule, PCOR_SIGNA } } - // The checks below are made optional to support DacDbiInterfaceImpl::FindLoadedFnptrType which doesn't - // consider calling convention (including unmanaged custom modifiers), module or signature. If the signature length - // is not provided do not include the comparison against the signature. - - if (sigLen == 0 && pTD->GetSignatureLen() == 0) + if (numMods != pTD->GetNumMods()) { - // Some keys don't have a signature; just skip in that case. - return TRUE; + return FALSE; } - if (sigLen == 0 || pTD->GetSignatureLen() == 0) + FnPtrTypeDescCustomMod *customModTypes2 = pTD->GetCustomModTypesPointer(); + for (DWORD i = 0; i < numMods; i++) { - // Only one side has a signature. - return FALSE; + if ((customModTypes2[i].elementType != customModTypes[i].elementType) || + (customModTypes2[i].typeHandle != customModTypes[i].typeHandle)) + { + return FALSE; + } } - - return MetaSig::CompareMethodSigs( - pTD->GetSignature(), pTD->GetSignatureLen(), pTD->GetModule(), NULL, - sig, sigLen, pModule, NULL, - FALSE); - + + return TRUE; + #else DacNotImpl(); return FALSE; diff --git a/src/coreclr/vm/typehash.h b/src/coreclr/vm/typehash.h index 188ec1e0bb052..f41917a0308f2 100644 --- a/src/coreclr/vm/typehash.h +++ b/src/coreclr/vm/typehash.h @@ -136,7 +136,7 @@ class EETypeHashTable : public DacEnumerableHashTablem_kind == ELEMENT_TYPE_FNPTR); - // See also EETypeHashTable::CompareFnPtrType + // See also EETypeHashTable::CompareFnPtrType for similar comparison logic. BYTE callConv = pKey1->u.asFnPtr.m_callConv; if (callConv != pKey2->u.asFnPtr.m_callConv || @@ -282,25 +280,24 @@ class TypeKey return FALSE; } - // Match the signatures. - uint32_t sigLen1 = pKey1->u.asFnPtr.m_sigLen; - uint32_t sigLen2 = pKey2->u.asFnPtr.m_sigLen; - if (sigLen1 == 0 && sigLen2 == 0) + // Match the custom modifiers. + uint32_t numMods = pKey1->u.asFnPtr.m_numMods; + if (numMods != pKey2->u.asFnPtr.m_numMods) { - // Some keys don't have a signature; just skip in that case. - return TRUE; + return FALSE; } - - if (sigLen1 == 0 || sigLen2 == 0) + + for (DWORD i = 0; i < numMods; i++) { - // Only one key has a signature. - return FALSE; + FnPtrTypeDescCustomMod customModTypes1 = pKey1->u.asFnPtr.m_pCustomModTypes[i]; + FnPtrTypeDescCustomMod customModTypes2 = pKey2->u.asFnPtr.m_pCustomModTypes[i]; + + if ((customModTypes1.elementType != customModTypes2.elementType) || + (customModTypes1.typeHandle != customModTypes2.typeHandle)) + return FALSE; } - return MetaSig::CompareMethodSigs( - pKey1->u.asFnPtr.m_sig, sigLen1, pKey1->u.asFnPtr.m_pModule, NULL, - pKey2->u.asFnPtr.m_sig, sigLen2, pKey2->u.asFnPtr.m_pModule, NULL, - FALSE); + return TRUE; } } }; diff --git a/src/libraries/Common/tests/System/FunctionPointerTests.cs b/src/libraries/Common/tests/System/FunctionPointerTests.cs index eb84377e516e7..d4982f0fae291 100644 --- a/src/libraries/Common/tests/System/FunctionPointerTests.cs +++ b/src/libraries/Common/tests/System/FunctionPointerTests.cs @@ -29,9 +29,10 @@ public static unsafe void TestTypeMembers() Assert.True(t.IsFunctionPointer); Assert.False(t.IsPointer); // A function pointer is not compatible with IsPointer semantics. Assert.False(t.IsUnmanagedFunctionPointer); + Assert.NotNull(t.Module); + Assert.NotNull(t.Assembly); // Common for all function pointers: - Assert.Equal(typeof(FunctionPointerTests).Project().Assembly, t.Assembly); Assert.Equal(TypeAttributes.Public, t.Attributes); Assert.Null(t.BaseType); Assert.False(t.ContainsGenericParameters); @@ -105,7 +106,6 @@ public static unsafe void TestTypeMembers() Assert.True(t.IsVisible); Assert.Equal(MemberTypes.TypeInfo, t.MemberType); Assert.True(t.MetadataToken != 0); - Assert.Equal(typeof(FunctionPointerTests).Project().Module, t.Module); Assert.Null(t.ReflectedType); Assert.Null(t.TypeInitializer); @@ -234,7 +234,6 @@ public static unsafe void TestMethod( Assert.Null(fnPtrType.AssemblyQualifiedName); Assert.Equal("System", fnPtrType.Namespace); Assert.Equal("*()", fnPtrType.Name); - Assert.Equal(t.Module, fnPtrType.Module); VerifyArg(fnPtrType.GetFunctionPointerReturnParameter(), expectedFcnPtrReturnName); @@ -262,7 +261,6 @@ public static unsafe void TestProperty(string name, string expectedToString) Assert.Equal(expectedToString + " " + name, p.ToString()); Type fnPtrType = p.PropertyType; - Assert.Equal(t.Module, fnPtrType.Module); Assert.Equal(expectedToString, fnPtrType.ToString()); VerifyFieldOrProperty(fnPtrType); } @@ -278,7 +276,6 @@ public static unsafe void TestField(string name, string expectedToString) Assert.Equal(expectedToString + " " + name, f.ToString()); Type fnPtrType = f.FieldType; - Assert.Equal(t.Module, fnPtrType.Module); Assert.Equal(expectedToString, fnPtrType.ToString()); VerifyFieldOrProperty(fnPtrType); } @@ -420,6 +417,7 @@ public static unsafe void TestSigEqualityInDifferentModule_MethodReturn(string n static Type GetFuncPtr(Type owner, string name) => owner.GetMethod(name, Bindings).ReturnParameter.ParameterType; } + // Tests to add: required modifiers, ask for optional return modifiers before\after calling conventions public unsafe class FunctionPointerHolder { public delegate* ToString_1; diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.cs index ab65623823caf..cf0ce2bcd31c7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.cs @@ -21,24 +21,41 @@ public Type[] GetCallingConventions() { if (_callingConventions == null) { - Type[] customModifiers = _returnInfo.GetOptionalCustomModifiers(); - List? list = null; + ComputeCallingConventions(); + Debug.Assert(_callingConventions != null); + } + + return CloneArray(_callingConventions); + } + + internal void ComputeCallingConventions() + { + if (_callingConventions == null) + { + ArrayBuilder ccBuilder = default; + ArrayBuilder allBuilder = default; + + Type[]? modifiers = GetCustomModifiersFromFunctionPointer(position: 0, required: false); bool foundCallingConvention = false; - for (int i = 0; i < customModifiers.Length; i++) + if (modifiers != null) { - Type type = customModifiers[i]; - if (type.FullName!.StartsWith(CallingConventionTypePrefix)) + for (int i = 0; i < modifiers.Length; i++) { - list ??= new List(); - list.Add(type); - - if (type == typeof(CallConvCdecl) || - type == typeof(CallConvFastcall) || - type == typeof(CallConvStdcall) || - type == typeof(CallConvThiscall)) + Type type = modifiers[i]; + allBuilder.Add(type); + if (type.FullName!.StartsWith(CallingConventionTypePrefix)) { - foundCallingConvention = true; + ccBuilder.Add(type); + + // todo: use StartsWith(CallingConventionTypePrefix) to get any new calling conventions + if (type == typeof(CallConvCdecl) || + type == typeof(CallConvFastcall) || + type == typeof(CallConvStdcall) || + type == typeof(CallConvThiscall)) + { + foundCallingConvention = true; + } } } } @@ -66,17 +83,14 @@ public Type[] GetCallingConventions() if (callConv != null) { - list ??= new List(); - list.Add(callConv); - _returnInfo.GetOptionalCustomModifiersList().Add(callConv); + allBuilder.Add(callConv); + ccBuilder.Add(callConv); } } - _callingConventions = list == null ? Type.EmptyTypes : list.ToArray(); - Debug.Assert(_callingConventions != null); + _returnInfo.SetCustomModifiersForReturnType(allBuilder.Count == 0 ? Type.EmptyTypes : allBuilder.ToArray()); + _callingConventions = ccBuilder.Count == 0 ? Type.EmptyTypes : ccBuilder.ToArray(); } - - return CloneArray(_callingConventions); } private static T[] CloneArray(T[] original) diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/TypeDelegator.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/TypeDelegator.cs index f51096d1f5f40..4a8d08a9bd77e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/TypeDelegator.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/TypeDelegator.cs @@ -10,7 +10,7 @@ namespace System.Reflection { - public partial class TypeDelegator : TypeInfo + public class TypeDelegator : TypeInfo { public override bool IsAssignableFrom([NotNullWhen(true)] TypeInfo? typeInfo) { diff --git a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/SampleMetadata/SampleMetadata.cs b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/SampleMetadata/SampleMetadata.cs index 69bb51454dcbb..dfe7b197b59b5 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/SampleMetadata/SampleMetadata.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/SampleMetadata/SampleMetadata.cs @@ -557,16 +557,5 @@ public class ClassWithDefaultMember1 where T : ClassWithDefaultMember1 public class PublicClass { internal class InternalNestedClass { } - } - -#if FUNCTIONPOINTER_SUPPORT - public unsafe class FunctionPointerHolder - { - public delegate* managed Field; - public delegate* managed Prop { get; } - public delegate* managed MethodReturnValue() => default; - public void MethodArg(delegate* managed f) { } - public void MethodArgNative(delegate* unmanaged[Stdcall, SuppressGCTransition] f) { } - } -#endif + } } diff --git a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Method/MethodTests.cs b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Method/MethodTests.cs index 9640e8fcbf750..1fab77de7cc4e 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Method/MethodTests.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Method/MethodTests.cs @@ -248,7 +248,6 @@ public static unsafe void TestCustomModifiers1() { using (MetadataLoadContext lc = new MetadataLoadContext(new CoreMetadataAssemblyResolver(), "mscorlib")) { - Assembly a = lc.LoadFromByteArray(TestData.s_CustomModifiersImage); Type t = a.GetType("N", throwOnError: true); Type reqA = a.GetType("ReqA", throwOnError: true); diff --git a/src/libraries/System.Reflection/tests/MethodInfoTests.cs b/src/libraries/System.Reflection/tests/MethodInfoTests.cs index 082542d9efd52..e91cbbd3483b0 100644 --- a/src/libraries/System.Reflection/tests/MethodInfoTests.cs +++ b/src/libraries/System.Reflection/tests/MethodInfoTests.cs @@ -6,6 +6,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Xunit; +using Xunit.Sdk; namespace System.Reflection.Tests { @@ -205,7 +206,6 @@ public void EqualsTest(Type type1, string name1, Type type2, string name2, bool [InlineData("DummyMethod1", "DummyMethod1", true)] //Verify two different MethodInfo objects are not equal [InlineData("DummyMethod1", "DummyMethod2", false)] - public void Equality1(string str1, string str2, bool expected) { MethodInfo mi1 = GetMethod(typeof(MethodInfoTests), str1); @@ -797,6 +797,35 @@ private static void SecondCall(MethodInfo mi) Assert.Contains("TestAssembly", asm.ToString()); } + [Fact] + private static unsafe void TestFunctionPointers() + { + void* fn = FunctionPointerMethods.GetFunctionPointer(); + + // Sanity checks for direct invocation. + Assert.True(FunctionPointerMethods.GetFunctionPointer()(42)); + Assert.True(FunctionPointerMethods.CallFcnPtr_IntPtr((IntPtr)fn, 42)); + Assert.True(FunctionPointerMethods.CallFcnPtr_Void(fn, 42)); + Assert.False(FunctionPointerMethods.GetFunctionPointer()(41)); + Assert.False(FunctionPointerMethods.CallFcnPtr_IntPtr((IntPtr)fn, 41)); + Assert.False(FunctionPointerMethods.CallFcnPtr_Void(fn, 41)); + + MethodInfo m; + + m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.CallFcnPtr_FP)); + // System.ArgumentException : Object of type 'System.IntPtr' cannot be converted to type 'System.Boolean(System.Int32)' + Assert.Throws(() => m.Invoke(null, new object[] { (IntPtr)fn, 42 })); + Assert.Throws(() => m.Invoke(null, new object[] { (IntPtr)fn, 41 })); + + m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.CallFcnPtr_IntPtr)); + Assert.True((bool)m.Invoke(null, new object[] { (IntPtr)fn, 42 })); + Assert.False((bool)m.Invoke(null, new object[] { (IntPtr)fn, 41 })); + + m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.CallFcnPtr_Void)); + Assert.True((bool)m.Invoke(null, new object[] { (IntPtr)fn, 42 })); + Assert.False((bool)m.Invoke(null, new object[] { (IntPtr)fn, 41 })); + } + //Methods for Reflection Metadata private void DummyMethod1(string str, int iValue, long lValue) { @@ -1154,6 +1183,31 @@ public static bool PassColorsShort(ColorsShort color) return true; } } + + public static class FunctionPointerMethods + { + public static bool CallMe(int i) + { + return i == 42; + } + + public static unsafe bool CallFcnPtr_FP(delegate* fn, int value) + { + return fn(value); + } + + public static unsafe bool CallFcnPtr_IntPtr(IntPtr fn, int value) + { + return ((delegate*)fn)(value); + } + + public static unsafe bool CallFcnPtr_Void(void* fn, int value) + { + return ((delegate*)fn)(value); + } + + public static unsafe delegate* GetFunctionPointer() => &CallMe; + } #pragma warning restore 0414 } diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index a0e1f008b825f..9e24fc7b6400e 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -1,4 +1,4 @@ - + false true @@ -278,6 +278,9 @@ + + /Link>=CommonSystem\Collections\Generic\ArrayBuilder.cs + diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.Mono.cs index 5d30777f17d02..b8f0f4c74df80 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.Mono.cs @@ -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. -using System.Collections.Generic; - namespace System.Reflection { internal partial class FunctionPointerInfo @@ -13,7 +11,8 @@ internal FunctionPointerInfo() _parameterInfos = default!; } - internal List GetOptionalCustomModifiersList() => throw new NotSupportedException(); + private Type[]? GetCustomModifiersFromFunctionPointer(int position, bool required) => throw new NotSupportedException(); private unsafe MdSigCallingConvention CallingConvention => throw new NotSupportedException(); + private RuntimeType FunctionPointerType => throw new NotSupportedException(); } } diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFunctionPointerParameterInfo.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFunctionPointerParameterInfo.Mono.cs index fb842e62f9229..b087fa299fb5d 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFunctionPointerParameterInfo.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFunctionPointerParameterInfo.Mono.cs @@ -14,6 +14,6 @@ public RuntimeFunctionPointerParameterInfo() public override Type[] GetOptionalCustomModifiers() => throw new NotSupportedException(); public override Type[] GetRequiredCustomModifiers() => throw new NotSupportedException(); - internal List GetOptionalCustomModifiersList() => throw new NotSupportedException(); + internal void SetCustomModifiersForReturnType(Type[] modifiers) => throw new NotSupportedException(); } } From 03ee5c554dc7476544d2112bc38be76f3c7985f8 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Sat, 9 Jul 2022 10:55:32 -0500 Subject: [PATCH 3/9] Fix compile error --- .../System.Private.CoreLib/System.Private.CoreLib.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index d275df663ece2..f4c2e31a1071a 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -247,6 +247,9 @@ + + Common\System\Collections\Generic\ArrayBuilder.cs + @@ -264,9 +267,6 @@ Common\System\Runtime\InteropServices\Variant.cs - - Common\System\Collections\Generic\ArrayBuilder.cs - From 385014531bfbc2e661c568a96c8319ff8826de49 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Sat, 9 Jul 2022 15:10:02 -0500 Subject: [PATCH 4/9] Feedback; naming; additional test --- .../src/System/RuntimeType.CoreCLR.cs | 9 +-- src/coreclr/debug/daccess/dacdbiimpl.cpp | 4 +- src/coreclr/vm/clsload.cpp | 21 +++-- src/coreclr/vm/clsload.hpp | 4 +- src/coreclr/vm/eedbginterfaceimpl.cpp | 8 +- src/coreclr/vm/runtimehandles.cpp | 8 +- src/coreclr/vm/siginfo.cpp | 22 ++--- src/coreclr/vm/typedesc.h | 20 +++-- src/coreclr/vm/typehandle.cpp | 2 +- src/coreclr/vm/typehash.cpp | 26 +++--- src/coreclr/vm/typehash.h | 2 +- src/coreclr/vm/typekey.h | 22 ++--- src/coreclr/vm/typestring.cpp | 2 +- .../tests/System/FunctionPointerTests.cs | 81 +++++++++++++------ .../System/Reflection/FunctionPointerInfo.cs | 9 +-- .../TypeLoading/General/CoreType.cs | 4 +- .../TypeLoading/General/Utf8Constants.cs | 2 +- .../Types/RoFunctionPointerType.cs | 10 +-- .../ConvertToLibraryImportAnalyzerTests.cs | 1 + .../System.Runtime/System.Runtime.sln | 6 ++ 20 files changed, 155 insertions(+), 108 deletions(-) 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 00a74b0336361..566fcfa787fb0 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -1565,7 +1565,7 @@ internal bool DomainInitialized if (!m_runtimeType.GetRootElementType().IsGenericTypeDefinition && m_runtimeType.ContainsGenericParameters) return null; - // Exclude function pointer; it requires a grammar update (see https://docs.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/specifying-fully-qualified-type-names) + // Exclude function pointer; it requires a grammar update (see https://docs.microsoft.com/dotnet/framework/reflection-and-codedom/specifying-fully-qualified-type-names) // and parsing support for Type.GetType(...) and related GetType() methods. if (m_runtimeType.IsFunctionPointer) return null; @@ -1582,16 +1582,15 @@ internal bool DomainInitialized } } - internal string GetNameSpace() + internal string? GetNameSpace() { // @Optimization - Use ConstructName to populate m_namespace if (m_namespace == null) { Type type = m_runtimeType; - // Since Function pointers don't have a TypeDef metadata record just use the namespace for System.Type. if (type.IsFunctionPointer) - return typeof(Type).Namespace!; + return null; type = type.GetRootElementType(); @@ -3354,7 +3353,7 @@ public override string? Namespace { get { - string ns = Cache.GetNameSpace(); + string? ns = Cache.GetNameSpace(); if (string.IsNullOrEmpty(ns)) { return null; diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 00137a8c753c3..e873638aa3699 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -2373,8 +2373,8 @@ TypeHandle DacDbiInterfaceImpl::FindLoadedFnptrType(DWORD numTypeArgs, TypeHandl return ClassLoader::LoadFnptrTypeThrowing(0, // callConv numTypeArgs - 1, pInst, - 0, // numMods - NULL, // customModTypes + 0, // numCustomMods + NULL, // customMods ClassLoader::DontLoadTypes); } // DacDbiInterfaceImpl::FindLoadedFnptrType diff --git a/src/coreclr/vm/clsload.cpp b/src/coreclr/vm/clsload.cpp index 6ea9d66c4333d..1f84cc20950e7 100644 --- a/src/coreclr/vm/clsload.cpp +++ b/src/coreclr/vm/clsload.cpp @@ -1618,8 +1618,8 @@ TypeHandle ClassLoader::LoadNativeValueTypeThrowing(TypeHandle baseType, TypeHandle ClassLoader::LoadFnptrTypeThrowing(BYTE callConv, DWORD ntypars, TypeHandle* inst, - DWORD numMods, - FnPtrTypeDescCustomMod* customModTypes, + DWORD numCustomMods, + FnPtrTypeDescCustomMod* customMods, LoadTypesFlag fLoadTypes/*=LoadTypes*/, ClassLoadLevel level/*=CLASS_LOADED*/) { @@ -1636,7 +1636,7 @@ TypeHandle ClassLoader::LoadFnptrTypeThrowing(BYTE callConv, } CONTRACT_END - TypeKey key(callConv, ntypars, inst, numMods, customModTypes); + TypeKey key(callConv, ntypars, inst, numCustomMods, customMods); RETURN(LoadConstructedTypeThrowing(&key, fLoadTypes, level)); } @@ -2955,26 +2955,25 @@ 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); PTR_LoaderHeap loaderHeap = pLoaderModule->GetAssembly()->GetLowFrequencyHeap(); - DWORD numArgs = pKey->GetNumArgs(); BYTE* mem = (BYTE*) pamTracker->Track(loaderHeap->AllocMem( S_SIZE_T(sizeof(FnPtrTypeDesc)) + S_SIZE_T(sizeof(TypeHandle)) * S_SIZE_T(numArgs))); - DWORD numMods = pKey->GetNumMods(); + DWORD numCustomMods = pKey->GetNumMods(); FnPtrTypeDescCustomMod* modTypes = NULL; - if (numMods) + if (numCustomMods) { - FnPtrTypeDescCustomMod* srcMods = pKey->GetCustomModTypes(); + FnPtrTypeDescCustomMod* srcMods = pKey->GetCustomMods(); modTypes = (FnPtrTypeDescCustomMod*) pamTracker->Track(loaderHeap->AllocMem( - S_SIZE_T(sizeof(FnPtrTypeDescCustomMod)) * S_SIZE_T(numMods))); + S_SIZE_T(sizeof(FnPtrTypeDescCustomMod)) * S_SIZE_T(numCustomMods))); - for (DWORD i = 0; i <= numMods; i++) + for (DWORD i = 0; i <= numCustomMods; i++) { modTypes[i] = srcMods[i]; } @@ -2984,7 +2983,7 @@ TypeHandle ClassLoader::CreateTypeHandleForTypeKey(TypeKey* pKey, AllocMemTracke pKey->GetCallConv(), numArgs, pKey->GetRetAndArgTypes(), - numMods, + numCustomMods, modTypes)); } else diff --git a/src/coreclr/vm/clsload.hpp b/src/coreclr/vm/clsload.hpp index 82cc323a5db52..55496583b7cb8 100644 --- a/src/coreclr/vm/clsload.hpp +++ b/src/coreclr/vm/clsload.hpp @@ -697,8 +697,8 @@ class ClassLoader static TypeHandle LoadFnptrTypeThrowing(BYTE callConv, DWORD numArgs, TypeHandle* retAndArgTypes, - DWORD numMods, - FnPtrTypeDescCustomMod* customModTypes, + DWORD numCustomMods, + FnPtrTypeDescCustomMod* customMods, LoadTypesFlag fLoadTypes = LoadTypes, ClassLoadLevel level = CLASS_LOADED); diff --git a/src/coreclr/vm/eedbginterfaceimpl.cpp b/src/coreclr/vm/eedbginterfaceimpl.cpp index 1e695784fde0b..fc23de3128135 100644 --- a/src/coreclr/vm/eedbginterfaceimpl.cpp +++ b/src/coreclr/vm/eedbginterfaceimpl.cpp @@ -887,8 +887,8 @@ TypeHandle EEDbgInterfaceImpl::FindLoadedFnptrType(TypeHandle *inst, return ClassLoader::LoadFnptrTypeThrowing(0, // callConv ntypars, inst, - 0, // numMods - NULL, // customModTypes + 0, // numCustomMods + NULL, // customMods // should this be FailIfNotLoaded? - NO - although we may // want to debug unrestored VCs, we can't do it because the debug API @@ -1017,8 +1017,8 @@ TypeHandle EEDbgInterfaceImpl::LoadFnptrType(TypeHandle *inst, return ClassLoader::LoadFnptrTypeThrowing(0, // callConv ntypars, inst, - 0, // numMods - NULL // customModTypes + 0, // numCustomMods + NULL // customMods ); } diff --git a/src/coreclr/vm/runtimehandles.cpp b/src/coreclr/vm/runtimehandles.cpp index f59b30a91140d..1759f5b091903 100644 --- a/src/coreclr/vm/runtimehandles.cpp +++ b/src/coreclr/vm/runtimehandles.cpp @@ -927,10 +927,10 @@ FCIMPL3(Object *, RuntimeTypeHandle::GetCustomModifiersFromFunctionPointer, Refl // Discover the number of required and optional custom modifiers. INT32 currentPos = 0; INT32 cMods = 0; - DWORD numMods = fnPtr->GetNumMods(); - FnPtrTypeDescCustomMod* mods = fnPtr->GetCustomModTypesPointer(); + DWORD numCustomMods = fnPtr->GetNumMods(); + FnPtrTypeDescCustomMod* mods = fnPtr->GetCustomModsPointer(); - for (DWORD i = 0; i < numMods; i++) + for (DWORD i = 0; i < numCustomMods; i++) { CorElementType cmodType = mods[i].elementType; @@ -962,7 +962,7 @@ FCIMPL3(Object *, RuntimeTypeHandle::GetCustomModifiersFromFunctionPointer, Refl gc.retVal = (PTRARRAYREF) AllocateSzArray(arrayHandle, cMods); currentPos = 0; - for (DWORD i = 0; i < numMods; i++) + for (DWORD i = 0; i < numCustomMods; i++) { CorElementType cmodType = mods[i].elementType; diff --git a/src/coreclr/vm/siginfo.cpp b/src/coreclr/vm/siginfo.cpp index fb37eb54bc5fc..f5b6aa2b01bed 100644 --- a/src/coreclr/vm/siginfo.cpp +++ b/src/coreclr/vm/siginfo.cpp @@ -1602,7 +1602,7 @@ TypeHandle SigPointer::GetTypeHandleThrowing( if ((uCallConv & IMAGE_CEE_CS_CALLCONV_GENERIC) > 0) THROW_BAD_FORMAT(BFA_FNPTR_CANNOT_BE_GENERIC, pOrigModule); - // Get arg count; + // Get the arg count. uint32_t cArgs = 0; IfFailThrowBF(psig.GetData(&cArgs), BFA_BAD_SIGNATURE, pOrigModule); @@ -1621,9 +1621,10 @@ TypeHandle SigPointer::GetTypeHandleThrowing( SigPointer psigModReread = psig; for (unsigned i = 0; i <= cArgs; i++) { + // Get the custom mod count. IfFailThrowBF(psig.PeekByte(&data), BFA_BAD_SIGNATURE, pOrigModule); CorElementType etyp = (CorElementType)data; - if (etyp == ELEMENT_TYPE_CMOD_OPT || typ == ELEMENT_TYPE_CMOD_REQD) + if (etyp == ELEMENT_TYPE_CMOD_OPT || etyp == ELEMENT_TYPE_CMOD_REQD) { mdToken tk; SigPointer psigTemp = psig; @@ -1637,6 +1638,7 @@ TypeHandle SigPointer::GetTypeHandleThrowing( } while (etyp == ELEMENT_TYPE_CMOD_OPT || etyp == ELEMENT_TYPE_CMOD_REQD); } + // Lookup type handle. retAndArgTypes[i] = psig.GetTypeHandleThrowing(pOrigModule, pTypeContext, fLoadTypes, @@ -1661,25 +1663,25 @@ TypeHandle SigPointer::GetTypeHandleThrowing( } FnPtrTypeDescCustomMod *customMods = NULL; - + int cModsAndSeparators = cMods; if (cMods) { + // We know the number of mods so allocate and re-parse. uint32_t cAllocaSize; - if (!ClrSafeInt::addition(cArgs, cMods, cAllocaSize) || - !ClrSafeInt::multiply(cAllocaSize, sizeof(FnPtrTypeDescCustomMod), cAllocaSize)) + cModsAndSeparators += cArgs; // Each arg has a separator except for the last. + if (!ClrSafeInt::multiply(cModsAndSeparators, sizeof(FnPtrTypeDescCustomMod), cAllocaSize)) { ThrowHR(COR_E_OVERFLOW); } customMods = (FnPtrTypeDescCustomMod*) _alloca(cAllocaSize); - // Re-parse and store the custom mods int iCurrent = 0; for (unsigned i = 0; i <= cArgs; i++) { IfFailThrowBF(psigModReread.PeekByte(&data), BFA_BAD_SIGNATURE, pOrigModule); CorElementType etyp = (CorElementType)data; - if (etyp == ELEMENT_TYPE_CMOD_OPT || typ == ELEMENT_TYPE_CMOD_REQD) + if (etyp == ELEMENT_TYPE_CMOD_OPT || etyp == ELEMENT_TYPE_CMOD_REQD) { do { @@ -1709,7 +1711,7 @@ TypeHandle SigPointer::GetTypeHandleThrowing( if (i != cArgs) { - // Create a mod separator for the parameter. + // Create a mod separator for each parameter except for the last. FnPtrTypeDescCustomMod mod; mod.elementType = ELEMENT_TYPE_END; mod.typeHandle = TypeHandle(); @@ -1723,8 +1725,8 @@ TypeHandle SigPointer::GetTypeHandleThrowing( uint32_t sigLen = sigStart - psig.m_dwLen; PCOR_SIGNATURE sig = (PCOR_SIGNATURE)psig.m_ptr - sigLen; - // Now find an existing function pointer or make a new one - thRet = ClassLoader::LoadFnptrTypeThrowing((BYTE) uCallConv, cArgs, retAndArgTypes, cMods, customMods, fLoadTypes, level); + // Find an existing function pointer or make a new one + thRet = ClassLoader::LoadFnptrTypeThrowing((BYTE) uCallConv, cArgs, retAndArgTypes, cModsAndSeparators, customMods, fLoadTypes, level); #else DacNotImpl(); thRet = TypeHandle(); diff --git a/src/coreclr/vm/typedesc.h b/src/coreclr/vm/typedesc.h index e27c54ee0768a..be465434b52ef 100644 --- a/src/coreclr/vm/typedesc.h +++ b/src/coreclr/vm/typedesc.h @@ -453,9 +453,14 @@ class FnPtrTypeDesc : public TypeDesc BYTE callConv, DWORD numArgs, TypeHandle * retAndArgTypes, - DWORD numMods, - FnPtrTypeDescCustomMod * customModTypes) - : TypeDesc(ELEMENT_TYPE_FNPTR), m_hExposedClassObject(0), m_NumArgs(numArgs), m_CallConv(callConv), m_NumMods(numMods), m_pCustomModTypes(customModTypes) + DWORD numCustomMods, + FnPtrTypeDescCustomMod * customMods) + : TypeDesc(ELEMENT_TYPE_FNPTR), + m_hExposedClassObject(0), + m_NumArgs(numArgs), + m_CallConv(callConv), + m_NumMods(numCustomMods), + m_pCustomMods(customMods) { LIMITED_METHOD_CONTRACT; @@ -500,6 +505,7 @@ class FnPtrTypeDesc : public TypeDesc return PTR_TypeHandle(m_RetAndArgTypes); } + // Includes a separator between each parameter except for the last. DWORD GetNumMods() { LIMITED_METHOD_CONTRACT; @@ -507,12 +513,12 @@ class FnPtrTypeDesc : public TypeDesc return m_NumMods; } - FnPtrTypeDescCustomMod* GetCustomModTypesPointer() + FnPtrTypeDescCustomMod* GetCustomModsPointer() { LIMITED_METHOD_CONTRACT; SUPPORTS_DAC; - return m_pCustomModTypes; + return m_pCustomMods; } #ifndef DACCESS_COMPILE @@ -567,8 +573,8 @@ class FnPtrTypeDesc : public TypeDesc DWORD m_CallConv; // Custom modifiers - DWORD m_NumMods; - FnPtrTypeDescCustomMod* m_pCustomModTypes; + DWORD m_NumMods; // Includes a separator between each parameter except for the last. + FnPtrTypeDescCustomMod* m_pCustomMods; // Return type first, then argument types TypeHandle m_RetAndArgTypes[1]; diff --git a/src/coreclr/vm/typehandle.cpp b/src/coreclr/vm/typehandle.cpp index ed5f6c947d2dc..e7c2402e297de 100644 --- a/src/coreclr/vm/typehandle.cpp +++ b/src/coreclr/vm/typehandle.cpp @@ -1463,7 +1463,7 @@ TypeKey TypeHandle::GetTypeKey() const pFTD->GetNumArgs(), pFTD->GetRetAndArgTypesPointer(), pFTD->GetNumMods(), - pFTD->GetCustomModTypesPointer()); + pFTD->GetCustomModsPointer()); return tk; } } diff --git a/src/coreclr/vm/typehash.cpp b/src/coreclr/vm/typehash.cpp index 60e995db370e5..686be7ebca0b9 100644 --- a/src/coreclr/vm/typehash.cpp +++ b/src/coreclr/vm/typehash.cpp @@ -319,13 +319,13 @@ EETypeHashEntry_t *EETypeHashTable::FindItem(TypeKey* pKey) BYTE callConv = pKey->GetCallConv(); DWORD numArgs = pKey->GetNumArgs(); TypeHandle *retAndArgTypes = pKey->GetRetAndArgTypes(); - DWORD numMods = pKey->GetNumMods(); - FnPtrTypeDescCustomMod *customModTypes = pKey->GetCustomModTypes(); + DWORD numCustomMods = pKey->GetNumMods(); + FnPtrTypeDescCustomMod *customMods = pKey->GetCustomMods(); pSearch = BaseFindFirstEntryByHash(dwHash, &sContext); while (pSearch) { - if (CompareFnPtrType(pSearch->GetTypeHandle(), callConv, numArgs, retAndArgTypes, numMods, customModTypes)) + if (CompareFnPtrType(pSearch->GetTypeHandle(), callConv, numArgs, retAndArgTypes, numCustomMods, customMods)) { result = pSearch; break; @@ -444,7 +444,13 @@ BOOL EETypeHashTable::CompareInstantiatedType(TypeHandle t, Module *pModule, mdT } // See also TypeKey::Equals for similar comparison logic. -BOOL EETypeHashTable::CompareFnPtrType(TypeHandle t, BYTE callConv, DWORD numArgs, TypeHandle *retAndArgTypes, DWORD numMods, FnPtrTypeDescCustomMod *customModTypes) +BOOL EETypeHashTable::CompareFnPtrType( + TypeHandle t, + BYTE callConv, + DWORD numArgs, + TypeHandle *retAndArgTypes, + DWORD numModsAndSeperators, + FnPtrTypeDescCustomMod *customMods) { CONTRACTL { @@ -454,7 +460,7 @@ BOOL EETypeHashTable::CompareFnPtrType(TypeHandle t, BYTE callConv, DWORD numArg MODE_ANY; PRECONDITION(CheckPointer(t)); PRECONDITION(CheckPointer(retAndArgTypes)); - PRECONDITION(CheckPointer(customModTypes, NULL_OK)); + PRECONDITION(CheckPointer(customMods, NULL_OK)); SUPPORTS_DAC; } CONTRACTL_END @@ -479,16 +485,16 @@ BOOL EETypeHashTable::CompareFnPtrType(TypeHandle t, BYTE callConv, DWORD numArg } } - if (numMods != pTD->GetNumMods()) + if (numModsAndSeperators != pTD->GetNumMods()) { return FALSE; } - FnPtrTypeDescCustomMod *customModTypes2 = pTD->GetCustomModTypesPointer(); - for (DWORD i = 0; i < numMods; i++) + FnPtrTypeDescCustomMod *customModTypes2 = pTD->GetCustomModsPointer(); + for (DWORD i = 0; i < numModsAndSeperators; i++) { - if ((customModTypes2[i].elementType != customModTypes[i].elementType) || - (customModTypes2[i].typeHandle != customModTypes[i].typeHandle)) + if ((customModTypes2[i].elementType != customMods[i].elementType) || + (customModTypes2[i].typeHandle != customMods[i].typeHandle)) { return FALSE; } diff --git a/src/coreclr/vm/typehash.h b/src/coreclr/vm/typehash.h index f41917a0308f2..6ed10e0b91885 100644 --- a/src/coreclr/vm/typehash.h +++ b/src/coreclr/vm/typehash.h @@ -136,7 +136,7 @@ class EETypeHashTable : public DacEnumerableHashTableu.asFnPtr.m_numMods; - if (numMods != pKey2->u.asFnPtr.m_numMods) + uint32_t numCustomMods = pKey1->u.asFnPtr.m_numMods; + if (numCustomMods != pKey2->u.asFnPtr.m_numMods) { return FALSE; } - for (DWORD i = 0; i < numMods; i++) + for (DWORD i = 0; i < numCustomMods; i++) { - FnPtrTypeDescCustomMod customModTypes1 = pKey1->u.asFnPtr.m_pCustomModTypes[i]; - FnPtrTypeDescCustomMod customModTypes2 = pKey2->u.asFnPtr.m_pCustomModTypes[i]; + FnPtrTypeDescCustomMod customModTypes1 = pKey1->u.asFnPtr.m_pCustomMods[i]; + FnPtrTypeDescCustomMod customModTypes2 = pKey2->u.asFnPtr.m_pCustomMods[i]; if ((customModTypes1.elementType != customModTypes2.elementType) || (customModTypes1.typeHandle != customModTypes2.typeHandle)) diff --git a/src/coreclr/vm/typestring.cpp b/src/coreclr/vm/typestring.cpp index d9e7302684474..29b5d7efad031 100644 --- a/src/coreclr/vm/typestring.cpp +++ b/src/coreclr/vm/typestring.cpp @@ -791,7 +791,7 @@ void TypeString::AppendType(TypeNameBuilder& tnb, TypeHandle ty, Instantiation t else if (ty.IsFnPtrType()) { // Currently function pointers return NULL for FullName and AssemblyQualifiedName and "*()" for Name. - // We need a grammar update in order to support parsing (see https://docs.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/specifying-fully-qualified-type-names) + // We need a grammar update in order to support parsing (see https://docs.microsoft.com/dotnet/framework/reflection-and-codedom/specifying-fully-qualified-type-names) if (format & FormatNamespace) { FnPtrTypeDesc* fnPtr = ty.AsFnPtrType(); diff --git a/src/libraries/Common/tests/System/FunctionPointerTests.cs b/src/libraries/Common/tests/System/FunctionPointerTests.cs index d4982f0fae291..63fa6d1e7abf1 100644 --- a/src/libraries/Common/tests/System/FunctionPointerTests.cs +++ b/src/libraries/Common/tests/System/FunctionPointerTests.cs @@ -14,7 +14,8 @@ 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/71095", TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))] public static unsafe void TestTypeMembers() { // Get an arbitrary function pointer @@ -25,14 +26,13 @@ public static unsafe void TestTypeMembers() Assert.Null(t.FullName); Assert.Null(t.AssemblyQualifiedName); Assert.Equal("*()", t.Name); - Assert.Equal("System", t.Namespace); // All function pointers report "System" + Assert.Null(t.Namespace); Assert.True(t.IsFunctionPointer); Assert.False(t.IsPointer); // A function pointer is not compatible with IsPointer semantics. Assert.False(t.IsUnmanagedFunctionPointer); - Assert.NotNull(t.Module); - Assert.NotNull(t.Assembly); // Common for all function pointers: + Assert.NotNull(t.Assembly); Assert.Equal(TypeAttributes.Public, t.Attributes); Assert.Null(t.BaseType); Assert.False(t.ContainsGenericParameters); @@ -106,6 +106,7 @@ public static unsafe void TestTypeMembers() 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); @@ -118,7 +119,8 @@ private static void MyMethod(){} [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)] + [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 TestNonFunctionPointerThrows() { Assert.Throws(() => typeof(int).GetFunctionPointerCallingConventions()); @@ -127,7 +129,8 @@ public static unsafe void TestNonFunctionPointerThrows() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)] + [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 @@ -148,7 +151,8 @@ public static unsafe void TestToString() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)] + [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 TestFunctionPointerReturn() { Type t = typeof(FunctionPointerHolder).Project(); @@ -165,7 +169,8 @@ public static unsafe void TestFunctionPointerReturn() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)] + [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 TestFunctionUnmanagedPointerReturn_DifferentReturnValue() { Type t = typeof(FunctionPointerHolder).Project(); @@ -181,7 +186,8 @@ public static unsafe void TestFunctionUnmanagedPointerReturn_DifferentReturnValu } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)] + [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 TestFunctionUnmanagedPointerReturn_DifferentCallingConventions() { Type t = typeof(FunctionPointerHolder).Project(); @@ -207,6 +213,23 @@ public static unsafe void TestFunctionUnmanagedPointerReturn_DifferentCallingCon Assert.Equal(2, modOpts.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 TestRequiredModifiers() + { + Type t = typeof(FunctionPointerHolder).Project(); + MethodInfo m = t.GetMethod(nameof(FunctionPointerHolder.RequiredModifiers), Bindings); + Type fcnPtr1 = m.ReturnType; + + FunctionPointerParameterInfo[] parameters = fcnPtr1.GetFunctionPointerParameterInfos(); + 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()", @@ -217,7 +240,8 @@ public static unsafe void TestFunctionUnmanagedPointerReturn_DifferentCallingCon "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/71095", TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))] public static unsafe void TestMethod( string methodName, string methodToStringPostfix, @@ -232,7 +256,6 @@ public static unsafe void TestMethod( Type fnPtrType = m.ReturnType; Assert.Null(fnPtrType.FullName); Assert.Null(fnPtrType.AssemblyQualifiedName); - Assert.Equal("System", fnPtrType.Namespace); Assert.Equal("*()", fnPtrType.Name); VerifyArg(fnPtrType.GetFunctionPointerReturnParameter(), expectedFcnPtrReturnName); @@ -253,7 +276,8 @@ static void VerifyArg(FunctionPointerParameterInfo paramInfo, string expected) [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/71095", TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))] public static unsafe void TestProperty(string name, string expectedToString) { Type t = typeof(FunctionPointerHolder).Project(); @@ -268,7 +292,8 @@ public static unsafe void TestProperty(string name, string expectedToString) [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/71095", TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))] public static unsafe void TestField(string name, string expectedToString) { Type t = typeof(FunctionPointerHolder).Project(); @@ -285,7 +310,6 @@ private static void VerifyFieldOrProperty(Type fnPtrType) Assert.Null(fnPtrType.FullName); Assert.Null(fnPtrType.AssemblyQualifiedName); Assert.Equal("*()", fnPtrType.Name); - Assert.Equal("System", fnPtrType.Namespace); Assert.Null(fnPtrType.DeclaringType); Assert.Null(fnPtrType.BaseType); Assert.Equal(Type.EmptyTypes, fnPtrType.GetFunctionPointerCallingConventions()); @@ -293,7 +317,8 @@ private static void VerifyFieldOrProperty(Type fnPtrType) } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)] + [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 TestManagedCallingConvention() { Type t = typeof(FunctionPointerHolder).Project(); @@ -311,10 +336,11 @@ public static unsafe void TestManagedCallingConvention() [Theory] [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Cdecl), typeof(CallConvCdecl))] - [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Fastcall), typeof(CallConvFastcall))] [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Stdcall), typeof(CallConvStdcall))] [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Thiscall), typeof(CallConvThiscall))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)] + [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 TestBaseCallingConventions(string methodName, Type callingConventionRuntime) { Type callingConvention = callingConventionRuntime.Project(); @@ -335,10 +361,11 @@ public static unsafe void TestBaseCallingConventions(string methodName, Type cal [Theory] [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Cdecl_SuppressGCTransition), typeof(CallConvCdecl))] - [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Fastcall_SuppressGCTransition), typeof(CallConvFastcall))] [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Stdcall_SuppressGCTransition), typeof(CallConvStdcall))] [InlineData(nameof(FunctionPointerHolder.MethodCallConv_Thiscall_SuppressGCTransition), typeof(CallConvThiscall))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)] + [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 TestOptionalCallingConventions(string methodName, Type callingConventionRuntime) { Type suppressGcTransitionType = typeof(CallConvSuppressGCTransition).Project(); @@ -363,7 +390,8 @@ public static unsafe void TestOptionalCallingConventions(string methodName, Type [Theory] [InlineData(nameof(FunctionPointerHolder.Field_Int), nameof(FunctionPointerHolder.Field_DateOnly))] [InlineData(nameof(FunctionPointerHolder.Field_DateOnly), nameof(FunctionPointerHolder.Field_Int))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)] + [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 TestSigEqualityInDifferentModule_Field(string name, string otherName) { Type fph1 = typeof(FunctionPointerHolder).Project(); @@ -382,7 +410,8 @@ public static unsafe void TestSigEqualityInDifferentModule_Field(string name, st [Theory] [InlineData(nameof(FunctionPointerHolder.Prop_Int), nameof(FunctionPointerHolder.Prop_DateOnly))] [InlineData(nameof(FunctionPointerHolder.Prop_DateOnly), nameof(FunctionPointerHolder.Prop_Int))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)] + [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 TestSigEqualityInDifferentModule_Property(string name, string otherName) { Type fph1 = typeof(FunctionPointerHolder).Project(); @@ -401,7 +430,8 @@ public static unsafe void TestSigEqualityInDifferentModule_Property(string name, [Theory] [InlineData(nameof(FunctionPointerHolder.MethodReturnValue_Int), nameof(FunctionPointerHolder.MethodReturnValue_DateOnly))] [InlineData(nameof(FunctionPointerHolder.MethodReturnValue_DateOnly), nameof(FunctionPointerHolder.MethodReturnValue_Int))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)] + [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 TestSigEqualityInDifferentModule_MethodReturn(string name, string otherName) { Type fph1 = typeof(FunctionPointerHolder).Project(); @@ -417,7 +447,6 @@ public static unsafe void TestSigEqualityInDifferentModule_MethodReturn(string n static Type GetFuncPtr(Type owner, string name) => owner.GetMethod(name, Bindings).ReturnParameter.ParameterType; } - // Tests to add: required modifiers, ask for optional return modifiers before\after calling conventions public unsafe class FunctionPointerHolder { public delegate* ToString_1; @@ -446,11 +475,11 @@ public unsafe class FunctionPointerHolder public delegate* unmanaged MethodUnmanagedReturnValue1() => default; public delegate* unmanaged MethodUnmanagedReturnValue2() => default; - public delegate* unmanaged[Cdecl, MemberFunction] MethodUnmanagedReturnValue_DifferentCallingConventions1() => default; public delegate* unmanaged[Stdcall, MemberFunction] MethodUnmanagedReturnValue_DifferentCallingConventions2() => default; public delegate* unmanaged[Stdcall, MemberFunction] SeveralArguments() => default; + public delegate* RequiredModifiers() => default; // Methods to verify calling conventions and synthesized modopts. // The non-SuppressGCTransition variants are encoded with the CallKind byte. @@ -458,12 +487,12 @@ public unsafe class FunctionPointerHolder 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_Fastcall(delegate* unmanaged[Fastcall] f) { } - public void MethodCallConv_Fastcall_SuppressGCTransition(delegate* unmanaged[Fastcall, 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 struct MyStruct { } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.cs index cf0ce2bcd31c7..d228bec1ba8b6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/FunctionPointerInfo.cs @@ -44,11 +44,10 @@ internal void ComputeCallingConventions() { Type type = modifiers[i]; allBuilder.Add(type); - if (type.FullName!.StartsWith(CallingConventionTypePrefix)) + if (type.FullName!.StartsWith(CallingConventionTypePrefix, StringComparison.Ordinal)) { ccBuilder.Add(type); - // todo: use StartsWith(CallingConventionTypePrefix) to get any new calling conventions if (type == typeof(CallConvCdecl) || type == typeof(CallConvFastcall) || type == typeof(CallConvStdcall) || @@ -70,15 +69,15 @@ internal void ComputeCallingConventions() case MdSigCallingConvention.C: callConv = typeof(CallConvCdecl); break; - case MdSigCallingConvention.FastCall: - callConv = typeof(CallConvFastcall); - break; case MdSigCallingConvention.StdCall: callConv = typeof(CallConvStdcall); break; case MdSigCallingConvention.ThisCall: callConv = typeof(CallConvThiscall); break; + case MdSigCallingConvention.FastCall: + callConv = typeof(CallConvFastcall); + break; } if (callConv != null) diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/CoreType.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/CoreType.cs index 1ff1c2d470651..656102c8e7ed3 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/CoreType.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/CoreType.cs @@ -55,9 +55,9 @@ internal enum CoreType // For calling convention processing CallConvCdecl, - CallConvFastcall, CallConvStdcall, CallConvThiscall, + CallConvFastcall, // Pseudo Custom Attributes ComImportAttribute, @@ -126,9 +126,9 @@ public static void GetFullName(this CoreType coreType, out ReadOnlySpan ns case CoreType.PreserveSigAttribute: ns = Utf8Constants.SystemRuntimeInteropServices; name = Utf8Constants.PreserveSigAttribute; return; case CoreType.FieldOffsetAttribute: ns = Utf8Constants.SystemRuntimeInteropServices; name = Utf8Constants.FieldOffsetAttribute; return; case CoreType.CallConvCdecl: ns = Utf8Constants.SystemRuntimeCompilerServices; name = Utf8Constants.CallConvCdecl; return; - case CoreType.CallConvFastcall: ns = Utf8Constants.SystemRuntimeCompilerServices; name = Utf8Constants.CallConvFastcall; return; case CoreType.CallConvStdcall: ns = Utf8Constants.SystemRuntimeCompilerServices; name = Utf8Constants.CallConvStdcall; return; case CoreType.CallConvThiscall: ns = Utf8Constants.SystemRuntimeCompilerServices; name = Utf8Constants.CallConvThiscall; return; + case CoreType.CallConvFastcall: ns = Utf8Constants.SystemRuntimeCompilerServices; name = Utf8Constants.CallConvFastcall; return; default: Debug.Fail("Unexpected coreType passed to GetCoreTypeFullName: " + coreType); ns = name = default; diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Utf8Constants.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Utf8Constants.cs index cc05d0718545a..22ef7ebdbdd47 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Utf8Constants.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Utf8Constants.cs @@ -61,8 +61,8 @@ internal static class Utf8Constants public static ReadOnlySpan DefaultMemberAttribute => "DefaultMemberAttribute"u8; public static ReadOnlySpan DateTimeConstantAttribute => "DateTimeConstantAttribute"u8; public static ReadOnlySpan CallConvCdecl => "CallConvCdecl"u8; - public static ReadOnlySpan CallConvFastcall => "CallConvFastcall"u8; public static ReadOnlySpan CallConvStdcall => "CallConvStdcall"u8; public static ReadOnlySpan CallConvThiscall => "CallConvThiscall"u8; + public static ReadOnlySpan CallConvFastcall => "CallConvFastcall"u8; } } diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Types/RoFunctionPointerType.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Types/RoFunctionPointerType.cs index 51df47daa0566..17981d99f3a83 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Types/RoFunctionPointerType.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Types/RoFunctionPointerType.cs @@ -100,10 +100,6 @@ private Type[] ComputeCallingConventions() callConv = Loader.GetCoreType(CoreType.CallConvCdecl); unmanaged = true; break; - case SignatureCallingConvention.FastCall: - callConv = Loader.GetCoreType(CoreType.CallConvFastcall); - unmanaged = true; - break; case SignatureCallingConvention.StdCall: callConv = Loader.GetCoreType(CoreType.CallConvStdcall); unmanaged = true; @@ -112,6 +108,10 @@ private Type[] ComputeCallingConventions() callConv = Loader.GetCoreType(CoreType.CallConvThiscall); unmanaged = true; break; + case SignatureCallingConvention.FastCall: + callConv = Loader.GetCoreType(CoreType.CallConvFastcall); + unmanaged = true; + break; case SignatureCallingConvention.Unmanaged: // There is no CallConvUnmanaged type. unmanaged = true; @@ -157,7 +157,7 @@ private void AppendParameters(StringBuilder sb) } } - protected sealed override string? ComputeNamespace() => typeof(Type).Namespace; + protected sealed override string? ComputeNamespace() => null; public sealed override string ToString() => GetToString(); public sealed override string? AssemblyQualifiedName => null; internal MethodSignature Signature => _signature; diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportAnalyzerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportAnalyzerTests.cs index 0ca217b9918e4..da3ce81fd3a4c 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportAnalyzerTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportAnalyzerTests.cs @@ -35,6 +35,7 @@ public static IEnumerable NoMarshallingRequiredTypes() => new[] new object[] { typeof(int*) }, new object[] { typeof(bool*) }, new object[] { typeof(char*) }, + new object[] { typeof(delegate* ) }, new object[] { typeof(IntPtr) }, new object[] { typeof(ConsoleKey) }, // enum }; diff --git a/src/libraries/System.Runtime/System.Runtime.sln b/src/libraries/System.Runtime/System.Runtime.sln index 06ef4eecf43a8..9980e0c11b3d3 100644 --- a/src/libraries/System.Runtime/System.Runtime.sln +++ b/src/libraries/System.Runtime/System.Runtime.sln @@ -418,6 +418,12 @@ Global {C230AC88-A377-4BEB-824F-AB174C14DC86}.Release|x64.Build.0 = Release|Any CPU {C230AC88-A377-4BEB-824F-AB174C14DC86}.Release|x86.ActiveCfg = Release|Any CPU {C230AC88-A377-4BEB-824F-AB174C14DC86}.Release|x86.Build.0 = Release|Any CPU + {C230AC88-A377-4BEB-824F-AB174C14DC86}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {C230AC88-A377-4BEB-824F-AB174C14DC86}.Checked|Any CPU.Build.0 = Debug|Any CPU + {C230AC88-A377-4BEB-824F-AB174C14DC86}.Checked|x64.ActiveCfg = Debug|Any CPU + {C230AC88-A377-4BEB-824F-AB174C14DC86}.Checked|x64.Build.0 = Debug|Any CPU + {C230AC88-A377-4BEB-824F-AB174C14DC86}.Checked|x86.ActiveCfg = Debug|Any CPU + {C230AC88-A377-4BEB-824F-AB174C14DC86}.Checked|x86.Build.0 = Debug|Any CPU {1BCCD2F5-A561-4641-8A0B-51F3EDCA35DC}.Checked|Any CPU.ActiveCfg = Debug|Any CPU {1BCCD2F5-A561-4641-8A0B-51F3EDCA35DC}.Checked|Any CPU.Build.0 = Debug|Any CPU {1BCCD2F5-A561-4641-8A0B-51F3EDCA35DC}.Checked|x64.ActiveCfg = Debug|Any CPU From 93900f1d996e468dc2b836504a3da26186838e2b Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Sat, 9 Jul 2022 18:33:08 -0500 Subject: [PATCH 5/9] Disable a couple tests --- src/libraries/System.Reflection/tests/MethodInfoTests.cs | 1 + .../ConvertToLibraryImportAnalyzerTests.cs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Reflection/tests/MethodInfoTests.cs b/src/libraries/System.Reflection/tests/MethodInfoTests.cs index 5754d46fdc46f..2874e98a2f2f9 100644 --- a/src/libraries/System.Reflection/tests/MethodInfoTests.cs +++ b/src/libraries/System.Reflection/tests/MethodInfoTests.cs @@ -853,6 +853,7 @@ private static void SecondCall(MethodInfo mi) } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/71883", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))] private static unsafe void TestFunctionPointers() { void* fn = FunctionPointerMethods.GetFunctionPointer(); diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportAnalyzerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportAnalyzerTests.cs index da3ce81fd3a4c..9f2966143321e 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportAnalyzerTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportAnalyzerTests.cs @@ -35,7 +35,8 @@ public static IEnumerable NoMarshallingRequiredTypes() => new[] new object[] { typeof(int*) }, new object[] { typeof(bool*) }, new object[] { typeof(char*) }, - new object[] { typeof(delegate* ) }, + // See issue https://github.com/dotnet/runtime/issues/71891 + // new object[] { typeof(delegate* ) }, new object[] { typeof(IntPtr) }, new object[] { typeof(ConsoleKey) }, // enum }; From e796d3e82cc9c00b8c30c4a0b5bd9fb6c61d8fb0 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Sat, 9 Jul 2022 19:50:17 -0500 Subject: [PATCH 6/9] Disable a test --- .../System.Runtime/tests/System/Reflection/TypeDelegatorTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Runtime/tests/System/Reflection/TypeDelegatorTests.cs b/src/libraries/System.Runtime/tests/System/Reflection/TypeDelegatorTests.cs index 9856193cc665c..bdf0f5453b6c1 100644 --- a/src/libraries/System.Runtime/tests/System/Reflection/TypeDelegatorTests.cs +++ b/src/libraries/System.Runtime/tests/System/Reflection/TypeDelegatorTests.cs @@ -59,6 +59,7 @@ public void Properties() [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 void FunctionPointers() { Assert.True(new TypeDelegator(typeof(delegate* unmanaged)).IsClass); From 0eeea90706ed45b7ba539106c973c41985a3b1a9 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Sun, 10 Jul 2022 14:08:12 -0500 Subject: [PATCH 7/9] Fix loop issue --- src/coreclr/vm/clsload.cpp | 2 +- src/coreclr/vm/typehash.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/vm/clsload.cpp b/src/coreclr/vm/clsload.cpp index 1f84cc20950e7..d30a6b142b28a 100644 --- a/src/coreclr/vm/clsload.cpp +++ b/src/coreclr/vm/clsload.cpp @@ -2973,7 +2973,7 @@ TypeHandle ClassLoader::CreateTypeHandleForTypeKey(TypeKey* pKey, AllocMemTracke modTypes = (FnPtrTypeDescCustomMod*) pamTracker->Track(loaderHeap->AllocMem( S_SIZE_T(sizeof(FnPtrTypeDescCustomMod)) * S_SIZE_T(numCustomMods))); - for (DWORD i = 0; i <= numCustomMods; i++) + for (DWORD i = 0; i < numCustomMods; i++) { modTypes[i] = srcMods[i]; } diff --git a/src/coreclr/vm/typehash.cpp b/src/coreclr/vm/typehash.cpp index 686be7ebca0b9..4008a1b10ae5f 100644 --- a/src/coreclr/vm/typehash.cpp +++ b/src/coreclr/vm/typehash.cpp @@ -449,7 +449,7 @@ BOOL EETypeHashTable::CompareFnPtrType( BYTE callConv, DWORD numArgs, TypeHandle *retAndArgTypes, - DWORD numModsAndSeperators, + DWORD numMods, FnPtrTypeDescCustomMod *customMods) { CONTRACTL @@ -485,13 +485,13 @@ BOOL EETypeHashTable::CompareFnPtrType( } } - if (numModsAndSeperators != pTD->GetNumMods()) + if (numMods != pTD->GetNumMods()) { return FALSE; } FnPtrTypeDescCustomMod *customModTypes2 = pTD->GetCustomModsPointer(); - for (DWORD i = 0; i < numModsAndSeperators; i++) + for (DWORD i = 0; i < numMods; i++) { if ((customModTypes2[i].elementType != customMods[i].elementType) || (customModTypes2[i].typeHandle != customMods[i].typeHandle)) From 7c73b8acaa23e728c856bbd647632ef053c6b526 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Sun, 10 Jul 2022 18:21:11 -0500 Subject: [PATCH 8/9] Use PeekByte instead of GetByte --- src/coreclr/vm/siginfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/siginfo.cpp b/src/coreclr/vm/siginfo.cpp index f5b6aa2b01bed..94472af9fae46 100644 --- a/src/coreclr/vm/siginfo.cpp +++ b/src/coreclr/vm/siginfo.cpp @@ -1633,7 +1633,7 @@ TypeHandle SigPointer::GetTypeHandleThrowing( cMods++; psigTemp.SkipBytes(1); IfFailThrowBF(psigTemp.GetToken(&tk), BFA_BAD_SIGNATURE, pOrigModule); - IfFailThrowBF(psigTemp.GetByte(&data), BFA_BAD_SIGNATURE, pOrigModule); + IfFailThrowBF(psigTemp.PeekByte(&data), BFA_BAD_SIGNATURE, pOrigModule); etyp = (CorElementType)data; } while (etyp == ELEMENT_TYPE_CMOD_OPT || etyp == ELEMENT_TYPE_CMOD_REQD); } From b48bcc38bbec1120b0727aa2f4bda863df9dfff2 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Sun, 10 Jul 2022 21:55:33 -0500 Subject: [PATCH 9/9] Remove unused code --- src/coreclr/vm/clsload.cpp | 8 ++++---- src/coreclr/vm/siginfo.cpp | 16 ++++++---------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/coreclr/vm/clsload.cpp b/src/coreclr/vm/clsload.cpp index d30a6b142b28a..ef6843deaed74 100644 --- a/src/coreclr/vm/clsload.cpp +++ b/src/coreclr/vm/clsload.cpp @@ -2965,17 +2965,17 @@ TypeHandle ClassLoader::CreateTypeHandleForTypeKey(TypeKey* pKey, AllocMemTracke S_SIZE_T(sizeof(TypeHandle)) * S_SIZE_T(numArgs))); DWORD numCustomMods = pKey->GetNumMods(); - FnPtrTypeDescCustomMod* modTypes = NULL; + FnPtrTypeDescCustomMod* dstMods = NULL; if (numCustomMods) { FnPtrTypeDescCustomMod* srcMods = pKey->GetCustomMods(); - modTypes = (FnPtrTypeDescCustomMod*) pamTracker->Track(loaderHeap->AllocMem( + dstMods = (FnPtrTypeDescCustomMod*) pamTracker->Track(loaderHeap->AllocMem( S_SIZE_T(sizeof(FnPtrTypeDescCustomMod)) * S_SIZE_T(numCustomMods))); for (DWORD i = 0; i < numCustomMods; i++) { - modTypes[i] = srcMods[i]; + dstMods[i] = srcMods[i]; } } @@ -2984,7 +2984,7 @@ TypeHandle ClassLoader::CreateTypeHandleForTypeKey(TypeKey* pKey, AllocMemTracke numArgs, pKey->GetRetAndArgTypes(), numCustomMods, - modTypes)); + dstMods)); } else { diff --git a/src/coreclr/vm/siginfo.cpp b/src/coreclr/vm/siginfo.cpp index 94472af9fae46..39b68a4d8dec7 100644 --- a/src/coreclr/vm/siginfo.cpp +++ b/src/coreclr/vm/siginfo.cpp @@ -1592,7 +1592,6 @@ TypeHandle SigPointer::GetTypeHandleThrowing( case ELEMENT_TYPE_FNPTR: { #ifndef DACCESS_COMPILE - uint32_t sigStart = psig.m_dwLen; uint32_t uCallConv = 0; IfFailThrowBF(psig.GetData(&uCallConv), BFA_BAD_SIGNATURE, pOrigModule); @@ -1640,12 +1639,12 @@ TypeHandle SigPointer::GetTypeHandleThrowing( // Lookup type handle. retAndArgTypes[i] = psig.GetTypeHandleThrowing(pOrigModule, - pTypeContext, - fLoadTypes, - level, - dropGenericArgumentLevel, - pSubst, - pZapSigContext); + pTypeContext, + fLoadTypes, + level, + dropGenericArgumentLevel, + pSubst, + pZapSigContext); if (retAndArgTypes[i].IsNull()) { @@ -1722,9 +1721,6 @@ TypeHandle SigPointer::GetTypeHandleThrowing( _ASSERT(iCurrent = cMods + cArgs); } - uint32_t sigLen = sigStart - psig.m_dwLen; - PCOR_SIGNATURE sig = (PCOR_SIGNATURE)psig.m_ptr - sigLen; - // Find an existing function pointer or make a new one thRet = ClassLoader::LoadFnptrTypeThrowing((BYTE) uCallConv, cArgs, retAndArgTypes, cModsAndSeparators, customMods, fLoadTypes, level); #else