Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fast FieldInfo reflection #98199

Merged
merged 12 commits into from
Feb 14, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -18,46 +18,18 @@ internal sealed unsafe class RtFieldInfo : RuntimeFieldInfo, IRuntimeFieldInfo
// lazy caching
private string? m_name;
private RuntimeType? m_fieldType;
private InvocationFlags m_invocationFlags;
internal InvocationFlags InvocationFlags
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => (m_invocationFlags & InvocationFlags.Initialized) != 0 ?
m_invocationFlags : InitializeInvocationFlags();
}
private FieldAccessor? m_fieldAccessor;

[MethodImpl(MethodImplOptions.NoInlining)]
private InvocationFlags InitializeInvocationFlags()
internal FieldAccessor FieldAccessor
{
Type? declaringType = DeclaringType;

InvocationFlags invocationFlags = 0;

// first take care of all the NO_INVOKE cases
if (declaringType != null && declaringType.ContainsGenericParameters)
{
invocationFlags |= InvocationFlags.NoInvoke;
}

// If the invocationFlags are still 0, then
// this should be an usable field, determine the other flags
if (invocationFlags == 0)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if ((m_fieldAttributes & FieldAttributes.InitOnly) != 0)
invocationFlags |= InvocationFlags.SpecialField;

if ((m_fieldAttributes & FieldAttributes.HasFieldRVA) != 0)
invocationFlags |= InvocationFlags.SpecialField;

// find out if the field type is one of the following: Primitive, Enum or Pointer
Type fieldType = FieldType;
if (fieldType.IsPointer || fieldType.IsEnum || fieldType.IsPrimitive)
invocationFlags |= InvocationFlags.FieldSpecialCast;
m_fieldAccessor ??= new FieldAccessor(this);
return m_fieldAccessor;
}

// must be last to avoid threading problems
return m_invocationFlags = invocationFlags | InvocationFlags.Initialized;
}

#endregion

