File: System\Reflection\Runtime\ParameterInfos\NativeFormat\NativeFormatMethodParameterInfo.cs
Web Access
Project: 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.Reflection;
using System.Reflection.Runtime.General;
using System.Reflection.Runtime.General.NativeFormat;
using System.Runtime.CompilerServices;

using Internal.Metadata.NativeFormat;
using Internal.Reflection.Core;
using Internal.Reflection.Core.Execution;

namespace System.Reflection.Runtime.ParameterInfos.NativeFormat
{
    //
    // This implements ParameterInfo objects owned by MethodBase objects that have an associated Parameter metadata entity.
    //
    internal sealed partial class NativeFormatMethodParameterInfo : RuntimeFatMethodParameterInfo
    {
        private NativeFormatMethodParameterInfo(MethodBase member, int position, ParameterHandle parameterHandle, QSignatureTypeHandle qualifiedParameterTypeHandle, TypeContext typeContext)
            : base(member, position, qualifiedParameterTypeHandle, typeContext)
        {
            _parameter = parameterHandle.GetParameter(Reader);
        }

        private MetadataReader Reader
        {
            get
            {
                Debug.Assert(QualifiedParameterTypeHandle.Reader is MetadataReader);
                return (MetadataReader)QualifiedParameterTypeHandle.Reader;
            }
        }

        public sealed override ParameterAttributes Attributes
        {
            get
            {
                return _parameter.Flags;
            }
        }

        public sealed override string Name
        {
            get
            {
                return _parameter.Name.GetStringOrNull(this.Reader);
            }
        }

        public sealed override int MetadataToken
        {
            get
            {
                throw new InvalidOperationException(SR.NoMetadataTokenAvailable);
            }
        }

        internal sealed override MetadataReader GetMetadataReader() => Reader;

        internal sealed override CustomAttributeHandleCollection GetCustomAttributeHandles() => _parameter.CustomAttributes;

        protected sealed override bool GetDefaultValueIfAvailable(bool raw, out object? defaultValue)
        {
            if (DefaultValueParser.GetDefaultValueFromConstantIfAny(Reader, _parameter.DefaultValue, ParameterType, raw, out defaultValue))
                return true;

            defaultValue = raw ? GetDefaultValueFromCustomAttributeData() : GetDefaultValueFromCustomAttributes();
            if (defaultValue != DBNull.Value)
                return true;

            defaultValue = null;
            return false;
        }

        private object? GetDefaultValueFromCustomAttributeData()
        {
            foreach (CustomAttributeData attributeData in GetCustomAttributesData())
            {
                Type attributeType = attributeData.AttributeType;
                if (attributeType == typeof(DecimalConstantAttribute))
                {
                    return GetRawDecimalConstant(attributeData);
                }
                else if (attributeType.IsSubclassOf(typeof(CustomConstantAttribute)))
                {
                    if (attributeType == typeof(DateTimeConstantAttribute))
                    {
                        return GetRawDateTimeConstant(attributeData);
                    }
                    return GetRawConstant(attributeData);
                }
            }
            return DBNull.Value;
        }

        private object? GetDefaultValueFromCustomAttributes()
        {
            object[] customAttributes = GetCustomAttributes(typeof(CustomConstantAttribute), false);
            if (customAttributes.Length != 0)
                return ((CustomConstantAttribute)customAttributes[0]).Value;

            customAttributes = GetCustomAttributes(typeof(DecimalConstantAttribute), false);
            if (customAttributes.Length != 0)
                return ((DecimalConstantAttribute)customAttributes[0]).Value;

            return DBNull.Value;
        }

        private static decimal GetRawDecimalConstant(CustomAttributeData attr)
        {
            Debug.Assert(attr.Constructor.DeclaringType == typeof(DecimalConstantAttribute));
            IList<CustomAttributeTypedArgument> args = attr.ConstructorArguments;
            Debug.Assert(args.Count == 5);

            return new decimal(
                lo: GetConstructorArgument(args, 4),
                mid: GetConstructorArgument(args, 3),
                hi: GetConstructorArgument(args, 2),
                isNegative: ((byte)args[1].Value!) != 0,
                scale: (byte)args[0].Value!);

            static int GetConstructorArgument(IList<CustomAttributeTypedArgument> args, int index)
            {
                // The constructor is overloaded to accept both signed and unsigned arguments
                object obj = args[index].Value!;
                return (obj is int value) ? value : (int)(uint)obj;
            }
        }

        private static DateTime GetRawDateTimeConstant(CustomAttributeData attr)
        {
            Debug.Assert(attr.Constructor.DeclaringType == typeof(DateTimeConstantAttribute));
            Debug.Assert(attr.ConstructorArguments.Count == 1);

            return new DateTime((long)attr.ConstructorArguments[0].Value!);
        }

        private static object? GetRawConstant(CustomAttributeData attr)
        {
            Debug.Assert(attr.AttributeType.IsSubclassOf(typeof(CustomConstantAttribute)));

            // We are relying only on named arguments for historical reasons
            foreach (CustomAttributeNamedArgument namedArgument in attr.NamedArguments)
            {
                if (namedArgument.MemberInfo.Name.Equals("Value"))
                    return namedArgument.TypedValue.Value;
            }
            return DBNull.Value;
        }

        private readonly Parameter _parameter;
    }
}