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