1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System;
6 using System.Reflection;
7 
8 using Internal.Runtime.Augments;
9 using Internal.Reflection.Core.Execution;
10 
11 namespace Internal.Reflection.Execution.FieldAccessors
12 {
13     internal abstract class InstanceFieldAccessor : FieldAccessor
14     {
InstanceFieldAccessor(RuntimeTypeHandle declaringTypeHandle, RuntimeTypeHandle fieldTypeHandle, int offsetPlusHeader)15         public InstanceFieldAccessor(RuntimeTypeHandle declaringTypeHandle, RuntimeTypeHandle fieldTypeHandle, int offsetPlusHeader)
16         {
17             this.DeclaringTypeHandle = declaringTypeHandle;
18             this.FieldTypeHandle = fieldTypeHandle;
19             this.OffsetPlusHeader = offsetPlusHeader;
20         }
21 
22         public abstract override int Offset { get; }
23 
GetField(Object obj)24         public sealed override Object GetField(Object obj)
25         {
26             if (obj == null)
27                 throw new TargetException(SR.RFLCT_Targ_StatFldReqTarg);
28             if (!RuntimeAugments.IsAssignable(obj, this.DeclaringTypeHandle))
29                 throw new ArgumentException();
30             return UncheckedGetField(obj);
31         }
32 
GetFieldDirect(TypedReference typedReference)33         public sealed override object GetFieldDirect(TypedReference typedReference)
34         {
35             if (RuntimeAugments.IsValueType(this.DeclaringTypeHandle))
36             {
37                 // We're being asked to read a field from the value type pointed to by the TypedReference. This code path
38                 // avoids boxing that value type by adding this field's offset to the TypedReference's managed pointer.
39                 Type targetType = TypedReference.GetTargetType(typedReference);
40                 if (!(targetType.TypeHandle.Equals(this.DeclaringTypeHandle)))
41                     throw new ArgumentException();
42                 return UncheckedGetFieldDirectFromValueType(typedReference);
43             }
44             else
45             {
46                 // 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
47                 // if this was a FieldInfo.GetValue() call.
48                 object obj = TypedReference.ToObject(typedReference);
49                 return GetField(obj);
50             }
51         }
52 
UncheckedGetFieldDirectFromValueType(TypedReference typedReference)53         protected abstract object UncheckedGetFieldDirectFromValueType(TypedReference typedReference);
54 
SetField(Object obj, Object value, BinderBundle binderBundle)55         public sealed override void SetField(Object obj, Object value, BinderBundle binderBundle)
56         {
57             if (obj == null)
58                 throw new TargetException(SR.RFLCT_Targ_StatFldReqTarg);
59             if (!RuntimeAugments.IsAssignable(obj, this.DeclaringTypeHandle))
60                 throw new ArgumentException();
61             value = RuntimeAugments.CheckArgument(value, this.FieldTypeHandle, binderBundle);
62             UncheckedSetField(obj, value);
63         }
64 
SetFieldDirect(TypedReference typedReference, object value)65         public sealed override void SetFieldDirect(TypedReference typedReference, object value)
66         {
67             if (RuntimeAugments.IsValueType(this.DeclaringTypeHandle))
68             {
69                 // We're being asked to store a field into the value type pointed to by the TypedReference. This code path
70                 // bypasses boxing that value type by adding this field's offset to the TypedReference's managed pointer.
71                 // (Otherwise, the store would go into a useless temporary copy rather than the intended destination.)
72                 Type targetType = TypedReference.GetTargetType(typedReference);
73                 if (!(targetType.TypeHandle.Equals(this.DeclaringTypeHandle)))
74                     throw new ArgumentException();
75                 value = RuntimeAugments.CheckArgumentForDirectFieldAccess(value, this.FieldTypeHandle);
76                 UncheckedSetFieldDirectIntoValueType(typedReference, value);
77             }
78             else
79             {
80                 // 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
81                 // if this was a FieldInfo.SetValue() call (but using SetValueDirect's argument coercing semantics)
82                 object obj = TypedReference.ToObject(typedReference);
83                 if (obj == null)
84                     throw new TargetException(SR.RFLCT_Targ_StatFldReqTarg);
85                 if (!RuntimeAugments.IsAssignable(obj, this.DeclaringTypeHandle))
86                     throw new ArgumentException();
87                 value = RuntimeAugments.CheckArgumentForDirectFieldAccess(value, this.FieldTypeHandle);
88                 UncheckedSetField(obj, value);
89             }
90         }
91 
UncheckedSetFieldDirectIntoValueType(TypedReference typedReference, object value)92         protected abstract void UncheckedSetFieldDirectIntoValueType(TypedReference typedReference, object value);
93 
UncheckedGetField(Object obj)94         protected abstract Object UncheckedGetField(Object obj);
UncheckedSetField(Object obj, Object value)95         protected abstract void UncheckedSetField(Object obj, Object value);
96 
97         protected int OffsetPlusHeader { get; }
98         protected RuntimeTypeHandle DeclaringTypeHandle { get; }
99         protected RuntimeTypeHandle FieldTypeHandle { get; }
100     }
101 }
102