// 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.CustomAttributes; using System.Reflection.Runtime.General; using System.Reflection.Runtime.MethodInfos; using System.Reflection.Runtime.ParameterInfos; using System.Reflection.Runtime.TypeInfos; using System.Runtime.CompilerServices; using System.Text; using Internal.Reflection.Core; using Internal.Reflection.Core.Execution; namespace System.Reflection.Runtime.PropertyInfos { // // The runtime's implementation of PropertyInfo's // internal abstract partial class RuntimePropertyInfo : PropertyInfo { // // propertyHandle - the "tkPropertyDef" that identifies the property. // definingType - the "tkTypeDef" that defined the field (this is where you get the metadata reader that created propertyHandle.) // 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 RuntimePropertyInfo(RuntimeTypeInfo contextTypeInfo, RuntimeTypeInfo reflectedType) { ContextTypeInfo = contextTypeInfo; _reflectedType = reflectedType; } public sealed override bool CanRead { get { return Getter != null; } } public sealed override bool CanWrite { get { return Setter != null; } } public sealed override Type DeclaringType { get { return ContextTypeInfo.ToType(); } } public sealed override ParameterInfo[] GetIndexParameters() { ParameterInfo[] indexParameters = _lazyIndexParameters; if (indexParameters == null) { bool useGetter = CanRead; RuntimeMethodInfo accessor = (useGetter ? Getter : Setter); RuntimeParameterInfo[] runtimeMethodParameterInfos = accessor.RuntimeParameters; int count = runtimeMethodParameterInfos.Length; if (!useGetter) count--; // If we're taking the parameters off the setter, subtract one for the "value" parameter. if (count == 0) { _lazyIndexParameters = indexParameters = Array.Empty<ParameterInfo>(); } else { indexParameters = new ParameterInfo[count]; for (int i = 0; i < count; i++) { indexParameters[i] = RuntimePropertyIndexParameterInfo.GetRuntimePropertyIndexParameterInfo(this, runtimeMethodParameterInfos[i]); } _lazyIndexParameters = indexParameters; } } int numParameters = indexParameters.Length; if (numParameters == 0) return indexParameters; ParameterInfo[] result = new ParameterInfo[numParameters]; for (int i = 0; i < numParameters; i++) { result[i] = indexParameters[i]; } return result; } public sealed override MethodInfo GetMethod { get { return Getter; } } public sealed override Type[] GetOptionalCustomModifiers() => PropertyTypeHandle.GetCustomModifiers(ContextTypeInfo.TypeContext, optional: true); public sealed override Type[] GetRequiredCustomModifiers() => PropertyTypeHandle.GetCustomModifiers(ContextTypeInfo.TypeContext, optional: false); public sealed override object? GetValue(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? index, CultureInfo culture) { if (_lazyGetterInvoker == null) { if (!CanRead) throw new ArgumentException(); _lazyGetterInvoker = Getter.GetUncachedMethodInvoker(Array.Empty<RuntimeTypeInfo>(), this); } index ??= Array.Empty<object>(); return _lazyGetterInvoker.Invoke(obj, index, binder, invokeAttr, culture); } public abstract override bool HasSameMetadataDefinitionAs(MemberInfo other); public sealed override Module Module { get { return DefiningTypeInfo.Module; } } public sealed override string Name { get { return MetadataName; } } public sealed override Type PropertyType { get { Type propertyType = _lazyPropertyType; if (propertyType == null) { TypeContext typeContext = ContextTypeInfo.TypeContext; _lazyPropertyType = propertyType = PropertyTypeHandle.Resolve(typeContext).ToType(); } return propertyType; } } public sealed override Type ReflectedType { get { return _reflectedType.ToType(); } } public sealed override MethodInfo SetMethod { get { return Setter; } } public sealed override void SetValue(object? obj, object? value, BindingFlags invokeAttr, Binder? binder, object?[]? index, CultureInfo culture) { if (_lazySetterInvoker == null) { if (!CanWrite) throw new ArgumentException(); _lazySetterInvoker = Setter.GetUncachedMethodInvoker(Array.Empty<RuntimeTypeInfo>(), this); } object?[] arguments; if (index == null) { arguments = new object?[] { value }; } else { arguments = new object[index.Length + 1]; for (int i = 0; i < index.Length; i++) { arguments[i] = index[i]; } arguments[index.Length] = value; } _lazySetterInvoker.Invoke(obj, arguments, binder, invokeAttr, culture); } public sealed override string ToString() { StringBuilder sb = new StringBuilder(30); sb.Append(PropertyType.FormatTypeName()); sb.Append(' '); sb.Append(this.Name); ParameterInfo[] indexParameters = this.GetIndexParameters(); if (indexParameters.Length != 0) { RuntimeParameterInfo[] indexRuntimeParameters = new RuntimeParameterInfo[indexParameters.Length]; for (int i = 0; i < indexParameters.Length; i++) indexRuntimeParameters[i] = (RuntimeParameterInfo)(indexParameters[i]); sb.Append(" ["); sb.Append(RuntimeMethodHelpers.ComputeParametersString(indexRuntimeParameters)); sb.Append(']'); } return sb.ToString(); } private RuntimeNamedMethodInfo Getter { get { RuntimeNamedMethodInfo getter = _lazyGetter; if (getter == null) { getter = GetPropertyMethod(PropertyMethodSemantics.Getter); if (getter == null) getter = RuntimeDummyMethodInfo.Instance; _lazyGetter = getter; } return object.ReferenceEquals(getter, RuntimeDummyMethodInfo.Instance) ? null : getter; } } private RuntimeNamedMethodInfo Setter { get { RuntimeNamedMethodInfo setter = _lazySetter; if (setter == null) { setter = GetPropertyMethod(PropertyMethodSemantics.Setter); if (setter == null) setter = RuntimeDummyMethodInfo.Instance; _lazySetter = setter; } return object.ReferenceEquals(setter, RuntimeDummyMethodInfo.Instance) ? null : setter; } } protected RuntimePropertyInfo WithDebugName() { #if DEBUG if (_debugName == null) { _debugName = "Constructing..."; // Protect against any inadvertent reentrancy. _debugName = MetadataName; } #endif return this; } // Types that derive from RuntimePropertyInfo must implement the following public surface area members public abstract override PropertyAttributes Attributes { get; } public abstract override IEnumerable<CustomAttributeData> CustomAttributes { get; } public abstract override bool Equals(object obj); public abstract override int GetHashCode(); public abstract override int MetadataToken { get; } public sealed override object GetConstantValue() => GetConstantValue(raw: false); public sealed override object GetRawConstantValue() => GetConstantValue(raw: true); protected abstract bool GetDefaultValueIfAny(bool raw, out object defaultValue); /// <summary> /// Return a qualified handle that can be used to get the type of the property. /// </summary> protected abstract QSignatureTypeHandle PropertyTypeHandle { get; } protected enum PropertyMethodSemantics { Getter, Setter, } /// <summary> /// Override to return the Method that corresponds to the specified semantic. /// Return null if a method of the appropriate semantic does not exist /// </summary> protected abstract RuntimeNamedMethodInfo GetPropertyMethod(PropertyMethodSemantics whichMethod); /// <summary> /// Override to provide the metadata based name of a property. (Different from the Name /// property in that it does not go into the reflection trace logic.) /// </summary> protected abstract string MetadataName { get; } /// <summary> /// Return the DefiningTypeInfo as a RuntimeTypeInfo (instead of as a format specific type info) /// </summary> protected abstract RuntimeTypeInfo DefiningTypeInfo { get; } protected readonly RuntimeTypeInfo ContextTypeInfo; protected readonly RuntimeTypeInfo _reflectedType; private object GetConstantValue(bool raw) { object defaultValue; if (!GetDefaultValueIfAny(raw, out defaultValue)) { throw new InvalidOperationException(SR.Arg_EnumLitValueNotFound); } return defaultValue; } private volatile MethodBaseInvoker _lazyGetterInvoker; private volatile MethodBaseInvoker _lazySetterInvoker; private volatile RuntimeNamedMethodInfo _lazyGetter; private volatile RuntimeNamedMethodInfo _lazySetter; private volatile ParameterInfo[] _lazyIndexParameters; private volatile Type _lazyPropertyType; #if DEBUG private string _debugName; #endif } } |