diff --git a/src/libraries/Common/tests/System/FunctionPointerTests.cs b/src/libraries/Common/tests/System/FunctionPointerTests.cs index 8a344e01e78955..eb6d82e117e270 100644 --- a/src/libraries/Common/tests/System/FunctionPointerTests.cs +++ b/src/libraries/Common/tests/System/FunctionPointerTests.cs @@ -189,6 +189,32 @@ public static unsafe void GenericFunctionPointer() Assert.True(fcnPtr2.ContainsGenericParameters); } + [Fact] + public static unsafe void FunctionPointerVisibility() + { + Type t = typeof(FunctionPointerHolder).Project(); + + // A function pointer with public return type should be visible + Type fnPtrPublicReturn = t.GetField(nameof(FunctionPointerHolder.Field_Int), Bindings)!.FieldType; + Assert.True(fnPtrPublicReturn.IsFunctionPointer); + Assert.True(fnPtrPublicReturn.IsVisible); + + // A function pointer with public parameter types should be visible + Type fnPtrPublicParams = t.GetField(nameof(FunctionPointerHolder.Field_PublicParam), Bindings)!.FieldType; + Assert.True(fnPtrPublicParams.IsFunctionPointer); + Assert.True(fnPtrPublicParams.IsVisible); + + // A function pointer with private return type should not be visible + Type fnPtrPrivateReturn = t.GetField("Field_PrivateReturn", Bindings)!.FieldType; + Assert.True(fnPtrPrivateReturn.IsFunctionPointer); + Assert.False(fnPtrPrivateReturn.IsVisible); + + // A function pointer with private parameter type should not be visible + Type fnPtrPrivateParam = t.GetField("Field_PrivateParam", Bindings)!.FieldType; + Assert.True(fnPtrPrivateParam.IsFunctionPointer); + Assert.False(fnPtrPrivateParam.IsVisible); + } + [Theory] [InlineData(nameof(FunctionPointerHolder.MethodReturnValue1), "MethodReturnValue1()", @@ -270,7 +296,9 @@ private static void VerifyFieldOrProperty(Type fnPtrType) private unsafe class FunctionPointerHolder { -#pragma warning disable 0649 + // CS0649: Field is never assigned to (fields are used via reflection) + // CS0169: Field is never used (private fields are accessed via reflection) +#pragma warning disable 0649, 0169 public delegate* ToString_1; public delegate*unmanaged ToString_2; public delegate* ToString_3; @@ -283,7 +311,12 @@ private unsafe class FunctionPointerHolder public delegate* managed Field_Int; public delegate* managed Field_MyClass; -#pragma warning restore 0649 + + // Fields for visibility tests + public delegate* managed Field_PublicParam; + private delegate* managed Field_PrivateReturn; + private delegate* managed Field_PrivateParam; +#pragma warning restore 0649, 0169 public delegate* managed Prop_Int { get; } public delegate* managed Prop_MyClass { get; } @@ -299,6 +332,7 @@ private unsafe class FunctionPointerHolder public class MyClass { } public struct MyStruct { } + private class PrivateClass { } } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Type.Helpers.cs b/src/libraries/System.Private.CoreLib/src/System/Type.Helpers.cs index 9b9fb68afc0a64..053c6f48f32f96 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Type.Helpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Type.Helpers.cs @@ -49,6 +49,20 @@ public virtual bool ContainsGenericParameters if (IsGenericParameter) return true; + if (IsFunctionPointer) + { + if (GetFunctionPointerReturnType().ContainsGenericParameters) + return true; + + foreach (Type parameterType in GetFunctionPointerParameterTypes()) + { + if (parameterType.ContainsGenericParameters) + return true; + } + + return false; + } + if (!IsGenericType) return false; @@ -88,6 +102,20 @@ public bool IsVisible if (HasElementType) return GetElementType()!.IsVisible; + if (IsFunctionPointer) + { + if (!GetFunctionPointerReturnType().IsVisible) + return false; + + foreach (Type parameterType in GetFunctionPointerParameterTypes()) + { + if (!parameterType.IsVisible) + return false; + } + + return true; + } + Type type = this; while (type.IsNested) { diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Type/TypeTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Type/TypeTests.cs index e800d331b0bf66..2424af0f4fbe91 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Type/TypeTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Type/TypeTests.cs @@ -1509,6 +1509,87 @@ public enum NestedEnum C } } + + /// + /// A mock function pointer type used to test the abstract ContainsGenericParameters implementation in Type.Helpers.cs. + /// This inherits from Type directly (not MockType) so that it uses the base ContainsGenericParameters implementation. + /// + private sealed class MockFunctionPointerType_ContainsGenericParameters : Type + { + private readonly Type _returnType; + private readonly Type[] _parameterTypes; + private static readonly Exception s_unexpected = new Exception("Did not expect to be called."); + + public MockFunctionPointerType_ContainsGenericParameters(Type returnType, Type[] parameterTypes) + { + _returnType = returnType; + _parameterTypes = parameterTypes ?? Type.EmptyTypes; + } + + // Required for ContainsGenericParameters to work correctly + protected override bool HasElementTypeImpl() => false; + public override bool IsGenericParameter => false; + public override bool IsFunctionPointer => true; + public override Type GetFunctionPointerReturnType() => _returnType; + public override Type[] GetFunctionPointerParameterTypes() => _parameterTypes; + + // Required abstract members + public override Assembly Assembly => throw s_unexpected; + public override string AssemblyQualifiedName => throw s_unexpected; + public override Type BaseType => throw s_unexpected; + public override string FullName => throw s_unexpected; + public override Guid GUID => throw s_unexpected; + public override Module Module => throw s_unexpected; + public override string Namespace => throw s_unexpected; + public override Type UnderlyingSystemType => throw s_unexpected; + public override string Name => throw s_unexpected; + public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) => throw s_unexpected; + public override object[] GetCustomAttributes(bool inherit) => throw s_unexpected; + public override object[] GetCustomAttributes(Type attributeType, bool inherit) => throw s_unexpected; + public override Type GetElementType() => throw s_unexpected; + public override EventInfo GetEvent(string name, BindingFlags bindingAttr) => throw s_unexpected; + public override EventInfo[] GetEvents(BindingFlags bindingAttr) => throw s_unexpected; + public override FieldInfo GetField(string name, BindingFlags bindingAttr) => throw s_unexpected; + public override FieldInfo[] GetFields(BindingFlags bindingAttr) => throw s_unexpected; + public override Type GetInterface(string name, bool ignoreCase) => throw s_unexpected; + public override Type[] GetInterfaces() => throw s_unexpected; + public override MemberInfo[] GetMembers(BindingFlags bindingAttr) => throw s_unexpected; + public override MethodInfo[] GetMethods(BindingFlags bindingAttr) => throw s_unexpected; + public override Type GetNestedType(string name, BindingFlags bindingAttr) => throw s_unexpected; + public override Type[] GetNestedTypes(BindingFlags bindingAttr) => throw s_unexpected; + public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) => throw s_unexpected; + public override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, System.Globalization.CultureInfo culture, string[] namedParameters) => throw s_unexpected; + public override bool IsDefined(Type attributeType, bool inherit) => throw s_unexpected; + protected override TypeAttributes GetAttributeFlagsImpl() => throw s_unexpected; + protected override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) => throw s_unexpected; + protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) => throw s_unexpected; + protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) => throw s_unexpected; + protected override bool IsArrayImpl() => throw s_unexpected; + protected override bool IsByRefImpl() => throw s_unexpected; + protected override bool IsCOMObjectImpl() => throw s_unexpected; + protected override bool IsPointerImpl() => throw s_unexpected; + protected override bool IsPrimitiveImpl() => throw s_unexpected; + } + + [Fact] + public static void FunctionPointerContainsGenericParameters() + { + // Function pointer with non-generic return type and no parameters should not contain generic parameters + var fnPtrNonGeneric = new MockFunctionPointerType_ContainsGenericParameters(typeof(string), Type.EmptyTypes); + Assert.False(fnPtrNonGeneric.ContainsGenericParameters); + + // Function pointer with generic return type should contain generic parameters + var fnPtrGenericReturn = new MockFunctionPointerType_ContainsGenericParameters(typeof(List<>), Type.EmptyTypes); + Assert.True(fnPtrGenericReturn.ContainsGenericParameters); + + // Function pointer with non-generic return type but generic parameter type should contain generic parameters + var fnPtrGenericParam = new MockFunctionPointerType_ContainsGenericParameters(typeof(string), new Type[] { typeof(List<>) }); + Assert.True(fnPtrGenericParam.ContainsGenericParameters); + + // Function pointer with non-generic return type and non-generic parameter type should not contain generic parameters + var fnPtrAllNonGeneric = new MockFunctionPointerType_ContainsGenericParameters(typeof(string), new Type[] { typeof(int) }); + Assert.False(fnPtrAllNonGeneric.ContainsGenericParameters); + } } public class NonGenericClass { }