Skip to content

Commit

Permalink
Split the MarshalAsParser into two classes (dotnet#93038)
Browse files Browse the repository at this point in the history
  • Loading branch information
jkoritzinsky authored Oct 5, 2023
1 parent e66bc8a commit 55be11c
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -151,15 +151,15 @@ private static MarshallingInfoParser CreateComImportMarshallingInfoParser(StubEn
var defaultInfo = new DefaultMarshallingInfo(CharEncoding.Utf16, null);

var useSiteAttributeParsers = ImmutableArray.Create<IUseSiteAttributeParser>(
new MarshalAsAttributeParser(env.Compilation, diagnostics, defaultInfo),
new MarshalAsAttributeParser(diagnostics, defaultInfo),
new MarshalUsingAttributeParser(env.Compilation, diagnostics));

return new MarshallingInfoParser(
diagnostics,
new MethodSignatureElementInfoProvider(env.Compilation, diagnostics, method, useSiteAttributeParsers),
useSiteAttributeParsers,
ImmutableArray.Create<IMarshallingInfoAttributeParser>(
new MarshalAsAttributeParser(env.Compilation, diagnostics, defaultInfo),
new MarshalAsWithCustomMarshallersParser(env.Compilation, diagnostics, new MarshalAsAttributeParser(diagnostics, defaultInfo)),
new MarshalUsingAttributeParser(env.Compilation, diagnostics),
new NativeMarshallingAttributeParser(env.Compilation, diagnostics),
new ComInterfaceMarshallingInfoProvider(env.Compilation)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ public static MarshallingInfoParser Create(StubEnvironment env, GeneratorDiagnos
var defaultInfo = new DefaultMarshallingInfo(defaultEncoding, interopAttributeData.StringMarshallingCustomType);

var useSiteAttributeParsers = ImmutableArray.Create<IUseSiteAttributeParser>(
new MarshalAsAttributeParser(env.Compilation, diagnostics, defaultInfo),
new MarshalAsAttributeParser(diagnostics, defaultInfo),
new MarshalUsingAttributeParser(env.Compilation, diagnostics));

return new MarshallingInfoParser(
diagnostics,
new MethodSignatureElementInfoProvider(env.Compilation, diagnostics, method, useSiteAttributeParsers),
useSiteAttributeParsers,
ImmutableArray.Create<IMarshallingInfoAttributeParser>(
new MarshalAsAttributeParser(env.Compilation, diagnostics, defaultInfo),
new MarshalAsWithCustomMarshallersParser(env.Compilation, diagnostics, new MarshalAsAttributeParser(diagnostics, defaultInfo)),
new MarshalUsingAttributeParser(env.Compilation, diagnostics),
new NativeMarshallingAttributeParser(env.Compilation, diagnostics),
new ComInterfaceMarshallingInfoProvider(env.Compilation)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Microsoft.Interop
/// <summary>
/// Simple User-application of System.Runtime.InteropServices.MarshalAsAttribute
/// </summary>
public sealed record MarshalAsInfo(
public abstract record MarshalAsInfo(
UnmanagedType UnmanagedType,
CharEncoding CharEncoding) : MarshallingInfoStringSupport(CharEncoding)
{
Expand All @@ -21,18 +21,25 @@ public sealed record MarshalAsInfo(
internal const UnmanagedType UnmanagedType_LPUTF8Str = (UnmanagedType)0x30;
}

public sealed record MarshalAsScalarInfo(
UnmanagedType UnmanagedType,
CharEncoding CharEncoding) : MarshalAsInfo(UnmanagedType, CharEncoding);

public sealed record MarshalAsArrayInfo(
UnmanagedType UnmanagedType,
CharEncoding CharEncoding,
UnmanagedType ArraySubType): MarshalAsInfo(UnmanagedType, CharEncoding);

/// <summary>
/// This class suppports parsing a System.Runtime.InteropServices.MarshalAsAttribute.
/// This class suppports parsing a System.Runtime.InteropServices.MarshalAsAttribute into a <see cref="MarshalAsInfo"/>.
/// </summary>
public sealed class MarshalAsAttributeParser : IMarshallingInfoAttributeParser, IUseSiteAttributeParser
{
private readonly Compilation _compilation;
private readonly GeneratorDiagnosticsBag _diagnostics;
private readonly DefaultMarshallingInfo _defaultInfo;

public MarshalAsAttributeParser(Compilation compilation, GeneratorDiagnosticsBag diagnostics, DefaultMarshallingInfo defaultInfo)
public MarshalAsAttributeParser(GeneratorDiagnosticsBag diagnostics, DefaultMarshallingInfo defaultInfo)
{
_compilation = compilation;
_diagnostics = diagnostics;
_defaultInfo = defaultInfo;
}
Expand Down Expand Up @@ -97,84 +104,9 @@ UseSiteAttributeData IUseSiteAttributeParser.ParseAttribute(AttributeData attrib
}
}

// We'll support the UnmanagedType.Interface option, but we'll explicitly
// exclude ComImport types as they will not work as expected
// unless they are migrated to [GeneratedComInterface].
if (unmanagedType == UnmanagedType.Interface)
{
if (type is INamedTypeSymbol { IsComImport: true })
{
return new MarshalAsInfo(unmanagedType, _defaultInfo.CharEncoding);
}
return ComInterfaceMarshallingInfoProvider.CreateComInterfaceMarshallingInfo(_compilation, type);
}

if (isArrayType)
{
if (type is not IArrayTypeSymbol { ElementType: ITypeSymbol elementType })
{
_diagnostics.ReportConfigurationNotSupported(attributeData, nameof(UnmanagedType), unmanagedType.ToString());
return NoMarshallingInfo.Instance;
}

MarshallingInfo elementMarshallingInfo = NoMarshallingInfo.Instance;
if (elementUnmanagedType != (UnmanagedType)SizeAndParamIndexInfo.UnspecifiedConstSize)
{
if (elementType.SpecialType == SpecialType.System_String)
{
elementMarshallingInfo = CreateStringMarshallingInfo(elementType, elementUnmanagedType);
}
else if (elementUnmanagedType == UnmanagedType.Interface && elementType is not INamedTypeSymbol { IsComImport: true })
{
elementMarshallingInfo = ComInterfaceMarshallingInfoProvider.CreateComInterfaceMarshallingInfo(_compilation, elementType);
}
else
{
elementMarshallingInfo = new MarshalAsInfo(elementUnmanagedType, _defaultInfo.CharEncoding);
}
}
else
{
elementMarshallingInfo = marshallingInfoCallback(elementType, useSiteAttributes, indirectionDepth + 1);
}

CountInfo countInfo = NoCountInfo.Instance;

if (useSiteAttributes.TryGetUseSiteAttributeInfo(indirectionDepth, out UseSiteAttributeData useSiteAttributeData))
{
countInfo = useSiteAttributeData.CountInfo;
}

return ArrayMarshallingInfoProvider.CreateArrayMarshallingInfo(_compilation, type, elementType, countInfo, elementMarshallingInfo);
}

if (type.SpecialType == SpecialType.System_String)
{
return CreateStringMarshallingInfo(type, unmanagedType);
}

return new MarshalAsInfo(unmanagedType, _defaultInfo.CharEncoding);
}

private MarshallingInfo CreateStringMarshallingInfo(
ITypeSymbol type,
UnmanagedType unmanagedType)
{
string? marshallerName = 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 new MarshalAsInfo(unmanagedType, _defaultInfo.CharEncoding);
}

return StringMarshallingInfoProvider.CreateStringMarshallingInfo(_compilation, type, marshallerName);
return isArrayType
? new MarshalAsArrayInfo(unmanagedType, _defaultInfo.CharEncoding, elementUnmanagedType)
: new MarshalAsScalarInfo(unmanagedType, _defaultInfo.CharEncoding);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// 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);
}

CountInfo countInfo = NoCountInfo.Instance;

if (useSiteAttributes.TryGetUseSiteAttributeInfo(indirectionDepth, out UseSiteAttributeData useSiteAttributeData))
{
countInfo = useSiteAttributeData.CountInfo;
}

return ArrayMarshallingInfoProvider.CreateArrayMarshallingInfo(_compilation, type, elementType, countInfo, elementMarshallingInfo);
}

if (type.SpecialType == SpecialType.System_String)
{
return CreateStringMarshallingInfo(type, marshalAsInfo);
}

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 StringMarshallingInfoProvider.CreateStringMarshallingInfo(_compilation, type, marshallerName);
}
}
}

0 comments on commit 55be11c

Please sign in to comment.