Skip to content

Commit

Permalink
Convert a few reflection FCalls to C# (#92512)
Browse files Browse the repository at this point in the history
* Convert a few reflection FCalls to C#

* Use IsActualValueType in more places

* Delete fixed TODO

* Better comment
  • Loading branch information
jkotas authored Sep 23, 2023
1 parent 9356b47 commit d45854c
Show file tree
Hide file tree
Showing 18 changed files with 124 additions and 188 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ public override void SetValue(object? obj, object? value, BindingFlags invokeAtt
RuntimeType fieldType = (RuntimeType)FieldType;
if (value is null)
{
if (RuntimeTypeHandle.IsValueType(fieldType))
if (fieldType.IsActualValueType)
{
fieldType.CheckValue(ref value, binder, culture, invokeAttr);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ private static CustomAttributeEncoding TypeToCustomAttributeEncoding(RuntimeType
if (type == typeof(int))
return CustomAttributeEncoding.Int32;

if (type.IsEnum)
if (type.IsActualEnum)
return CustomAttributeEncoding.Enum;

if (type == typeof(string))
Expand Down Expand Up @@ -172,7 +172,7 @@ private static CustomAttributeEncoding TypeToCustomAttributeEncoding(RuntimeType
if (type.IsInterface)
return CustomAttributeEncoding.Object;

if (type.IsValueType)
if (type.IsActualValueType)
return CustomAttributeEncoding.Undefined;

throw new ArgumentException(SR.Argument_InvalidKindOfTypeForCA, nameof(type));
Expand Down Expand Up @@ -1477,7 +1477,7 @@ internal static object[] CreateAttributeArrayHelper(RuntimeType caType, int elem
{
useAttributeArray = true;
}
else if (caType.IsValueType)
else if (caType.IsActualValueType)
{
useObjectArray = true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,7 @@ internal unsafe struct MethodTable
private const uint enum_flag_GenericsMask_SharedInst = 0x00000020; // shared instantiation, e.g. List<__Canon> or List<MyValueType<__Canon>>
private const uint enum_flag_GenericsMask_TypicalInst = 0x00000030; // the type instantiated at its formal parameters, e.g. List<T>
private const uint enum_flag_HasDefaultCtor = 0x00000200;
private const uint enum_flag_IsByRefLike = 0x00001000;

// WFLAGS_HIGH_ENUM
private const uint enum_flag_ContainsPointers = 0x01000000;
Expand All @@ -487,6 +488,7 @@ internal unsafe struct MethodTable
private const uint enum_flag_Category_ValueType = 0x00040000;
private const uint enum_flag_Category_Nullable = 0x00050000;
private const uint enum_flag_Category_ValueType_Mask = 0x000C0000;
private const uint enum_flag_Category_Interface = 0x000C0000;
// Types that require non-trivial interface cast have this bit set in the category
private const uint enum_flag_NonTrivialInterfaceCast = 0x00080000 // enum_flag_Category_Array
| 0x40000000 // enum_flag_ComObject
Expand Down Expand Up @@ -555,10 +557,14 @@ public int MultiDimensionalArrayRank
}
}

public bool IsInterface => (Flags & enum_flag_Category_Mask) == enum_flag_Category_Interface;

public bool IsValueType => (Flags & enum_flag_Category_ValueType_Mask) == enum_flag_Category_ValueType;

public bool IsNullable => (Flags & enum_flag_Category_Mask) == enum_flag_Category_Nullable;

public bool IsByRefLike => (Flags & (enum_flag_HasComponentSize | enum_flag_IsByRefLike)) == enum_flag_IsByRefLike;

public bool HasInstantiation => (Flags & enum_flag_HasComponentSize) == 0 && (Flags & enum_flag_GenericsMask) != enum_flag_GenericsMask_NonGeneric;

public bool IsGenericTypeDefinition => (Flags & (enum_flag_HasComponentSize | enum_flag_GenericsMask)) == enum_flag_GenericsMask_TypicalInst;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -463,12 +463,6 @@ internal static bool IsComObject(RuntimeType type, bool isGenericCOM)
#endif
}

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool IsInterface(RuntimeType type);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool IsByRefLike(RuntimeType type);

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeTypeHandle_IsVisible")]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool _IsVisible(QCallTypeHandle typeHandle);
Expand All @@ -478,9 +472,6 @@ internal static bool IsVisible(RuntimeType type)
return _IsVisible(new QCallTypeHandle(ref type));
}

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool IsValueType(RuntimeType type);

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeTypeHandle_ConstructName")]
private static partial void ConstructName(QCallTypeHandle handle, TypeNameFormatFlags formatFlags, StringHandleOnStack retString);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ private unsafe RuntimeMethodInfo[] PopulateMethods(Filter filter)
RuntimeType declaringType = ReflectedType;
Debug.Assert(declaringType != null);

if (RuntimeTypeHandle.IsInterface(declaringType))
if (declaringType.IsInterface)
{
#region IsInterface

Expand Down Expand Up @@ -653,7 +653,7 @@ private unsafe RuntimeMethodInfo[] PopulateMethods(Filter filter)
bool* overrides = stackalloc bool[numVirtuals];
new Span<bool>(overrides, numVirtuals).Clear();

bool isValueType = declaringType.IsValueType;
bool isValueType = declaringType.IsActualValueType;

do
{
Expand Down Expand Up @@ -1144,7 +1144,7 @@ private RuntimeEventInfo[] PopulateEvents(Filter filter)
RuntimeType declaringType = ReflectedType;
ListBuilder<RuntimeEventInfo> list = default;

if (!RuntimeTypeHandle.IsInterface(declaringType))
if (!declaringType.IsInterface)
{
while (RuntimeTypeHandle.IsGenericVariable(declaringType))
declaringType = declaringType.GetBaseType()!;
Expand Down Expand Up @@ -1234,7 +1234,7 @@ private RuntimePropertyInfo[] PopulateProperties(Filter filter)

ListBuilder<RuntimePropertyInfo> list = default;

if (!RuntimeTypeHandle.IsInterface(declaringType))
if (!declaringType.IsInterface)
{
while (RuntimeTypeHandle.IsGenericVariable(declaringType))
declaringType = declaringType.GetBaseType()!;
Expand Down Expand Up @@ -3382,6 +3382,19 @@ protected override unsafe bool IsValueTypeImpl()
return isValueType;
}

// This returns true for actual value types only, ignoring generic parameter constraints.
internal unsafe bool IsActualValueType
{
get
{
TypeHandle th = GetNativeTypeHandle();

bool isValueType = !th.IsTypeDesc && th.AsMethodTable()->IsValueType;
GC.KeepAlive(this);
return isValueType;
}
}

public override unsafe bool IsEnum
{
get
Expand All @@ -3398,7 +3411,7 @@ public override unsafe bool IsEnum
}
}

// This returns true for actual enum types only.
// This returns true for actual enum types only, ignoring generic parameter constraints.
internal unsafe bool IsActualEnum
{
[Intrinsic]
Expand All @@ -3412,6 +3425,30 @@ internal unsafe bool IsActualEnum
}
}

internal new unsafe bool IsInterface
{
get
{
TypeHandle th = GetNativeTypeHandle();

bool isInterface = !th.IsTypeDesc && th.AsMethodTable()->IsInterface;
GC.KeepAlive(this);
return isInterface;
}
}

public override unsafe bool IsByRefLike
{
get
{
TypeHandle th = GetNativeTypeHandle();

bool isByRefLike = !th.IsTypeDesc && th.AsMethodTable()->IsByRefLike;
GC.KeepAlive(this);
return isByRefLike;
}
}

internal unsafe bool IsDelegate()
{
TypeHandle th = GetNativeTypeHandle();
Expand Down
10 changes: 0 additions & 10 deletions src/coreclr/System.Private.CoreLib/src/System/Type.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,6 @@ namespace System
{
public abstract partial class Type : MemberInfo, IReflect
{
public bool IsInterface
{
get
{
if (this is RuntimeType rt)
return RuntimeTypeHandle.IsInterface(rt);
return (GetAttributeFlagsImpl() & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface;
}
}

[RequiresUnreferencedCode("The type might be removed")]
[DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
public static Type? GetType(string typeName, bool throwOnError, bool ignoreCase)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,6 @@
<Compile Include="System\Threading\SyncTable.cs" />
<Compile Include="System\Threading\Thread.NativeAot.cs" />
<Compile Include="System\Type.NativeAot.cs" />
<Compile Include="System\Type.Internal.cs" />
<Compile Include="System\TypedReference.cs" />
<Compile Include="System\TypeUnificationKey.cs" />
<Compile Include="System\TypeLoadException.NativeAot.cs" />
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// 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.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
Expand All @@ -18,8 +19,6 @@ namespace System
{
public abstract partial class Type : MemberInfo, IReflect
{
public bool IsInterface => (GetAttributeFlagsImpl() & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface;

[Intrinsic]
public static unsafe Type? GetTypeFromHandle(RuntimeTypeHandle handle) => handle.IsNull ? null : GetTypeFromMethodTable(handle.ToMethodTable());

Expand Down Expand Up @@ -65,6 +64,54 @@ private static unsafe Type GetTypeFromMethodTableSlow(MethodTable* pMT, ref GCHa
return result;
}

internal EETypePtr GetEEType()
{
RuntimeTypeHandle typeHandle = RuntimeAugments.Callbacks.GetTypeHandleIfAvailable(this);
Debug.Assert(!typeHandle.IsNull);
return typeHandle.ToEETypePtr();
}

internal bool TryGetEEType(out EETypePtr eeType)
{
RuntimeTypeHandle typeHandle = RuntimeAugments.Callbacks.GetTypeHandleIfAvailable(this);
if (typeHandle.IsNull)
{
eeType = default(EETypePtr);
return false;
}
eeType = typeHandle.ToEETypePtr();
return true;
}

//
// This is a port of the desktop CLR's RuntimeType.FormatTypeName() routine. This routine is used by various Reflection ToString() methods
// to display the name of a type. Do not use for any other purpose as it inherits some pretty quirky desktop behavior.
//
internal string FormatTypeNameForReflection()
{
// Legacy: this doesn't make sense, why use only Name for nested types but otherwise
// ToString() which contains namespace.
Type rootElementType = this;
while (rootElementType.HasElementType)
rootElementType = rootElementType.GetElementType()!;
if (rootElementType.IsNested)
{
return Name!;
}

// Legacy: why removing "System"? Is it just because C# has keywords for these types?
// If so why don't we change it to lower case to match the C# keyword casing?
string typeName = ToString();
if (typeName.StartsWith("System."))
{
if (rootElementType.IsPrimitive || rootElementType == typeof(void))
{
typeName = typeName.Substring("System.".Length);
}
}
return typeName;
}

[Intrinsic]
[RequiresUnreferencedCode("The type might be removed")]
public static Type GetType(string typeName) => GetType(typeName, throwOnError: false, ignoreCase: false);
Expand Down
3 changes: 0 additions & 3 deletions src/coreclr/vm/ecalllist.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,6 @@ FCFuncStart(gCOMTypeHandleFuncs)
FCFuncElement("_GetMetadataImport", RuntimeTypeHandle::GetMetadataImport)
FCFuncElement("GetNumVirtuals", RuntimeTypeHandle::GetNumVirtuals)
FCFuncElement("GetNumVirtualsAndStaticVirtuals", RuntimeTypeHandle::GetNumVirtualsAndStaticVirtuals)
FCFuncElement("IsValueType", RuntimeTypeHandle::IsValueType)
FCFuncElement("IsInterface", RuntimeTypeHandle::IsInterface)
FCFuncElement("IsByRefLike", RuntimeTypeHandle::IsByRefLike)
FCFuncElement("CanCastTo", RuntimeTypeHandle::CanCastTo)
FCFuncElement("GetGenericVariableIndex", RuntimeTypeHandle::GetGenericVariableIndex)
FCFuncElement("IsGenericVariable", RuntimeTypeHandle::IsGenericVariable)
Expand Down
Loading

0 comments on commit d45854c

Please sign in to comment.