File: MarshalAsWithCustomMarshallersParser.cs
Web Access
Project: src\src\libraries\System.Runtime.InteropServices\gen\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj (Microsoft.Interop.SourceGeneration)
// 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.Collections.Immutable;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis;
 
namespace Microsoft.Interop
{
    /// <summary>
    /// This class suppports parsing a System.Runtime.InteropServices.MarshalAsAttribute into either a <see cref="MarshalAsInfo"/> or a <see cref="NativeMarshallingAttributeInfo"/>
    /// if the marshalling is implemented with a custom marshaller in the framework.
    /// </summary>
    public sealed class MarshalAsWithCustomMarshallersParser : IMarshallingInfoAttributeParser
    {
        private readonly Compilation _compilation;
        private readonly GeneratorDiagnosticsBag _diagnostics;
        private readonly IMarshallingInfoAttributeParser _marshalAsAttributeParser;
 
        /// <summary>
        /// Create a new instance of <see cref="MarshalAsWithCustomMarshallersParser"/>.
        /// </summary>
        /// <param name="compilation">The compilation that the attributes are defined within.</param>
        /// <param name="diagnostics">The diagnostics bag to which to report diagnostics.</param>
        /// <param name="marshalAsAttributeParser">The parser that will do basic parsing of a MarshalAsAttribute into a <see cref="MarshalAsInfo"/> element.</param>
        public MarshalAsWithCustomMarshallersParser(Compilation compilation, GeneratorDiagnosticsBag diagnostics, IMarshallingInfoAttributeParser marshalAsAttributeParser)
        {
            _compilation = compilation;
            _diagnostics = diagnostics;
            _marshalAsAttributeParser = marshalAsAttributeParser;
        }
 
        public bool CanParseAttributeType(INamedTypeSymbol attributeType) => attributeType.ToDisplayString() == TypeNames.System_Runtime_InteropServices_MarshalAsAttribute;
 
        MarshallingInfo? IMarshallingInfoAttributeParser.ParseAttribute(AttributeData attributeData, ITypeSymbol type, int indirectionDepth, UseSiteAttributeProvider useSiteAttributes, GetMarshallingInfoCallback marshallingInfoCallback)
        {
            var marshalAsInfo = (MarshalAsInfo)_marshalAsAttributeParser.ParseAttribute(attributeData, type, indirectionDepth, useSiteAttributes, marshallingInfoCallback);
 
            // We'll support the UnmanagedType.Interface option, but we'll explicitly
            // leave ComImport types with the MarshalAs info instead of the custom marshaller
            // as they will not work as expected unless they are migrated to [GeneratedComInterface].
            if (marshalAsInfo.UnmanagedType == UnmanagedType.Interface)
            {
                return type is INamedTypeSymbol { IsComImport: true }
                    ? marshalAsInfo
                    : ComInterfaceMarshallingInfoProvider.CreateComInterfaceMarshallingInfo(_compilation, type);
            }
 
            if (marshalAsInfo is MarshalAsArrayInfo arrayInfo)
            {
                if (type is not IArrayTypeSymbol { ElementType: ITypeSymbol elementType })
                {
                    _diagnostics.ReportConfigurationNotSupported(attributeData, nameof(UnmanagedType), arrayInfo.UnmanagedType.ToString());
                    return NoMarshallingInfo.Instance;
                }
 
                MarshallingInfo elementMarshallingInfo = NoMarshallingInfo.Instance;
                if (arrayInfo.ArraySubType != (UnmanagedType)SizeAndParamIndexInfo.UnspecifiedConstSize)
                {
                    if (elementType.SpecialType == SpecialType.System_String)
                    {
                        elementMarshallingInfo = CreateStringMarshallingInfo(elementType, new MarshalAsScalarInfo(arrayInfo.ArraySubType, arrayInfo.CharEncoding));
                    }
                    else if (arrayInfo.ArraySubType == UnmanagedType.Interface && elementType is not INamedTypeSymbol { IsComImport: true })
                    {
                        elementMarshallingInfo = ComInterfaceMarshallingInfoProvider.CreateComInterfaceMarshallingInfo(_compilation, elementType);
                    }
                    else
                    {
                        elementMarshallingInfo = new MarshalAsScalarInfo(arrayInfo.ArraySubType, arrayInfo.CharEncoding);
                    }
                }
                else
                {
                    elementMarshallingInfo = marshallingInfoCallback(elementType, useSiteAttributes, indirectionDepth + 1);
                }
 
                return ArrayMarshallingInfoProvider.CreateArrayMarshallingInfo(_compilation, type, elementType, arrayInfo.CountInfo, elementMarshallingInfo);
            }
 
            if (type.SpecialType == SpecialType.System_String)
            {
                return CreateStringMarshallingInfo(type, marshalAsInfo);
            }
 
            if (type.SpecialType == SpecialType.System_Object && marshalAsInfo is MarshalAsScalarInfo(UnmanagedType.Struct, _))
            {
                return CustomMarshallingInfoHelper.CreateMarshallingInfoByMarshallerTypeName(_compilation, type, TypeNames.ComVariantMarshaller);
            }
 
            return marshalAsInfo;
        }
 
        private MarshallingInfo CreateStringMarshallingInfo(
            ITypeSymbol type,
            MarshalAsInfo marshalAsInfo)
        {
            string? marshallerName = marshalAsInfo.UnmanagedType switch
            {
                UnmanagedType.BStr => TypeNames.BStrStringMarshaller,
                UnmanagedType.LPStr => TypeNames.AnsiStringMarshaller,
                UnmanagedType.LPTStr or UnmanagedType.LPWStr => TypeNames.Utf16StringMarshaller,
                MarshalAsInfo.UnmanagedType_LPUTF8Str => TypeNames.Utf8StringMarshaller,
                _ => null
            };
 
            if (marshallerName is null)
            {
                return marshalAsInfo;
            }
 
            return CustomMarshallingInfoHelper.CreateMarshallingInfoByMarshallerTypeName(_compilation, type, marshallerName);
        }
    }
}