File: System\Reflection\RuntimeParameterInfo.NativeAot.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.Diagnostics;
using System.Reflection.Runtime.General;
using System.Reflection.Runtime.MethodInfos;
using System.Reflection.Runtime.TypeInfos;

using Internal.Metadata.NativeFormat;

namespace System.Reflection;

internal sealed partial class RuntimeParameterInfo : ParameterInfo
{
    private readonly ParameterHandle _tkParamDef;
    private readonly MetadataReader? _scope;
    private readonly QSignatureTypeHandle _signature;
    private readonly TypeContext _typeContext;

    internal static RuntimeParameterInfo[] GetParameters<TRuntimeMethodCommon>(
        ref TRuntimeMethodCommon method, MethodBase member, RuntimeTypeInfo[] methodTypeArguments)
        where TRuntimeMethodCommon : IRuntimeMethodCommon<TRuntimeMethodCommon>, IEquatable<TRuntimeMethodCommon>
    {
        return GetParameters(ref method, member, methodTypeArguments, out _, fetchReturnParameter: false);
    }

    internal static RuntimeParameterInfo GetReturnParameter<TRuntimeMethodCommon>(
        ref TRuntimeMethodCommon method, MethodBase member, RuntimeTypeInfo[] methodTypeArguments)
        where TRuntimeMethodCommon : IRuntimeMethodCommon<TRuntimeMethodCommon>, IEquatable<TRuntimeMethodCommon>
    {
        GetParameters(ref method, member, methodTypeArguments, out RuntimeParameterInfo? returnParameter, fetchReturnParameter: true);
        Debug.Assert(returnParameter is not null);
        return returnParameter;
    }

    private static RuntimeParameterInfo[] GetParameters<TRuntimeMethodCommon>(
        ref TRuntimeMethodCommon method, MethodBase member, RuntimeTypeInfo[] methodTypeArguments,
        out RuntimeParameterInfo? returnParameter, bool fetchReturnParameter)
        where TRuntimeMethodCommon : IRuntimeMethodCommon<TRuntimeMethodCommon>, IEquatable<TRuntimeMethodCommon>
    {
        TypeContext typeContext = member.DeclaringType.ToRuntimeTypeInfo().TypeContext;
        typeContext = new TypeContext(typeContext.GenericTypeArguments, methodTypeArguments);
        QSignatureTypeHandle[] sig = method.QualifiedMethodSignature;
        Debug.Assert(sig.Length > 0);

        returnParameter = null;
        int sigArgCount = sig.Length - 1;
        RuntimeParameterInfo[] args =
            fetchReturnParameter ? null! :
            sigArgCount == 0 ? [] :
            new RuntimeParameterInfo[sigArgCount];

        MetadataReader scope = method.GetMetadataReader();
        ParameterHandleCollection parameterHandles = method.ParameterHandles;
        int cParamDefs = parameterHandles.Count;
        if (cParamDefs > sigArgCount + 1)
            throw new BadImageFormatException(SR.BadImageFormat_ParameterSignatureMismatch);

        foreach (ParameterHandle parameterHandle in parameterHandles)
        {
            Parameter parameter = scope.GetParameter(parameterHandle);
            int position = parameter.Sequence - 1;

            if (fetchReturnParameter && position == -1)
            {
                if (returnParameter is not null)
                    throw new BadImageFormatException(SR.BadImageFormat_ParameterSignatureMismatch);

                returnParameter = new RuntimeParameterInfo(sig[0], typeContext, scope, parameterHandle, position, parameter.Flags, member);
            }
            else if (!fetchReturnParameter && position >= 0)
            {
                if (position >= sigArgCount)
                    throw new BadImageFormatException(SR.BadImageFormat_ParameterSignatureMismatch);

                args[position] = new RuntimeParameterInfo(sig[position + 1], typeContext, scope, parameterHandle, position, parameter.Flags, member);
            }
        }

        if (fetchReturnParameter)
        {
            returnParameter ??= new RuntimeParameterInfo(sig[0], typeContext, null, default, -1, ParameterAttributes.None, member);
        }
        else if (cParamDefs < args.Length + 1)
        {
            for (int i = 0; i < args.Length; i++)
            {
                args[i] ??= new RuntimeParameterInfo(sig[i + 1], typeContext, null, default, i, ParameterAttributes.None, member);
            }
        }

        return args;
    }

    private RuntimeParameterInfo(
        QSignatureTypeHandle signature, TypeContext typeContext, MetadataReader? scope, ParameterHandle parameterHandle,
        int position, ParameterAttributes attributes, MemberInfo member)
    {
        Debug.Assert(parameterHandle.IsNil == (scope is null));

        PositionImpl = position;
        MemberImpl = member;
        _signature = signature;
        _typeContext = typeContext;
        _tkParamDef = parameterHandle;
        _scope = scope;
        AttrsImpl = attributes;
    }

    // Array members have no metadata signatures, but otherwise behave like ordinary tokenless parameters.
    internal RuntimeParameterInfo(MemberInfo owner, Type parameterType, int position)
    {
        MemberImpl = owner;
        ClassImpl = parameterType;
        PositionImpl = position;
        _nameIsCached = true;
    }

    private object? GetDefaultValueFromMetadata(bool raw)
    {
        Debug.Assert(_scope is not null && !_tkParamDef.IsNil);
        Type parameterType = ParameterType;
        Handle constantHandle = _scope.GetParameter(_tkParamDef).DefaultValue;
        if (constantHandle.IsNil)
            return DBNull.Value;

        object? value = constantHandle.ParseConstantValue(_scope);
        if (parameterType.IsEnum && !raw)
        {
            if (value is null)
                return null;

            if (value is not (char or sbyte or byte or short or ushort or int or uint or long or ulong))
                throw new FormatException(SR.Arg_BadLiteralFormat);

            if (!parameterType.ContainsGenericParameters)
                return Enum.ToObject(parameterType, value);
        }
        else if (parameterType == typeof(DateTime))
        {
            return value switch
            {
                null => null,
                long ticks => new DateTime(ticks),
                ulong ticks => new DateTime(unchecked((long)ticks)),
                _ => throw new FormatException(SR.Arg_BadLiteralFormat),
            };
        }

        return value;
    }

    internal MetadataReader? GetMetadataReader() => _scope;

    internal CustomAttributeHandleCollection GetCustomAttributeHandles()
    {
        if (_tkParamDef.IsNil)
            return default;

        Debug.Assert(_scope is not null);
        return _scope.GetParameter(_tkParamDef).CustomAttributes;
    }

    public override int MetadataToken => _tkParamDef.IsNil
        ? base.MetadataToken
        : throw new InvalidOperationException(SR.NoMetadataTokenAvailable);

    public override Type[] GetRequiredCustomModifiers() =>
        _signature.Reader is null ? [] : _signature.GetCustomModifiers(_typeContext, optional: false);

    public override Type[] GetOptionalCustomModifiers() =>
        _signature.Reader is null ? [] : _signature.GetCustomModifiers(_typeContext, optional: true);

    public override Type GetModifiedParameterType() =>
        _signature.GetModifiedType(ParameterType);
}