Skip to content
This repository was archived by the owner on Nov 1, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<byte>(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<byte>(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<byte, object>(ref Unsafe.Add<byte>(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<byte, object>(ref Unsafe.Add<byte>(ref typedReference.Value, fieldOffset));
}

public static unsafe int ObjectHeaderSize => sizeof(EETypePtr);

[DebuggerGuidedStepThroughAttribute]
public static object CallDynamicInvokeMethod(
object thisPtr,
Expand Down Expand Up @@ -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;
Expand Down
5 changes: 4 additions & 1 deletion src/System.Private.CoreLib/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -2254,4 +2254,7 @@
<data name="Serialization_NonSerType" xml:space="preserve">
<value>Type '{0}' in Assembly '{1}' is not marked as serializable.</value>
</data>
</root>
<data name="NotSupported_NYI" xml:space="preserve">
<value>This feature is not currently implemented.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,7 @@
<Link>Interop\Windows\mincore\Interop.CLSIDFromProgID.cs</Link>
</Compile>
<Compile Condition="'$(TargetsUnix)'=='true'" Include="System\Type.Unix.cs" />
<Compile Include="System\TypedReference.cs" />
<Compile Include="System\TypeUnificationKey.cs" />
<Compile Include="System\TypeLoadException.cs" />
<Compile Include="System\UInt16.cs" />
Expand Down
16 changes: 15 additions & 1 deletion src/System.Private.CoreLib/src/System/InvokeUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Type> getExactTypeForCustomBinder = null)
Expand All @@ -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
{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions src/System.Private.CoreLib/src/System/Reflection/FieldInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down
100 changes: 100 additions & 0 deletions src/System.Private.CoreLib/src/System/TypedReference.cs
Original file line number Diff line number Diff line change
@@ -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<T> is fixed, uncomment the following line and delete the one after it.
//_value = new ByReference<byte>(ref Unsafe.Add<byte>(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<byte, object>(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<T> is broken on Project N right now.
// Once that's fixed, delete this class and replace references to ByReferenceOfByte to ByReference<byte>.
private struct ByReferenceOfByte
{
public ByReferenceOfByte(object target, int offset)
{
_target = target;
_offset = offset;
}

public ref byte Value => ref Unsafe.Add<byte>(ref Unsafe.As<IntPtr, byte>(ref _target.m_pEEType), _offset);

private readonly object _target;
private readonly int _offset;
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ public abstract class ExecutionEnvironment
//==============================================================================================
public abstract MethodInvoker GetSyntheticMethodInvoker(RuntimeTypeHandle thisType, RuntimeTypeHandle[] parameterTypes, InvokerOptions options, Func<Object, Object[], Object> invoker);
public abstract bool IsCOMObject(Type type);
public abstract FieldAccessor CreateLiteralFieldAccessor(object value, RuntimeTypeHandle fieldTypeHandle);

//==============================================================================================
// Non-public methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System;
using System.Reflection;
using System.Diagnostics;
using System.Collections.Generic;

namespace Internal.Reflection.Core.Execution
Expand All @@ -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);

/// <summary>
/// Returns the field offset (asserts and throws if not an instance field). Does not include the size of the object header.
/// </summary>
public abstract int Offset { get; }
}
}
21 changes: 21 additions & 0 deletions src/System.Private.Reflection.Core/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -334,4 +334,25 @@
<data name="Argument_MustBeRuntimeMethodInfo" xml:space="preserve">
<value>MethodInfo must be a runtime MethodInfo object.</value>
</data>
<data name="Arg_ArrayZeroError" xml:space="preserve">
<value>Array must not be of length zero.</value>
</data>
<data name="Argument_MustBeRuntimeFieldInfo" xml:space="preserve">
<value>FieldInfo must be a runtime FieldInfo object.</value>
</data>
<data name="Argument_TypedReferenceInvalidField" xml:space="preserve">
<value>Field in TypedReferences cannot be static or init only.</value>
</data>
<data name="MissingMemberTypeRef" xml:space="preserve">
<value>FieldInfo does not match the target Type.</value>
</data>
<data name="Arg_TypeRefPrimitve" xml:space="preserve">
<value>TypedReferences cannot be redefined as primitives.</value>
</data>
<data name="MissingMemberNestErr" xml:space="preserve">
<value>TypedReference can only be made on nested value Types.</value>
</data>
<data name="Arg_TypedReference_Null" xml:space="preserve">
<value>The TypedReference must be initialized.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@
<Compile Include="System\Reflection\Runtime\EventInfos\RuntimeEventInfo.cs" />
<Compile Include="System\Reflection\Runtime\FieldInfos\NativeFormat\NativeFormatRuntimeFieldInfo.cs" />
<Compile Include="System\Reflection\Runtime\FieldInfos\RuntimeFieldInfo.cs" />
<Compile Include="System\Reflection\Runtime\FieldInfos\LiteralFieldAccessor.cs" />
<Compile Include="System\Reflection\Runtime\General\Assignability.cs" />
<Compile Include="System\Reflection\Runtime\General\BlockedRuntimeTypeNameGenerator.cs" />
<Compile Include="System\Reflection\Runtime\General\Dispensers.NativeFormat.cs" />
Expand Down

This file was deleted.

Loading