From 62d8c716366711b68264f0eebee494e38059318c Mon Sep 17 00:00:00 2001 From: Atsushi Kanamori Date: Wed, 15 Mar 2017 08:45:25 -0700 Subject: [PATCH] Move MemberInfo to shared partition There are really two easy changes here, just to reduce CR overhead. - CoreRt side of https://github.com/dotnet/coreclr/pull/10167 - Now that we have the shiny new IsConstructedGenericType api, let's use it... --- .../Reflection/Augments/ReflectionAugments.cs | 2 + .../Runtime/Augments/RuntimeAugments.cs | 42 ++++++++ .../src/Resources/Strings.resx | 5 +- .../src/System.Private.CoreLib.csproj | 1 + .../src/System/InvokeUtils.cs | 16 ++- .../src/System/Reflection/FieldInfo.cs | 5 + .../src/System/TypedReference.cs | 100 ++++++++++++++++++ .../Core/Execution/ExecutionEnvironment.cs | 1 + .../Core/Execution/FieldAccessor.cs | 9 ++ .../src/Resources/Strings.resx | 21 ++++ .../src/System.Private.Reflection.Core.csproj | 1 - .../FieldInfos/LiteralFieldAccessor.cs | 41 ------- .../Runtime/FieldInfos/RuntimeFieldInfo.cs | 25 ++++- .../ReflectionCoreCallbacksImplementation.cs | 40 +++++++ ...cutionEnvironmentImplementation.Runtime.cs | 26 +++-- .../FieldAccessors/InstanceFieldAccessor.cs | 53 ++++++++++ .../FieldAccessors/LiteralFieldAccessor.cs | 33 ++++++ ...renceTypeFieldAccessorForInstanceFields.cs | 12 +++ ...ferenceTypeFieldAccessorForStaticFields.cs | 7 +- ...eTypeFieldAccessorForThreadStaticFields.cs | 7 +- ...dAccessorForUniversalThreadStaticFields.cs | 7 +- .../FieldAccessors/StaticFieldAccessor.cs | 32 +++++- ...ValueTypeFieldAccessorForInstanceFields.cs | 12 +++ .../ValueTypeFieldAccessorForStaticFields.cs | 7 +- ...eTypeFieldAccessorForThreadStaticFields.cs | 7 +- ...dAccessorForUniversalThreadStaticFields.cs | 7 +- .../WritableStaticFieldAccessor.cs | 34 ++++++ .../src/Resources/Strings.resx | 3 + ...System.Private.Reflection.Execution.csproj | 2 + 29 files changed, 476 insertions(+), 82 deletions(-) create mode 100644 src/System.Private.CoreLib/src/System/TypedReference.cs delete mode 100644 src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/LiteralFieldAccessor.cs create mode 100644 src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/LiteralFieldAccessor.cs create mode 100644 src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/WritableStaticFieldAccessor.cs diff --git a/src/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs b/src/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs index cc66b0d068e..d9669b03a0d 100644 --- a/src/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs +++ b/src/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs @@ -147,5 +147,7 @@ public abstract class ReflectionCoreCallbacks public abstract IntPtr GetFunctionPointer(RuntimeMethodHandle runtimeMethodHandle, RuntimeTypeHandle declaringTypeHandle); public abstract void RunModuleConstructor(Module module); + + public abstract void MakeTypedReference(object target, FieldInfo[] flds, out Type type, out int offset); } } diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs index 509f1d048ed..12bc670ac7e 100644 --- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs +++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs @@ -310,6 +310,41 @@ public static unsafe Object LoadReferenceTypeField(Object obj, int fieldOffset) } } + [CLSCompliant(false)] + public static void StoreValueTypeFieldValueIntoValueType(TypedReference typedReference, int fieldOffset, object fieldValue, RuntimeTypeHandle fieldTypeHandle) + { + Debug.Assert(TypedReference.TargetTypeToken(typedReference).ToEETypePtr().IsValueType); + + RuntimeImports.RhUnbox(fieldValue, ref Unsafe.Add(ref typedReference.Value, fieldOffset), fieldTypeHandle.ToEETypePtr()); + } + + [CLSCompliant(false)] + public static object LoadValueTypeFieldValueFromValueType(TypedReference typedReference, int fieldOffset, RuntimeTypeHandle fieldTypeHandle) + { + Debug.Assert(TypedReference.TargetTypeToken(typedReference).ToEETypePtr().IsValueType); + Debug.Assert(fieldTypeHandle.ToEETypePtr().IsValueType); + + return RuntimeImports.RhBox(fieldTypeHandle.ToEETypePtr(), ref Unsafe.Add(ref typedReference.Value, fieldOffset)); + } + + [CLSCompliant(false)] + public static void StoreReferenceTypeFieldValueIntoValueType(TypedReference typedReference, int fieldOffset, object fieldValue) + { + Debug.Assert(TypedReference.TargetTypeToken(typedReference).ToEETypePtr().IsValueType); + + Unsafe.As(ref Unsafe.Add(ref typedReference.Value, fieldOffset)) = fieldValue; + } + + [CLSCompliant(false)] + public static object LoadReferenceTypeFieldValueFromValueType(TypedReference typedReference, int fieldOffset) + { + Debug.Assert(TypedReference.TargetTypeToken(typedReference).ToEETypePtr().IsValueType); + + return Unsafe.As(ref Unsafe.Add(ref typedReference.Value, fieldOffset)); + } + + public static unsafe int ObjectHeaderSize => sizeof(EETypePtr); + [DebuggerGuidedStepThroughAttribute] public static object CallDynamicInvokeMethod( object thisPtr, @@ -652,6 +687,13 @@ public static Object CheckArgument(Object srcObject, RuntimeTypeHandle dstType, return InvokeUtils.CheckArgument(srcObject, dstType, binderBundle); } + // FieldInfo.SetValueDirect() has a completely different set of rules on how to coerce the argument from + // the other Reflection api. + public static object CheckArgumentForDirectFieldAccess(object srcObject, RuntimeTypeHandle dstType) + { + return InvokeUtils.CheckArgument(srcObject, dstType.ToEETypePtr(), InvokeUtils.CheckArgumentSemantics.SetFieldDirect, binderBundle: null); + } + public static bool IsAssignable(Object srcObject, RuntimeTypeHandle dstType) { EETypePtr srcEEType = srcObject.EETypePtr; diff --git a/src/System.Private.CoreLib/src/Resources/Strings.resx b/src/System.Private.CoreLib/src/Resources/Strings.resx index 7e4005f719c..d22d341bbed 100644 --- a/src/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/System.Private.CoreLib/src/Resources/Strings.resx @@ -2254,4 +2254,7 @@ Type '{0}' in Assembly '{1}' is not marked as serializable. - \ No newline at end of file + + This feature is not currently implemented. + + diff --git a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 07baee8ec2d..c15bb9f687f 100644 --- a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -604,6 +604,7 @@ Interop\Windows\mincore\Interop.CLSIDFromProgID.cs + diff --git a/src/System.Private.CoreLib/src/System/InvokeUtils.cs b/src/System.Private.CoreLib/src/System/InvokeUtils.cs index 6ba582ec56b..f33b4ecd933 100644 --- a/src/System.Private.CoreLib/src/System/InvokeUtils.cs +++ b/src/System.Private.CoreLib/src/System/InvokeUtils.cs @@ -43,11 +43,12 @@ public static Object CheckArgument(Object srcObject, RuntimeTypeHandle dstType, return CheckArgument(srcObject, dstEEType, CheckArgumentSemantics.DynamicInvoke, binderBundle, getExactTypeForCustomBinder: null); } - // This option does nothing but decide which type of exception to throw to match the legacy behavior. + // This option tweaks the coercion rules to match classic inconsistencies. internal enum CheckArgumentSemantics { ArraySet, // Throws InvalidCastException DynamicInvoke, // Throws ArgumentException + SetFieldDirect, // Throws ArgumentException - other than that, like DynamicInvoke except that enums and integers cannot be intermingled, and null cannot substitute for default(valuetype). } internal static Object CheckArgument(Object srcObject, EETypePtr dstEEType, CheckArgumentSemantics semantics, BinderBundle binderBundle, Func getExactTypeForCustomBinder = null) @@ -56,9 +57,15 @@ internal static Object CheckArgument(Object srcObject, EETypePtr dstEEType, Chec { // null -> default(T) if (dstEEType.IsValueType && !dstEEType.IsNullable) + { + if (semantics == CheckArgumentSemantics.SetFieldDirect) + throw CreateChangeTypeException(CommonRuntimeTypes.Object.TypeHandle.ToEETypePtr(), dstEEType, semantics); return Runtime.RuntimeImports.RhNewObject(dstEEType); + } else + { return null; + } } else { @@ -110,6 +117,12 @@ internal static Object CheckArgument(Object srcObject, EETypePtr dstEEType, Chec // Special coersion rules for primitives, enums and pointer. private static Exception ConvertOrWidenPrimitivesEnumsAndPointersIfPossible(object srcObject, EETypePtr srcEEType, EETypePtr dstEEType, CheckArgumentSemantics semantics, out object dstObject) { + if (semantics == CheckArgumentSemantics.SetFieldDirect && (srcEEType.IsEnum || dstEEType.IsEnum)) + { + dstObject = null; + return CreateChangeTypeException(srcEEType, dstEEType, semantics); + } + if (!((srcEEType.IsEnum || srcEEType.IsPrimitive) && (dstEEType.IsEnum || dstEEType.IsPrimitive || dstEEType.IsPointer))) { dstObject = null; @@ -214,6 +227,7 @@ private static Exception CreateChangeTypeException(EETypePtr srcEEType, EETypePt switch (semantics) { case CheckArgumentSemantics.DynamicInvoke: + case CheckArgumentSemantics.SetFieldDirect: return CreateChangeTypeArgumentException(srcEEType, dstEEType); case CheckArgumentSemantics.ArraySet: return CreateChangeTypeInvalidCastException(srcEEType, dstEEType); diff --git a/src/System.Private.CoreLib/src/System/Reflection/FieldInfo.cs b/src/System.Private.CoreLib/src/System/Reflection/FieldInfo.cs index 34ad3f1b7f7..110be32f6b3 100644 --- a/src/System.Private.CoreLib/src/System/Reflection/FieldInfo.cs +++ b/src/System.Private.CoreLib/src/System/Reflection/FieldInfo.cs @@ -61,6 +61,11 @@ protected FieldInfo() { } public void SetValue(object obj, object value) => SetValue(obj, value, BindingFlags.Default, Type.DefaultBinder, null); public abstract void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, CultureInfo culture); + [CLSCompliant(false)] + public virtual void SetValueDirect(TypedReference obj, object value) { throw new NotSupportedException(SR.NotSupported_AbstractNonCLS); } + [CLSCompliant(false)] + public virtual object GetValueDirect(TypedReference obj) { throw new NotSupportedException(SR.NotSupported_AbstractNonCLS); } + public virtual object GetRawConstantValue() { throw new NotSupportedException(SR.NotSupported_AbstractNonCLS); } public virtual Type[] GetOptionalCustomModifiers() { throw NotImplemented.ByDesign; } diff --git a/src/System.Private.CoreLib/src/System/TypedReference.cs b/src/System.Private.CoreLib/src/System/TypedReference.cs new file mode 100644 index 00000000000..603113a5270 --- /dev/null +++ b/src/System.Private.CoreLib/src/System/TypedReference.cs @@ -0,0 +1,100 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Reflection; +using System.Diagnostics; +using System.Runtime; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +using Internal.Runtime.Augments; +using Internal.Reflection.Augments; + +namespace System.Reflection //@TODO: Intentionally placing TypedReference in the wrong namespace to work around NUTC's inability to handle ELEMENT_TYPE_TYPEDBYREF. +{ + [CLSCompliant(false)] + [StructLayout(LayoutKind.Sequential)] + public struct TypedReference + { + // Do not change the ordering of these fields. The JIT has a dependency on this layout. + private readonly ByReferenceOfByte _value; + private readonly RuntimeTypeHandle _typeHandle; + + private TypedReference(object target, int offset, RuntimeTypeHandle typeHandle) + { + //@todo: Once ByReference is fixed, uncomment the following line and delete the one after it. + //_value = new ByReference(ref Unsafe.Add(ref target.GetRawData(), offset)); + _value = new ByReferenceOfByte(target, offset); + _typeHandle = typeHandle; + } + + public static TypedReference MakeTypedReference(object target, FieldInfo[] flds) + { + Type type; + int offset; + ReflectionAugments.ReflectionCoreCallbacks.MakeTypedReference(target, flds, out type, out offset); + return new TypedReference(target, offset, type.TypeHandle); + } + + public static Type GetTargetType(TypedReference value) => Type.GetTypeFromHandle(value._typeHandle); + + public static RuntimeTypeHandle TargetTypeToken(TypedReference value) + { + if (value._typeHandle.IsNull) + throw new NullReferenceException(); // For compatibility; + return value._typeHandle; + } + + public static object ToObject(TypedReference value) + { + RuntimeTypeHandle typeHandle = value._typeHandle; + if (typeHandle.IsNull) + throw new ArgumentNullException(); // For compatibility. + + EETypePtr eeType = typeHandle.ToEETypePtr(); + if (eeType.IsValueType) + { + return RuntimeImports.RhBox(eeType, ref value.Value); + } + else + { + return Unsafe.As(ref value.Value); + } + } + + public static void SetTypedReference(TypedReference target, object value) { throw new NotSupportedException(); } + + public override bool Equals(object o) { throw new NotSupportedException(SR.NotSupported_NYI); } + public override int GetHashCode() => _typeHandle.IsNull ? 0 : _typeHandle.GetHashCode(); + + // Not an api - declared public because of CoreLib/Reflection.Core divide. + public bool IsNull => _typeHandle.IsNull; + + internal ref byte Value + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return ref _value.Value; + } + } + + // @todo: ByReferenceOfByte is a workaround for the fact that ByReference is broken on Project N right now. + // Once that's fixed, delete this class and replace references to ByReferenceOfByte to ByReference. + private struct ByReferenceOfByte + { + public ByReferenceOfByte(object target, int offset) + { + _target = target; + _offset = offset; + } + + public ref byte Value => ref Unsafe.Add(ref Unsafe.As(ref _target.m_pEEType), _offset); + + private readonly object _target; + private readonly int _offset; + } + } +} + diff --git a/src/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/ExecutionEnvironment.cs b/src/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/ExecutionEnvironment.cs index 9aee5d97c45..e5344edf49a 100644 --- a/src/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/ExecutionEnvironment.cs +++ b/src/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/ExecutionEnvironment.cs @@ -110,6 +110,7 @@ public abstract class ExecutionEnvironment //============================================================================================== public abstract MethodInvoker GetSyntheticMethodInvoker(RuntimeTypeHandle thisType, RuntimeTypeHandle[] parameterTypes, InvokerOptions options, Func invoker); public abstract bool IsCOMObject(Type type); + public abstract FieldAccessor CreateLiteralFieldAccessor(object value, RuntimeTypeHandle fieldTypeHandle); //============================================================================================== // Non-public methods diff --git a/src/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/FieldAccessor.cs b/src/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/FieldAccessor.cs index 7fdc0a17147..73e768e0642 100644 --- a/src/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/FieldAccessor.cs +++ b/src/System.Private.Reflection.Core/src/Internal/Reflection/Core/Execution/FieldAccessor.cs @@ -4,6 +4,7 @@ using System; using System.Reflection; +using System.Diagnostics; using System.Collections.Generic; namespace Internal.Reflection.Core.Execution @@ -15,6 +16,14 @@ public abstract class FieldAccessor { protected FieldAccessor() { } public abstract Object GetField(Object obj); + public abstract object GetFieldDirect(TypedReference typedReference); + public abstract void SetField(Object obj, Object value, BinderBundle binderBundle); + public abstract void SetFieldDirect(TypedReference typedReference, object value); + + /// + /// Returns the field offset (asserts and throws if not an instance field). Does not include the size of the object header. + /// + public abstract int Offset { get; } } } diff --git a/src/System.Private.Reflection.Core/src/Resources/Strings.resx b/src/System.Private.Reflection.Core/src/Resources/Strings.resx index f4bc1316518..4d368a698ff 100644 --- a/src/System.Private.Reflection.Core/src/Resources/Strings.resx +++ b/src/System.Private.Reflection.Core/src/Resources/Strings.resx @@ -334,4 +334,25 @@ MethodInfo must be a runtime MethodInfo object. + + Array must not be of length zero. + + + FieldInfo must be a runtime FieldInfo object. + + + Field in TypedReferences cannot be static or init only. + + + FieldInfo does not match the target Type. + + + TypedReferences cannot be redefined as primitives. + + + TypedReference can only be made on nested value Types. + + + The TypedReference must be initialized. + diff --git a/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.csproj b/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.csproj index 8bea1fffb09..eeb0af9d21b 100644 --- a/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.csproj +++ b/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.csproj @@ -106,7 +106,6 @@ - diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/LiteralFieldAccessor.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/LiteralFieldAccessor.cs deleted file mode 100644 index dedcc611f6e..00000000000 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/LiteralFieldAccessor.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Reflection; -using System.Diagnostics; -using System.Collections.Generic; - -using System.Reflection.Runtime.General; -using System.Reflection.Runtime.TypeInfos; -using System.Reflection.Runtime.CustomAttributes; - -using Internal.Reflection.Core; -using Internal.Reflection.Core.Execution; - -using FieldAccessException = System.MemberAccessException; - -namespace System.Reflection.Runtime.FieldInfos -{ - internal sealed class LiteralFieldAccessor : FieldAccessor - { - public LiteralFieldAccessor(Object value) - { - _value = value; - } - - public sealed override Object GetField(Object obj) - { - return _value; - } - - public sealed override void SetField(Object obj, Object value, BinderBundle binderBundle) - { - throw new FieldAccessException(SR.Acc_ReadOnly); - } - - private readonly Object _value; - } -} - diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/RuntimeFieldInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/RuntimeFieldInfo.cs index dd9e6636b9e..77fa3abf369 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/RuntimeFieldInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/RuntimeFieldInfo.cs @@ -95,6 +95,15 @@ public sealed override Object GetValue(Object obj) return fieldAccessor.GetField(obj); } + public sealed override object GetValueDirect(TypedReference obj) + { + if (obj.IsNull) + throw new ArgumentException(SR.Arg_TypedReference_Null); + + FieldAccessor fieldAccessor = this.FieldAccessor; + return fieldAccessor.GetFieldDirect(obj); + } + public sealed override Module Module { get @@ -123,6 +132,15 @@ public sealed override void SetValue(object obj, object value, BindingFlags invo fieldAccessor.SetField(obj, value, binderBundle); } + public sealed override void SetValueDirect(TypedReference obj, object value) + { + if (obj.IsNull) + throw new ArgumentException(SR.Arg_TypedReference_Null); + + FieldAccessor fieldAccessor = this.FieldAccessor; + fieldAccessor.SetFieldDirect(obj, value); + } + Type ITraceableTypeMember.ContainingType { get @@ -195,7 +213,7 @@ private FieldAccessor FieldAccessor throw new BadImageFormatException(); // Field marked literal but has no default value. } - _lazyFieldAccessor = fieldAccessor = new LiteralFieldAccessor(defaultValue); + _lazyFieldAccessor = fieldAccessor = ReflectionCoreExecution.ExecutionEnvironment.CreateLiteralFieldAccessor(defaultValue, FieldType.TypeHandle); } else { @@ -235,6 +253,11 @@ protected RuntimeFieldInfo WithDebugName() /// protected abstract RuntimeTypeInfo DefiningType { get; } + /// + /// Returns the field offset (asserts and throws if not an instance field). Does not include the size of the object header. + /// + internal int Offset => FieldAccessor.Offset; + protected readonly RuntimeTypeInfo _contextTypeInfo; protected readonly RuntimeTypeInfo _reflectedType; diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs index fc388219f31..900d22d2417 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs @@ -11,11 +11,13 @@ using System.Reflection.Runtime.TypeInfos; using System.Reflection.Runtime.TypeInfos.NativeFormat; using System.Reflection.Runtime.Assemblies; +using System.Reflection.Runtime.FieldInfos; using System.Reflection.Runtime.FieldInfos.NativeFormat; using System.Reflection.Runtime.MethodInfos; using System.Reflection.Runtime.BindingFlagSupport; using System.Reflection.Runtime.Modules; +using Internal.Runtime.Augments; using Internal.Reflection.Augments; using Internal.Reflection.Core.Execution; using Internal.Metadata.NativeFormat; @@ -346,5 +348,43 @@ public sealed override void RunModuleConstructor(Module module) RuntimeAssembly assembly = (RuntimeAssembly)module.Assembly; assembly.RunModuleConstructor(); } + + public sealed override void MakeTypedReference(object target, FieldInfo[] flds, out Type type, out int offset) + { + if (target == null) + throw new ArgumentNullException(nameof(target)); + if (flds == null) + throw new ArgumentNullException(nameof(flds)); + if (flds.Length == 0) + throw new ArgumentException(SR.Arg_ArrayZeroError); + + offset = RuntimeAugments.ObjectHeaderSize; + Type targetType = target.GetType(); + for (int i = 0; i < flds.Length; i++) + { + RuntimeFieldInfo field = flds[i] as RuntimeFieldInfo; + if (field == null) + throw new ArgumentException(SR.Argument_MustBeRuntimeFieldInfo); + if (field.IsInitOnly || field.IsStatic) + throw new ArgumentException(SR.Argument_TypedReferenceInvalidField); + + // For proper handling of Nullable don't change to something like 'IsAssignableFrom' + // Currently we can't make a TypedReference to fields of Nullable, which is fine. + Type declaringType = field.DeclaringType; + if (targetType != declaringType && !targetType.IsSubclassOf(declaringType)) + throw new MissingMemberException(SR.MissingMemberTypeRef); // MissingMemberException is a strange exception to throw, but it is the compatible exception. + + Type fieldType = field.FieldType; + if (fieldType.IsPrimitive) + throw new ArgumentException(SR.Arg_TypeRefPrimitve); // This check exists for compatibility (why such an ad-hoc restriction?) + if (i < (flds.Length - 1) && !fieldType.IsValueType) + throw new MissingMemberException(SR.MissingMemberNestErr); // MissingMemberException is a strange exception to throw, but it is the compatible exception. + + targetType = fieldType; + offset = checked(offset + field.Offset); + } + + type = targetType; + } } } diff --git a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Runtime.cs b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Runtime.cs index 88f841de12f..8aeb9f6fe05 100644 --- a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Runtime.cs +++ b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Runtime.cs @@ -2,18 +2,18 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using global::System; -using global::System.Reflection; -using global::System.Collections.Generic; -using global::System.Runtime.InteropServices; +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Runtime.InteropServices; -using global::Internal.Runtime.Augments; +using Internal.Runtime.Augments; -using global::Internal.Reflection.Core; -using global::Internal.Reflection.Core.Execution; -using global::Internal.Reflection.Execution.MethodInvokers; +using Internal.Reflection.Core.Execution; +using Internal.Reflection.Execution.FieldAccessors; +using Internal.Reflection.Execution.MethodInvokers; -using global::Internal.Metadata.NativeFormat; +using Internal.Metadata.NativeFormat; namespace Internal.Reflection.Execution { @@ -145,6 +145,14 @@ public sealed override IEnumerable GetPseudoCustomAttribute { return Empty.Enumerable; } + + //============================================================================================== + // Miscellaneous + //============================================================================================== + public sealed override FieldAccessor CreateLiteralFieldAccessor(object value, RuntimeTypeHandle fieldTypeHandle) + { + return new LiteralFieldAccessor(value, fieldTypeHandle); + } } } diff --git a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/InstanceFieldAccessor.cs b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/InstanceFieldAccessor.cs index b66013e16d7..59365405ca7 100644 --- a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/InstanceFieldAccessor.cs +++ b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/InstanceFieldAccessor.cs @@ -24,6 +24,8 @@ public InstanceFieldAccessor(RuntimeTypeHandle declaringTypeHandle, RuntimeTypeH this.FieldTypeHandle = fieldTypeHandle; } + public abstract override int Offset { get; } + public sealed override Object GetField(Object obj) { if (obj == null) @@ -33,6 +35,28 @@ public sealed override Object GetField(Object obj) return UncheckedGetField(obj); } + public sealed override object GetFieldDirect(TypedReference typedReference) + { + if (RuntimeAugments.IsValueType(this.DeclaringTypeHandle)) + { + // We're being asked to read a field from the value type pointed to by the TypedReference. This code path + // avoids boxing that value type by adding this field's offset to the TypedReference's managed pointer. + Type targetType = TypedReference.GetTargetType(typedReference); + if (!(targetType.TypeHandle.Equals(this.DeclaringTypeHandle))) + throw new ArgumentException(); + return UncheckedGetFieldDirectFromValueType(typedReference); + } + else + { + // We're being asked to read a field from a reference type. There's no boxing to optimize out in that case so just handle it as + // if this was a FieldInfo.GetValue() call. + object obj = TypedReference.ToObject(typedReference); + return GetField(obj); + } + } + + protected abstract object UncheckedGetFieldDirectFromValueType(TypedReference typedReference); + public sealed override void SetField(Object obj, Object value, BinderBundle binderBundle) { if (obj == null) @@ -43,6 +67,35 @@ public sealed override void SetField(Object obj, Object value, BinderBundle bind UncheckedSetField(obj, value); } + public sealed override void SetFieldDirect(TypedReference typedReference, object value) + { + if (RuntimeAugments.IsValueType(this.DeclaringTypeHandle)) + { + // We're being asked to store a field into the value type pointed to by the TypedReference. This code path + // bypasses boxing that value type by adding this field's offset to the TypedReference's managed pointer. + // (Otherwise, the store would go into a useless temporary copy rather than the intended destination.) + Type targetType = TypedReference.GetTargetType(typedReference); + if (!(targetType.TypeHandle.Equals(this.DeclaringTypeHandle))) + throw new ArgumentException(); + value = RuntimeAugments.CheckArgumentForDirectFieldAccess(value, this.FieldTypeHandle); + UncheckedSetFieldDirectIntoValueType(typedReference, value); + } + else + { + // We're being asked to store a field from a reference type. There's no boxing to bypass in that case so just handle it as + // if this was a FieldInfo.SetValue() call (but using SetValueDirect's argument coercing semantics) + object obj = TypedReference.ToObject(typedReference); + if (obj == null) + throw new TargetException(SR.RFLCT_Targ_StatFldReqTarg); + if (!RuntimeAugments.IsAssignable(obj, this.DeclaringTypeHandle)) + throw new ArgumentException(); + value = RuntimeAugments.CheckArgumentForDirectFieldAccess(value, this.FieldTypeHandle); + UncheckedSetField(obj, value); + } + } + + protected abstract void UncheckedSetFieldDirectIntoValueType(TypedReference typedReference, object value); + protected abstract Object UncheckedGetField(Object obj); protected abstract void UncheckedSetField(Object obj, Object value); diff --git a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/LiteralFieldAccessor.cs b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/LiteralFieldAccessor.cs new file mode 100644 index 00000000000..4ea218494ea --- /dev/null +++ b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/LiteralFieldAccessor.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Reflection; +using System.Diagnostics; + +namespace Internal.Reflection.Execution.FieldAccessors +{ + internal sealed class LiteralFieldAccessor : StaticFieldAccessor + { + public LiteralFieldAccessor(object value, RuntimeTypeHandle fieldTypeHandle) + : base(IntPtr.Zero, fieldTypeHandle) + { + _value = value; + } + + protected sealed override object GetFieldBypassCctor() => _value; + + protected sealed override void SetFieldBypassCctor(object value, BinderBundle binderBundle) + { + throw new FieldAccessException(SR.Acc_ReadOnly); + } + + protected sealed override void SetFieldDirectBypassCctor(object value) + { + throw new FieldAccessException(SR.Acc_ReadOnly); + } + + private readonly object _value; + } +} diff --git a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForInstanceFields.cs b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForInstanceFields.cs index c0514362358..27539b69867 100644 --- a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForInstanceFields.cs +++ b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForInstanceFields.cs @@ -26,14 +26,26 @@ public ReferenceTypeFieldAccessorForInstanceFields(int offset, RuntimeTypeHandle _offset = offset; } + public sealed override int Offset => _offset - RuntimeAugments.ObjectHeaderSize; + protected sealed override Object UncheckedGetField(Object obj) { return RuntimeAugments.LoadReferenceTypeField(obj, _offset); } + protected sealed override object UncheckedGetFieldDirectFromValueType(TypedReference typedReference) + { + return RuntimeAugments.LoadReferenceTypeFieldValueFromValueType(typedReference, this.Offset); + } + protected sealed override void UncheckedSetField(Object obj, Object value) { RuntimeAugments.StoreReferenceTypeField(obj, _offset, value); } + + protected sealed override void UncheckedSetFieldDirectIntoValueType(TypedReference typedReference, object value) + { + RuntimeAugments.StoreReferenceTypeFieldValueIntoValueType(typedReference, this.Offset, value); + } } } diff --git a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForStaticFields.cs b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForStaticFields.cs index 3f5d412802e..f2009f08742 100644 --- a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForStaticFields.cs +++ b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForStaticFields.cs @@ -16,7 +16,7 @@ namespace Internal.Reflection.Execution.FieldAccessors { - internal sealed class ReferenceTypeFieldAccessorForStaticFields : StaticFieldAccessor + internal sealed class ReferenceTypeFieldAccessorForStaticFields : WritableStaticFieldAccessor { private IntPtr _fieldAddress; @@ -26,14 +26,13 @@ public ReferenceTypeFieldAccessorForStaticFields(IntPtr cctorContext, IntPtr fie _fieldAddress = fieldAddress; } - protected sealed override Object GetFieldBypassCctor(Object obj) + protected sealed override Object GetFieldBypassCctor() { return RuntimeAugments.LoadReferenceTypeField(_fieldAddress); } - protected sealed override void SetFieldBypassCctor(Object obj, Object value, BinderBundle binderBundle) + protected sealed override void UncheckedSetFieldBypassCctor(Object value) { - value = RuntimeAugments.CheckArgument(value, FieldTypeHandle, binderBundle); RuntimeAugments.StoreReferenceTypeField(_fieldAddress, value); } } diff --git a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForThreadStaticFields.cs b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForThreadStaticFields.cs index dcf126e5a94..eb7062b41bb 100644 --- a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForThreadStaticFields.cs +++ b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForThreadStaticFields.cs @@ -16,7 +16,7 @@ namespace Internal.Reflection.Execution.FieldAccessors { - internal sealed class ReferenceTypeFieldAccessorForThreadStaticFields : StaticFieldAccessor + internal sealed class ReferenceTypeFieldAccessorForThreadStaticFields : WritableStaticFieldAccessor { private IntPtr _cookie; private RuntimeTypeHandle _declaringTypeHandle; @@ -28,15 +28,14 @@ public ReferenceTypeFieldAccessorForThreadStaticFields(IntPtr cctorContext, Runt _declaringTypeHandle = declaringTypeHandle; } - protected sealed override Object GetFieldBypassCctor(Object obj) + protected sealed override Object GetFieldBypassCctor() { IntPtr fieldAddress = RuntimeAugments.GetThreadStaticFieldAddress(_declaringTypeHandle, _cookie); return RuntimeAugments.LoadReferenceTypeField(fieldAddress); } - protected sealed override void SetFieldBypassCctor(Object obj, Object value, BinderBundle binderBundle) + protected sealed override void UncheckedSetFieldBypassCctor(Object value) { - value = RuntimeAugments.CheckArgument(value, FieldTypeHandle, binderBundle); IntPtr fieldAddress = RuntimeAugments.GetThreadStaticFieldAddress(_declaringTypeHandle, _cookie); RuntimeAugments.StoreReferenceTypeField(fieldAddress, value); } diff --git a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForUniversalThreadStaticFields.cs b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForUniversalThreadStaticFields.cs index d74ba6c6dc1..12f498b50d0 100644 --- a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForUniversalThreadStaticFields.cs +++ b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ReferenceTypeFieldAccessorForUniversalThreadStaticFields.cs @@ -16,7 +16,7 @@ namespace Internal.Reflection.Execution.FieldAccessors { - internal sealed class ReferenceTypeFieldAccessorForUniversalThreadStaticFields : StaticFieldAccessor + internal sealed class ReferenceTypeFieldAccessorForUniversalThreadStaticFields : WritableStaticFieldAccessor { private int _fieldOffset; private RuntimeTypeHandle _declaringTypeHandle; @@ -28,16 +28,15 @@ public ReferenceTypeFieldAccessorForUniversalThreadStaticFields(IntPtr cctorCont _declaringTypeHandle = declaringTypeHandle; } - protected sealed override Object GetFieldBypassCctor(Object obj) + protected sealed override Object GetFieldBypassCctor() { IntPtr tlsFieldsStartAddress = RuntimeAugments.GetThreadStaticFieldAddress(_declaringTypeHandle, IntPtr.Zero); IntPtr fieldAddress = tlsFieldsStartAddress + _fieldOffset; return RuntimeAugments.LoadReferenceTypeField(fieldAddress); } - protected sealed override void SetFieldBypassCctor(Object obj, Object value, BinderBundle binderBundle) + protected sealed override void UncheckedSetFieldBypassCctor(Object value) { - value = RuntimeAugments.CheckArgument(value, FieldTypeHandle, binderBundle); IntPtr tlsFieldsStartAddress = RuntimeAugments.GetThreadStaticFieldAddress(_declaringTypeHandle, IntPtr.Zero); IntPtr fieldAddress = tlsFieldsStartAddress + _fieldOffset; RuntimeAugments.StoreReferenceTypeField(fieldAddress, value); diff --git a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/StaticFieldAccessor.cs b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/StaticFieldAccessor.cs index e5d010c8181..3f58a22b696 100644 --- a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/StaticFieldAccessor.cs +++ b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/StaticFieldAccessor.cs @@ -34,19 +34,43 @@ public sealed override Object GetField(Object obj) { RuntimeAugments.EnsureClassConstructorRun(_cctorContext); } - return GetFieldBypassCctor(obj); + return GetFieldBypassCctor(); } + // GetValueDirect() can be used on static fields though this seems like a silly thing to do. + public sealed override object GetFieldDirect(TypedReference typedReference) => GetField(null); + public sealed override void SetField(Object obj, Object value, BinderBundle binderBundle) { if (_cctorContext != IntPtr.Zero) { RuntimeAugments.EnsureClassConstructorRun(_cctorContext); } - SetFieldBypassCctor(obj, value, binderBundle); + SetFieldBypassCctor(value, binderBundle); + } + + // SetValueDirect() can be used on static fields though this seems like a silly thing to do. + // Note that the argument coercion rules are different from SetValue. + public sealed override void SetFieldDirect(TypedReference typedReference, object value) + { + if (_cctorContext != IntPtr.Zero) + { + RuntimeAugments.EnsureClassConstructorRun(_cctorContext); + } + SetFieldDirectBypassCctor(value); + } + + public sealed override int Offset + { + get + { + Debug.Fail("Cannot call Offset on a static field."); + throw new InvalidOperationException(); + } } - protected abstract Object GetFieldBypassCctor(Object obj); - protected abstract void SetFieldBypassCctor(Object obj, Object value, BinderBundle binderBundle); + protected abstract Object GetFieldBypassCctor(); + protected abstract void SetFieldBypassCctor(Object value, BinderBundle binderBundle); + protected abstract void SetFieldDirectBypassCctor(object value); } } diff --git a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForInstanceFields.cs b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForInstanceFields.cs index 56b76cfc004..354a68a0054 100644 --- a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForInstanceFields.cs +++ b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForInstanceFields.cs @@ -26,14 +26,26 @@ public ValueTypeFieldAccessorForInstanceFields(int offset, RuntimeTypeHandle dec _offset = offset; } + public sealed override int Offset => _offset - RuntimeAugments.ObjectHeaderSize; + protected sealed override Object UncheckedGetField(Object obj) { return RuntimeAugments.LoadValueTypeField(obj, _offset, this.FieldTypeHandle); } + protected sealed override object UncheckedGetFieldDirectFromValueType(TypedReference typedReference) + { + return RuntimeAugments.LoadValueTypeFieldValueFromValueType(typedReference, this.Offset, this.FieldTypeHandle); + } + protected sealed override void UncheckedSetField(Object obj, Object value) { RuntimeAugments.StoreValueTypeField(obj, _offset, value, this.FieldTypeHandle); } + + protected sealed override void UncheckedSetFieldDirectIntoValueType(TypedReference typedReference, object value) + { + RuntimeAugments.StoreValueTypeFieldValueIntoValueType(typedReference, this.Offset, value, this.FieldTypeHandle); + } } } diff --git a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForStaticFields.cs b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForStaticFields.cs index eff2c9178e2..8b6d5e411db 100644 --- a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForStaticFields.cs +++ b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForStaticFields.cs @@ -18,7 +18,7 @@ namespace Internal.Reflection.Execution.FieldAccessors { - internal sealed class ValueTypeFieldAccessorForStaticFields : StaticFieldAccessor + internal sealed class ValueTypeFieldAccessorForStaticFields : WritableStaticFieldAccessor { private IntPtr _fieldAddress; @@ -28,14 +28,13 @@ public ValueTypeFieldAccessorForStaticFields(IntPtr cctorContext, IntPtr fieldAd _fieldAddress = fieldAddress; } - protected sealed override Object GetFieldBypassCctor(Object obj) + protected sealed override Object GetFieldBypassCctor() { return RuntimeAugments.LoadValueTypeField(_fieldAddress, FieldTypeHandle); } - protected sealed override void SetFieldBypassCctor(Object obj, Object value, BinderBundle binderBundle) + protected sealed override void UncheckedSetFieldBypassCctor(Object value) { - value = RuntimeAugments.CheckArgument(value, FieldTypeHandle, binderBundle); RuntimeAugments.StoreValueTypeField(_fieldAddress, value, FieldTypeHandle); } } diff --git a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForThreadStaticFields.cs b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForThreadStaticFields.cs index 00b594d5c93..11938050748 100644 --- a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForThreadStaticFields.cs +++ b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForThreadStaticFields.cs @@ -18,7 +18,7 @@ namespace Internal.Reflection.Execution.FieldAccessors { - internal sealed class ValueTypeFieldAccessorForThreadStaticFields : StaticFieldAccessor + internal sealed class ValueTypeFieldAccessorForThreadStaticFields : WritableStaticFieldAccessor { private IntPtr _cookie; private RuntimeTypeHandle _declaringTypeHandle; @@ -30,15 +30,14 @@ public ValueTypeFieldAccessorForThreadStaticFields(IntPtr cctorContext, RuntimeT _declaringTypeHandle = declaringTypeHandle; } - protected sealed override Object GetFieldBypassCctor(Object obj) + protected sealed override Object GetFieldBypassCctor() { IntPtr fieldAddress = RuntimeAugments.GetThreadStaticFieldAddress(_declaringTypeHandle, _cookie); return RuntimeAugments.LoadValueTypeField(fieldAddress, FieldTypeHandle); } - protected sealed override void SetFieldBypassCctor(Object obj, Object value, BinderBundle binderBundle) + protected sealed override void UncheckedSetFieldBypassCctor(Object value) { - value = RuntimeAugments.CheckArgument(value, FieldTypeHandle, binderBundle); IntPtr fieldAddress = RuntimeAugments.GetThreadStaticFieldAddress(_declaringTypeHandle, _cookie); RuntimeAugments.StoreValueTypeField(fieldAddress, value, FieldTypeHandle); } diff --git a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForUniversalThreadStaticFields.cs b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForUniversalThreadStaticFields.cs index 5dc5020585a..70e50e210ce 100644 --- a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForUniversalThreadStaticFields.cs +++ b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/ValueTypeFieldAccessorForUniversalThreadStaticFields.cs @@ -18,7 +18,7 @@ namespace Internal.Reflection.Execution.FieldAccessors { - internal sealed class ValueTypeFieldAccessorForUniversalThreadStaticFields : StaticFieldAccessor + internal sealed class ValueTypeFieldAccessorForUniversalThreadStaticFields : WritableStaticFieldAccessor { private int _fieldOffset; private RuntimeTypeHandle _declaringTypeHandle; @@ -30,16 +30,15 @@ public ValueTypeFieldAccessorForUniversalThreadStaticFields(IntPtr cctorContext, _declaringTypeHandle = declaringTypeHandle; } - protected sealed override Object GetFieldBypassCctor(Object obj) + protected sealed override Object GetFieldBypassCctor() { IntPtr tlsFieldsStartAddress = RuntimeAugments.GetThreadStaticFieldAddress(_declaringTypeHandle, IntPtr.Zero); IntPtr fieldAddress = tlsFieldsStartAddress + _fieldOffset; return RuntimeAugments.LoadValueTypeField(fieldAddress, FieldTypeHandle); } - protected sealed override void SetFieldBypassCctor(Object obj, Object value, BinderBundle binderBundle) + protected sealed override void UncheckedSetFieldBypassCctor(Object value) { - value = RuntimeAugments.CheckArgument(value, FieldTypeHandle, binderBundle); IntPtr tlsFieldsStartAddress = RuntimeAugments.GetThreadStaticFieldAddress(_declaringTypeHandle, IntPtr.Zero); IntPtr fieldAddress = tlsFieldsStartAddress + _fieldOffset; RuntimeAugments.StoreValueTypeField(fieldAddress, value, FieldTypeHandle); diff --git a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/WritableStaticFieldAccessor.cs b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/WritableStaticFieldAccessor.cs new file mode 100644 index 00000000000..3d76322d14c --- /dev/null +++ b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/FieldAccessors/WritableStaticFieldAccessor.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Reflection; +using Internal.Runtime.Augments; + +namespace Internal.Reflection.Execution.FieldAccessors +{ + internal abstract class WritableStaticFieldAccessor : StaticFieldAccessor + { + protected WritableStaticFieldAccessor(IntPtr cctorContext, RuntimeTypeHandle fieldTypeHandle) + : base(cctorContext, fieldTypeHandle) + { + } + + protected abstract override object GetFieldBypassCctor(); + + protected sealed override void SetFieldBypassCctor(object value, BinderBundle binderBundle) + { + value = RuntimeAugments.CheckArgument(value, FieldTypeHandle, binderBundle); + UncheckedSetFieldBypassCctor(value); + } + + protected sealed override void SetFieldDirectBypassCctor(object value) + { + value = RuntimeAugments.CheckArgumentForDirectFieldAccess(value, FieldTypeHandle); + UncheckedSetFieldBypassCctor(value); + } + + protected abstract void UncheckedSetFieldBypassCctor(object value); + } +} diff --git a/src/System.Private.Reflection.Execution/src/Resources/Strings.resx b/src/System.Private.Reflection.Execution/src/Resources/Strings.resx index 520612abf33..30f8324e3d6 100644 --- a/src/System.Private.Reflection.Execution/src/Resources/Strings.resx +++ b/src/System.Private.Reflection.Execution/src/Resources/Strings.resx @@ -231,4 +231,7 @@ Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection. + + Cannot set a constant field. + diff --git a/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj b/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj index 6a4a099de4e..8e9a7609ed5 100644 --- a/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj +++ b/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj @@ -75,6 +75,7 @@ + @@ -84,6 +85,7 @@ +