Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still used as a IMarshallingInfoAttributeParser now?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's intended to optionally be used that way. We don't use it in the same way currently, but we delegate to this implementation from the other one through the interface.

{
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);
}
}
}