未验证 提交 bc9c3710 编写于 作者: J Jeremy Koritzinsky 提交者: GitHub

Refactor marshalling info parser to split overall logic from ordering (#72687)

* Refactor marshalling info parser to split overall logic from ordering

Split our marshalling attribute parser into separate classes for each attribute and category and make the parser itself only handle executing the various different stages of marshalling info parsing. All logic for parsing a given attribute is handled now by the separate classes.

This PR allows the JS marshaller to reuse the core of our parsing and opt-in to more of the logic as they see fit.

* Add doc comments for all of the new APIs

* Fix failing test

* Make the JS known managed type info hang off the JS marshalling info instead of inheriting from ManagedTypeInfo. ManagedTypeInfo is meant to represent just enough info from an ITypeSymbol that we can accurately generate code based on any language/typesystem rules. It is not meant for storing generator-specific marshalling info.

* Create JSTypeInfo based on symbols, not type name string parsing.

* Apply suggestions from code review
Co-authored-by: NMarek Fišera <mara@neptuo.com>

* PR feedback.

* Add comments
Co-authored-by: NMarek Fišera <mara@neptuo.com>
上级 ca916f28
// 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.Text;
using Microsoft.CodeAnalysis;
namespace Microsoft.Interop.JavaScript
{
/// <summary>
/// Always returns a JSMissingMarshallingInfo.
/// </summary>
internal sealed class FallbackJSMarshallingInfoProvider : ITypeBasedMarshallingInfoProvider
{
public bool CanProvideMarshallingInfoForType(ITypeSymbol type) => true;
public MarshallingInfo GetMarshallingInfo(ITypeSymbol type, int indirectionDepth, UseSiteAttributeProvider useSiteAttributes, GetMarshallingInfoCallback marshallingInfoCallback)
{
return new JSMissingMarshallingInfo(JSTypeInfo.CreateJSTypeInfoForTypeSymbol(type));
}
}
}
......@@ -40,9 +40,9 @@ internal sealed class JSExportCodeGenerator : JSCodeGenerator
}
// validate task + span mix
if (_marshallers.ManagedReturnMarshaller.TypeInfo.ManagedType is JSTaskTypeInfo)
if (_marshallers.ManagedReturnMarshaller.TypeInfo.MarshallingAttributeInfo is JSMarshallingInfo(_, JSTaskTypeInfo))
{
BoundGenerator spanArg = _marshallers.AllMarshallers.FirstOrDefault(m => m.TypeInfo.ManagedType is JSSpanTypeInfo);
BoundGenerator spanArg = _marshallers.AllMarshallers.FirstOrDefault(m => m.TypeInfo.MarshallingAttributeInfo is JSMarshallingInfo(_, JSSpanTypeInfo));
if (spanArg != default)
{
marshallingNotSupportedCallback(spanArg.TypeInfo, new MarshallingNotSupportedException(spanArg.TypeInfo, _context)
......
......@@ -7,6 +7,7 @@
using Microsoft.CodeAnalysis;
using System.Collections.Generic;
using System.Diagnostics;
using System.Data;
namespace Microsoft.Interop.JavaScript
{
......@@ -34,51 +35,51 @@ Exception fail(string failReason)
}
bool isToJs = info.ManagedIndex != TypePositionInfo.ReturnIndex ^ context is JSExportCodeContext;
switch (info)
switch (jsMarshalingInfo)
{
// invalid
case { ManagedType: JSInvalidTypeInfo }:
case { TypeInfo: JSInvalidTypeInfo }:
throw new MarshallingNotSupportedException(info, context);
// void
case { ManagedType: SpecialTypeInfo sd } when sd.SpecialType == SpecialType.System_Void && jsMarshalingInfo.JSType == JSTypeFlags.Discard:
case { ManagedType: SpecialTypeInfo sv } when sv.SpecialType == SpecialType.System_Void && jsMarshalingInfo.JSType == JSTypeFlags.Void:
case { ManagedType: SpecialTypeInfo sn } when sn.SpecialType == SpecialType.System_Void && jsMarshalingInfo.JSType == JSTypeFlags.None:
case { ManagedType: SpecialTypeInfo sm } when sm.SpecialType == SpecialType.System_Void && jsMarshalingInfo.JSType == JSTypeFlags.Missing:
case { TypeInfo: JSSimpleTypeInfo(KnownManagedType.Void), JSType: JSTypeFlags.Discard }:
case { TypeInfo: JSSimpleTypeInfo(KnownManagedType.Void), JSType: JSTypeFlags.Void }:
case { TypeInfo: JSSimpleTypeInfo(KnownManagedType.Void), JSType: JSTypeFlags.None }:
case { TypeInfo: JSSimpleTypeInfo(KnownManagedType.Void), JSType: JSTypeFlags.Missing }:
return new VoidGenerator(jsMarshalingInfo.JSType == JSTypeFlags.Void ? MarshalerType.Void : MarshalerType.Discard);
// discard no void
case { } when jsMarshalingInfo.JSType == JSTypeFlags.Discard:
case { JSType: JSTypeFlags.Discard }:
throw fail(SR.DiscardOnlyVoid);
// primitive
case { ManagedType: JSSimpleTypeInfo simple }:
case { TypeInfo: JSSimpleTypeInfo simple }:
return Create(info, isToJs, simple.KnownType, Array.Empty<KnownManagedType>(), jsMarshalingInfo.JSType, Array.Empty<JSTypeFlags>(), fail);
// nullable
case { ManagedType: JSNullableTypeInfo nullable }:
case { TypeInfo: JSNullableTypeInfo nullable }:
return Create(info, isToJs, nullable.KnownType, new[] { nullable.ResultTypeInfo.KnownType }, jsMarshalingInfo.JSType, null, fail);
// array
case { ManagedType: JSArrayTypeInfo array }:
case { TypeInfo: JSArrayTypeInfo array }:
return Create(info, isToJs, array.KnownType, new[] { array.ElementTypeInfo.KnownType }, jsMarshalingInfo.JSType, jsMarshalingInfo.JSTypeArguments, fail);
// array segment
case { ManagedType: JSArraySegmentTypeInfo segment }:
case { TypeInfo: JSArraySegmentTypeInfo segment }:
return Create(info, isToJs, segment.KnownType, new[] { segment.ElementTypeInfo.KnownType }, jsMarshalingInfo.JSType, jsMarshalingInfo.JSTypeArguments, fail);
// span
case { ManagedType: JSSpanTypeInfo span }:
case { TypeInfo: JSSpanTypeInfo span }:
return Create(info, isToJs, span.KnownType, new[] { span.ElementTypeInfo.KnownType }, jsMarshalingInfo.JSType, jsMarshalingInfo.JSTypeArguments, fail);
// task
case { ManagedType: JSTaskTypeInfo task } when task.ResultTypeInfo is JSSimpleTypeInfo taskRes && taskRes.FullTypeName == "void":
case { TypeInfo: JSTaskTypeInfo(JSSimpleTypeInfo(KnownManagedType.Void)) task }:
return Create(info, isToJs, task.KnownType, Array.Empty<KnownManagedType>(), jsMarshalingInfo.JSType, jsMarshalingInfo.JSTypeArguments, fail);
case { ManagedType: JSTaskTypeInfo task }:
case { TypeInfo: JSTaskTypeInfo task }:
return Create(info, isToJs, task.KnownType, new[] { task.ResultTypeInfo.KnownType }, jsMarshalingInfo.JSType, jsMarshalingInfo.JSTypeArguments, fail);
// action + function
case { ManagedType: JSFunctionTypeInfo function }:
case { TypeInfo: JSFunctionTypeInfo function }:
return Create(info, isToJs, function.KnownType, function.ArgsTypeInfo.Select(a => a.KnownType).ToArray(), jsMarshalingInfo.JSType, jsMarshalingInfo.JSTypeArguments, fail);
default:
......
......@@ -47,9 +47,9 @@ internal sealed class JSImportCodeGenerator : JSCodeGenerator
}
// validate task + span mix
if (_marshallers.ManagedReturnMarshaller.TypeInfo.ManagedType is JSTaskTypeInfo)
if (_marshallers.ManagedReturnMarshaller.TypeInfo.MarshallingAttributeInfo is JSMarshallingInfo(_, JSTaskTypeInfo))
{
BoundGenerator spanArg = _marshallers.AllMarshallers.FirstOrDefault(m => m.TypeInfo.ManagedType is JSSpanTypeInfo);
BoundGenerator spanArg = _marshallers.AllMarshallers.FirstOrDefault(m => m.TypeInfo.MarshallingAttributeInfo is JSMarshallingInfo(_, JSSpanTypeInfo));
if (spanArg != default)
{
marshallingNotSupportedCallback(spanArg.TypeInfo, new MarshallingNotSupportedException(spanArg.TypeInfo, _context)
......
......@@ -145,19 +145,22 @@ static bool IsSkipLocalsInitAttribute(AttributeData a)
private static (ImmutableArray<TypePositionInfo>, IMarshallingGeneratorFactory) GenerateTypeInformation(IMethodSymbol method, GeneratorDiagnostics diagnostics, StubEnvironment env)
{
var jsMarshallingAttributeParser = new JSMarshallingAttributeInfoParser(env.Compilation, diagnostics, method);
ImmutableArray<IUseSiteAttributeParser> useSiteAttributeParsers = ImmutableArray.Create<IUseSiteAttributeParser>(new JSMarshalAsAttributeParser(env.Compilation));
var jsMarshallingAttributeParser = new MarshallingInfoParser(
diagnostics,
new MethodSignatureElementInfoProvider(env.Compilation, diagnostics, method, useSiteAttributeParsers),
useSiteAttributeParsers,
ImmutableArray.Create<IMarshallingInfoAttributeParser>(new JSMarshalAsAttributeParser(env.Compilation)),
ImmutableArray.Create<ITypeBasedMarshallingInfoProvider>(new FallbackJSMarshallingInfoProvider()));
// Determine parameter and return types
ImmutableArray<TypePositionInfo>.Builder typeInfos = ImmutableArray.CreateBuilder<TypePositionInfo>();
for (int i = 0; i < method.Parameters.Length; i++)
{
IParameterSymbol param = method.Parameters[i];
MarshallingInfo marshallingInfo = NoMarshallingInfo.Instance;
MarshallingInfo jsMarshallingInfo = jsMarshallingAttributeParser.ParseMarshallingInfo(param.Type, param.GetAttributes(), marshallingInfo);
MarshallingInfo jsMarshallingInfo = jsMarshallingAttributeParser.ParseMarshallingInfo(param.Type, param.GetAttributes());
var typeInfo = TypePositionInfo.CreateForParameter(param, marshallingInfo, env.Compilation);
typeInfo = JSTypeInfo.CreateForType(typeInfo, param.Type, jsMarshallingInfo, env.Compilation);
typeInfo = typeInfo with
var typeInfo = TypePositionInfo.CreateForParameter(param, jsMarshallingInfo, env.Compilation) with
{
ManagedIndex = i,
NativeIndex = typeInfos.Count,
......@@ -165,12 +168,9 @@ private static (ImmutableArray<TypePositionInfo>, IMarshallingGeneratorFactory)
typeInfos.Add(typeInfo);
}
MarshallingInfo retMarshallingInfo = NoMarshallingInfo.Instance;
MarshallingInfo retJSMarshallingInfo = jsMarshallingAttributeParser.ParseMarshallingInfo(method.ReturnType, method.GetReturnTypeAttributes(), retMarshallingInfo);
MarshallingInfo retJSMarshallingInfo = jsMarshallingAttributeParser.ParseMarshallingInfo(method.ReturnType, method.GetReturnTypeAttributes());
var retTypeInfo = new TypePositionInfo(ManagedTypeInfo.CreateTypeInfoForTypeSymbol(method.ReturnType), retMarshallingInfo);
retTypeInfo = JSTypeInfo.CreateForType(retTypeInfo, method.ReturnType, retJSMarshallingInfo, env.Compilation);
retTypeInfo = retTypeInfo with
var retTypeInfo = new TypePositionInfo(ManagedTypeInfo.CreateTypeInfoForTypeSymbol(method.ReturnType), retJSMarshallingInfo)
{
ManagedIndex = TypePositionInfo.ReturnIndex,
NativeIndex = TypePositionInfo.ReturnIndex,
......
// 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.Data;
using System.Linq;
using System.Runtime.InteropServices.JavaScript;
using Microsoft.CodeAnalysis;
namespace Microsoft.Interop.JavaScript
{
internal sealed class JSMarshalAsAttributeParser : IMarshallingInfoAttributeParser, IUseSiteAttributeParser
{
private readonly INamedTypeSymbol _jsMarshalAsAttribute;
public JSMarshalAsAttributeParser(Compilation compilation)
{
_jsMarshalAsAttribute = compilation.GetTypeByMetadataName(Constants.JSMarshalAsAttribute)!.ConstructUnboundGenericType();
}
public bool CanParseAttributeType(INamedTypeSymbol attributeType) => attributeType.IsGenericType && SymbolEqualityComparer.Default.Equals(_jsMarshalAsAttribute, attributeType.ConstructUnboundGenericType());
public MarshallingInfo ParseAttribute(AttributeData attributeData, ITypeSymbol type, int indirectionDepth, UseSiteAttributeProvider useSiteAttributes, GetMarshallingInfoCallback marshallingInfoCallback)
{
JSTypeFlags jsType = JSTypeFlags.None;
List<JSTypeFlags> jsTypeArguments = new List<JSTypeFlags>();
INamedTypeSymbol? jsTypeArgs = attributeData.AttributeClass.TypeArguments[0] as INamedTypeSymbol;
if (jsTypeArgs.IsGenericType)
{
string gt = jsTypeArgs.ConstructUnboundGenericType().ToDisplayString();
string name = gt.Substring(gt.IndexOf("JSType") + "JSType.".Length);
name = name.Substring(0, name.IndexOf("<"));
Enum.TryParse(name, out jsType);
foreach (var ta in jsTypeArgs.TypeArguments.Cast<INamedTypeSymbol>().Select(x => x.ToDisplayString()))
{
string argName = ta.Substring(ta.IndexOf("JSType") + "JSType.".Length);
JSTypeFlags jsTypeArg = JSTypeFlags.None;
Enum.TryParse(argName, out jsTypeArg);
jsTypeArguments.Add(jsTypeArg);
}
}
else
{
string st = jsTypeArgs.ToDisplayString();
string name = st.Substring(st.IndexOf("JSType") + "JSType.".Length);
Enum.TryParse(name, out jsType);
}
if (jsType == JSTypeFlags.None)
{
return new JSMissingMarshallingInfo(JSTypeInfo.CreateJSTypeInfoForTypeSymbol(type));
}
return new JSMarshallingInfo(NoMarshallingInfo.Instance, JSTypeInfo.CreateJSTypeInfoForTypeSymbol(type))
{
JSType = jsType,
JSTypeArguments = jsTypeArguments.ToArray(),
};
}
UseSiteAttributeData IUseSiteAttributeParser.ParseAttribute(AttributeData attributeData, IElementInfoProvider elementInfoProvider, GetMarshallingInfoCallback marshallingInfoCallback)
{
return new UseSiteAttributeData(0, NoCountInfo.Instance, attributeData);
}
}
}
// 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.Linq;
using System.Runtime.InteropServices.JavaScript;
using Microsoft.CodeAnalysis;
namespace Microsoft.Interop.JavaScript
{
public sealed class JSMarshallingAttributeInfoParser
{
private readonly ITypeSymbol _jsMarshalAsAttribute;
private readonly ITypeSymbol _marshalUsingAttribute;
public JSMarshallingAttributeInfoParser(
Compilation compilation,
IGeneratorDiagnostics diagnostics,
ISymbol contextSymbol)
{
_jsMarshalAsAttribute = compilation.GetTypeByMetadataName(Constants.JSMarshalAsAttribute)!.ConstructUnboundGenericType();
_marshalUsingAttribute = compilation.GetTypeByMetadataName(Constants.MarshalUsingAttribute)!;
}
public MarshallingInfo ParseMarshallingInfo(
ITypeSymbol managedType,
IEnumerable<AttributeData> useSiteAttributes,
MarshallingInfo inner)
{
JSTypeFlags jsType = JSTypeFlags.None;
List<JSTypeFlags> jsTypeArguments = new List<JSTypeFlags>();
foreach (AttributeData useSiteAttribute in useSiteAttributes)
{
INamedTypeSymbol attributeClass = useSiteAttribute.AttributeClass!;
if (attributeClass.IsGenericType && SymbolEqualityComparer.Default.Equals(_jsMarshalAsAttribute, attributeClass.ConstructUnboundGenericType()))
{
INamedTypeSymbol? jsTypeArgs = attributeClass.TypeArguments[0] as INamedTypeSymbol;
if (jsTypeArgs.IsGenericType)
{
string gt = jsTypeArgs.ConstructUnboundGenericType().ToDisplayString();
string name = gt.Substring(gt.IndexOf("JSType") + "JSType.".Length);
name = name.Substring(0, name.IndexOf("<"));
Enum.TryParse(name, out jsType);
foreach (var ta in jsTypeArgs.TypeArguments.Cast<INamedTypeSymbol>().Select(x => x.ToDisplayString()))
{
string argName = ta.Substring(ta.IndexOf("JSType") + "JSType.".Length);
JSTypeFlags jsTypeArg = JSTypeFlags.None;
Enum.TryParse(argName, out jsTypeArg);
jsTypeArguments.Add(jsTypeArg);
}
}
else
{
string st = jsTypeArgs.ToDisplayString();
string name = st.Substring(st.IndexOf("JSType") + "JSType.".Length);
Enum.TryParse(name, out jsType);
}
}
if (SymbolEqualityComparer.Default.Equals(_marshalUsingAttribute, attributeClass))
{
return new JSMarshallingInfo(inner)
{
JSType = JSTypeFlags.Array,
JSTypeArguments = Array.Empty<JSTypeFlags>(),
};
}
}
if (jsType == JSTypeFlags.None)
{
return new JSMissingMarshallingInfo();
}
return new JSMarshallingInfo(inner)
{
JSType = jsType,
JSTypeArguments = jsTypeArguments.ToArray(),
};
}
}
}
......@@ -6,26 +6,24 @@
namespace Microsoft.Interop.JavaScript
{
internal record JSMarshallingInfo : MarshallingInfo
internal record JSMarshallingInfo(MarshallingInfo Inner, JSTypeInfo TypeInfo) : MarshallingInfo
{
public MarshallingInfo Inner;
public JSTypeFlags JSType;
public JSTypeFlags[] JSTypeArguments;
public JSMarshallingInfo(MarshallingInfo inner)
{
Inner = inner;
}
protected JSMarshallingInfo()
:this(NoMarshallingInfo.Instance, new JSInvalidTypeInfo())
{
Inner = null;
}
public JSTypeFlags JSType { get; init; }
public JSTypeFlags[] JSTypeArguments { get; init; }
}
internal sealed record JSMissingMarshallingInfo : JSMarshallingInfo
{
public JSMissingMarshallingInfo()
public JSMissingMarshallingInfo(JSTypeInfo typeInfo)
{
JSType = JSTypeFlags.Missing;
TypeInfo = typeInfo;
}
}
}
......@@ -48,9 +48,9 @@ public override IEnumerable<StatementSyntax> Generate(TypePositionInfo info, Stu
? Argument(IdentifierName(context.GetIdentifiers(info).native))
: _inner.AsArgument(info, context);
var jsty = (JSFunctionTypeInfo)info.ManagedType;
var jsty = (JSFunctionTypeInfo)((JSMarshallingInfo)info.MarshallingAttributeInfo).TypeInfo;
var sourceTypes = jsty.ArgsTypeInfo
.Select(a => ParseTypeName(a.FullTypeName))
.Select(a => a.Syntax)
.ToArray();
if (context.CurrentStage == StubCodeContext.Stage.Unmarshal && context.Direction == CustomTypeMarshallingDirection.In && info.IsManagedReturnPosition)
......
......@@ -20,8 +20,8 @@ public TaskJSGenerator(MarshalerType resultMarshalerType)
public override IEnumerable<ExpressionSyntax> GenerateBind(TypePositionInfo info, StubCodeContext context)
{
var jsty = (JSTaskTypeInfo)info.ManagedType;
if (jsty.ResultTypeInfo.FullTypeName == "void")
var jsty = (JSTaskTypeInfo)((JSMarshallingInfo)info.MarshallingAttributeInfo).TypeInfo;
if (jsty.ResultTypeInfo is JSSimpleTypeInfo(KnownManagedType.Void))
{
yield return InvocationExpression(MarshalerTypeName(MarshalerType.Task), ArgumentList());
}
......@@ -34,7 +34,7 @@ public override IEnumerable<ExpressionSyntax> GenerateBind(TypePositionInfo info
public override IEnumerable<StatementSyntax> Generate(TypePositionInfo info, StubCodeContext context)
{
var jsty = (JSTaskTypeInfo)info.ManagedType;
var jsty = (JSTaskTypeInfo)((JSMarshallingInfo)info.MarshallingAttributeInfo).TypeInfo;
string argName = context.GetAdditionalIdentifier(info, "js_arg");
var target = info.IsManagedReturnPosition
......@@ -47,14 +47,14 @@ public override IEnumerable<StatementSyntax> Generate(TypePositionInfo info, Stu
if (context.CurrentStage == StubCodeContext.Stage.Unmarshal && context.Direction == CustomTypeMarshallingDirection.In && info.IsManagedReturnPosition)
{
yield return jsty.ResultTypeInfo.FullTypeName == "void"
yield return jsty.ResultTypeInfo is JSSimpleTypeInfo(KnownManagedType.Void)
? ToManagedMethodVoid(target, source)
: ToManagedMethod(target, source, jsty.ResultTypeInfo.Syntax);
}
if (context.CurrentStage == StubCodeContext.Stage.Marshal && context.Direction == CustomTypeMarshallingDirection.Out && info.IsManagedReturnPosition)
{
yield return jsty.ResultTypeInfo.FullTypeName == "void"
yield return jsty.ResultTypeInfo is JSSimpleTypeInfo(KnownManagedType.Void)
? ToJSMethodVoid(target, source)
: ToJSMethod(target, source, jsty.ResultTypeInfo.Syntax);
}
......@@ -66,14 +66,14 @@ public override IEnumerable<StatementSyntax> Generate(TypePositionInfo info, Stu
if (context.CurrentStage == StubCodeContext.Stage.Invoke && context.Direction == CustomTypeMarshallingDirection.In && !info.IsManagedReturnPosition)
{
yield return jsty.ResultTypeInfo.FullTypeName == "void"
yield return jsty.ResultTypeInfo is JSSimpleTypeInfo(KnownManagedType.Void)
? ToJSMethodVoid(target, source)
: ToJSMethod(target, source, jsty.ResultTypeInfo.Syntax);
}
if (context.CurrentStage == StubCodeContext.Stage.Unmarshal && context.Direction == CustomTypeMarshallingDirection.Out && !info.IsManagedReturnPosition)
{
yield return jsty.ResultTypeInfo.FullTypeName == "void"
yield return jsty.ResultTypeInfo is JSSimpleTypeInfo(KnownManagedType.Void)
? ToManagedMethodVoid(target, source)
: ToManagedMethod(target, source, jsty.ResultTypeInfo.Syntax);
}
......
......@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis;
......@@ -86,7 +87,8 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo
// later user work.
AnyDiagnosticsSink diagnostics = new();
StubEnvironment env = context.Compilation.CreateStubEnvironment();
SignatureContext targetSignatureContext = SignatureContext.Create(method, CreateInteropAttributeDataFromDllImport(dllImportData), env, diagnostics, typeof(ConvertToLibraryImportAnalyzer).Assembly);
AttributeData dllImportAttribute = method.GetAttributes().First(attr => attr.AttributeClass.ToDisplayString() == TypeNames.DllImportAttribute);
SignatureContext targetSignatureContext = SignatureContext.Create(method, CreateInteropAttributeDataFromDllImport(dllImportData), env, diagnostics, dllImportAttribute, typeof(ConvertToLibraryImportAnalyzer).Assembly);
var generatorFactoryKey = LibraryImportGeneratorHelpers.CreateGeneratorFactory(env, new LibraryImportGeneratorOptions(context.Options.AnalyzerConfigOptionsProvider.GlobalOptions));
......
......@@ -312,7 +312,7 @@ private static SyntaxTokenList StripTriviaFromModifiers(SyntaxTokenList tokenLis
}
// Create the stub.
var signatureContext = SignatureContext.Create(symbol, libraryImportData, environment, generatorDiagnostics, typeof(LibraryImportGenerator).Assembly);
var signatureContext = SignatureContext.Create(symbol, libraryImportData, environment, generatorDiagnostics, generatedDllImportAttr, typeof(LibraryImportGenerator).Assembly);
var containingTypeContext = new ContainingSyntaxContext(originalSyntax);
......
// 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.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
namespace Microsoft.Interop
{
/// <summary>
/// Marshalling information provider for single-dimensional zero-based array types using the <c>System.Runtime.InteropServices.Marshalling.ArrayMarshaller</c> and <c>System.Runtime.InteropServices.Marshalling.PointerArrayMarshaller</c>
/// built-in types.
/// </summary>
public sealed class ArrayMarshallingInfoProvider : ITypeBasedMarshallingInfoProvider
{
private readonly Compilation _compilation;
public ArrayMarshallingInfoProvider(Compilation compilation)
{
_compilation = compilation;
}
public bool CanProvideMarshallingInfoForType(ITypeSymbol type) => type is IArrayTypeSymbol { IsSZArray: true };
public MarshallingInfo GetMarshallingInfo(ITypeSymbol type, int indirectionDepth, UseSiteAttributeProvider useSiteAttributes, GetMarshallingInfoCallback marshallingInfoCallback)
{
CountInfo countInfo = NoCountInfo.Instance;
if (useSiteAttributes.TryGetUseSiteAttributeInfo(indirectionDepth, out UseSiteAttributeData useSiteInfo))
{
countInfo = useSiteInfo.CountInfo;
}
ITypeSymbol elementType = ((IArrayTypeSymbol)type).ElementType;
return CreateArrayMarshallingInfo(_compilation, type, elementType, countInfo, marshallingInfoCallback(elementType, useSiteAttributes, indirectionDepth + 1));
}
public static MarshallingInfo CreateArrayMarshallingInfo(
Compilation compilation,
ITypeSymbol managedType,
ITypeSymbol elementType,
CountInfo countInfo,
MarshallingInfo elementMarshallingInfo)
{
ITypeSymbol typeArgumentToInsert = elementType;
INamedTypeSymbol? arrayMarshaller;
if (elementType is IPointerTypeSymbol { PointedAtType: ITypeSymbol pointedAt })
{
arrayMarshaller = compilation.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_PointerArrayMarshaller_Metadata);
typeArgumentToInsert = pointedAt;
}
else
{
arrayMarshaller = compilation.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_ArrayMarshaller_Metadata);
}
if (arrayMarshaller is null)
{
// If the array marshaler type is not available, then we cannot marshal arrays but indicate it is missing.
return new MissingSupportCollectionMarshallingInfo(countInfo, elementMarshallingInfo);
}
if (ManualTypeMarshallingHelper.HasEntryPointMarshallerAttribute(arrayMarshaller)
&& ManualTypeMarshallingHelper.IsLinearCollectionEntryPoint(arrayMarshaller))
{
arrayMarshaller = arrayMarshaller.Construct(
typeArgumentToInsert,
arrayMarshaller.TypeArguments.Last());
Func<ITypeSymbol, MarshallingInfo> getMarshallingInfoForElement = (ITypeSymbol elementType) => elementMarshallingInfo;
if (ManualTypeMarshallingHelper.TryGetLinearCollectionMarshallersFromEntryType(arrayMarshaller, managedType, compilation, getMarshallingInfoForElement, out CustomTypeMarshallers? marshallers))
{
return new NativeLinearCollectionMarshallingInfo(
ManagedTypeInfo.CreateTypeInfoForTypeSymbol(arrayMarshaller),
marshallers.Value,
countInfo,
ManagedTypeInfo.CreateTypeInfoForTypeSymbol(arrayMarshaller.TypeParameters.Last()));
}
}
Debug.WriteLine("Default marshallers for arrays should be a valid shape.");
return NoMarshallingInfo.Instance;
}
}
}
// 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.Text;
using Microsoft.CodeAnalysis;
namespace Microsoft.Interop
{
/// <summary>
/// Marshalling information provider for unmanaged types that may be blittable.
/// </summary>
public sealed class BlittableTypeMarshallingInfoProvider : ITypeBasedMarshallingInfoProvider
{
private readonly Compilation _compilation;
public BlittableTypeMarshallingInfoProvider(Compilation compilation)
{
_compilation = compilation;
}
public bool CanProvideMarshallingInfoForType(ITypeSymbol type) => type is INamedTypeSymbol { IsUnmanagedType: true } unmanagedType
&& unmanagedType.IsConsideredBlittable();
public MarshallingInfo GetMarshallingInfo(ITypeSymbol type, int indirectionDepth, UseSiteAttributeProvider useSiteAttributes, GetMarshallingInfoCallback marshallingInfoCallback)
{
if (type.TypeKind is TypeKind.Enum or TypeKind.Pointer or TypeKind.FunctionPointer
|| type.SpecialType.IsAlwaysBlittable())
{
// Treat primitive types and enums as having no marshalling info.
// They are supported in configurations where runtime marshalling is enabled.
return NoMarshallingInfo.Instance;
}
else if (_compilation.GetTypeByMetadataName(TypeNames.System_Runtime_CompilerServices_DisableRuntimeMarshallingAttribute) is null)
{
// If runtime marshalling cannot be disabled, then treat this as a "missing support" scenario so we can gracefully fall back to using the forwarder downlevel.
return new MissingSupportMarshallingInfo();
}
else
{
return new UnmanagedBlittableMarshallingInfo(type.IsStrictlyBlittable());
}
}
}
}
// 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.Text;
using Microsoft.CodeAnalysis;
namespace Microsoft.Interop
{
/// <summary>
/// Marshalling information provider for <c>bool</c> elements without any marshalling information.
/// </summary>
public sealed class BooleanMarshallingInfoProvider : ITypeBasedMarshallingInfoProvider
{
public bool CanProvideMarshallingInfoForType(ITypeSymbol type) => type.SpecialType == SpecialType.System_Boolean;
public MarshallingInfo GetMarshallingInfo(ITypeSymbol type, int indirectionDepth, UseSiteAttributeProvider useSiteAttributes, GetMarshallingInfoCallback marshallingInfoCallback)
{
// We intentionally don't support marshalling bool with no marshalling info
// as treating bool as a non-normalized 1-byte value is generally not a good default.
// Additionally, that default is different than the runtime marshalling, so by explicitly
// blocking bool marshalling without additional info, we make it a little easier
// to transition by explicitly notifying people of changing behavior.
return NoMarshallingInfo.Instance;
}
}
}
// 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.Text;
using Microsoft.CodeAnalysis;
namespace Microsoft.Interop
{
/// <summary>
/// Marshalling information provider for <c>char</c> elements without any marshalling information on the element itself.
/// </summary>
public sealed class CharMarshallingInfoProvider : ITypeBasedMarshallingInfoProvider
{
private readonly DefaultMarshallingInfo _defaultMarshallingInfo;
public CharMarshallingInfoProvider(DefaultMarshallingInfo defaultMarshallingInfo)
{
_defaultMarshallingInfo = defaultMarshallingInfo;
}
public bool CanProvideMarshallingInfoForType(ITypeSymbol type) => type.SpecialType == SpecialType.System_Char;
public MarshallingInfo GetMarshallingInfo(ITypeSymbol type, int indirectionDepth, UseSiteAttributeProvider useSiteAttributes, GetMarshallingInfoCallback marshallingInfoCallback)
{
// No marshalling info was computed, but a character encoding was provided.
// If the type is a character then pass on these details.
return _defaultMarshallingInfo.CharEncoding == CharEncoding.Undefined ? new UnmanagedBlittableMarshallingInfo(IsStrictlyBlittable: false) : new MarshallingInfoStringSupport(_defaultMarshallingInfo.CharEncoding);
}
}
}
// 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.Linq;
using Microsoft.CodeAnalysis;
namespace Microsoft.Interop
{
internal static class CustomMarshallingInfoHelper
{
public static MarshallingInfo CreateNativeMarshallingInfo(
ITypeSymbol type,
INamedTypeSymbol entryPointType,
AttributeData attrData,
UseSiteAttributeProvider useSiteAttributeProvider,
GetMarshallingInfoCallback getMarshallingInfoCallback,
int indirectionDepth,
CountInfo parsedCountInfo,
IGeneratorDiagnostics diagnostics,
Compilation compilation)
{
if (!ManualTypeMarshallingHelper.HasEntryPointMarshallerAttribute(entryPointType))
{
return NoMarshallingInfo.Instance;
}
if (!(entryPointType.IsStatic && entryPointType.TypeKind == TypeKind.Class)
&& entryPointType.TypeKind != TypeKind.Struct)
{
diagnostics.ReportInvalidMarshallingAttributeInfo(attrData, nameof(SR.MarshallerTypeMustBeStaticClassOrStruct), entryPointType.ToDisplayString(), type.ToDisplayString());
return NoMarshallingInfo.Instance;
}
ManagedTypeInfo entryPointTypeInfo = ManagedTypeInfo.CreateTypeInfoForTypeSymbol(entryPointType);
bool isLinearCollectionMarshalling = ManualTypeMarshallingHelper.IsLinearCollectionEntryPoint(entryPointType);
if (isLinearCollectionMarshalling)
{
// Update the entry point type with the type arguments based on the managed type
if (type is IArrayTypeSymbol arrayManagedType)
{
// Generally, we require linear collection marshallers to have "arity of managed type + 1" arity.
// However, arrays aren't "generic" over their element type as they're generics, but we want to treat the element type
// as a generic type parameter. As a result, we require an arity of 2 for array marshallers, 1 for the array element type,
// and 1 for the native element type (the required additional type parameter for linear collection marshallers).
if (entryPointType.Arity != 2)
{
diagnostics.ReportInvalidMarshallingAttributeInfo(attrData, nameof(SR.MarshallerEntryPointTypeMustMatchArity), entryPointType.ToDisplayString(), type.ToDisplayString());
return NoMarshallingInfo.Instance;
}
entryPointType = entryPointType.ConstructedFrom.Construct(
arrayManagedType.ElementType,
entryPointType.TypeArguments.Last());
}
else if (type is INamedTypeSymbol namedManagedCollectionType && entryPointType.IsUnboundGenericType)
{
if (!ManualTypeMarshallingHelper.TryResolveEntryPointType(
namedManagedCollectionType,
entryPointType,
isLinearCollectionMarshalling,
(type, entryPointType) => diagnostics.ReportInvalidMarshallingAttributeInfo(attrData, nameof(SR.MarshallerEntryPointTypeMustMatchArity), entryPointType.ToDisplayString(), type.ToDisplayString()),
out ITypeSymbol resolvedEntryPointType))
{
return NoMarshallingInfo.Instance;
}
entryPointType = (INamedTypeSymbol)resolvedEntryPointType;
}
else
{
diagnostics.ReportInvalidMarshallingAttributeInfo(attrData, nameof(SR.MarshallerEntryPointTypeMustMatchArity), entryPointType.ToDisplayString(), type.ToDisplayString());
return NoMarshallingInfo.Instance;
}
Func<ITypeSymbol, MarshallingInfo> getMarshallingInfoForElement = (ITypeSymbol elementType) => getMarshallingInfoCallback(elementType, useSiteAttributeProvider, indirectionDepth + 1);
if (ManualTypeMarshallingHelper.TryGetLinearCollectionMarshallersFromEntryType(entryPointType, type, compilation, getMarshallingInfoForElement, out CustomTypeMarshallers? collectionMarshallers))
{
return new NativeLinearCollectionMarshallingInfo(
entryPointTypeInfo,
collectionMarshallers.Value,
parsedCountInfo,
ManagedTypeInfo.CreateTypeInfoForTypeSymbol(entryPointType.TypeParameters.Last()));
}
return NoMarshallingInfo.Instance;
}
if (type is INamedTypeSymbol namedManagedType && entryPointType.IsUnboundGenericType)
{
if (!ManualTypeMarshallingHelper.TryResolveEntryPointType(
namedManagedType,
entryPointType,
isLinearCollectionMarshalling,
(type, entryPointType) => diagnostics.ReportInvalidMarshallingAttributeInfo(attrData, nameof(SR.MarshallerEntryPointTypeMustMatchArity), entryPointType.ToDisplayString(), type.ToDisplayString()),
out ITypeSymbol resolvedEntryPointType))
{
return NoMarshallingInfo.Instance;
}
entryPointType = (INamedTypeSymbol)resolvedEntryPointType;
}
if (ManualTypeMarshallingHelper.TryGetValueMarshallersFromEntryType(entryPointType, type, compilation, out CustomTypeMarshallers? marshallers))
{
return new NativeMarshallingAttributeInfo(entryPointTypeInfo, marshallers.Value);
}
return NoMarshallingInfo.Instance;
}
}
}
// 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.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis;
namespace Microsoft.Interop
{
/// <summary>
/// Simple User-application of System.Runtime.InteropServices.MarshalAsAttribute
/// </summary>
public sealed record MarshalAsInfo(
UnmanagedType UnmanagedType,
CharEncoding CharEncoding) : MarshallingInfoStringSupport(CharEncoding)
{
// UnmanagedType.LPUTF8Str is not in netstandard2.0, so we define a constant for the value here.
// See https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.unmanagedtype
internal const UnmanagedType UnmanagedType_LPUTF8Str = (UnmanagedType)0x30;
}
/// <summary>
/// This class suppports parsing a System.Runtime.InteropServices.MarshalAsAttribute.
/// </summary>
public sealed class MarshalAsAttributeParser : IMarshallingInfoAttributeParser, IUseSiteAttributeParser
{
private readonly Compilation _compilation;
private readonly IGeneratorDiagnostics _diagnostics;
private readonly DefaultMarshallingInfo _defaultInfo;
public MarshalAsAttributeParser(Compilation compilation, IGeneratorDiagnostics diagnostics, DefaultMarshallingInfo defaultInfo)
{
_compilation = compilation;
_diagnostics = diagnostics;
_defaultInfo = defaultInfo;
}
public bool CanParseAttributeType(INamedTypeSymbol attributeType) => attributeType.ToDisplayString() == TypeNames.System_Runtime_InteropServices_MarshalAsAttribute;
UseSiteAttributeData IUseSiteAttributeParser.ParseAttribute(AttributeData attributeData, IElementInfoProvider elementInfoProvider, GetMarshallingInfoCallback marshallingInfoCallback)
{
ImmutableDictionary<string, TypedConstant> namedArguments = ImmutableDictionary.CreateRange(attributeData.NamedArguments);
SizeAndParamIndexInfo arraySizeInfo = SizeAndParamIndexInfo.Unspecified;
if (namedArguments.TryGetValue(nameof(MarshalAsAttribute.SizeConst), out TypedConstant sizeConstArg))
{
arraySizeInfo = arraySizeInfo with { ConstSize = (int)sizeConstArg.Value! };
}
if (namedArguments.TryGetValue(nameof(MarshalAsAttribute.SizeParamIndex), out TypedConstant sizeParamIndexArg))
{
if (!elementInfoProvider.TryGetInfoForParamIndex(attributeData, (short)sizeParamIndexArg.Value!, marshallingInfoCallback, out TypePositionInfo paramIndexInfo))
{
_diagnostics.ReportConfigurationNotSupported(attributeData, nameof(MarshalAsAttribute.SizeParamIndex), sizeParamIndexArg.Value.ToString());
}
arraySizeInfo = arraySizeInfo with { ParamAtIndex = paramIndexInfo };
}
return new UseSiteAttributeData(0, arraySizeInfo, attributeData);
}
MarshallingInfo? IMarshallingInfoAttributeParser.ParseAttribute(AttributeData attributeData, ITypeSymbol type, int indirectionDepth, UseSiteAttributeProvider useSiteAttributes, GetMarshallingInfoCallback marshallingInfoCallback)
{
object unmanagedTypeObj = attributeData.ConstructorArguments[0].Value!;
UnmanagedType unmanagedType = unmanagedTypeObj is short unmanagedTypeAsShort
? (UnmanagedType)unmanagedTypeAsShort
: (UnmanagedType)unmanagedTypeObj;
if (!Enum.IsDefined(typeof(UnmanagedType), unmanagedType)
|| unmanagedType == UnmanagedType.CustomMarshaler
|| unmanagedType == UnmanagedType.SafeArray)
{
_diagnostics.ReportConfigurationNotSupported(attributeData, nameof(UnmanagedType), unmanagedType.ToString());
}
bool isArrayType = unmanagedType == UnmanagedType.LPArray || unmanagedType == UnmanagedType.ByValArray;
UnmanagedType elementUnmanagedType = (UnmanagedType)SizeAndParamIndexInfo.UnspecifiedConstSize;
// All other data on attribute is defined as NamedArguments.
foreach (KeyValuePair<string, TypedConstant> namedArg in attributeData.NamedArguments)
{
switch (namedArg.Key)
{
case nameof(MarshalAsAttribute.SafeArraySubType):
case nameof(MarshalAsAttribute.SafeArrayUserDefinedSubType):
case nameof(MarshalAsAttribute.IidParameterIndex):
case nameof(MarshalAsAttribute.MarshalTypeRef):
case nameof(MarshalAsAttribute.MarshalType):
case nameof(MarshalAsAttribute.MarshalCookie):
_diagnostics.ReportConfigurationNotSupported(attributeData, $"{attributeData.AttributeClass!.Name}{Type.Delimiter}{namedArg.Key}");
break;
case nameof(MarshalAsAttribute.ArraySubType):
if (!isArrayType)
{
_diagnostics.ReportConfigurationNotSupported(attributeData, $"{attributeData.AttributeClass!.Name}{Type.Delimiter}{namedArg.Key}");
}
elementUnmanagedType = (UnmanagedType)namedArg.Value.Value!;
break;
}
}
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)
{
elementMarshallingInfo = elementType.SpecialType == SpecialType.System_String
? CreateStringMarshallingInfo(elementType, elementUnmanagedType)
: 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);
}
}
}
// 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.Diagnostics;
using Microsoft.CodeAnalysis;
namespace Microsoft.Interop
{
/// <summary>
/// This class suppports parsing a System.Runtime.InteropServices.Marshalling.MarshalUsingAttribute.
/// </summary>
public sealed class MarshalUsingAttributeParser : IMarshallingInfoAttributeParser, IUseSiteAttributeParser
{
private readonly Compilation _compilation;
private readonly IGeneratorDiagnostics _diagnostics;
public MarshalUsingAttributeParser(Compilation compilation, IGeneratorDiagnostics diagnostics)
{
_compilation = compilation;
_diagnostics = diagnostics;
}
public bool CanParseAttributeType(INamedTypeSymbol attributeType) => attributeType.ToDisplayString() == TypeNames.MarshalUsingAttribute;
MarshallingInfo? IMarshallingInfoAttributeParser.ParseAttribute(AttributeData attributeData, ITypeSymbol type, int indirectionDepth, UseSiteAttributeProvider useSiteAttributes, GetMarshallingInfoCallback marshallingInfoCallback)
{
Debug.Assert(attributeData.AttributeClass!.ToDisplayString() == TypeNames.MarshalUsingAttribute);
CountInfo countInfo = NoCountInfo.Instance;
if (useSiteAttributes.TryGetUseSiteAttributeInfo(indirectionDepth, out UseSiteAttributeData useSiteInfo))
{
countInfo = useSiteInfo.CountInfo;
}
if (attributeData.ConstructorArguments.Length == 0)
{
// This attribute only has count information.
// It does not provide any marshalling info.
// Return null here to respresent the lack of any marshalling info,
// instead of the presence of invalid marshalling info.
return null;
}
if (attributeData.ConstructorArguments[0].Value is not INamedTypeSymbol namedType)
{
return NoMarshallingInfo.Instance;
}
return CustomMarshallingInfoHelper.CreateNativeMarshallingInfo(
type,
namedType,
attributeData,
useSiteAttributes,
marshallingInfoCallback,
indirectionDepth,
countInfo,
_diagnostics,
_compilation
);
}
UseSiteAttributeData IUseSiteAttributeParser.ParseAttribute(AttributeData attributeData, IElementInfoProvider elementInfoProvider, GetMarshallingInfoCallback marshallingInfoCallback)
{
ImmutableDictionary<string, TypedConstant> namedArgs = ImmutableDictionary.CreateRange(attributeData.NamedArguments);
CountInfo countInfo = ParseCountInfo(attributeData, namedArgs, elementInfoProvider, marshallingInfoCallback);
int elementIndirectionDepth = namedArgs.TryGetValue(ManualTypeMarshallingHelper.MarshalUsingProperties.ElementIndirectionDepth, out TypedConstant value) ? (int)value.Value! : 0;
return new UseSiteAttributeData(elementIndirectionDepth, countInfo, attributeData);
}
private CountInfo ParseCountInfo(AttributeData attributeData, ImmutableDictionary<string, TypedConstant> namedArguments, IElementInfoProvider elementInfoProvider, GetMarshallingInfoCallback marshallingInfoCallback)
{
int? constSize = null;
string? elementName = null;
foreach (KeyValuePair<string, TypedConstant> arg in attributeData.NamedArguments)
{
if (arg.Key == ManualTypeMarshallingHelper.MarshalUsingProperties.ConstantElementCount)
{
constSize = (int)arg.Value.Value!;
}
else if (arg.Key == ManualTypeMarshallingHelper.MarshalUsingProperties.CountElementName)
{
if (arg.Value.Value is null)
{
_diagnostics.ReportConfigurationNotSupported(attributeData, ManualTypeMarshallingHelper.MarshalUsingProperties.CountElementName, "null");
return NoCountInfo.Instance;
}
elementName = (string)arg.Value.Value!;
}
}
if (constSize is not null && elementName is not null)
{
_diagnostics.ReportInvalidMarshallingAttributeInfo(attributeData, nameof(SR.ConstantAndElementCountInfoDisallowed));
}
else if (constSize is not null)
{
return new ConstSizeCountInfo(constSize.Value);
}
else if (elementName is not null)
{
if (!elementInfoProvider.TryGetInfoForElementName(attributeData, elementName, marshallingInfoCallback, out TypePositionInfo elementInfo))
{
_diagnostics.ReportConfigurationNotSupported(attributeData, ManualTypeMarshallingHelper.MarshalUsingProperties.CountElementName, elementName);
return NoCountInfo.Instance;
}
return new CountElementCountInfo(elementInfo);
}
return NoCountInfo.Instance;
}
}
}
......@@ -281,13 +281,21 @@ public bool AnyIncomingEdge(int to)
{
if (nestedCollection.ElementCountInfo is CountElementCountInfo { ElementInfo: TypePositionInfo nestedCountElement })
{
yield return nestedCountElement;
// Do not include dependent elements with no managed or native index.
// These values are dummy values that are inserted earlier to avoid emitting extra diagnostics.
if (nestedCountElement.ManagedIndex != TypePositionInfo.UnsetIndex || nestedCountElement.NativeIndex != TypePositionInfo.UnsetIndex)
{
yield return nestedCountElement;
}
}
foreach (KeyValuePair<MarshalMode, CustomTypeMarshallerData> mode in nestedCollection.Marshallers.Modes)
{
foreach (TypePositionInfo nestedElements in GetDependentElementsOfMarshallingInfo(mode.Value.CollectionElementMarshallingInfo))
foreach (TypePositionInfo nestedElement in GetDependentElementsOfMarshallingInfo(mode.Value.CollectionElementMarshallingInfo))
{
yield return nestedElements;
if (nestedElement.ManagedIndex != TypePositionInfo.UnsetIndex || nestedElement.NativeIndex != TypePositionInfo.UnsetIndex)
{
yield return nestedElement;
}
}
}
}
......
// 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.Text;
using Microsoft.CodeAnalysis;
namespace Microsoft.Interop
{
public sealed class MethodSignatureElementInfoProvider : IElementInfoProvider
{
private readonly Compilation _compilation;
private readonly IGeneratorDiagnostics _generatorDiagnostics;
private readonly IMethodSymbol _method;
private readonly ImmutableArray<IUseSiteAttributeParser> _useSiteAttributeParsers;
public MethodSignatureElementInfoProvider(Compilation compilation, IGeneratorDiagnostics generatorDiagnostics, IMethodSymbol method, ImmutableArray<IUseSiteAttributeParser> useSiteAttributeParsers)
{
_compilation = compilation;
_generatorDiagnostics = generatorDiagnostics;
_method = method;
_useSiteAttributeParsers = useSiteAttributeParsers;
}
public string FindNameForParamIndex(int paramIndex) => paramIndex >= _method.Parameters.Length ? string.Empty : _method.Parameters[paramIndex].Name;
public bool TryGetInfoForElementName(AttributeData attrData, string elementName, GetMarshallingInfoCallback marshallingInfoCallback, IElementInfoProvider rootProvider, out TypePositionInfo info)
{
if (elementName == CountElementCountInfo.ReturnValueElementName)
{
info = new TypePositionInfo(
ManagedTypeInfo.CreateTypeInfoForTypeSymbol(_method.ReturnType),
marshallingInfoCallback(_method.ReturnType, new UseSiteAttributeProvider(_useSiteAttributeParsers, _method.GetReturnTypeAttributes(), rootProvider, _generatorDiagnostics, marshallingInfoCallback), 0)) with
{
ManagedIndex = TypePositionInfo.ReturnIndex
};
return true;
}
for (int i = 0; i < _method.Parameters.Length; i++)
{
IParameterSymbol param = _method.Parameters[i];
if (param.Name == elementName)
{
info = TypePositionInfo.CreateForParameter(
param,
marshallingInfoCallback(param.Type, new UseSiteAttributeProvider(_useSiteAttributeParsers, param.GetAttributes(), rootProvider, _generatorDiagnostics, marshallingInfoCallback), 0), _compilation) with
{
ManagedIndex = i
};
return true;
}
}
info = null;
return false;
}
public bool TryGetInfoForParamIndex(AttributeData attrData, int paramIndex, GetMarshallingInfoCallback marshallingInfoCallback, IElementInfoProvider rootProvider, out TypePositionInfo info)
{
if (paramIndex >= _method.Parameters.Length)
{
info = null;
return false;
}
IParameterSymbol param = _method.Parameters[paramIndex];
info = TypePositionInfo.CreateForParameter(
param,
marshallingInfoCallback(param.Type, new UseSiteAttributeProvider(_useSiteAttributeParsers, param.GetAttributes(), rootProvider, _generatorDiagnostics, marshallingInfoCallback), 0), _compilation) with
{
ManagedIndex = paramIndex
};
return true;
}
}
}
// 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 Microsoft.CodeAnalysis;
namespace Microsoft.Interop
{
public sealed class NativeMarshallingAttributeParser : IMarshallingInfoAttributeParser
{
private readonly Compilation _compilation;
private readonly IGeneratorDiagnostics _diagnostics;
public NativeMarshallingAttributeParser(Compilation compilation, IGeneratorDiagnostics diagnostics)
{
_compilation = compilation;
_diagnostics = diagnostics;
}
public bool CanParseAttributeType(INamedTypeSymbol attributeType) => attributeType.ToDisplayString() == TypeNames.NativeMarshallingAttribute;
public MarshallingInfo? ParseAttribute(AttributeData attributeData, ITypeSymbol type, int indirectionDepth, UseSiteAttributeProvider useSiteAttributes, GetMarshallingInfoCallback marshallingInfoCallback)
{
Debug.Assert(attributeData.AttributeClass!.ToDisplayString() == TypeNames.NativeMarshallingAttribute);
CountInfo countInfo = NoCountInfo.Instance;
if (useSiteAttributes.TryGetUseSiteAttributeInfo(indirectionDepth, out var useSiteInfo))
{
countInfo = useSiteInfo.CountInfo;
}
if (attributeData.ConstructorArguments[0].Value is not INamedTypeSymbol entryPointType)
{
return NoMarshallingInfo.Instance;
}
return CustomMarshallingInfoHelper.CreateNativeMarshallingInfo(
type,
entryPointType,
attributeData,
useSiteAttributes,
marshallingInfoCallback,
indirectionDepth,
countInfo,
_diagnostics,
_compilation
);
}
}
}
// 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.Text;
using Microsoft.CodeAnalysis;
namespace Microsoft.Interop
{
/// <summary>
/// The type of the element is a SafeHandle-derived type with no marshalling attributes.
/// </summary>
public sealed record SafeHandleMarshallingInfo(bool AccessibleDefaultConstructor, bool IsAbstract) : MarshallingInfo;
/// <summary>
/// This class supports generating marshalling info for SafeHandle-derived types.
/// </summary>
public sealed class SafeHandleMarshallingInfoProvider : ITypeBasedMarshallingInfoProvider
{
private readonly Compilation _compilation;
private readonly ITypeSymbol _containingScope;
public SafeHandleMarshallingInfoProvider(Compilation compilation, ITypeSymbol containingScope)
{
_compilation = compilation;
_containingScope = containingScope;
}
public bool CanProvideMarshallingInfoForType(ITypeSymbol type)
{
// Check for an implicit SafeHandle conversion.
// The SafeHandle type might not be defined if we're using one of the test CoreLib implementations used for NativeAOT.
ITypeSymbol? safeHandleType = _compilation.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_SafeHandle);
if (safeHandleType is not null)
{
CodeAnalysis.Operations.CommonConversion conversion = _compilation.ClassifyCommonConversion(type, safeHandleType);
if (conversion.Exists
&& conversion.IsImplicit
&& (conversion.IsReference || conversion.IsIdentity))
{
return true;
}
}
return false;
}
public MarshallingInfo GetMarshallingInfo(ITypeSymbol type, int indirectionDepth, UseSiteAttributeProvider useSiteAttributes, GetMarshallingInfoCallback marshallingInfoCallback)
{
bool hasAccessibleDefaultConstructor = false;
if (type is INamedTypeSymbol named && !named.IsAbstract && named.InstanceConstructors.Length > 0)
{
foreach (IMethodSymbol ctor in named.InstanceConstructors)
{
if (ctor.Parameters.Length == 0)
{
hasAccessibleDefaultConstructor = _compilation.IsSymbolAccessibleWithin(ctor, _containingScope);
break;
}
}
}
return new SafeHandleMarshallingInfo(hasAccessibleDefaultConstructor, type.IsAbstract);
}
}
}
......@@ -56,9 +56,10 @@ public IEnumerable<ParameterSyntax> StubParameters
InteropAttributeData interopAttributeData,
StubEnvironment env,
IGeneratorDiagnostics diagnostics,
AttributeData signatureWideMarshallingAttributeData,
Assembly generatorInfoAssembly)
{
ImmutableArray<TypePositionInfo> typeInfos = GenerateTypeInformation(method, interopAttributeData, diagnostics, env);
ImmutableArray<TypePositionInfo> typeInfos = GenerateTypeInformation(method, interopAttributeData, diagnostics, env, signatureWideMarshallingAttributeData);
ImmutableArray<AttributeListSyntax>.Builder additionalAttrs = ImmutableArray.CreateBuilder<AttributeListSyntax>();
......@@ -99,7 +100,12 @@ public IEnumerable<ParameterSyntax> StubParameters
};
}
private static ImmutableArray<TypePositionInfo> GenerateTypeInformation(IMethodSymbol method, InteropAttributeData interopAttributeData, IGeneratorDiagnostics diagnostics, StubEnvironment env)
private static ImmutableArray<TypePositionInfo> GenerateTypeInformation(
IMethodSymbol method,
InteropAttributeData interopAttributeData,
IGeneratorDiagnostics diagnostics,
StubEnvironment env,
AttributeData signatureWideMarshallingAttributeData)
{
// Compute the current default string encoding value.
CharEncoding defaultEncoding = CharEncoding.Undefined;
......@@ -120,14 +126,32 @@ private static ImmutableArray<TypePositionInfo> GenerateTypeInformation(IMethodS
var defaultInfo = new DefaultMarshallingInfo(defaultEncoding, interopAttributeData.StringMarshallingCustomType);
var marshallingAttributeParser = new MarshallingAttributeInfoParser(env.Compilation, diagnostics, defaultInfo, method);
var useSiteAttributeParsers = ImmutableArray.Create<IUseSiteAttributeParser>(
new MarshalAsAttributeParser(env.Compilation, diagnostics, defaultInfo),
new MarshalUsingAttributeParser(env.Compilation, diagnostics));
var marshallingInfoParser = new MarshallingInfoParser(
diagnostics,
new MethodSignatureElementInfoProvider(env.Compilation, diagnostics, method, useSiteAttributeParsers),
useSiteAttributeParsers,
ImmutableArray.Create<IMarshallingInfoAttributeParser>(
new MarshalAsAttributeParser(env.Compilation, diagnostics, defaultInfo),
new MarshalUsingAttributeParser(env.Compilation, diagnostics),
new NativeMarshallingAttributeParser(env.Compilation, diagnostics)),
ImmutableArray.Create<ITypeBasedMarshallingInfoProvider>(
new SafeHandleMarshallingInfoProvider(env.Compilation, method.ContainingType),
new ArrayMarshallingInfoProvider(env.Compilation),
new CharMarshallingInfoProvider(defaultInfo),
new StringMarshallingInfoProvider(env.Compilation, diagnostics, signatureWideMarshallingAttributeData, defaultInfo),
new BooleanMarshallingInfoProvider(),
new BlittableTypeMarshallingInfoProvider(env.Compilation)));
// Determine parameter and return types
ImmutableArray<TypePositionInfo>.Builder typeInfos = ImmutableArray.CreateBuilder<TypePositionInfo>();
for (int i = 0; i < method.Parameters.Length; i++)
{
IParameterSymbol param = method.Parameters[i];
MarshallingInfo marshallingInfo = marshallingAttributeParser.ParseMarshallingInfo(param.Type, param.GetAttributes());
MarshallingInfo marshallingInfo = marshallingInfoParser.ParseMarshallingInfo(param.Type, param.GetAttributes());
var typeInfo = TypePositionInfo.CreateForParameter(param, marshallingInfo, env.Compilation);
typeInfo = typeInfo with
{
......@@ -137,7 +161,7 @@ private static ImmutableArray<TypePositionInfo> GenerateTypeInformation(IMethodS
typeInfos.Add(typeInfo);
}
TypePositionInfo retTypeInfo = new(ManagedTypeInfo.CreateTypeInfoForTypeSymbol(method.ReturnType), marshallingAttributeParser.ParseMarshallingInfo(method.ReturnType, method.GetReturnTypeAttributes()));
TypePositionInfo retTypeInfo = new(ManagedTypeInfo.CreateTypeInfoForTypeSymbol(method.ReturnType), marshallingInfoParser.ParseMarshallingInfo(method.ReturnType, method.GetReturnTypeAttributes()));
retTypeInfo = retTypeInfo with
{
ManagedIndex = TypePositionInfo.ReturnIndex,
......
// 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.Text;
using Microsoft.CodeAnalysis;
namespace Microsoft.Interop
{
/// <summary>
/// This class supports generating marshalling info for the <see cref="string"/> type.
/// This includes support for the <c>System.Runtime.InteropServices.StringMarshalling</c> enum.
/// </summary>
public sealed class StringMarshallingInfoProvider : ITypeBasedMarshallingInfoProvider
{
private readonly Compilation _compilation;
private readonly IGeneratorDiagnostics _diagnostics;
private readonly AttributeData _stringMarshallingCustomAttribute;
private readonly DefaultMarshallingInfo _defaultMarshallingInfo;
public StringMarshallingInfoProvider(Compilation compilation, IGeneratorDiagnostics diagnostics, AttributeData stringMarshallingCustomAttribute, DefaultMarshallingInfo defaultMarshallingInfo)
{
_compilation = compilation;
_diagnostics = diagnostics;
_stringMarshallingCustomAttribute = stringMarshallingCustomAttribute;
_defaultMarshallingInfo = defaultMarshallingInfo;
}
public bool CanProvideMarshallingInfoForType(ITypeSymbol type) => type.SpecialType == SpecialType.System_String;
public MarshallingInfo GetMarshallingInfo(ITypeSymbol type, int indirectionDepth, UseSiteAttributeProvider useSiteAttributes, GetMarshallingInfoCallback marshallingInfoCallback)
{
if (_defaultMarshallingInfo.CharEncoding == CharEncoding.Undefined)
{
return NoMarshallingInfo.Instance;
}
else if (_defaultMarshallingInfo.CharEncoding == CharEncoding.Custom)
{
if (_defaultMarshallingInfo.StringMarshallingCustomType is not null)
{
CountInfo countInfo = NoCountInfo.Instance;
if (useSiteAttributes.TryGetUseSiteAttributeInfo(indirectionDepth, out var useSiteInfo))
{
countInfo = useSiteInfo.CountInfo;
}
return CustomMarshallingInfoHelper.CreateNativeMarshallingInfo(
type,
_defaultMarshallingInfo.StringMarshallingCustomType,
_stringMarshallingCustomAttribute,
useSiteAttributes,
marshallingInfoCallback,
indirectionDepth,
countInfo,
_diagnostics,
_compilation);
}
}
else
{
// No marshalling info was computed, but a character encoding was provided.
return _defaultMarshallingInfo.CharEncoding switch
{
CharEncoding.Utf16 => CreateStringMarshallingInfo(_compilation, type, TypeNames.Utf16StringMarshaller),
CharEncoding.Utf8 => CreateStringMarshallingInfo(_compilation, type, TypeNames.Utf8StringMarshaller),
_ => throw new InvalidOperationException()
};
}
return new MarshallingInfoStringSupport(_defaultMarshallingInfo.CharEncoding);
}
public static MarshallingInfo CreateStringMarshallingInfo(
Compilation compilation,
ITypeSymbol type,
string marshallerName)
{
INamedTypeSymbol? stringMarshaller = compilation.GetTypeByMetadataName(marshallerName);
if (stringMarshaller is null)
return new MissingSupportMarshallingInfo();
if (ManualTypeMarshallingHelper.HasEntryPointMarshallerAttribute(stringMarshaller))
{
if (ManualTypeMarshallingHelper.TryGetValueMarshallersFromEntryType(stringMarshaller, type, compilation, out CustomTypeMarshallers? marshallers))
{
return new NativeMarshallingAttributeInfo(
EntryPointType: ManagedTypeInfo.CreateTypeInfoForTypeSymbol(stringMarshaller),
Marshallers: marshallers.Value);
}
}
return new MissingSupportMarshallingInfo();
}
}
}
// 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 Microsoft.CodeAnalysis;
namespace Microsoft.Interop
{
/// <summary>
/// Parses information for all use-site attributes and tracks their usage.
/// </summary>
public sealed class UseSiteAttributeProvider
{
private readonly ImmutableDictionary<int, UseSiteAttributeData> _useSiteAttributesByIndirectionDepth;
private readonly int _maxIndirectionLevelDataProvided;
private readonly IGeneratorDiagnostics _diagnostics;
private int _maxIndirectionLevelUsed;
/// <summary>
/// Construct a new <see cref="UseSiteAttributeProvider"/> for a given usage site.
/// </summary>
/// <param name="useSiteAttributeParsers">The parsers for the attributes at the given usage site.</param>
/// <param name="useSiteAttributes">The attributes at the usage site.</param>
/// <param name="elementInfoProvider">The provider for additional element information, used by the attribute parsers.</param>
/// <param name="diagnostics">Diagnostics sink for any invalid configurations.</param>
/// <param name="getMarshallingInfoCallback">A callback to get marshalling information for other elements. Used by <paramref name="elementInfoProvider"/>.</param>
internal UseSiteAttributeProvider(
ImmutableArray<IUseSiteAttributeParser> useSiteAttributeParsers,
IEnumerable<AttributeData> useSiteAttributes,
IElementInfoProvider elementInfoProvider,
IGeneratorDiagnostics diagnostics,
GetMarshallingInfoCallback getMarshallingInfoCallback)
{
ImmutableDictionary<int, UseSiteAttributeData>.Builder useSiteAttributesByIndirectionDepth = ImmutableDictionary.CreateBuilder<int, UseSiteAttributeData>();
_maxIndirectionLevelDataProvided = 0;
foreach (AttributeData attribute in useSiteAttributes)
{
UseSiteAttributeData? useSiteAttributeData = GetUseSiteInfoForAttribute(attribute);
if (useSiteAttributeData is not null)
{
int indirectionDepth = useSiteAttributeData.IndirectionDepth;
if (useSiteAttributesByIndirectionDepth.ContainsKey(indirectionDepth))
{
diagnostics.ReportInvalidMarshallingAttributeInfo(attribute, nameof(SR.DuplicateMarshallingInfo), indirectionDepth.ToString());
}
else
{
useSiteAttributesByIndirectionDepth.Add(indirectionDepth, useSiteAttributeData);
_maxIndirectionLevelDataProvided = Math.Max(_maxIndirectionLevelDataProvided, indirectionDepth);
}
}
}
_useSiteAttributesByIndirectionDepth = useSiteAttributesByIndirectionDepth.ToImmutable();
_diagnostics = diagnostics;
UseSiteAttributeData? GetUseSiteInfoForAttribute(AttributeData attribute)
{
foreach (var parser in useSiteAttributeParsers)
{
// Automatically ignore invalid attributes.
// The compiler will already error on them.
if (attribute.AttributeConstructor is not null && parser.CanParseAttributeType(attribute.AttributeClass))
{
return parser.ParseAttribute(attribute, elementInfoProvider, getMarshallingInfoCallback);
}
}
return null;
}
}
/// <summary>
/// Get the <see cref="UseSiteAttributeData"/> provided for a given <paramref name="indirectionDepth"/>, if it exists.
/// </summary>
/// <param name="indirectionDepth">The indirection depth to retrieve info for.</param>
/// <param name="useSiteInfo">The use site information, if it exists.</param>
/// <returns><c>true</c> if an attribute was provided for the given indirection depth.</returns>
public bool TryGetUseSiteAttributeInfo(int indirectionDepth, out UseSiteAttributeData useSiteInfo)
{
_maxIndirectionLevelUsed = Math.Max(indirectionDepth, _maxIndirectionLevelUsed);
return _useSiteAttributesByIndirectionDepth.TryGetValue(indirectionDepth, out useSiteInfo);
}
/// <summary>
/// Call when no more of the use-site attribute information will be used.
/// Records any information or diagnostics about unused marshalling information.
/// </summary>
internal void OnAttributeUsageFinished()
{
if (_maxIndirectionLevelUsed < _maxIndirectionLevelDataProvided)
{
_diagnostics.ReportInvalidMarshallingAttributeInfo(
_useSiteAttributesByIndirectionDepth[_maxIndirectionLevelDataProvided].AttributeData,
nameof(SR.ExtraneousMarshallingInfo),
_maxIndirectionLevelDataProvided.ToString(),
_maxIndirectionLevelUsed.ToString());
}
}
}
}
......@@ -120,8 +120,8 @@ public static IEnumerable<object[]> CodeSnippetsToCompile()
// Generic collection marshaller has different arity than collection.
yield return new object[] { ID(), CodeSnippets.CustomCollectionMarshalling.Stateless.GenericCollectionMarshallingArityMismatch, 2, 0 };
yield return new object[] { ID(), CodeSnippets.MarshalAsAndMarshalUsingOnReturnValue, 2, 0 };
yield return new object[] { ID(), CodeSnippets.CustomCollectionMarshalling.Stateless.CustomElementMarshallingDuplicateElementIndirectionDepth, 2, 0 };
yield return new object[] { ID(), CodeSnippets.MarshalAsAndMarshalUsingOnReturnValue, 1, 0 };
yield return new object[] { ID(), CodeSnippets.CustomCollectionMarshalling.Stateless.CustomElementMarshallingDuplicateElementIndirectionDepth, 1, 0 };
yield return new object[] { ID(), CodeSnippets.CustomCollectionMarshalling.Stateless.CustomElementMarshallingUnusedElementIndirectionDepth, 1, 0 };
yield return new object[] { ID(), CodeSnippets.RecursiveCountElementNameOnReturnValue, 2, 0 };
yield return new object[] { ID(), CodeSnippets.RecursiveCountElementNameOnParameter, 2, 0 };
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册