From 9a452cc046941f545820591aba7c7659135cc678 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 28 Jun 2022 10:57:00 -0700 Subject: [PATCH 1/6] Update to use CustomMarshaller entry point type. Tests do not pass. Added TODOs for questions about behavior between new and old implementation. --- .../Analyzers/CustomTypeMarshallerAnalyzer.cs | 63 +++--- .../ManualTypeMarshallingHelper.cs | 192 ++++++++++++------ ...ributedMarshallingModelGeneratorFactory.cs | 24 +-- .../MarshallingAttributeInfo.cs | 4 +- .../Microsoft.Interop.SourceGeneration.csproj | 4 +- .../TypeNames.cs | 2 +- .../CustomMarshallerAttribute.cs | 36 ++++ .../CustomTypeMarshallersAttributeBase.cs | 26 --- .../ManagedToUnmanagedMarshallersAttribute.cs | 38 ---- .../tests/Ancillary.Interop/Scenario.cs | 68 +++++++ .../CustomMarshallingTests.cs | 4 +- .../CodeSnippets.cs | 16 +- .../TestAssets/SharedTypes/NonBlittable.cs | 15 +- 13 files changed, 299 insertions(+), 193 deletions(-) create mode 100644 src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/CustomMarshallerAttribute.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/CustomTypeMarshallersAttributeBase.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ManagedToUnmanagedMarshallersAttribute.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Scenario.cs diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerAnalyzer.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerAnalyzer.cs index dfc4139263dc5..1342ce5e46aa7 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerAnalyzer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerAnalyzer.cs @@ -405,7 +405,7 @@ private void PrepareForAnalysis(CompilationStartAnalysisContext context) // Analyze NativeMarshalling/MarshalUsing for correctness context.RegisterSymbolAction(PerCompilationAnalyzer.AnalyzeTypeDefinition, SymbolKind.NamedType); - context.RegisterSymbolAction(PerCompilationAnalyzer.AnalyzeElement, SymbolKind.Parameter, SymbolKind.Field); + context.RegisterSymbolAction(PerCompilationAnalyzer.AnalyzeParameterOrField, SymbolKind.Parameter, SymbolKind.Field); context.RegisterSymbolAction(PerCompilationAnalyzer.AnalyzeReturnType, SymbolKind.Method); // Analyze marshaller type to validate shape. @@ -431,18 +431,16 @@ public PerCompilationAnalyzer(INamedTypeSymbol spanOfT, INamedTypeSymbol? spanOf public static void AnalyzeTypeDefinition(SymbolAnalysisContext context) { INamedTypeSymbol type = (INamedTypeSymbol)context.Symbol; - - (AttributeData? attributeData, INamedTypeSymbol? marshallerType) = ManualTypeMarshallingHelper.GetDefaultMarshallerInfo(type); - + (AttributeData? attributeData, INamedTypeSymbol? entryType) = ManualTypeMarshallingHelper.GetDefaultMarshallerEntryType(type); if (attributeData is null) { return; } - AnalyzeManagedTypeMarshallingInfo(context, type, attributeData, marshallerType); + AnalyzeManagedTypeMarshallingInfo(context, type, attributeData, entryType); } - public static void AnalyzeElement(SymbolAnalysisContext context) + public static void AnalyzeParameterOrField(SymbolAnalysisContext context) { ITypeSymbol managedType = context.Symbol switch { @@ -455,6 +453,7 @@ public static void AnalyzeElement(SymbolAnalysisContext context) { return; } + AnalyzeManagedTypeMarshallingInfo(context, managedType, attributeData, attributeData.ConstructorArguments[0].Value as INamedTypeSymbol); } @@ -467,57 +466,47 @@ public static void AnalyzeReturnType(SymbolAnalysisContext context) { return; } + AnalyzeManagedTypeMarshallingInfo(context, managedType, attributeData, attributeData.ConstructorArguments[0].Value as INamedTypeSymbol); } - private static void AnalyzeManagedTypeMarshallingInfo(SymbolAnalysisContext context, ITypeSymbol type, AttributeData attributeData, INamedTypeSymbol? marshallerType) + private static void AnalyzeManagedTypeMarshallingInfo( + SymbolAnalysisContext context, + ITypeSymbol managedType, + AttributeData attributeData, + INamedTypeSymbol? entryType) { - if (marshallerType is null) + if (entryType is null) { context.ReportDiagnostic( attributeData.CreateDiagnostic( NativeTypeMustHaveCustomTypeMarshallerAttributeRule, - type.ToDisplayString())); + managedType.ToDisplayString())); return; } - if (marshallerType.IsUnboundGenericType) + if (entryType.IsUnboundGenericType) { context.ReportDiagnostic( attributeData.CreateDiagnostic( NativeGenericTypeMustBeClosedOrMatchArityRule, - marshallerType.ToDisplayString(), - type.ToDisplayString())); + entryType.ToDisplayString(), + managedType.ToDisplayString())); } - (bool hasCustomTypeMarshallerAttribute, ITypeSymbol? marshallerManagedType, _) = ManualTypeMarshallingHelper_V1.GetMarshallerShapeInfo(marshallerType); - - marshallerManagedType = ManualTypeMarshallingHelper.ResolveManagedType(marshallerManagedType, marshallerType, context.Compilation); - - if (!hasCustomTypeMarshallerAttribute) + bool isLinearCollectionMarshalling = ManualTypeMarshallingHelper.IsLinearCollectionEntryPoint(entryType); + bool hasMarshallers = ManualTypeMarshallingHelper.TryGetMarshallersFromEntryType( + entryType, + managedType, + isLinearCollectionMarshalling, + context.Compilation, + out CustomTypeMarshallers? _); + if (!hasMarshallers) { context.ReportDiagnostic( attributeData.CreateDiagnostic( NativeTypeMustHaveCustomTypeMarshallerAttributeRule, - type.ToDisplayString())); - return; - } - - if (marshallerManagedType is null) - { - context.ReportDiagnostic( - attributeData.CreateDiagnostic( - NativeTypeMustHaveCustomTypeMarshallerAttributeRule, - type.ToDisplayString())); - return; - } - - if (!TypeSymbolsConstructedFromEqualTypes(type, marshallerManagedType)) - { - context.ReportDiagnostic( - attributeData.CreateDiagnostic( - NativeTypeMustHaveCustomTypeMarshallerAttributeRule, - type.ToDisplayString())); + managedType.ToDisplayString())); return; } } @@ -846,7 +835,7 @@ public void AnalyzeMarshallerType(SymbolAnalysisContext context) type.ToDisplayString())); } - if (SymbolEqualityComparer.Default.Equals(ManualTypeMarshallingHelper.GetDefaultMarshallerInfo(type).marshallerType, marshallerType) + if (SymbolEqualityComparer.Default.Equals(ManualTypeMarshallingHelper.GetDefaultMarshallerEntryType(type).entryType, marshallerType) && ManualTypeMarshallingHelper.FindGetPinnableReference(type) is IMethodSymbol managedGetPinnableReferenceMethod) { if (!managedGetPinnableReferenceMethod.ReturnType.IsConsideredBlittable()) diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs index ca64749c523d0..d20ec7712a71e 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs @@ -4,7 +4,10 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Microsoft.CodeAnalysis; @@ -17,7 +20,35 @@ public readonly record struct CustomTypeMarshallerData( bool IsStrictlyBlittable, ManagedTypeInfo? BufferElementType); - public readonly record struct CustomTypeMarshallers(CustomTypeMarshallerData? In, CustomTypeMarshallerData? Ref, CustomTypeMarshallerData? Out); + public readonly record struct CustomTypeMarshallers( + ImmutableDictionary Scenarios) + { + public CustomTypeMarshallerData GetScenarioOrDefault(Scenario scenario) + { + CustomTypeMarshallerData data; + if (Scenarios.TryGetValue(scenario, out data)) + return data; + + if (Scenarios.TryGetValue(Scenario.Default, out data)) + return data; + + // TODO: Hard failure based on previous implementation + throw new InvalidOperationException(); + } + + public bool TryGetScenarioOrDefault(Scenario scenario, out CustomTypeMarshallerData data) + { + if (Scenarios.TryGetValue(scenario, out data)) + return true; + + return Scenarios.TryGetValue(Scenario.Default, out data); + } + + public bool IsDefinedOrDefault(Scenario scenario) + { + return Scenarios.ContainsKey(scenario) || Scenarios.ContainsKey(Scenario.Default); + } + } public static class ManualTypeMarshallingHelper { @@ -28,13 +59,6 @@ public static class MarshalUsingProperties public const string ConstantElementCount = nameof(ConstantElementCount); } - internal static class MarshallersProperties - { - public const string InMarshaller = nameof(InMarshaller); - public const string RefMarshaller = nameof(RefMarshaller); - public const string OutMarshaller = nameof(OutMarshaller); - } - [Flags] private enum MarshallingDirection { @@ -49,54 +73,109 @@ public static bool IsLinearCollectionEntryPoint(ITypeSymbol entryPointType) return false; } - public static bool TryGetMarshallers(ITypeSymbol entryPointType, ITypeSymbol managedType, bool isLinearCollectionMarshalling, Compilation compilation, out CustomTypeMarshallers? marshallers) + public static bool TryGetMarshallersFromEntryType( + INamedTypeSymbol entryPointType, + ITypeSymbol managedType, + bool isLinearCollectionMarshalling, + Compilation compilation, + out CustomTypeMarshallers? marshallers) { marshallers = null; - var attr = entryPointType.GetAttributes().FirstOrDefault(attr => attr.AttributeClass.ToDisplayString() == TypeNames.ManagedToUnmanagedMarshallersAttribute); - if (attr is null || attr.ConstructorArguments.Length == 0) + var attrs = entryPointType.GetAttributes().Where(attr => attr.AttributeClass.ToDisplayString() == TypeNames.CustomMarshallerAttribute).ToArray(); + if (attrs is null || attrs.Length == 0) return false; - ITypeSymbol? managedTypeOnAttr = attr.ConstructorArguments[0].Value as ITypeSymbol; - if (!SymbolEqualityComparer.Default.Equals(managedType, managedTypeOnAttr) - && !compilation.HasImplicitConversion(managedType, managedTypeOnAttr)) - return false; + Dictionary scenarios = new(); + foreach (AttributeData attr in attrs) + { + Debug.Assert(attr.ConstructorArguments.Length == 3); + + // Verify the defined marshaller is for the managed type. + ITypeSymbol? managedTypeOnAttr = attr.ConstructorArguments[0].Value as ITypeSymbol; + if (!SymbolEqualityComparer.Default.Equals(managedType, managedTypeOnAttr) + && !compilation.HasImplicitConversion(managedType, managedTypeOnAttr)) + { + continue; + } + + // Verify any instantiation of Generic parameters is provided by entry point. + // TODO: Hard failure based on previous implementation + ITypeSymbol? managedTypeInst = ResolveManagedType(managedTypeOnAttr, entryPointType, compilation); + if (managedTypeInst is null) + return false; + + // Verify any instantiated managed types are derived properly. + // TODO: Hard failure based on previous implementation + if (!TypeSymbolsConstructedFromEqualTypes(managedType, managedTypeInst)) + return false; + + var marshallerScenario = (Scenario)attr.ConstructorArguments[1].Value!; + + ITypeSymbol? marshallerTypeOnAttr = attr.ConstructorArguments[2].Value as ITypeSymbol; + if (marshallerTypeOnAttr is null) + continue; - var namedArguments = attr.NamedArguments.ToImmutableDictionary(); - CustomTypeMarshallerData? inMarshaller = - GetNamedArgumentAsMarshallerData(namedArguments, MarshallersProperties.InMarshaller, MarshallingDirection.ManagedToUnmanaged, managedTypeOnAttr, compilation) ?? - GetMarshallerDataForType(entryPointType, MarshallingDirection.ManagedToUnmanaged, managedTypeOnAttr, compilation); + CustomTypeMarshallerData? data = marshallerScenario switch + { + Scenario.Default => GetMarshallerDataForType(entryPointType, MarshallingDirection.Bidirectional, managedTypeOnAttr, compilation), + + Scenario.ManagedToUnmanagedIn + or Scenario.ManagedToUnmanagedRef + or Scenario.ManagedToUnmanagedOut => GetMarshallerDataForType(entryPointType, MarshallingDirection.ManagedToUnmanaged, managedTypeOnAttr, compilation), - CustomTypeMarshallerData? refMarshaller = - GetNamedArgumentAsMarshallerData(namedArguments, MarshallersProperties.RefMarshaller, MarshallingDirection.Bidirectional, managedTypeOnAttr, compilation) ?? - GetMarshallerDataForType(entryPointType, MarshallingDirection.Bidirectional, managedTypeOnAttr, compilation); + Scenario.UnmanagedToManagedIn + or Scenario.UnmanagedToManagedRef + or Scenario.UnmanagedToManagedOut => GetMarshallerDataForType(entryPointType, MarshallingDirection.UnmanagedToManaged, managedTypeOnAttr, compilation), - CustomTypeMarshallerData? outMarshaller = - GetNamedArgumentAsMarshallerData(namedArguments, MarshallersProperties.OutMarshaller, MarshallingDirection.UnmanagedToManaged, managedTypeOnAttr, compilation) ?? - GetMarshallerDataForType(entryPointType, MarshallingDirection.UnmanagedToManaged, managedTypeOnAttr, compilation); + Scenario.ElementIn + or Scenario.ElementRef + or Scenario.ElementOut => GetMarshallerDataForType(entryPointType, MarshallingDirection.Bidirectional, managedTypeOnAttr, compilation), - if (inMarshaller is null && refMarshaller is null && outMarshaller is null) + _ => null + }; + + // TODO: Should we fire a diagnostic for duplicated scenarios or just take the last one? + if (data is null + || scenarios.ContainsKey(marshallerScenario)) + { + continue; + } + + scenarios.Add(marshallerScenario, data.Value); + } + + if (scenarios.Count == 0) return false; marshallers = new CustomTypeMarshallers() { - In = inMarshaller, - Ref = refMarshaller, - Out = outMarshaller, + Scenarios = scenarios.ToImmutableDictionary() }; return true; + + static bool TypeSymbolsConstructedFromEqualTypes(ITypeSymbol left, ITypeSymbol right) + { + return (left, right) switch + { + (INamedTypeSymbol namedLeft, INamedTypeSymbol namedRight) => SymbolEqualityComparer.Default.Equals(namedLeft.ConstructedFrom, namedRight.ConstructedFrom), + _ => SymbolEqualityComparer.Default.Equals(left, right) + }; + } } /// - /// Resolve a non- to the correct managed type if is generic and is using any placeholder types. + /// Resolve a non- to the correct + /// managed type if is generic and + /// is using any placeholder types. /// /// The non-named managed type. - /// The marshaller type. + /// The marshaller type. /// The compilation to use to make new type symbols. /// The resolved managed type, or if the provided type did not have any placeholders. - public static ITypeSymbol? ResolveManagedType(ITypeSymbol? managedType, INamedTypeSymbol marshallerType, Compilation compilation) + public static ITypeSymbol? ResolveManagedType(ITypeSymbol? managedType, INamedTypeSymbol entryType, Compilation compilation) { - if (managedType is null || !marshallerType.IsGenericType) + if (managedType is null || !entryType.IsGenericType) { return managedType; } @@ -121,7 +200,7 @@ public static bool TryGetMarshallers(ITypeSymbol entryPointType, ITypeSymbol man return managedType; } - ITypeSymbol resultType = marshallerType.TypeArguments[0]; + ITypeSymbol resultType = entryType.TypeArguments[0]; while (typeStack.Count > 0) { @@ -142,39 +221,46 @@ public static bool TryGetMarshallers(ITypeSymbol entryPointType, ITypeSymbol man return resultType; } - public static (AttributeData? attribute, INamedTypeSymbol? marshallerType) GetDefaultMarshallerInfo(ITypeSymbol managedType) + /// + /// Get the managed type's defined marshaller entry type. + /// + /// The managed type. + /// The attribute data and entry type for marshalling. + public static (AttributeData? attribute, INamedTypeSymbol? entryType) GetDefaultMarshallerEntryType(ITypeSymbol managedType) { AttributeData? attr = managedType.GetAttributes().FirstOrDefault(attr => attr.AttributeClass.ToDisplayString() == TypeNames.NativeMarshallingAttribute); - if (attr is null) - { - return (attr, null); - } - INamedTypeSymbol? marshallerType = null; - if (attr.ConstructorArguments.Length == 0) + if (attr is null || attr.ConstructorArguments.Length == 0) { return (attr, null); } - marshallerType = attr.ConstructorArguments[0].Value as INamedTypeSymbol; - if (managedType is not INamedTypeSymbol namedType || marshallerType is null) + INamedTypeSymbol? entryType = attr.ConstructorArguments[0].Value as INamedTypeSymbol; + if (managedType is not INamedTypeSymbol namedType || entryType is null) { return (attr, null); } + + // Non-generic types involved, return the entry defined in the attribute. if (namedType.TypeArguments.Length == 0) { - return (attr, marshallerType); + return (attr, entryType); } - else if (marshallerType.TypeArguments.Length != namedType.TypeArguments.Length) + + // Mismatch of generic type arguments between the type and entry. + if (namedType.TypeArguments.Length != entryType.TypeArguments.Length) { return (attr, null); } - else if (marshallerType.IsGenericType) + + // If the marshaller is generic, instantiate it based on the type. + if (entryType.IsGenericType) { // Construct the marshaler type around the same type arguments as the managed type. - return (attr, marshallerType.ConstructedFrom.Construct(namedType.TypeArguments, namedType.TypeArgumentNullableAnnotations)); + return (attr, entryType.ConstructedFrom.Construct(namedType.TypeArguments, namedType.TypeArgumentNullableAnnotations)); } - return (attr, marshallerType); + // Entry isn't generic, just return it. + return (attr, entryType); } public static IMethodSymbol? FindGetPinnableReference(ITypeSymbol type) @@ -189,16 +275,6 @@ public static (AttributeData? attribute, INamedTypeSymbol? marshallerType) GetDe ({ ReturnsByRef: true } or { ReturnsByRefReadonly: true })); } - private static CustomTypeMarshallerData? GetNamedArgumentAsMarshallerData(ImmutableDictionary namedArguments, string name, MarshallingDirection direction, ITypeSymbol managedType, Compilation compilation) - { - ITypeSymbol? marshallerType = namedArguments.TryGetValue(name, out TypedConstant typeMaybe) ? typeMaybe.Value as ITypeSymbol : null; - if (marshallerType is null) - return null; - - // TODO: Report invalid shape - return GetMarshallerDataForType(marshallerType, direction, managedType, compilation); - } - private static CustomTypeMarshallerData? GetMarshallerDataForType(ITypeSymbol marshallerType, MarshallingDirection direction, ITypeSymbol managedType, Compilation compilation) { (MarshallerShape shape, Dictionary methodsByShape) = MarshallerShapeHelper.GetShapeForType(marshallerType, managedType, compilation); diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/AttributedMarshallingModelGeneratorFactory.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/AttributedMarshallingModelGeneratorFactory.cs index 41a20f14606e9..b96d5cca47d5a 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/AttributedMarshallingModelGeneratorFactory.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/AttributedMarshallingModelGeneratorFactory.cs @@ -213,15 +213,15 @@ private IMarshallingGenerator CreateCustomNativeTypeMarshaller(TypePositionInfo CustomTypeMarshallerData marshallerData; if (info.IsManagedReturnPosition) { - marshallerData = marshalInfo.ManagedToUnmanagedMarshallers.Out.Value; + marshallerData = marshalInfo.Marshallers.GetScenarioOrDefault(Scenario.ManagedToUnmanagedOut); } else { marshallerData = info.RefKind switch { - RefKind.None or RefKind.In => marshalInfo.ManagedToUnmanagedMarshallers.In.Value, - RefKind.Ref => marshalInfo.ManagedToUnmanagedMarshallers.Ref.Value, - RefKind.Out => marshalInfo.ManagedToUnmanagedMarshallers.Out.Value, + RefKind.None or RefKind.In => marshalInfo.Marshallers.GetScenarioOrDefault(Scenario.ManagedToUnmanagedIn), + RefKind.Ref => marshalInfo.Marshallers.GetScenarioOrDefault(Scenario.ManagedToUnmanagedRef), + RefKind.Out => marshalInfo.Marshallers.GetScenarioOrDefault(Scenario.ManagedToUnmanagedOut), _ => throw new MarshallingNotSupportedException(info, context) }; } @@ -250,30 +250,30 @@ private static void ValidateCustomNativeTypeMarshallingSupported(TypePositionInf { // Marshalling out or return parameter, but no out marshaller is specified if ((info.RefKind == RefKind.Out || info.IsManagedReturnPosition) - && !marshalInfo.ManagedToUnmanagedMarshallers.Out.HasValue) + && !marshalInfo.Marshallers.IsDefinedOrDefault(Scenario.ManagedToUnmanagedOut)) { throw new MarshallingNotSupportedException(info, context) { - NotSupportedDetails = string.Format(SR.UnmanagedToManagedMissingRequiredMarshaller, ManualTypeMarshallingHelper.MarshallersProperties.OutMarshaller, marshalInfo.EntryPointType.FullTypeName) + NotSupportedDetails = string.Format(SR.UnmanagedToManagedMissingRequiredMarshaller, marshalInfo.EntryPointType.FullTypeName) }; } // Marshalling ref parameter, but no ref marshaller is specified - if (info.RefKind == RefKind.Ref && !marshalInfo.ManagedToUnmanagedMarshallers.Ref.HasValue) + if (info.RefKind == RefKind.Ref && !marshalInfo.Marshallers.IsDefinedOrDefault(Scenario.ManagedToUnmanagedRef)) { throw new MarshallingNotSupportedException(info, context) { - NotSupportedDetails = string.Format(SR.BidirectionalMissingRequiredMarshaller, ManualTypeMarshallingHelper.MarshallersProperties.RefMarshaller, marshalInfo.EntryPointType.FullTypeName) + NotSupportedDetails = string.Format(SR.BidirectionalMissingRequiredMarshaller, marshalInfo.EntryPointType.FullTypeName) }; } // Marshalling in parameter, but no in marshaller is specified if (info.RefKind == RefKind.In - && !marshalInfo.ManagedToUnmanagedMarshallers.In.HasValue) + && !marshalInfo.Marshallers.IsDefinedOrDefault(Scenario.ManagedToUnmanagedIn)) { throw new MarshallingNotSupportedException(info, context) { - NotSupportedDetails = string.Format(SR.ManagedToUnmanagedMissingRequiredMarshaller, ManualTypeMarshallingHelper.MarshallersProperties.InMarshaller, marshalInfo.EntryPointType.FullTypeName) + NotSupportedDetails = string.Format(SR.ManagedToUnmanagedMissingRequiredMarshaller, marshalInfo.EntryPointType.FullTypeName) }; } @@ -281,11 +281,11 @@ private static void ValidateCustomNativeTypeMarshallingSupported(TypePositionInf if (!info.IsByRef && !info.IsManagedReturnPosition && context.SingleFrameSpansNativeContext - && !(marshalInfo.IsPinnableManagedType || marshalInfo.ManagedToUnmanagedMarshallers.In.HasValue)) + && !(marshalInfo.IsPinnableManagedType || marshalInfo.Marshallers.IsDefinedOrDefault(Scenario.ManagedToUnmanagedIn))) { throw new MarshallingNotSupportedException(info, context) { - NotSupportedDetails = string.Format(SR.ManagedToUnmanagedMissingRequiredMarshaller, ManualTypeMarshallingHelper.MarshallersProperties.InMarshaller, marshalInfo.EntryPointType.FullTypeName) + NotSupportedDetails = string.Format(SR.ManagedToUnmanagedMissingRequiredMarshaller, marshalInfo.EntryPointType.FullTypeName) }; } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshallingAttributeInfo.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshallingAttributeInfo.cs index 94cb51c028feb..81021677c4f02 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshallingAttributeInfo.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/MarshallingAttributeInfo.cs @@ -142,7 +142,7 @@ public sealed record SizeAndParamIndexInfo(int ConstSize, TypePositionInfo? Para /// public record NativeMarshallingAttributeInfo( ManagedTypeInfo EntryPointType, - CustomTypeMarshallers ManagedToUnmanagedMarshallers, + CustomTypeMarshallers Marshallers, bool IsPinnableManagedType) : MarshallingInfo; /// @@ -584,7 +584,7 @@ private MarshallingInfo CreateNativeMarshallingInfo( ref int maxIndirectionDepthUsed) { bool isLinearCollectionMarshalling = ManualTypeMarshallingHelper.IsLinearCollectionEntryPoint(entryPointType); - if (ManualTypeMarshallingHelper.TryGetMarshallers(entryPointType, type, isLinearCollectionMarshalling, _compilation, out CustomTypeMarshallers? marshallers)) + if (ManualTypeMarshallingHelper.TryGetMarshallersFromEntryType(entryPointType, type, isLinearCollectionMarshalling, _compilation, out CustomTypeMarshallers? marshallers)) { if (!entryPointType.IsStatic) { diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Microsoft.Interop.SourceGeneration.csproj b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Microsoft.Interop.SourceGeneration.csproj index 1033928da4f67..e3325f0935ac9 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Microsoft.Interop.SourceGeneration.csproj +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Microsoft.Interop.SourceGeneration.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 @@ -17,6 +17,8 @@ Link="Production\CustomTypeMarshallerFeatures.cs" /> + diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs index b73f03ca140a4..f9c3128f77243 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeNames.cs @@ -16,7 +16,7 @@ public static class TypeNames public const string CustomTypeMarshallerAttribute = "System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerAttribute"; public const string CustomTypeMarshallerAttributeGenericPlaceholder = "System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerAttribute.GenericPlaceholder"; - public const string ManagedToUnmanagedMarshallersAttribute = "System.Runtime.InteropServices.Marshalling.ManagedToUnmanagedMarshallersAttribute"; + public const string CustomMarshallerAttribute = "System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute"; public const string AnsiStringMarshaller = "System.Runtime.InteropServices.Marshalling.AnsiStringMarshaller"; public const string BStrStringMarshaller = "System.Runtime.InteropServices.Marshalling.BStrStringMarshaller"; diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/CustomMarshallerAttribute.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/CustomMarshallerAttribute.cs new file mode 100644 index 0000000000000..2c8de498a974b --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/CustomMarshallerAttribute.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#nullable enable + +using System; + +namespace System.Runtime.InteropServices.Marshalling +{ + /// + /// Attribute to indicate an entry point type for defining a marshaller. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] +#if LIBRARYIMPORT_GENERATOR_TEST + public +#else + internal +#endif + sealed class CustomMarshallerAttribute : Attribute + { + /// + /// Create a instance. + /// + /// Managed type to marshal. + /// Marshalling scenario. + /// Type used for marshalling. + public CustomMarshallerAttribute(Type managedType, Scenario scenario, Type marshallerType) { } + + /// + /// Placeholder type for generic parameter + /// + public struct GenericPlaceholder + { + } + } +} + diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/CustomTypeMarshallersAttributeBase.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/CustomTypeMarshallersAttributeBase.cs deleted file mode 100644 index be020d0f71696..0000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/CustomTypeMarshallersAttributeBase.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Runtime.InteropServices.Marshalling -{ - /// - /// Base class attribute for custom marshaller attributes. - /// - /// - /// Use a base class here to allow doing ManagedToUnmanagedMarshallersAttribute.GenericPlaceholder, etc. without having 3 separate placeholder types. - /// For the following attribute types, any marshaller types that are provided will be validated by an analyzer to have the correct members to prevent - /// developers from accidentally typoing a member like Free() and causing memory leaks. - /// -#if LIBRARYIMPORT_GENERATOR_TEST - public -#else - internal -#endif - abstract class CustomUnmanagedTypeMarshallersAttributeBase : Attribute - { - /// - /// Placeholder type for generic parameter - /// - public struct GenericPlaceholder { } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ManagedToUnmanagedMarshallersAttribute.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ManagedToUnmanagedMarshallersAttribute.cs deleted file mode 100644 index d950e222992fc..0000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/ManagedToUnmanagedMarshallersAttribute.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -#nullable enable - -namespace System.Runtime.InteropServices.Marshalling -{ - /// - /// Specify marshallers used in the managed to unmanaged direction (that is, P/Invoke) - /// -#if LIBRARYIMPORT_GENERATOR_TEST - public -#else - internal -#endif - sealed class ManagedToUnmanagedMarshallersAttribute : CustomUnmanagedTypeMarshallersAttributeBase - { - /// - /// Create instance of . - /// - /// Managed type to marshal - public ManagedToUnmanagedMarshallersAttribute(Type managedType) { } - - /// - /// Marshaller to use when a parameter of the managed type is passed by-value or with the in keyword. - /// - public Type? InMarshaller { get; set; } - - /// - /// Marshaller to use when a parameter of the managed type is passed by-value or with the ref keyword. - /// - public Type? RefMarshaller { get; set; } - - /// - /// Marshaller to use when a parameter of the managed type is passed by-value or with the out keyword. - /// - public Type? OutMarshaller { get; set; } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Scenario.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Scenario.cs new file mode 100644 index 0000000000000..860875bf6d2ad --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Scenario.cs @@ -0,0 +1,68 @@ +// 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.Text; +using System.Threading.Tasks; + +#if MICROSOFT_INTEROP_SOURCEGENERATION +namespace Microsoft.Interop +#else +namespace System.Runtime.InteropServices +#endif +{ + /// + /// An enumeration representing the different marshalling scenarios in our marshalling model. + /// +#if LIBRARYIMPORT_GENERATOR_TEST || MICROSOFT_INTEROP_SOURCEGENERATION + public +#else + internal +#endif + enum Scenario + { + /// + /// All scenarios. A marshaller specified with this scenario will be used if there is not a specific + /// marshaller specified for a given usage scenario. + /// + Default, + /// + /// By-value and in parameters in managed-to-unmanaged scenarios, like P/Invoke. + /// + ManagedToUnmanagedIn, + /// + /// ref parameters in managed-to-unmanaged scenarios, like P/Invoke. + /// + ManagedToUnmanagedRef, + /// + /// out parameters in managed-to-unmanaged scenarios, like P/Invoke. + /// + ManagedToUnmanagedOut, + /// + /// By-value and in parameters in unmanaged-to-managed scenarios, like Reverse P/Invoke. + /// + UnmanagedToManagedIn, + /// + /// ref parameters in unmanaged-to-managed scenarios, like Reverse P/Invoke. + /// + UnmanagedToManagedRef, + /// + /// out parameters in unmanaged-to-managed scenarios, like Reverse P/Invoke. + /// + UnmanagedToManagedOut, + /// + /// Elements of arrays passed with in or by-value in interop scenarios. + /// + ElementIn, + /// + /// Elements of arrays passed with ref or passed by-value with both and in interop scenarios. + /// + ElementRef, + /// + /// Elements of arrays passed with out or passed by-value with only in interop scenarios. + /// + ElementOut + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CustomMarshallingTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CustomMarshallingTests.cs index 7288cd3615ca7..8b31f79446a4b 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CustomMarshallingTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CustomMarshallingTests.cs @@ -43,13 +43,13 @@ public static partial void NegateBools( [return: MarshalUsing(typeof(IntGuaranteedUnmarshal))] public static partial int GuaranteedUnmarshal([MarshalUsing(typeof(ExceptionOnUnmarshal))] out int ret); - [ManagedToUnmanagedMarshallers(typeof(int))] + [CustomMarshaller(typeof(int), Scenario.ManagedToUnmanagedIn, typeof(ExceptionOnUnmarshal))] public static class ExceptionOnUnmarshal { public static int ConvertToManaged(int unmanaged) => throw new Exception(); } - [ManagedToUnmanagedMarshallers(typeof(int))] + [CustomMarshaller(typeof(int), Scenario.Default, typeof(IntGuaranteedUnmarshal))] public static unsafe class IntGuaranteedUnmarshal { public static bool ConvertToManagedGuaranteedCalled = false; diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs index 70261fd077f39..451d7a2eb60f8 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs @@ -690,7 +690,7 @@ public struct S }} "; private static string NonStatic = @" -[ManagedToUnmanagedMarshallers(typeof(S))] +[CustomMarshaller(typeof(S), Scenario.ManagedToUnmanagedIn, typeof(Marshaller))] public class Marshaller { public struct Native { } @@ -699,7 +699,7 @@ public struct Native { } } "; private static string StatelessIn = @" -[ManagedToUnmanagedMarshallers(typeof(S))] +[CustomMarshaller(typeof(S), Scenario.ManagedToUnmanagedIn, typeof(Marshaller))] public static class Marshaller { public struct Native { } @@ -708,7 +708,7 @@ public struct Native { } } "; private static string StatelessInBuffer = @" -[ManagedToUnmanagedMarshallers(typeof(S))] +[CustomMarshaller(typeof(S), Scenario.ManagedToUnmanagedIn, typeof(Marshaller))] public static class Marshaller { public struct Native { } @@ -718,7 +718,7 @@ public struct Native { } } "; private static string StatelessOut = @" -[ManagedToUnmanagedMarshallers(typeof(S))] +[CustomMarshaller(typeof(S), Scenario.UnmanagedToManagedOut, typeof(Marshaller))] public static class Marshaller { public struct Native { } @@ -727,7 +727,7 @@ public struct Native { } } "; private static string StatelessOutGuaranteed = @" -[ManagedToUnmanagedMarshallers(typeof(S))] +[CustomMarshaller(typeof(S), Scenario.UnmanagedToManagedOut, typeof(Marshaller))] public static class Marshaller { public struct Native { } @@ -736,7 +736,7 @@ public struct Native { } } "; public static string StatelessRef = @" -[ManagedToUnmanagedMarshallers(typeof(S))] +[CustomMarshaller(typeof(S), Scenario.Default, typeof(Marshaller))] public static class Marshaller { public struct Native { } @@ -746,7 +746,7 @@ public struct Native { } } "; public static string StatelessRefBuffer = @" -[ManagedToUnmanagedMarshallers(typeof(S))] +[CustomMarshaller(typeof(S), Scenario.Default, typeof(Marshaller))] public static class Marshaller { public struct Native { } @@ -757,7 +757,7 @@ public struct Native { } } "; public static string StatelessRefOptionalBuffer = @" -[ManagedToUnmanagedMarshallers(typeof(S))] +[CustomMarshaller(typeof(S), Scenario.Default, typeof(Marshaller))] public static class Marshaller { public struct Native { } diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs index 6451bc7e86b7e..609ef8c8ab5d2 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs @@ -19,10 +19,9 @@ public struct StringContainer public string str2; } - [ManagedToUnmanagedMarshallers(typeof(StringContainer), - InMarshaller = typeof(In), - RefMarshaller = typeof(Ref), - OutMarshaller = typeof(Out))] + [CustomMarshaller(typeof(StringContainer), Scenario.ManagedToUnmanagedIn, typeof(In))] + [CustomMarshaller(typeof(StringContainer), Scenario.ManagedToUnmanagedRef, typeof(Ref))] + [CustomMarshaller(typeof(StringContainer), Scenario.ManagedToUnmanagedOut, typeof(Out))] public static class StringContainerMarshaller { public struct StringContainerNative @@ -77,7 +76,7 @@ public static void Free(StringContainerNative unmanaged) } } - [ManagedToUnmanagedMarshallers(typeof(double))] + [CustomMarshaller(typeof(double), Scenario.ManagedToUnmanagedIn, typeof(DoubleToBytesBigEndianMarshaller))] public static unsafe class DoubleToBytesBigEndianMarshaller { public const int BufferSize = 8; @@ -89,7 +88,7 @@ public static unsafe class DoubleToBytesBigEndianMarshaller } } - [ManagedToUnmanagedMarshallers(typeof(double))] + [CustomMarshaller(typeof(double), Scenario.ManagedToUnmanagedIn, typeof(DoubleToLongMarshaller))] public static class DoubleToLongMarshaller { public static long ConvertToUnmanaged(double managed) @@ -106,7 +105,7 @@ public struct BoolStruct public bool b3; } - [ManagedToUnmanagedMarshallers(typeof(BoolStruct))] + [CustomMarshaller(typeof(BoolStruct), Scenario.Default, typeof(BoolStructMarshaller))] public static class BoolStructMarshaller { public struct BoolStructNative @@ -145,7 +144,7 @@ public class IntWrapper public ref int GetPinnableReference() => ref i; } - [ManagedToUnmanagedMarshallers(typeof(IntWrapper))] + [CustomMarshaller(typeof(IntWrapper), Scenario.Default, typeof(IntWrapperMarshaller))] public static unsafe class IntWrapperMarshaller { public static int* ConvertToUnmanaged(IntWrapper managed) From 0a57a6596574946859543f4f11e81723eaee6868 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Tue, 28 Jun 2022 11:44:18 -0700 Subject: [PATCH 2/6] Update included source files --- eng/generators.targets | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/generators.targets b/eng/generators.targets index 92c76103b1706..cc04480a7ff6d 100644 --- a/eng/generators.targets +++ b/eng/generators.targets @@ -69,8 +69,8 @@ - - + + From 398cccd85d291de477a3cba6f8f4525f14d7af46 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Tue, 28 Jun 2022 11:44:38 -0700 Subject: [PATCH 3/6] Fix getting marshaller data for scenario --- .../ManualTypeMarshallingHelper.cs | 27 ++++++++++++------- .../CustomMarshallerAttribute.cs | 1 - .../CustomMarshallingTests.cs | 4 +-- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs index d20ec7712a71e..7bc23bcf9c3cb 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs @@ -115,26 +115,35 @@ public static bool TryGetMarshallersFromEntryType( if (marshallerTypeOnAttr is null) continue; - CustomTypeMarshallerData? data = marshallerScenario switch + MarshallingDirection direction = marshallerScenario switch { - Scenario.Default => GetMarshallerDataForType(entryPointType, MarshallingDirection.Bidirectional, managedTypeOnAttr, compilation), + Scenario.Default + => MarshallingDirection.Bidirectional, Scenario.ManagedToUnmanagedIn - or Scenario.ManagedToUnmanagedRef - or Scenario.ManagedToUnmanagedOut => GetMarshallerDataForType(entryPointType, MarshallingDirection.ManagedToUnmanaged, managedTypeOnAttr, compilation), + or Scenario.UnmanagedToManagedOut + => MarshallingDirection.ManagedToUnmanaged, - Scenario.UnmanagedToManagedIn + Scenario.ManagedToUnmanagedOut + or Scenario.UnmanagedToManagedIn + => MarshallingDirection.UnmanagedToManaged, + + Scenario.ManagedToUnmanagedRef or Scenario.UnmanagedToManagedRef - or Scenario.UnmanagedToManagedOut => GetMarshallerDataForType(entryPointType, MarshallingDirection.UnmanagedToManaged, managedTypeOnAttr, compilation), + => MarshallingDirection.Bidirectional, Scenario.ElementIn or Scenario.ElementRef - or Scenario.ElementOut => GetMarshallerDataForType(entryPointType, MarshallingDirection.Bidirectional, managedTypeOnAttr, compilation), + or Scenario.ElementOut + => MarshallingDirection.Bidirectional, - _ => null + _ => throw new UnreachableException() }; - // TODO: Should we fire a diagnostic for duplicated scenarios or just take the last one? + CustomTypeMarshallerData? data = GetMarshallerDataForType(marshallerTypeOnAttr, direction, managedTypeOnAttr, compilation); + + // TODO: Report invalid shape for scenario + // Should we fire a diagnostic for duplicated scenarios or just take the last one? if (data is null || scenarios.ContainsKey(marshallerScenario)) { diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/CustomMarshallerAttribute.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/CustomMarshallerAttribute.cs index 2c8de498a974b..55abce124ab5c 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/CustomMarshallerAttribute.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/CustomMarshallerAttribute.cs @@ -33,4 +33,3 @@ public struct GenericPlaceholder } } } - diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CustomMarshallingTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CustomMarshallingTests.cs index 8b31f79446a4b..f35fdcd05ba52 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CustomMarshallingTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CustomMarshallingTests.cs @@ -43,13 +43,13 @@ public static partial void NegateBools( [return: MarshalUsing(typeof(IntGuaranteedUnmarshal))] public static partial int GuaranteedUnmarshal([MarshalUsing(typeof(ExceptionOnUnmarshal))] out int ret); - [CustomMarshaller(typeof(int), Scenario.ManagedToUnmanagedIn, typeof(ExceptionOnUnmarshal))] + [CustomMarshaller(typeof(int), Scenario.ManagedToUnmanagedOut, typeof(ExceptionOnUnmarshal))] public static class ExceptionOnUnmarshal { public static int ConvertToManaged(int unmanaged) => throw new Exception(); } - [CustomMarshaller(typeof(int), Scenario.Default, typeof(IntGuaranteedUnmarshal))] + [CustomMarshaller(typeof(int), Scenario.ManagedToUnmanagedOut, typeof(IntGuaranteedUnmarshal))] public static unsafe class IntGuaranteedUnmarshal { public static bool ConvertToManagedGuaranteedCalled = false; From d0ff86ae0ecda271fd3f21a80d3bd51e0349f369 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Tue, 28 Jun 2022 12:28:03 -0700 Subject: [PATCH 4/6] Revert changes to analyzer behaviour --- .../Analyzers/CustomTypeMarshallerAnalyzer.cs | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerAnalyzer.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerAnalyzer.cs index 1342ce5e46aa7..6b6e1e1589aaa 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerAnalyzer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomTypeMarshallerAnalyzer.cs @@ -494,14 +494,29 @@ private static void AnalyzeManagedTypeMarshallingInfo( managedType.ToDisplayString())); } - bool isLinearCollectionMarshalling = ManualTypeMarshallingHelper.IsLinearCollectionEntryPoint(entryType); - bool hasMarshallers = ManualTypeMarshallingHelper.TryGetMarshallersFromEntryType( - entryType, - managedType, - isLinearCollectionMarshalling, - context.Compilation, - out CustomTypeMarshallers? _); - if (!hasMarshallers) + (bool hasCustomTypeMarshallerAttribute, ITypeSymbol? marshallerManagedType, _) = ManualTypeMarshallingHelper_V1.GetMarshallerShapeInfo(entryType); + + marshallerManagedType = ManualTypeMarshallingHelper.ResolveManagedType(marshallerManagedType, entryType, context.Compilation); + + if (!hasCustomTypeMarshallerAttribute) + { + context.ReportDiagnostic( + attributeData.CreateDiagnostic( + NativeTypeMustHaveCustomTypeMarshallerAttributeRule, + managedType.ToDisplayString())); + return; + } + + if (marshallerManagedType is null) + { + context.ReportDiagnostic( + attributeData.CreateDiagnostic( + NativeTypeMustHaveCustomTypeMarshallerAttributeRule, + managedType.ToDisplayString())); + return; + } + + if (!TypeSymbolsConstructedFromEqualTypes(managedType, marshallerManagedType)) { context.ReportDiagnostic( attributeData.CreateDiagnostic( From a40e14858be51a771f4c3f1c8ff27db96b11d2bd Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Tue, 28 Jun 2022 12:33:28 -0700 Subject: [PATCH 5/6] Fix unit tests --- .../ManualTypeMarshallingHelper.cs | 6 ++++-- .../tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs | 7 ++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs index 7bc23bcf9c3cb..6ec395354d2fc 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs @@ -115,6 +115,7 @@ public static bool TryGetMarshallersFromEntryType( if (marshallerTypeOnAttr is null) continue; + // TODO: We can probably get rid of MarshallingDirection and just use Scenario instead MarshallingDirection direction = marshallerScenario switch { Scenario.Default @@ -140,10 +141,11 @@ or Scenario.ElementOut _ => throw new UnreachableException() }; + // TODO: Report invalid shape for scenario + // Skip checking for bidirectional support for Default scenario - always take / store marshaller data CustomTypeMarshallerData? data = GetMarshallerDataForType(marshallerTypeOnAttr, direction, managedTypeOnAttr, compilation); - // TODO: Report invalid shape for scenario - // Should we fire a diagnostic for duplicated scenarios or just take the last one? + // TODO: Should we fire a diagnostic for duplicated scenarios or just take the last one? if (data is null || scenarios.ContainsKey(marshallerScenario)) { diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs index 451d7a2eb60f8..940cd5ab2b02e 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs @@ -718,7 +718,7 @@ public struct Native { } } "; private static string StatelessOut = @" -[CustomMarshaller(typeof(S), Scenario.UnmanagedToManagedOut, typeof(Marshaller))] +[CustomMarshaller(typeof(S), Scenario.ManagedToUnmanagedOut, typeof(Marshaller))] public static class Marshaller { public struct Native { } @@ -727,7 +727,7 @@ public struct Native { } } "; private static string StatelessOutGuaranteed = @" -[CustomMarshaller(typeof(S), Scenario.UnmanagedToManagedOut, typeof(Marshaller))] +[CustomMarshaller(typeof(S), Scenario.ManagedToUnmanagedOut, typeof(Marshaller))] public static class Marshaller { public struct Native { } @@ -746,7 +746,8 @@ public struct Native { } } "; public static string StatelessRefBuffer = @" -[CustomMarshaller(typeof(S), Scenario.Default, typeof(Marshaller))] +[CustomMarshaller(typeof(S), Scenario.ManagedToUnmanagedIn, typeof(Marshaller))] +[CustomMarshaller(typeof(S), Scenario.ManagedToUnmanagedOut, typeof(Marshaller))] public static class Marshaller { public struct Native { } From 211c1d73e39fc319b22610535b74f09041efcd74 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Tue, 28 Jun 2022 12:44:36 -0700 Subject: [PATCH 6/6] Update product --- src/libraries/Common/src/Interop/Interop.Ldap.cs | 2 +- .../Windows/CryptUI/Interop.CryptUIDlgCertificate.cs | 4 ++-- .../Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs | 2 +- .../src/Interop/Windows/WinHttp/Interop.winhttp_types.cs | 2 +- .../src/Interop/Windows/WinMm/Interop.waveOutGetDevCaps.cs | 2 +- .../src/Interop/Windows/WinSock/Interop.WinsockBSD.cs | 3 ++- .../src/System/Diagnostics/Reader/UnsafeNativeMethods.cs | 2 +- .../src/Interop/Windows/Interop.Gdi32.cs | 2 +- .../tests/Ancillary.Interop/Scenario.cs | 6 ------ 9 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/libraries/Common/src/Interop/Interop.Ldap.cs b/src/libraries/Common/src/Interop/Interop.Ldap.cs index cce83e2111c66..cc830f8da215d 100644 --- a/src/libraries/Common/src/Interop/Interop.Ldap.cs +++ b/src/libraries/Common/src/Interop/Interop.Ldap.cs @@ -49,7 +49,7 @@ internal struct SEC_WINNT_AUTH_IDENTITY_EX public int packageListLength; #if NET7_0_OR_GREATER - [ManagedToUnmanagedMarshallers(typeof(SEC_WINNT_AUTH_IDENTITY_EX), InMarshaller = typeof(Marshaller))] + [CustomMarshaller(typeof(SEC_WINNT_AUTH_IDENTITY_EX), Scenario.ManagedToUnmanagedIn, typeof(Marshaller))] internal static class Marshaller { public static Native ConvertToUnmanaged(SEC_WINNT_AUTH_IDENTITY_EX managed) diff --git a/src/libraries/Common/src/Interop/Windows/CryptUI/Interop.CryptUIDlgCertificate.cs b/src/libraries/Common/src/Interop/Windows/CryptUI/Interop.CryptUIDlgCertificate.cs index 23819aef23053..8342b5c405af5 100644 --- a/src/libraries/Common/src/Interop/Windows/CryptUI/Interop.CryptUIDlgCertificate.cs +++ b/src/libraries/Common/src/Interop/Windows/CryptUI/Interop.CryptUIDlgCertificate.cs @@ -40,7 +40,7 @@ internal struct CRYPTUI_VIEWCERTIFICATE_STRUCTW internal uint nStartPage; #if NET7_0_OR_GREATER - [ManagedToUnmanagedMarshallers(typeof(CRYPTUI_VIEWCERTIFICATE_STRUCTW), InMarshaller = typeof(Marshaller), RefMarshaller = typeof(Marshaller), OutMarshaller = typeof(Marshaller))] + [CustomMarshaller(typeof(CRYPTUI_VIEWCERTIFICATE_STRUCTW), Scenario.Default, typeof(Marshaller))] public static class Marshaller { public static Native ConvertToUnmanaged(CRYPTUI_VIEWCERTIFICATE_STRUCTW managed) => new(managed); @@ -152,7 +152,7 @@ internal struct CRYPTUI_SELECTCERTIFICATE_STRUCTW internal IntPtr hSelectedCertStore; #if NET7_0_OR_GREATER - [ManagedToUnmanagedMarshallers(typeof(CRYPTUI_SELECTCERTIFICATE_STRUCTW), InMarshaller = typeof(Marshaller), RefMarshaller = typeof(Marshaller), OutMarshaller = typeof(Marshaller))] + [CustomMarshaller(typeof(CRYPTUI_SELECTCERTIFICATE_STRUCTW), Scenario.Default, typeof(Marshaller))] public static class Marshaller { public static Native ConvertToUnmanaged(CRYPTUI_SELECTCERTIFICATE_STRUCTW managed) => new(managed); diff --git a/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs b/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs index a40773a564dc9..14306c82e366c 100644 --- a/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs +++ b/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs @@ -56,7 +56,7 @@ public static partial bool WinHttpAddRequestHeaders( uint modifiers); #if NET7_0_OR_GREATER - [ManagedToUnmanagedMarshallers(typeof(StringBuilder), InMarshaller = typeof(SimpleStringBufferMarshaller))] + [CustomMarshaller(typeof(StringBuilder), Scenario.ManagedToUnmanagedIn, typeof(SimpleStringBufferMarshaller))] private static unsafe class SimpleStringBufferMarshaller { public static void* ConvertToUnmanaged(StringBuilder builder) diff --git a/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp_types.cs b/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp_types.cs index f83448e81b131..fe86bda775829 100644 --- a/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp_types.cs +++ b/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp_types.cs @@ -262,7 +262,7 @@ public struct WINHTTP_AUTOPROXY_OPTIONS [MarshalAs(UnmanagedType.Bool)] public bool AutoLoginIfChallenged; #if NET7_0_OR_GREATER - [ManagedToUnmanagedMarshallers(typeof(WINHTTP_AUTOPROXY_OPTIONS), InMarshaller = typeof(Marshaller), RefMarshaller = typeof(Marshaller), OutMarshaller = typeof(Marshaller))] + [CustomMarshaller(typeof(WINHTTP_AUTOPROXY_OPTIONS), Scenario.Default, typeof(Marshaller))] public static class Marshaller { public static Native ConvertToUnmanaged(WINHTTP_AUTOPROXY_OPTIONS managed) => new(managed); diff --git a/src/libraries/Common/src/Interop/Windows/WinMm/Interop.waveOutGetDevCaps.cs b/src/libraries/Common/src/Interop/Windows/WinMm/Interop.waveOutGetDevCaps.cs index 329a997c5e9b7..34c29cf317306 100644 --- a/src/libraries/Common/src/Interop/Windows/WinMm/Interop.waveOutGetDevCaps.cs +++ b/src/libraries/Common/src/Interop/Windows/WinMm/Interop.waveOutGetDevCaps.cs @@ -28,7 +28,7 @@ internal struct WAVEOUTCAPS private ushort wReserved1; private ushort dwSupport; #if NET7_0_OR_GREATER - [ManagedToUnmanagedMarshallers(typeof(WAVEOUTCAPS), InMarshaller = typeof(Marshaller), RefMarshaller = typeof(Marshaller), OutMarshaller = typeof(Marshaller))] + [CustomMarshaller(typeof(WAVEOUTCAPS), Scenario.Default, typeof(Marshaller))] public static class Marshaller { public static Native ConvertToUnmanaged(WAVEOUTCAPS managed) => new(managed); diff --git a/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WinsockBSD.cs b/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WinsockBSD.cs index 2a8421a376e83..54bb6149a024c 100644 --- a/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WinsockBSD.cs +++ b/src/libraries/Common/src/Interop/Windows/WinSock/Interop.WinsockBSD.cs @@ -74,7 +74,8 @@ internal struct IPv6MulticastRequest { internal byte[] MulticastAddress; // IP address of group. internal int InterfaceIndex; // Local interface index. - [ManagedToUnmanagedMarshallers(typeof(IPv6MulticastRequest), InMarshaller = typeof(Marshaller), RefMarshaller = typeof(Marshaller), OutMarshaller = typeof(Marshaller))] + + [CustomMarshaller(typeof(IPv6MulticastRequest), Scenario.Default, typeof(Marshaller))] public static class Marshaller { public static Native ConvertToUnmanaged(IPv6MulticastRequest managed) => new(managed); diff --git a/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/Reader/UnsafeNativeMethods.cs b/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/Reader/UnsafeNativeMethods.cs index 3c6a4f5e61baf..49015a133a3d3 100644 --- a/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/Reader/UnsafeNativeMethods.cs +++ b/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/Reader/UnsafeNativeMethods.cs @@ -703,7 +703,7 @@ internal struct EvtStringVariant public uint Type; #if NET7_0_OR_GREATER - [ManagedToUnmanagedMarshallers(typeof(EvtStringVariant), InMarshaller = typeof(Marshaller), RefMarshaller = typeof(Marshaller), OutMarshaller = typeof(Marshaller))] + [CustomMarshaller(typeof(EvtStringVariant), Scenario.Default, typeof(Marshaller))] public static class Marshaller { public static Native ConvertToUnmanaged(EvtStringVariant managed) => new(managed); diff --git a/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Gdi32.cs b/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Gdi32.cs index 08bd1c3971082..140356c796716 100644 --- a/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Gdi32.cs +++ b/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Gdi32.cs @@ -188,7 +188,7 @@ internal sealed class DOCINFO internal int fwType; #if NET7_0_OR_GREATER - [ManagedToUnmanagedMarshallers(typeof(DOCINFO), InMarshaller = typeof(Marshaller))] + [CustomMarshaller(typeof(DOCINFO), Scenario.ManagedToUnmanagedIn, typeof(Marshaller))] public static class Marshaller { public static Native ConvertToUnmanaged(DOCINFO managed) => new(managed); diff --git a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Scenario.cs b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Scenario.cs index 860875bf6d2ad..485d241870649 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Scenario.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/Ancillary.Interop/Scenario.cs @@ -1,12 +1,6 @@ // 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.Text; -using System.Threading.Tasks; - #if MICROSOFT_INTEROP_SOURCEGENERATION namespace Microsoft.Interop #else