File: System\Reflection\Runtime\FieldInfos\RuntimeFieldInfo.cs
Web Access
Project: src\src\runtime\src\coreclr\nativeaot\System.Private.CoreLib\src\System.Private.CoreLib.csproj (System.Private.CoreLib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Reflection.Runtime.BindingFlagSupport;
using System.Reflection.Runtime.CustomAttributes;
using System.Reflection.Runtime.General;
using System.Reflection.Runtime.TypeInfos;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

using Internal.Reflection.Core;
using Internal.Reflection.Core.Execution;

namespace System.Reflection.Runtime.FieldInfos
{
    //
    // The Runtime's implementation of fields.
    //
    internal abstract partial class RuntimeFieldInfo : FieldInfo
    {
        //
        // contextType    - the type that supplies the type context (i.e. substitutions for generic parameters.) Though you
        //                  get your raw information from "definingType", you report "contextType" as your DeclaringType property.
        //
        //  For example:
        //
        //       typeof(Foo<>).GetTypeInfo().DeclaredMembers
        //
        //           The definingType and contextType are both Foo<>
        //
        //       typeof(Foo<int,String>).GetTypeInfo().DeclaredMembers
        //
        //          The definingType is "Foo<,>"
        //          The contextType is "Foo<int,String>"
        //
        //  We don't report any DeclaredMembers for arrays or generic parameters so those don't apply.
        //
        protected RuntimeFieldInfo(RuntimeTypeInfo contextTypeInfo, RuntimeTypeInfo reflectedType)
        {
            _contextTypeInfo = contextTypeInfo;
            _reflectedType = reflectedType;
        }

        public sealed override IEnumerable<CustomAttributeData> CustomAttributes
        {
            get
            {
                foreach (CustomAttributeData cad in TrueCustomAttributes)
                    yield return cad;

                if (DeclaringType.IsExplicitLayout)
                {
                    int offset = ExplicitLayoutFieldOffsetData;
                    CustomAttributeTypedArgument offsetArgument = new CustomAttributeTypedArgument(typeof(int), offset);
                    yield return new RuntimePseudoCustomAttributeData(typeof(FieldOffsetAttribute), new CustomAttributeTypedArgument[] { offsetArgument });
                }

                FieldAttributes attributes = Attributes;
#pragma warning disable SYSLIB0050 // Legacy serialization infrastructure is obsolete
                if (0 != (attributes & FieldAttributes.NotSerialized))
                {
                    yield return new RuntimePseudoCustomAttributeData(typeof(NonSerializedAttribute), null);
                }
#pragma warning restore SYSLIB0050
            }
        }

        public sealed override Type DeclaringType
        {
            get
            {
                return _contextTypeInfo.ToType();
            }
        }

        public sealed override Type FieldType
        {
            get
            {
                Type fieldType = _lazyFieldType;
                if (fieldType == null)
                {
                    _lazyFieldType = fieldType = this.FieldRuntimeType.ToType();
                }

                return fieldType;
            }
        }

        public abstract override Type[] GetOptionalCustomModifiers();

        public abstract override Type[] GetRequiredCustomModifiers();

        public sealed override object GetValue(object obj)
        {
            FieldAccessor fieldAccessor = this.FieldAccessor;
            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 abstract override bool HasSameMetadataDefinitionAs(MemberInfo other);

        public sealed override Module Module
        {
            get
            {
                return DefiningType.Module;
            }
        }

        public sealed override Type ReflectedType
        {
            get
            {
                return _reflectedType.ToType();
            }
        }

        public sealed override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, CultureInfo culture)
        {
            FieldAccessor fieldAccessor = this.FieldAccessor;
            BinderBundle binderBundle = binder.ToBinderBundle(invokeAttr, culture);
            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);
        }

        /// <summary>
        /// Override to provide the metadata based name of a field. (Different from the Name
        /// property in that it does not go into the reflection trace logic.)
        /// </summary>
        protected abstract string MetadataName { get; }

        public sealed override string Name
        {
            get
            {
                return MetadataName;
            }
        }

        public sealed override object GetRawConstantValue()
        {
            if (!IsLiteral)
                throw new InvalidOperationException();

            object defaultValue;
            if (!GetDefaultValueIfAvailable(raw: true, defaultValue: out defaultValue))
                throw new BadImageFormatException(); // Field marked literal but has no default value.

            return defaultValue;
        }

        // Types that derive from RuntimeFieldInfo must implement the following public surface area members
        public abstract override FieldAttributes Attributes { get; }
        public abstract override int MetadataToken { get; }
        public abstract override string ToString();
        public abstract override bool Equals(object obj);
        public abstract override int GetHashCode();
        public abstract override RuntimeFieldHandle FieldHandle { get; }

        /// <summary>
        /// Get the default value if exists for a field by parsing metadata. Return false if there is no default value.
        /// </summary>
        protected abstract bool GetDefaultValueIfAvailable(bool raw, out object defaultValue);

        /// <summary>
        /// Return a FieldAccessor object for accessing the value of a non-literal field. May rely on metadata to create correct accessor.
        /// </summary>
        protected abstract FieldAccessor TryGetFieldAccessor();

        private FieldAccessor FieldAccessor
        {
            get
            {
                FieldAccessor fieldAccessor = _lazyFieldAccessor;
                if (fieldAccessor == null)
                {
                    if (this.IsLiteral)
                    {
                        // Legacy: ECMA335 does not require that the metadata literal match the type of the field that declares it.
                        // For desktop compat, we return the metadata literal as is and do not attempt to convert or validate against the Field type.

                        object defaultValue;
                        if (!GetDefaultValueIfAvailable(raw: false, defaultValue: out defaultValue))
                        {
                            throw new BadImageFormatException(); // Field marked literal but has no default value.
                        }

                        _lazyFieldAccessor = fieldAccessor = ReflectionCoreExecution.ExecutionEnvironment.CreateLiteralFieldAccessor(defaultValue, FieldType.TypeHandle);
                    }
                    else
                    {
                        _lazyFieldAccessor = fieldAccessor = TryGetFieldAccessor();
                        if (fieldAccessor == null)
                            throw ReflectionCoreExecution.ExecutionEnvironment.CreateNonInvokabilityException(this);
                    }
                }
                return fieldAccessor;
            }
        }

        /// <summary>
        /// Return the type of the field by parsing metadata.
        /// </summary>
        protected abstract RuntimeTypeInfo FieldRuntimeType { get; }

        protected RuntimeFieldInfo WithDebugName()
        {
#if DEBUG
            if (_debugName == null)
            {
                _debugName = "Constructing..."; // Protect against any inadvertent reentrancy.
                _debugName = MetadataName;
            }
#endif
            return this;
        }

        /// <summary>
        /// Return the DefiningTypeInfo as a RuntimeTypeInfo (instead of as a format specific type info)
        /// </summary>
        protected abstract RuntimeTypeInfo DefiningType { get; }

        protected abstract IEnumerable<CustomAttributeData> TrueCustomAttributes { get; }
        protected abstract int ExplicitLayoutFieldOffsetData { get; }

        /// <summary>
        /// Returns the field offset (asserts and throws if not an instance field). Does not include the size of the object header.
        /// </summary>
        internal int Offset => FieldAccessor.Offset;

        protected readonly RuntimeTypeInfo _contextTypeInfo;
        protected readonly RuntimeTypeInfo _reflectedType;

        private volatile FieldAccessor _lazyFieldAccessor;

        private volatile Type _lazyFieldType;

#if DEBUG
        private string _debugName;
#endif
    }
}