Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 36 additions & 2 deletions src/libraries/Common/tests/System/FunctionPointerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()",
Expand Down Expand Up @@ -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*<void> ToString_1;
public delegate*unmanaged<void> ToString_2;
public delegate*<int> ToString_3;
Expand All @@ -283,7 +311,12 @@ private unsafe class FunctionPointerHolder

public delegate* managed<int> Field_Int;
public delegate* managed<MyClass> Field_MyClass;
#pragma warning restore 0649

// Fields for visibility tests
public delegate* managed<int, void> Field_PublicParam;
private delegate* managed<PrivateClass> Field_PrivateReturn;
private delegate* managed<PrivateClass, void> Field_PrivateParam;
#pragma warning restore 0649, 0169

public delegate* managed<int> Prop_Int { get; }
public delegate* managed<MyClass> Prop_MyClass { get; }
Expand All @@ -299,6 +332,7 @@ private unsafe class FunctionPointerHolder

public class MyClass { }
public struct MyStruct { }
private class PrivateClass { }
}
}
}
28 changes: 28 additions & 0 deletions src/libraries/System.Private.CoreLib/src/System/Type.Helpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1509,6 +1509,87 @@ public enum NestedEnum
C
}
}

/// <summary>
/// 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.
/// </summary>
private sealed class MockFunctionPointerType : 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(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(typeof(string), Type.EmptyTypes);
Assert.False(fnPtrNonGeneric.ContainsGenericParameters);

// Function pointer with generic return type should contain generic parameters
var fnPtrGenericReturn = new MockFunctionPointerType(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(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(typeof(string), new Type[] { typeof(int) });
Assert.False(fnPtrAllNonGeneric.ContainsGenericParameters);
}
}

public class NonGenericClass { }
Expand Down
Loading