Skip to content

Commit

Permalink
Fast FieldInfo GetValue and SetValue (#98199)
Browse files Browse the repository at this point in the history
  • Loading branch information
steveharter authored Feb 14, 2024
1 parent c858f50 commit d3ebc97
Show file tree
Hide file tree
Showing 18 changed files with 975 additions and 249 deletions.
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
14 changes: 12 additions & 2 deletions 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,17 +1190,26 @@ 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);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern object? GetValue(RtFieldInfo field, object? instance, RuntimeType fieldType, RuntimeType? declaringType, ref bool domainInitialized);
internal static extern object? GetValue(RtFieldInfo field, object? instance, RuntimeType fieldType, RuntimeType? declaringType, ref bool isClassInitialized);

[MethodImpl(MethodImplOptions.InternalCall)]
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 isClassInitialized);

[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
10 changes: 9 additions & 1 deletion 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 *pLoaderAllocator = GetApproxEnclosingMethodTable()->GetLoaderAllocator();
return pLoaderAllocator->IsCollectible();
}

// Was this field added by EnC?
// If this is true, then this object is an instance of EnCFieldDesc
BOOL IsEnCNew()
Expand Down Expand Up @@ -518,7 +526,7 @@ class FieldDesc
}
}

VOID CheckRunClassInitThrowing()
void CheckRunClassInitThrowing()
{
CONTRACTL
{
Expand Down
Loading

0 comments on commit d3ebc97

Please sign in to comment.