#region Constructor
Expand All @@ -75,28 +47,6 @@ internal RtFieldInfo(
#endregion

#region Internal Members
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void CheckConsistency(object? target)
{
// only test instance fields
if ((m_fieldAttributes & FieldAttributes.Static) != FieldAttributes.Static)
{
if (!m_declaringType.IsInstanceOfType(target))
{
if (target == null)
{
throw new TargetException(SR.RFLCT_Targ_StatFldReqTarg);
}
else
{
throw new ArgumentException(
SR.Format(SR.Arg_FieldDeclTarget,
Name, m_declaringType, target.GetType()));
}
}
}
}

internal override bool CacheEquals(object? o)
{
return o is RtFieldInfo m && m.m_fieldHandle == m_fieldHandle;
Expand Down Expand Up @@ -131,36 +81,7 @@ public override int GetHashCode() =>
#region FieldInfo Overrides
[DebuggerStepThrough]
[DebuggerHidden]
public override object? GetValue(object? obj)
{
InvocationFlags invocationFlags = InvocationFlags;
RuntimeType? declaringType = DeclaringType as RuntimeType;

if ((invocationFlags & InvocationFlags.NoInvoke) != 0)
{
if (declaringType != null && DeclaringType!.ContainsGenericParameters)
throw new InvalidOperationException(SR.Arg_UnboundGenField);

throw new FieldAccessException();
}

CheckConsistency(obj);

RuntimeType fieldType = (RuntimeType)FieldType;

bool domainInitialized = false;
if (declaringType == null)
{
return RuntimeFieldHandle.GetValue(this, obj, fieldType, null, ref domainInitialized);
}
else
{
domainInitialized = declaringType.DomainInitialized;
object? retVal = RuntimeFieldHandle.GetValue(this, obj, fieldType, declaringType, ref domainInitialized);
declaringType.DomainInitialized = domainInitialized;
return retVal;
}
}
public override object? GetValue(object? obj) => FieldAccessor.GetValue(obj);

public override object GetRawConstantValue() { throw new InvalidOperationException(); }

Expand All @@ -180,45 +101,7 @@ public override int GetHashCode() =>
[DebuggerStepThrough]
[DebuggerHidden]
public override void SetValue(object? obj, object? value, BindingFlags invokeAttr, Binder? binder, CultureInfo? culture)
{
InvocationFlags invocationFlags = InvocationFlags;
RuntimeType? declaringType = DeclaringType as RuntimeType;

if ((invocationFlags & InvocationFlags.NoInvoke) != 0)
{
if (declaringType != null && declaringType.ContainsGenericParameters)
throw new InvalidOperationException(SR.Arg_UnboundGenField);

throw new FieldAccessException();
}

CheckConsistency(obj);

RuntimeType fieldType = (RuntimeType)FieldType;
if (value is null)
{
if (fieldType.IsActualValueType)
{
fieldType.CheckValue(ref value, binder, culture, invokeAttr);
}
}
else if (!ReferenceEquals(value.GetType(), fieldType))
{
fieldType.CheckValue(ref value, binder, culture, invokeAttr);
}

bool domainInitialized = false;
if (declaringType is null)
{
RuntimeFieldHandle.SetValue(this, obj, value, fieldType, m_fieldAttributes, null, ref domainInitialized);
}
else
{
domainInitialized = declaringType.DomainInitialized;
RuntimeFieldHandle.SetValue(this, obj, value, fieldType, m_fieldAttributes, declaringType, ref domainInitialized);
declaringType.DomainInitialized = domainInitialized;
}
}
=> FieldAccessor.SetValue(obj, value, invokeAttr, binder, culture);

[DebuggerStepThrough]
[DebuggerHidden]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal abstract class RuntimeFieldInfo : FieldInfo
#region Private Data Members
private readonly BindingFlags m_bindingFlags;
protected readonly RuntimeTypeCache m_reflectedTypeCache;
protected readonly RuntimeType m_declaringType;
protected internal readonly RuntimeType m_declaringType;
#endregion

#region Constructor
Expand Down
12 changes: 11 additions & 1 deletion src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1087,6 +1087,7 @@ public RuntimeFieldInfoStub(RuntimeFieldHandleInternal fieldHandle, object keepa
private object? m_d;
private int m_b;
private object? m_e;
private object? m_f;
private RuntimeFieldHandleInternal m_fieldHandle;
#pragma warning restore 414, 169, IDE0044

Expand Down Expand Up @@ -1189,6 +1190,15 @@ internal static RuntimeType GetApproxDeclaringType(IRuntimeFieldInfo field)
return type;
}

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool IsFastPathSupported(RtFieldInfo field);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern int GetInstanceFieldOffset(RtFieldInfo field);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern IntPtr GetStaticFieldAddress(RtFieldInfo field);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern int GetToken(RtFieldInfo field);

Expand All @@ -1199,7 +1209,7 @@ internal static RuntimeType GetApproxDeclaringType(IRuntimeFieldInfo field)
internal static extern object? GetValueDirect(RtFieldInfo field, RuntimeType fieldType, void* pTypedRef, RuntimeType? contextType);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void SetValue(RtFieldInfo field, object? obj, object? value, RuntimeType fieldType, FieldAttributes fieldAttr, RuntimeType? declaringType, ref bool domainInitialized);
internal static extern void SetValue(RtFieldInfo field, object? obj, object? value, RuntimeType fieldType, RuntimeType? declaringType, ref bool domainInitialized);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void SetValueDirect(RtFieldInfo field, RuntimeType fieldType, void* pTypedRef, object? value, RuntimeType? contextType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1438,7 +1438,6 @@ internal T[] GetMemberList(MemberListType listType, string? name, CacheType cach
private string? m_toString;
private string? m_namespace;
private readonly bool m_isGlobal;
private bool m_bIsDomainInitialized;
private MemberInfoCache<RuntimeMethodInfo>? m_methodInfoCache;
private MemberInfoCache<RuntimeConstructorInfo>? m_constructorInfoCache;
private MemberInfoCache<RuntimeFieldInfo>? m_fieldInfoCache;
Expand Down Expand Up @@ -1523,12 +1522,6 @@ internal Type[] FunctionPointerReturnAndParameterTypes
}
}

internal bool DomainInitialized
{
get => m_bIsDomainInitialized;
set => m_bIsDomainInitialized = value;
}

internal string? GetName(TypeNameKind kind)
{
switch (kind)
Expand Down Expand Up @@ -1935,12 +1928,6 @@ internal object? GenericCache
set => Cache.GenericCache = value;
}

internal bool DomainInitialized
{
get => Cache.DomainInitialized;
set => Cache.DomainInitialized = value;
}

internal static FieldInfo GetFieldInfo(IRuntimeFieldInfo fieldHandle)
{
return GetFieldInfo(RuntimeFieldHandle.GetApproxDeclaringType(fieldHandle), fieldHandle);
Expand Down
8 changes: 4 additions & 4 deletions src/coreclr/vm/corelib.h
Original file line number Diff line number Diff line change
Expand Up @@ -337,13 +337,13 @@ DEFINE_FIELD(RT_TYPE_HANDLE, M_TYPE, m_type)
DEFINE_CLASS(TYPE_NAME_PARSER, Reflection, TypeNameParser)
DEFINE_METHOD(TYPE_NAME_PARSER, GET_TYPE_HELPER, GetTypeHelper, SM_Type_CharPtr_RuntimeAssembly_Bool_Bool_RetRuntimeType)

DEFINE_CLASS_U(Reflection, RtFieldInfo, NoClass)
DEFINE_FIELD_U(m_fieldHandle, ReflectFieldObject, m_pFD)
DEFINE_CLASS_U(Reflection, RtFieldInfo, NoClass)
DEFINE_FIELD_U(m_fieldHandle, ReflectFieldObject, m_pFD)
DEFINE_CLASS(RT_FIELD_INFO, Reflection, RtFieldInfo)
DEFINE_FIELD(RT_FIELD_INFO, HANDLE, m_fieldHandle)

DEFINE_CLASS_U(System, RuntimeFieldInfoStub, ReflectFieldObject)
DEFINE_FIELD_U(m_fieldHandle, ReflectFieldObject, m_pFD)
DEFINE_CLASS_U(System, RuntimeFieldInfoStub, ReflectFieldObject)
DEFINE_FIELD_U(m_fieldHandle, ReflectFieldObject, m_pFD)
DEFINE_CLASS(STUBFIELDINFO, System, RuntimeFieldInfoStub)
#if FOR_ILLINK
DEFINE_METHOD(STUBFIELDINFO, CTOR, .ctor, IM_RetVoid)
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/vm/ecalllist.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,9 @@ FCFuncStart(gCOMFieldHandleNewFuncs)
FCFuncElement("GetStaticFieldForGenericType", RuntimeFieldHandle::GetStaticFieldForGenericType)
FCFuncElement("AcquiresContextFromThis", RuntimeFieldHandle::AcquiresContextFromThis)
FCFuncElement("GetLoaderAllocator", RuntimeFieldHandle::GetLoaderAllocator)
FCFuncElement("IsFastPathSupported", RuntimeFieldHandle::IsFastPathSupported)
FCFuncElement("GetInstanceFieldOffset", RuntimeFieldHandle::GetInstanceFieldOffset)
FCFuncElement("GetStaticFieldAddress", RuntimeFieldHandle::GetStaticFieldAddress)
FCFuncEnd()

FCFuncStart(gCOMModuleHandleFuncs)
Expand Down
12 changes: 4 additions & 8 deletions src/coreclr/vm/field.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,8 @@ void* FieldDesc::GetStaticAddress(void *base)

void* ret = GetStaticAddressHandle(base); // Get the handle

// For value classes, the handle points at an OBJECTREF
// which holds the boxed value class, so dereference and unbox.
// For value classes, the handle points at an OBJECTREF
// which holds the boxed value class, so dereference and unbox.
if (GetFieldType() == ELEMENT_TYPE_VALUETYPE && !IsRVA())
{
OBJECTREF obj = ObjectToOBJECTREF(*(Object**) ret);
Expand Down Expand Up @@ -211,11 +211,10 @@ MethodTable * FieldDesc::GetExactDeclaringType(MethodTable * ownerOrSubType)

#endif // #ifndef DACCESS_COMPILE

// static value classes are actually stored in their boxed form.
// this means that their address moves.
// Static value classes are actually stored in their boxed form.
// This means that their address moves.
PTR_VOID FieldDesc::GetStaticAddressHandle(PTR_VOID base)
{

CONTRACTL
{
INSTANCE_CHECK;
Expand Down Expand Up @@ -255,7 +254,6 @@ PTR_VOID FieldDesc::GetStaticAddressHandle(PTR_VOID base)
}
#endif // FEATURE_METADATA_UPDATER


if (IsRVA())
{
Module* pModule = GetModule();
Expand All @@ -270,12 +268,10 @@ PTR_VOID FieldDesc::GetStaticAddressHandle(PTR_VOID base)

PTR_VOID ret = PTR_VOID(dac_cast<PTR_BYTE>(base) + GetOffset());


return ret;
}



// These routines encapsulate the operation of getting and setting
// fields.
void FieldDesc::GetInstanceField(OBJECTREF o, VOID * pOutVal)
Expand Down
8 changes: 8 additions & 0 deletions src/coreclr/vm/field.h
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,14 @@ class FieldDesc
SetOffset(FIELD_OFFSET_NEW_ENC);
}

BOOL IsCollectible()
{
LIMITED_METHOD_DAC_CONTRACT;

LoaderAllocator *pLoaderAllocatorOfMethod = GetApproxEnclosingMethodTable()->GetLoaderAllocator();
return pLoaderAllocatorOfMethod->IsCollectible();
steveharter marked this conversation as resolved.
Show resolved Hide resolved
}

// Was this field added by EnC?
// If this is true, then this object is an instance of EnCFieldDesc
BOOL IsEnCNew()
Expand Down
8 changes: 3 additions & 5 deletions src/coreclr/vm/invokeutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -741,7 +741,7 @@ void InvokeUtil::ValidateObjectTarget(FieldDesc *pField, TypeHandle enclosingTyp

// SetValidField
// Given an target object, a value object and a field this method will set the field
// on the target object. The field must be validate before calling this.
// on the target object. The field must be validated before calling this.
void InvokeUtil::SetValidField(CorElementType fldType,
TypeHandle fldTH,
FieldDesc *pField,
Expand Down Expand Up @@ -971,8 +971,6 @@ void InvokeUtil::SetValidField(CorElementType fldType,
}
}

// GetFieldValue
// This method will return an ARG_SLOT containing the value of the field.
// GetFieldValue
// This method will return an ARG_SLOT containing the value of the field.
OBJECTREF InvokeUtil::GetFieldValue(FieldDesc* pField, TypeHandle fieldType, OBJECTREF* target, TypeHandle declaringType, CLR_BOOL *pDomainInitialized) {
Expand All @@ -999,7 +997,7 @@ OBJECTREF InvokeUtil::GetFieldValue(FieldDesc* pField, TypeHandle fieldType, OBJ
{
pDeclMT = declaringType.GetMethodTable();

// We don't allow getting the field just so we don't have more specical
// We don't allow getting the field just so we don't have more special
// cases than we need to. Then we need at least the throw check to ensure
// we don't allow data corruption.
if (Nullable::IsNullableType(pDeclMT))
Expand Down Expand Up @@ -1084,7 +1082,7 @@ OBJECTREF InvokeUtil::GetFieldValue(FieldDesc* pField, TypeHandle fieldType, OBJ

case ELEMENT_TYPE_VALUETYPE:
{
// Value classes require createing a boxed version of the field and then
// Value classes require creating a boxed version of the field and then
// copying from the source...
// Allocate an object to return...
_ASSERTE(!fieldType.IsTypeDesc());
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -1130,6 +1130,7 @@ class ReflectFieldObject : public BaseObjectWithCachedData
INT32 m_empty2;
OBJECTREF m_empty3;
OBJECTREF m_empty4;
OBJECTREF m_empty5;
FieldDesc * m_pFD;

public:
Expand Down
Loading
Loading